Chromium Code Reviews| 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(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 Loading... | |
| 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 } |
| OLD | NEW |