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

Side by Side Diff: lib/compiler/implementation/ssa/optimize.dart

Issue 10964016: Change the type inference for fields in dart2js (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Minor fixes and rebased Created 8 years, 3 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 abstract class OptimizationPhase { 5 abstract class OptimizationPhase {
6 String get name; 6 String get name;
7 void visitGraph(HGraph graph); 7 void visitGraph(HGraph graph);
8 } 8 }
9 9
10 class SsaOptimizerTask extends CompilerTask { 10 class SsaOptimizerTask extends CompilerTask {
(...skipping 29 matching lines...) Expand all
40 new SsaCheckInserter(backend, types, context.boundsChecked), 40 new SsaCheckInserter(backend, types, context.boundsChecked),
41 new SsaConstantFolder(constantSystem, backend, work, types), 41 new SsaConstantFolder(constantSystem, backend, work, types),
42 new SsaRedundantPhiEliminator(), 42 new SsaRedundantPhiEliminator(),
43 new SsaDeadPhiEliminator(), 43 new SsaDeadPhiEliminator(),
44 new SsaGlobalValueNumberer(compiler, types), 44 new SsaGlobalValueNumberer(compiler, types),
45 new SsaCodeMotion(), 45 new SsaCodeMotion(),
46 // Previous optimizations may have generated new 46 // Previous optimizations may have generated new
47 // opportunities for constant folding. 47 // opportunities for constant folding.
48 new SsaConstantFolder(constantSystem, backend, work, types), 48 new SsaConstantFolder(constantSystem, backend, work, types),
49 new SsaDeadCodeEliminator(types), 49 new SsaDeadCodeEliminator(types),
50 new SsaRegisterRecompilationCandidates(backend, work, types)]; 50 new SsaConstructionFieldTypes(backend, work, types)];
51 runPhases(graph, phases); 51 runPhases(graph, phases);
52 }); 52 });
53 } 53 }
54 54
55 bool trySpeculativeOptimizations(WorkItem work, HGraph graph) { 55 bool trySpeculativeOptimizations(WorkItem work, HGraph graph) {
56 if (work.element.isField()) { 56 if (work.element.isField()) {
57 // Lazy initializers may not have bailout methods. 57 // Lazy initializers may not have bailout methods.
58 return false; 58 return false;
59 } 59 }
60 JavaScriptItemCompilationContext context = work.compilationContext; 60 JavaScriptItemCompilationContext context = work.compilationContext;
61 HTypeMap types = context.types; 61 HTypeMap types = context.types;
62 return measure(() { 62 return measure(() {
63 // Run the phases that will generate type guards. 63 // Run the phases that will generate type guards.
64 List<OptimizationPhase> phases = <OptimizationPhase>[ 64 List<OptimizationPhase> phases = <OptimizationPhase>[
65 new SsaRecompilationFieldTypePropagator(backend, work, types),
66 new SsaSpeculativeTypePropagator(compiler, types), 65 new SsaSpeculativeTypePropagator(compiler, types),
67 new SsaTypeGuardInserter(compiler, work, types), 66 new SsaTypeGuardInserter(compiler, work, types),
68 new SsaEnvironmentBuilder(compiler), 67 new SsaEnvironmentBuilder(compiler),
69 // Change the propagated types back to what they were before we 68 // Change the propagated types back to what they were before we
70 // speculatively propagated, so that we can generate the bailout 69 // speculatively propagated, so that we can generate the bailout
71 // version. 70 // version.
72 // Note that we do this even if there were no guards inserted. If a 71 // Note that we do this even if there were no guards inserted. If a
73 // guard is not beneficial enough we don't emit one, but there might 72 // guard is not beneficial enough we don't emit one, but there might
74 // still be speculative types on the instructions. 73 // still be speculative types on the instructions.
75 new SsaTypePropagator(compiler, types), 74 new SsaTypePropagator(compiler, types),
(...skipping 529 matching lines...) Expand 10 before | Expand all | Expand 10 after
605 Modifiers modifiers = field.modifiers; 604 Modifiers modifiers = field.modifiers;
606 bool isFinalOrConst = false; 605 bool isFinalOrConst = false;
607 if (modifiers != null) { 606 if (modifiers != null) {
608 isFinalOrConst = modifiers.isFinal() || modifiers.isConst(); 607 isFinalOrConst = modifiers.isFinal() || modifiers.isConst();
609 } 608 }
610 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) { 609 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) {
611 // If no setter is ever used for this field it is only initialized in the 610 // If no setter is ever used for this field it is only initialized in the
612 // initializer list. 611 // initializer list.
613 isFinalOrConst = true; 612 isFinalOrConst = true;
614 } 613 }
615 if (!isFinalOrConst && 614 HFieldGet result = new HFieldGet(
616 !compiler.codegenWorld.hasInvokedSetter(field, compiler) && 615 field, node.inputs[0], isAssignable: !isFinalOrConst);
617 !compiler.codegenWorld.hasFieldSetter(field, compiler)) { 616 if (work.element.isMember()) {
618 switch (compiler.phase) { 617 HType type = backend.optimisticFieldType(field);
619 case Compiler.PHASE_COMPILING: 618 if (type != null) {
620 compiler.enqueuer.codegen.registerRecompilationCandidate( 619 result.guaranteedType = type;
621 work.element); 620 backend.registerFieldTypesOptimization(
622 break; 621 work.element, field, result.guaranteedType);
623 case Compiler.PHASE_RECOMPILING:
624 // If field is not final or const but no setters are used then the
625 // field might be considered final anyway as it will be either
626 // un-initialized or initialized in the constructor initializer list.
627 isFinalOrConst = true;
628 break;
629 } 622 }
630 } 623 }
631 return new HFieldGet(field, node.inputs[0], isAssignable: !isFinalOrConst); 624 return result;
632 } 625 }
633 626
634 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { 627 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
635 Element field = 628 Element field =
636 findConcreteFieldForDynamicAccess(node.receiver, node.selector); 629 findConcreteFieldForDynamicAccess(node.receiver, node.selector);
637 if (field === null) return node; 630 if (field === null) return node;
638 HInstruction value = node.inputs[1]; 631 HInstruction value = node.inputs[1];
639 if (compiler.enableTypeAssertions) { 632 if (compiler.enableTypeAssertions) {
640 HInstruction other = value.convertType( 633 HInstruction other = value.convertType(
641 compiler, field, HTypeConversion.CHECKED_MODE_CHECK); 634 compiler, field, HTypeConversion.CHECKED_MODE_CHECK);
(...skipping 571 matching lines...) Expand 10 before | Expand all | Expand 10 after
1213 1206
1214 for (HIf ifUser in notIfUsers) { 1207 for (HIf ifUser in notIfUsers) {
1215 changeUsesDominatedBy(ifUser.elseBlock, input, convertedType); 1208 changeUsesDominatedBy(ifUser.elseBlock, input, convertedType);
1216 // TODO(ngeoffray): Also change uses for the then block on a HType 1209 // TODO(ngeoffray): Also change uses for the then block on a HType
1217 // that knows it is not of a specific Type. 1210 // that knows it is not of a specific Type.
1218 } 1211 }
1219 } 1212 }
1220 } 1213 }
1221 1214
1222 1215
1223 // Base class for the handling of recompilation based on inferred 1216 // Analyze the constructors to see if some fields will always have a specific
1224 // field types. 1217 // type after construction. This is mainly done to override to possible null
ngeoffray 2012/09/20 14:43:01 to override to -> to override. But the sentence i
Søren Gjesse 2012/09/21 13:49:44 Rephrased.
1225 class BaseRecompilationVisitor extends HBaseVisitor { 1218 // value set by field initializer.
1219 class SsaConstructionFieldTypes
1220 extends HBaseVisitor implements OptimizationPhase {
1226 final JavaScriptBackend backend; 1221 final JavaScriptBackend backend;
1227 final WorkItem work; 1222 final WorkItem work;
1228 final HTypeMap types; 1223 final HTypeMap types;
1229 Compiler get compiler => backend.compiler; 1224 final String name = "SsaConstructionFieldTypes";
1225 final Set<Element> allSetters;
1226 final Map<Element, HType> exitDominatedSetters;
1227 final Map<HBasicBlock, Map<Element, HType>> blockFieldSetters;
1228 bool thisExposed = false;
1229 HGraph currentGraph;
1230 bool collectSetters;
1231 Map<Element, HType> currentFieldSetters;
1230 1232
1231 BaseRecompilationVisitor(this.backend, this.work, this.types); 1233 SsaConstructionFieldTypes(JavaScriptBackend this.backend,
1234 WorkItem this.work,
1235 HTypeMap this.types)
1236 : allSetters = new Set<Element>(),
1237 exitDominatedSetters = new Map<Element, HType>(),
1238 blockFieldSetters = new Map<HBasicBlock, Map<Element, HType>>();
1232 1239
1233 abstract void handleFieldGet(HFieldGet node, HType type); 1240 void visitGraph(HGraph graph) {
1234 abstract void handleFieldNumberOperation(HFieldGet field, HType type); 1241 currentGraph = graph;
1242 if (!work.element.isGenerativeConstructorBody() &&
1243 !work.element.isGenerativeConstructor()) return;
1244 visitDominatorTree(graph);
1245 }
1235 1246
1236 // Checks if the binary invocation operates on a field and a 1247 visitBasicBlock(HBasicBlock node) {
1237 // constant number. If it does [handleFieldNumberOperation] is 1248 currentBlock = node;
ngeoffray 2012/09/20 14:43:01 Do you need currentBlock?
Søren Gjesse 2012/09/21 13:49:44 No. It is an instance variable in the HBaseVisitor
1238 // called with the field and the type inferred for the field so far. 1249 collectSetters = true;
1239 void checkFieldNumberOperation(HInvokeBinary node) { 1250 currentFieldSetters = null;
1240 // Determine if one of the operands is an HFieldGet. 1251 if (node.predecessors.length == 1) {
1241 HFieldGet field; 1252 // If there is just one predeecessor start out with the field setters
1242 HInstruction other; 1253 // from there.
1243 if (node.left is HFieldGet) { 1254 currentFieldSetters = blockFieldSetters[node.predecessors[0]];
1244 field = node.left; 1255 } else {
1245 other = node.right; 1256 // If there is more than one predeecessor combine the field setters from
1246 } else if (node.right is HFieldGet) { 1257 // all of them.
ngeoffray 2012/09/20 14:43:01 I don't really like these null checks here. Could
Søren Gjesse 2012/09/21 13:49:44 Done.
1247 field = node.right; 1258 node.predecessors.forEach((HBasicBlock block) {
1248 other = node.left; 1259 Map<Element, HType> predecessorsFieldSetters = blockFieldSetters[block];
1260 if (predecessorsFieldSetters === null) {
1261 currentFieldSetters = null;
1262 collectSetters = false;
1263 } else if (collectSetters) {
1264 if (currentFieldSetters === null) {
1265 // Just copy the predecessor setters if this is the first
1266 // predecessor.
1267 currentFieldSetters = predecessorsFieldSetters;
1268 } else {
1269 Map<Element, HType> newFieldSetters = new Map<Element, HType>();
1270 currentFieldSetters.forEach((Element element, HType currentType) {
1271 HType type = predecessorsFieldSetters[element];
1272 if (type != null) {
1273 newFieldSetters[element] = currentType.union(type);
1274 }
1275 });
1276 currentFieldSetters = newFieldSetters;
1277 }
1278 }
1279 });
1249 } 1280 }
1250 // Try to optimize the case where a field which is known to always 1281 HInstruction instruction = node.first;
ngeoffray 2012/09/20 14:43:01 Change this loop to block.forEachInstruction(..._
Søren Gjesse 2012/09/21 13:49:44 Done.
1251 // be an integer is compared with a constant number. 1282 while (instruction !== null) {
1252 if (other != null && 1283 instruction.accept(this);
1253 other.isConstantNumber() && 1284 instruction = instruction.next;
1254 field.element != null && 1285 }
1255 field.element.isMember()) { 1286 if (currentFieldSetters != null) {
1256 // Calculate the field type from the information available. If 1287 blockFieldSetters[node] = currentFieldSetters;
1257 // we have type information for the field and it contains NUMBER 1288 if (node.dominates(currentGraph.exit)) {
1258 // we use it as a candidate for recompilation. 1289 currentFieldSetters.forEach((Element field, HType type) {
1259 Element fieldElement = field.element; 1290 exitDominatedSetters[field] = type;
ngeoffray 2012/09/20 14:43:01 Do you need this? I would think that when you reac
Søren Gjesse 2012/09/21 13:49:44 The exitDominatedSetters is now gone.
1260 HType fieldSettersType = backend.fieldSettersTypeSoFar(fieldElement); 1291 });
1261 HType initializersType = backend.typeFromInitializersSoFar(fieldElement);
1262 HType fieldType = fieldSettersType.union(initializersType);
1263 HType type = HType.NUMBER.union(fieldType);
1264 if (type == HType.NUMBER) {
1265 handleFieldNumberOperation(field, fieldType);
1266 } 1292 }
1267 } 1293 }
1268 } 1294 }
1269 1295
1270 void visitFieldGet(HFieldGet node) { 1296 visitInstruction(HInstruction instruction) {
1271 if (!node.element.isInstanceMember()) return; 1297 // Instruction not explicitly handled stops the collection of field setter
1298 // information.
1299 currentFieldSetters = null;
1300 collectSetters = false;
1301 }
1302
1303 visitForeignNew(HForeignNew node) {
ngeoffray 2012/09/20 14:43:01 Document which element has a foreign new, and what
Søren Gjesse 2012/09/21 13:49:44 Done.
1304 int j = 0;
1305 node.element.forEachInstanceField(
1306 includeBackendMembers: true,
1307 includeSuperMembers: true,
1308 f: (ClassElement enclosingClass, Element element) {
1309 backend.registerFieldInitializer(element, types[node.inputs[j]]);
1310 j++;
1311 });
1312 }
1313
1314 visitStatic(HStatic node) {
1315 // Ignore static.
1316 }
1317
1318 visitInvoke(HInvoke node) {
1319 for (int i = 0; i < node.inputs.length && !thisExposed; i++) {
1320 if (node.inputs[i] is HThis) thisExposed = true;
1321 }
1322 }
1323
1324 visitFieldSet(HFieldSet node) {
1272 Element field = node.element; 1325 Element field = node.element;
1273 if (field != null) { 1326 HInstruction value = node.value;
1274 HType type = backend.optimisticFieldTypeAfterConstruction(field); 1327 HType type = types[value];
1275 if (!type.isUnknown()) { 1328 allSetters.add(field);
1276 // Allow handling even if we haven't seen any types for this 1329 if (work.element.isGenerativeConstructorBody() &&
ngeoffray 2012/09/20 14:43:01 Why this first check? Otherwise it's a generative
Søren Gjesse 2012/09/21 13:49:44 Removed. I was not 100% sure of what the generativ
1277 // field yet. There might still be only one setter in an 1330 node.element.isMember() &&
ngeoffray 2012/09/20 14:43:01 Shouldn't you also check that the class of the el
Søren Gjesse 2012/09/21 13:49:44 Done.
1278 // initializer list or constructor body and recompilation 1331 node.value.hasGuaranteedType() &&
1279 // can therefore pay off. 1332 node.block.dominates(currentGraph.exit)) {
ngeoffray 2012/09/20 14:43:01 Again, I don't think this is needed if you just lo
Søren Gjesse 2012/09/21 13:49:44 Done.
1280 handleFieldGet(node, type); 1333 exitDominatedSetters[field] = value.guaranteedType;
1334 } else {
ngeoffray 2012/09/20 14:43:01 I don't understand why an else here.
Søren Gjesse 2012/09/21 13:49:44 The code here is now simplified.
1335 if (collectSetters) {
1336 if (type.isUseful()) {
1337 if (currentFieldSetters === null) {
1338 currentFieldSetters = new Map<Element, HType>();
1339 }
1340 if (currentFieldSetters.containsKey(field)) {
1341 currentFieldSetters[field] = currentFieldSetters[field].union(type);
ngeoffray 2012/09/20 14:43:01 Maybe fetch currentFieldSetters[field] before, and
Søren Gjesse 2012/09/21 13:49:44 Done.
1342 } else {
1343 currentFieldSetters[field] = type;
1344 }
1345 }
1281 } 1346 }
1282 } 1347 }
1283 } 1348 }
1284 1349
1285 HInstruction visitEquals(HEquals node) { 1350 visitGoto(HGoto node) {
1286 checkFieldNumberOperation(node); 1351 // Ignore gotos.
1287 } 1352 }
1288 1353
1289 HInstruction visitBinaryArithmetic(HBinaryArithmetic node) { 1354 visitExit(HExit node) {
1290 checkFieldNumberOperation(node); 1355 // If this has been exposed then we cannot say anything about types after
1356 // construction.
1357 if (thisExposed) return;
1358
1359 // Register the known field types.
1360 exitDominatedSetters.forEach((Element element, HType type) {
1361 backend.registerFieldConstructor(element, type);
1362 allSetters.remove(element);
1363 });
1364 allSetters.forEach((Element element) {
1365 backend.registerFieldConstructor(element, HType.UNKNOWN);
1366 });
1291 } 1367 }
1292 } 1368 }
1293
1294
1295 // Visitor that registers candidates for recompilation.
1296 class SsaRegisterRecompilationCandidates
1297 extends BaseRecompilationVisitor implements OptimizationPhase {
1298 final String name = "SsaRegisterRecompileCandidates";
1299 HGraph graph;
1300
1301 SsaRegisterRecompilationCandidates(JavaScriptBackend backend,
1302 WorkItem work,
1303 HTypeMap types)
1304 : super(backend, work, types);
1305
1306 void visitGraph(HGraph visitee) {
1307 graph = visitee;
1308 if (compiler.phase == Compiler.PHASE_COMPILING) {
1309 visitDominatorTree(visitee);
1310 }
1311 }
1312
1313 void handleFieldGet(HFieldGet node, HType type) {
1314 assert(compiler.phase == Compiler.PHASE_COMPILING);
1315 compiler.enqueuer.codegen.registerRecompilationCandidate(
1316 work.element);
1317 }
1318
1319 void handleFieldNumberOperation(HFieldGet node, HType type) {
1320 assert(compiler.phase == Compiler.PHASE_COMPILING);
1321 compiler.enqueuer.codegen.registerRecompilationCandidate(
1322 work.element);
1323 }
1324 }
1325
1326
1327 // Visitor that sets the known or suspected type of fields during
1328 // recompilation.
1329 class SsaRecompilationFieldTypePropagator
1330 extends BaseRecompilationVisitor implements OptimizationPhase {
1331 final String name = "SsaRecompilationFieldTypePropagator";
1332 HGraph graph;
1333
1334 SsaRecompilationFieldTypePropagator(JavaScriptBackend backend,
1335 WorkItem work,
1336 HTypeMap types)
1337 : super(backend, work, types);
1338
1339 void visitGraph(HGraph visitee) {
1340 graph = visitee;
1341 if (compiler.phase == Compiler.PHASE_RECOMPILING) {
1342 visitDominatorTree(visitee);
1343 }
1344 }
1345
1346 void handleFieldGet(HFieldGet field, HType type) {
1347 assert(compiler.phase == Compiler.PHASE_RECOMPILING);
1348 if (!type.isConflicting()) {
1349 // If there are no invoked setters with this name, the union of
1350 // the types of the initializers and the setters is guaranteed
1351 // otherwise it is only speculative.
1352 Element element = field.element;
1353 assert(!element.isGenerativeConstructorBody());
1354 if (!compiler.codegenWorld.hasInvokedSetter(element, compiler)) {
1355 field.guaranteedType =
1356 type.union(backend.fieldSettersTypeSoFar(element));
1357 } else {
1358 types[field] = type.union(backend.fieldSettersTypeSoFar(element));
1359 }
1360 }
1361 }
1362
1363 void handleFieldNumberOperation(HFieldGet field, HType type) {
1364 assert(compiler.phase == Compiler.PHASE_RECOMPILING);
1365 if (compiler.codegenWorld.hasInvokedSetter(field.element, compiler)) {
1366 // If there are invoked setters we don't know for sure
1367 // that the field will hold a value of the calculated
1368 // type, but the fact that the class itself sticks to
1369 // this type for the field is still a strong signal
1370 // indicating the expected type of the field.
1371 types[field] = type;
1372 } else {
1373 // If there are no invoked setters we know the type of
1374 // this field for sure.
1375 field.guaranteedType = type;
1376 }
1377 }
1378 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698