From e478ced1cedc800cfefb313ca5a72df42863f3ef Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Feb 2022 19:12:30 -0500 Subject: [PATCH] RequirementMachine: Move some code into a new SimplifySubstitutions.cpp --- lib/AST/CMakeLists.txt | 1 + lib/AST/RequirementMachine/PropertyMap.cpp | 171 ---------- lib/AST/RequirementMachine/RewriteSystem.cpp | 112 ------- .../SimplifySubstitutions.cpp | 300 ++++++++++++++++++ 4 files changed, 301 insertions(+), 283 deletions(-) create mode 100644 lib/AST/RequirementMachine/SimplifySubstitutions.cpp diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 00e6121f567..ffe6601c6f5 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -89,6 +89,7 @@ add_swift_host_library(swiftAST STATIC RequirementMachine/RewriteContext.cpp RequirementMachine/RewriteLoop.cpp RequirementMachine/RewriteSystem.cpp + RequirementMachine/SimplifySubstitutions.cpp RequirementMachine/Symbol.cpp RequirementMachine/Term.cpp RequirementMachine/TypeDifference.cpp diff --git a/lib/AST/RequirementMachine/PropertyMap.cpp b/lib/AST/RequirementMachine/PropertyMap.cpp index 0216639e4df..f60cbe700e8 100644 --- a/lib/AST/RequirementMachine/PropertyMap.cpp +++ b/lib/AST/RequirementMachine/PropertyMap.cpp @@ -414,177 +414,6 @@ void PropertyMap::buildPropertyMap() { verify(); } -/// Similar to RewriteSystem::simplifySubstitutions(), but also replaces type -/// parameters with concrete types and builds a type difference describing -/// the transformation. -/// -/// Returns None if the concrete type symbol cannot be simplified further. -/// -/// Otherwise returns an index which can be passed to -/// RewriteSystem::getTypeDifference(). -Optional -PropertyMap::concretelySimplifySubstitutions(Term baseTerm, Symbol symbol, - RewritePath *path) const { - assert(symbol.hasSubstitutions()); - - // Fast path if the type is fully concrete. - auto substitutions = symbol.getSubstitutions(); - if (substitutions.empty()) - return None; - - // Save the original rewrite path length so that we can reset if if we don't - // find anything to simplify. - unsigned oldSize = (path ? path->size() : 0); - - if (path) { - // The term is at the top of the primary stack. Push all substitutions onto - // the primary stack. - path->add(RewriteStep::forDecompose(substitutions.size(), - /*inverse=*/false)); - - // Move all substitutions but the first one to the secondary stack. - for (unsigned i = 1; i < substitutions.size(); ++i) - path->add(RewriteStep::forShift(/*inverse=*/false)); - } - - // Simplify and collect substitutions. - llvm::SmallVector, 1> sameTypes; - llvm::SmallVector, 1> concreteTypes; - - for (unsigned index : indices(substitutions)) { - // Move the next substitution from the secondary stack to the primary stack. - if (index != 0 && path) - path->add(RewriteStep::forShift(/*inverse=*/true)); - - auto term = symbol.getSubstitutions()[index]; - MutableTerm mutTerm(term); - - // Note that it's of course possible that the term both requires - // simplification, and the simplified term has a concrete type. - // - // This isn't handled with our current representation of - // TypeDifference, but that should be fine since the caller - // has to iterate until fixed point anyway. - // - // This should be rare in practice. - if (System.simplify(mutTerm, path)) { - // Record a mapping from this substitution to the simplified term. - sameTypes.emplace_back(index, Term::get(mutTerm, Context)); - } else { - auto *props = lookUpProperties(mutTerm); - - if (props && props->ConcreteType) { - // The property map entry might apply to a suffix of the substitution - // term, so prepend the appropriate prefix to its own substitutions. - auto prefix = props->getPrefixAfterStrippingKey(mutTerm); - auto concreteSymbol = - props->ConcreteType->prependPrefixToConcreteSubstitutions( - prefix, Context); - - // Record a mapping from this substitution to the concrete type. - concreteTypes.emplace_back(index, concreteSymbol); - - // If U.V is the substitution term and V is the property map key, - // apply the rewrite step U.(V => V.[concrete: C]) followed by - // prepending the prefix U to each substitution in the concrete type - // symbol if |U| > 0. - if (path) { - path->add(RewriteStep::forRewriteRule(/*startOffset=*/prefix.size(), - /*endOffset=*/0, - /*ruleID=*/*props->ConcreteTypeRule, - /*inverse=*/true)); - - if (!prefix.empty()) { - path->add(RewriteStep::forPrefixSubstitutions(/*length=*/prefix.size(), - /*endOffset=*/0, - /*inverse=*/false)); - } - } - } - } - } - - // If nothing changed, we don't have to build the type difference. - if (sameTypes.empty() && concreteTypes.empty()) { - if (path) { - // The rewrite path should consist of a Decompose, followed by a number - // of Shifts, followed by a Compose. - #ifndef NDEBUG - for (auto iter = path->begin() + oldSize; iter < path->end(); ++iter) { - assert(iter->Kind == RewriteStep::Shift || - iter->Kind == RewriteStep::Decompose); - } - #endif - - path->resize(oldSize); - } - return None; - } - - auto difference = buildTypeDifference(baseTerm, symbol, - sameTypes, concreteTypes, - Context); - assert(difference.LHS != difference.RHS); - - unsigned differenceID = System.recordTypeDifference(difference); - - // All simplified substitutions are now on the primary stack. Collect them to - // produce the new term. - if (path) { - path->add(RewriteStep::forDecomposeConcrete(differenceID, - /*inverse=*/true)); - } - - return differenceID; -} - -void PropertyMap::concretelySimplifyLeftHandSideSubstitutions() const { - for (unsigned ruleID = 0, e = System.getRules().size(); ruleID < e; ++ruleID) { - auto &rule = System.getRule(ruleID); - if (rule.isLHSSimplified() || - rule.isRHSSimplified() || - rule.isSubstitutionSimplified()) - continue; - - auto optSymbol = rule.isPropertyRule(); - if (!optSymbol || !optSymbol->hasSubstitutions()) - continue; - - auto symbol = *optSymbol; - - RewritePath path; - - auto differenceID = concretelySimplifySubstitutions( - rule.getRHS(), symbol, &path); - if (!differenceID) - continue; - - rule.markSubstitutionSimplified(); - - auto difference = System.getTypeDifference(*differenceID); - assert(difference.LHS == symbol); - - // If the original rule is (T.[concrete: C] => T) and [concrete: C'] is - // the simplified symbol, then difference.LHS == [concrete: C] and - // difference.RHS == [concrete: C'], and the rewrite path we just - // built takes T.[concrete: C] to T.[concrete: C']. - // - // We want a path from T.[concrete: C'] to T, so invert the path to get - // a path from T.[concrete: C'] to T.[concrete: C], and add a final step - // applying the original rule (T.[concrete: C] => T). - path.invert(); - path.add(RewriteStep::forRewriteRule(/*startOffset=*/0, - /*endOffset=*/0, - /*ruleID=*/ruleID, - /*inverted=*/false)); - MutableTerm rhs(rule.getRHS()); - MutableTerm lhs(rhs); - lhs.add(difference.RHS); - - System.addRule(lhs, rhs, &path); - } -} - void PropertyMap::dump(llvm::raw_ostream &out) const { out << "Property map: {\n"; for (const auto &props : Entries) { diff --git a/lib/AST/RequirementMachine/RewriteSystem.cpp b/lib/AST/RequirementMachine/RewriteSystem.cpp index c2d4e8e96ba..e42e100b862 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.cpp +++ b/lib/AST/RequirementMachine/RewriteSystem.cpp @@ -333,81 +333,6 @@ bool RewriteSystem::simplify(MutableTerm &term, RewritePath *path) const { return changed; } -/// Simplify terms appearing in the substitutions of the last symbol of \p term, -/// which must be a superclass or concrete type symbol. -bool RewriteSystem::simplifySubstitutions(Symbol &symbol, - RewritePath *path) const { - assert(symbol.hasSubstitutions()); - - // Fast path if the type is fully concrete. - auto substitutions = symbol.getSubstitutions(); - if (substitutions.empty()) - return false; - - // Save the original rewrite path length so that we can reset if if we don't - // find anything to simplify. - unsigned oldSize = (path ? path->size() : 0); - - if (path) { - // The term is at the top of the primary stack. Push all substitutions onto - // the primary stack. - path->add(RewriteStep::forDecompose(substitutions.size(), - /*inverse=*/false)); - - // Move all substitutions but the first one to the secondary stack. - for (unsigned i = 1; i < substitutions.size(); ++i) - path->add(RewriteStep::forShift(/*inverse=*/false)); - } - - // Simplify and collect substitutions. - SmallVector newSubstitutions; - newSubstitutions.reserve(substitutions.size()); - - bool first = true; - bool anyChanged = false; - for (auto substitution : substitutions) { - // Move the next substitution from the secondary stack to the primary stack. - if (!first && path) - path->add(RewriteStep::forShift(/*inverse=*/true)); - first = false; - - // The current substitution is at the top of the primary stack; simplify it. - MutableTerm mutTerm(substitution); - anyChanged |= simplify(mutTerm, path); - - // Record the new substitution. - newSubstitutions.push_back(Term::get(mutTerm, Context)); - } - - // All simplified substitutions are now on the primary stack. Collect them to - // produce the new term. - if (path) { - path->add(RewriteStep::forDecompose(substitutions.size(), - /*inverse=*/true)); - } - - // If nothing changed, we don't have to rebuild the symbol. - if (!anyChanged) { - if (path) { - // The rewrite path should consist of a Decompose, followed by a number - // of Shifts, followed by a Compose. - #ifndef NDEBUG - for (auto iter = path->begin() + oldSize; iter < path->end(); ++iter) { - assert(iter->Kind == RewriteStep::Shift || - iter->Kind == RewriteStep::Decompose); - } - #endif - - path->resize(oldSize); - } - return false; - } - - // Build the new symbol with simplified substitutions. - symbol = symbol.withConcreteSubstitutions(newSubstitutions, Context); - return true; -} - /// Adds a rewrite rule, returning true if the new rule was non-trivial. /// /// If both sides simplify to the same term, the rule is trivial and discarded, @@ -643,43 +568,6 @@ void RewriteSystem::simplifyRightHandSides() { } } -/// Simplify substitution terms in superclass, concrete type and concrete -/// conformance symbols. -void RewriteSystem::simplifyLeftHandSideSubstitutions() { - for (unsigned ruleID = 0, e = Rules.size(); ruleID < e; ++ruleID) { - auto &rule = getRule(ruleID); - if (rule.isSubstitutionSimplified()) - continue; - - auto lhs = rule.getLHS(); - auto symbol = lhs.back(); - if (!symbol.hasSubstitutions()) - continue; - - RewritePath path; - - // (1) First, apply the original rule to produce the original lhs. - path.add(RewriteStep::forRewriteRule(/*startOffset=*/0, /*endOffset=*/0, - ruleID, /*inverse=*/true)); - - // (2) Now, simplify the substitutions to get the new lhs. - if (!simplifySubstitutions(symbol, &path)) - continue; - - // We're either going to add a new rule or record an identity, so - // mark the old rule as simplified. - rule.markSubstitutionSimplified(); - - MutableTerm newLHS(lhs.begin(), lhs.end() - 1); - newLHS.add(symbol); - - // Invert the path to get a path from the new lhs to the old rhs. - path.invert(); - - addRule(newLHS, MutableTerm(rule.getRHS()), &path); - } -} - /// When minimizing a generic signature, we only care about loops where the /// basepoint is a generic parameter symbol. /// diff --git a/lib/AST/RequirementMachine/SimplifySubstitutions.cpp b/lib/AST/RequirementMachine/SimplifySubstitutions.cpp new file mode 100644 index 00000000000..eabe973f0eb --- /dev/null +++ b/lib/AST/RequirementMachine/SimplifySubstitutions.cpp @@ -0,0 +1,300 @@ +//===--- PropertyUnification.cpp - Rules added w/ building property map ---===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "PropertyMap.h" +#include "RewriteSystem.h" + +using namespace swift; +using namespace rewriting; + +/// Simplify terms appearing in the substitutions of the last symbol of \p term, +/// which must be a superclass or concrete type symbol. +bool RewriteSystem::simplifySubstitutions(Symbol &symbol, + RewritePath *path) const { + assert(symbol.hasSubstitutions()); + + // Fast path if the type is fully concrete. + auto substitutions = symbol.getSubstitutions(); + if (substitutions.empty()) + return false; + + // Save the original rewrite path length so that we can reset if if we don't + // find anything to simplify. + unsigned oldSize = (path ? path->size() : 0); + + if (path) { + // The term is at the top of the primary stack. Push all substitutions onto + // the primary stack. + path->add(RewriteStep::forDecompose(substitutions.size(), + /*inverse=*/false)); + + // Move all substitutions but the first one to the secondary stack. + for (unsigned i = 1; i < substitutions.size(); ++i) + path->add(RewriteStep::forShift(/*inverse=*/false)); + } + + // Simplify and collect substitutions. + SmallVector newSubstitutions; + newSubstitutions.reserve(substitutions.size()); + + bool first = true; + bool anyChanged = false; + for (auto substitution : substitutions) { + // Move the next substitution from the secondary stack to the primary stack. + if (!first && path) + path->add(RewriteStep::forShift(/*inverse=*/true)); + first = false; + + // The current substitution is at the top of the primary stack; simplify it. + MutableTerm mutTerm(substitution); + anyChanged |= simplify(mutTerm, path); + + // Record the new substitution. + newSubstitutions.push_back(Term::get(mutTerm, Context)); + } + + // All simplified substitutions are now on the primary stack. Collect them to + // produce the new term. + if (path) { + path->add(RewriteStep::forDecompose(substitutions.size(), + /*inverse=*/true)); + } + + // If nothing changed, we don't have to rebuild the symbol. + if (!anyChanged) { + if (path) { + // The rewrite path should consist of a Decompose, followed by a number + // of Shifts, followed by a Compose. + #ifndef NDEBUG + for (auto iter = path->begin() + oldSize; iter < path->end(); ++iter) { + assert(iter->Kind == RewriteStep::Shift || + iter->Kind == RewriteStep::Decompose); + } + #endif + + path->resize(oldSize); + } + return false; + } + + // Build the new symbol with simplified substitutions. + symbol = symbol.withConcreteSubstitutions(newSubstitutions, Context); + return true; +} + +/// Simplify substitution terms in superclass, concrete type and concrete +/// conformance symbols. +void RewriteSystem::simplifyLeftHandSideSubstitutions() { + for (unsigned ruleID = 0, e = Rules.size(); ruleID < e; ++ruleID) { + auto &rule = getRule(ruleID); + if (rule.isSubstitutionSimplified()) + continue; + + auto lhs = rule.getLHS(); + auto symbol = lhs.back(); + if (!symbol.hasSubstitutions()) + continue; + + RewritePath path; + + // (1) First, apply the original rule to produce the original lhs. + path.add(RewriteStep::forRewriteRule(/*startOffset=*/0, /*endOffset=*/0, + ruleID, /*inverse=*/true)); + + // (2) Now, simplify the substitutions to get the new lhs. + if (!simplifySubstitutions(symbol, &path)) + continue; + + // We're either going to add a new rule or record an identity, so + // mark the old rule as simplified. + rule.markSubstitutionSimplified(); + + MutableTerm newLHS(lhs.begin(), lhs.end() - 1); + newLHS.add(symbol); + + // Invert the path to get a path from the new lhs to the old rhs. + path.invert(); + + addRule(newLHS, MutableTerm(rule.getRHS()), &path); + } +} + +/// Similar to RewriteSystem::simplifySubstitutions(), but also replaces type +/// parameters with concrete types and builds a type difference describing +/// the transformation. +/// +/// Returns None if the concrete type symbol cannot be simplified further. +/// +/// Otherwise returns an index which can be passed to +/// RewriteSystem::getTypeDifference(). +Optional +PropertyMap::concretelySimplifySubstitutions(Term baseTerm, Symbol symbol, + RewritePath *path) const { + assert(symbol.hasSubstitutions()); + + // Fast path if the type is fully concrete. + auto substitutions = symbol.getSubstitutions(); + if (substitutions.empty()) + return None; + + // Save the original rewrite path length so that we can reset if if we don't + // find anything to simplify. + unsigned oldSize = (path ? path->size() : 0); + + if (path) { + // The term is at the top of the primary stack. Push all substitutions onto + // the primary stack. + path->add(RewriteStep::forDecompose(substitutions.size(), + /*inverse=*/false)); + + // Move all substitutions but the first one to the secondary stack. + for (unsigned i = 1; i < substitutions.size(); ++i) + path->add(RewriteStep::forShift(/*inverse=*/false)); + } + + // Simplify and collect substitutions. + llvm::SmallVector, 1> sameTypes; + llvm::SmallVector, 1> concreteTypes; + + for (unsigned index : indices(substitutions)) { + // Move the next substitution from the secondary stack to the primary stack. + if (index != 0 && path) + path->add(RewriteStep::forShift(/*inverse=*/true)); + + auto term = symbol.getSubstitutions()[index]; + MutableTerm mutTerm(term); + + // Note that it's of course possible that the term both requires + // simplification, and the simplified term has a concrete type. + // + // This isn't handled with our current representation of + // TypeDifference, but that should be fine since the caller + // has to iterate until fixed point anyway. + // + // This should be rare in practice. + if (System.simplify(mutTerm, path)) { + // Record a mapping from this substitution to the simplified term. + sameTypes.emplace_back(index, Term::get(mutTerm, Context)); + } else { + auto *props = lookUpProperties(mutTerm); + + if (props && props->ConcreteType) { + // The property map entry might apply to a suffix of the substitution + // term, so prepend the appropriate prefix to its own substitutions. + auto prefix = props->getPrefixAfterStrippingKey(mutTerm); + auto concreteSymbol = + props->ConcreteType->prependPrefixToConcreteSubstitutions( + prefix, Context); + + // Record a mapping from this substitution to the concrete type. + concreteTypes.emplace_back(index, concreteSymbol); + + // If U.V is the substitution term and V is the property map key, + // apply the rewrite step U.(V => V.[concrete: C]) followed by + // prepending the prefix U to each substitution in the concrete type + // symbol if |U| > 0. + if (path) { + path->add(RewriteStep::forRewriteRule(/*startOffset=*/prefix.size(), + /*endOffset=*/0, + /*ruleID=*/*props->ConcreteTypeRule, + /*inverse=*/true)); + + if (!prefix.empty()) { + path->add(RewriteStep::forPrefixSubstitutions(/*length=*/prefix.size(), + /*endOffset=*/0, + /*inverse=*/false)); + } + } + } + } + } + + // If nothing changed, we don't have to build the type difference. + if (sameTypes.empty() && concreteTypes.empty()) { + if (path) { + // The rewrite path should consist of a Decompose, followed by a number + // of Shifts, followed by a Compose. + #ifndef NDEBUG + for (auto iter = path->begin() + oldSize; iter < path->end(); ++iter) { + assert(iter->Kind == RewriteStep::Shift || + iter->Kind == RewriteStep::Decompose); + } + #endif + + path->resize(oldSize); + } + return None; + } + + auto difference = buildTypeDifference(baseTerm, symbol, + sameTypes, concreteTypes, + Context); + assert(difference.LHS != difference.RHS); + + unsigned differenceID = System.recordTypeDifference(difference); + + // All simplified substitutions are now on the primary stack. Collect them to + // produce the new term. + if (path) { + path->add(RewriteStep::forDecomposeConcrete(differenceID, + /*inverse=*/true)); + } + + return differenceID; +} + +void PropertyMap::concretelySimplifyLeftHandSideSubstitutions() const { + for (unsigned ruleID = 0, e = System.getRules().size(); ruleID < e; ++ruleID) { + auto &rule = System.getRule(ruleID); + if (rule.isLHSSimplified() || + rule.isRHSSimplified() || + rule.isSubstitutionSimplified()) + continue; + + auto optSymbol = rule.isPropertyRule(); + if (!optSymbol || !optSymbol->hasSubstitutions()) + continue; + + auto symbol = *optSymbol; + + RewritePath path; + + auto differenceID = concretelySimplifySubstitutions( + rule.getRHS(), symbol, &path); + if (!differenceID) + continue; + + rule.markSubstitutionSimplified(); + + auto difference = System.getTypeDifference(*differenceID); + assert(difference.LHS == symbol); + + // If the original rule is (T.[concrete: C] => T) and [concrete: C'] is + // the simplified symbol, then difference.LHS == [concrete: C] and + // difference.RHS == [concrete: C'], and the rewrite path we just + // built takes T.[concrete: C] to T.[concrete: C']. + // + // We want a path from T.[concrete: C'] to T, so invert the path to get + // a path from T.[concrete: C'] to T.[concrete: C], and add a final step + // applying the original rule (T.[concrete: C] => T). + path.invert(); + path.add(RewriteStep::forRewriteRule(/*startOffset=*/0, + /*endOffset=*/0, + /*ruleID=*/ruleID, + /*inverted=*/false)); + MutableTerm rhs(rule.getRHS()); + MutableTerm lhs(rhs); + lhs.add(difference.RHS); + + System.addRule(lhs, rhs, &path); + } +} \ No newline at end of file