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

Side by Side Diff: pkg/compiler/lib/src/ssa/builder_kernel.dart

Issue 2648443004: Implement complex switch statement (switch with continue). (Closed)
Patch Set: . Created 3 years, 11 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
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2016, 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 import 'package:kernel/ast.dart' as ir; 5 import 'package:kernel/ast.dart' as ir;
6 6
7 import '../closure.dart'; 7 import '../closure.dart';
8 import '../common.dart'; 8 import '../common.dart';
9 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem; 9 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem;
10 import '../common/names.dart'; 10 import '../common/names.dart';
(...skipping 1125 matching lines...) Expand 10 before | Expand all | Expand 10 after
1136 /// to distinguish the synthesized loop created for a switch statement with 1136 /// to distinguish the synthesized loop created for a switch statement with
1137 /// continue statements from simple switch statements. 1137 /// continue statements from simple switch statements.
1138 JumpHandler createJumpHandler(ir.TreeNode node, {bool isLoopJump: false}) { 1138 JumpHandler createJumpHandler(ir.TreeNode node, {bool isLoopJump: false}) {
1139 JumpTarget target = astAdapter.getJumpTarget(node); 1139 JumpTarget target = astAdapter.getJumpTarget(node);
1140 assert(target is KernelJumpTarget); 1140 assert(target is KernelJumpTarget);
1141 if (target == null) { 1141 if (target == null) {
1142 // No breaks or continues to this node. 1142 // No breaks or continues to this node.
1143 return new NullJumpHandler(compiler.reporter); 1143 return new NullJumpHandler(compiler.reporter);
1144 } 1144 }
1145 if (isLoopJump && node is ir.SwitchStatement) { 1145 if (isLoopJump && node is ir.SwitchStatement) {
1146 throw 'Kernel Switch Statement handler not yet implemented.'; 1146 return new KernelSwitchCaseJumpHandler(this, target, node, astAdapter);
1147 } 1147 }
1148 1148
1149 return new JumpHandler(this, target); 1149 return new JumpHandler(this, target);
1150 } 1150 }
1151 1151
1152 @override 1152 @override
1153 void visitBreakStatement(ir.BreakStatement breakStatement) { 1153 void visitBreakStatement(ir.BreakStatement breakStatement) {
1154 assert(!isAborted()); 1154 assert(!isAborted());
1155 handleInTryStatement(); 1155 handleInTryStatement();
1156 JumpTarget target = astAdapter.getJumpTarget(breakStatement.target); 1156 JumpTarget target = astAdapter.getJumpTarget(breakStatement.target);
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
1229 JumpTarget target = astAdapter.getJumpTarget(switchStatement.target); 1229 JumpTarget target = astAdapter.getJumpTarget(switchStatement.target);
1230 assert(target != null); 1230 assert(target != null);
1231 JumpHandler handler = jumpTargets[target]; 1231 JumpHandler handler = jumpTargets[target];
1232 assert(handler != null); 1232 assert(handler != null);
1233 assert(target.labels.isNotEmpty); 1233 assert(target.labels.isNotEmpty);
1234 handler.generateContinue(target.labels.first); 1234 handler.generateContinue(target.labels.first);
1235 } 1235 }
1236 1236
1237 @override 1237 @override
1238 void visitSwitchStatement(ir.SwitchStatement switchStatement) { 1238 void visitSwitchStatement(ir.SwitchStatement switchStatement) {
1239 Map<ir.Expression, ConstantValue> constants =
1240 _buildSwitchCaseConstants(switchStatement);
1241
1242 // The switch case indices must match those computed in 1239 // The switch case indices must match those computed in
1243 // [KernelSwitchCaseJumpHandler]. 1240 // [KernelSwitchCaseJumpHandler].
1244 bool hasContinue = false; 1241 bool hasContinue = false;
1245 Map<ir.SwitchCase, int> caseIndex = new Map<ir.SwitchCase, int>(); 1242 Map<ir.SwitchCase, int> caseIndex = new Map<ir.SwitchCase, int>();
1246 int switchIndex = 1; 1243 int switchIndex = 1;
1247 bool hasDefault = false; 1244 bool hasDefault = false;
1248 for (ir.SwitchCase switchCase in switchStatement.cases) { 1245 for (ir.SwitchCase switchCase in switchStatement.cases) {
1249 if (SwitchContinueAnalysis.containsContinue(switchCase.body)) { 1246 if (SwitchContinueAnalysis.containsContinue(switchCase.body)) {
1250 hasContinue = true; 1247 hasContinue = true;
1251 } 1248 }
1252 if (switchCase.isDefault) { 1249 if (switchCase.isDefault) {
1253 hasDefault = true; 1250 hasDefault = true;
1254 } 1251 }
1255 caseIndex[switchCase] = switchIndex; 1252 caseIndex[switchCase] = switchIndex;
1256 switchIndex++; 1253 switchIndex++;
1257 } 1254 }
1258 1255
1259 JumpHandler jumpHandler = createJumpHandler(switchStatement); 1256 JumpHandler jumpHandler = createJumpHandler(switchStatement);
1260 if (!hasContinue) { 1257 if (!hasContinue) {
1261 // If the switch statement has no switch cases targeted by continue 1258 // If the switch statement has no switch cases targeted by continue
1262 // statements we encode the switch statement directly. 1259 // statements we encode the switch statement directly.
1263 _buildSimpleSwitchStatement(switchStatement, jumpHandler, constants); 1260 _buildSimpleSwitchStatement(switchStatement, jumpHandler);
1264 } else { 1261 } else {
1265 throw 'Complex switch statement with continue label not implemented yet.'; 1262 _buildComplexSwitchStatement(switchStatement, jumpHandler,
1263 caseIndex, hasDefault);
1266 } 1264 }
1267 } 1265 }
1268 1266
1269 /// Helper for building switch statements. 1267 /// Shared helper for building switch statements.
1270 static bool _isDefaultCase(ir.SwitchCase switchCase) => 1268 static bool _isDefaultCase(ir.SwitchCase switchCase) =>
1271 switchCase == null || switchCase.isDefault; 1269 switchCase == null || switchCase.isDefault;
1272 1270
1271 /// Shared helper for building switch statements.
1272 HInstruction _buildExpression(ir.SwitchStatement switchStatement) {
1273 switchStatement.expression.accept(this);
1274 return pop();
1275 }
1276
1277 /// Shared helper method for creating the list of constants that make up the
sra1 2017/01/19 23:03:47 We could drop 'Shared'
Emily Fortuna 2017/01/19 23:19:01 Done.
1278 /// switch case branches.
1279 List<ConstantValue> _getSwitchConstants(
1280 ir.SwitchStatement parentSwitch, ir.SwitchCase switchCase) {
1281 Map<ir.Expression, ConstantValue> constantsLookup =
1282 _buildSwitchCaseConstants(parentSwitch);
1283 List<ConstantValue> constantList = <ConstantValue>[];
1284 if (switchCase != null) {
1285 for (var expression in switchCase.expressions) {
1286 constantList.add(constantsLookup[expression]);
1287 }
1288 }
1289 return constantList;
1290 }
1291
1273 /// Builds a simple switch statement which does not handle uses of continue 1292 /// Builds a simple switch statement which does not handle uses of continue
1274 /// statements to labeled switch cases. 1293 /// statements to labeled switch cases.
1275 void _buildSimpleSwitchStatement(ir.SwitchStatement switchStatement, 1294 void _buildSimpleSwitchStatement(ir.SwitchStatement switchStatement,
1276 JumpHandler jumpHandler, Map<ir.Expression, ConstantValue> constants) { 1295 JumpHandler jumpHandler) {
1277 void buildSwitchCase(ir.SwitchCase switchCase) { 1296 void buildSwitchCase(ir.SwitchCase switchCase) {
1278 switchCase.body.accept(this); 1297 switchCase.body.accept(this);
1279 } 1298 }
1280 1299
1281 handleSwitch(switchStatement, jumpHandler, switchStatement.cases, 1300 _handleSwitch(switchStatement, jumpHandler, _buildExpression,
1282 _isDefaultCase, buildSwitchCase, constants); 1301 switchStatement.cases, _getSwitchConstants, _isDefaultCase,
1302 buildSwitchCase);
1283 jumpHandler.close(); 1303 jumpHandler.close();
1284 } 1304 }
1285 1305
1306 /// Builds a switch statement that can handle arbitrary uses of continue
1307 /// statements to labeled switch cases.
1308 void _buildComplexSwitchStatement(
1309 ir.SwitchStatement switchStatement,
1310 JumpHandler jumpHandler,
1311 Map<ir.SwitchCase, int> caseIndex,
1312 bool hasDefault) {
1313 // If the switch statement has switch cases targeted by continue
1314 // statements we create the following encoding:
1315 //
1316 // switch (e) {
1317 // l_1: case e0: s_1; break;
1318 // l_2: case e1: s_2; continue l_i;
1319 // ...
1320 // l_n: default: s_n; continue l_j;
1321 // }
1322 //
1323 // is encoded as
1324 //
1325 // var target;
1326 // switch (e) {
1327 // case e1: target = 1; break;
1328 // case e2: target = 2; break;
1329 // ...
1330 // default: target = n; break;
1331 // }
1332 // l: while (true) {
1333 // switch (target) {
1334 // case 1: s_1; break l;
1335 // case 2: s_2; target = i; continue l;
1336 // ...
1337 // case n: s_n; target = j; continue l;
1338 // }
1339 // }
1340 //
1341 // This is because JS does not have this same "continue label" semantics so
1342 // we encode it in the form of a state machine.
1343
1344 JumpTarget switchTarget = astAdapter.getJumpTarget(switchStatement.parent);
1345 localsHandler.updateLocal(switchTarget, graph.addConstantNull(closedWorld));
1346
1347 var switchCases = switchStatement.cases;
1348 if (!hasDefault) {
1349 // Use null as the marker for a synthetic default clause.
1350 // The synthetic default is added because otherwise there would be no
1351 // good place to give a default value to the local.
1352 switchCases = new List<ir.SwitchCase>.from(switchCases);
1353 switchCases.add(null);
1354 }
1355
1356 void buildSwitchCase(ir.SwitchCase switchCase) {
1357 if (switchCase != null) {
1358 // Generate 'target = i; break;' for switch case i.
1359 int index = caseIndex[switchCase];
1360 HInstruction value = graph.addConstantInt(index, closedWorld);
1361 localsHandler.updateLocal(switchTarget, value);
1362 } else {
1363 // Generate synthetic default case 'target = null; break;'.
1364 HInstruction nullValue = graph.addConstantNull(closedWorld);
1365 localsHandler.updateLocal(switchTarget, nullValue);
1366 }
1367 jumpTargets[switchTarget].generateBreak();
1368 }
1369
1370 _handleSwitch(switchStatement, jumpHandler, _buildExpression, switchCases,
1371 _getSwitchConstants, _isDefaultCase, buildSwitchCase);
1372 jumpHandler.close();
1373
1374 HInstruction buildCondition() => graph.addConstantBool(true, closedWorld);
1375
1376 void buildSwitch() {
1377 HInstruction buildExpression(ir.SwitchStatement notUsed) {
1378 return localsHandler.readLocal(switchTarget);
1379 }
1380
1381 List<ConstantValue> getConstants(ir.SwitchStatement parentSwitch,
1382 ir.SwitchCase switchCase) {
1383 return <ConstantValue>[backend.constantSystem.createInt(
1384 caseIndex[switchCase])];
1385 }
1386
1387 void buildSwitchCase(ir.SwitchCase switchCase) {
1388 switchCase.body.accept(this);
1389 if (!isAborted()) {
1390 // Ensure that we break the loop if the case falls through. (This
1391 // is only possible for the last case.)
1392 jumpTargets[switchTarget].generateBreak();
1393 }
1394 }
1395
1396 // Pass a [NullJumpHandler] because the target for the contained break
1397 // is not the generated switch statement but instead the loop generated
1398 // in the call to [handleLoop] below.
1399 _handleSwitch(
1400 switchStatement, // nor is buildExpression.
1401 new NullJumpHandler(compiler.reporter),
1402 buildExpression,
1403 switchStatement.cases,
1404 getConstants,
1405 (_) => false, // No case is default.
1406 buildSwitchCase);
1407 }
1408
1409 void buildLoop() {
1410 loopHandler.handleLoop(switchStatement, () {}, buildCondition, () {},
1411 buildSwitch);
1412 }
1413
1414 if (hasDefault) {
1415 buildLoop();
1416 } else {
1417 // If the switch statement has no default case, surround the loop with
1418 // a test of the target.
1419 void buildCondition() {
1420 js.Template code = js.js.parseForeignJS('#');
1421 push(new HForeignCode(
1422 code, commonMasks.boolType, [localsHandler.readLocal(switchTarget)],
1423 nativeBehavior: native.NativeBehavior.PURE));
sra1 2017/01/19 23:03:47 So are we using a HForeignCode to pretend the `int
Emily Fortuna 2017/01/19 23:19:01 yup. that's what's happening. it's rather gross. a
1424 }
1425
1426 handleIf(
1427 node: switchStatement,
1428 visitCondition: buildCondition,
1429 visitThen: buildLoop,
1430 visitElse: () => {});
1431 }
1432 }
1433
1286 /// Creates a switch statement. 1434 /// Creates a switch statement.
1287 /// 1435 ///
1288 /// [jumpHandler] is the [JumpHandler] for the created switch statement. 1436 /// [jumpHandler] is the [JumpHandler] for the created switch statement.
1289 /// [buildSwitchCase] creates the statements for the switch case. 1437 /// [buildSwitchCase] creates the statements for the switch case.
1290 void handleSwitch( 1438 void _handleSwitch(
1291 ir.SwitchStatement switchStatement, 1439 ir.SwitchStatement switchStatement,
1292 JumpHandler jumpHandler, 1440 JumpHandler jumpHandler,
1441 HInstruction buildExpression(ir.SwitchStatement statement),
1293 List<ir.SwitchCase> switchCases, 1442 List<ir.SwitchCase> switchCases,
1443 List<ConstantValue> getConstants(
1444 ir.SwitchStatement parentSwitch, ir.SwitchCase switchCase),
1294 bool isDefaultCase(ir.SwitchCase switchCase), 1445 bool isDefaultCase(ir.SwitchCase switchCase),
1295 void buildSwitchCase(ir.SwitchCase switchCase), 1446 void buildSwitchCase(ir.SwitchCase switchCase)) {
1296 Map<ir.Expression, ConstantValue> constantsLookup) {
1297 HBasicBlock expressionStart = openNewBlock(); 1447 HBasicBlock expressionStart = openNewBlock();
1298 switchStatement.expression.accept(this); 1448 HInstruction expression = buildExpression(switchStatement);
1299 HInstruction expression = pop();
1300
1301 List<ConstantValue> getConstants(ir.SwitchCase switchCase) {
1302 List<ConstantValue> constantList = <ConstantValue>[];
1303 if (switchCase != null) {
1304 for (var expression in switchCase.expressions) {
1305 constantList.add(constantsLookup[expression]);
1306 }
1307 }
1308 return constantList;
1309 }
1310 1449
1311 if (switchCases.isEmpty) { 1450 if (switchCases.isEmpty) {
1312 return; 1451 return;
1313 } 1452 }
1314 1453
1315 HSwitch switchInstruction = new HSwitch(<HInstruction>[expression]); 1454 HSwitch switchInstruction = new HSwitch(<HInstruction>[expression]);
1316 HBasicBlock expressionEnd = close(switchInstruction); 1455 HBasicBlock expressionEnd = close(switchInstruction);
1317 LocalsHandler savedLocals = localsHandler; 1456 LocalsHandler savedLocals = localsHandler;
1318 1457
1319 List<HStatementInformation> statements = <HStatementInformation>[]; 1458 List<HStatementInformation> statements = <HStatementInformation>[];
1320 bool hasDefault = false; 1459 bool hasDefault = false;
1321 for (ir.SwitchCase switchCase in switchCases) { 1460 for (ir.SwitchCase switchCase in switchCases) {
1322 HBasicBlock block = graph.addNewBlock(); 1461 HBasicBlock block = graph.addNewBlock();
1323 for (ConstantValue constant in getConstants(switchCase)) { 1462 for (ConstantValue constant in getConstants(
1463 switchStatement, switchCase)) {
1324 HConstant hConstant = graph.addConstant(constant, closedWorld); 1464 HConstant hConstant = graph.addConstant(constant, closedWorld);
1325 switchInstruction.inputs.add(hConstant); 1465 switchInstruction.inputs.add(hConstant);
1326 hConstant.usedBy.add(switchInstruction); 1466 hConstant.usedBy.add(switchInstruction);
1327 expressionEnd.addSuccessor(block); 1467 expressionEnd.addSuccessor(block);
1328 } 1468 }
1329 1469
1330 if (isDefaultCase(switchCase)) { 1470 if (isDefaultCase(switchCase)) {
1331 // An HSwitch has n inputs and n+1 successors, the last being the 1471 // An HSwitch has n inputs and n+1 successors, the last being the
1332 // default case. 1472 // default case.
1333 expressionEnd.addSuccessor(block); 1473 expressionEnd.addSuccessor(block);
(...skipping 1545 matching lines...) Expand 10 before | Expand all | Expand 10 after
2879 enterBlock.setBlockFlow( 3019 enterBlock.setBlockFlow(
2880 new HTryBlockInformation( 3020 new HTryBlockInformation(
2881 kernelBuilder.wrapStatementGraph(bodyGraph), 3021 kernelBuilder.wrapStatementGraph(bodyGraph),
2882 exception, 3022 exception,
2883 kernelBuilder.wrapStatementGraph(catchGraph), 3023 kernelBuilder.wrapStatementGraph(catchGraph),
2884 kernelBuilder.wrapStatementGraph(finallyGraph)), 3024 kernelBuilder.wrapStatementGraph(finallyGraph)),
2885 exitBlock); 3025 exitBlock);
2886 kernelBuilder.inTryStatement = previouslyInTryStatement; 3026 kernelBuilder.inTryStatement = previouslyInTryStatement;
2887 } 3027 }
2888 } 3028 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698