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

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: Update test expectations. Created 4 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) 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 1155 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 assert(irBuilder.isOpen); 1175 assert(irBuilder.isOpen);
1176 // We do not handle switch statements with continue to labeled cases. 1176 // Preprocess: compute a list of cases that are the target of continue.
1177 for (ast.SwitchCase switchCase in node.cases) { 1177 // These are the so-called 'recursive' cases.
1178 List<JumpTarget> continueTargets = <JumpTarget>[];
1179 List<ast.SwitchCase> switchCases = node.cases.nodes.toList();
1180 for (ast.SwitchCase switchCase in switchCases) {
1178 for (ast.Node labelOrCase in switchCase.labelsAndCases) { 1181 for (ast.Node labelOrCase in switchCase.labelsAndCases) {
1179 if (labelOrCase is ast.Label) { 1182 if (labelOrCase is ast.Label) {
1180 LabelDefinition definition = elements.getLabelDefinition(labelOrCase); 1183 LabelDefinition definition = elements.getLabelDefinition(labelOrCase);
1181 if (definition != null && definition.isContinueTarget) { 1184 if (definition != null && definition.isContinueTarget) {
1182 return giveup(node, "continue to a labeled switch case"); 1185 continueTargets.add(definition.target);
1183 } 1186 }
1184 } 1187 }
1185 } 1188 }
1186 } 1189 }
1187 1190
1188 // Each switch case contains a list of interleaved labels and expressions 1191 // 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 1192 // implement a state machine. The initial value is -1.
1190 // jump targets. 1193 ir.Primitive initial;
1194 int stateIndex;
1195 if (continueTargets.isNotEmpty) {
1196 initial = irBuilder.buildIntegerConstant(-1);
1197 stateIndex = irBuilder.environment.length;
1198 irBuilder.environment.extend(null, initial);
1199 }
1200
1201 // Use a simple switch for the non-recursive cases. A break will go to the
1202 // join-point after the switch. A continue to a labeled case will assign
1203 // to the state variable and go to the join-point.
1204 ir.Primitive value = visit(node.expression);
1205 JumpCollector join = new ForwardJumpCollector(irBuilder.environment,
1206 target: elements.getTargetDefinition(node));
1207 irBuilder.state.breakCollectors.add(join);
1208 for (int i = 0; i < continueTargets.length; ++i) {
1209 // The state value is i, the case's position in the list of recursive
1210 // cases.
1211 irBuilder.state.continueCollectors.add(new GotoJumpCollector(
1212 continueTargets[i], stateIndex, i, join));
1213 }
1214
1215 // For each non-default case use a pair of functions, one to translate the
1216 // condition and one to translate the body. For the default case use a
1217 // function to translate the body. Use continueTargetIterator as a pointer
1218 // to the next recursive case.
1219 Iterator<JumpTarget> continueTargetIterator = continueTargets.iterator;
1220 continueTargetIterator.moveNext();
1191 List<SwitchCaseInfo> cases = <SwitchCaseInfo>[]; 1221 List<SwitchCaseInfo> cases = <SwitchCaseInfo>[];
1192 SwitchCaseInfo defaultCase; 1222 SubbuildFunction buildDefaultBody;
1193 for (ast.SwitchCase switchCase in node.cases) { 1223 for (ast.SwitchCase switchCase in switchCases) {
1194 SwitchCaseInfo caseInfo = 1224 JumpTarget nextContinueTarget = continueTargetIterator.current;
1195 new SwitchCaseInfo(subbuildSequence(switchCase.statements));
1196 if (switchCase.isDefaultCase) { 1225 if (switchCase.isDefaultCase) {
1197 defaultCase = caseInfo; 1226 if (nextContinueTarget != null &&
1227 switchCase == nextContinueTarget.statement) {
1228 // In this simple switch, recursive cases are as if they immediately
1229 // continued to themselves.
asgerf 2016/01/19 22:45:24 I don't understand what's going one here, and the
Kevin Millikin (Google) 2016/01/25 12:46:37 Sorry. I originally had such an example but I did
1230 buildDefaultBody = nested(() {
1231 irBuilder.buildContinue(nextContinueTarget);
1232 });
1233 continueTargetIterator.moveNext();
1234 } else {
1235 // Non-recursive cases consist of the translation of the body.
1236 // For the default case, there is implicitly a break if control
1237 // flow reaches the end.
1238 buildDefaultBody = nested(() {
1239 irBuilder.buildSequence(switchCase.statements, visit);
1240 if (irBuilder.isOpen) irBuilder.jumpTo(join);
1241 });
1242 }
1243 continue;
1244 }
1245
1246 ir.Primitive buildCondition(IrBuilder builder) {
1247 // There can be multiple cases sharing the same body, because empty
1248 // cases are allowed to fall through to the next one. Each case is
1249 // a comparison, build a short-circuited disjunction of all of them.
1250 return withBuilder(builder, () {
1251 ir.Primitive condition;
1252 for (ast.Node labelOrCase in switchCase.labelsAndCases) {
1253 if (labelOrCase is ast.CaseMatch) {
1254 ir.Primitive buildComparison() {
1255 ir.Primitive constant =
1256 translateConstant(labelOrCase.expression);
1257 return irBuilder.buildIdentical(value, constant);
1258 }
1259
1260 if (condition == null) {
1261 condition = buildComparison();
1262 } else {
1263 condition = irBuilder.buildLogicalOperator(condition,
1264 nested(buildComparison), isLazyOr: true);
1265 }
1266 }
1267 }
1268 return condition;
1269 });
1270 }
1271
1272 SubbuildFunction buildBody;
1273 if (nextContinueTarget != null &&
1274 switchCase == nextContinueTarget.statement) {
1275 // Recursive cases are as if they immediately continued to themselves.
asgerf 2016/01/19 22:45:24 Same as above, I just can't tell what the branch c
1276 buildBody = nested(() {
1277 irBuilder.buildContinue(nextContinueTarget);
1278 });
1279 continueTargetIterator.moveNext();
1198 } else { 1280 } else {
1199 cases.add(caseInfo); 1281 // Non-recursive cases consist of the translation of the body. It is a
1200 for (ast.Node labelOrCase in switchCase.labelsAndCases) { 1282 // runtime error if control-flow reaches the end of the body of any but
1201 if (labelOrCase is ast.CaseMatch) { 1283 // the last case.
1202 ir.Primitive constant = translateConstant(labelOrCase.expression); 1284 buildBody = (IrBuilder builder) {
1203 caseInfo.addConstant(constant); 1285 withBuilder(builder, () {
1286 irBuilder.buildSequence(switchCase.statements, visit);
1287 if (irBuilder.isOpen) {
1288 if (switchCase == switchCases.last) {
1289 irBuilder.jumpTo(join);
1290 } else {
1291 Element error = helpers.fallThroughError;
1292 ir.Primitive exception = irBuilder.buildInvokeStatic(
1293 error,
1294 new Selector.fromElement(error),
1295 <ir.Primitive>[],
1296 sourceInformationBuilder.buildGeneric(node));
1297 irBuilder.buildThrow(exception);
1298 }
1299 }
1300 });
1301 return null;
1302 };
1303 }
1304
1305 cases.add(new SwitchCaseInfo(buildCondition, buildBody));
1306 }
1307
1308 irBuilder.buildSimpleSwitch(join, cases, buildDefaultBody);
1309 irBuilder.state.breakCollectors.removeLast();
1310 irBuilder.state.continueCollectors.length -= continueTargets.length;
1311 if (continueTargets.isEmpty) return;
1312
1313 // If there were recursive cases build a while loop whose body is a
1314 // switch containing (only) the recursive cases. The condition is
1315 // 'state != initialValue' so the loop is not taken when the state variable
1316 // has not been assigned.
1317 //
1318 // 'loop' is the join-point of the exits from the inner switch which will
1319 // perform another iteration of the loop. 'exit' is the join-point of the
1320 // breaks from the switch, outside the loop.
1321 JumpCollector loop = new ForwardJumpCollector(irBuilder.environment);
1322 JumpCollector exit = new ForwardJumpCollector(irBuilder.environment,
1323 target: elements.getTargetDefinition(node));
1324 irBuilder.state.breakCollectors.add(exit);
1325 for (int i = 0; i < continueTargets.length; ++i) {
1326 irBuilder.state.continueCollectors.add(new GotoJumpCollector(
1327 continueTargets[i], stateIndex, i, loop));
1328 }
1329 cases.clear();
1330 for (int i = 0; i < continueTargets.length; ++i) {
1331 // The conditions compare to the recursive case index.
1332 ir.Primitive buildCondition(IrBuilder builder) {
1333 ir.Primitive constant = builder.buildIntegerConstant(i);
1334 return builder.buildIdentical(
1335 builder.environment.index2value[stateIndex], constant);
1336 }
1337
1338 ir.Primitive buildBody(IrBuilder builder) {
1339 withBuilder(builder, () {
1340 ast.SwitchCase switchCase = continueTargets[i].statement;
1341 irBuilder.buildSequence(switchCase.statements, visit);
1342 if (irBuilder.isOpen) {
1343 if (switchCase == switchCases.last) {
1344 irBuilder.jumpTo(exit);
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 }
1204 } 1354 }
1205 } 1355 });
1206 } 1356 return null;
1207 } 1357 }
1208 ir.Primitive value = visit(node.expression); 1358
1209 JumpTarget target = elements.getTargetDefinition(node); 1359 cases.add(new SwitchCaseInfo(buildCondition, buildBody));
1210 Element error = helpers.fallThroughError; 1360 }
1211 irBuilder.buildSimpleSwitch(target, value, cases, defaultCase, error, 1361
1212 sourceInformationBuilder.buildGeneric(node)); 1362 // A loop with a simple switch in the body.
1363 IrBuilder whileBuilder = irBuilder.makeDelimitedBuilder();
1364 whileBuilder.buildWhile(
1365 buildCondition: (IrBuilder builder) {
1366 ir.Primitive condition = builder.buildIdentical(
1367 builder.environment.index2value[stateIndex], initial);
1368 return builder.buildNegation(condition);
1369 },
1370 buildBody: (IrBuilder builder) {
1371 builder.buildSimpleSwitch(loop, cases, null);
1372 });
1373 // Jump to the exit continuation. This jump is the body of the loop exit
1374 // continuation, so the loop exit continuation can be eta-reduced. The
1375 // jump is here for simplicity because `buildWhile` does not expose the
1376 // loop's exit continuation directly and has already emitted all jumps
1377 // to it anyway.
1378 whileBuilder.jumpTo(exit);
1379 irBuilder.add(new ir.LetCont(exit.continuation, whileBuilder.root));
1380 irBuilder.environment = exit.environment;
1381 irBuilder.environment.discard(1); // Discard the state variable.
1382 irBuilder.state.breakCollectors.removeLast();
1383 irBuilder.state.continueCollectors.length -= continueTargets.length;
1213 } 1384 }
1214 1385
1215 visitTryStatement(ast.TryStatement node) { 1386 visitTryStatement(ast.TryStatement node) {
1216 List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[]; 1387 List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[];
1217 for (ast.CatchBlock catchClause in node.catchBlocks.nodes) { 1388 for (ast.CatchBlock catchClause in node.catchBlocks.nodes) {
1218 LocalVariableElement exceptionVariable; 1389 LocalVariableElement exceptionVariable;
1219 if (catchClause.exception != null) { 1390 if (catchClause.exception != null) {
1220 exceptionVariable = elements[catchClause.exception]; 1391 exceptionVariable = elements[catchClause.exception];
1221 } 1392 }
1222 LocalVariableElement stackTraceVariable; 1393 LocalVariableElement stackTraceVariable;
(...skipping 2326 matching lines...) Expand 10 before | Expand all | Expand 10 after
3549 } 3720 }
3550 3721
3551 Element get closureConverter { 3722 Element get closureConverter {
3552 return _backend.helpers.closureConverter; 3723 return _backend.helpers.closureConverter;
3553 } 3724 }
3554 3725
3555 void addNativeMethod(FunctionElement function) { 3726 void addNativeMethod(FunctionElement function) {
3556 _backend.emitter.nativeEmitter.nativeMethods.add(function); 3727 _backend.emitter.nativeEmitter.nativeMethods.add(function);
3557 } 3728 }
3558 } 3729 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698