| 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 part of ssa; | 5 part of ssa; |
| 6 | 6 |
| 7 abstract class OptimizationPhase { | 7 abstract class OptimizationPhase { |
| 8 String get name; | 8 String get name; |
| 9 void visitGraph(HGraph graph); | 9 void visitGraph(HGraph graph); |
| 10 } | 10 } |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 52 new SsaNonSpeculativeTypePropagator(compiler), | 52 new SsaNonSpeculativeTypePropagator(compiler), |
| 53 new SsaGlobalValueNumberer(compiler), | 53 new SsaGlobalValueNumberer(compiler), |
| 54 new SsaCodeMotion(), | 54 new SsaCodeMotion(), |
| 55 new SsaValueRangeAnalyzer(compiler, constantSystem, work), | 55 new SsaValueRangeAnalyzer(compiler, constantSystem, work), |
| 56 // Previous optimizations may have generated new | 56 // Previous optimizations may have generated new |
| 57 // opportunities for constant folding. | 57 // opportunities for constant folding. |
| 58 new SsaConstantFolder(constantSystem, backend, work), | 58 new SsaConstantFolder(constantSystem, backend, work), |
| 59 new SsaSimplifyInterceptors(compiler, constantSystem, work), | 59 new SsaSimplifyInterceptors(compiler, constantSystem, work), |
| 60 new SsaDeadCodeEliminator()]; | 60 new SsaDeadCodeEliminator()]; |
| 61 runPhases(graph, phases); | 61 runPhases(graph, phases); |
| 62 if (!speculative) { | |
| 63 runPhase(graph, new SsaConstructionFieldTypes(backend, work)); | |
| 64 } | |
| 65 }); | 62 }); |
| 66 } | 63 } |
| 67 | 64 |
| 68 bool trySpeculativeOptimizations(CodegenWorkItem work, HGraph graph) { | 65 bool trySpeculativeOptimizations(CodegenWorkItem work, HGraph graph) { |
| 69 if (work.element.isField()) { | 66 if (work.element.isField()) { |
| 70 // Lazy initializers may not have bailout methods. | 67 // Lazy initializers may not have bailout methods. |
| 71 return false; | 68 return false; |
| 72 } | 69 } |
| 73 JavaScriptItemCompilationContext context = work.compilationContext; | 70 JavaScriptItemCompilationContext context = work.compilationContext; |
| 74 ConstantSystem constantSystem = compiler.backend.constantSystem; | 71 ConstantSystem constantSystem = compiler.backend.constantSystem; |
| (...skipping 611 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 686 isAssignable = true; | 683 isAssignable = true; |
| 687 } | 684 } |
| 688 HFieldGet result = new HFieldGet( | 685 HFieldGet result = new HFieldGet( |
| 689 field, receiver, isAssignable: isAssignable); | 686 field, receiver, isAssignable: isAssignable); |
| 690 | 687 |
| 691 if (field.getEnclosingClass().isNative()) { | 688 if (field.getEnclosingClass().isNative()) { |
| 692 result.instructionType = new HType.fromNativeBehavior( | 689 result.instructionType = new HType.fromNativeBehavior( |
| 693 native.NativeBehavior.ofFieldLoad(field, compiler), | 690 native.NativeBehavior.ofFieldLoad(field, compiler), |
| 694 compiler); | 691 compiler); |
| 695 } else { | 692 } else { |
| 696 HType type = new HType.inferredTypeForElement(field, compiler); | 693 result.instructionType = |
| 697 if (type.isUnknown()) { | 694 new HType.inferredTypeForElement(field, compiler); |
| 698 type = backend.optimisticFieldType(field); | |
| 699 if (type != null) { | |
| 700 backend.registerFieldTypesOptimization( | |
| 701 work.element, field, result.instructionType); | |
| 702 } | |
| 703 } | |
| 704 if (type != null) { | |
| 705 result.instructionType = type; | |
| 706 } | |
| 707 } | 695 } |
| 708 return result; | 696 return result; |
| 709 } | 697 } |
| 710 | 698 |
| 711 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { | 699 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { |
| 712 if (node.isInterceptedCall) { | 700 if (node.isInterceptedCall) { |
| 713 HInstruction folded = handleInterceptedCall(node); | 701 HInstruction folded = handleInterceptedCall(node); |
| 714 if (folded != node) return folded; | 702 if (folded != node) return folded; |
| 715 } | 703 } |
| 716 | 704 |
| (...skipping 623 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1340 // that knows it is not of a specific Type. | 1328 // that knows it is not of a specific Type. |
| 1341 } | 1329 } |
| 1342 | 1330 |
| 1343 for (HIf ifUser in notIfUsers) { | 1331 for (HIf ifUser in notIfUsers) { |
| 1344 changeUsesDominatedBy(ifUser.elseBlock, input, convertedType); | 1332 changeUsesDominatedBy(ifUser.elseBlock, input, convertedType); |
| 1345 // TODO(ngeoffray): Also change uses for the then block on a HType | 1333 // TODO(ngeoffray): Also change uses for the then block on a HType |
| 1346 // that knows it is not of a specific Type. | 1334 // that knows it is not of a specific Type. |
| 1347 } | 1335 } |
| 1348 } | 1336 } |
| 1349 } | 1337 } |
| 1350 | |
| 1351 | |
| 1352 // Analyze the constructors to see if some fields will always have a specific | |
| 1353 // type after construction. If this is the case we can ignore the type given | |
| 1354 // by the field initializer. This is especially useful when the field | |
| 1355 // initializer is initializing the field to null. | |
| 1356 class SsaConstructionFieldTypes | |
| 1357 extends HBaseVisitor implements OptimizationPhase { | |
| 1358 final JavaScriptBackend backend; | |
| 1359 final CodegenWorkItem work; | |
| 1360 final String name = "SsaConstructionFieldTypes"; | |
| 1361 final Set<HInstruction> thisUsers; | |
| 1362 final Set<Element> allSetters; | |
| 1363 final Map<HBasicBlock, Map<Element, HType>> blockFieldSetters; | |
| 1364 bool thisExposed = false; | |
| 1365 HGraph currentGraph; | |
| 1366 Map<Element, HType> currentFieldSetters; | |
| 1367 | |
| 1368 SsaConstructionFieldTypes(this.backend, this.work) | |
| 1369 : thisUsers = new Set<HInstruction>(), | |
| 1370 allSetters = new Set<Element>(), | |
| 1371 blockFieldSetters = new Map<HBasicBlock, Map<Element, HType>>(); | |
| 1372 | |
| 1373 void visitGraph(HGraph graph) { | |
| 1374 currentGraph = graph; | |
| 1375 if (!work.element.isGenerativeConstructorBody() && | |
| 1376 !work.element.isGenerativeConstructor()) return; | |
| 1377 visitDominatorTree(graph); | |
| 1378 if (work.element.isGenerativeConstructor()) { | |
| 1379 backend.registerConstructor(work.element); | |
| 1380 } | |
| 1381 } | |
| 1382 | |
| 1383 visitBasicBlock(HBasicBlock block) { | |
| 1384 if (block.predecessors.length == 0) { | |
| 1385 // Create a new empty map for the first block. | |
| 1386 currentFieldSetters = new Map<Element, HType>(); | |
| 1387 } else { | |
| 1388 // Build a map which intersects the fields from all predecessors. For | |
| 1389 // each field in this intersection it unions the types. | |
| 1390 currentFieldSetters = | |
| 1391 new Map.from(blockFieldSetters[block.predecessors[0]]); | |
| 1392 // Loop headers are the only nodes with back edges. | |
| 1393 if (!block.isLoopHeader()) { | |
| 1394 for (int i = 1; i < block.predecessors.length; i++) { | |
| 1395 Map<Element, HType> predecessorsFieldSetters = | |
| 1396 blockFieldSetters[block.predecessors[i]]; | |
| 1397 Map<Element, HType> newFieldSetters = new Map<Element, HType>(); | |
| 1398 predecessorsFieldSetters.forEach((Element element, HType type) { | |
| 1399 HType currentType = currentFieldSetters[element]; | |
| 1400 if (currentType != null) { | |
| 1401 newFieldSetters[element] = | |
| 1402 currentType.union(type, backend.compiler); | |
| 1403 } | |
| 1404 }); | |
| 1405 currentFieldSetters = newFieldSetters; | |
| 1406 } | |
| 1407 } else { | |
| 1408 assert(block.predecessors.length <= 2); | |
| 1409 } | |
| 1410 } | |
| 1411 block.forEachPhi((HPhi phi) => phi.accept(this)); | |
| 1412 block.forEachInstruction( | |
| 1413 (HInstruction instruction) => instruction.accept(this)); | |
| 1414 assert(currentFieldSetters != null); | |
| 1415 blockFieldSetters[block] = currentFieldSetters; | |
| 1416 } | |
| 1417 | |
| 1418 visitInstruction(HInstruction instruction) { | |
| 1419 // All instructions not explicitly handled below will flag the this | |
| 1420 // exposure if using this. | |
| 1421 thisExposed = thisExposed || thisUsers.contains(instruction); | |
| 1422 } | |
| 1423 | |
| 1424 visitPhi(HPhi phi) { | |
| 1425 if (thisUsers.contains(phi)) { | |
| 1426 thisUsers.addAll(phi.usedBy); | |
| 1427 } | |
| 1428 } | |
| 1429 | |
| 1430 visitThis(HThis instruction) { | |
| 1431 // Collect all users of this in a set to make the this exposed check simple | |
| 1432 // and cheap. | |
| 1433 thisUsers.addAll(instruction.usedBy); | |
| 1434 } | |
| 1435 | |
| 1436 visitFieldGet(HInstruction _) { | |
| 1437 // The field get instruction is allowed to use this. | |
| 1438 } | |
| 1439 | |
| 1440 visitForeignNew(HForeignNew node) { | |
| 1441 if (!work.element.isGenerativeConstructor()) return; | |
| 1442 // Check if this is the new object allocated by this generative | |
| 1443 // constructor. Inlining might add other [HForeignNew] | |
| 1444 // instructions in the graph. | |
| 1445 if (!node.usedBy.any((user) => user is HReturn)) return; | |
| 1446 // The HForeignNew instruction is used in the generative constructor to | |
| 1447 // initialize all fields in newly created objects. The fields are | |
| 1448 // initialized to the value present in the initializer list or set to null | |
| 1449 // if not otherwise initialized. | |
| 1450 // Here we handle members in superclasses as well, as the handling of | |
| 1451 // the generative constructor bodies will ensure, that the initializer | |
| 1452 // type will not be used if the field is in any of these. | |
| 1453 int j = 0; | |
| 1454 node.element.forEachInstanceField( | |
| 1455 (ClassElement enclosingClass, Element element) { | |
| 1456 backend.registerFieldInitializer( | |
| 1457 element, node.inputs[j].instructionType); | |
| 1458 j++; | |
| 1459 }, | |
| 1460 includeSuperAndInjectedMembers: true); | |
| 1461 } | |
| 1462 | |
| 1463 visitFieldSet(HFieldSet node) { | |
| 1464 Element field = node.element; | |
| 1465 HInstruction value = node.value; | |
| 1466 HType type = value.instructionType; | |
| 1467 // [HFieldSet] is also used for variables in try/catch. | |
| 1468 if (field.isField()) allSetters.add(field); | |
| 1469 // Don't handle fields defined in superclasses. Given that the field is | |
| 1470 // always added to the [allSetters] set, setting a field defined in a | |
| 1471 // superclass will get an inferred type of UNKNOWN. | |
| 1472 if (work.element.getEnclosingClass() == field.getEnclosingClass()) { | |
| 1473 currentFieldSetters[field] = type; | |
| 1474 } | |
| 1475 } | |
| 1476 | |
| 1477 visitExit(HExit node) { | |
| 1478 // If this has been exposed then we cannot say anything about types after | |
| 1479 // construction. | |
| 1480 if (!thisExposed) { | |
| 1481 // Register the known field types. | |
| 1482 currentFieldSetters.forEach((Element element, HType type) { | |
| 1483 if (type.isUnknown()) return; | |
| 1484 backend.registerFieldConstructor(element, type); | |
| 1485 allSetters.remove(element); | |
| 1486 }); | |
| 1487 } | |
| 1488 | |
| 1489 // For other fields having setters in the generative constructor body, set | |
| 1490 // the type to UNKNOWN to avoid relying on the type set in the initializer | |
| 1491 // list. | |
| 1492 allSetters.forEach((Element element) { | |
| 1493 backend.registerFieldConstructor(element, HType.UNKNOWN); | |
| 1494 }); | |
| 1495 } | |
| 1496 } | |
| OLD | NEW |