|
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