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

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: Final round of changes Created 8 years, 2 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 {
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
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
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;
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) {
1272 Element field = node.element; 1253 // Create a new empty map for the first block.
1273 if (field != null) { 1254 currentFieldSetters = new Map<Element, HType>();
1274 HType type = backend.optimisticFieldTypeAfterConstruction(field); 1255 } else {
1275 if (!type.isUnknown()) { 1256 // Build a map which intersects the fields from all predecessors. For
1276 // Allow handling even if we haven't seen any types for this 1257 // each field in this intersection it unions the types.
1277 // field yet. There might still be only one setter in an 1258 currentFieldSetters =
1278 // initializer list or constructor body and recompilation 1259 new Map.from(blockFieldSetters[block.predecessors[0]]);
1279 // can therefore pay off. 1260 // Loop headers are the only nodes with back edges.
1280 handleFieldGet(node, type); 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);
1281 } 1276 }
1282 } 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 if (phi.inputs.indexOf(thisInstruction) != -1) {
1293 thisUsers.addAll(phi.usedBy);
1294 }
1283 } 1295 }
1284 1296
1285 HInstruction visitEquals(HEquals node) { 1297 visitThis(HThis instruction) {
1286 checkFieldNumberOperation(node); 1298 thisInstruction = instruction;
1299 // Collect all users of this in a set to make the this exposed check simple
1300 // and cheap.
1301 thisUsers.addAll(instruction.usedBy);
1287 } 1302 }
1288 1303
1289 HInstruction visitBinaryArithmetic(HBinaryArithmetic node) { 1304 visitFieldGet(HInstruction _) {
1290 checkFieldNumberOperation(node); 1305 // The field get instruction is allowed to use this.
1291 } 1306 }
1292 }
1293 1307
1308 visitForeignNew(HForeignNew node) {
1309 // The HForeignNew instruction is used in the generative constructor to
1310 // initialize all fields in newly created objects. The fields are
1311 // initialized to the value present in the initializer list or set to null
1312 // if not otherwise initialized.
1313 // Here we handle members in superclasses as well, as the handling of
1314 // the generative constructor bodies will ensure, that the initializer
1315 // type will not be used if the field is in any of these.
1316 int j = 0;
1317 node.element.forEachInstanceField(
1318 includeBackendMembers: false,
1319 includeSuperMembers: true,
1320 f: (ClassElement enclosingClass, Element element) {
1321 backend.registerFieldInitializer(element, types[node.inputs[j]]);
1322 j++;
1323 });
1324 }
1294 1325
1295 // Visitor that registers candidates for recompilation. 1326 visitFieldSet(HFieldSet node) {
1296 class SsaRegisterRecompilationCandidates 1327 Element field = node.element;
1297 extends BaseRecompilationVisitor implements OptimizationPhase { 1328 HInstruction value = node.value;
1298 final String name = "SsaRegisterRecompileCandidates"; 1329 HType type = types[value];
1299 HGraph graph; 1330 allSetters.add(field);
1300 1331 // Don't handle fields defined in superclasses. Given that the field is
1301 SsaRegisterRecompilationCandidates(JavaScriptBackend backend, 1332 // always added to the [allSetters] set, setting a field defined in a
1302 WorkItem work, 1333 // superclass will get an inferred type of UNKNOWN.
1303 HTypeMap types) 1334 if (work.element.getEnclosingClass() === field.getEnclosingClass() &&
1304 : super(backend, work, types); 1335 value.hasGuaranteedType()) {
1305 1336 currentFieldSetters[field] = type;
1306 void visitGraph(HGraph visitee) {
1307 graph = visitee;
1308 if (compiler.phase == Compiler.PHASE_COMPILING) {
1309 visitDominatorTree(visitee);
1310 } 1337 }
1311 } 1338 }
1312 1339
1313 void handleFieldGet(HFieldGet node, HType type) { 1340 visitExit(HExit node) {
1314 assert(compiler.phase == Compiler.PHASE_COMPILING); 1341 // If this has been exposed then we cannot say anything about types after
1315 compiler.enqueuer.codegen.registerRecompilationCandidate( 1342 // construction.
1316 work.element); 1343 if (!thisExposed) {
1317 } 1344 // Register the known field types.
1345 currentFieldSetters.forEach((Element element, HType type) {
1346 backend.registerFieldConstructor(element, type);
1347 allSetters.remove(element);
1348 });
1349 }
1318 1350
1319 void handleFieldNumberOperation(HFieldGet node, HType type) { 1351 // For other fields having setters in the generative constructor body, set
1320 assert(compiler.phase == Compiler.PHASE_COMPILING); 1352 // the type to UNKNOWN to avoid relying on the type set in the initializer
1321 compiler.enqueuer.codegen.registerRecompilationCandidate( 1353 // list.
1322 work.element); 1354 allSetters.forEach((Element element) {
1355 backend.registerFieldConstructor(element, HType.UNKNOWN);
1356 });
1323 } 1357 }
1324 } 1358 }
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
« no previous file with comments | « lib/compiler/implementation/ssa/codegen.dart ('k') | lib/compiler/implementation/universe/function_set.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698