Skip to content

Commit 67fad89

Browse files
committed
RequirementMachine: Compute left canonical forms of paths
1 parent 5f03c73 commit 67fad89

File tree

3 files changed

+124
-13
lines changed

3 files changed

+124
-13
lines changed

lib/AST/RequirementMachine/Debug.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#ifndef SWIFT_RQM_DEBUG_H
1414
#define SWIFT_RQM_DEBUG_H
1515

16+
#include "swift/Basic/OptionSet.h"
17+
1618
namespace swift {
1719

1820
namespace rewriting {

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 114 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "swift/Basic/Range.h"
1314
#include "llvm/ADT/DenseMap.h"
1415
#include "llvm/ADT/DenseSet.h"
16+
#include "llvm/Support/Debug.h"
1517
#include "llvm/Support/raw_ostream.h"
1618
#include <algorithm>
1719
#include "RewriteSystem.h"
@@ -310,7 +312,7 @@ bool RewritePath::replaceRuleWithPath(unsigned ruleID,
310312

311313
/// Returns true if this rewrite step is an inverse of \p other
312314
/// (and vice versa).
313-
bool RewriteStep::checkCancellation(const RewriteStep &other) const {
315+
bool RewriteStep::isInverseOf(const RewriteStep &other) const {
314316
if (Kind != other.Kind)
315317
return false;
316318

@@ -328,15 +330,92 @@ bool RewriteStep::checkCancellation(const RewriteStep &other) const {
328330
return true;
329331
};
330332

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+
331408
/// Cancels out adjacent rewrite steps that are inverses of each other.
332409
/// This does not change either endpoint of the path, and the path does
333410
/// not necessarily need to be a loop.
334-
void RewritePath::computeFreelyReducedPath() {
411+
bool RewritePath::computeFreelyReducedPath() {
335412
SmallVector<RewriteStep, 4> newSteps;
413+
bool changed = false;
336414

337415
for (const auto &step : Steps) {
338416
if (!newSteps.empty() &&
339-
newSteps.back().checkCancellation(step)) {
417+
newSteps.back().isInverseOf(step)) {
418+
changed = true;
340419
newSteps.pop_back();
341420
continue;
342421
}
@@ -345,6 +424,7 @@ void RewritePath::computeFreelyReducedPath() {
345424
}
346425

347426
std::swap(newSteps, Steps);
427+
return changed;
348428
}
349429

350430
/// Given a path that is a loop around the given basepoint, cancels out
@@ -358,14 +438,14 @@ void RewritePath::computeFreelyReducedPath() {
358438
/// The first step is the inverse of the last step, so the cyclic
359439
/// reduction is the 3-cell (Y.A => Y.B) * Y.(B => A), with a new
360440
/// basepoint 'Y.A'.
361-
void RewritePath::computeCyclicallyReducedLoop(MutableTerm &basepoint,
441+
bool RewritePath::computeCyclicallyReducedLoop(MutableTerm &basepoint,
362442
const RewriteSystem &system) {
363443
unsigned count = 0;
364444

365445
while (2 * count + 1 < size()) {
366446
auto left = Steps[count];
367447
auto right = Steps[Steps.size() - count - 1];
368-
if (!left.checkCancellation(right))
448+
if (!left.isInverseOf(right))
369449
break;
370450

371451
// Update the basepoint by applying the first step in the path.
@@ -376,6 +456,22 @@ void RewritePath::computeCyclicallyReducedLoop(MutableTerm &basepoint,
376456

377457
std::rotate(Steps.begin(), Steps.begin() + count, Steps.end() - count);
378458
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;
379475
}
380476

381477
/// Dumps a series of rewrite steps applied to \p term.
@@ -465,12 +561,20 @@ void RewriteSystem::minimizeRewriteSystem() {
465561

466562
if (changed) {
467563
unsigned size = loop.second.size();
468-
loop.second.computeFreelyReducedPath();
469-
loop.second.computeCyclicallyReducedLoop(loop.first, *this);
470564

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+
}
474578
}
475579

476580
if (Debug.contains(DebugFlags::HomotopyReduction)) {

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,10 @@ struct RewriteStep {
157157

158158
void apply(MutableTerm &term, const RewriteSystem &system) const;
159159

160-
bool checkCancellation(const RewriteStep &other) const;
160+
bool isInverseOf(const RewriteStep &other) const;
161+
162+
bool maybeSwapRewriteSteps(RewriteStep &other,
163+
const RewriteSystem &system);
161164

162165
void dump(llvm::raw_ostream &out,
163166
MutableTerm &term,
@@ -199,11 +202,13 @@ struct RewritePath {
199202

200203
bool replaceRuleWithPath(unsigned ruleID, const RewritePath &path);
201204

202-
void computeFreelyReducedPath();
205+
bool computeFreelyReducedPath();
203206

204-
void computeCyclicallyReducedLoop(MutableTerm &basepoint,
207+
bool computeCyclicallyReducedLoop(MutableTerm &basepoint,
205208
const RewriteSystem &system);
206209

210+
bool computeLeftCanonicalForm(const RewriteSystem &system);
211+
207212
void invert();
208213

209214
void dump(llvm::raw_ostream &out,

0 commit comments

Comments
 (0)