Index: pkg/compiler/lib/src/cps_ir/loop_effects.dart |
diff --git a/pkg/compiler/lib/src/cps_ir/loop_effects.dart b/pkg/compiler/lib/src/cps_ir/loop_effects.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9dd39ff78518b5cfe9fc6e2d725f2c49fe7695f2 |
--- /dev/null |
+++ b/pkg/compiler/lib/src/cps_ir/loop_effects.dart |
@@ -0,0 +1,176 @@ |
+library dart2js.cps_ir.loop_effects; |
+ |
+import 'cps_ir_nodes.dart'; |
+import 'loop_hierarchy.dart'; |
+import 'type_mask_system.dart'; |
+import '../universe/side_effects.dart'; |
+import '../elements/elements.dart'; |
+import '../world.dart'; |
+ |
+/// Determines which the [SideEffects] that may occur during each loop in |
+/// a given function, in addition to whether the loop may change the length |
+/// of an indexable object. |
+/// |
+/// TODO(asgerf): Make length a flag on [SideEffects] for better precision and |
+/// so we don't need to special case the length in this class. |
+class LoopSideEffects extends TrampolineRecursiveVisitor { |
+ LoopHierarchy loopHierarchy; |
+ final World world; |
+ final Map<Continuation, List<Continuation>> exitContinuations = {}; |
+ final Map<Continuation, SideEffects> loopSideEffects = {}; |
+ final Set<Continuation> loopsChangingLength = new Set<Continuation>(); |
+ Continuation currentLoopHeader; |
+ SideEffects currentLoopSideEffects = new SideEffects.empty(); |
+ bool currentLoopChangesLength = false; |
+ |
+ LoopSideEffects(FunctionDefinition node, this.world, {this.loopHierarchy}) { |
+ if (loopHierarchy == null) { |
+ loopHierarchy = new LoopHierarchy(node); |
+ } |
+ visit(node); |
+ } |
+ |
+ /// Returns the accumulated effects and dependencies on all paths from the |
+ /// loop entry to any recursive invocation of the loop. |
+ SideEffects getSideEffectsInLoop(Continuation loop) { |
+ return loopSideEffects[loop]; |
+ } |
+ |
+ /// True if the length of an indexable object may change between the loop |
+ /// entry and a recursive invocation of the loop. |
+ bool loopChangesLength(Continuation loop) { |
+ return loopsChangingLength.contains(loop); |
+ } |
+ |
+ @override |
+ Expression traverseContinuation(Continuation cont) { |
+ if (cont.isRecursive) { |
+ SideEffects oldEffects = currentLoopSideEffects; |
+ Continuation oldLoopHeader = currentLoopHeader; |
+ bool oldChangesLength = currentLoopChangesLength; |
+ currentLoopHeader = cont; |
+ loopSideEffects[cont] = currentLoopSideEffects = new SideEffects.empty(); |
+ exitContinuations[cont] = <Continuation>[]; |
+ pushAction(() { |
+ oldEffects.add(currentLoopSideEffects); |
+ if (currentLoopChangesLength) { |
+ loopsChangingLength.add(cont); |
+ } |
+ currentLoopChangesLength = currentLoopChangesLength || oldChangesLength; |
+ currentLoopHeader = oldLoopHeader; |
+ currentLoopSideEffects = oldEffects; |
+ exitContinuations[cont].forEach(push); |
+ }); |
+ } |
+ return cont.body; |
+ } |
+ |
+ @override |
+ Expression traverseLetHandler(LetHandler node) { |
+ enqueueContinuation(node.handler); |
+ return node.body; |
+ } |
+ |
+ @override |
+ Expression traverseLetCont(LetCont node) { |
+ node.continuations.forEach(enqueueContinuation); |
+ return node.body; |
+ } |
+ |
+ void enqueueContinuation(Continuation cont) { |
+ Continuation loop = loopHierarchy.getEnclosingLoop(cont); |
+ if (loop == currentLoopHeader) { |
+ push(cont); |
+ } else { |
+ // Multiple loops can be exited at once. |
+ // Register as an exit from the outermost loop being exited. |
+ Continuation inner = currentLoopHeader; |
+ Continuation outer = loopHierarchy.getEnclosingLoop(currentLoopHeader); |
+ while (outer != loop) { |
+ inner = outer; |
+ outer = loopHierarchy.getEnclosingLoop(outer); |
+ } |
+ exitContinuations[inner].add(cont); |
+ } |
+ } |
+ |
+ void addSideEffects(SideEffects effects) { |
+ currentLoopSideEffects.add(effects); |
+ if (effects.changesIndex()) { |
+ currentLoopChangesLength = true; |
+ } |
+ } |
+ |
+ void addAllSideEffects() { |
+ currentLoopSideEffects.setAllSideEffects(); |
+ currentLoopSideEffects.setDependsOnSomething(); |
+ currentLoopChangesLength = true; |
+ } |
+ |
+ void visitInvokeMethod(InvokeMethod node) { |
+ addSideEffects(world.getSideEffectsOfSelector(node.selector, node.mask)); |
+ } |
+ |
+ void visitInvokeStatic(InvokeStatic node) { |
+ addSideEffects(world.getSideEffectsOfElement(node.target)); |
+ } |
+ |
+ void visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
+ FunctionElement target = node.target; |
+ if (target is ConstructorBodyElement) { |
+ ConstructorBodyElement body = target; |
+ target = body.constructor; |
+ } |
+ addSideEffects(world.getSideEffectsOfElement(target)); |
+ } |
+ |
+ void visitInvokeConstructor(InvokeConstructor node) { |
+ addSideEffects(world.getSideEffectsOfElement(node.target)); |
+ } |
+ |
+ void visitSetStatic(SetStatic node) { |
+ currentLoopSideEffects.setChangesStaticProperty(); |
+ } |
+ |
+ void visitGetStatic(GetStatic node) { |
+ currentLoopSideEffects.setDependsOnStaticPropertyStore(); |
+ } |
+ |
+ void visitGetField(GetField node) { |
+ currentLoopSideEffects.setDependsOnInstancePropertyStore(); |
+ } |
+ |
+ void visitSetField(SetField node) { |
+ currentLoopSideEffects.setChangesInstanceProperty(); |
+ } |
+ |
+ void visitGetIndex(GetIndex node) { |
+ currentLoopSideEffects.setDependsOnIndexStore(); |
+ } |
+ |
+ void visitSetIndex(SetIndex node) { |
+ // Set the change index flag without setting the change length flag. |
+ currentLoopSideEffects.setChangesIndex(); |
+ } |
+ |
+ void visitForeignCode(ForeignCode node) { |
+ addSideEffects(node.nativeBehavior.sideEffects); |
+ } |
+ |
+ void visitGetLazyStatic(GetLazyStatic node) { |
+ // TODO(asgerf): How do we get the side effects of a lazy field initializer? |
+ addAllSideEffects(); |
+ } |
+ |
+ void visitAwait(Await node) { |
+ addAllSideEffects(); |
+ } |
+ |
+ void visitYield(Yield node) { |
+ addAllSideEffects(); |
+ } |
+ |
+ void visitApplyBuiltinMethod(ApplyBuiltinMethod node) { |
+ currentLoopChangesLength = true; // Push and pop. |
+ } |
+} |