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 interface OptimizationPhase { | 5 interface 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 19 matching lines...) Expand all Loading... |
30 new SsaConstantFolder(backend, work), | 30 new SsaConstantFolder(backend, work), |
31 new SsaTypeConversionInserter(compiler), | 31 new SsaTypeConversionInserter(compiler), |
32 new SsaTypePropagator(compiler), | 32 new SsaTypePropagator(compiler), |
33 new SsaCheckInserter(backend), | 33 new SsaCheckInserter(backend), |
34 new SsaConstantFolder(backend, work), | 34 new SsaConstantFolder(backend, work), |
35 new SsaRedundantPhiEliminator(), | 35 new SsaRedundantPhiEliminator(), |
36 new SsaDeadPhiEliminator(), | 36 new SsaDeadPhiEliminator(), |
37 new SsaGlobalValueNumberer(compiler), | 37 new SsaGlobalValueNumberer(compiler), |
38 new SsaCodeMotion(), | 38 new SsaCodeMotion(), |
39 new SsaDeadCodeEliminator(), | 39 new SsaDeadCodeEliminator(), |
40 new SsaProcessRecompileCandidates(backend, work)]; | 40 new SsaRegisterRecompilationCandidates(backend, work)]; |
41 runPhases(graph, phases); | 41 runPhases(graph, phases); |
42 }); | 42 }); |
43 } | 43 } |
44 | 44 |
45 bool trySpeculativeOptimizations(WorkItem work, HGraph graph) { | 45 bool trySpeculativeOptimizations(WorkItem work, HGraph graph) { |
46 return measure(() { | 46 return measure(() { |
47 // Run the phases that will generate type guards. | 47 // Run the phases that will generate type guards. |
48 List<OptimizationPhase> phases = <OptimizationPhase>[ | 48 List<OptimizationPhase> phases = <OptimizationPhase>[ |
| 49 new SsaRecompilationFieldTypePropagator(backend, work), |
49 new SsaSpeculativeTypePropagator(compiler), | 50 new SsaSpeculativeTypePropagator(compiler), |
50 new SsaTypeGuardInserter(compiler, work), | 51 new SsaTypeGuardInserter(compiler, work), |
51 new SsaEnvironmentBuilder(compiler), | 52 new SsaEnvironmentBuilder(compiler), |
52 // Change the propagated types back to what they were before we | 53 // Change the propagated types back to what they were before we |
53 // speculatively propagated, so that we can generate the bailout | 54 // speculatively propagated, so that we can generate the bailout |
54 // version. | 55 // version. |
55 // Note that we do this even if there were no guards inserted. If a | 56 // Note that we do this even if there were no guards inserted. If a |
56 // guard is not beneficial enough we don't emit one, but there might | 57 // guard is not beneficial enough we don't emit one, but there might |
57 // still be speculative types on the instructions. | 58 // still be speculative types on the instructions. |
58 new SsaTypePropagator(compiler), | 59 new SsaTypePropagator(compiler), |
(...skipping 1090 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1149 } | 1150 } |
1150 | 1151 |
1151 for (HIf ifUser in notIfUsers) { | 1152 for (HIf ifUser in notIfUsers) { |
1152 changeUsesDominatedBy(ifUser.elseBlock, input, convertedType); | 1153 changeUsesDominatedBy(ifUser.elseBlock, input, convertedType); |
1153 // TODO(ngeoffray): Also change uses for the then block on a HType | 1154 // TODO(ngeoffray): Also change uses for the then block on a HType |
1154 // that knows it is not of a specific Type. | 1155 // that knows it is not of a specific Type. |
1155 } | 1156 } |
1156 } | 1157 } |
1157 } | 1158 } |
1158 | 1159 |
1159 class SsaProcessRecompileCandidates | 1160 |
1160 extends HBaseVisitor implements OptimizationPhase { | 1161 // Base class for the handling of recompilation based on inferred |
1161 final String name = "SsaProcessRecompileCandidates"; | 1162 // field types. |
| 1163 class BaseRecompilationVisitor extends HBaseVisitor { |
1162 final JavaScriptBackend backend; | 1164 final JavaScriptBackend backend; |
1163 final WorkItem work; | 1165 final WorkItem work; |
1164 HGraph graph; | |
1165 Compiler get compiler() => backend.compiler; | 1166 Compiler get compiler() => backend.compiler; |
1166 | 1167 |
1167 SsaProcessRecompileCandidates(this.backend, this.work); | 1168 BaseRecompilationVisitor(this.backend, this.work); |
1168 | 1169 |
1169 void visitGraph(HGraph visitee) { | 1170 abstract void handleFieldGet(HFieldGet node, HType type); |
1170 graph = visitee; | 1171 abstract void handleFieldNumberOperation(HFieldGet field, HType type); |
1171 visitDominatorTree(visitee); | |
1172 } | |
1173 | 1172 |
1174 void visitFieldGet(HFieldGet node) { | 1173 // Checks if the binary invocation operates on a field and a |
1175 if (!node.element.enclosingElement.isClass()) return; | 1174 // constant number. If it does [handleFieldNumberOperation] is |
1176 Element field = node.element; | 1175 // called with the field and the type inferred for the field so far. |
1177 HType type = backend.optimisticFieldTypeAfterConstruction(field); | 1176 void checkFieldNumberOperation(HInvokeBinary node) { |
1178 if (!type.isUnknown()) { | |
1179 switch (compiler.phase) { | |
1180 case Compiler.PHASE_COMPILING: | |
1181 // Recompile even if we haven't seen any types for this | |
1182 // field yet. There might still be only one setter in an | |
1183 // initializer list or constructor body. | |
1184 compiler.enqueuer.codegen.registerRecompilationCandidate( | |
1185 work.element); | |
1186 break; | |
1187 case Compiler.PHASE_RECOMPILING: | |
1188 if (!type.isConflicting()) { | |
1189 // Check if optimistic type is based on a setter in the | |
1190 // constructor body. | |
1191 if (backend.hasConstructorBodyFieldSetter(field)) { | |
1192 // If there are no other field setters then the one in | |
1193 // the constructor body, the type is guaranteed for this | |
1194 // field after construction. | |
1195 assert(!node.element.isGenerativeConstructorBody()); | |
1196 if (!compiler.codegenWorld.hasInvokedSetter(field, compiler)) { | |
1197 node.guaranteedType = | |
1198 type.union(backend.fieldSettersTypeSoFar(node.element)); | |
1199 } else { | |
1200 node.propagatedType = | |
1201 type.union(backend.fieldSettersTypeSoFar(node.element)); | |
1202 } | |
1203 } else { | |
1204 // If there are no setters the initializer list type is | |
1205 // guaranteed to remain constant. | |
1206 // | |
1207 // TODO(ager): Why is this treated differently from the | |
1208 // case above? It seems to me that we could/should use | |
1209 // the union of the types for the field setters and the | |
1210 // initializer list here? It would give the same when | |
1211 // there are none and potentially better information for | |
1212 // more cases. | |
1213 if (!compiler.codegenWorld.hasFieldSetter(field, compiler) && | |
1214 !compiler.codegenWorld.hasInvokedSetter(field, compiler)) { | |
1215 node.guaranteedType = type; | |
1216 } else { | |
1217 node.propagatedType = type; | |
1218 } | |
1219 } | |
1220 } | |
1221 break; | |
1222 } | |
1223 } | |
1224 } | |
1225 | |
1226 HInstruction visitEquals(HEquals node) { | |
1227 // Determine if one of the operands is an HFieldGet. | 1177 // Determine if one of the operands is an HFieldGet. |
1228 HFieldGet field; | 1178 HFieldGet field; |
1229 HInstruction other; | 1179 HInstruction other; |
1230 if (node.left is HFieldGet) { | |
1231 field = node.left; | |
1232 other = node.right; | |
1233 } else if (node.right is HFieldGet) { | |
1234 field = node.right; | |
1235 other = node.left; | |
1236 } | |
1237 // Try to optimize the case where a field which is known to always be an | |
1238 // integer is compared with a constant integer literal. | |
1239 if (other != null && | |
1240 other.isConstantInteger() && | |
1241 field.element != null && | |
1242 field.element.enclosingElement.isClass()) { | |
1243 // Calculate the field type from the information available. | |
1244 HType type = | |
1245 backend.fieldSettersTypeSoFar(field.element).union( | |
1246 backend.typeFromInitializersSoFar(field.element)); | |
1247 if (!type.isUnknown()) { | |
1248 switch (compiler.phase) { | |
1249 case Compiler.PHASE_COMPILING: | |
1250 compiler.enqueuer.codegen.registerRecompilationCandidate( | |
1251 work.element); | |
1252 break; | |
1253 case Compiler.PHASE_RECOMPILING: | |
1254 if (compiler.codegenWorld.hasInvokedSetter(field.element, | |
1255 compiler)) { | |
1256 // If there are invoked setters we don't know for sure that the | |
1257 // field will hold the calculated, but the fact that the class | |
1258 // itself stick to this type in the field is still a strong | |
1259 // signal to indicate the expected type of the field. | |
1260 field.propagatedType = type; | |
1261 graph.highTypeLikelyhood = true; | |
1262 } else { | |
1263 // If there are no invoked setters we know the type of this | |
1264 // field for sure. | |
1265 field.guaranteedType = type; | |
1266 } | |
1267 break; | |
1268 default: | |
1269 assert(false); | |
1270 break; | |
1271 } | |
1272 } | |
1273 } | |
1274 } | |
1275 | |
1276 HInstruction visitBinaryArithmetic(HBinaryArithmetic node) { | |
1277 // Determine if one of the operands is an HFieldGet. | |
1278 HFieldGet field; | |
1279 HInstruction other; | |
1280 if (node.left is HFieldGet) { | 1180 if (node.left is HFieldGet) { |
1281 field = node.left; | 1181 field = node.left; |
1282 other = node.right; | 1182 other = node.right; |
1283 } else if (node.right is HFieldGet) { | 1183 } else if (node.right is HFieldGet) { |
1284 field = node.right; | 1184 field = node.right; |
1285 other = node.left; | 1185 other = node.left; |
1286 } | 1186 } |
1287 // Check that the other operand is a number and that we have type | 1187 // Try to optimize the case where a field which is known to always |
1288 // information for the field get. | 1188 // be an integer is compared with a constant number. |
1289 if (other != null && | 1189 if (other != null && |
1290 other.isConstantNumber() && | 1190 other.isConstantNumber() && |
1291 field.element != null && | 1191 field.element != null && |
1292 field.element.enclosingElement.isClass()) { | 1192 field.element.enclosingElement.isClass()) { |
1293 // If we have type information for the field and it contains | 1193 // Calculate the field type from the information available. If |
1294 // NUMBER, we mark for recompilation. | 1194 // we have type information for the field and it contains NUMBER |
| 1195 // we use it as a candidate for recompilation. |
1295 Element fieldElement = field.element; | 1196 Element fieldElement = field.element; |
1296 HType fieldSettersType = backend.fieldSettersTypeSoFar(fieldElement); | 1197 HType fieldSettersType = backend.fieldSettersTypeSoFar(fieldElement); |
1297 HType initializersType = backend.typeFromInitializersSoFar(fieldElement); | 1198 HType initializersType = backend.typeFromInitializersSoFar(fieldElement); |
1298 HType fieldType = fieldSettersType.union(initializersType); | 1199 HType fieldType = fieldSettersType.union(initializersType); |
1299 HType type = HType.NUMBER.union(fieldType); | 1200 HType type = HType.NUMBER.union(fieldType); |
1300 if (type == HType.NUMBER) { | 1201 if (type == HType.NUMBER) { |
1301 switch (compiler.phase) { | 1202 handleFieldNumberOperation(field, fieldType); |
1302 case Compiler.PHASE_COMPILING: | 1203 } |
1303 compiler.enqueuer.codegen.registerRecompilationCandidate( | 1204 } |
1304 work.element); | 1205 } |
1305 break; | 1206 |
1306 case Compiler.PHASE_RECOMPILING: | 1207 void visitFieldGet(HFieldGet node) { |
1307 if (compiler.codegenWorld.hasInvokedSetter(fieldElement, | 1208 if (!node.element.isInstanceMember()) return; |
1308 compiler)) { | 1209 Element field = node.element; |
1309 // If there are invoked setters we don't know for sure | 1210 HType type = backend.optimisticFieldTypeAfterConstruction(field); |
1310 // that the field will hold a value of the calculated | 1211 if (!type.isUnknown()) { |
1311 // type, but the fact that the class itself sticks to | 1212 // Allow handling even if we haven't seen any types for this |
1312 // this type for the field is still a strong signal | 1213 // field yet. There might still be only one setter in an |
1313 // indicating the expected type of the field. | 1214 // initializer list or constructor body and recompilation |
1314 field.propagatedType = type; | 1215 // can therefore pay off. |
1315 graph.highTypeLikelyhood = true; | 1216 handleFieldGet(node, type); |
1316 } else { | 1217 } |
1317 // If there are no invoked setters we know the type of | 1218 } |
1318 // this field for sure. | 1219 |
1319 field.guaranteedType = type; | 1220 HInstruction visitEquals(HEquals node) { |
1320 } | 1221 checkFieldNumberOperation(node); |
1321 break; | 1222 } |
1322 default: | 1223 |
1323 assert(false); | 1224 HInstruction visitBinaryArithmetic(HBinaryArithmetic node) { |
1324 break; | 1225 checkFieldNumberOperation(node); |
| 1226 } |
| 1227 } |
| 1228 |
| 1229 |
| 1230 // Visitor that registers candidates for recompilation. |
| 1231 class SsaRegisterRecompilationCandidates |
| 1232 extends BaseRecompilationVisitor implements OptimizationPhase { |
| 1233 final String name = "SsaRegisterRecompileCandidates"; |
| 1234 HGraph graph; |
| 1235 |
| 1236 SsaRegisterRecompilationCandidates( |
| 1237 JavaScriptBackend backend, WorkItem work) : super(backend, work); |
| 1238 |
| 1239 void visitGraph(HGraph visitee) { |
| 1240 graph = visitee; |
| 1241 if (compiler.phase == Compiler.PHASE_COMPILING) { |
| 1242 visitDominatorTree(visitee); |
| 1243 } |
| 1244 } |
| 1245 |
| 1246 void handleFieldGet(HFieldGet node, HType type) { |
| 1247 assert(compiler.phase == Compiler.PHASE_COMPILING); |
| 1248 compiler.enqueuer.codegen.registerRecompilationCandidate( |
| 1249 work.element); |
| 1250 } |
| 1251 |
| 1252 void handleFieldNumberOperation(HFieldGet node, HType type) { |
| 1253 assert(compiler.phase == Compiler.PHASE_COMPILING); |
| 1254 compiler.enqueuer.codegen.registerRecompilationCandidate( |
| 1255 work.element); |
| 1256 } |
| 1257 } |
| 1258 |
| 1259 |
| 1260 // Visitor that sets the known or suspected type of fields during |
| 1261 // recompilation. |
| 1262 class SsaRecompilationFieldTypePropagator |
| 1263 extends BaseRecompilationVisitor implements OptimizationPhase { |
| 1264 final String name = "SsaRecompilationFieldTypePropagator"; |
| 1265 HGraph graph; |
| 1266 |
| 1267 SsaRecompilationFieldTypePropagator( |
| 1268 JavaScriptBackend backend, WorkItem work) : super(backend, work); |
| 1269 |
| 1270 void visitGraph(HGraph visitee) { |
| 1271 graph = visitee; |
| 1272 if (compiler.phase == Compiler.PHASE_RECOMPILING) { |
| 1273 visitDominatorTree(visitee); |
| 1274 } |
| 1275 } |
| 1276 |
| 1277 void handleFieldGet(HFieldGet field, HType type) { |
| 1278 assert(compiler.phase == Compiler.PHASE_RECOMPILING); |
| 1279 if (!type.isConflicting()) { |
| 1280 Element element = field.element; |
| 1281 // Check if optimistic type is based on a setter in the |
| 1282 // constructor body. |
| 1283 if (backend.hasConstructorBodyFieldSetter(element)) { |
| 1284 // If there are no other field setters then the one in |
| 1285 // the constructor body, the type is guaranteed for this |
| 1286 // field after construction. |
| 1287 assert(!element.isGenerativeConstructorBody()); |
| 1288 if (!compiler.codegenWorld.hasInvokedSetter(element, compiler)) { |
| 1289 field.guaranteedType = |
| 1290 type.union(backend.fieldSettersTypeSoFar(element)); |
| 1291 } else { |
| 1292 field.propagatedType = |
| 1293 type.union(backend.fieldSettersTypeSoFar(element)); |
| 1294 } |
| 1295 } else { |
| 1296 // If there are no setters the initializer list type is |
| 1297 // guaranteed to remain constant. |
| 1298 // |
| 1299 // TODO(ager): Why is this treated differently from the |
| 1300 // case above? It seems to me that we could/should use |
| 1301 // the union of the types for the field setters and the |
| 1302 // initializer list here? It would give the same when |
| 1303 // there are none and potentially better information for |
| 1304 // more cases. |
| 1305 if (!compiler.codegenWorld.hasFieldSetter(element, compiler) && |
| 1306 !compiler.codegenWorld.hasInvokedSetter(element, compiler)) { |
| 1307 field.guaranteedType = type; |
| 1308 } else { |
| 1309 field.propagatedType = type; |
1325 } | 1310 } |
1326 } | 1311 } |
1327 } | 1312 } |
1328 } | 1313 } |
| 1314 |
| 1315 void handleFieldNumberOperation(HFieldGet field, HType type) { |
| 1316 assert(compiler.phase == Compiler.PHASE_RECOMPILING); |
| 1317 if (compiler.codegenWorld.hasInvokedSetter(field.element, compiler)) { |
| 1318 // If there are invoked setters we don't know for sure |
| 1319 // that the field will hold a value of the calculated |
| 1320 // type, but the fact that the class itself sticks to |
| 1321 // this type for the field is still a strong signal |
| 1322 // indicating the expected type of the field. |
| 1323 field.propagatedType = type; |
| 1324 } else { |
| 1325 // If there are no invoked setters we know the type of |
| 1326 // this field for sure. |
| 1327 field.guaranteedType = type; |
| 1328 } |
| 1329 } |
1329 } | 1330 } |
OLD | NEW |