Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(156)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/ssa/builder.dart

Issue 14969004: Implement continue for switch. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Comment updated. Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of ssa; 5 part of ssa;
6 6
7 /** 7 /**
8 * A special element for the extra parameter taken by intercepted 8 * A special element for the extra parameter taken by intercepted
9 * methods. We need to override [Element.computeType] because our 9 * methods. We need to override [Element.computeType] because our
10 * optimizers may look at its declared type. 10 * optimizers may look at its declared type.
(...skipping 515 matching lines...) Expand 10 before | Expand all | Expand 10 after
526 void enterLoopBody(Node node) { 526 void enterLoopBody(Node node) {
527 ClosureScope scopeData = closureData.capturingScopes[node]; 527 ClosureScope scopeData = closureData.capturingScopes[node];
528 if (scopeData == null) return; 528 if (scopeData == null) return;
529 // If there are no declared boxed loop variables then we did not create the 529 // If there are no declared boxed loop variables then we did not create the
530 // box before the initializer and we have to create the box now. 530 // box before the initializer and we have to create the box now.
531 if (!scopeData.hasBoxedLoopVariables()) { 531 if (!scopeData.hasBoxedLoopVariables()) {
532 enterScope(node, null); 532 enterScope(node, null);
533 } 533 }
534 } 534 }
535 535
536 void enterLoopUpdates(Loop node) { 536 void enterLoopUpdates(Node node) {
537 // If there are declared boxed loop variables then the updates might have 537 // If there are declared boxed loop variables then the updates might have
538 // access to the box and we must switch to a new box before executing the 538 // access to the box and we must switch to a new box before executing the
539 // updates. 539 // updates.
540 // In all other cases a new box will be created when entering the body of 540 // In all other cases a new box will be created when entering the body of
541 // the next iteration. 541 // the next iteration.
542 ClosureScope scopeData = closureData.capturingScopes[node]; 542 ClosureScope scopeData = closureData.capturingScopes[node];
543 if (scopeData == null) return; 543 if (scopeData == null) return;
544 if (scopeData.hasBoxedLoopVariables()) { 544 if (scopeData.hasBoxedLoopVariables()) {
545 updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables); 545 updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables);
546 } 546 }
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
729 builder.close(breakInstruction); 729 builder.close(breakInstruction);
730 jumps.add(new JumpHandlerEntry(breakInstruction, locals)); 730 jumps.add(new JumpHandlerEntry(breakInstruction, locals));
731 } 731 }
732 732
733 void generateContinue([LabelElement label]) { 733 void generateContinue([LabelElement label]) {
734 HInstruction continueInstruction; 734 HInstruction continueInstruction;
735 if (label == null) { 735 if (label == null) {
736 continueInstruction = new HContinue(target); 736 continueInstruction = new HContinue(target);
737 } else { 737 } else {
738 continueInstruction = new HContinue.toLabel(label); 738 continueInstruction = new HContinue.toLabel(label);
739 // Switch case continue statements must be handled by the
740 // [SwitchCaseJumpHandler].
741 assert(label.target.statement is! SwitchCase);
739 } 742 }
740 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler); 743 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
741 builder.close(continueInstruction); 744 builder.close(continueInstruction);
742 jumps.add(new JumpHandlerEntry(continueInstruction, locals)); 745 jumps.add(new JumpHandlerEntry(continueInstruction, locals));
743 } 746 }
744 747
745 void forEachBreak(Function action) { 748 void forEachBreak(Function action) {
746 for (JumpHandlerEntry entry in jumps) { 749 for (JumpHandlerEntry entry in jumps) {
747 if (entry.isBreak()) action(entry.jumpInstruction, entry.locals); 750 if (entry.isBreak()) action(entry.jumpInstruction, entry.locals);
748 } 751 }
(...skipping 27 matching lines...) Expand all
776 List<LabelElement> labels() { 779 List<LabelElement> labels() {
777 List<LabelElement> result = null; 780 List<LabelElement> result = null;
778 for (LabelElement element in target.labels) { 781 for (LabelElement element in target.labels) {
779 if (result == null) result = <LabelElement>[]; 782 if (result == null) result = <LabelElement>[];
780 result.add(element); 783 result.add(element);
781 } 784 }
782 return (result == null) ? const <LabelElement>[] : result; 785 return (result == null) ? const <LabelElement>[] : result;
783 } 786 }
784 } 787 }
785 788
789 /// Special [JumpHandler] implementation used to handle continue statements
790 /// targeting switch cases.
791 class SwitchCaseJumpHandler extends TargetJumpHandler {
792 /// Map from switch case targets to indices used to encode the flow of the
793 /// switch case loop.
794 final Map<TargetElement, int> targetIndexMap = new Map<TargetElement, int>();
795
796 SwitchCaseJumpHandler(SsaBuilder builder,
797 TargetElement target,
798 SwitchStatement node)
799 : super(builder, target) {
800 // The switch case indices must match those computed in
801 // [SsaBuilder.buildSwitchCaseConstants].
802 // Switch indices are 1-based so we can bypass the synthetic loop when no
803 // cases match simply by branching on the index (which defaults to null).
804 int switchIndex = 1;
805 for (SwitchCase switchCase in node.cases) {
806 for (Node labelOrCase in switchCase.labelsAndCases) {
807 Node label = labelOrCase.asLabel();
808 if (label != null) {
809 LabelElement labelElement = builder.elements[label];
810 if (labelElement != null && labelElement.isContinueTarget) {
811 TargetElement continueTarget = labelElement.target;
812 targetIndexMap[continueTarget] = switchIndex;
813 assert(builder.jumpTargets[continueTarget] == null);
814 builder.jumpTargets[continueTarget] = this;
815 }
816 }
817 }
818 switchIndex++;
819 }
820 }
821
822 void generateBreak([LabelElement label]) {
823 if (label == null) {
824 // Creates a special break instruction for the synthetic loop generated
825 // for a switch statement with continue statements. See
826 // [SsaBuilder.buildComplexSwitchStatement] for detail.
827
828 HInstruction breakInstruction =
829 new HBreak(target, breakSwitchContinueLoop: true);
830 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
831 builder.close(breakInstruction);
832 jumps.add(new JumpHandlerEntry(breakInstruction, locals));
833 } else {
834 super.generateBreak(label);
835 }
836 }
837
838 bool isContinueToSwitchCase(LabelElement label) {
839 return label != null && targetIndexMap.containsKey(label.target);
840 }
841
842 void generateContinue([LabelElement label]) {
843 if (isContinueToSwitchCase(label)) {
844 // Creates the special instructions 'label = i; continue l;' used in
845 // switch statements with continue statements. See
846 // [SsaBuilder.buildComplexSwitchStatement] for detail.
847
848 assert(label != null);
849 HInstruction value = builder.graph.addConstantInt(
850 targetIndexMap[label.target],
851 builder.constantSystem);
852 builder.localsHandler.updateLocal(target, value);
853
854 assert(label.target.labels.contains(label));
855 HInstruction continueInstruction = new HContinue(target);
856 LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
857 builder.close(continueInstruction);
858 jumps.add(new JumpHandlerEntry(continueInstruction, locals));
859 } else {
860 super.generateContinue(label);
861 }
862 }
863
864 void close() {
865 // The mapping from TargetElement to JumpHandler is no longer needed.
866 for (TargetElement target in targetIndexMap.keys) {
867 builder.jumpTargets.remove(target);
868 }
869 super.close();
870 }
871 }
872
786 class SsaBuilder extends ResolvedVisitor implements Visitor { 873 class SsaBuilder extends ResolvedVisitor implements Visitor {
787 final SsaBuilderTask builder; 874 final SsaBuilderTask builder;
788 final JavaScriptBackend backend; 875 final JavaScriptBackend backend;
789 final CodegenWorkItem work; 876 final CodegenWorkItem work;
790 final ConstantSystem constantSystem; 877 final ConstantSystem constantSystem;
791 HGraph graph; 878 HGraph graph;
792 LocalsHandler localsHandler; 879 LocalsHandler localsHandler;
793 HInstruction rethrowableException; 880 HInstruction rethrowableException;
794 Map<Element, HInstruction> parameters; 881 Map<Element, HInstruction> parameters;
795 final RuntimeTypes rti; 882 final RuntimeTypes rti;
(...skipping 1101 matching lines...) Expand 10 before | Expand all | Expand 10 after
1897 1984
1898 /** 1985 /**
1899 * Creates a new loop-header block. The previous [current] block 1986 * Creates a new loop-header block. The previous [current] block
1900 * is closed with an [HGoto] and replaced by the newly created block. 1987 * is closed with an [HGoto] and replaced by the newly created block.
1901 * Also notifies the locals handler that we're entering a loop. 1988 * Also notifies the locals handler that we're entering a loop.
1902 */ 1989 */
1903 JumpHandler beginLoopHeader(Node node) { 1990 JumpHandler beginLoopHeader(Node node) {
1904 assert(!isAborted()); 1991 assert(!isAborted());
1905 HBasicBlock previousBlock = close(new HGoto()); 1992 HBasicBlock previousBlock = close(new HGoto());
1906 1993
1907 JumpHandler jumpHandler = createJumpHandler(node); 1994 JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: true);
1908 HBasicBlock loopEntry = graph.addNewLoopHeaderBlock( 1995 HBasicBlock loopEntry = graph.addNewLoopHeaderBlock(
1909 jumpHandler.target, 1996 jumpHandler.target,
1910 jumpHandler.labels()); 1997 jumpHandler.labels());
1911 previousBlock.addSuccessor(loopEntry); 1998 previousBlock.addSuccessor(loopEntry);
1912 open(loopEntry); 1999 open(loopEntry);
1913 2000
1914 localsHandler.beginLoopHeader(loopEntry); 2001 localsHandler.beginLoopHeader(loopEntry);
1915 return jumpHandler; 2002 return jumpHandler;
1916 } 2003 }
1917 2004
(...skipping 2200 matching lines...) Expand 10 before | Expand all | Expand 10 after
4118 LabelElement label = elements[node.target]; 4205 LabelElement label = elements[node.target];
4119 assert(label != null); 4206 assert(label != null);
4120 handler.generateContinue(label); 4207 handler.generateContinue(label);
4121 } 4208 }
4122 } 4209 }
4123 4210
4124 /** 4211 /**
4125 * Creates a [JumpHandler] for a statement. The node must be a jump 4212 * Creates a [JumpHandler] for a statement. The node must be a jump
4126 * target. If there are no breaks or continues targeting the statement, 4213 * target. If there are no breaks or continues targeting the statement,
4127 * a special "null handler" is returned. 4214 * a special "null handler" is returned.
4215 *
4216 * [isLoopJump] is [:true:] when the jump handler is for a loop. This is used
4217 * to distinguish the synthetized loop created for a switch statement with
4218 * continue statements from simple switch statements.
4128 */ 4219 */
4129 JumpHandler createJumpHandler(Statement node) { 4220 JumpHandler createJumpHandler(Statement node, {bool isLoopJump}) {
4130 TargetElement element = elements[node]; 4221 TargetElement element = elements[node];
4131 if (element == null || !identical(element.statement, node)) { 4222 if (element == null || !identical(element.statement, node)) {
4132 // No breaks or continues to this node. 4223 // No breaks or continues to this node.
4133 return new NullJumpHandler(compiler); 4224 return new NullJumpHandler(compiler);
4134 } 4225 }
4226 if (isLoopJump && node is SwitchStatement) {
4227 // Create a special jump handler for loops created for switch statements
4228 // with continue statements.
4229 return new SwitchCaseJumpHandler(this, element, node);
4230 }
4135 return new JumpHandler(this, element); 4231 return new JumpHandler(this, element);
4136 } 4232 }
4137 4233
4138 visitForIn(ForIn node) { 4234 visitForIn(ForIn node) {
4139 // Generate a structure equivalent to: 4235 // Generate a structure equivalent to:
4140 // Iterator<E> $iter = <iterable>.iterator; 4236 // Iterator<E> $iter = <iterable>.iterator;
4141 // while ($iter.moveNext()) { 4237 // while ($iter.moveNext()) {
4142 // E <declaredIdentifier> = $iter.current; 4238 // E <declaredIdentifier> = $iter.current;
4143 // <body> 4239 // <body>
4144 // } 4240 // }
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
4263 4359
4264 visitLiteralMapEntry(LiteralMapEntry node) { 4360 visitLiteralMapEntry(LiteralMapEntry node) {
4265 visit(node.value); 4361 visit(node.value);
4266 visit(node.key); 4362 visit(node.key);
4267 } 4363 }
4268 4364
4269 visitNamedArgument(NamedArgument node) { 4365 visitNamedArgument(NamedArgument node) {
4270 visit(node.expression); 4366 visit(node.expression);
4271 } 4367 }
4272 4368
4273 visitSwitchStatement(SwitchStatement node) { 4369 Map<CaseMatch,Constant> buildSwitchCaseConstants(SwitchStatement node) {
4274 if (tryBuildConstantSwitch(node)) return;
4275
4276 LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
4277 HBasicBlock startBlock = openNewBlock();
4278 visit(node.expression);
4279 HInstruction expression = pop();
4280 if (node.cases.isEmpty) {
4281 return;
4282 }
4283
4284 Link<Node> cases = node.cases.nodes;
4285 JumpHandler jumpHandler = createJumpHandler(node);
4286
4287 buildSwitchCases(cases, expression);
4288
4289 HBasicBlock lastBlock = lastOpenedBlock;
4290
4291 // Create merge block for break targets.
4292 HBasicBlock joinBlock = new HBasicBlock();
4293 List<LocalsHandler> caseHandlers = <LocalsHandler>[];
4294 jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) {
4295 instruction.block.addSuccessor(joinBlock);
4296 caseHandlers.add(locals);
4297 });
4298 if (!isAborted()) {
4299 // The current flow is only aborted if the switch has a default that
4300 // aborts (all previous cases must abort, and if there is no default,
4301 // it's possible to miss all the cases).
4302 caseHandlers.add(localsHandler);
4303 goto(current, joinBlock);
4304 }
4305 if (caseHandlers.length != 0) {
4306 graph.addBlock(joinBlock);
4307 open(joinBlock);
4308 if (caseHandlers.length == 1) {
4309 localsHandler = caseHandlers[0];
4310 } else {
4311 localsHandler = savedLocals.mergeMultiple(caseHandlers, joinBlock);
4312 }
4313 } else {
4314 // The joinblock is not used.
4315 joinBlock = null;
4316 }
4317 startBlock.setBlockFlow(
4318 new HLabeledBlockInformation.implicit(
4319 new HSubGraphBlockInformation(new SubGraph(startBlock, lastBlock)),
4320 elements[node]),
4321 joinBlock);
4322 jumpHandler.close();
4323 }
4324
4325 bool tryBuildConstantSwitch(SwitchStatement node) {
4326 Map<CaseMatch, Constant> constants = new Map<CaseMatch, Constant>(); 4370 Map<CaseMatch, Constant> constants = new Map<CaseMatch, Constant>();
4327 // First check whether all case expressions are compile-time constants, 4371 // First check whether all case expressions are compile-time constants,
4328 // and all have the same type that doesn't override operator==. 4372 // and all have the same type that doesn't override operator==.
4329 // TODO(lrn): Move the constant resolution to the resolver, so 4373 // TODO(lrn): Move the constant resolution to the resolver, so
4330 // we can report an error before reaching the backend. 4374 // we can report an error before reaching the backend.
4331 DartType firstConstantType = null; 4375 DartType firstConstantType = null;
4332 bool failure = false; 4376 bool failure = false;
4333 for (SwitchCase switchCase in node.cases) { 4377 for (SwitchCase switchCase in node.cases) {
4334 for (Node labelOrCase in switchCase.labelsAndCases) { 4378 for (Node labelOrCase in switchCase.labelsAndCases) {
4335 if (labelOrCase is CaseMatch) { 4379 if (labelOrCase is CaseMatch) {
4336 CaseMatch match = labelOrCase; 4380 CaseMatch match = labelOrCase;
4337 Constant constant = 4381 Constant constant =
4338 compiler.constantHandler.tryCompileNodeWithDefinitions( 4382 compiler.constantHandler.compileNodeWithDefinitions(
4339 match.expression, elements); 4383 match.expression, elements, isConst: true);
4340 if (constant == null) {
4341 compiler.reportWarning(match.expression,
4342 MessageKind.NOT_A_COMPILE_TIME_CONSTANT.error());
4343 failure = true;
4344 continue;
4345 }
4346 if (firstConstantType == null) { 4384 if (firstConstantType == null) {
4347 firstConstantType = constant.computeType(compiler); 4385 firstConstantType = constant.computeType(compiler);
4348 if (nonPrimitiveTypeOverridesEquals(constant)) { 4386 if (nonPrimitiveTypeOverridesEquals(constant)) {
4349 compiler.reportWarning(match.expression, 4387 compiler.reportError(match.expression,
4350 MessageKind.SWITCH_CASE_VALUE_OVERRIDES_EQUALS.error()); 4388 MessageKind.SWITCH_CASE_VALUE_OVERRIDES_EQUALS.error());
4351 failure = true; 4389 failure = true;
4352 } 4390 }
4353 } else { 4391 } else {
4354 DartType constantType = 4392 DartType constantType =
4355 constant.computeType(compiler); 4393 constant.computeType(compiler);
4356 if (constantType != firstConstantType) { 4394 if (constantType != firstConstantType) {
4357 compiler.reportWarning(match.expression, 4395 compiler.reportError(match.expression,
4358 MessageKind.SWITCH_CASE_TYPES_NOT_EQUAL.error()); 4396 MessageKind.SWITCH_CASE_TYPES_NOT_EQUAL.error());
4359 failure = true; 4397 failure = true;
4360 } 4398 }
4361 } 4399 }
4362 constants[labelOrCase] = constant; 4400 constants[labelOrCase] = constant;
4363 } else { 4401 }
4364 compiler.reportWarning(node, "Unsupported: Labels on cases"); 4402 }
4365 failure = true; 4403 }
4366 } 4404 return constants;
4367 } 4405 }
4368 } 4406
4369 if (failure) { 4407 visitSwitchStatement(SwitchStatement node) {
4370 return false; 4408 Map<CaseMatch,Constant> constants = buildSwitchCaseConstants(node);
4371 } 4409
4410 // The switch case indices must match those computed in
4411 // [SwitchCaseJumpHandler].
4412 bool hasContinue = false;
4413 Map<SwitchCase, int> caseIndex = new Map<SwitchCase, int>();
4414 int switchIndex = 1;
4415 bool hasDefault = false;
4416 for (SwitchCase switchCase in node.cases) {
4417 for (Node labelOrCase in switchCase.labelsAndCases) {
4418 Node label = labelOrCase.asLabel();
4419 if (label != null) {
4420 LabelElement labelElement = elements[label];
4421 if (labelElement != null && labelElement.isContinueTarget) {
4422 hasContinue = true;
4423 }
4424 }
4425 }
4426 if (switchCase.isDefaultCase) {
4427 hasDefault = true;
4428 }
4429 caseIndex[switchCase] = switchIndex;
4430 switchIndex++;
4431 }
4432 if (!hasContinue) {
4433 // If the switch statement has no switch cases targeted by continue
4434 // statements we encode the switch statement directly.
4435 buildSimpleSwitchStatement(node, constants);
4436 } else {
4437 buildComplexSwitchStatement(node, constants, caseIndex, hasDefault);
4438 }
4439 }
4440
4441 /**
4442 * Builds a simple switch statement which does not handle uses of continue
4443 * statements to labeled switch cases.
4444 */
4445 void buildSimpleSwitchStatement(SwitchStatement node,
4446 Map<CaseMatch, Constant> constants) {
4447 JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: false);
4448 HInstruction buildExpression() {
4449 visit(node.expression);
4450 return pop();
4451 }
4452 Iterable<Constant> getConstants(SwitchCase switchCase) {
4453 List<Constant> constantList = <Constant>[];
4454 for (Node labelOrCase in switchCase.labelsAndCases) {
4455 if (labelOrCase is CaseMatch) {
4456 constantList.add(constants[labelOrCase]);
4457 }
4458 }
4459 return constantList;
4460 }
4461 bool isDefaultCase(SwitchCase switchCase) {
4462 return switchCase.isDefaultCase;
4463 }
4464 void buildSwitchCase(SwitchCase node) {
4465 visit(node.statements);
4466 }
4467 handleSwitch(node,
4468 jumpHandler,
4469 buildExpression,
4470 node.cases,
4471 getConstants,
4472 isDefaultCase,
4473 buildSwitchCase);
4474 jumpHandler.close();
4475 }
4476
4477 /**
4478 * Builds a switch statement that can handle arbitrary uses of continue
4479 * statements to labeled switch cases.
4480 */
4481 void buildComplexSwitchStatement(SwitchStatement node,
4482 Map<CaseMatch, Constant> constants,
4483 Map<SwitchCase, int> caseIndex,
4484 bool hasDefault) {
4485 // If the switch statement has switch cases targeted by continue
4486 // statements we create the following encoding:
4487 //
4488 // switch (e) {
4489 // l_1: case e0: s_1; break;
4490 // l_2: case e1: s_2; continue l_i;
4491 // ...
4492 // l_n: default: s_n; continue l_j;
4493 // }
4494 //
4495 // is encoded as
4496 //
4497 // var target;
4498 // switch (e) {
4499 // case e1: target = 1; break;
4500 // case e2: target = 2; break;
4501 // ...
4502 // default: target = n; break;
4503 // }
4504 // l: while (true) {
4505 // switch (target) {
4506 // case 1: s_1; break l;
4507 // case 2: s_2; target = i; continue l;
4508 // ...
4509 // case n: s_n; target = j; continue l;
4510 // }
4511 // }
4512
4513 TargetElement switchTarget = elements[node];
4514 HInstruction initialValue = graph.addConstantNull(constantSystem);
4515 localsHandler.updateLocal(switchTarget, initialValue);
4516
4517 JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: false);
4518 var switchCases = node.cases;
4519 if (!hasDefault) {
4520 // Use [:null:] as the marker for a synthetic default clause.
4521 // The synthetic default is added because otherwise, there would be no
4522 // good place to give a default value to the local.
4523 switchCases = node.cases.nodes.toList()..add(null);
4524 }
4525 HInstruction buildExpression() {
4526 visit(node.expression);
4527 return pop();
4528 }
4529 Iterable<Constant> getConstants(SwitchCase switchCase) {
4530 List<Constant> constantList = <Constant>[];
4531 if (switchCase != null) {
4532 for (Node labelOrCase in switchCase.labelsAndCases) {
4533 if (labelOrCase is CaseMatch) {
4534 constantList.add(constants[labelOrCase]);
4535 }
4536 }
4537 }
4538 return constantList;
4539 }
4540 bool isDefaultCase(SwitchCase switchCase) {
4541 return switchCase == null || switchCase.isDefaultCase;
4542 }
4543 void buildSwitchCase(SwitchCase switchCase) {
4544 if (switchCase != null) {
4545 // Generate 'target = i; break;' for switch case i.
4546 int index = caseIndex[switchCase];
4547 HInstruction value = graph.addConstantInt(index, constantSystem);
4548 localsHandler.updateLocal(switchTarget, value);
4549 } else {
4550 // Generate synthetic default case 'target = null; break;'.
4551 HInstruction value = graph.addConstantNull(constantSystem);
4552 localsHandler.updateLocal(switchTarget, value);
4553 }
4554 jumpTargets[switchTarget].generateBreak();
4555 }
4556 handleSwitch(node,
4557 jumpHandler,
4558 buildExpression,
4559 switchCases,
4560 getConstants,
4561 isDefaultCase,
4562 buildSwitchCase);
4563 jumpHandler.close();
4564
4565 HInstruction buildCondition() =>
4566 graph.addConstantBool(true, constantSystem);
4567
4568 void buildSwitch() {
4569 HInstruction buildExpression() {
4570 return localsHandler.readLocal(switchTarget);
4571 }
4572 Iterable<Constant> getConstants(SwitchCase switchCase) {
4573 return <Constant>[constantSystem.createInt(caseIndex[switchCase])];
4574 }
4575 void buildSwitchCase(SwitchCase switchCase) {
4576 visit(switchCase.statements);
4577 if (!isAborted()) {
4578 // Ensure that we break the loop if the case falls through. (This
4579 // is only possible for the last case.)
4580 jumpTargets[switchTarget].generateBreak();
4581 }
4582 }
4583 // Pass a [NullJumpHandler] because the target for the contained break
4584 // is not the generated switch statement but instead the loop generated
4585 // in the call to [handleLoop] below.
4586 handleSwitch(node,
4587 new NullJumpHandler(compiler),
4588 buildExpression, node.cases, getConstants,
4589 (_) => false, // No case is default.
4590 buildSwitchCase);
4591 }
4592
4593 void buildLoop() {
4594 handleLoop(node,
4595 () {},
4596 buildCondition,
4597 () {},
4598 buildSwitch);
4599 }
4600
4601 if (hasDefault) {
4602 buildLoop();
4603 } else {
4604 // If the switch statement has no default case, surround the loop with
4605 // a test of the target.
4606 void buildCondition() {
4607 push(createForeign('#', HType.BOOLEAN,
4608 [localsHandler.readLocal(switchTarget)]));
4609 }
4610 handleIf(node, buildCondition, buildLoop, () => {});
4611 }
4612 }
4613
4614 /**
4615 * Creates a switch statement.
4616 *
4617 * [jumpHandler] is the [JumpHandler] for the created switch statement.
4618 * [buildExpression] creates the switch expression.
4619 * [switchCases] must be either an [Iterable] of [SwitchCase] nodes or
4620 * a [Link] or a [NodeList] of [SwitchCase] nodes.
4621 * [getConstants] returns the set of constants for a switch case.
4622 * [isDefaultCase] returns [:true:] if the provided switch case should be
4623 * considered default for the created switch statement.
4624 * [buildSwitchCase] creates the statements for the switch case.
4625 */
4626 void handleSwitch(Node errorNode,
4627 JumpHandler jumpHandler,
4628 HInstruction buildExpression(),
4629 var switchCases,
4630 Iterable<Constant> getConstants(SwitchCase switchCase),
4631 bool isDefaultCase(SwitchCase switchCase),
4632 void buildSwitchCase(SwitchCase switchCase)) {
4633 Map<CaseMatch, Constant> constants = new Map<CaseMatch, Constant>();
4372 4634
4373 // TODO(ngeoffray): Handle switch-instruction in bailout code. 4635 // TODO(ngeoffray): Handle switch-instruction in bailout code.
4374 work.allowSpeculativeOptimization = false; 4636 work.allowSpeculativeOptimization = false;
4375 // Then build a switch structure. 4637 // Then build a switch structure.
4376 HBasicBlock expressionStart = openNewBlock(); 4638 HBasicBlock expressionStart = openNewBlock();
4377 visit(node.expression); 4639 HInstruction expression = buildExpression();
4378 HInstruction expression = pop(); 4640 if (switchCases.isEmpty) {
4379 if (node.cases.isEmpty) { 4641 return;
4380 return true;
4381 } 4642 }
4382 HBasicBlock expressionEnd = current; 4643 HBasicBlock expressionEnd = current;
4383 4644
4384 HSwitch switchInstruction = new HSwitch(<HInstruction>[expression]); 4645 HSwitch switchInstruction = new HSwitch(<HInstruction>[expression]);
4385 HBasicBlock expressionBlock = close(switchInstruction); 4646 HBasicBlock expressionBlock = close(switchInstruction);
4386 JumpHandler jumpHandler = createJumpHandler(node);
4387 LocalsHandler savedLocals = localsHandler; 4647 LocalsHandler savedLocals = localsHandler;
4388 4648
4389 List<List<Constant>> matchExpressions = <List<Constant>>[]; 4649 List<List<Constant>> matchExpressions = <List<Constant>>[];
4390 List<HStatementInformation> statements = <HStatementInformation>[]; 4650 List<HStatementInformation> statements = <HStatementInformation>[];
4391 bool hasDefault = false; 4651 bool hasDefault = false;
4392 Element getFallThroughErrorElement = backend.getFallThroughError(); 4652 Element getFallThroughErrorElement = backend.getFallThroughError();
4393 HasNextIterator<Node> caseIterator = 4653 HasNextIterator<Node> caseIterator =
4394 new HasNextIterator<Node>(node.cases.iterator); 4654 new HasNextIterator<Node>(switchCases.iterator);
4395 while (caseIterator.hasNext) { 4655 while (caseIterator.hasNext) {
4396 SwitchCase switchCase = caseIterator.next(); 4656 SwitchCase switchCase = caseIterator.next();
4397 List<Constant> caseConstants = <Constant>[]; 4657 List<Constant> caseConstants = <Constant>[];
4398 HBasicBlock block = graph.addNewBlock(); 4658 HBasicBlock block = graph.addNewBlock();
4399 for (Node labelOrCase in switchCase.labelsAndCases) { 4659 for (Constant constant in getConstants(switchCase)) {
4400 if (labelOrCase is CaseMatch) { 4660 caseConstants.add(constant);
4401 Constant constant = constants[labelOrCase]; 4661 HConstant hConstant = graph.addConstant(constant);
4402 caseConstants.add(constant); 4662 switchInstruction.inputs.add(hConstant);
4403 HConstant hConstant = graph.addConstant(constant); 4663 hConstant.usedBy.add(switchInstruction);
4404 switchInstruction.inputs.add(hConstant); 4664 expressionBlock.addSuccessor(block);
4405 hConstant.usedBy.add(switchInstruction);
4406 expressionBlock.addSuccessor(block);
4407 }
4408 } 4665 }
4409 matchExpressions.add(caseConstants); 4666 matchExpressions.add(caseConstants);
4410 4667
4411 if (switchCase.isDefaultCase) { 4668 if (isDefaultCase(switchCase)) {
4412 // An HSwitch has n inputs and n+1 successors, the last being the 4669 // An HSwitch has n inputs and n+1 successors, the last being the
4413 // default case. 4670 // default case.
4414 expressionBlock.addSuccessor(block); 4671 expressionBlock.addSuccessor(block);
4415 hasDefault = true; 4672 hasDefault = true;
4416 } 4673 }
4417 open(block); 4674 open(block);
4418 localsHandler = new LocalsHandler.from(savedLocals); 4675 localsHandler = new LocalsHandler.from(savedLocals);
4419 visit(switchCase.statements); 4676 buildSwitchCase(switchCase);
4420 if (!isAborted() && caseIterator.hasNext) { 4677 if (!isAborted() && caseIterator.hasNext) {
4421 pushInvokeHelper0(getFallThroughErrorElement, HType.UNKNOWN); 4678 pushInvokeHelper0(getFallThroughErrorElement, HType.UNKNOWN);
4422 HInstruction error = pop(); 4679 HInstruction error = pop();
4423 closeAndGotoExit(new HThrow(error)); 4680 closeAndGotoExit(new HThrow(error));
4424 } 4681 }
4425 statements.add( 4682 statements.add(
4426 new HSubGraphBlockInformation(new SubGraph(block, lastOpenedBlock))); 4683 new HSubGraphBlockInformation(new SubGraph(block, lastOpenedBlock)));
4427 } 4684 }
4428 4685
4429 // Add a join-block if necessary. 4686 // Add a join-block if necessary.
4430 // We create [joinBlock] early, and then go through the cases that might 4687 // We create [joinBlock] early, and then go through the cases that might
4431 // want to jump to it. In each case, if we add [joinBlock] as a successor 4688 // want to jump to it. In each case, if we add [joinBlock] as a successor
4432 // of another block, we also add an element to [caseHandlers] that is used 4689 // of another block, we also add an element to [caseHandlers] that is used
4433 // to create the phis in [joinBlock]. 4690 // to create the phis in [joinBlock].
4434 // If we never jump to the join block, [caseHandlers] will stay empty, and 4691 // If we never jump to the join block, [caseHandlers] will stay empty, and
4435 // the join block is never added to the graph. 4692 // the join block is never added to the graph.
4436 HBasicBlock joinBlock = new HBasicBlock(); 4693 HBasicBlock joinBlock = new HBasicBlock();
4437 List<LocalsHandler> caseHandlers = <LocalsHandler>[]; 4694 List<LocalsHandler> caseHandlers = <LocalsHandler>[];
4438 jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) { 4695 jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) {
4439 instruction.block.addSuccessor(joinBlock); 4696 instruction.block.addSuccessor(joinBlock);
4440 caseHandlers.add(locals); 4697 caseHandlers.add(locals);
4441 }); 4698 });
4699 jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) {
4700 assert(invariant(errorNode, false,
4701 message: 'Continue cannot target a switch.'));
4702 });
4442 if (!isAborted()) { 4703 if (!isAborted()) {
4443 current.close(new HGoto()); 4704 current.close(new HGoto());
4444 lastOpenedBlock.addSuccessor(joinBlock); 4705 lastOpenedBlock.addSuccessor(joinBlock);
4445 caseHandlers.add(localsHandler); 4706 caseHandlers.add(localsHandler);
4446 } 4707 }
4447 if (!hasDefault) { 4708 if (!hasDefault) {
4448 // The current flow is only aborted if the switch has a default that 4709 // The current flow is only aborted if the switch has a default that
4449 // aborts (all previous cases must abort, and if there is no default, 4710 // aborts (all previous cases must abort, and if there is no default,
4450 // it's possible to miss all the cases). 4711 // it's possible to miss all the cases).
4451 expressionEnd.addSuccessor(joinBlock); 4712 expressionEnd.addSuccessor(joinBlock);
(...skipping 19 matching lines...) Expand all
4471 expressionStart.setBlockFlow( 4732 expressionStart.setBlockFlow(
4472 new HSwitchBlockInformation(expressionInfo, 4733 new HSwitchBlockInformation(expressionInfo,
4473 matchExpressions, 4734 matchExpressions,
4474 statements, 4735 statements,
4475 hasDefault, 4736 hasDefault,
4476 jumpHandler.target, 4737 jumpHandler.target,
4477 jumpHandler.labels()), 4738 jumpHandler.labels()),
4478 joinBlock); 4739 joinBlock);
4479 4740
4480 jumpHandler.close(); 4741 jumpHandler.close();
4481 return true;
4482 } 4742 }
4483 4743
4484 bool nonPrimitiveTypeOverridesEquals(Constant constant) { 4744 bool nonPrimitiveTypeOverridesEquals(Constant constant) {
4485 // Function values override equals. Even static ones, since 4745 // Function values override equals. Even static ones, since
4486 // they inherit from [Function]. 4746 // they inherit from [Function].
4487 if (constant.isFunction()) return true; 4747 if (constant.isFunction()) return true;
4488 4748
4489 // [Map] and [List] do not override equals. 4749 // [Map] and [List] do not override equals.
4490 // If constant is primitive, just return false. We know 4750 // If constant is primitive, just return false. We know
4491 // about the equals methods of num/String classes. 4751 // about the equals methods of num/String classes.
(...skipping 17 matching lines...) Expand all
4509 // If the operator== declaration is in Object, it's not overridden. 4769 // If the operator== declaration is in Object, it's not overridden.
4510 return (operatorEq.getEnclosingClass() != compiler.objectClass); 4770 return (operatorEq.getEnclosingClass() != compiler.objectClass);
4511 } 4771 }
4512 4772
4513 Element lookupOperator(ClassElement classElement, SourceString operatorName) { 4773 Element lookupOperator(ClassElement classElement, SourceString operatorName) {
4514 SourceString dartMethodName = 4774 SourceString dartMethodName =
4515 Elements.constructOperatorName(operatorName, false); 4775 Elements.constructOperatorName(operatorName, false);
4516 return classElement.lookupMember(dartMethodName); 4776 return classElement.lookupMember(dartMethodName);
4517 } 4777 }
4518 4778
4519
4520 // Recursively build an if/else structure to match the cases.
4521 void buildSwitchCases(Link<Node> cases, HInstruction expression,
4522 [int encounteredCaseTypes = 0]) {
4523 final int NO_TYPE = 0;
4524 final int INT_TYPE = 1;
4525 final int STRING_TYPE = 2;
4526 final int CONFLICT_TYPE = 3;
4527 int combine(int type1, int type2) => type1 | type2;
4528
4529 SwitchCase node = cases.head;
4530 // Called for the statements on all but the last case block.
4531 // Ensures that a user expecting a fallthrough gets an error.
4532 void visitStatementsAndAbort() {
4533 visit(node.statements);
4534 if (!isAborted()) {
4535 compiler.reportWarning(node, 'Missing break at end of switch case');
4536 Element element =
4537 compiler.findHelper(const SourceString("getFallThroughError"));
4538 pushInvokeHelper0(element, HType.UNKNOWN);
4539 HInstruction error = pop();
4540 closeAndGotoExit(new HThrow(error));
4541 }
4542 }
4543
4544 Link<Node> skipLabels(Link<Node> labelsAndCases) {
4545 while (!labelsAndCases.isEmpty && labelsAndCases.head is Label) {
4546 labelsAndCases = labelsAndCases.tail;
4547 }
4548 return labelsAndCases;
4549 }
4550
4551 Link<Node> labelsAndCases = skipLabels(node.labelsAndCases.nodes);
4552 if (labelsAndCases.isEmpty) {
4553 // Default case with no expressions.
4554 if (!node.isDefaultCase) {
4555 compiler.internalError("Case with no expression and not default",
4556 node: node);
4557 }
4558 visit(node.statements);
4559 // This must be the final case (otherwise "default" would be invalid),
4560 // so we don't need to check for fallthrough.
4561 return;
4562 }
4563
4564 // Recursively build the test conditions. Leaves the result on the
4565 // expression stack.
4566 void buildTests(Link<Node> remainingCases) {
4567 // Build comparison for one case expression.
4568 void left() {
4569 CaseMatch match = remainingCases.head;
4570 // TODO(lrn): Move the constant resolution to the resolver, so
4571 // we can report an error before reaching the backend.
4572 Constant constant =
4573 compiler.constantHandler.tryCompileNodeWithDefinitions(
4574 match.expression, elements);
4575 if (constant != null) {
4576 stack.add(graph.addConstant(constant));
4577 } else {
4578 visit(match.expression);
4579 }
4580 push(new HIdentity(pop(), expression));
4581 }
4582
4583 // If this is the last expression, just return it.
4584 Link<Node> tail = skipLabels(remainingCases.tail);
4585 if (tail.isEmpty) {
4586 left();
4587 return;
4588 }
4589
4590 void right() {
4591 buildTests(tail);
4592 }
4593 SsaBranchBuilder branchBuilder =
4594 new SsaBranchBuilder(this, remainingCases.head);
4595 branchBuilder.handleLogicalAndOr(left, right, isAnd: false);
4596 }
4597
4598 if (node.isDefaultCase) {
4599 // Default case must be last.
4600 assert(cases.tail.isEmpty);
4601 // Perform the tests until one of them match, but then always execute the
4602 // statements.
4603 // TODO(lrn): Stop performing tests when all expressions are compile-time
4604 // constant strings or integers.
4605 handleIf(node, () { buildTests(labelsAndCases); }, (){}, null);
4606 visit(node.statements);
4607 } else {
4608 if (cases.tail.isEmpty) {
4609 handleIf(node,
4610 () { buildTests(labelsAndCases); },
4611 () { visit(node.statements); },
4612 null);
4613 } else {
4614 handleIf(node,
4615 () { buildTests(labelsAndCases); },
4616 () { visitStatementsAndAbort(); },
4617 () { buildSwitchCases(cases.tail, expression,
4618 encounteredCaseTypes); });
4619 }
4620 }
4621 }
4622
4623 visitSwitchCase(SwitchCase node) { 4779 visitSwitchCase(SwitchCase node) {
4624 compiler.internalError('SsaBuilder.visitSwitchCase'); 4780 compiler.internalError('SsaBuilder.visitSwitchCase');
4625 } 4781 }
4626 4782
4627 visitCaseMatch(CaseMatch node) { 4783 visitCaseMatch(CaseMatch node) {
4628 compiler.internalError('SsaBuilder.visitCaseMatch'); 4784 compiler.internalError('SsaBuilder.visitCaseMatch');
4629 } 4785 }
4630 4786
4631 visitTryStatement(TryStatement node) { 4787 visitTryStatement(TryStatement node) {
4632 work.allowSpeculativeOptimization = false; 4788 work.allowSpeculativeOptimization = false;
(...skipping 644 matching lines...) Expand 10 before | Expand all | Expand 10 after
5277 new HSubGraphBlockInformation(elseBranch.graph)); 5433 new HSubGraphBlockInformation(elseBranch.graph));
5278 5434
5279 HBasicBlock conditionStartBlock = conditionBranch.block; 5435 HBasicBlock conditionStartBlock = conditionBranch.block;
5280 conditionStartBlock.setBlockFlow(info, joinBlock); 5436 conditionStartBlock.setBlockFlow(info, joinBlock);
5281 SubGraph conditionGraph = conditionBranch.graph; 5437 SubGraph conditionGraph = conditionBranch.graph;
5282 HIf branch = conditionGraph.end.last; 5438 HIf branch = conditionGraph.end.last;
5283 assert(branch is HIf); 5439 assert(branch is HIf);
5284 branch.blockInformation = conditionStartBlock.blockFlow; 5440 branch.blockInformation = conditionStartBlock.blockFlow;
5285 } 5441 }
5286 } 5442 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698