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

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

Issue 10702167: Fix bug in recompilation handling. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments. Created 8 years, 5 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 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
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
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 }
OLDNEW
« no previous file with comments | « lib/compiler/implementation/ssa/nodes.dart ('k') | tests/language/field_optimization3_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698