Skip to content

Commit 1bf6102

Browse files
committed
RequirementMachine: Don't bother normalizing loops
1 parent 0e2fd5a commit 1bf6102

File tree

2 files changed

+1
-284
lines changed

2 files changed

+1
-284
lines changed

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 1 addition & 275 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,14 @@
3030
//
3131
// Any occurrence of the rule in the remaining loops is replaced with the
3232
// alternate definition obtained by splitting the loop that witnessed the
33-
// redundancy. After substitution, every loop is normalized to a cyclically
34-
// reduced left-canonical form. The loop witnessing the redundancy normalizes
35-
// to the empty loop and is deleted.
33+
// redundancy.
3634
//
3735
// Iterating this process eventually produces a minimal set of rewrite rules.
3836
//
3937
// For a description of the general algorithm, see "A Homotopical Completion
4038
// Procedure with Applications to Coherence of Monoids",
4139
// https://hal.inria.fr/hal-00818253.
4240
//
43-
// The idea of computing a left-canonical form for rewrite loops is from
44-
// "Homotopy reduction systems for monoid presentations",
45-
// https://www.sciencedirect.com/science/article/pii/S0022404997000959
46-
//
4741
// Note that in the world of Swift, rewrite rules for introducing associated
4842
// type symbols are marked 'permanent'; they are always re-added when a new
4943
// rewrite system is built from a minimal generic signature, so instead of
@@ -321,232 +315,6 @@ bool RewritePath::replaceRuleWithPath(unsigned ruleID,
321315
return true;
322316
}
323317

