Chromium Code Reviews| 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 { |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } | |
| OLD | NEW |