10
10
//
11
11
// ===----------------------------------------------------------------------===//
12
12
13
+ #include " swift/Basic/Range.h"
13
14
#include " llvm/ADT/DenseMap.h"
14
15
#include " llvm/ADT/DenseSet.h"
16
+ #include " llvm/Support/Debug.h"
15
17
#include " llvm/Support/raw_ostream.h"
16
18
#include < algorithm>
17
19
#include " RewriteSystem.h"
@@ -310,7 +312,7 @@ bool RewritePath::replaceRuleWithPath(unsigned ruleID,
310
312
311
313
// / Returns true if this rewrite step is an inverse of \p other
312
314
// / (and vice versa).
313
- bool RewriteStep::checkCancellation (const RewriteStep &other) const {
315
+ bool RewriteStep::isInverseOf (const RewriteStep &other) const {
314
316
if (Kind != other.Kind )
315
317
return false ;
316
318
@@ -328,15 +330,92 @@ bool RewriteStep::checkCancellation(const RewriteStep &other) const {
328
330
return true ;
329
331
};
330
332
333
+ bool RewriteStep::maybeSwapRewriteSteps (RewriteStep &other,
334
+ const RewriteSystem &system) {
335
+ if (Kind != RewriteStep::ApplyRewriteRule ||
336
+ other.Kind != RewriteStep::ApplyRewriteRule)
337
+ return false ;
338
+
339
+ // Two rewrite steps are _orthogonal_ if they rewrite disjoint subterms
340
+ // in context. Orthogonal rewrite steps commute, so we can canonicalize
341
+ // a path by placing the left-most step first.
342
+ //
343
+ // Eg, A.U.B.(X => Y).C ⊗ A.(U => V).B.Y == A.(U => V).B.X ⊗ A.V.B.(X => Y).
344
+ //
345
+ // Or, in diagram form. We want to turn this:
346
+ //
347
+ // ----- time ----->
348
+ // +---------+---------+
349
+ // | A | A |
350
+ // +---------+---------+
351
+ // | U | U ==> V |
352
+ // +---------+---------+
353
+ // | B | B |
354
+ // +---------+---------+
355
+ // | X ==> Y | Y |
356
+ // +---------+---------+
357
+ // | C | C |
358
+ // +---------+---------+
359
+ //
360
+ // Into this:
361
+ //
362
+ // +---------+---------+
363
+ // | A | A |
364
+ // +---------+---------+
365
+ // | U ==> V | V |
366
+ // +---------+---------+
367
+ // | B | B |
368
+ // +---------+---------+
369
+ // | X | X ==> Y |
370
+ // +---------+---------+
371
+ // | C | C |
372
+ // +---------+---------+
373
+ //
374
+ // Note that
375
+ //
376
+ // StartOffset == |A|+|U|+|B|
377
+ // EndOffset = |C|
378
+ //
379
+ // other.StartOffset = |A|
380
+ // other.EndOffset = |B|+|Y|+|C|
381
+ //
382
+ // After interchange, we adjust:
383
+ //
384
+ // StartOffset = |A|
385
+ // EndOffset = |B|+|X|+|C|
386
+ //
387
+ // other.StartOffset = |A|+|V|+|B|
388
+ // other.EndOffset = |C|
389
+
390
+ const auto &rule = system.getRule (RuleID);
391
+ auto lhs = (Inverse ? rule.getRHS () : rule.getLHS ());
392
+ auto rhs = (Inverse ? rule.getLHS () : rule.getRHS ());
393
+
394
+ const auto &otherRule = system.getRule (other.RuleID );
395
+ auto otherLHS = (other.Inverse ? otherRule.getRHS () : otherRule.getLHS ());
396
+ auto otherRHS = (other.Inverse ? otherRule.getLHS () : otherRule.getRHS ());
397
+
398
+ if (StartOffset < other.StartOffset + otherLHS.size ())
399
+ return false ;
400
+
401
+ std::swap (*this , other);
402
+ EndOffset += (lhs.size () - rhs.size ());
403
+ other.StartOffset += (otherRHS.size () - otherLHS.size ());
404
+
405
+ return true ;
406
+ }
407
+
331
408
// / Cancels out adjacent rewrite steps that are inverses of each other.
332
409
// / This does not change either endpoint of the path, and the path does
333
410
// / not necessarily need to be a loop.
334
- void RewritePath::computeFreelyReducedPath () {
411
+ bool RewritePath::computeFreelyReducedPath () {
335
412
SmallVector<RewriteStep, 4 > newSteps;
413
+ bool changed = false ;
336
414
337
415
for (const auto &step : Steps) {
338
416
if (!newSteps.empty () &&
339
- newSteps.back ().checkCancellation (step)) {
417
+ newSteps.back ().isInverseOf (step)) {
418
+ changed = true ;
340
419
newSteps.pop_back ();
341
420
continue ;
342
421
}
@@ -345,6 +424,7 @@ void RewritePath::computeFreelyReducedPath() {
345
424
}
346
425
347
426
std::swap (newSteps, Steps);
427
+ return changed;
348
428
}
349
429
350
430
// / Given a path that is a loop around the given basepoint, cancels out
@@ -358,14 +438,14 @@ void RewritePath::computeFreelyReducedPath() {
358
438
// / The first step is the inverse of the last step, so the cyclic
359
439
// / reduction is the 3-cell (Y.A => Y.B) * Y.(B => A), with a new
360
440
// / basepoint 'Y.A'.
361
- void RewritePath::computeCyclicallyReducedLoop (MutableTerm &basepoint,
441
+ bool RewritePath::computeCyclicallyReducedLoop (MutableTerm &basepoint,
362
442
const RewriteSystem &system) {
363
443
unsigned count = 0 ;
364
444
365
445
while (2 * count + 1 < size ()) {
366
446
auto left = Steps[count];
367
447
auto right = Steps[Steps.size () - count - 1 ];
368
- if (!left.checkCancellation (right))
448
+ if (!left.isInverseOf (right))
369
449
break ;
370
450
371
451
// Update the basepoint by applying the first step in the path.
@@ -376,6 +456,22 @@ void RewritePath::computeCyclicallyReducedLoop(MutableTerm &basepoint,
376
456
377
457
std::rotate (Steps.begin (), Steps.begin () + count, Steps.end () - count);
378
458
Steps.erase (Steps.end () - 2 * count, Steps.end ());
459
+
460
+ return count > 0 ;
461
+ }
462
+
463
+ bool RewritePath::computeLeftCanonicalForm (const RewriteSystem &system) {
464
+ bool changed = false ;
465
+
466
+ for (unsigned i = 1 , e = Steps.size (); i < e; ++i) {
467
+ auto &prevStep = Steps[i - 1 ];
468
+ auto &step = Steps[i];
469
+
470
+ if (prevStep.maybeSwapRewriteSteps (step, system))
471
+ changed = true ;
472
+ }
473
+
474
+ return changed;
379
475
}
380
476
381
477
// / Dumps a series of rewrite steps applied to \p term.
@@ -465,12 +561,20 @@ void RewriteSystem::minimizeRewriteSystem() {
465
561
466
562
if (changed) {
467
563
unsigned size = loop.second .size ();
468
- loop.second .computeFreelyReducedPath ();
469
- loop.second .computeCyclicallyReducedLoop (loop.first , *this );
470
564
471
- if (size != loop.second .size ()) {
472
- llvm::dbgs () << " ** Note: Cyclically reduced the loop to eliminate "
473
- << (size - loop.second .size ()) << " steps\n " ;
565
+ bool changed;
566
+ do {
567
+ changed = false ;
568
+ changed |= loop.second .computeFreelyReducedPath ();
569
+ changed |= loop.second .computeCyclicallyReducedLoop (loop.first , *this );
570
+ changed |= loop.second .computeLeftCanonicalForm (*this );
571
+ } while (changed);
572
+
573
+ if (Debug.contains (DebugFlags::HomotopyReduction)) {
574
+ if (size != loop.second .size ()) {
575
+ llvm::dbgs () << " ** Note: Cyclically reduced the loop to eliminate "
576
+ << (size - loop.second .size ()) << " steps\n " ;
577
+ }
474
578
}
475
579
476
580
if (Debug.contains (DebugFlags::HomotopyReduction)) {
0 commit comments