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 |