324-
/// Returns true if this rewrite step is an inverse of \p other
325-
/// (and vice versa).
326-
bool RewriteStep::isInverseOf(const RewriteStep &other) const {
327-
if (Kind != other.Kind)
328-
return false;
329-
330-
if (StartOffset != other.StartOffset)
331-
return false;
332-
333-
if (Inverse != !other.Inverse)
334-
return false;
335-
336-
switch (Kind) {
337-
case RewriteStep::ApplyRewriteRule:
338-
return RuleID == other.RuleID;
339-
340-
case RewriteStep::AdjustConcreteType:
341-
return true;
342-
343-
case RewriteStep::Shift:
344-
return true;
345-
346-
case RewriteStep::Decompose:
347-
return RuleID == other.RuleID;
348-
349-
case RewriteStep::ConcreteConformance:
350-
case RewriteStep::SuperclassConformance:
351-
return true;
352-
}
353-
354-
assert(EndOffset == other.EndOffset && "Bad whiskering?");
355-
return true;
356-
}
357-
358-
bool RewriteStep::maybeSwapRewriteSteps(RewriteStep &other,
359-
const RewriteSystem &system) {
360-
if (Kind != RewriteStep::ApplyRewriteRule ||
361-
other.Kind != RewriteStep::ApplyRewriteRule)
362-
return false;
363-
364-
// Two rewrite steps are _orthogonal_ if they rewrite disjoint subterms
365-
// in context. Orthogonal rewrite steps commute, so we can canonicalize
366-
// a path by placing the left-most step first.
367-
//
368-
// Eg, A.U.B.(X => Y).C ⊗ A.(U => V).B.Y == A.(U => V).B.X ⊗ A.V.B.(X => Y).
369-
//
370-
// Or, in diagram form. We want to turn this:
371-
//
372-
// ----- time ----->
373-
// +---------+---------+
374-
// | A | A |
375-
// +---------+---------+
376-
// | U | U ==> V |
377-
// +---------+---------+
378-
// | B | B |
379-
// +---------+---------+
380-
// | X ==> Y | Y |
381-
// +---------+---------+
382-
// | C | C |
383-
// +---------+---------+
384-
//
385-
// Into this:
386-
//
387-
// +---------+---------+
388-
// | A | A |
389-
// +---------+---------+
390-
// | U ==> V | V |
391-
// +---------+---------+
392-
// | B | B |
393-
// +---------+---------+
394-
// | X | X ==> Y |
395-
// +---------+---------+
396-
// | C | C |
397-
// +---------+---------+
398-
//
399-
// Note that
400-
//
401-
// StartOffset == |A|+|U|+|B|
402-
// EndOffset = |C|
403-
//
404-
// other.StartOffset = |A|
405-
// other.EndOffset = |B|+|Y|+|C|
406-
//
407-
// After interchange, we adjust:
408-
//
409-
// StartOffset = |A|
410-
// EndOffset = |B|+|X|+|C|
411-
//
412-
// other.StartOffset = |A|+|V|+|B|
413-
// other.EndOffset = |C|
414-
415-
const auto &rule = system.getRule(RuleID);
416-
auto lhs = (Inverse ? rule.getRHS() : rule.getLHS());
417-
auto rhs = (Inverse ? rule.getLHS() : rule.getRHS());
418-
419-
const auto &otherRule = system.getRule(other.RuleID);
420-
auto otherLHS = (other.Inverse ? otherRule.getRHS() : otherRule.getLHS());
421-
auto otherRHS = (other.Inverse ? otherRule.getLHS() : otherRule.getRHS());
422-
423-
if (StartOffset < other.StartOffset + otherLHS.size())
424-
return false;
425-
426-
std::swap(*this, other);
427-
EndOffset += (lhs.size() - rhs.size());
428-
other.StartOffset += (otherRHS.size() - otherLHS.size());
429-
430-
return true;
431-
}
432-
433-
/// Cancels out adjacent rewrite steps that are inverses of each other.
434-
/// This does not change either endpoint of the path, and the path does
435-
/// not necessarily need to be a loop.
436-
bool RewritePath::computeFreelyReducedPath() {
437-
SmallVector<RewriteStep, 4> newSteps;
438-
bool changed = false;
439-
440-
for (const auto &step : Steps) {
441-
if (!newSteps.empty() &&
442-
newSteps.back().isInverseOf(step)) {
443-
changed = true;
444-
newSteps.pop_back();
445-
continue;
446-
}
447-
448-
newSteps.push_back(step);
449-
}
450-
451-
std::swap(newSteps, Steps);
452-
return changed;
453-
}
454-
455-
/// Given a path that is a loop around the given basepoint, cancels out
456-
/// pairs of terms from the ends that are inverses of each other, applying
457-
/// the corresponding translation to the basepoint.
458-
///
459-
/// For example, consider this loop with basepoint 'X':
460-
///
461-
/// (X => Y.A) * (Y.A => Y.B) * Y.(B => A) * (Y.A => X)
462-
///
463-
/// The first step is the inverse of the last step, so the cyclic
464-
/// reduction is the loop (Y.A => Y.B) * Y.(B => A), with a new
465-
/// basepoint 'Y.A'.
466-
bool RewritePath::computeCyclicallyReducedLoop(MutableTerm &basepoint,
467-
const RewriteSystem &system) {
468-
RewritePathEvaluator evaluator(basepoint);
469-
unsigned count = 0;
470-
471-
while (2 * count + 1 < size()) {
472-
auto left = Steps[count];
473-
auto right = Steps[Steps.size() - count - 1];
474-
if (!left.isInverseOf(right))
475-
break;
476-
477-
// Update the basepoint by applying the first step in the path.
478-
evaluator.apply(left, system);
479-
480-
++count;
481-
}
482-
483-
std::rotate(Steps.begin(), Steps.begin() + count, Steps.end() - count);
484-
Steps.erase(Steps.end() - 2 * count, Steps.end());
485-
486-
basepoint = evaluator.getCurrentTerm();
487-
return count > 0;
488-
}
489-
490-
/// Apply the interchange rule until fixed point (see maybeSwapRewriteSteps()).
491-
bool RewritePath::computeLeftCanonicalForm(const RewriteSystem &system) {
492-
bool changed = false;
493-
494-
for (unsigned i = 1, e = Steps.size(); i < e; ++i) {
495-
auto &prevStep = Steps[i - 1];
496-
auto &step = Steps[i];
497-
498-
if (prevStep.maybeSwapRewriteSteps(step, system))
499-
changed = true;
500-
}
501-
502-
return changed;
503-
}
504-
505-
/// Compute cyclically-reduced left-canonical normal form of a loop.
506-
void RewriteLoop::normalize(const RewriteSystem &system) {
507-
// FIXME: This can be more efficient.
508-
bool changed;
509-
do {
510-
changed = false;
511-
changed |= Path.computeFreelyReducedPath();
512-
changed |= Path.computeCyclicallyReducedLoop(Basepoint, system);
513-
changed |= Path.computeLeftCanonicalForm(system);
514-
} while (changed);
515-
}
516-
517-
/// A loop is "in context" if every rewrite step has a left or right whisker.
518-
bool RewriteLoop::isInContext(const RewriteSystem &system) const {
519-
RewritePathEvaluator evaluator(Basepoint);
520-
521-
unsigned minStartOffset = (unsigned) -1;
522-
unsigned minEndOffset = (unsigned) -1;
523-
524-
for (const auto &step : Path) {
525-
if (!evaluator.isInContext()) {
526-
switch (step.Kind) {
527-
case RewriteStep::ApplyRewriteRule:
528-
minStartOffset = std::min(minStartOffset, step.StartOffset);
529-
minEndOffset = std::min(minEndOffset, step.EndOffset);
530-
break;
531-
532-
case RewriteStep::AdjustConcreteType:
533-
case RewriteStep::Shift:
534-
case RewriteStep::Decompose:
535-
case RewriteStep::ConcreteConformance:
536-
case RewriteStep::SuperclassConformance:
537-
break;
538-
}
539-
540-
if (minStartOffset == 0 && minEndOffset == 0)
541-
break;
542-
}
543-
544-
evaluator.apply(step, system);
545-
}
546-
547-
return (minStartOffset > 0 || minEndOffset > 0);
548-
}
549-
550318
/// Check if a rewrite rule is a candidate for deletion in this pass of the
551319
/// minimization algorithm.
552320
bool RewriteSystem::
@@ -688,39 +456,6 @@ void RewriteSystem::deleteRule(unsigned ruleID,
688456
if (!changed)
689457
continue;
690458

691-
unsigned size = loop.Path.size();
692-
693-
loop.normalize(*this);
694-
695-
if (Debug.contains(DebugFlags::HomotopyReduction)) {
696-
if (size != loop.Path.size()) {
697-
llvm::dbgs() << "** Note: loop normalization eliminated "
698-
<< (size - loop.Path.size()) << " steps\n";
699-
}
700-
}
701-
702-
if (loop.Path.empty()) {
703-
if (Debug.contains(DebugFlags::HomotopyReduction)) {
704-
llvm::dbgs() << "** Deleting trivial loop at basepoint ";
705-
llvm::dbgs() << loop.Basepoint << "\n";
706-
}
707-
708-
loop.markDeleted();
709-
continue;
710-
}
711-
712-
// FIXME: Is this correct?
713-
if (loop.isInContext(*this)) {
714-
if (Debug.contains(DebugFlags::HomotopyReduction)) {
715-
llvm::dbgs() << "** Deleting loop in context: ";
716-
loop.dump(llvm::dbgs(), *this);
717-
llvm::dbgs() << "\n";
718-
}
719-
720-
loop.markDeleted();
721-
continue;
722-
}
723-
724459
if (Debug.contains(DebugFlags::HomotopyReduction)) {
725460
llvm::dbgs() << "** Updated loop: ";
726461
loop.dump(llvm::dbgs(), *this);
@@ -754,15 +489,6 @@ void RewriteSystem::minimizeRewriteSystem() {
754489
assert(!Minimized);
755490
Minimized = 1;
756491

757-
/// Begin by normalizing all loops to cyclically-reduced left-canonical
758-
/// form.
759-
for (auto &loop : Loops) {
760-
if (loop.isDeleted())
761-
continue;
762-
763-
loop.normalize(*this);
764-
}
765-
766492
// Check invariants before homotopy reduction.
767493
verifyRewriteLoops();
768494

lib/AST/RequirementMachine/RewriteLoop.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,6 @@ class RewritePath {
221221

222222
bool replaceRuleWithPath(unsigned ruleID, const RewritePath &path);
223223

224-
bool computeFreelyReducedPath();
225-
226-
bool computeCyclicallyReducedLoop(MutableTerm &basepoint,
227-
const RewriteSystem &system);
228-
229-
bool computeLeftCanonicalForm(const RewriteSystem &system);
230-
231224
void invert();
232225

233226
void dump(llvm::raw_ostream &out,
@@ -266,8 +259,6 @@ class RewriteLoop {
266259
Deleted = true;
267260
}
268261

269-
void normalize(const RewriteSystem &system);
270-
271262
bool isInContext(const RewriteSystem &system) const;
272263

273264
llvm::SmallVector<unsigned, 1>

0 commit comments

Comments
 (0)