OLD | NEW |
---|---|
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 { |
11 final JavaScriptBackend backend; | 11 final JavaScriptBackend backend; |
12 SsaOptimizerTask(JavaScriptBackend backend) | 12 SsaOptimizerTask(JavaScriptBackend backend) |
13 : this.backend = backend, | 13 : this.backend = backend, |
14 super(backend.compiler); | 14 super(backend.compiler); |
15 String get name => 'SSA optimizer'; | 15 String get name => 'SSA optimizer'; |
16 Compiler get compiler => backend.compiler; | 16 Compiler get compiler => backend.compiler; |
17 | 17 |
18 void runPhases(HGraph graph, List<OptimizationPhase> phases) { | 18 void runPhases(HGraph graph, List<OptimizationPhase> phases) { |
19 for (OptimizationPhase phase in phases) { | 19 for (OptimizationPhase phase in phases) { |
20 runPhase(graph, phase); | 20 runPhase(graph, phase); |
21 } | 21 } |
22 } | 22 } |
23 | 23 |
24 void runPhase(HGraph graph, OptimizationPhase phase) { | 24 void runPhase(HGraph graph, OptimizationPhase phase) { |
25 phase.visitGraph(graph); | 25 phase.visitGraph(graph); |
26 compiler.tracer.traceGraph(phase.name, graph); | 26 compiler.tracer.traceGraph(phase.name, graph); |
27 } | 27 } |
28 | 28 |
29 void optimize(WorkItem work, HGraph graph) { | 29 void optimize(WorkItem work, HGraph graph, bool speculative) { |
30 ConstantSystem constantSystem = compiler.backend.constantSystem; | 30 ConstantSystem constantSystem = compiler.backend.constantSystem; |
31 JavaScriptItemCompilationContext context = work.compilationContext; | 31 JavaScriptItemCompilationContext context = work.compilationContext; |
32 HTypeMap types = context.types; | 32 HTypeMap types = context.types; |
33 measure(() { | 33 measure(() { |
34 List<OptimizationPhase> phases = <OptimizationPhase>[ | 34 List<OptimizationPhase> phases = <OptimizationPhase>[ |
35 // Run trivial constant folding first to optimize | 35 // Run trivial constant folding first to optimize |
36 // some patterns useful for type conversion. | 36 // some patterns useful for type conversion. |
37 new SsaConstantFolder(constantSystem, backend, work, types), | 37 new SsaConstantFolder(constantSystem, backend, work, types), |
38 new SsaTypeConversionInserter(compiler), | 38 new SsaTypeConversionInserter(compiler), |
39 new SsaTypePropagator(compiler, types), | 39 new SsaTypePropagator(compiler, types), |
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)]; | |
51 runPhases(graph, phases); | 50 runPhases(graph, phases); |
51 if (!speculative) { | |
52 runPhase(graph, new SsaConstructionFieldTypes(backend, work, types)); | |
53 } | |
52 }); | 54 }); |
53 } | 55 } |
54 | 56 |
55 bool trySpeculativeOptimizations(WorkItem work, HGraph graph) { | 57 bool trySpeculativeOptimizations(WorkItem work, HGraph graph) { |
56 if (work.element.isField()) { | 58 if (work.element.isField()) { |
57 // Lazy initializers may not have bailout methods. | 59 // Lazy initializers may not have bailout methods. |
58 return false; | 60 return false; |
59 } | 61 } |
60 JavaScriptItemCompilationContext context = work.compilationContext; | 62 JavaScriptItemCompilationContext context = work.compilationContext; |
61 HTypeMap types = context.types; | 63 HTypeMap types = context.types; |
62 return measure(() { | 64 return measure(() { |
63 // Run the phases that will generate type guards. | 65 // Run the phases that will generate type guards. |
64 List<OptimizationPhase> phases = <OptimizationPhase>[ | 66 List<OptimizationPhase> phases = <OptimizationPhase>[ |
65 new SsaRecompilationFieldTypePropagator(backend, work, types), | |
66 new SsaSpeculativeTypePropagator(compiler, types), | 67 new SsaSpeculativeTypePropagator(compiler, types), |
67 new SsaTypeGuardInserter(compiler, work, types), | 68 new SsaTypeGuardInserter(compiler, work, types), |
68 new SsaEnvironmentBuilder(compiler), | 69 new SsaEnvironmentBuilder(compiler), |
69 // Change the propagated types back to what they were before we | 70 // Change the propagated types back to what they were before we |
70 // speculatively propagated, so that we can generate the bailout | 71 // speculatively propagated, so that we can generate the bailout |
71 // version. | 72 // version. |
72 // Note that we do this even if there were no guards inserted. If a | 73 // 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 | 74 // guard is not beneficial enough we don't emit one, but there might |
74 // still be speculative types on the instructions. | 75 // still be speculative types on the instructions. |
75 new SsaTypePropagator(compiler, types), | 76 new SsaTypePropagator(compiler, types), |
(...skipping 529 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
605 Modifiers modifiers = field.modifiers; | 606 Modifiers modifiers = field.modifiers; |
606 bool isFinalOrConst = false; | 607 bool isFinalOrConst = false; |
607 if (modifiers != null) { | 608 if (modifiers != null) { |
608 isFinalOrConst = modifiers.isFinal() || modifiers.isConst(); | 609 isFinalOrConst = modifiers.isFinal() || modifiers.isConst(); |
609 } | 610 } |
610 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) { | 611 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) { |
611 // If no setter is ever used for this field it is only initialized in the | 612 // If no setter is ever used for this field it is only initialized in the |
612 // initializer list. | 613 // initializer list. |
613 isFinalOrConst = true; | 614 isFinalOrConst = true; |
614 } | 615 } |
615 if (!isFinalOrConst && | 616 HFieldGet result = new HFieldGet( |
616 !compiler.codegenWorld.hasInvokedSetter(field, compiler) && | 617 field, node.inputs[0], isAssignable: !isFinalOrConst); |
617 !compiler.codegenWorld.hasFieldSetter(field, compiler)) { | 618 HType type = backend.optimisticFieldType(field); |
618 switch (compiler.phase) { | 619 if (type != null) { |
619 case Compiler.PHASE_COMPILING: | 620 result.guaranteedType = type; |
620 compiler.enqueuer.codegen.registerRecompilationCandidate( | 621 backend.registerFieldTypesOptimization( |
621 work.element); | 622 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 } | 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 Loading... | |
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. If this is the case we can ignore the type given |
1225 class BaseRecompilationVisitor extends HBaseVisitor { | 1218 // by the field initializer. This is especially useful when the field |
1219 // initializer is initializing the field to null. | |
1220 class SsaConstructionFieldTypes | |
1221 extends HBaseVisitor implements OptimizationPhase { | |
1226 final JavaScriptBackend backend; | 1222 final JavaScriptBackend backend; |
1227 final WorkItem work; | 1223 final WorkItem work; |
1228 final HTypeMap types; | 1224 final HTypeMap types; |
1229 Compiler get compiler => backend.compiler; | 1225 final String name = "SsaConstructionFieldTypes"; |
1226 final Set<HInstruction> thisUsers; | |
1227 final Set<Element> allSetters; | |
1228 final Map<HBasicBlock, Map<Element, HType>> blockFieldSetters; | |
1229 bool thisExposed = false; | |
1230 HGraph currentGraph; | |
1231 HInstruction thisInstruction; | |
ngeoffray
2012/09/27 12:35:46
You don't need this field.
Søren Gjesse
2012/09/27 13:22:49
Now I do - see below.
| |
1232 Map<Element, HType> currentFieldSetters; | |
1230 | 1233 |
1231 BaseRecompilationVisitor(this.backend, this.work, this.types); | 1234 SsaConstructionFieldTypes(JavaScriptBackend this.backend, |
1235 WorkItem this.work, | |
1236 HTypeMap this.types) | |
1237 : thisUsers = new Set<HInstruction>(), | |
1238 allSetters = new Set<Element>(), | |
1239 blockFieldSetters = new Map<HBasicBlock, Map<Element, HType>>(); | |
1232 | 1240 |
1233 abstract void handleFieldGet(HFieldGet node, HType type); | 1241 void visitGraph(HGraph graph) { |
1234 abstract void handleFieldNumberOperation(HFieldGet field, HType type); | 1242 currentGraph = graph; |
1235 | 1243 if (!work.element.isGenerativeConstructorBody() && |
1236 // Checks if the binary invocation operates on a field and a | 1244 !work.element.isGenerativeConstructor()) return; |
1237 // constant number. If it does [handleFieldNumberOperation] is | 1245 visitDominatorTree(graph); |
1238 // called with the field and the type inferred for the field so far. | 1246 if (work.element.isGenerativeConstructor()) { |
1239 void checkFieldNumberOperation(HInvokeBinary node) { | 1247 backend.registerConstructor(work.element); |
1240 // Determine if one of the operands is an HFieldGet. | |
1241 HFieldGet field; | |
1242 HInstruction other; | |
1243 if (node.left is HFieldGet) { | |
1244 field = node.left; | |
1245 other = node.right; | |
1246 } else if (node.right is HFieldGet) { | |
1247 field = node.right; | |
1248 other = node.left; | |
1249 } | |
1250 // Try to optimize the case where a field which is known to always | |
1251 // be an integer is compared with a constant number. | |
1252 if (other != null && | |
1253 other.isConstantNumber() && | |
1254 field.element != null && | |
1255 field.element.isMember()) { | |
1256 // Calculate the field type from the information available. If | |
1257 // we have type information for the field and it contains NUMBER | |
1258 // we use it as a candidate for recompilation. | |
1259 Element fieldElement = field.element; | |
1260 HType fieldSettersType = backend.fieldSettersTypeSoFar(fieldElement); | |
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 } | |
1267 } | 1248 } |
1268 } | 1249 } |
1269 | 1250 |
1270 void visitFieldGet(HFieldGet node) { | 1251 visitBasicBlock(HBasicBlock block) { |
1271 if (!node.element.isInstanceMember()) return; | 1252 if (block.predecessors.length == 0) { |
1253 // Create a new empty map for the first block. | |
1254 currentFieldSetters = new Map<Element, HType>(); | |
1255 } else { | |
1256 // Build a map which intersects the fields from all predecessors. For | |
1257 // each field in this intersection it unions the types. | |
1258 currentFieldSetters = | |
1259 new Map.from(blockFieldSetters[block.predecessors[0]]); | |
1260 // Loop headers are the only nodes with back edges. | |
1261 if (!block.isLoopHeader()) { | |
1262 for (int i = 1; i < block.predecessors.length; i++) { | |
1263 Map<Element, HType> predecessorsFieldSetters = | |
1264 blockFieldSetters[block.predecessors[i]]; | |
1265 Map<Element, HType> newFieldSetters = new Map<Element, HType>(); | |
1266 predecessorsFieldSetters.forEach((Element element, HType type) { | |
1267 HType currentType = currentFieldSetters[element]; | |
1268 if (currentType != null) { | |
1269 newFieldSetters[element] = currentType.union(type); | |
1270 } | |
1271 }); | |
1272 currentFieldSetters = newFieldSetters; | |
1273 } | |
1274 } else { | |
1275 assert(block.predecessors.length <= 2); | |
1276 } | |
1277 } | |
1278 block.forEachPhi((HPhi phi) => phi.accept(this)); | |
1279 block.forEachInstruction( | |
1280 (HInstruction instruction) => instruction.accept(this)); | |
1281 assert(currentFieldSetters != null); | |
1282 blockFieldSetters[block] = currentFieldSetters; | |
1283 } | |
1284 | |
1285 visitInstruction(HInstruction instruction) { | |
1286 // All instructions not explicitly handled below will flag the this | |
1287 // exposure if using this. | |
1288 thisExposed = thisExposed || thisUsers.contains(instruction); | |
1289 } | |
1290 | |
1291 visitPhi(HPhi phi) { | |
1292 thisExposed = thisExposed || thisUsers.contains(phi); | |
ngeoffray
2012/09/27 12:35:46
We talked about adding the users of the phi to thi
Søren Gjesse
2012/09/27 13:22:49
That is actually why I still had the thisInstructi
ngeoffray
2012/09/27 13:25:03
How about if (thisUsers.contains(phi)) ?
| |
1293 } | |
1294 | |
1295 visitThis(HThis instruction) { | |
1296 thisInstruction = instruction; | |
1297 // Collect all users of this in a set to make the this exposed check simple | |
1298 // and cheap. | |
1299 thisUsers.addAll(instruction.usedBy); | |
1300 } | |
1301 | |
1302 visitFieldGet(HInstruction _) { | |
1303 // The field get instruction is allowed to use this. | |
1304 } | |
1305 | |
1306 visitForeignNew(HForeignNew node) { | |
1307 // The HForeignNew instruction is used in the generative constructor to | |
1308 // initialize all fields in newly created objects. The fields are | |
1309 // initialized to the value present in the initializer list or set to null | |
1310 // if not otherwise initialized. | |
1311 int j = 0; | |
1312 node.element.forEachInstanceField( | |
1313 includeBackendMembers: true, | |
1314 includeSuperMembers: true, | |
ngeoffray
2012/09/27 12:35:46
Please add a comment on why you (can?) include bac
Søren Gjesse
2012/09/27 13:22:49
Removed backend members and added comment.
| |
1315 f: (ClassElement enclosingClass, Element element) { | |
1316 backend.registerFieldInitializer(element, types[node.inputs[j]]); | |
1317 j++; | |
1318 }); | |
1319 } | |
1320 | |
1321 visitFieldSet(HFieldSet node) { | |
1272 Element field = node.element; | 1322 Element field = node.element; |
1273 if (field != null) { | 1323 HInstruction value = node.value; |
1274 HType type = backend.optimisticFieldTypeAfterConstruction(field); | 1324 HType type = types[value]; |
1275 if (!type.isUnknown()) { | 1325 allSetters.add(field); |
ngeoffray
2012/09/27 12:35:46
As discussed, please add a comment here.
Søren Gjesse
2012/09/27 13:22:49
Done.
| |
1276 // Allow handling even if we haven't seen any types for this | 1326 if (work.element.getEnclosingClass() === field.getEnclosingClass() && |
1277 // field yet. There might still be only one setter in an | 1327 value.hasGuaranteedType()) { |
1278 // initializer list or constructor body and recompilation | 1328 currentFieldSetters[field] = type; |
1279 // can therefore pay off. | |
1280 handleFieldGet(node, type); | |
1281 } | |
1282 } | 1329 } |
1283 } | 1330 } |
1284 | 1331 |
1285 HInstruction visitEquals(HEquals node) { | 1332 visitExit(HExit node) { |
1286 checkFieldNumberOperation(node); | 1333 // If this has been exposed then we cannot say anything about types after |
1287 } | 1334 // construction. |
1335 if (!thisExposed) { | |
1336 // Register the known field types. | |
1337 currentFieldSetters.forEach((Element element, HType type) { | |
1338 backend.registerFieldConstructor(element, type); | |
1339 allSetters.remove(element); | |
1340 }); | |
1341 } | |
1288 | 1342 |
1289 HInstruction visitBinaryArithmetic(HBinaryArithmetic node) { | 1343 // For other fields having setters in the generative constructor body, set |
1290 checkFieldNumberOperation(node); | 1344 // the type to UNKNOWN to avoid relying on the type set in the initializer |
1345 // list. | |
1346 allSetters.forEach((Element element) { | |
1347 backend.registerFieldConstructor(element, HType.UNKNOWN); | |
1348 }); | |
1291 } | 1349 } |
1292 } | 1350 } |
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 } | |
OLD | NEW |