|
65 | 65 | using namespace swift;
|
66 | 66 | using namespace rewriting;
|
67 | 67 |
|
68 |
| -/// Recompute various cached values if needed. |
69 |
| -void RewriteLoop::recompute(const RewriteSystem &system) { |
70 |
| - if (!Dirty) |
71 |
| - return; |
72 |
| - Dirty = 0; |
73 |
| - |
74 |
| - Useful = 0; |
75 |
| - ProjectionCount = 0; |
76 |
| - DecomposeCount = 0; |
77 |
| - HasConcreteTypeAliasRule = 0; |
78 |
| - |
79 |
| - RewritePathEvaluator evaluator(Basepoint); |
80 |
| - for (auto step : Path) { |
81 |
| - switch (step.Kind) { |
82 |
| - case RewriteStep::Rule: { |
83 |
| - Useful |= (!step.isInContext() && !evaluator.isInContext()); |
84 |
| - |
85 |
| - const auto &rule = system.getRule(step.getRuleID()); |
86 |
| - if (rule.isDerivedFromConcreteProtocolTypeAliasRule()) |
87 |
| - HasConcreteTypeAliasRule = 1; |
88 |
| - |
89 |
| - break; |
90 |
| - } |
91 |
| - |
92 |
| - case RewriteStep::LeftConcreteProjection: |
93 |
| - ++ProjectionCount; |
94 |
| - break; |
95 |
| - |
96 |
| - case RewriteStep::Decompose: |
97 |
| - ++DecomposeCount; |
98 |
| - break; |
99 |
| - |
100 |
| - case RewriteStep::PrefixSubstitutions: |
101 |
| - case RewriteStep::Shift: |
102 |
| - case RewriteStep::Relation: |
103 |
| - case RewriteStep::DecomposeConcrete: |
104 |
| - case RewriteStep::RightConcreteProjection: |
105 |
| - break; |
106 |
| - } |
107 |
| - |
108 |
| - evaluator.apply(step, system); |
109 |
| - } |
110 |
| - |
111 |
| - RulesInEmptyContext = Path.getRulesInEmptyContext(Basepoint, system); |
112 |
| -} |
113 |
| - |
114 |
| -/// A rewrite rule is redundant if it appears exactly once in a loop |
115 |
| -/// without context. |
116 |
| -ArrayRef<unsigned> |
117 |
| -RewriteLoop::findRulesAppearingOnceInEmptyContext( |
118 |
| - const RewriteSystem &system) const { |
119 |
| - const_cast<RewriteLoop *>(this)->recompute(system); |
120 |
| - return RulesInEmptyContext; |
121 |
| -} |
122 |
| - |
123 |
| -/// The number of LeftConcreteProjection steps, used by the elimination order to |
124 |
| -/// prioritize loops that are not concrete unification projections. |
125 |
| -unsigned RewriteLoop::getProjectionCount( |
126 |
| - const RewriteSystem &system) const { |
127 |
| - const_cast<RewriteLoop *>(this)->recompute(system); |
128 |
| - return ProjectionCount; |
129 |
| -} |
130 |
| - |
131 |
| -/// The number of Decompose steps, used by the elimination order to prioritize |
132 |
| -/// loops that are not concrete simplifications. |
133 |
| -unsigned RewriteLoop::getDecomposeCount( |
134 |
| - const RewriteSystem &system) const { |
135 |
| - const_cast<RewriteLoop *>(this)->recompute(system); |
136 |
| - return DecomposeCount; |
137 |
| -} |
138 |
| - |
139 |
| -/// Returns true if the loop contains at least one concrete protocol typealias rule, |
140 |
| -/// which have the form ([P].A.[concrete: C] => [P].A). |
141 |
| -bool RewriteLoop::hasConcreteTypeAliasRule( |
142 |
| - const RewriteSystem &system) const { |
143 |
| - const_cast<RewriteLoop *>(this)->recompute(system); |
144 |
| - return HasConcreteTypeAliasRule; |
145 |
| -} |
146 |
| - |
147 |
| -/// The number of Decompose steps, used by the elimination order to prioritize |
148 |
| -/// loops that are not concrete simplifications. |
149 |
| -bool RewriteLoop::isUseful( |
150 |
| - const RewriteSystem &system) const { |
151 |
| - const_cast<RewriteLoop *>(this)->recompute(system); |
152 |
| - return Useful; |
153 |
| -} |
154 |
| - |
155 | 68 | /// If a rewrite loop contains an explicit rule in empty context, propagate the
|
156 | 69 | /// explicit bit to all other rules appearing in empty context within the same
|
157 | 70 | /// loop.
|
@@ -268,214 +181,6 @@ void RewriteSystem::processConflicts() {
|
268 | 181 | }
|
269 | 182 | }
|
270 | 183 |
|
271 |
| -/// Given a rewrite rule which appears exactly once in a loop |
272 |
| -/// without context, return a new definition for this rewrite rule. |
273 |
| -/// The new definition is the path obtained by deleting the |
274 |
| -/// rewrite rule from the loop. |
275 |
| -RewritePath RewritePath::splitCycleAtRule(unsigned ruleID) const { |
276 |
| - // A cycle is a path from the basepoint to the basepoint. |
277 |
| - // Somewhere in this path, an application of \p ruleID |
278 |
| - // appears in an empty context. |
279 |
| - |
280 |
| - // First, we split the cycle into two paths: |
281 |
| - // |
282 |
| - // (1) A path from the basepoint to the rule's |
283 |
| - // left hand side, |
284 |
| - RewritePath basepointToLhs; |
285 |
| - // (2) And a path from the rule's right hand side |
286 |
| - // to the basepoint. |
287 |
| - RewritePath rhsToBasepoint; |
288 |
| - |
289 |
| - // Because the rule only appears once, we know that basepointToLhs |
290 |
| - // and rhsToBasepoint do not involve the rule itself. |
291 |
| - |
292 |
| - // If the rule is inverted, we have to invert the whole thing |
293 |
| - // again at the end. |
294 |
| - bool ruleWasInverted = false; |
295 |
| - |
296 |
| - bool sawRule = false; |
297 |
| - |
298 |
| - for (auto step : Steps) { |
299 |
| - switch (step.Kind) { |
300 |
| - case RewriteStep::Rule: { |
301 |
| - if (step.getRuleID() != ruleID) |
302 |
| - break; |
303 |
| - |
304 |
| - assert(!sawRule && "Rule appears more than once?"); |
305 |
| - assert(!step.isInContext() && "Rule appears in context?"); |
306 |
| - |
307 |
| - ruleWasInverted = step.Inverse; |
308 |
| - sawRule = true; |
309 |
| - continue; |
310 |
| - } |
311 |
| - case RewriteStep::PrefixSubstitutions: |
312 |
| - case RewriteStep::Shift: |
313 |
| - case RewriteStep::Decompose: |
314 |
| - case RewriteStep::Relation: |
315 |
| - case RewriteStep::DecomposeConcrete: |
316 |
| - case RewriteStep::LeftConcreteProjection: |
317 |
| - case RewriteStep::RightConcreteProjection: |
318 |
| - break; |
319 |
| - } |
320 |
| - |
321 |
| - if (sawRule) |
322 |
| - rhsToBasepoint.add(step); |
323 |
| - else |
324 |
| - basepointToLhs.add(step); |
325 |
| - } |
326 |
| - |
327 |
| - // Build a path from the rule's lhs to the rule's rhs via the |
328 |
| - // basepoint. |
329 |
| - RewritePath result = rhsToBasepoint; |
330 |
| - result.append(basepointToLhs); |
331 |
| - |
332 |
| - // We want a path from the lhs to the rhs, so invert it unless |
333 |
| - // the rewrite step was also inverted. |
334 |
| - if (!ruleWasInverted) |
335 |
| - result.invert(); |
336 |
| - |
337 |
| - return result; |
338 |
| -} |
339 |
| - |
340 |
| -/// Replace every rewrite step involving the given rewrite rule with |
341 |
| -/// either the replacement path (or its inverse, if the step was |
342 |
| -/// inverted). |
343 |
| -/// |
344 |
| -/// The replacement path is re-contextualized at each occurrence of a |
345 |
| -/// rewrite step involving the given rule. |
346 |
| -/// |
347 |
| -/// Returns true if any rewrite steps were replaced; false means the |
348 |
| -/// rule did not appear in this path. |
349 |
| -bool RewritePath::replaceRuleWithPath(unsigned ruleID, |
350 |
| - const RewritePath &path) { |
351 |
| - bool foundAny = false; |
352 |
| - |
353 |
| - for (const auto &step : Steps) { |
354 |
| - if (step.Kind == RewriteStep::Rule && |
355 |
| - step.getRuleID() == ruleID) { |
356 |
| - foundAny = true; |
357 |
| - break; |
358 |
| - } |
359 |
| - } |
360 |
| - |
361 |
| - if (!foundAny) |
362 |
| - return false; |
363 |
| - |
364 |
| - SmallVector<RewriteStep, 4> newSteps; |
365 |
| - |
366 |
| - for (const auto &step : Steps) { |
367 |
| - switch (step.Kind) { |
368 |
| - case RewriteStep::Rule: { |
369 |
| - // All other rewrite rules remain unchanged. |
370 |
| - if (step.getRuleID() != ruleID) { |
371 |
| - newSteps.push_back(step); |
372 |
| - break; |
373 |
| - } |
374 |
| - |
375 |
| - // Ok, we found a rewrite step referencing the redundant rule. |
376 |
| - // Replace this step with the provided path. If this rewrite step has |
377 |
| - // context, the path's own steps must be re-contextualized. |
378 |
| - |
379 |
| - // Keep track of rewrite step pairs which push and pop the stack. Any |
380 |
| - // rewrite steps enclosed with a push/pop are not re-contextualized. |
381 |
| - unsigned pushCount = 0; |
382 |
| - |
383 |
| - auto recontextualizeStep = [&](RewriteStep newStep) { |
384 |
| - bool inverse = newStep.Inverse ^ step.Inverse; |
385 |
| - |
386 |
| - if (newStep.pushesTermsOnStack() && inverse) { |
387 |
| - assert(pushCount > 0); |
388 |
| - --pushCount; |
389 |
| - } |
390 |
| - |
391 |
| - if (pushCount == 0) { |
392 |
| - newStep.StartOffset += step.StartOffset; |
393 |
| - newStep.EndOffset += step.EndOffset; |
394 |
| - } |
395 |
| - |
396 |
| - newStep.Inverse = inverse; |
397 |
| - newSteps.push_back(newStep); |
398 |
| - |
399 |
| - if (newStep.pushesTermsOnStack() && !inverse) { |
400 |
| - ++pushCount; |
401 |
| - } |
402 |
| - }; |
403 |
| - |
404 |
| - // If this rewrite step is inverted, invert the entire path. |
405 |
| - if (step.Inverse) { |
406 |
| - for (auto newStep : llvm::reverse(path)) |
407 |
| - recontextualizeStep(newStep); |
408 |
| - } else { |
409 |
| - for (auto newStep : path) |
410 |
| - recontextualizeStep(newStep); |
411 |
| - } |
412 |
| - |
413 |
| - // Rewrite steps which push and pop the stack must come in balanced pairs. |
414 |
| - assert(pushCount == 0); |
415 |
| - |
416 |
| - break; |
417 |
| - } |
418 |
| - case RewriteStep::PrefixSubstitutions: |
419 |
| - case RewriteStep::Shift: |
420 |
| - case RewriteStep::Decompose: |
421 |
| - case RewriteStep::Relation: |
422 |
| - case RewriteStep::DecomposeConcrete: |
423 |
| - case RewriteStep::LeftConcreteProjection: |
424 |
| - case RewriteStep::RightConcreteProjection: |
425 |
| - newSteps.push_back(step); |
426 |
| - break; |
427 |
| - } |
428 |
| - } |
429 |
| - |
430 |
| - std::swap(newSteps, Steps); |
431 |
| - return true; |
432 |
| -} |
433 |
| - |
434 |
| -SmallVector<unsigned, 1> |
435 |
| -RewritePath::getRulesInEmptyContext(const MutableTerm &term, |
436 |
| - const RewriteSystem &system) { |
437 |
| - // Rules appearing in empty context (possibly more than once). |
438 |
| - llvm::SmallDenseSet<unsigned, 2> rulesInEmptyContext; |
439 |
| - // The number of times each rule appears (with or without context). |
440 |
| - llvm::SmallDenseMap<unsigned, unsigned, 2> ruleFrequency; |
441 |
| - |
442 |
| - RewritePathEvaluator evaluator(term); |
443 |
| - for (auto step : Steps) { |
444 |
| - switch (step.Kind) { |
445 |
| - case RewriteStep::Rule: { |
446 |
| - if (!step.isInContext() && !evaluator.isInContext()) |
447 |
| - rulesInEmptyContext.insert(step.getRuleID()); |
448 |
| - |
449 |
| - ++ruleFrequency[step.getRuleID()]; |
450 |
| - break; |
451 |
| - } |
452 |
| - |
453 |
| - case RewriteStep::LeftConcreteProjection: |
454 |
| - case RewriteStep::Decompose: |
455 |
| - case RewriteStep::PrefixSubstitutions: |
456 |
| - case RewriteStep::Shift: |
457 |
| - case RewriteStep::Relation: |
458 |
| - case RewriteStep::DecomposeConcrete: |
459 |
| - case RewriteStep::RightConcreteProjection: |
460 |
| - break; |
461 |
| - } |
462 |
| - |
463 |
| - evaluator.apply(step, system); |
464 |
| - } |
465 |
| - |
466 |
| - // Collect all rules that we saw exactly once in empty context. |
467 |
| - SmallVector<unsigned, 1> rulesOnceInEmptyContext; |
468 |
| - for (auto rule : rulesInEmptyContext) { |
469 |
| - auto found = ruleFrequency.find(rule); |
470 |
| - assert(found != ruleFrequency.end()); |
471 |
| - |
472 |
| - if (found->second == 1) |
473 |
| - rulesOnceInEmptyContext.push_back(rule); |
474 |
| - } |
475 |
| - |
476 |
| - return rulesOnceInEmptyContext; |
477 |
| -} |
478 |
| - |
479 | 184 | /// Find a rule to delete by looking through all loops for rewrite rules appearing
|
480 | 185 | /// once in empty context. Returns a pair consisting of a loop ID and a rule ID,
|
481 | 186 | /// otherwise returns None.
|
@@ -680,12 +385,6 @@ findRuleToDelete(EliminationPredicate isRedundantRuleFn) {
|
680 | 385 | /// occurrences of the rule in all loops with the replacement path.
|
681 | 386 | void RewriteSystem::deleteRule(unsigned ruleID,
|
682 | 387 | const RewritePath &replacementPath) {
|
683 |
| - // Replace all occurrences of the rule with the replacement path in |
684 |
| - // all redundant rule paths recorded so far. |
685 |
| - for (auto &pair : RedundantRules) { |
686 |
| - (void) pair.second.replaceRuleWithPath(ruleID, replacementPath); |
687 |
| - } |
688 |
| - |
689 | 388 | // Replace all occurrences of the rule with the replacement path in
|
690 | 389 | // all remaining rewrite loops.
|
691 | 390 | for (unsigned loopID : indices(Loops)) {
|
@@ -753,8 +452,34 @@ void RewriteSystem::performHomotopyReduction(
|
753 | 452 | }
|
754 | 453 |
|
755 | 454 | void RewriteSystem::normalizeRedundantRules() {
|
756 |
| - for (auto &pair : RedundantRules) { |
| 455 | + llvm::DenseMap<unsigned, unsigned> RedundantRuleMap; |
| 456 | + |
| 457 | + // A redundant path in the range [0, i-1] might contain rewrite steps naming |
| 458 | + // rules that subsequently became redundant in the range [i, e-1]. |
| 459 | + // |
| 460 | + // We back-substitute later rules into earlier paths here. |
| 461 | + for (unsigned i = 0, e = RedundantRules.size(); i < e; ++i) { |
| 462 | + // Pre-condition: Redundant paths in the range [i+1, e-1] do not involve |
| 463 | + // any other redundant rules. |
| 464 | + unsigned j = e - i - 1; |
| 465 | + |
| 466 | + // Replace all occurrences of redundant rules with their path at |
| 467 | + // RedundantRules[i]. |
| 468 | + auto &pair = RedundantRules[j]; |
| 469 | + pair.second.replaceRulesWithPaths( |
| 470 | + [&](unsigned ruleID) -> RewritePath * { |
| 471 | + auto found = RedundantRuleMap.find(ruleID); |
| 472 | + if (found != RedundantRuleMap.end()) |
| 473 | + return &RedundantRules[found->second].second; |
| 474 | + |
| 475 | + return nullptr; |
| 476 | + }); |
757 | 477 | pair.second.computeNormalForm(*this);
|
| 478 | + |
| 479 | + RedundantRuleMap[RedundantRules[j].first] = j; |
| 480 | + |
| 481 | + // Post-condition: the path for RedundantRules[i] does not contain any |
| 482 | + // redundant rules. |
758 | 483 | }
|
759 | 484 |
|
760 | 485 | if (Debug.contains(DebugFlags::RedundantRules)) {
|
@@ -911,12 +636,12 @@ void RewriteSystem::minimizeRewriteSystem() {
|
911 | 636 | return false;
|
912 | 637 | });
|
913 | 638 |
|
| 639 | + normalizeRedundantRules(); |
| 640 | + |
914 | 641 | // Check invariants after homotopy reduction.
|
915 | 642 | verifyRewriteLoops();
|
916 | 643 | verifyRedundantConformances(redundantConformances);
|
917 | 644 | verifyMinimizedRules(redundantConformances);
|
918 |
| - |
919 |
| - normalizeRedundantRules(); |
920 | 645 | }
|
921 | 646 |
|
922 | 647 | /// Returns flags indicating if the rewrite system has unresolved or
|
@@ -1121,5 +846,17 @@ void RewriteSystem::verifyMinimizedRules(
|
1121 | 846 | dump(llvm::errs());
|
1122 | 847 | abort();
|
1123 | 848 | }
|
| 849 | + |
| 850 | + for (const auto &step : pair.second) { |
| 851 | + if (step.Kind == RewriteStep::Rule) { |
| 852 | + const auto &rule = getRule(step.getRuleID()); |
| 853 | + if (rule.isRedundant()) { |
| 854 | + llvm::errs() << "Redundant requirement path contains a redundant " |
| 855 | + "rule " << rule << "\n"; |
| 856 | + dump(llvm::errs()); |
| 857 | + abort(); |
| 858 | + } |
| 859 | + } |
| 860 | + } |
1124 | 861 | }
|
1125 | 862 | }
|
0 commit comments