Skip to content

Commit f810d3f

Browse files
committed
RequirementMachine: Optimize Rewrite{Path,Loop}::computeNormalForm()
1 parent 4df7bf9 commit f810d3f

File tree

2 files changed

+72
-37
lines changed

2 files changed

+72
-37
lines changed

lib/AST/RequirementMachine/NormalizeRewritePath.cpp

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- LeftCanonicalForm.cpp - Left canonical form of a rewrite path ----===//
1+
//===--- NormalizeRewritePath.cpp - Canonical form of a rewrite path ------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
@@ -144,50 +144,91 @@ bool RewriteStep::maybeSwapRewriteSteps(RewriteStep &other,
144144
/// This does not change either endpoint of the path, and the path does
145145
/// not necessarily need to be a loop.
146146
bool RewritePath::computeFreelyReducedForm() {
147-
SmallVector<RewriteStep, 4> newSteps;
148-
bool changed = false;
149-
150-
for (const auto &step : Steps) {
151-
if (!newSteps.empty() &&
152-
newSteps.back().isInverseOf(step)) {
153-
changed = true;
154-
newSteps.pop_back();
155-
continue;
147+
unsigned j = 0;
148+
149+
for (unsigned i = 0, e = Steps.size(); i < e; ++i) {
150+
// Pre-condition:
151+
// - Steps in the range [0, j-1] are freely reduced.
152+
// - Steps in the range [i, e-1] remain to be considered.
153+
if (j > 0) {
154+
// If Steps[j-1] and Steps[i] are inverses of each other,
155+
// discard both Steps[j-1] and Steps[i].
156+
const auto &otherStep = Steps[j - 1];
157+
const auto &step = Steps[i];
158+
159+
if (otherStep.isInverseOf(step)) {
160+
--j;
161+
continue;
162+
}
156163
}
157164

158-
newSteps.push_back(step);
165+
// Post-condition:
166+
// - Steps in the range [0, j] are freely reduced.
167+
// - Steps in the range [i+1, e-1] remain to be considered.
168+
Steps[j] = Steps[i];
169+
++j;
159170
}
160171

161-
if (!changed)
172+
if (j == Steps.size())
162173
return false;
163-
std::swap(newSteps, Steps);
164-
return changed;
174+
175+
Steps.erase(Steps.begin() + j, Steps.end());
176+
return true;
165177
}
166178

167179
/// Apply the interchange rule until fixed point (see maybeSwapRewriteSteps()).
168180
bool RewritePath::computeLeftCanonicalForm(const RewriteSystem &system) {
169181
bool changed = false;
170182

171183
for (unsigned i = 1, e = Steps.size(); i < e; ++i) {
172-
auto &prevStep = Steps[i - 1];
173-
auto &step = Steps[i];
184+
// Pre-condition: steps in the range [0, i-1] are in left-canonical
185+
// normal form.
186+
//
187+
// If Steps[i] can be swapped with Steps[i-1], swap them, and check
188+
// if Steps[i-1] (the old Steps[i]) can be swapped with Steps[i-2],
189+
// etc.
190+
for (unsigned j = i; j >= 1; --j) {
191+
auto &prevStep = Steps[j - 1];
192+
auto &step = Steps[j];
193+
194+
if (!prevStep.maybeSwapRewriteSteps(step, system))
195+
break;
174196

175-
if (prevStep.maybeSwapRewriteSteps(step, system))
176197
changed = true;
198+
}
199+
200+
// Post-condition: steps in the range [0, i] are in left-canonical
201+
// normal form.
177202
}
178203

179204
return changed;
180205
}
181206

182207
/// Compute freely-reduced left-canonical normal form of a path.
183-
void RewritePath::computeNormalForm(const RewriteSystem &system) {
184-
// FIXME: This can be more efficient.
185-
bool changed;
186-
do {
187-
changed = false;
188-
changed |= computeFreelyReducedForm();
189-
changed |= computeLeftCanonicalForm(system);
190-
} while (changed);
208+
bool RewritePath::computeNormalForm(const RewriteSystem &system) {
209+
// Note that computeFreelyReducedForm() and computeLeftCanonicalForm()
210+
// are both idempotent, but their composition is not.
211+
212+
// Begin by freely reducing the path.
213+
bool changed = computeFreelyReducedForm();
214+
215+
// Then, bring it into left canonical form.
216+
while (computeLeftCanonicalForm(system)) {
217+
changed = true;
218+
219+
// If it was not already in left-canonical form, freely reduce it
220+
// again.
221+
if (!computeFreelyReducedForm()) {
222+
// If it was already freely reduced, then we're done, because it
223+
// is freely reduced *and* in left-canonical form.
224+
break;
225+
}
226+
227+
// Otherwise, perform another round, since freely reducing may have
228+
// opened up new opportunities for left-canonicalization.
229+
}
230+
231+
return changed;
191232
}
192233

193234
/// Given a path that is a loop around the given basepoint, cancels out
@@ -227,15 +268,9 @@ bool RewritePath::computeCyclicallyReducedForm(MutableTerm &basepoint,
227268

228269
/// Compute cyclically-reduced left-canonical normal form of a loop.
229270
void RewriteLoop::computeNormalForm(const RewriteSystem &system) {
230-
// FIXME: This can be more efficient.
231-
bool changed;
232-
do {
233-
changed = false;
234-
changed |= Path.computeFreelyReducedForm();
235-
changed |= Path.computeCyclicallyReducedForm(Basepoint, system);
236-
changed |= Path.computeLeftCanonicalForm(system);
237-
238-
if (changed)
239-
markDirty();
240-
} while (changed);
271+
bool changed = Path.computeNormalForm(system);
272+
changed |= Path.computeCyclicallyReducedForm(Basepoint, system);
273+
274+
if (changed)
275+
markDirty();
241276
}

lib/AST/RequirementMachine/RewriteLoop.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ class RewritePath {
421421

422422
bool computeLeftCanonicalForm(const RewriteSystem &system);
423423

424-
void computeNormalForm(const RewriteSystem &system);
424+
bool computeNormalForm(const RewriteSystem &system);
425425

426426
void dump(llvm::raw_ostream &out,
427427
MutableTerm term,

0 commit comments

Comments
 (0)