| 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 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 104 /// of identifying gvn-able lengths and mis-identifies some unions of fixed | 104 /// of identifying gvn-able lengths and mis-identifies some unions of fixed |
| 105 /// length indexables (see TODO) as not fixed length. | 105 /// length indexables (see TODO) as not fixed length. |
| 106 bool isFixedLength(mask, Compiler compiler) { | 106 bool isFixedLength(mask, Compiler compiler) { |
| 107 ClassWorld classWorld = compiler.world; | 107 ClassWorld classWorld = compiler.world; |
| 108 JavaScriptBackend backend = compiler.backend; | 108 JavaScriptBackend backend = compiler.backend; |
| 109 if (mask.isContainer && mask.length != null) { | 109 if (mask.isContainer && mask.length != null) { |
| 110 // A container on which we have inferred the length. | 110 // A container on which we have inferred the length. |
| 111 return true; | 111 return true; |
| 112 } | 112 } |
| 113 // TODO(sra): Recognize any combination of fixed length indexables. | 113 // TODO(sra): Recognize any combination of fixed length indexables. |
| 114 if (mask.containsOnly(backend.jsFixedArrayClass) || | 114 if (mask.containsOnly(backend.helpers.jsFixedArrayClass) || |
| 115 mask.containsOnly(backend.jsUnmodifiableArrayClass) || | 115 mask.containsOnly(backend.helpers.jsUnmodifiableArrayClass) || |
| 116 mask.containsOnlyString(classWorld) || | 116 mask.containsOnlyString(classWorld) || |
| 117 backend.isTypedArray(mask)) { | 117 backend.isTypedArray(mask)) { |
| 118 return true; | 118 return true; |
| 119 } | 119 } |
| 120 return false; | 120 return false; |
| 121 } | 121 } |
| 122 | 122 |
| 123 /** | 123 /** |
| 124 * If both inputs to known operations are available execute the operation at | 124 * If both inputs to known operations are available execute the operation at |
| 125 * compile-time. | 125 * compile-time. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 140 Compiler get compiler => backend.compiler; | 140 Compiler get compiler => backend.compiler; |
| 141 final SsaOptimizerTask optimizer; | 141 final SsaOptimizerTask optimizer; |
| 142 | 142 |
| 143 SsaInstructionSimplifier(this.constantSystem, | 143 SsaInstructionSimplifier(this.constantSystem, |
| 144 this.backend, | 144 this.backend, |
| 145 this.optimizer, | 145 this.optimizer, |
| 146 this.work); | 146 this.work); |
| 147 | 147 |
| 148 CoreClasses get coreClasses => compiler.coreClasses; | 148 CoreClasses get coreClasses => compiler.coreClasses; |
| 149 | 149 |
| 150 BackendHelpers get helpers => backend.helpers; |
| 151 |
| 150 void visitGraph(HGraph visitee) { | 152 void visitGraph(HGraph visitee) { |
| 151 graph = visitee; | 153 graph = visitee; |
| 152 visitDominatorTree(visitee); | 154 visitDominatorTree(visitee); |
| 153 } | 155 } |
| 154 | 156 |
| 155 visitBasicBlock(HBasicBlock block) { | 157 visitBasicBlock(HBasicBlock block) { |
| 156 HInstruction instruction = block.first; | 158 HInstruction instruction = block.first; |
| 157 while (instruction != null) { | 159 while (instruction != null) { |
| 158 HInstruction next = instruction.next; | 160 HInstruction next = instruction.next; |
| 159 HInstruction replacement = instruction.accept(this); | 161 HInstruction replacement = instruction.accept(this); |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 245 | 247 |
| 246 // If the code is unreachable, remove the HBoolify. This can happen when | 248 // If the code is unreachable, remove the HBoolify. This can happen when |
| 247 // there is a throw expression in a short-circuit conditional. Removing the | 249 // there is a throw expression in a short-circuit conditional. Removing the |
| 248 // unreachable HBoolify makes it easier to reconstruct the short-circuit | 250 // unreachable HBoolify makes it easier to reconstruct the short-circuit |
| 249 // operation. | 251 // operation. |
| 250 if (input.instructionType.isEmpty && !input.instructionType.isNullable) | 252 if (input.instructionType.isEmpty && !input.instructionType.isNullable) |
| 251 return input; | 253 return input; |
| 252 | 254 |
| 253 // All values that cannot be 'true' are boolified to false. | 255 // All values that cannot be 'true' are boolified to false. |
| 254 TypeMask mask = input.instructionType; | 256 TypeMask mask = input.instructionType; |
| 255 if (!mask.contains(backend.jsBoolClass, compiler.world)) { | 257 if (!mask.contains(helpers.jsBoolClass, compiler.world)) { |
| 256 return graph.addConstantBool(false, compiler); | 258 return graph.addConstantBool(false, compiler); |
| 257 } | 259 } |
| 258 return node; | 260 return node; |
| 259 } | 261 } |
| 260 | 262 |
| 261 HInstruction visitNot(HNot node) { | 263 HInstruction visitNot(HNot node) { |
| 262 List<HInstruction> inputs = node.inputs; | 264 List<HInstruction> inputs = node.inputs; |
| 263 assert(inputs.length == 1); | 265 assert(inputs.length == 1); |
| 264 HInstruction input = inputs[0]; | 266 HInstruction input = inputs[0]; |
| 265 if (input is HConstant) { | 267 if (input is HConstant) { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 292 if (actualReceiver.isIndexablePrimitive(compiler)) { | 294 if (actualReceiver.isIndexablePrimitive(compiler)) { |
| 293 if (actualReceiver.isConstantString()) { | 295 if (actualReceiver.isConstantString()) { |
| 294 HConstant constantInput = actualReceiver; | 296 HConstant constantInput = actualReceiver; |
| 295 StringConstantValue constant = constantInput.constant; | 297 StringConstantValue constant = constantInput.constant; |
| 296 return graph.addConstantInt(constant.length, compiler); | 298 return graph.addConstantInt(constant.length, compiler); |
| 297 } else if (actualReceiver.isConstantList()) { | 299 } else if (actualReceiver.isConstantList()) { |
| 298 HConstant constantInput = actualReceiver; | 300 HConstant constantInput = actualReceiver; |
| 299 ListConstantValue constant = constantInput.constant; | 301 ListConstantValue constant = constantInput.constant; |
| 300 return graph.addConstantInt(constant.length, compiler); | 302 return graph.addConstantInt(constant.length, compiler); |
| 301 } | 303 } |
| 302 Element element = backend.jsIndexableLength; | 304 Element element = helpers.jsIndexableLength; |
| 303 bool isFixed = isFixedLength(actualReceiver.instructionType, compiler); | 305 bool isFixed = isFixedLength(actualReceiver.instructionType, compiler); |
| 304 TypeMask actualType = node.instructionType; | 306 TypeMask actualType = node.instructionType; |
| 305 ClassWorld classWorld = compiler.world; | 307 ClassWorld classWorld = compiler.world; |
| 306 TypeMask resultType = backend.positiveIntType; | 308 TypeMask resultType = backend.positiveIntType; |
| 307 // If we already have computed a more specific type, keep that type. | 309 // If we already have computed a more specific type, keep that type. |
| 308 if (HInstruction.isInstanceOf( | 310 if (HInstruction.isInstanceOf( |
| 309 actualType, backend.jsUInt31Class, classWorld)) { | 311 actualType, helpers.jsUInt31Class, classWorld)) { |
| 310 resultType = backend.uint31Type; | 312 resultType = backend.uint31Type; |
| 311 } else if (HInstruction.isInstanceOf( | 313 } else if (HInstruction.isInstanceOf( |
| 312 actualType, backend.jsUInt32Class, classWorld)) { | 314 actualType, helpers.jsUInt32Class, classWorld)) { |
| 313 resultType = backend.uint32Type; | 315 resultType = backend.uint32Type; |
| 314 } | 316 } |
| 315 HFieldGet result = new HFieldGet( | 317 HFieldGet result = new HFieldGet( |
| 316 element, actualReceiver, resultType, | 318 element, actualReceiver, resultType, |
| 317 isAssignable: !isFixed); | 319 isAssignable: !isFixed); |
| 318 return result; | 320 return result; |
| 319 } else if (actualReceiver.isConstantMap()) { | 321 } else if (actualReceiver.isConstantMap()) { |
| 320 HConstant constantInput = actualReceiver; | 322 HConstant constantInput = actualReceiver; |
| 321 MapConstantValue constant = constantInput.constant; | 323 MapConstantValue constant = constantInput.constant; |
| 322 return graph.addConstantInt(constant.length, compiler); | 324 return graph.addConstantInt(constant.length, compiler); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 346 World world = compiler.world; | 348 World world = compiler.world; |
| 347 | 349 |
| 348 bool applies(Element element) { | 350 bool applies(Element element) { |
| 349 return selector.applies(element, world) && | 351 return selector.applies(element, world) && |
| 350 (mask == null || mask.canHit(element, selector, world)); | 352 (mask == null || mask.canHit(element, selector, world)); |
| 351 } | 353 } |
| 352 | 354 |
| 353 if (selector.isCall || selector.isOperator) { | 355 if (selector.isCall || selector.isOperator) { |
| 354 Element target; | 356 Element target; |
| 355 if (input.isExtendableArray(compiler)) { | 357 if (input.isExtendableArray(compiler)) { |
| 356 if (applies(backend.jsArrayRemoveLast)) { | 358 if (applies(helpers.jsArrayRemoveLast)) { |
| 357 target = backend.jsArrayRemoveLast; | 359 target = helpers.jsArrayRemoveLast; |
| 358 } else if (applies(backend.jsArrayAdd)) { | 360 } else if (applies(helpers.jsArrayAdd)) { |
| 359 // The codegen special cases array calls, but does not | 361 // The codegen special cases array calls, but does not |
| 360 // inline argument type checks. | 362 // inline argument type checks. |
| 361 if (!compiler.enableTypeAssertions) { | 363 if (!compiler.enableTypeAssertions) { |
| 362 target = backend.jsArrayAdd; | 364 target = helpers.jsArrayAdd; |
| 363 } | 365 } |
| 364 } | 366 } |
| 365 } else if (input.isStringOrNull(compiler)) { | 367 } else if (input.isStringOrNull(compiler)) { |
| 366 if (applies(backend.jsStringSplit)) { | 368 if (applies(helpers.jsStringSplit)) { |
| 367 HInstruction argument = node.inputs[2]; | 369 HInstruction argument = node.inputs[2]; |
| 368 if (argument.isString(compiler)) { | 370 if (argument.isString(compiler)) { |
| 369 target = backend.jsStringSplit; | 371 target = helpers.jsStringSplit; |
| 370 } | 372 } |
| 371 } else if (applies(backend.jsStringOperatorAdd)) { | 373 } else if (applies(helpers.jsStringOperatorAdd)) { |
| 372 // `operator+` is turned into a JavaScript '+' so we need to | 374 // `operator+` is turned into a JavaScript '+' so we need to |
| 373 // make sure the receiver and the argument are not null. | 375 // make sure the receiver and the argument are not null. |
| 374 // TODO(sra): Do this via [node.specializer]. | 376 // TODO(sra): Do this via [node.specializer]. |
| 375 HInstruction argument = node.inputs[2]; | 377 HInstruction argument = node.inputs[2]; |
| 376 if (argument.isString(compiler) | 378 if (argument.isString(compiler) |
| 377 && !input.canBeNull()) { | 379 && !input.canBeNull()) { |
| 378 return new HStringConcat(input, argument, null, | 380 return new HStringConcat(input, argument, null, |
| 379 node.instructionType); | 381 node.instructionType); |
| 380 } | 382 } |
| 381 } else if (applies(backend.jsStringToString) | 383 } else if (applies(helpers.jsStringToString) |
| 382 && !input.canBeNull()) { | 384 && !input.canBeNull()) { |
| 383 return input; | 385 return input; |
| 384 } | 386 } |
| 385 } | 387 } |
| 386 if (target != null) { | 388 if (target != null) { |
| 387 // TODO(ngeoffray): There is a strong dependency between codegen | 389 // TODO(ngeoffray): There is a strong dependency between codegen |
| 388 // and this optimization that the dynamic invoke does not need an | 390 // and this optimization that the dynamic invoke does not need an |
| 389 // interceptor. We currently need to keep a | 391 // interceptor. We currently need to keep a |
| 390 // HInvokeDynamicMethod and not create a HForeign because | 392 // HInvokeDynamicMethod and not create a HForeign because |
| 391 // HForeign is too opaque for the SsaCheckInserter (that adds a | 393 // HForeign is too opaque for the SsaCheckInserter (that adds a |
| 392 // bounds check on removeLast). Once we start inlining, the | 394 // bounds check on removeLast). Once we start inlining, the |
| 393 // bounds check will become explicit, so we won't need this | 395 // bounds check will become explicit, so we won't need this |
| 394 // optimization. | 396 // optimization. |
| 395 HInvokeDynamicMethod result = new HInvokeDynamicMethod( | 397 HInvokeDynamicMethod result = new HInvokeDynamicMethod( |
| 396 node.selector, node.mask, | 398 node.selector, node.mask, |
| 397 node.inputs.sublist(1), node.instructionType); | 399 node.inputs.sublist(1), node.instructionType); |
| 398 result.element = target; | 400 result.element = target; |
| 399 return result; | 401 return result; |
| 400 } | 402 } |
| 401 } else if (selector.isGetter) { | 403 } else if (selector.isGetter) { |
| 402 if (selector.applies(backend.jsIndexableLength, world)) { | 404 if (selector.applies(helpers.jsIndexableLength, world)) { |
| 403 HInstruction optimized = tryOptimizeLengthInterceptedGetter(node); | 405 HInstruction optimized = tryOptimizeLengthInterceptedGetter(node); |
| 404 if (optimized != null) return optimized; | 406 if (optimized != null) return optimized; |
| 405 } | 407 } |
| 406 } | 408 } |
| 407 | 409 |
| 408 return node; | 410 return node; |
| 409 } | 411 } |
| 410 | 412 |
| 411 HInstruction visitInvokeDynamicMethod(HInvokeDynamicMethod node) { | 413 HInstruction visitInvokeDynamicMethod(HInvokeDynamicMethod node) { |
| 412 propagateConstantValueToUses(node); | 414 propagateConstantValueToUses(node); |
| (...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 776 | 778 |
| 777 VariableElement findConcreteFieldForDynamicAccess(HInstruction receiver, | 779 VariableElement findConcreteFieldForDynamicAccess(HInstruction receiver, |
| 778 Selector selector) { | 780 Selector selector) { |
| 779 TypeMask receiverType = receiver.instructionType; | 781 TypeMask receiverType = receiver.instructionType; |
| 780 return compiler.world.locateSingleField(selector, receiverType); | 782 return compiler.world.locateSingleField(selector, receiverType); |
| 781 } | 783 } |
| 782 | 784 |
| 783 HInstruction visitFieldGet(HFieldGet node) { | 785 HInstruction visitFieldGet(HFieldGet node) { |
| 784 if (node.isNullCheck) return node; | 786 if (node.isNullCheck) return node; |
| 785 var receiver = node.receiver; | 787 var receiver = node.receiver; |
| 786 if (node.element == backend.jsIndexableLength) { | 788 if (node.element == helpers.jsIndexableLength) { |
| 787 JavaScriptItemCompilationContext context = work.compilationContext; | 789 JavaScriptItemCompilationContext context = work.compilationContext; |
| 788 if (context.allocatedFixedLists.contains(receiver)) { | 790 if (context.allocatedFixedLists.contains(receiver)) { |
| 789 // TODO(ngeoffray): checking if the second input is an integer | 791 // TODO(ngeoffray): checking if the second input is an integer |
| 790 // should not be necessary but it currently makes it easier for | 792 // should not be necessary but it currently makes it easier for |
| 791 // other optimizations to reason about a fixed length constructor | 793 // other optimizations to reason about a fixed length constructor |
| 792 // that we know takes an int. | 794 // that we know takes an int. |
| 793 if (receiver.inputs[0].isInteger(compiler)) { | 795 if (receiver.inputs[0].isInteger(compiler)) { |
| 794 return receiver.inputs[0]; | 796 return receiver.inputs[0]; |
| 795 } | 797 } |
| 796 } else if (receiver.isConstantList() || receiver.isConstantString()) { | 798 } else if (receiver.isConstantList() || receiver.isConstantString()) { |
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 998 final bool trustPrimitives; | 1000 final bool trustPrimitives; |
| 999 final JavaScriptBackend backend; | 1001 final JavaScriptBackend backend; |
| 1000 final String name = "SsaCheckInserter"; | 1002 final String name = "SsaCheckInserter"; |
| 1001 HGraph graph; | 1003 HGraph graph; |
| 1002 | 1004 |
| 1003 SsaCheckInserter(this.trustPrimitives, | 1005 SsaCheckInserter(this.trustPrimitives, |
| 1004 this.backend, | 1006 this.backend, |
| 1005 this.work, | 1007 this.work, |
| 1006 this.boundsChecked); | 1008 this.boundsChecked); |
| 1007 | 1009 |
| 1010 BackendHelpers get helpers => backend.helpers; |
| 1011 |
| 1008 void visitGraph(HGraph graph) { | 1012 void visitGraph(HGraph graph) { |
| 1009 this.graph = graph; | 1013 this.graph = graph; |
| 1010 | 1014 |
| 1011 // In --trust-primitives mode we don't add bounds checks. This is better | 1015 // In --trust-primitives mode we don't add bounds checks. This is better |
| 1012 // than trying to remove them later as the limit expression would become | 1016 // than trying to remove them later as the limit expression would become |
| 1013 // dead and require DCE. | 1017 // dead and require DCE. |
| 1014 if (trustPrimitives) return; | 1018 if (trustPrimitives) return; |
| 1015 | 1019 |
| 1016 visitDominatorTree(graph); | 1020 visitDominatorTree(graph); |
| 1017 } | 1021 } |
| 1018 | 1022 |
| 1019 void visitBasicBlock(HBasicBlock block) { | 1023 void visitBasicBlock(HBasicBlock block) { |
| 1020 HInstruction instruction = block.first; | 1024 HInstruction instruction = block.first; |
| 1021 while (instruction != null) { | 1025 while (instruction != null) { |
| 1022 HInstruction next = instruction.next; | 1026 HInstruction next = instruction.next; |
| 1023 instruction = instruction.accept(this); | 1027 instruction = instruction.accept(this); |
| 1024 instruction = next; | 1028 instruction = next; |
| 1025 } | 1029 } |
| 1026 } | 1030 } |
| 1027 | 1031 |
| 1028 HBoundsCheck insertBoundsCheck(HInstruction indexNode, | 1032 HBoundsCheck insertBoundsCheck(HInstruction indexNode, |
| 1029 HInstruction array, | 1033 HInstruction array, |
| 1030 HInstruction indexArgument) { | 1034 HInstruction indexArgument) { |
| 1031 Compiler compiler = backend.compiler; | 1035 Compiler compiler = backend.compiler; |
| 1032 HFieldGet length = new HFieldGet( | 1036 HFieldGet length = new HFieldGet( |
| 1033 backend.jsIndexableLength, array, backend.positiveIntType, | 1037 helpers.jsIndexableLength, array, backend.positiveIntType, |
| 1034 isAssignable: !isFixedLength(array.instructionType, compiler)); | 1038 isAssignable: !isFixedLength(array.instructionType, compiler)); |
| 1035 indexNode.block.addBefore(indexNode, length); | 1039 indexNode.block.addBefore(indexNode, length); |
| 1036 | 1040 |
| 1037 TypeMask type = indexArgument.isPositiveInteger(compiler) | 1041 TypeMask type = indexArgument.isPositiveInteger(compiler) |
| 1038 ? indexArgument.instructionType | 1042 ? indexArgument.instructionType |
| 1039 : backend.positiveIntType; | 1043 : backend.positiveIntType; |
| 1040 HBoundsCheck check = new HBoundsCheck( | 1044 HBoundsCheck check = new HBoundsCheck( |
| 1041 indexArgument, length, array, type); | 1045 indexArgument, length, array, type); |
| 1042 indexNode.block.addBefore(indexNode, check); | 1046 indexNode.block.addBefore(indexNode, check); |
| 1043 // If the index input to the bounds check was not known to be an integer | 1047 // If the index input to the bounds check was not known to be an integer |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1062 | 1066 |
| 1063 void visitIndexAssign(HIndexAssign node) { | 1067 void visitIndexAssign(HIndexAssign node) { |
| 1064 if (boundsChecked.contains(node)) return; | 1068 if (boundsChecked.contains(node)) return; |
| 1065 HInstruction index = node.index; | 1069 HInstruction index = node.index; |
| 1066 index = insertBoundsCheck(node, node.receiver, index); | 1070 index = insertBoundsCheck(node, node.receiver, index); |
| 1067 } | 1071 } |
| 1068 | 1072 |
| 1069 void visitInvokeDynamicMethod(HInvokeDynamicMethod node) { | 1073 void visitInvokeDynamicMethod(HInvokeDynamicMethod node) { |
| 1070 Element element = node.element; | 1074 Element element = node.element; |
| 1071 if (node.isInterceptedCall) return; | 1075 if (node.isInterceptedCall) return; |
| 1072 if (element != backend.jsArrayRemoveLast) return; | 1076 if (element != helpers.jsArrayRemoveLast) return; |
| 1073 if (boundsChecked.contains(node)) return; | 1077 if (boundsChecked.contains(node)) return; |
| 1074 // `0` is the index we want to check, but we want to report `-1`, as if we | 1078 // `0` is the index we want to check, but we want to report `-1`, as if we |
| 1075 // executed `a[a.length-1]` | 1079 // executed `a[a.length-1]` |
| 1076 HBoundsCheck check = insertBoundsCheck( | 1080 HBoundsCheck check = insertBoundsCheck( |
| 1077 node, node.receiver, graph.addConstantInt(0, backend.compiler)); | 1081 node, node.receiver, graph.addConstantInt(0, backend.compiler)); |
| 1078 HInstruction minusOne = graph.addConstantInt(-1, backend.compiler); | 1082 HInstruction minusOne = graph.addConstantInt(-1, backend.compiler); |
| 1079 check.inputs.add(minusOne); | 1083 check.inputs.add(minusOne); |
| 1080 minusOne.usedBy.add(check); | 1084 minusOne.usedBy.add(check); |
| 1081 } | 1085 } |
| 1082 } | 1086 } |
| (...skipping 1315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2398 | 2402 |
| 2399 keyedValues.forEach((receiver, values) { | 2403 keyedValues.forEach((receiver, values) { |
| 2400 result.keyedValues[receiver] = | 2404 result.keyedValues[receiver] = |
| 2401 new Map<HInstruction, HInstruction>.from(values); | 2405 new Map<HInstruction, HInstruction>.from(values); |
| 2402 }); | 2406 }); |
| 2403 | 2407 |
| 2404 result.nonEscapingReceivers.addAll(nonEscapingReceivers); | 2408 result.nonEscapingReceivers.addAll(nonEscapingReceivers); |
| 2405 return result; | 2409 return result; |
| 2406 } | 2410 } |
| 2407 } | 2411 } |
| OLD | NEW |