Index: pkg/compiler/lib/src/ssa/jump_handler.dart |
diff --git a/pkg/compiler/lib/src/ssa/jump_handler.dart b/pkg/compiler/lib/src/ssa/jump_handler.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a50d49662f9877a5e44dca617e6580b04ca45bff |
--- /dev/null |
+++ b/pkg/compiler/lib/src/ssa/jump_handler.dart |
@@ -0,0 +1,234 @@ |
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+import '../common.dart'; |
+import '../elements/elements.dart'; |
+import '../tree/tree.dart' as ast; |
+ |
+import 'builder.dart'; |
+import 'graph_builder.dart'; |
+import 'locals_handler.dart'; |
+import 'nodes.dart'; |
+ |
+/// A single break/continue instruction. |
+class JumpHandlerEntry { |
+ final HJump jumpInstruction; |
+ final LocalsHandler locals; |
+ bool isBreak() => jumpInstruction is HBreak; |
+ bool isContinue() => jumpInstruction is HContinue; |
+ JumpHandlerEntry(this.jumpInstruction, this.locals); |
+} |
+ |
+abstract class JumpHandler { |
+ factory JumpHandler(GraphBuilder builder, JumpTarget target) { |
+ return new TargetJumpHandler(builder, target); |
+ } |
+ void generateBreak([LabelDefinition label]); |
+ void generateContinue([LabelDefinition label]); |
+ void forEachBreak(void action(HBreak instruction, LocalsHandler locals)); |
+ void forEachContinue( |
+ void action(HContinue instruction, LocalsHandler locals)); |
+ bool hasAnyContinue(); |
+ bool hasAnyBreak(); |
+ void close(); |
+ final JumpTarget target; |
+ List<LabelDefinition> labels(); |
+} |
+ |
+/// Jump handler used to avoid null checks when a target isn't used as the |
+/// target of a break, and therefore doesn't need a break handler associated |
+/// with it. |
+class NullJumpHandler implements JumpHandler { |
+ final DiagnosticReporter reporter; |
+ |
+ NullJumpHandler(this.reporter); |
+ |
+ void generateBreak([LabelDefinition label]) { |
+ reporter.internalError(CURRENT_ELEMENT_SPANNABLE, |
+ 'NullJumpHandler.generateBreak should not be called.'); |
+ } |
+ |
+ void generateContinue([LabelDefinition label]) { |
+ reporter.internalError(CURRENT_ELEMENT_SPANNABLE, |
+ 'NullJumpHandler.generateContinue should not be called.'); |
+ } |
+ |
+ void forEachBreak(Function ignored) {} |
+ void forEachContinue(Function ignored) {} |
+ void close() {} |
+ bool hasAnyContinue() => false; |
+ bool hasAnyBreak() => false; |
+ |
+ List<LabelDefinition> labels() => const <LabelDefinition>[]; |
+ JumpTarget get target => null; |
+} |
+ |
+/// Jump handler that records breaks until a target block is available. |
+/// |
+/// Breaks are always forward jumps. Continues in loops are implemented as |
+/// breaks of the body. Continues in switches is currently not handled. |
+class TargetJumpHandler implements JumpHandler { |
+ final GraphBuilder builder; |
+ final JumpTarget target; |
+ final List<JumpHandlerEntry> jumps; |
+ |
+ TargetJumpHandler(GraphBuilder builder, this.target) |
+ : this.builder = builder, |
+ jumps = <JumpHandlerEntry>[] { |
+ assert(builder.jumpTargets[target] == null); |
+ builder.jumpTargets[target] = this; |
+ } |
+ |
+ void generateBreak([LabelDefinition label]) { |
+ HInstruction breakInstruction; |
+ if (label == null) { |
+ breakInstruction = new HBreak(target); |
+ } else { |
+ breakInstruction = new HBreak.toLabel(label); |
+ } |
+ LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); |
+ builder.close(breakInstruction); |
+ jumps.add(new JumpHandlerEntry(breakInstruction, locals)); |
+ } |
+ |
+ void generateContinue([LabelDefinition label]) { |
+ HInstruction continueInstruction; |
+ if (label == null) { |
+ continueInstruction = new HContinue(target); |
+ } else { |
+ continueInstruction = new HContinue.toLabel(label); |
+ // Switch case continue statements must be handled by the |
+ // [SwitchCaseJumpHandler]. |
+ assert(label.target.statement is! ast.SwitchCase); |
+ } |
+ LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); |
+ builder.close(continueInstruction); |
+ jumps.add(new JumpHandlerEntry(continueInstruction, locals)); |
+ } |
+ |
+ void forEachBreak(Function action) { |
+ for (JumpHandlerEntry entry in jumps) { |
+ if (entry.isBreak()) action(entry.jumpInstruction, entry.locals); |
+ } |
+ } |
+ |
+ void forEachContinue(Function action) { |
+ for (JumpHandlerEntry entry in jumps) { |
+ if (entry.isContinue()) action(entry.jumpInstruction, entry.locals); |
+ } |
+ } |
+ |
+ bool hasAnyContinue() { |
+ for (JumpHandlerEntry entry in jumps) { |
+ if (entry.isContinue()) return true; |
+ } |
+ return false; |
+ } |
+ |
+ bool hasAnyBreak() { |
+ for (JumpHandlerEntry entry in jumps) { |
+ if (entry.isBreak()) return true; |
+ } |
+ return false; |
+ } |
+ |
+ void close() { |
+ // The mapping from TargetElement to JumpHandler is no longer needed. |
+ builder.jumpTargets.remove(target); |
+ } |
+ |
+ List<LabelDefinition> labels() { |
+ List<LabelDefinition> result = null; |
+ for (LabelDefinition element in target.labels) { |
+ if (result == null) result = <LabelDefinition>[]; |
+ result.add(element); |
+ } |
+ return (result == null) ? const <LabelDefinition>[] : result; |
+ } |
+} |
+ |
+/// Special [JumpHandler] implementation used to handle continue statements |
+/// targeting switch cases. |
+class SwitchCaseJumpHandler extends TargetJumpHandler { |
+ /// Map from switch case targets to indices used to encode the flow of the |
+ /// switch case loop. |
+ final Map<JumpTarget, int> targetIndexMap = new Map<JumpTarget, int>(); |
+ |
+ SwitchCaseJumpHandler( |
+ GraphBuilder builder, JumpTarget target, ast.SwitchStatement node) |
+ : super(builder, target) { |
+ // The switch case indices must match those computed in |
+ // [SsaFromAstMixin.buildSwitchCaseConstants]. |
+ // Switch indices are 1-based so we can bypass the synthetic loop when no |
+ // cases match simply by branching on the index (which defaults to null). |
+ int switchIndex = 1; |
+ for (ast.SwitchCase switchCase in node.cases) { |
+ for (ast.Node labelOrCase in switchCase.labelsAndCases) { |
+ ast.Node label = labelOrCase.asLabel(); |
+ if (label != null) { |
+ LabelDefinition labelElement = |
+ builder.elements.getLabelDefinition(label); |
+ if (labelElement != null && labelElement.isContinueTarget) { |
+ JumpTarget continueTarget = labelElement.target; |
+ targetIndexMap[continueTarget] = switchIndex; |
+ assert(builder.jumpTargets[continueTarget] == null); |
+ builder.jumpTargets[continueTarget] = this; |
+ } |
+ } |
+ } |
+ switchIndex++; |
+ } |
+ } |
+ |
+ void generateBreak([LabelDefinition label]) { |
+ if (label == null) { |
+ // Creates a special break instruction for the synthetic loop generated |
+ // for a switch statement with continue statements. See |
+ // [SsaFromAstMixin.buildComplexSwitchStatement] for detail. |
+ |
+ HInstruction breakInstruction = |
+ new HBreak(target, breakSwitchContinueLoop: true); |
+ LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); |
+ builder.close(breakInstruction); |
+ jumps.add(new JumpHandlerEntry(breakInstruction, locals)); |
+ } else { |
+ super.generateBreak(label); |
+ } |
+ } |
+ |
+ bool isContinueToSwitchCase(LabelDefinition label) { |
+ return label != null && targetIndexMap.containsKey(label.target); |
+ } |
+ |
+ void generateContinue([LabelDefinition label]) { |
+ if (isContinueToSwitchCase(label)) { |
+ // Creates the special instructions 'label = i; continue l;' used in |
+ // switch statements with continue statements. See |
+ // [SsaFromAstMixin.buildComplexSwitchStatement] for detail. |
+ |
+ assert(label != null); |
+ // TODO(het): change the graph 'addConstantXXX' to take a ConstantSystem |
+ // instead of a Compiler. |
+ HInstruction value = builder.graph |
+ .addConstantInt(targetIndexMap[label.target], builder.compiler); |
+ builder.localsHandler.updateLocal(target, value); |
+ |
+ assert(label.target.labels.contains(label)); |
+ HInstruction continueInstruction = new HContinue(target); |
+ LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); |
+ builder.close(continueInstruction); |
+ jumps.add(new JumpHandlerEntry(continueInstruction, locals)); |
+ } else { |
+ super.generateContinue(label); |
+ } |
+ } |
+ |
+ void close() { |
+ // The mapping from TargetElement to JumpHandler is no longer needed. |
+ for (JumpTarget target in targetIndexMap.keys) { |
+ builder.jumpTargets.remove(target); |
+ } |
+ super.close(); |
+ } |
+} |