| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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( |
| 1263 switchStatement, jumpHandler, caseIndex, hasDefault); |
| 1266 } | 1264 } |
| 1267 } | 1265 } |
| 1268 | 1266 |
| 1269 /// Helper for building switch statements. | 1267 /// 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 /// Helper for building switch statements. |
| 1272 HInstruction _buildExpression(ir.SwitchStatement switchStatement) { |
| 1273 switchStatement.expression.accept(this); |
| 1274 return pop(); |
| 1275 } |
| 1276 |
| 1277 /// Helper method for creating the list of constants that make up the |
| 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( |
| 1276 JumpHandler jumpHandler, Map<ir.Expression, ConstantValue> constants) { | 1295 ir.SwitchStatement switchStatement, 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( |
| 1282 _isDefaultCase, buildSwitchCase, constants); | 1301 switchStatement, |
| 1302 jumpHandler, |
| 1303 _buildExpression, |
| 1304 switchStatement.cases, |
| 1305 _getSwitchConstants, |
| 1306 _isDefaultCase, |
| 1307 buildSwitchCase); |
| 1283 jumpHandler.close(); | 1308 jumpHandler.close(); |
| 1284 } | 1309 } |
| 1285 | 1310 |
| 1311 /// Builds a switch statement that can handle arbitrary uses of continue |
| 1312 /// statements to labeled switch cases. |
| 1313 void _buildComplexSwitchStatement( |
| 1314 ir.SwitchStatement switchStatement, |
| 1315 JumpHandler jumpHandler, |
| 1316 Map<ir.SwitchCase, int> caseIndex, |
| 1317 bool hasDefault) { |
| 1318 // If the switch statement has switch cases targeted by continue |
| 1319 // statements we create the following encoding: |
| 1320 // |
| 1321 // switch (e) { |
| 1322 // l_1: case e0: s_1; break; |
| 1323 // l_2: case e1: s_2; continue l_i; |
| 1324 // ... |
| 1325 // l_n: default: s_n; continue l_j; |
| 1326 // } |
| 1327 // |
| 1328 // is encoded as |
| 1329 // |
| 1330 // var target; |
| 1331 // switch (e) { |
| 1332 // case e1: target = 1; break; |
| 1333 // case e2: target = 2; break; |
| 1334 // ... |
| 1335 // default: target = n; break; |
| 1336 // } |
| 1337 // l: while (true) { |
| 1338 // switch (target) { |
| 1339 // case 1: s_1; break l; |
| 1340 // case 2: s_2; target = i; continue l; |
| 1341 // ... |
| 1342 // case n: s_n; target = j; continue l; |
| 1343 // } |
| 1344 // } |
| 1345 // |
| 1346 // This is because JS does not have this same "continue label" semantics so |
| 1347 // we encode it in the form of a state machine. |
| 1348 |
| 1349 JumpTarget switchTarget = astAdapter.getJumpTarget(switchStatement.parent); |
| 1350 localsHandler.updateLocal(switchTarget, graph.addConstantNull(closedWorld)); |
| 1351 |
| 1352 var switchCases = switchStatement.cases; |
| 1353 if (!hasDefault) { |
| 1354 // Use null as the marker for a synthetic default clause. |
| 1355 // The synthetic default is added because otherwise there would be no |
| 1356 // good place to give a default value to the local. |
| 1357 switchCases = new List<ir.SwitchCase>.from(switchCases); |
| 1358 switchCases.add(null); |
| 1359 } |
| 1360 |
| 1361 void buildSwitchCase(ir.SwitchCase switchCase) { |
| 1362 if (switchCase != null) { |
| 1363 // Generate 'target = i; break;' for switch case i. |
| 1364 int index = caseIndex[switchCase]; |
| 1365 HInstruction value = graph.addConstantInt(index, closedWorld); |
| 1366 localsHandler.updateLocal(switchTarget, value); |
| 1367 } else { |
| 1368 // Generate synthetic default case 'target = null; break;'. |
| 1369 HInstruction nullValue = graph.addConstantNull(closedWorld); |
| 1370 localsHandler.updateLocal(switchTarget, nullValue); |
| 1371 } |
| 1372 jumpTargets[switchTarget].generateBreak(); |
| 1373 } |
| 1374 |
| 1375 _handleSwitch(switchStatement, jumpHandler, _buildExpression, switchCases, |
| 1376 _getSwitchConstants, _isDefaultCase, buildSwitchCase); |
| 1377 jumpHandler.close(); |
| 1378 |
| 1379 HInstruction buildCondition() => graph.addConstantBool(true, closedWorld); |
| 1380 |
| 1381 void buildSwitch() { |
| 1382 HInstruction buildExpression(ir.SwitchStatement notUsed) { |
| 1383 return localsHandler.readLocal(switchTarget); |
| 1384 } |
| 1385 |
| 1386 List<ConstantValue> getConstants( |
| 1387 ir.SwitchStatement parentSwitch, ir.SwitchCase switchCase) { |
| 1388 return <ConstantValue>[ |
| 1389 backend.constantSystem.createInt(caseIndex[switchCase]) |
| 1390 ]; |
| 1391 } |
| 1392 |
| 1393 void buildSwitchCase(ir.SwitchCase switchCase) { |
| 1394 switchCase.body.accept(this); |
| 1395 if (!isAborted()) { |
| 1396 // Ensure that we break the loop if the case falls through. (This |
| 1397 // is only possible for the last case.) |
| 1398 jumpTargets[switchTarget].generateBreak(); |
| 1399 } |
| 1400 } |
| 1401 |
| 1402 // Pass a [NullJumpHandler] because the target for the contained break |
| 1403 // is not the generated switch statement but instead the loop generated |
| 1404 // in the call to [handleLoop] below. |
| 1405 _handleSwitch( |
| 1406 switchStatement, // nor is buildExpression. |
| 1407 new NullJumpHandler(compiler.reporter), |
| 1408 buildExpression, |
| 1409 switchStatement.cases, |
| 1410 getConstants, |
| 1411 (_) => false, // No case is default. |
| 1412 buildSwitchCase); |
| 1413 } |
| 1414 |
| 1415 void buildLoop() { |
| 1416 loopHandler.handleLoop( |
| 1417 switchStatement, () {}, buildCondition, () {}, buildSwitch); |
| 1418 } |
| 1419 |
| 1420 if (hasDefault) { |
| 1421 buildLoop(); |
| 1422 } else { |
| 1423 // If the switch statement has no default case, surround the loop with |
| 1424 // a test of the target. So: |
| 1425 // `if (target) while (true) ...` If there's no default case, target is |
| 1426 // null, so we don't drop into the while loop. |
| 1427 void buildCondition() { |
| 1428 js.Template code = js.js.parseForeignJS('#'); |
| 1429 push(new HForeignCode( |
| 1430 code, commonMasks.boolType, [localsHandler.readLocal(switchTarget)], |
| 1431 nativeBehavior: native.NativeBehavior.PURE)); |
| 1432 } |
| 1433 |
| 1434 handleIf( |
| 1435 node: switchStatement, |
| 1436 visitCondition: buildCondition, |
| 1437 visitThen: buildLoop, |
| 1438 visitElse: () => {}); |
| 1439 } |
| 1440 } |
| 1441 |
| 1286 /// Creates a switch statement. | 1442 /// Creates a switch statement. |
| 1287 /// | 1443 /// |
| 1288 /// [jumpHandler] is the [JumpHandler] for the created switch statement. | 1444 /// [jumpHandler] is the [JumpHandler] for the created switch statement. |
| 1289 /// [buildSwitchCase] creates the statements for the switch case. | 1445 /// [buildSwitchCase] creates the statements for the switch case. |
| 1290 void handleSwitch( | 1446 void _handleSwitch( |
| 1291 ir.SwitchStatement switchStatement, | 1447 ir.SwitchStatement switchStatement, |
| 1292 JumpHandler jumpHandler, | 1448 JumpHandler jumpHandler, |
| 1449 HInstruction buildExpression(ir.SwitchStatement statement), |
| 1293 List<ir.SwitchCase> switchCases, | 1450 List<ir.SwitchCase> switchCases, |
| 1451 List<ConstantValue> getConstants( |
| 1452 ir.SwitchStatement parentSwitch, ir.SwitchCase switchCase), |
| 1294 bool isDefaultCase(ir.SwitchCase switchCase), | 1453 bool isDefaultCase(ir.SwitchCase switchCase), |
| 1295 void buildSwitchCase(ir.SwitchCase switchCase), | 1454 void buildSwitchCase(ir.SwitchCase switchCase)) { |
| 1296 Map<ir.Expression, ConstantValue> constantsLookup) { | |
| 1297 HBasicBlock expressionStart = openNewBlock(); | 1455 HBasicBlock expressionStart = openNewBlock(); |
| 1298 switchStatement.expression.accept(this); | 1456 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 | 1457 |
| 1311 if (switchCases.isEmpty) { | 1458 if (switchCases.isEmpty) { |
| 1312 return; | 1459 return; |
| 1313 } | 1460 } |
| 1314 | 1461 |
| 1315 HSwitch switchInstruction = new HSwitch(<HInstruction>[expression]); | 1462 HSwitch switchInstruction = new HSwitch(<HInstruction>[expression]); |
| 1316 HBasicBlock expressionEnd = close(switchInstruction); | 1463 HBasicBlock expressionEnd = close(switchInstruction); |
| 1317 LocalsHandler savedLocals = localsHandler; | 1464 LocalsHandler savedLocals = localsHandler; |
| 1318 | 1465 |
| 1319 List<HStatementInformation> statements = <HStatementInformation>[]; | 1466 List<HStatementInformation> statements = <HStatementInformation>[]; |
| 1320 bool hasDefault = false; | 1467 bool hasDefault = false; |
| 1321 for (ir.SwitchCase switchCase in switchCases) { | 1468 for (ir.SwitchCase switchCase in switchCases) { |
| 1322 HBasicBlock block = graph.addNewBlock(); | 1469 HBasicBlock block = graph.addNewBlock(); |
| 1323 for (ConstantValue constant in getConstants(switchCase)) { | 1470 for (ConstantValue constant |
| 1471 in getConstants(switchStatement, switchCase)) { |
| 1324 HConstant hConstant = graph.addConstant(constant, closedWorld); | 1472 HConstant hConstant = graph.addConstant(constant, closedWorld); |
| 1325 switchInstruction.inputs.add(hConstant); | 1473 switchInstruction.inputs.add(hConstant); |
| 1326 hConstant.usedBy.add(switchInstruction); | 1474 hConstant.usedBy.add(switchInstruction); |
| 1327 expressionEnd.addSuccessor(block); | 1475 expressionEnd.addSuccessor(block); |
| 1328 } | 1476 } |
| 1329 | 1477 |
| 1330 if (isDefaultCase(switchCase)) { | 1478 if (isDefaultCase(switchCase)) { |
| 1331 // An HSwitch has n inputs and n+1 successors, the last being the | 1479 // An HSwitch has n inputs and n+1 successors, the last being the |
| 1332 // default case. | 1480 // default case. |
| 1333 expressionEnd.addSuccessor(block); | 1481 expressionEnd.addSuccessor(block); |
| (...skipping 1545 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2879 enterBlock.setBlockFlow( | 3027 enterBlock.setBlockFlow( |
| 2880 new HTryBlockInformation( | 3028 new HTryBlockInformation( |
| 2881 kernelBuilder.wrapStatementGraph(bodyGraph), | 3029 kernelBuilder.wrapStatementGraph(bodyGraph), |
| 2882 exception, | 3030 exception, |
| 2883 kernelBuilder.wrapStatementGraph(catchGraph), | 3031 kernelBuilder.wrapStatementGraph(catchGraph), |
| 2884 kernelBuilder.wrapStatementGraph(finallyGraph)), | 3032 kernelBuilder.wrapStatementGraph(finallyGraph)), |
| 2885 exitBlock); | 3033 exitBlock); |
| 2886 kernelBuilder.inTryStatement = previouslyInTryStatement; | 3034 kernelBuilder.inTryStatement = previouslyInTryStatement; |
| 2887 } | 3035 } |
| 2888 } | 3036 } |
| OLD | NEW |