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

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: Addressed second round of comments 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),
76 // Then run the [SsaCheckInserter] because the type propagator also 75 // Then run the [SsaCheckInserter] because the type propagator also
77 // propagated types non-speculatively. For example, it might have 76 // propagated types non-speculatively. For example, it might have
78 // propagated the type array for a call to the List constructor. 77 // propagated the type array for a call to the List constructor.
79 new SsaCheckInserter(backend, types, context.boundsChecked)]; 78 new SsaCheckInserter(backend, types, context.boundsChecked),
79 new SsaConstructionFieldTypes(backend, work, types)];
ngeoffray 2012/09/24 15:39:29 That's not right, the speculative optimization sho
Søren Gjesse 2012/09/25 13:33:21 Sorry my bad. As discussed offline added boolean a
80 runPhases(graph, phases); 80 runPhases(graph, phases);
81 return !work.guards.isEmpty(); 81 return !work.guards.isEmpty();
82 }); 82 });
83 } 83 }
84 84
85 void prepareForSpeculativeOptimizations(WorkItem work, HGraph graph) { 85 void prepareForSpeculativeOptimizations(WorkItem work, HGraph graph) {
86 JavaScriptItemCompilationContext context = work.compilationContext; 86 JavaScriptItemCompilationContext context = work.compilationContext;
87 HTypeMap types = context.types; 87 HTypeMap types = context.types;
88 measure(() { 88 measure(() {
89 // In order to generate correct code for the bailout version, we did not 89 // In order to generate correct code for the bailout version, we did not
(...skipping 515 matching lines...) Expand 10 before | Expand all | Expand 10 after
605 Modifiers modifiers = field.modifiers; 605 Modifiers modifiers = field.modifiers;
606 bool isFinalOrConst = false; 606 bool isFinalOrConst = false;
607 if (modifiers != null) { 607 if (modifiers != null) {
608 isFinalOrConst = modifiers.isFinal() || modifiers.isConst(); 608 isFinalOrConst = modifiers.isFinal() || modifiers.isConst();
609 } 609 }
610 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) { 610 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) {
611 // If no setter is ever used for this field it is only initialized in the 611 // If no setter is ever used for this field it is only initialized in the
612 // initializer list. 612 // initializer list.
613 isFinalOrConst = true; 613 isFinalOrConst = true;
614 } 614 }
615 if (!isFinalOrConst && 615 HFieldGet result = new HFieldGet(
616 !compiler.codegenWorld.hasInvokedSetter(field, compiler) && 616 field, node.inputs[0], isAssignable: !isFinalOrConst);
617 !compiler.codegenWorld.hasFieldSetter(field, compiler)) { 617 HType type = backend.optimisticFieldType(field);
618 switch (compiler.phase) { 618 if (type != null) {
619 case Compiler.PHASE_COMPILING: 619 result.guaranteedType = type;
620 compiler.enqueuer.codegen.registerRecompilationCandidate( 620 backend.registerFieldTypesOptimization(
621 work.element); 621 work.element, field, result.guaranteedType);
622 break;
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 }
630 } 622 }
631 return new HFieldGet(field, node.inputs[0], isAssignable: !isFinalOrConst); 623 return result;
632 } 624 }
633 625
634 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { 626 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
635 Element field = 627 Element field =
636 findConcreteFieldForDynamicAccess(node.receiver, node.selector); 628 findConcreteFieldForDynamicAccess(node.receiver, node.selector);
637 if (field === null) return node; 629 if (field === null) return node;
638 HInstruction value = node.inputs[1]; 630 HInstruction value = node.inputs[1];
639 if (compiler.enableTypeAssertions) { 631 if (compiler.enableTypeAssertions) {
640 HInstruction other = value.convertType( 632 HInstruction other = value.convertType(
641 compiler, field, HTypeConversion.CHECKED_MODE_CHECK); 633 compiler, field, HTypeConversion.CHECKED_MODE_CHECK);
(...skipping 571 matching lines...) Expand 10 before | Expand all | Expand 10 after
1213 1205
1214 for (HIf ifUser in notIfUsers) { 1206 for (HIf ifUser in notIfUsers) {
1215 changeUsesDominatedBy(ifUser.elseBlock, input, convertedType); 1207 changeUsesDominatedBy(ifUser.elseBlock, input, convertedType);
1216 // TODO(ngeoffray): Also change uses for the then block on a HType 1208 // TODO(ngeoffray): Also change uses for the then block on a HType
1217 // that knows it is not of a specific Type. 1209 // that knows it is not of a specific Type.
1218 } 1210 }
1219 } 1211 }
1220 } 1212 }
1221 1213
1222 1214
1223 // Base class for the handling of recompilation based on inferred 1215 // Analyze the constructors to see if some fields will always have a specific
1224 // field types. 1216 // type after construction. If this is the case we can ignore the type given
1225 class BaseRecompilationVisitor extends HBaseVisitor { 1217 // by the field initializer. This is especially useful when the field
1218 // initializer is initializing the field to null.
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<HInstruction> thisUsers;
1226 final Set<Element> allSetters;
1227 final Map<HBasicBlock, Map<Element, HType>> blockFieldSetters;
1228 bool thisExposed = false;
1229 HGraph currentGraph;
1230 Map<Element, HType> currentFieldSetters;
1230 1231
1231 BaseRecompilationVisitor(this.backend, this.work, this.types); 1232 SsaConstructionFieldTypes(JavaScriptBackend this.backend,
1233 WorkItem this.work,
1234 HTypeMap this.types)
1235 : thisUsers = new Set<HInstruction>(),
1236 allSetters = new Set<Element>(),
1237 blockFieldSetters = new Map<HBasicBlock, Map<Element, HType>>();
1232 1238
1233 abstract void handleFieldGet(HFieldGet node, HType type); 1239 void visitGraph(HGraph graph) {
1234 abstract void handleFieldNumberOperation(HFieldGet field, HType type); 1240 currentGraph = graph;
1241 if (!work.element.isGenerativeConstructorBody() &&
1242 !work.element.isGenerativeConstructor()) return;
1243 visitDominatorTree(graph);
1244 }
1235 1245
1236 // Checks if the binary invocation operates on a field and a 1246 visitBasicBlock(HBasicBlock block) {
1237 // constant number. If it does [handleFieldNumberOperation] is 1247 if (block.predecessors.length == 0) {
1238 // called with the field and the type inferred for the field so far. 1248 // Create a new empty map for the first block.
1239 void checkFieldNumberOperation(HInvokeBinary node) { 1249 currentFieldSetters = new Map<Element, HType>();
1240 // Determine if one of the operands is an HFieldGet. 1250 } else {
1241 HFieldGet field; 1251 // Build a map which intersects the fields from all predecessors. For
1242 HInstruction other; 1252 // each field in this intersection it unions the types.
1243 if (node.left is HFieldGet) { 1253 currentFieldSetters =
1244 field = node.left; 1254 new Map.from(blockFieldSetters[block.predecessors[0]]);
1245 other = node.right; 1255 // Loop headers are the only nodes with back edges.
1246 } else if (node.right is HFieldGet) { 1256 if (!block.isLoopHeader()) {
1247 field = node.right; 1257 for (int i = 1; i < block.predecessors.length; i++) {
1248 other = node.left; 1258 Map<Element, HType> predecessorsFieldSetters =
1259 blockFieldSetters[block.predecessors[i]];
1260 Map<Element, HType> newFieldSetters = new Map<Element, HType>();
1261 predecessorsFieldSetters.forEach((Element element, HType type) {
1262 HType currentType = currentFieldSetters[element];
1263 if (currentType != null) {
1264 newFieldSetters[element] = currentType.union(type);
1265 } else {
1266 newFieldSetters[element] = type;
1267 }
1268 });
1269 currentFieldSetters = newFieldSetters;
1270 }
1271 } else {
1272 Expect.isTrue(block.predecessors.length <= 2);
1273 }
1249 } 1274 }
1250 // Try to optimize the case where a field which is known to always 1275 block.forEachInstruction(
1251 // be an integer is compared with a constant number. 1276 (HInstruction instruction) => instruction.accept(this));
1252 if (other != null && 1277 assert(currentFieldSetters != null);
1253 other.isConstantNumber() && 1278 blockFieldSetters[block] = currentFieldSetters;
1254 field.element != null && 1279 }
1255 field.element.isMember()) { 1280
1256 // Calculate the field type from the information available. If 1281 visitInstruction(HInstruction instruction) {
1257 // we have type information for the field and it contains NUMBER 1282 // All instructions not explicitly handled below will flag the this
1258 // we use it as a candidate for recompilation. 1283 // exposure if using this.
1259 Element fieldElement = field.element; 1284 thisExposed = thisExposed || thisUsers.contains(instruction);
1260 HType fieldSettersType = backend.fieldSettersTypeSoFar(fieldElement); 1285 }
1261 HType initializersType = backend.typeFromInitializersSoFar(fieldElement); 1286
1262 HType fieldType = fieldSettersType.union(initializersType); 1287 visitThis(HThis instruction) {
1263 HType type = HType.NUMBER.union(fieldType); 1288 // Collect all users of this in a set to make the this exposed check simple
1264 if (type == HType.NUMBER) { 1289 // and cheap.
1265 handleFieldNumberOperation(field, fieldType); 1290 instruction.usedBy.forEach(thisUsers.add);
1266 } 1291 }
1292
1293 visitFieldGet(HInstruction _) {
1294 // The field get instruction is allowed to use this.
1295 }
1296
1297 visitForeignNew(HForeignNew node) {
1298 // The HForeignNew instruction is used in the generative constructor to
1299 // initialize all fields in newly created objects. The fields are
1300 // initialized to the value present in the initializer list or set to null
1301 // if not otherwise initialized.
1302 int j = 0;
1303 node.element.forEachInstanceField(
1304 includeBackendMembers: true,
1305 includeSuperMembers: true,
1306 f: (ClassElement enclosingClass, Element element) {
1307 backend.registerFieldInitializer(element, types[node.inputs[j]]);
1308 j++;
1309 });
1310 }
1311
1312 visitFieldSet(HFieldSet node) {
1313 Element field = node.element;
1314 HInstruction value = node.value;
1315 HType type = types[value];
1316 allSetters.add(field);
1317 if (work.element.getEnclosingClass() === field.getEnclosingClass() &&
1318 value.hasGuaranteedType()) {
1319 currentFieldSetters[field] = type;
1267 } 1320 }
1268 } 1321 }
1269 1322
1270 void visitFieldGet(HFieldGet node) { 1323 visitExit(HExit node) {
1271 if (!node.element.isInstanceMember()) return; 1324 // If this has been exposed then we cannot say anything about types after
1272 Element field = node.element; 1325 // construction.
1273 if (field != null) { 1326 if (thisExposed) return;
1274 HType type = backend.optimisticFieldTypeAfterConstruction(field);
1275 if (!type.isUnknown()) {
1276 // Allow handling even if we haven't seen any types for this
1277 // field yet. There might still be only one setter in an
1278 // initializer list or constructor body and recompilation
1279 // can therefore pay off.
1280 handleFieldGet(node, type);
1281 }
1282 }
1283 }
1284 1327
1285 HInstruction visitEquals(HEquals node) { 1328 // Register the known field types.
1286 checkFieldNumberOperation(node); 1329 currentFieldSetters.forEach((Element element, HType type) {
1287 } 1330 backend.registerFieldConstructor(element, type);
1288 1331 allSetters.remove(element);
1289 HInstruction visitBinaryArithmetic(HBinaryArithmetic node) { 1332 });
1290 checkFieldNumberOperation(node); 1333 allSetters.forEach((Element element) {
1334 backend.registerFieldConstructor(element, HType.UNKNOWN);
ngeoffray 2012/09/24 15:39:29 So this is just to prevent thinking a field has th
Søren Gjesse 2012/09/25 13:33:21 Done.
Søren Gjesse 2012/09/25 13:33:21 Done.
1335 });
1291 } 1336 }
1292 } 1337 }
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