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

Side by Side Diff: pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart

Issue 1585503002: dart2js: CPS translation of switches with continue to their labels. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Rebase. Created 4 years, 10 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) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 library dart2js.ir_builder_task; 5 library dart2js.ir_builder_task;
6 6
7 import '../closure.dart' as closure; 7 import '../closure.dart' as closure;
8 import '../common.dart'; 8 import '../common.dart';
9 import '../common/names.dart' show 9 import '../common/names.dart' show
10 Names, 10 Names,
(...skipping 1154 matching lines...) Expand 10 before | Expand all | Expand 10 after
1165 irBuilder.buildRedirectingNativeFunctionBody(function, name, source); 1165 irBuilder.buildRedirectingNativeFunctionBody(function, name, source);
1166 } 1166 }
1167 } else { 1167 } else {
1168 irBuilder.buildReturn( 1168 irBuilder.buildReturn(
1169 value: build(node.expression), 1169 value: build(node.expression),
1170 sourceInformation: source); 1170 sourceInformation: source);
1171 } 1171 }
1172 } 1172 }
1173 1173
1174 visitSwitchStatement(ast.SwitchStatement node) { 1174 visitSwitchStatement(ast.SwitchStatement node) {
1175 // Dart switch cases can be labeled and be the target of continue from
1176 // within the switch. Such cases are 'recursive'. If there are any
1177 // recursive cases, we implement the switch using a pair of switches with
1178 // the second one switching over a state variable in a loop. The first
1179 // switch contains the non-recursive cases, and the second switch contains
1180 // the recursive ones.
1181 //
1182 // For example, for the Dart switch:
1183 //
1184 // switch (E) {
1185 // case 0:
1186 // BODY0;
1187 // break;
1188 // LABEL0: case 1:
1189 // BODY1;
1190 // break;
1191 // case 2:
1192 // BODY2;
1193 // continue LABEL1;
1194 // LABEL1: case 3:
1195 // BODY3;
1196 // continue LABEL0;
1197 // default:
1198 // BODY4;
1199 // }
1200 //
1201 // We translate it as if it were the JavaScript:
1202 //
1203 // var state = -1;
1204 // switch (E) {
1205 // case 0:
1206 // BODY0;
1207 // break;
1208 // case 1:
1209 // state = 0; // Recursive, label ID = 0.
1210 // break;
1211 // case 2:
1212 // BODY2;
1213 // state = 1; // Continue to label ID = 1.
1214 // break;
1215 // case 3:
1216 // state = 1; // Recursive, label ID = 1.
1217 // break;
1218 // default:
1219 // BODY4;
1220 // }
1221 // L: while (state != -1) {
1222 // case 0:
1223 // BODY1;
1224 // break L; // Break from switch becomes break from loop.
1225 // case 1:
1226 // BODY2;
1227 // state = 0; // Continue to label ID = 0.
1228 // break;
1229 // }
1175 assert(irBuilder.isOpen); 1230 assert(irBuilder.isOpen);
1176 // We do not handle switch statements with continue to labeled cases. 1231 // Preprocess: compute a list of cases that are the target of continue.
1177 for (ast.SwitchCase switchCase in node.cases) { 1232 // These are the so-called 'recursive' cases.
1233 List<JumpTarget> continueTargets = <JumpTarget>[];
1234 List<ast.SwitchCase> switchCases = node.cases.nodes.toList();
1235 for (ast.SwitchCase switchCase in switchCases) {
1178 for (ast.Node labelOrCase in switchCase.labelsAndCases) { 1236 for (ast.Node labelOrCase in switchCase.labelsAndCases) {
1179 if (labelOrCase is ast.Label) { 1237 if (labelOrCase is ast.Label) {
1180 LabelDefinition definition = elements.getLabelDefinition(labelOrCase); 1238 LabelDefinition definition = elements.getLabelDefinition(labelOrCase);
1181 if (definition != null && definition.isContinueTarget) { 1239 if (definition != null && definition.isContinueTarget) {
1182 return giveup(node, "continue to a labeled switch case"); 1240 continueTargets.add(definition.target);
1183 } 1241 }
1184 } 1242 }
1185 } 1243 }
1186 } 1244 }
1187 1245
1188 // Each switch case contains a list of interleaved labels and expressions 1246 // If any cases are continue targets, use an anonymous local value to
1189 // and a non-empty body. We can ignore the labels because they are not 1247 // implement a state machine. The initial value is -1.
1190 // jump targets. 1248 ir.Primitive initial;
1249 int stateIndex;
1250 if (continueTargets.isNotEmpty) {
1251 initial = irBuilder.buildIntegerConstant(-1);
1252 stateIndex = irBuilder.environment.length;
1253 irBuilder.environment.extend(null, initial);
1254 }
1255
1256 // Use a simple switch for the non-recursive cases. A break will go to the
1257 // join-point after the switch. A continue to a labeled case will assign
1258 // to the state variable and go to the join-point.
1259 ir.Primitive value = visit(node.expression);
1260 JumpCollector join = new ForwardJumpCollector(irBuilder.environment,
1261 target: elements.getTargetDefinition(node));
1262 irBuilder.state.breakCollectors.add(join);
1263 for (int i = 0; i < continueTargets.length; ++i) {
1264 // The state value is i, the case's position in the list of recursive
1265 // cases.
1266 irBuilder.state.continueCollectors.add(new GotoJumpCollector(
1267 continueTargets[i], stateIndex, i, join));
1268 }
1269
1270 // For each non-default case use a pair of functions, one to translate the
1271 // condition and one to translate the body. For the default case use a
1272 // function to translate the body. Use continueTargetIterator as a pointer
1273 // to the next recursive case.
1274 Iterator<JumpTarget> continueTargetIterator = continueTargets.iterator;
1275 continueTargetIterator.moveNext();
1191 List<SwitchCaseInfo> cases = <SwitchCaseInfo>[]; 1276 List<SwitchCaseInfo> cases = <SwitchCaseInfo>[];
1192 SwitchCaseInfo defaultCase; 1277 SubbuildFunction buildDefaultBody;
1193 for (ast.SwitchCase switchCase in node.cases) { 1278 for (ast.SwitchCase switchCase in switchCases) {
1194 SwitchCaseInfo caseInfo = 1279 JumpTarget nextContinueTarget = continueTargetIterator.current;
1195 new SwitchCaseInfo(subbuildSequence(switchCase.statements));
1196 if (switchCase.isDefaultCase) { 1280 if (switchCase.isDefaultCase) {
1197 defaultCase = caseInfo; 1281 if (nextContinueTarget != null &&
1282 switchCase == nextContinueTarget.statement) {
1283 // In this simple switch, recursive cases are as if they immediately
1284 // continued to themselves.
1285 buildDefaultBody = nested(() {
1286 irBuilder.buildContinue(nextContinueTarget);
1287 });
1288 continueTargetIterator.moveNext();
1289 } else {
1290 // Non-recursive cases consist of the translation of the body.
1291 // For the default case, there is implicitly a break if control
1292 // flow reaches the end.
1293 buildDefaultBody = nested(() {
1294 irBuilder.buildSequence(switchCase.statements, visit);
1295 if (irBuilder.isOpen) irBuilder.jumpTo(join);
1296 });
1297 }
1298 continue;
1299 }
1300
1301 ir.Primitive buildCondition(IrBuilder builder) {
1302 // There can be multiple cases sharing the same body, because empty
1303 // cases are allowed to fall through to the next one. Each case is
1304 // a comparison, build a short-circuited disjunction of all of them.
1305 return withBuilder(builder, () {
1306 ir.Primitive condition;
1307 for (ast.Node labelOrCase in switchCase.labelsAndCases) {
1308 if (labelOrCase is ast.CaseMatch) {
1309 ir.Primitive buildComparison() {
1310 ir.Primitive constant =
1311 translateConstant(labelOrCase.expression);
1312 return irBuilder.buildIdentical(value, constant);
1313 }
1314
1315 if (condition == null) {
1316 condition = buildComparison();
1317 } else {
1318 condition = irBuilder.buildLogicalOperator(condition,
1319 nested(buildComparison), isLazyOr: true);
1320 }
1321 }
1322 }
1323 return condition;
1324 });
1325 }
1326
1327 SubbuildFunction buildBody;
1328 if (nextContinueTarget != null &&
1329 switchCase == nextContinueTarget.statement) {
1330 // Recursive cases are as if they immediately continued to themselves.
1331 buildBody = nested(() {
1332 irBuilder.buildContinue(nextContinueTarget);
1333 });
1334 continueTargetIterator.moveNext();
1198 } else { 1335 } else {
1199 cases.add(caseInfo); 1336 // Non-recursive cases consist of the translation of the body. It is a
1200 for (ast.Node labelOrCase in switchCase.labelsAndCases) { 1337 // runtime error if control-flow reaches the end of the body of any but
1201 if (labelOrCase is ast.CaseMatch) { 1338 // the last case.
1202 ir.Primitive constant = translateConstant(labelOrCase.expression); 1339 buildBody = (IrBuilder builder) {
1203 caseInfo.addConstant(constant); 1340 withBuilder(builder, () {
1341 irBuilder.buildSequence(switchCase.statements, visit);
1342 if (irBuilder.isOpen) {
1343 if (switchCase == switchCases.last) {
1344 irBuilder.jumpTo(join);
1345 } else {
1346 Element error = helpers.fallThroughError;
1347 ir.Primitive exception = irBuilder.buildInvokeStatic(
1348 error,
1349 new Selector.fromElement(error),
1350 <ir.Primitive>[],
1351 sourceInformationBuilder.buildGeneric(node));
1352 irBuilder.buildThrow(exception);
1353 }
1354 }
1355 });
1356 return null;
1357 };
1358 }
1359
1360 cases.add(new SwitchCaseInfo(buildCondition, buildBody));
1361 }
1362
1363 irBuilder.buildSimpleSwitch(join, cases, buildDefaultBody);
1364 irBuilder.state.breakCollectors.removeLast();
1365 irBuilder.state.continueCollectors.length -= continueTargets.length;
1366 if (continueTargets.isEmpty) return;
1367
1368 // If there were recursive cases build a while loop whose body is a
1369 // switch containing (only) the recursive cases. The condition is
1370 // 'state != initialValue' so the loop is not taken when the state variable
1371 // has not been assigned.
1372 //
1373 // 'loop' is the join-point of the exits from the inner switch which will
1374 // perform another iteration of the loop. 'exit' is the join-point of the
1375 // breaks from the switch, outside the loop.
1376 JumpCollector loop = new ForwardJumpCollector(irBuilder.environment);
1377 JumpCollector exit = new ForwardJumpCollector(irBuilder.environment,
1378 target: elements.getTargetDefinition(node));
1379 irBuilder.state.breakCollectors.add(exit);
1380 for (int i = 0; i < continueTargets.length; ++i) {
1381 irBuilder.state.continueCollectors.add(new GotoJumpCollector(
1382 continueTargets[i], stateIndex, i, loop));
1383 }
1384 cases.clear();
1385 for (int i = 0; i < continueTargets.length; ++i) {
1386 // The conditions compare to the recursive case index.
1387 ir.Primitive buildCondition(IrBuilder builder) {
1388 ir.Primitive constant = builder.buildIntegerConstant(i);
1389 return builder.buildIdentical(
1390 builder.environment.index2value[stateIndex], constant);
1391 }
1392
1393 ir.Primitive buildBody(IrBuilder builder) {
1394 withBuilder(builder, () {
1395 ast.SwitchCase switchCase = continueTargets[i].statement;
1396 irBuilder.buildSequence(switchCase.statements, visit);
1397 if (irBuilder.isOpen) {
1398 if (switchCase == switchCases.last) {
1399 irBuilder.jumpTo(exit);
1400 } else {
1401 Element error = helpers.fallThroughError;
1402 ir.Primitive exception = irBuilder.buildInvokeStatic(
1403 error,
1404 new Selector.fromElement(error),
1405 <ir.Primitive>[],
1406 sourceInformationBuilder.buildGeneric(node));
1407 irBuilder.buildThrow(exception);
1408 }
1204 } 1409 }
1205 } 1410 });
1206 } 1411 return null;
1207 } 1412 }
1208 ir.Primitive value = visit(node.expression); 1413
1209 JumpTarget target = elements.getTargetDefinition(node); 1414 cases.add(new SwitchCaseInfo(buildCondition, buildBody));
1210 Element error = helpers.fallThroughError; 1415 }
1211 irBuilder.buildSimpleSwitch(target, value, cases, defaultCase, error, 1416
1212 sourceInformationBuilder.buildGeneric(node)); 1417 // A loop with a simple switch in the body.
1418 IrBuilder whileBuilder = irBuilder.makeDelimitedBuilder();
1419 whileBuilder.buildWhile(
1420 buildCondition: (IrBuilder builder) {
1421 ir.Primitive condition = builder.buildIdentical(
1422 builder.environment.index2value[stateIndex], initial);
1423 return builder.buildNegation(condition);
1424 },
1425 buildBody: (IrBuilder builder) {
1426 builder.buildSimpleSwitch(loop, cases, null);
1427 });
1428 // Jump to the exit continuation. This jump is the body of the loop exit
1429 // continuation, so the loop exit continuation can be eta-reduced. The
1430 // jump is here for simplicity because `buildWhile` does not expose the
1431 // loop's exit continuation directly and has already emitted all jumps
1432 // to it anyway.
1433 whileBuilder.jumpTo(exit);
1434 irBuilder.add(new ir.LetCont(exit.continuation, whileBuilder.root));
1435 irBuilder.environment = exit.environment;
1436 irBuilder.environment.discard(1); // Discard the state variable.
1437 irBuilder.state.breakCollectors.removeLast();
1438 irBuilder.state.continueCollectors.length -= continueTargets.length;
1213 } 1439 }
1214 1440
1215 visitTryStatement(ast.TryStatement node) { 1441 visitTryStatement(ast.TryStatement node) {
1216 List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[]; 1442 List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[];
1217 for (ast.CatchBlock catchClause in node.catchBlocks.nodes) { 1443 for (ast.CatchBlock catchClause in node.catchBlocks.nodes) {
1218 LocalVariableElement exceptionVariable; 1444 LocalVariableElement exceptionVariable;
1219 if (catchClause.exception != null) { 1445 if (catchClause.exception != null) {
1220 exceptionVariable = elements[catchClause.exception]; 1446 exceptionVariable = elements[catchClause.exception];
1221 } 1447 }
1222 LocalVariableElement stackTraceVariable; 1448 LocalVariableElement stackTraceVariable;
(...skipping 2366 matching lines...) Expand 10 before | Expand all | Expand 10 after
3589 } 3815 }
3590 3816
3591 Element get closureConverter { 3817 Element get closureConverter {
3592 return _backend.helpers.closureConverter; 3818 return _backend.helpers.closureConverter;
3593 } 3819 }
3594 3820
3595 void addNativeMethod(FunctionElement function) { 3821 void addNativeMethod(FunctionElement function) {
3596 _backend.emitter.nativeEmitter.nativeMethods.add(function); 3822 _backend.emitter.nativeEmitter.nativeMethods.add(function);
3597 } 3823 }
3598 } 3824 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart ('k') | pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698