| 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 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem; | 5 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem; |
| 6 import '../common/names.dart' show Selectors; | 6 import '../common/names.dart' show Selectors; |
| 7 import '../common/tasks.dart' show CompilerTask; | 7 import '../common/tasks.dart' show CompilerTask; |
| 8 import '../compiler.dart' show Compiler; | 8 import '../compiler.dart' show Compiler; |
| 9 import '../constants/constant_system.dart'; | 9 import '../constants/constant_system.dart'; |
| 10 import '../constants/values.dart'; | 10 import '../constants/values.dart'; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 78 trustPrimitives, backend, closedWorld, boundsChecked), | 78 trustPrimitives, backend, closedWorld, boundsChecked), |
| 79 new SsaTypePropagator(compiler, closedWorld), | 79 new SsaTypePropagator(compiler, closedWorld), |
| 80 // Run a dead code eliminator before LICM because dead | 80 // Run a dead code eliminator before LICM because dead |
| 81 // interceptors are often in the way of LICM'able instructions. | 81 // interceptors are often in the way of LICM'able instructions. |
| 82 new SsaDeadCodeEliminator(closedWorld, this), | 82 new SsaDeadCodeEliminator(closedWorld, this), |
| 83 new SsaGlobalValueNumberer(), | 83 new SsaGlobalValueNumberer(), |
| 84 // After GVN, some instructions might need their type to be | 84 // After GVN, some instructions might need their type to be |
| 85 // updated because they now have different inputs. | 85 // updated because they now have different inputs. |
| 86 new SsaTypePropagator(compiler, closedWorld), | 86 new SsaTypePropagator(compiler, closedWorld), |
| 87 codeMotion = new SsaCodeMotion(), | 87 codeMotion = new SsaCodeMotion(), |
| 88 new SsaLoadElimination(compiler, closedWorld), | 88 new SsaLoadElimination(backend, compiler, closedWorld), |
| 89 new SsaRedundantPhiEliminator(), | 89 new SsaRedundantPhiEliminator(), |
| 90 new SsaDeadPhiEliminator(), | 90 new SsaDeadPhiEliminator(), |
| 91 // After GVN and load elimination the same value may be used in code | 91 // After GVN and load elimination the same value may be used in code |
| 92 // controlled by a test on the value, so redo 'conversion insertion' to | 92 // controlled by a test on the value, so redo 'conversion insertion' to |
| 93 // learn from the refined type. | 93 // learn from the refined type. |
| 94 new SsaTypeConversionInserter(closedWorld), | 94 new SsaTypeConversionInserter(closedWorld), |
| 95 new SsaTypePropagator(compiler, closedWorld), | 95 new SsaTypePropagator(compiler, closedWorld), |
| 96 new SsaValueRangeAnalyzer(backend.helpers, closedWorld, this), | 96 new SsaValueRangeAnalyzer(backend.helpers, closedWorld, this), |
| 97 // Previous optimizations may have generated new | 97 // Previous optimizations may have generated new |
| 98 // opportunities for instruction simplification. | 98 // opportunities for instruction simplification. |
| (...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 342 if (actualReceiver.isIndexablePrimitive(closedWorld)) { | 342 if (actualReceiver.isIndexablePrimitive(closedWorld)) { |
| 343 if (actualReceiver.isConstantString()) { | 343 if (actualReceiver.isConstantString()) { |
| 344 HConstant constantInput = actualReceiver; | 344 HConstant constantInput = actualReceiver; |
| 345 StringConstantValue constant = constantInput.constant; | 345 StringConstantValue constant = constantInput.constant; |
| 346 return graph.addConstantInt(constant.length, closedWorld); | 346 return graph.addConstantInt(constant.length, closedWorld); |
| 347 } else if (actualReceiver.isConstantList()) { | 347 } else if (actualReceiver.isConstantList()) { |
| 348 HConstant constantInput = actualReceiver; | 348 HConstant constantInput = actualReceiver; |
| 349 ListConstantValue constant = constantInput.constant; | 349 ListConstantValue constant = constantInput.constant; |
| 350 return graph.addConstantInt(constant.length, closedWorld); | 350 return graph.addConstantInt(constant.length, closedWorld); |
| 351 } | 351 } |
| 352 MemberEntity element = helpers.jsIndexableLength; | |
| 353 bool isFixed = isFixedLength(actualReceiver.instructionType, closedWorld); | 352 bool isFixed = isFixedLength(actualReceiver.instructionType, closedWorld); |
| 354 TypeMask actualType = node.instructionType; | 353 TypeMask actualType = node.instructionType; |
| 355 TypeMask resultType = closedWorld.commonMasks.positiveIntType; | 354 TypeMask resultType = closedWorld.commonMasks.positiveIntType; |
| 356 // If we already have computed a more specific type, keep that type. | 355 // If we already have computed a more specific type, keep that type. |
| 357 if (HInstruction.isInstanceOf( | 356 if (HInstruction.isInstanceOf( |
| 358 actualType, helpers.jsUInt31Class, closedWorld)) { | 357 actualType, helpers.jsUInt31Class, closedWorld)) { |
| 359 resultType = closedWorld.commonMasks.uint31Type; | 358 resultType = closedWorld.commonMasks.uint31Type; |
| 360 } else if (HInstruction.isInstanceOf( | 359 } else if (HInstruction.isInstanceOf( |
| 361 actualType, helpers.jsUInt32Class, closedWorld)) { | 360 actualType, helpers.jsUInt32Class, closedWorld)) { |
| 362 resultType = closedWorld.commonMasks.uint32Type; | 361 resultType = closedWorld.commonMasks.uint32Type; |
| 363 } | 362 } |
| 364 HFieldGet result = new HFieldGet(element, actualReceiver, resultType, | 363 HGetLength result = |
| 365 isAssignable: !isFixed); | 364 new HGetLength(actualReceiver, resultType, isAssignable: !isFixed); |
| 366 return result; | 365 return result; |
| 367 } else if (actualReceiver.isConstantMap()) { | 366 } else if (actualReceiver.isConstantMap()) { |
| 368 HConstant constantInput = actualReceiver; | 367 HConstant constantInput = actualReceiver; |
| 369 MapConstantValue constant = constantInput.constant; | 368 MapConstantValue constant = constantInput.constant; |
| 370 return graph.addConstantInt(constant.length, closedWorld); | 369 return graph.addConstantInt(constant.length, closedWorld); |
| 371 } | 370 } |
| 372 return null; | 371 return null; |
| 373 } | 372 } |
| 374 | 373 |
| 375 HInstruction handleInterceptedCall(HInvokeDynamic node) { | 374 HInstruction handleInterceptedCall(HInvokeDynamic node) { |
| (...skipping 486 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 862 | 861 |
| 863 FieldEntity findConcreteFieldForDynamicAccess( | 862 FieldEntity findConcreteFieldForDynamicAccess( |
| 864 HInstruction receiver, Selector selector) { | 863 HInstruction receiver, Selector selector) { |
| 865 TypeMask receiverType = receiver.instructionType; | 864 TypeMask receiverType = receiver.instructionType; |
| 866 return closedWorld.locateSingleField(selector, receiverType); | 865 return closedWorld.locateSingleField(selector, receiverType); |
| 867 } | 866 } |
| 868 | 867 |
| 869 HInstruction visitFieldGet(HFieldGet node) { | 868 HInstruction visitFieldGet(HFieldGet node) { |
| 870 if (node.isNullCheck) return node; | 869 if (node.isNullCheck) return node; |
| 871 var receiver = node.receiver; | 870 var receiver = node.receiver; |
| 872 if (node.element == helpers.jsIndexableLength) { | |
| 873 if (graph.allocatedFixedLists.contains(receiver)) { | |
| 874 // TODO(ngeoffray): checking if the second input is an integer | |
| 875 // should not be necessary but it currently makes it easier for | |
| 876 // other optimizations to reason about a fixed length constructor | |
| 877 // that we know takes an int. | |
| 878 if (receiver.inputs[0].isInteger(closedWorld)) { | |
| 879 return receiver.inputs[0]; | |
| 880 } | |
| 881 } else if (receiver.isConstantList() || receiver.isConstantString()) { | |
| 882 return graph.addConstantInt(receiver.constant.length, closedWorld); | |
| 883 } else { | |
| 884 var type = receiver.instructionType; | |
| 885 if (type.isContainer && type.length != null) { | |
| 886 HInstruction constant = | |
| 887 graph.addConstantInt(type.length, closedWorld); | |
| 888 if (type.isNullable) { | |
| 889 // If the container can be null, we update all uses of the | |
| 890 // length access to use the constant instead, but keep the | |
| 891 // length access in the graph, to ensure we still have a | |
| 892 // null check. | |
| 893 node.block.rewrite(node, constant); | |
| 894 return node; | |
| 895 } else { | |
| 896 return constant; | |
| 897 } | |
| 898 } | |
| 899 } | |
| 900 } | |
| 901 | 871 |
| 902 // HFieldGet of a constructed constant can be replaced with the constant's | 872 // HFieldGet of a constructed constant can be replaced with the constant's |
| 903 // field. | 873 // field. |
| 904 if (receiver is HConstant) { | 874 if (receiver is HConstant) { |
| 905 ConstantValue constant = receiver.constant; | 875 ConstantValue constant = receiver.constant; |
| 906 if (constant.isConstructedObject) { | 876 if (constant.isConstructedObject) { |
| 907 ConstructedConstantValue constructedConstant = constant; | 877 ConstructedConstantValue constructedConstant = constant; |
| 908 Map<FieldEntity, ConstantValue> fields = constructedConstant.fields; | 878 Map<FieldEntity, ConstantValue> fields = constructedConstant.fields; |
| 909 ConstantValue value = fields[node.element]; | 879 ConstantValue value = fields[node.element]; |
| 910 if (value != null) { | 880 if (value != null) { |
| 911 return graph.addConstant(value, closedWorld); | 881 return graph.addConstant(value, closedWorld); |
| 912 } | 882 } |
| 913 } | 883 } |
| 914 } | 884 } |
| 915 | 885 |
| 916 return node; | 886 return node; |
| 917 } | 887 } |
| 918 | 888 |
| 889 HInstruction visitGetLength(HGetLength node) { |
| 890 var receiver = node.receiver; |
| 891 if (graph.allocatedFixedLists.contains(receiver)) { |
| 892 // TODO(ngeoffray): checking if the second input is an integer |
| 893 // should not be necessary but it currently makes it easier for |
| 894 // other optimizations to reason about a fixed length constructor |
| 895 // that we know takes an int. |
| 896 if (receiver.inputs[0].isInteger(closedWorld)) { |
| 897 return receiver.inputs[0]; |
| 898 } |
| 899 } else if (receiver.isConstantList() || receiver.isConstantString()) { |
| 900 return graph.addConstantInt(receiver.constant.length, closedWorld); |
| 901 } else { |
| 902 var type = receiver.instructionType; |
| 903 if (type.isContainer && type.length != null) { |
| 904 HInstruction constant = graph.addConstantInt(type.length, closedWorld); |
| 905 if (type.isNullable) { |
| 906 // If the container can be null, we update all uses of the |
| 907 // length access to use the constant instead, but keep the |
| 908 // length access in the graph, to ensure we still have a |
| 909 // null check. |
| 910 node.block.rewrite(node, constant); |
| 911 return node; |
| 912 } else { |
| 913 return constant; |
| 914 } |
| 915 } |
| 916 } |
| 917 return node; |
| 918 } |
| 919 |
| 919 HInstruction visitIndex(HIndex node) { | 920 HInstruction visitIndex(HIndex node) { |
| 920 if (node.receiver.isConstantList() && node.index.isConstantInteger()) { | 921 if (node.receiver.isConstantList() && node.index.isConstantInteger()) { |
| 921 var instruction = node.receiver; | 922 var instruction = node.receiver; |
| 922 List<ConstantValue> entries = instruction.constant.entries; | 923 List<ConstantValue> entries = instruction.constant.entries; |
| 923 instruction = node.index; | 924 instruction = node.index; |
| 924 int index = instruction.constant.primitiveValue; | 925 int index = instruction.constant.primitiveValue; |
| 925 if (index >= 0 && index < entries.length) { | 926 if (index >= 0 && index < entries.length) { |
| 926 return graph.addConstant(entries[index], closedWorld); | 927 return graph.addConstant(entries[index], closedWorld); |
| 927 } | 928 } |
| 928 } | 929 } |
| (...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1341 HInstruction instruction = block.first; | 1342 HInstruction instruction = block.first; |
| 1342 while (instruction != null) { | 1343 while (instruction != null) { |
| 1343 HInstruction next = instruction.next; | 1344 HInstruction next = instruction.next; |
| 1344 instruction = instruction.accept(this); | 1345 instruction = instruction.accept(this); |
| 1345 instruction = next; | 1346 instruction = next; |
| 1346 } | 1347 } |
| 1347 } | 1348 } |
| 1348 | 1349 |
| 1349 HBoundsCheck insertBoundsCheck( | 1350 HBoundsCheck insertBoundsCheck( |
| 1350 HInstruction indexNode, HInstruction array, HInstruction indexArgument) { | 1351 HInstruction indexNode, HInstruction array, HInstruction indexArgument) { |
| 1351 HFieldGet length = new HFieldGet(helpers.jsIndexableLength, array, | 1352 HGetLength length = new HGetLength( |
| 1352 closedWorld.commonMasks.positiveIntType, | 1353 array, closedWorld.commonMasks.positiveIntType, |
| 1353 isAssignable: !isFixedLength(array.instructionType, closedWorld)); | 1354 isAssignable: !isFixedLength(array.instructionType, closedWorld)); |
| 1354 indexNode.block.addBefore(indexNode, length); | 1355 indexNode.block.addBefore(indexNode, length); |
| 1355 | 1356 |
| 1356 TypeMask type = indexArgument.isPositiveInteger(closedWorld) | 1357 TypeMask type = indexArgument.isPositiveInteger(closedWorld) |
| 1357 ? indexArgument.instructionType | 1358 ? indexArgument.instructionType |
| 1358 : closedWorld.commonMasks.positiveIntType; | 1359 : closedWorld.commonMasks.positiveIntType; |
| 1359 HBoundsCheck check = new HBoundsCheck(indexArgument, length, array, type); | 1360 HBoundsCheck check = new HBoundsCheck(indexArgument, length, array, type); |
| 1360 indexNode.block.addBefore(indexNode, check); | 1361 indexNode.block.addBefore(indexNode, check); |
| 1361 // If the index input to the bounds check was not known to be an integer | 1362 // If the index input to the bounds check was not known to be an integer |
| 1362 // then we replace its uses with the bounds check, which is known to be an | 1363 // then we replace its uses with the bounds check, which is known to be an |
| (...skipping 875 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2238 } | 2239 } |
| 2239 } | 2240 } |
| 2240 } | 2241 } |
| 2241 | 2242 |
| 2242 /** | 2243 /** |
| 2243 * Optimization phase that tries to eliminate memory loads (for | 2244 * Optimization phase that tries to eliminate memory loads (for |
| 2244 * example [HFieldGet]), when it knows the value stored in that memory | 2245 * example [HFieldGet]), when it knows the value stored in that memory |
| 2245 * location. | 2246 * location. |
| 2246 */ | 2247 */ |
| 2247 class SsaLoadElimination extends HBaseVisitor implements OptimizationPhase { | 2248 class SsaLoadElimination extends HBaseVisitor implements OptimizationPhase { |
| 2249 final JavaScriptBackend backend; |
| 2248 final Compiler compiler; | 2250 final Compiler compiler; |
| 2249 final ClosedWorld closedWorld; | 2251 final ClosedWorld closedWorld; |
| 2250 final String name = "SsaLoadElimination"; | 2252 final String name = "SsaLoadElimination"; |
| 2251 MemorySet memorySet; | 2253 MemorySet memorySet; |
| 2252 List<MemorySet> memories; | 2254 List<MemorySet> memories; |
| 2253 | 2255 |
| 2254 SsaLoadElimination(this.compiler, this.closedWorld); | 2256 SsaLoadElimination(this.backend, this.compiler, this.closedWorld); |
| 2255 | 2257 |
| 2256 void visitGraph(HGraph graph) { | 2258 void visitGraph(HGraph graph) { |
| 2257 memories = new List<MemorySet>(graph.blocks.length); | 2259 memories = new List<MemorySet>(graph.blocks.length); |
| 2258 List<HBasicBlock> blocks = graph.blocks; | 2260 List<HBasicBlock> blocks = graph.blocks; |
| 2259 for (int i = 0; i < blocks.length; i++) { | 2261 for (int i = 0; i < blocks.length; i++) { |
| 2260 HBasicBlock block = blocks[i]; | 2262 HBasicBlock block = blocks[i]; |
| 2261 visitBasicBlock(block); | 2263 visitBasicBlock(block); |
| 2262 if (block.successors.isNotEmpty && block.successors[0].isLoopHeader()) { | 2264 if (block.successors.isNotEmpty && block.successors[0].isLoopHeader()) { |
| 2263 // We've reached the ending block of a loop. Iterate over the | 2265 // We've reached the ending block of a loop. Iterate over the |
| 2264 // blocks of the loop again to take values that flow from that | 2266 // blocks of the loop again to take values that flow from that |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2299 HInstruction instruction = block.first; | 2301 HInstruction instruction = block.first; |
| 2300 while (instruction != null) { | 2302 while (instruction != null) { |
| 2301 HInstruction next = instruction.next; | 2303 HInstruction next = instruction.next; |
| 2302 instruction.accept(this); | 2304 instruction.accept(this); |
| 2303 instruction = next; | 2305 instruction = next; |
| 2304 } | 2306 } |
| 2305 } | 2307 } |
| 2306 | 2308 |
| 2307 void visitFieldGet(HFieldGet instruction) { | 2309 void visitFieldGet(HFieldGet instruction) { |
| 2308 if (instruction.isNullCheck) return; | 2310 if (instruction.isNullCheck) return; |
| 2309 MemberEntity element = instruction.element; | 2311 FieldEntity element = instruction.element; |
| 2310 HInstruction receiver = instruction.getDartReceiver(closedWorld).nonCheck(); | 2312 HInstruction receiver = instruction.getDartReceiver(closedWorld).nonCheck(); |
| 2313 _visitFieldGet(element, receiver, instruction); |
| 2314 } |
| 2315 |
| 2316 void visitGetLength(HGetLength instruction) { |
| 2317 _visitFieldGet(backend.helpers.jsIndexableLength, |
| 2318 instruction.receiver.nonCheck(), instruction); |
| 2319 } |
| 2320 |
| 2321 void _visitFieldGet( |
| 2322 MemberEntity element, HInstruction receiver, HInstruction instruction) { |
| 2311 HInstruction existing = memorySet.lookupFieldValue(element, receiver); | 2323 HInstruction existing = memorySet.lookupFieldValue(element, receiver); |
| 2312 if (existing != null) { | 2324 if (existing != null) { |
| 2313 instruction.block.rewriteWithBetterUser(instruction, existing); | 2325 instruction.block.rewriteWithBetterUser(instruction, existing); |
| 2314 instruction.block.remove(instruction); | 2326 instruction.block.remove(instruction); |
| 2315 } else { | 2327 } else { |
| 2316 memorySet.registerFieldValue(element, receiver, instruction); | 2328 memorySet.registerFieldValue(element, receiver, instruction); |
| 2317 } | 2329 } |
| 2318 } | 2330 } |
| 2319 | 2331 |
| 2320 void visitFieldSet(HFieldSet instruction) { | 2332 void visitFieldSet(HFieldSet instruction) { |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2457 * other checks removed to ensure that checks and type refinements do not | 2469 * other checks removed to ensure that checks and type refinements do not |
| 2458 * confuse aliasing. Values stored into a memory place keep the type | 2470 * confuse aliasing. Values stored into a memory place keep the type |
| 2459 * refinements to help further optimizations. | 2471 * refinements to help further optimizations. |
| 2460 */ | 2472 */ |
| 2461 class MemorySet { | 2473 class MemorySet { |
| 2462 final ClosedWorld closedWorld; | 2474 final ClosedWorld closedWorld; |
| 2463 | 2475 |
| 2464 /** | 2476 /** |
| 2465 * Maps a field to a map of receiver to value. | 2477 * Maps a field to a map of receiver to value. |
| 2466 */ | 2478 */ |
| 2479 // The key is [MemberEntity] rather than [FieldEntity] so that HGetLength can |
| 2480 // be modeled as the JSIndexable.length abstract getter. |
| 2481 // TODO(25544): Split length effects from other effects and model lengths |
| 2482 // separately. |
| 2467 final Map<MemberEntity, Map<HInstruction, HInstruction>> fieldValues = | 2483 final Map<MemberEntity, Map<HInstruction, HInstruction>> fieldValues = |
| 2468 <MemberEntity, Map<HInstruction, HInstruction>>{}; | 2484 <MemberEntity, Map<HInstruction, HInstruction>>{}; |
| 2469 | 2485 |
| 2470 /** | 2486 /** |
| 2471 * Maps a receiver to a map of keys to value. | 2487 * Maps a receiver to a map of keys to value. |
| 2472 */ | 2488 */ |
| 2473 final Map<HInstruction, Map<HInstruction, HInstruction>> keyedValues = | 2489 final Map<HInstruction, Map<HInstruction, HInstruction>> keyedValues = |
| 2474 <HInstruction, Map<HInstruction, HInstruction>>{}; | 2490 <HInstruction, Map<HInstruction, HInstruction>>{}; |
| 2475 | 2491 |
| 2476 /** | 2492 /** |
| (...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2764 | 2780 |
| 2765 keyedValues.forEach((receiver, values) { | 2781 keyedValues.forEach((receiver, values) { |
| 2766 result.keyedValues[receiver] = | 2782 result.keyedValues[receiver] = |
| 2767 new Map<HInstruction, HInstruction>.from(values); | 2783 new Map<HInstruction, HInstruction>.from(values); |
| 2768 }); | 2784 }); |
| 2769 | 2785 |
| 2770 result.nonEscapingReceivers.addAll(nonEscapingReceivers); | 2786 result.nonEscapingReceivers.addAll(nonEscapingReceivers); |
| 2771 return result; | 2787 return result; |
| 2772 } | 2788 } |
| 2773 } | 2789 } |
| OLD | NEW |