| 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'; |
| 11 import '../common_elements.dart' show CommonElements; | 11 import '../common_elements.dart' show CommonElements; |
| 12 import '../elements/elements.dart' | 12 import '../elements/elements.dart' |
| 13 show ClassElement, FieldElement, MethodElement; | 13 show ClassElement, FieldElement, MethodElement; |
| 14 import '../elements/entities.dart'; | 14 import '../elements/entities.dart'; |
| 15 import '../elements/resolution_types.dart'; | 15 import '../elements/resolution_types.dart'; |
| 16 import '../js/js.dart' as js; | 16 import '../js/js.dart' as js; |
| 17 import '../js_backend/backend_helpers.dart' show BackendHelpers; | 17 import '../js_backend/backend_helpers.dart' show BackendHelpers; |
| 18 import '../js_backend/js_backend.dart'; | 18 import '../js_backend/js_backend.dart'; |
| 19 import '../js_backend/interceptor_data.dart' show InterceptorData; |
| 20 import '../js_backend/native_data.dart' show NativeData; |
| 19 import '../native/native.dart' as native; | 21 import '../native/native.dart' as native; |
| 22 import '../options.dart'; |
| 20 import '../tree/dartstring.dart' as ast; | 23 import '../tree/dartstring.dart' as ast; |
| 21 import '../types/types.dart'; | 24 import '../types/types.dart'; |
| 22 import '../universe/selector.dart' show Selector; | 25 import '../universe/selector.dart' show Selector; |
| 23 import '../universe/side_effects.dart' show SideEffects; | 26 import '../universe/side_effects.dart' show SideEffects; |
| 24 import '../util/util.dart'; | 27 import '../util/util.dart'; |
| 25 import '../world.dart' show ClosedWorld; | 28 import '../world.dart' show ClosedWorld; |
| 26 import 'interceptor_simplifier.dart'; | 29 import 'interceptor_simplifier.dart'; |
| 27 import 'nodes.dart'; | 30 import 'nodes.dart'; |
| 28 import 'types.dart'; | 31 import 'types.dart'; |
| 29 import 'types_propagation.dart'; | 32 import 'types_propagation.dart'; |
| 30 import 'value_range_analyzer.dart'; | 33 import 'value_range_analyzer.dart'; |
| 31 import 'value_set.dart'; | 34 import 'value_set.dart'; |
| 32 | 35 |
| 33 abstract class OptimizationPhase { | 36 abstract class OptimizationPhase { |
| 34 String get name; | 37 String get name; |
| 35 void visitGraph(HGraph graph); | 38 void visitGraph(HGraph graph); |
| 36 } | 39 } |
| 37 | 40 |
| 38 class SsaOptimizerTask extends CompilerTask { | 41 class SsaOptimizerTask extends CompilerTask { |
| 39 final JavaScriptBackend backend; | 42 final JavaScriptBackend _backend; |
| 40 | 43 |
| 41 Map<HInstruction, Range> ranges = <HInstruction, Range>{}; | 44 Map<HInstruction, Range> ranges = <HInstruction, Range>{}; |
| 42 | 45 |
| 43 SsaOptimizerTask(JavaScriptBackend backend) | 46 SsaOptimizerTask(this._backend) : super(_backend.compiler.measurer); |
| 44 : this.backend = backend, | |
| 45 super(backend.compiler.measurer); | |
| 46 | 47 |
| 47 String get name => 'SSA optimizer'; | 48 String get name => 'SSA optimizer'; |
| 48 | 49 |
| 49 Compiler get compiler => backend.compiler; | 50 Compiler get _compiler => _backend.compiler; |
| 51 |
| 52 GlobalTypeInferenceResults get _results => _compiler.globalInference.results; |
| 53 |
| 54 BackendHelpers get _helpers => _backend.helpers; |
| 55 |
| 56 CompilerOptions get _options => _compiler.options; |
| 57 |
| 58 RuntimeTypesSubstitutions get _rtiSubstitutions => _backend.rtiSubstitutions; |
| 59 |
| 60 InterceptorData get _interceptorData => _backend.interceptorData; |
| 50 | 61 |
| 51 void optimize(CodegenWorkItem work, HGraph graph, ClosedWorld closedWorld) { | 62 void optimize(CodegenWorkItem work, HGraph graph, ClosedWorld closedWorld) { |
| 52 void runPhase(OptimizationPhase phase) { | 63 void runPhase(OptimizationPhase phase) { |
| 53 measureSubtask(phase.name, () => phase.visitGraph(graph)); | 64 measureSubtask(phase.name, () => phase.visitGraph(graph)); |
| 54 backend.tracer.traceGraph(phase.name, graph); | 65 _backend.tracer.traceGraph(phase.name, graph); |
| 55 assert(graph.isValid()); | 66 assert(graph.isValid()); |
| 56 } | 67 } |
| 57 | 68 |
| 58 bool trustPrimitives = compiler.options.trustPrimitives; | 69 bool trustPrimitives = _options.trustPrimitives; |
| 59 CodegenRegistry registry = work.registry; | 70 CodegenRegistry registry = work.registry; |
| 60 Set<HInstruction> boundsChecked = new Set<HInstruction>(); | 71 Set<HInstruction> boundsChecked = new Set<HInstruction>(); |
| 61 SsaCodeMotion codeMotion; | 72 SsaCodeMotion codeMotion; |
| 62 SsaLoadElimination loadElimination; | 73 SsaLoadElimination loadElimination; |
| 63 measure(() { | 74 measure(() { |
| 64 List<OptimizationPhase> phases = <OptimizationPhase>[ | 75 List<OptimizationPhase> phases = <OptimizationPhase>[ |
| 65 // Run trivial instruction simplification first to optimize | 76 // Run trivial instruction simplification first to optimize |
| 66 // some patterns useful for type conversion. | 77 // some patterns useful for type conversion. |
| 67 new SsaInstructionSimplifier(backend, closedWorld, this, registry), | 78 new SsaInstructionSimplifier(_results, _options, _helpers, |
| 79 _rtiSubstitutions, closedWorld, registry), |
| 68 new SsaTypeConversionInserter(closedWorld), | 80 new SsaTypeConversionInserter(closedWorld), |
| 69 new SsaRedundantPhiEliminator(), | 81 new SsaRedundantPhiEliminator(), |
| 70 new SsaDeadPhiEliminator(), | 82 new SsaDeadPhiEliminator(), |
| 71 new SsaTypePropagator(compiler, closedWorld), | 83 new SsaTypePropagator(_results, _options, _helpers, closedWorld), |
| 72 // After type propagation, more instructions can be | 84 // After type propagation, more instructions can be |
| 73 // simplified. | 85 // simplified. |
| 74 new SsaInstructionSimplifier(backend, closedWorld, this, registry), | 86 new SsaInstructionSimplifier(_results, _options, _helpers, |
| 87 _rtiSubstitutions, closedWorld, registry), |
| 75 new SsaCheckInserter( | 88 new SsaCheckInserter( |
| 76 trustPrimitives, backend, closedWorld, boundsChecked), | 89 trustPrimitives, _helpers, closedWorld, boundsChecked), |
| 77 new SsaInstructionSimplifier(backend, closedWorld, this, registry), | 90 new SsaInstructionSimplifier(_results, _options, _helpers, |
| 91 _rtiSubstitutions, closedWorld, registry), |
| 78 new SsaCheckInserter( | 92 new SsaCheckInserter( |
| 79 trustPrimitives, backend, closedWorld, boundsChecked), | 93 trustPrimitives, _helpers, closedWorld, boundsChecked), |
| 80 new SsaTypePropagator(compiler, closedWorld), | 94 new SsaTypePropagator(_results, _options, _helpers, closedWorld), |
| 81 // Run a dead code eliminator before LICM because dead | 95 // Run a dead code eliminator before LICM because dead |
| 82 // interceptors are often in the way of LICM'able instructions. | 96 // interceptors are often in the way of LICM'able instructions. |
| 83 new SsaDeadCodeEliminator(closedWorld, this), | 97 new SsaDeadCodeEliminator(closedWorld, this), |
| 84 new SsaGlobalValueNumberer(), | 98 new SsaGlobalValueNumberer(), |
| 85 // After GVN, some instructions might need their type to be | 99 // After GVN, some instructions might need their type to be |
| 86 // updated because they now have different inputs. | 100 // updated because they now have different inputs. |
| 87 new SsaTypePropagator(compiler, closedWorld), | 101 new SsaTypePropagator(_results, _options, _helpers, closedWorld), |
| 88 codeMotion = new SsaCodeMotion(), | 102 codeMotion = new SsaCodeMotion(), |
| 89 loadElimination = | 103 loadElimination = |
| 90 new SsaLoadElimination(backend, compiler, closedWorld), | 104 new SsaLoadElimination(_helpers, _compiler, closedWorld), |
| 91 new SsaRedundantPhiEliminator(), | 105 new SsaRedundantPhiEliminator(), |
| 92 new SsaDeadPhiEliminator(), | 106 new SsaDeadPhiEliminator(), |
| 93 // After GVN and load elimination the same value may be used in code | 107 // After GVN and load elimination the same value may be used in code |
| 94 // controlled by a test on the value, so redo 'conversion insertion' to | 108 // controlled by a test on the value, so redo 'conversion insertion' to |
| 95 // learn from the refined type. | 109 // learn from the refined type. |
| 96 new SsaTypeConversionInserter(closedWorld), | 110 new SsaTypeConversionInserter(closedWorld), |
| 97 new SsaTypePropagator(compiler, closedWorld), | 111 new SsaTypePropagator(_results, _options, _helpers, closedWorld), |
| 98 new SsaValueRangeAnalyzer(backend.helpers, closedWorld, this), | 112 new SsaValueRangeAnalyzer(_helpers, closedWorld, this), |
| 99 // Previous optimizations may have generated new | 113 // Previous optimizations may have generated new |
| 100 // opportunities for instruction simplification. | 114 // opportunities for instruction simplification. |
| 101 new SsaInstructionSimplifier(backend, closedWorld, this, registry), | 115 new SsaInstructionSimplifier(_results, _options, _helpers, |
| 116 _rtiSubstitutions, closedWorld, registry), |
| 102 new SsaCheckInserter( | 117 new SsaCheckInserter( |
| 103 trustPrimitives, backend, closedWorld, boundsChecked), | 118 trustPrimitives, _helpers, closedWorld, boundsChecked), |
| 104 ]; | 119 ]; |
| 105 phases.forEach(runPhase); | 120 phases.forEach(runPhase); |
| 106 | 121 |
| 107 // Simplifying interceptors is not strictly just an optimization, it is | 122 // Simplifying interceptors is not strictly just an optimization, it is |
| 108 // required for implementation correctness because the code generator | 123 // required for implementation correctness because the code generator |
| 109 // assumes it is always performed. | 124 // assumes it is always performed. |
| 110 runPhase(new SsaSimplifyInterceptors( | 125 runPhase(new SsaSimplifyInterceptors(closedWorld, _helpers, |
| 111 compiler, closedWorld, work.element.enclosingClass)); | 126 _interceptorData, work.element.enclosingClass)); |
| 112 | 127 |
| 113 SsaDeadCodeEliminator dce = new SsaDeadCodeEliminator(closedWorld, this); | 128 SsaDeadCodeEliminator dce = new SsaDeadCodeEliminator(closedWorld, this); |
| 114 runPhase(dce); | 129 runPhase(dce); |
| 115 if (codeMotion.movedCode || | 130 if (codeMotion.movedCode || |
| 116 dce.eliminatedSideEffects || | 131 dce.eliminatedSideEffects || |
| 117 loadElimination.newGvnCandidates) { | 132 loadElimination.newGvnCandidates) { |
| 118 phases = <OptimizationPhase>[ | 133 phases = <OptimizationPhase>[ |
| 119 new SsaTypePropagator(compiler, closedWorld), | 134 new SsaTypePropagator(_results, _options, _helpers, closedWorld), |
| 120 new SsaGlobalValueNumberer(), | 135 new SsaGlobalValueNumberer(), |
| 121 new SsaCodeMotion(), | 136 new SsaCodeMotion(), |
| 122 new SsaValueRangeAnalyzer(backend.helpers, closedWorld, this), | 137 new SsaValueRangeAnalyzer(_helpers, closedWorld, this), |
| 123 new SsaInstructionSimplifier(backend, closedWorld, this, registry), | 138 new SsaInstructionSimplifier(_results, _options, _helpers, |
| 139 _rtiSubstitutions, closedWorld, registry), |
| 124 new SsaCheckInserter( | 140 new SsaCheckInserter( |
| 125 trustPrimitives, backend, closedWorld, boundsChecked), | 141 trustPrimitives, _helpers, closedWorld, boundsChecked), |
| 126 new SsaSimplifyInterceptors( | 142 new SsaSimplifyInterceptors(closedWorld, _helpers, _interceptorData, |
| 127 compiler, closedWorld, work.element.enclosingClass), | 143 work.element.enclosingClass), |
| 128 new SsaDeadCodeEliminator(closedWorld, this), | 144 new SsaDeadCodeEliminator(closedWorld, this), |
| 129 ]; | 145 ]; |
| 130 } else { | 146 } else { |
| 131 phases = <OptimizationPhase>[ | 147 phases = <OptimizationPhase>[ |
| 132 new SsaTypePropagator(compiler, closedWorld), | 148 new SsaTypePropagator(_results, _options, _helpers, closedWorld), |
| 133 // Run the simplifier to remove unneeded type checks inserted by | 149 // Run the simplifier to remove unneeded type checks inserted by |
| 134 // type propagation. | 150 // type propagation. |
| 135 new SsaInstructionSimplifier(backend, closedWorld, this, registry), | 151 new SsaInstructionSimplifier(_results, _options, _helpers, |
| 152 _rtiSubstitutions, closedWorld, registry), |
| 136 ]; | 153 ]; |
| 137 } | 154 } |
| 138 phases.forEach(runPhase); | 155 phases.forEach(runPhase); |
| 139 }); | 156 }); |
| 140 } | 157 } |
| 141 } | 158 } |
| 142 | 159 |
| 143 /// Returns `true` if [mask] represents only types that have a length that | 160 /// Returns `true` if [mask] represents only types that have a length that |
| 144 /// cannot change. The current implementation is conservative for the purpose | 161 /// cannot change. The current implementation is conservative for the purpose |
| 145 /// of identifying gvn-able lengths and mis-identifies some unions of fixed | 162 /// of identifying gvn-able lengths and mis-identifies some unions of fixed |
| (...skipping 18 matching lines...) Expand all Loading... |
| 164 * compile-time. | 181 * compile-time. |
| 165 */ | 182 */ |
| 166 class SsaInstructionSimplifier extends HBaseVisitor | 183 class SsaInstructionSimplifier extends HBaseVisitor |
| 167 implements OptimizationPhase { | 184 implements OptimizationPhase { |
| 168 // We don't produce constant-folded strings longer than this unless they have | 185 // We don't produce constant-folded strings longer than this unless they have |
| 169 // a single use. This protects against exponentially large constant folded | 186 // a single use. This protects against exponentially large constant folded |
| 170 // strings. | 187 // strings. |
| 171 static const MAX_SHARED_CONSTANT_FOLDED_STRING_LENGTH = 512; | 188 static const MAX_SHARED_CONSTANT_FOLDED_STRING_LENGTH = 512; |
| 172 | 189 |
| 173 final String name = "SsaInstructionSimplifier"; | 190 final String name = "SsaInstructionSimplifier"; |
| 174 final JavaScriptBackend backend; | 191 final GlobalTypeInferenceResults _globalInferenceResults; |
| 175 final ClosedWorld closedWorld; | 192 final CompilerOptions _options; |
| 176 final CodegenRegistry registry; | 193 final BackendHelpers _helpers; |
| 177 HGraph graph; | 194 final RuntimeTypesSubstitutions _rtiSubstitutions; |
| 178 Compiler get compiler => backend.compiler; | 195 final ClosedWorld _closedWorld; |
| 179 final SsaOptimizerTask optimizer; | 196 final CodegenRegistry _registry; |
| 197 HGraph _graph; |
| 180 | 198 |
| 181 SsaInstructionSimplifier( | 199 SsaInstructionSimplifier(this._globalInferenceResults, this._options, |
| 182 this.backend, this.closedWorld, this.optimizer, this.registry); | 200 this._helpers, this._rtiSubstitutions, this._closedWorld, this._registry); |
| 183 | 201 |
| 184 CommonElements get commonElements => closedWorld.commonElements; | 202 CommonElements get commonElements => _closedWorld.commonElements; |
| 185 | 203 |
| 186 BackendHelpers get helpers => backend.helpers; | 204 ConstantSystem get constantSystem => _closedWorld.constantSystem; |
| 187 | 205 |
| 188 ConstantSystem get constantSystem => closedWorld.constantSystem; | 206 NativeData get _nativeData => _closedWorld.nativeData; |
| 189 | |
| 190 GlobalTypeInferenceResults get globalInferenceResults => | |
| 191 compiler.globalInference.results; | |
| 192 | 207 |
| 193 void visitGraph(HGraph visitee) { | 208 void visitGraph(HGraph visitee) { |
| 194 graph = visitee; | 209 _graph = visitee; |
| 195 visitDominatorTree(visitee); | 210 visitDominatorTree(visitee); |
| 196 } | 211 } |
| 197 | 212 |
| 198 visitBasicBlock(HBasicBlock block) { | 213 visitBasicBlock(HBasicBlock block) { |
| 199 HInstruction instruction = block.first; | 214 HInstruction instruction = block.first; |
| 200 while (instruction != null) { | 215 while (instruction != null) { |
| 201 HInstruction next = instruction.next; | 216 HInstruction next = instruction.next; |
| 202 HInstruction replacement = instruction.accept(this); | 217 HInstruction replacement = instruction.accept(this); |
| 203 if (replacement != instruction) { | 218 if (replacement != instruction) { |
| 204 block.rewrite(instruction, replacement); | 219 block.rewrite(instruction, replacement); |
| 205 | 220 |
| 206 // The intersection of double and int return conflicting, and | 221 // The intersection of double and int return conflicting, and |
| 207 // because of our number implementation for JavaScript, it | 222 // because of our number implementation for JavaScript, it |
| 208 // might be that an operation thought to return double, can be | 223 // might be that an operation thought to return double, can be |
| 209 // simplified to an int. For example: | 224 // simplified to an int. For example: |
| 210 // `2.5 * 10`. | 225 // `2.5 * 10`. |
| 211 if (!(replacement.isNumberOrNull(closedWorld) && | 226 if (!(replacement.isNumberOrNull(_closedWorld) && |
| 212 instruction.isNumberOrNull(closedWorld))) { | 227 instruction.isNumberOrNull(_closedWorld))) { |
| 213 // If we can replace [instruction] with [replacement], then | 228 // If we can replace [instruction] with [replacement], then |
| 214 // [replacement]'s type can be narrowed. | 229 // [replacement]'s type can be narrowed. |
| 215 TypeMask newType = replacement.instructionType | 230 TypeMask newType = replacement.instructionType |
| 216 .intersection(instruction.instructionType, closedWorld); | 231 .intersection(instruction.instructionType, _closedWorld); |
| 217 replacement.instructionType = newType; | 232 replacement.instructionType = newType; |
| 218 } | 233 } |
| 219 | 234 |
| 220 // If the replacement instruction does not know its | 235 // If the replacement instruction does not know its |
| 221 // source element, use the source element of the | 236 // source element, use the source element of the |
| 222 // instruction. | 237 // instruction. |
| 223 if (replacement.sourceElement == null) { | 238 if (replacement.sourceElement == null) { |
| 224 replacement.sourceElement = instruction.sourceElement; | 239 replacement.sourceElement = instruction.sourceElement; |
| 225 } | 240 } |
| 226 if (replacement.sourceInformation == null) { | 241 if (replacement.sourceInformation == null) { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 253 } | 268 } |
| 254 // TODO(het): consider supporting other values (short strings?) | 269 // TODO(het): consider supporting other values (short strings?) |
| 255 } | 270 } |
| 256 return null; | 271 return null; |
| 257 } | 272 } |
| 258 | 273 |
| 259 void propagateConstantValueToUses(HInstruction node) { | 274 void propagateConstantValueToUses(HInstruction node) { |
| 260 if (node.usedBy.isEmpty) return; | 275 if (node.usedBy.isEmpty) return; |
| 261 ConstantValue value = getConstantFromType(node); | 276 ConstantValue value = getConstantFromType(node); |
| 262 if (value != null) { | 277 if (value != null) { |
| 263 HConstant constant = graph.addConstant(value, closedWorld); | 278 HConstant constant = _graph.addConstant(value, _closedWorld); |
| 264 for (HInstruction user in node.usedBy.toList()) { | 279 for (HInstruction user in node.usedBy.toList()) { |
| 265 user.changeUse(node, constant); | 280 user.changeUse(node, constant); |
| 266 } | 281 } |
| 267 } | 282 } |
| 268 } | 283 } |
| 269 | 284 |
| 270 HInstruction visitParameterValue(HParameterValue node) { | 285 HInstruction visitParameterValue(HParameterValue node) { |
| 271 // [HParameterValue]s are either the value of the parameter (in fully SSA | 286 // [HParameterValue]s are either the value of the parameter (in fully SSA |
| 272 // converted code), or the mutable variable containing the value (in | 287 // converted code), or the mutable variable containing the value (in |
| 273 // incompletely SSA converted code, e.g. methods containing exceptions). | 288 // incompletely SSA converted code, e.g. methods containing exceptions). |
| (...skipping 15 matching lines...) Expand all Loading... |
| 289 } | 304 } |
| 290 | 305 |
| 291 propagateConstantValueToUses(node); | 306 propagateConstantValueToUses(node); |
| 292 return node; | 307 return node; |
| 293 } | 308 } |
| 294 | 309 |
| 295 HInstruction visitBoolify(HBoolify node) { | 310 HInstruction visitBoolify(HBoolify node) { |
| 296 List<HInstruction> inputs = node.inputs; | 311 List<HInstruction> inputs = node.inputs; |
| 297 assert(inputs.length == 1); | 312 assert(inputs.length == 1); |
| 298 HInstruction input = inputs[0]; | 313 HInstruction input = inputs[0]; |
| 299 if (input.isBoolean(closedWorld)) return input; | 314 if (input.isBoolean(_closedWorld)) return input; |
| 300 | 315 |
| 301 // If the code is unreachable, remove the HBoolify. This can happen when | 316 // If the code is unreachable, remove the HBoolify. This can happen when |
| 302 // there is a throw expression in a short-circuit conditional. Removing the | 317 // there is a throw expression in a short-circuit conditional. Removing the |
| 303 // unreachable HBoolify makes it easier to reconstruct the short-circuit | 318 // unreachable HBoolify makes it easier to reconstruct the short-circuit |
| 304 // operation. | 319 // operation. |
| 305 if (input.instructionType.isEmpty) return input; | 320 if (input.instructionType.isEmpty) return input; |
| 306 | 321 |
| 307 // All values that cannot be 'true' are boolified to false. | 322 // All values that cannot be 'true' are boolified to false. |
| 308 TypeMask mask = input.instructionType; | 323 TypeMask mask = input.instructionType; |
| 309 if (!mask.contains(helpers.jsBoolClass, closedWorld)) { | 324 if (!mask.contains(_helpers.jsBoolClass, _closedWorld)) { |
| 310 return graph.addConstantBool(false, closedWorld); | 325 return _graph.addConstantBool(false, _closedWorld); |
| 311 } | 326 } |
| 312 return node; | 327 return node; |
| 313 } | 328 } |
| 314 | 329 |
| 315 HInstruction visitNot(HNot node) { | 330 HInstruction visitNot(HNot node) { |
| 316 List<HInstruction> inputs = node.inputs; | 331 List<HInstruction> inputs = node.inputs; |
| 317 assert(inputs.length == 1); | 332 assert(inputs.length == 1); |
| 318 HInstruction input = inputs[0]; | 333 HInstruction input = inputs[0]; |
| 319 if (input is HConstant) { | 334 if (input is HConstant) { |
| 320 HConstant constant = input; | 335 HConstant constant = input; |
| 321 bool isTrue = constant.constant.isTrue; | 336 bool isTrue = constant.constant.isTrue; |
| 322 return graph.addConstantBool(!isTrue, closedWorld); | 337 return _graph.addConstantBool(!isTrue, _closedWorld); |
| 323 } else if (input is HNot) { | 338 } else if (input is HNot) { |
| 324 return input.inputs[0]; | 339 return input.inputs[0]; |
| 325 } | 340 } |
| 326 return node; | 341 return node; |
| 327 } | 342 } |
| 328 | 343 |
| 329 HInstruction visitInvokeUnary(HInvokeUnary node) { | 344 HInstruction visitInvokeUnary(HInvokeUnary node) { |
| 330 HInstruction folded = | 345 HInstruction folded = |
| 331 foldUnary(node.operation(constantSystem), node.operand); | 346 foldUnary(node.operation(constantSystem), node.operand); |
| 332 return folded != null ? folded : node; | 347 return folded != null ? folded : node; |
| 333 } | 348 } |
| 334 | 349 |
| 335 HInstruction foldUnary(UnaryOperation operation, HInstruction operand) { | 350 HInstruction foldUnary(UnaryOperation operation, HInstruction operand) { |
| 336 if (operand is HConstant) { | 351 if (operand is HConstant) { |
| 337 HConstant receiver = operand; | 352 HConstant receiver = operand; |
| 338 ConstantValue folded = operation.fold(receiver.constant); | 353 ConstantValue folded = operation.fold(receiver.constant); |
| 339 if (folded != null) return graph.addConstant(folded, closedWorld); | 354 if (folded != null) return _graph.addConstant(folded, _closedWorld); |
| 340 } | 355 } |
| 341 return null; | 356 return null; |
| 342 } | 357 } |
| 343 | 358 |
| 344 HInstruction tryOptimizeLengthInterceptedGetter(HInvokeDynamic node) { | 359 HInstruction tryOptimizeLengthInterceptedGetter(HInvokeDynamic node) { |
| 345 HInstruction actualReceiver = node.inputs[1]; | 360 HInstruction actualReceiver = node.inputs[1]; |
| 346 if (actualReceiver.isIndexablePrimitive(closedWorld)) { | 361 if (actualReceiver.isIndexablePrimitive(_closedWorld)) { |
| 347 if (actualReceiver.isConstantString()) { | 362 if (actualReceiver.isConstantString()) { |
| 348 HConstant constantInput = actualReceiver; | 363 HConstant constantInput = actualReceiver; |
| 349 StringConstantValue constant = constantInput.constant; | 364 StringConstantValue constant = constantInput.constant; |
| 350 return graph.addConstantInt(constant.length, closedWorld); | 365 return _graph.addConstantInt(constant.length, _closedWorld); |
| 351 } else if (actualReceiver.isConstantList()) { | 366 } else if (actualReceiver.isConstantList()) { |
| 352 HConstant constantInput = actualReceiver; | 367 HConstant constantInput = actualReceiver; |
| 353 ListConstantValue constant = constantInput.constant; | 368 ListConstantValue constant = constantInput.constant; |
| 354 return graph.addConstantInt(constant.length, closedWorld); | 369 return _graph.addConstantInt(constant.length, _closedWorld); |
| 355 } | 370 } |
| 356 bool isFixed = isFixedLength(actualReceiver.instructionType, closedWorld); | 371 bool isFixed = |
| 372 isFixedLength(actualReceiver.instructionType, _closedWorld); |
| 357 TypeMask actualType = node.instructionType; | 373 TypeMask actualType = node.instructionType; |
| 358 TypeMask resultType = closedWorld.commonMasks.positiveIntType; | 374 TypeMask resultType = _closedWorld.commonMasks.positiveIntType; |
| 359 // If we already have computed a more specific type, keep that type. | 375 // If we already have computed a more specific type, keep that type. |
| 360 if (HInstruction.isInstanceOf( | 376 if (HInstruction.isInstanceOf( |
| 361 actualType, helpers.jsUInt31Class, closedWorld)) { | 377 actualType, _helpers.jsUInt31Class, _closedWorld)) { |
| 362 resultType = closedWorld.commonMasks.uint31Type; | 378 resultType = _closedWorld.commonMasks.uint31Type; |
| 363 } else if (HInstruction.isInstanceOf( | 379 } else if (HInstruction.isInstanceOf( |
| 364 actualType, helpers.jsUInt32Class, closedWorld)) { | 380 actualType, _helpers.jsUInt32Class, _closedWorld)) { |
| 365 resultType = closedWorld.commonMasks.uint32Type; | 381 resultType = _closedWorld.commonMasks.uint32Type; |
| 366 } | 382 } |
| 367 HGetLength result = | 383 HGetLength result = |
| 368 new HGetLength(actualReceiver, resultType, isAssignable: !isFixed); | 384 new HGetLength(actualReceiver, resultType, isAssignable: !isFixed); |
| 369 return result; | 385 return result; |
| 370 } else if (actualReceiver.isConstantMap()) { | 386 } else if (actualReceiver.isConstantMap()) { |
| 371 HConstant constantInput = actualReceiver; | 387 HConstant constantInput = actualReceiver; |
| 372 MapConstantValue constant = constantInput.constant; | 388 MapConstantValue constant = constantInput.constant; |
| 373 return graph.addConstantInt(constant.length, closedWorld); | 389 return _graph.addConstantInt(constant.length, _closedWorld); |
| 374 } | 390 } |
| 375 return null; | 391 return null; |
| 376 } | 392 } |
| 377 | 393 |
| 378 HInstruction handleInterceptedCall(HInvokeDynamic node) { | 394 HInstruction handleInterceptedCall(HInvokeDynamic node) { |
| 379 // Try constant folding the instruction. | 395 // Try constant folding the instruction. |
| 380 Operation operation = node.specializer.operation(constantSystem); | 396 Operation operation = node.specializer.operation(constantSystem); |
| 381 if (operation != null) { | 397 if (operation != null) { |
| 382 HInstruction instruction = node.inputs.length == 2 | 398 HInstruction instruction = node.inputs.length == 2 |
| 383 ? foldUnary(operation, node.inputs[1]) | 399 ? foldUnary(operation, node.inputs[1]) |
| 384 : foldBinary(operation, node.inputs[1], node.inputs[2]); | 400 : foldBinary(operation, node.inputs[1], node.inputs[2]); |
| 385 if (instruction != null) return instruction; | 401 if (instruction != null) return instruction; |
| 386 } | 402 } |
| 387 | 403 |
| 388 // Try converting the instruction to a builtin instruction. | 404 // Try converting the instruction to a builtin instruction. |
| 389 HInstruction instruction = | 405 HInstruction instruction = node.specializer.tryConvertToBuiltin( |
| 390 node.specializer.tryConvertToBuiltin(node, compiler, closedWorld); | 406 node, _globalInferenceResults, _options, _helpers, _closedWorld); |
| 391 if (instruction != null) return instruction; | 407 if (instruction != null) return instruction; |
| 392 | 408 |
| 393 Selector selector = node.selector; | 409 Selector selector = node.selector; |
| 394 TypeMask mask = node.mask; | 410 TypeMask mask = node.mask; |
| 395 HInstruction input = node.inputs[1]; | 411 HInstruction input = node.inputs[1]; |
| 396 | 412 |
| 397 bool applies(MemberEntity element) { | 413 bool applies(MemberEntity element) { |
| 398 return selector.applies(element) && | 414 return selector.applies(element) && |
| 399 (mask == null || mask.canHit(element, selector, closedWorld)); | 415 (mask == null || mask.canHit(element, selector, _closedWorld)); |
| 400 } | 416 } |
| 401 | 417 |
| 402 if (selector.isCall || selector.isOperator) { | 418 if (selector.isCall || selector.isOperator) { |
| 403 FunctionEntity target; | 419 FunctionEntity target; |
| 404 if (input.isExtendableArray(closedWorld)) { | 420 if (input.isExtendableArray(_closedWorld)) { |
| 405 if (applies(helpers.jsArrayRemoveLast)) { | 421 if (applies(_helpers.jsArrayRemoveLast)) { |
| 406 target = helpers.jsArrayRemoveLast; | 422 target = _helpers.jsArrayRemoveLast; |
| 407 } else if (applies(helpers.jsArrayAdd)) { | 423 } else if (applies(_helpers.jsArrayAdd)) { |
| 408 // The codegen special cases array calls, but does not | 424 // The codegen special cases array calls, but does not |
| 409 // inline argument type checks. | 425 // inline argument type checks. |
| 410 if (!compiler.options.enableTypeAssertions) { | 426 if (!_options.enableTypeAssertions) { |
| 411 target = helpers.jsArrayAdd; | 427 target = _helpers.jsArrayAdd; |
| 412 } | 428 } |
| 413 } | 429 } |
| 414 } else if (input.isStringOrNull(closedWorld)) { | 430 } else if (input.isStringOrNull(_closedWorld)) { |
| 415 if (applies(helpers.jsStringSplit)) { | 431 if (applies(_helpers.jsStringSplit)) { |
| 416 HInstruction argument = node.inputs[2]; | 432 HInstruction argument = node.inputs[2]; |
| 417 if (argument.isString(closedWorld)) { | 433 if (argument.isString(_closedWorld)) { |
| 418 target = helpers.jsStringSplit; | 434 target = _helpers.jsStringSplit; |
| 419 } | 435 } |
| 420 } else if (applies(helpers.jsStringOperatorAdd)) { | 436 } else if (applies(_helpers.jsStringOperatorAdd)) { |
| 421 // `operator+` is turned into a JavaScript '+' so we need to | 437 // `operator+` is turned into a JavaScript '+' so we need to |
| 422 // make sure the receiver and the argument are not null. | 438 // make sure the receiver and the argument are not null. |
| 423 // TODO(sra): Do this via [node.specializer]. | 439 // TODO(sra): Do this via [node.specializer]. |
| 424 HInstruction argument = node.inputs[2]; | 440 HInstruction argument = node.inputs[2]; |
| 425 if (argument.isString(closedWorld) && !input.canBeNull()) { | 441 if (argument.isString(_closedWorld) && !input.canBeNull()) { |
| 426 return new HStringConcat(input, argument, node.instructionType); | 442 return new HStringConcat(input, argument, node.instructionType); |
| 427 } | 443 } |
| 428 } else if (applies(helpers.jsStringToString) && !input.canBeNull()) { | 444 } else if (applies(_helpers.jsStringToString) && !input.canBeNull()) { |
| 429 return input; | 445 return input; |
| 430 } | 446 } |
| 431 } | 447 } |
| 432 if (target != null) { | 448 if (target != null) { |
| 433 // TODO(ngeoffray): There is a strong dependency between codegen | 449 // TODO(ngeoffray): There is a strong dependency between codegen |
| 434 // and this optimization that the dynamic invoke does not need an | 450 // and this optimization that the dynamic invoke does not need an |
| 435 // interceptor. We currently need to keep a | 451 // interceptor. We currently need to keep a |
| 436 // HInvokeDynamicMethod and not create a HForeign because | 452 // HInvokeDynamicMethod and not create a HForeign because |
| 437 // HForeign is too opaque for the SsaCheckInserter (that adds a | 453 // HForeign is too opaque for the SsaCheckInserter (that adds a |
| 438 // bounds check on removeLast). Once we start inlining, the | 454 // bounds check on removeLast). Once we start inlining, the |
| 439 // bounds check will become explicit, so we won't need this | 455 // bounds check will become explicit, so we won't need this |
| 440 // optimization. | 456 // optimization. |
| 441 HInvokeDynamicMethod result = new HInvokeDynamicMethod(node.selector, | 457 HInvokeDynamicMethod result = new HInvokeDynamicMethod(node.selector, |
| 442 node.mask, node.inputs.sublist(1), node.instructionType); | 458 node.mask, node.inputs.sublist(1), node.instructionType); |
| 443 result.element = target; | 459 result.element = target; |
| 444 return result; | 460 return result; |
| 445 } | 461 } |
| 446 } else if (selector.isGetter) { | 462 } else if (selector.isGetter) { |
| 447 if (selector.applies(helpers.jsIndexableLength)) { | 463 if (selector.applies(_helpers.jsIndexableLength)) { |
| 448 HInstruction optimized = tryOptimizeLengthInterceptedGetter(node); | 464 HInstruction optimized = tryOptimizeLengthInterceptedGetter(node); |
| 449 if (optimized != null) return optimized; | 465 if (optimized != null) return optimized; |
| 450 } | 466 } |
| 451 } | 467 } |
| 452 | 468 |
| 453 return node; | 469 return node; |
| 454 } | 470 } |
| 455 | 471 |
| 456 HInstruction visitInvokeDynamicMethod(HInvokeDynamicMethod node) { | 472 HInstruction visitInvokeDynamicMethod(HInvokeDynamicMethod node) { |
| 457 propagateConstantValueToUses(node); | 473 propagateConstantValueToUses(node); |
| 458 if (node.isInterceptedCall) { | 474 if (node.isInterceptedCall) { |
| 459 HInstruction folded = handleInterceptedCall(node); | 475 HInstruction folded = handleInterceptedCall(node); |
| 460 if (folded != node) return folded; | 476 if (folded != node) return folded; |
| 461 } | 477 } |
| 462 | 478 |
| 463 TypeMask receiverType = node.getDartReceiver(closedWorld).instructionType; | 479 TypeMask receiverType = node.getDartReceiver(_closedWorld).instructionType; |
| 464 MemberEntity element = | 480 MemberEntity element = |
| 465 closedWorld.locateSingleElement(node.selector, receiverType); | 481 _closedWorld.locateSingleElement(node.selector, receiverType); |
| 466 // TODO(ngeoffray): Also fold if it's a getter or variable. | 482 // TODO(ngeoffray): Also fold if it's a getter or variable. |
| 467 if (element != null && | 483 if (element != null && |
| 468 element.isFunction | 484 element.isFunction |
| 469 // If we found out that the only target is an implicitly called | 485 // If we found out that the only target is an implicitly called |
| 470 // [:noSuchMethod:] we just ignore it. | 486 // [:noSuchMethod:] we just ignore it. |
| 471 && | 487 && |
| 472 node.selector.applies(element)) { | 488 node.selector.applies(element)) { |
| 473 MethodElement method = element; | 489 MethodElement method = element; |
| 474 | 490 |
| 475 if (backend.nativeData.isNativeMember(method)) { | 491 if (_nativeData.isNativeMember(method)) { |
| 476 HInstruction folded = tryInlineNativeMethod(node, method); | 492 HInstruction folded = tryInlineNativeMethod(node, method); |
| 477 if (folded != null) return folded; | 493 if (folded != null) return folded; |
| 478 } else { | 494 } else { |
| 479 // TODO(ngeoffray): If the method has optional parameters, | 495 // TODO(ngeoffray): If the method has optional parameters, |
| 480 // we should pass the default values. | 496 // we should pass the default values. |
| 481 ResolutionFunctionType type = method.type; | 497 ResolutionFunctionType type = method.type; |
| 482 int optionalParameterCount = | 498 int optionalParameterCount = |
| 483 type.optionalParameterTypes.length + type.namedParameters.length; | 499 type.optionalParameterTypes.length + type.namedParameters.length; |
| 484 if (optionalParameterCount == 0 || | 500 if (optionalParameterCount == 0 || |
| 485 type.parameterTypes.length + optionalParameterCount == | 501 type.parameterTypes.length + optionalParameterCount == |
| 486 node.selector.argumentCount) { | 502 node.selector.argumentCount) { |
| 487 node.element = method; | 503 node.element = method; |
| 488 } | 504 } |
| 489 } | 505 } |
| 490 return node; | 506 return node; |
| 491 } | 507 } |
| 492 | 508 |
| 493 // Replace method calls through fields with a closure call on the value of | 509 // Replace method calls through fields with a closure call on the value of |
| 494 // the field. This usually removes the demand for the call-through stub and | 510 // the field. This usually removes the demand for the call-through stub and |
| 495 // makes the field load available to further optimization, e.g. LICM. | 511 // makes the field load available to further optimization, e.g. LICM. |
| 496 | 512 |
| 497 if (element != null && | 513 if (element != null && |
| 498 element.isField && | 514 element.isField && |
| 499 element.name == node.selector.name) { | 515 element.name == node.selector.name) { |
| 500 FieldEntity field = element; | 516 FieldEntity field = element; |
| 501 if (!backend.nativeData.isNativeMember(field) && | 517 if (!_nativeData.isNativeMember(field) && |
| 502 !node.isCallOnInterceptor(closedWorld)) { | 518 !node.isCallOnInterceptor(_closedWorld)) { |
| 503 HInstruction receiver = node.getDartReceiver(closedWorld); | 519 HInstruction receiver = node.getDartReceiver(_closedWorld); |
| 504 TypeMask type = TypeMaskFactory.inferredTypeForElement( | 520 TypeMask type = TypeMaskFactory.inferredTypeForElement( |
| 505 field as Entity, globalInferenceResults); | 521 field as Entity, _globalInferenceResults); |
| 506 HInstruction load = new HFieldGet(field, receiver, type); | 522 HInstruction load = new HFieldGet(field, receiver, type); |
| 507 node.block.addBefore(node, load); | 523 node.block.addBefore(node, load); |
| 508 Selector callSelector = new Selector.callClosureFrom(node.selector); | 524 Selector callSelector = new Selector.callClosureFrom(node.selector); |
| 509 List<HInstruction> inputs = <HInstruction>[load] | 525 List<HInstruction> inputs = <HInstruction>[load] |
| 510 ..addAll(node.inputs.skip(node.isInterceptedCall ? 2 : 1)); | 526 ..addAll(node.inputs.skip(node.isInterceptedCall ? 2 : 1)); |
| 511 HInstruction closureCall = | 527 HInstruction closureCall = |
| 512 new HInvokeClosure(callSelector, inputs, node.instructionType) | 528 new HInvokeClosure(callSelector, inputs, node.instructionType) |
| 513 ..sourceInformation = node.sourceInformation; | 529 ..sourceInformation = node.sourceInformation; |
| 514 node.block.addAfter(load, closureCall); | 530 node.block.addAfter(load, closureCall); |
| 515 return closureCall; | 531 return closureCall; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 541 if (type.namedParameters.isNotEmpty) return null; | 557 if (type.namedParameters.isNotEmpty) return null; |
| 542 | 558 |
| 543 // Return types on native methods don't need to be checked, since the | 559 // Return types on native methods don't need to be checked, since the |
| 544 // declaration has to be truthful. | 560 // declaration has to be truthful. |
| 545 | 561 |
| 546 // The call site might omit optional arguments. The inlined code must | 562 // The call site might omit optional arguments. The inlined code must |
| 547 // preserve the number of arguments, so check only the actual arguments. | 563 // preserve the number of arguments, so check only the actual arguments. |
| 548 | 564 |
| 549 List<HInstruction> inputs = node.inputs.sublist(1); | 565 List<HInstruction> inputs = node.inputs.sublist(1); |
| 550 bool canInline = true; | 566 bool canInline = true; |
| 551 if (compiler.options.enableTypeAssertions && inputs.length > 1) { | 567 if (_options.enableTypeAssertions && inputs.length > 1) { |
| 552 // TODO(sra): Check if [input] is guaranteed to pass the parameter | 568 // TODO(sra): Check if [input] is guaranteed to pass the parameter |
| 553 // type check. Consider using a strengthened type check to avoid | 569 // type check. Consider using a strengthened type check to avoid |
| 554 // passing `null` to primitive types since the native methods usually | 570 // passing `null` to primitive types since the native methods usually |
| 555 // have non-nullable primitive parameter types. | 571 // have non-nullable primitive parameter types. |
| 556 canInline = false; | 572 canInline = false; |
| 557 } else { | 573 } else { |
| 558 int inputPosition = 1; // Skip receiver. | 574 int inputPosition = 1; // Skip receiver. |
| 559 void checkParameterType(ResolutionDartType type) { | 575 void checkParameterType(ResolutionDartType type) { |
| 560 if (inputPosition++ < inputs.length && canInline) { | 576 if (inputPosition++ < inputs.length && canInline) { |
| 561 if (type.unaliased.isFunctionType) { | 577 if (type.unaliased.isFunctionType) { |
| 562 canInline = false; | 578 canInline = false; |
| 563 } | 579 } |
| 564 } | 580 } |
| 565 } | 581 } |
| 566 | 582 |
| 567 type.parameterTypes.forEach(checkParameterType); | 583 type.parameterTypes.forEach(checkParameterType); |
| 568 type.optionalParameterTypes.forEach(checkParameterType); | 584 type.optionalParameterTypes.forEach(checkParameterType); |
| 569 type.namedParameterTypes.forEach(checkParameterType); | 585 type.namedParameterTypes.forEach(checkParameterType); |
| 570 } | 586 } |
| 571 | 587 |
| 572 if (!canInline) return null; | 588 if (!canInline) return null; |
| 573 | 589 |
| 574 // Strengthen instruction type from annotations to help optimize | 590 // Strengthen instruction type from annotations to help optimize |
| 575 // dependent instructions. | 591 // dependent instructions. |
| 576 native.NativeBehavior nativeBehavior = | 592 native.NativeBehavior nativeBehavior = |
| 577 backend.nativeData.getNativeMethodBehavior(method); | 593 _nativeData.getNativeMethodBehavior(method); |
| 578 TypeMask returnType = | 594 TypeMask returnType = |
| 579 TypeMaskFactory.fromNativeBehavior(nativeBehavior, closedWorld); | 595 TypeMaskFactory.fromNativeBehavior(nativeBehavior, _closedWorld); |
| 580 HInvokeDynamicMethod result = | 596 HInvokeDynamicMethod result = |
| 581 new HInvokeDynamicMethod(node.selector, node.mask, inputs, returnType); | 597 new HInvokeDynamicMethod(node.selector, node.mask, inputs, returnType); |
| 582 result.element = method; | 598 result.element = method; |
| 583 return result; | 599 return result; |
| 584 } | 600 } |
| 585 | 601 |
| 586 HInstruction visitBoundsCheck(HBoundsCheck node) { | 602 HInstruction visitBoundsCheck(HBoundsCheck node) { |
| 587 HInstruction index = node.index; | 603 HInstruction index = node.index; |
| 588 if (index.isInteger(closedWorld)) return node; | 604 if (index.isInteger(_closedWorld)) return node; |
| 589 if (index.isConstant()) { | 605 if (index.isConstant()) { |
| 590 HConstant constantInstruction = index; | 606 HConstant constantInstruction = index; |
| 591 assert(!constantInstruction.constant.isInt); | 607 assert(!constantInstruction.constant.isInt); |
| 592 if (!constantSystem.isInt(constantInstruction.constant)) { | 608 if (!constantSystem.isInt(constantInstruction.constant)) { |
| 593 // -0.0 is a double but will pass the runtime integer check. | 609 // -0.0 is a double but will pass the runtime integer check. |
| 594 node.staticChecks = HBoundsCheck.ALWAYS_FALSE; | 610 node.staticChecks = HBoundsCheck.ALWAYS_FALSE; |
| 595 } | 611 } |
| 596 } | 612 } |
| 597 return node; | 613 return node; |
| 598 } | 614 } |
| 599 | 615 |
| 600 HInstruction foldBinary( | 616 HInstruction foldBinary( |
| 601 BinaryOperation operation, HInstruction left, HInstruction right) { | 617 BinaryOperation operation, HInstruction left, HInstruction right) { |
| 602 if (left is HConstant && right is HConstant) { | 618 if (left is HConstant && right is HConstant) { |
| 603 HConstant op1 = left; | 619 HConstant op1 = left; |
| 604 HConstant op2 = right; | 620 HConstant op2 = right; |
| 605 ConstantValue folded = operation.fold(op1.constant, op2.constant); | 621 ConstantValue folded = operation.fold(op1.constant, op2.constant); |
| 606 if (folded != null) return graph.addConstant(folded, closedWorld); | 622 if (folded != null) return _graph.addConstant(folded, _closedWorld); |
| 607 } | 623 } |
| 608 return null; | 624 return null; |
| 609 } | 625 } |
| 610 | 626 |
| 611 HInstruction visitAdd(HAdd node) { | 627 HInstruction visitAdd(HAdd node) { |
| 612 HInstruction left = node.left; | 628 HInstruction left = node.left; |
| 613 HInstruction right = node.right; | 629 HInstruction right = node.right; |
| 614 // We can only perform this rewriting on Integer, as it is not | 630 // We can only perform this rewriting on Integer, as it is not |
| 615 // valid for -0.0. | 631 // valid for -0.0. |
| 616 if (left.isInteger(closedWorld) && right.isInteger(closedWorld)) { | 632 if (left.isInteger(_closedWorld) && right.isInteger(_closedWorld)) { |
| 617 if (left is HConstant && left.constant.isZero) return right; | 633 if (left is HConstant && left.constant.isZero) return right; |
| 618 if (right is HConstant && right.constant.isZero) return left; | 634 if (right is HConstant && right.constant.isZero) return left; |
| 619 } | 635 } |
| 620 return super.visitAdd(node); | 636 return super.visitAdd(node); |
| 621 } | 637 } |
| 622 | 638 |
| 623 HInstruction visitMultiply(HMultiply node) { | 639 HInstruction visitMultiply(HMultiply node) { |
| 624 HInstruction left = node.left; | 640 HInstruction left = node.left; |
| 625 HInstruction right = node.right; | 641 HInstruction right = node.right; |
| 626 if (left.isNumber(closedWorld) && right.isNumber(closedWorld)) { | 642 if (left.isNumber(_closedWorld) && right.isNumber(_closedWorld)) { |
| 627 if (left is HConstant && left.constant.isOne) return right; | 643 if (left is HConstant && left.constant.isOne) return right; |
| 628 if (right is HConstant && right.constant.isOne) return left; | 644 if (right is HConstant && right.constant.isOne) return left; |
| 629 } | 645 } |
| 630 return super.visitMultiply(node); | 646 return super.visitMultiply(node); |
| 631 } | 647 } |
| 632 | 648 |
| 633 HInstruction visitInvokeBinary(HInvokeBinary node) { | 649 HInstruction visitInvokeBinary(HInvokeBinary node) { |
| 634 HInstruction left = node.left; | 650 HInstruction left = node.left; |
| 635 HInstruction right = node.right; | 651 HInstruction right = node.right; |
| 636 BinaryOperation operation = node.operation(constantSystem); | 652 BinaryOperation operation = node.operation(constantSystem); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 657 // in the remaining optimizations. | 673 // in the remaining optimizations. |
| 658 return super.visitRelational(node); | 674 return super.visitRelational(node); |
| 659 } | 675 } |
| 660 | 676 |
| 661 HInstruction handleIdentityCheck(HRelational node) { | 677 HInstruction handleIdentityCheck(HRelational node) { |
| 662 HInstruction left = node.left; | 678 HInstruction left = node.left; |
| 663 HInstruction right = node.right; | 679 HInstruction right = node.right; |
| 664 TypeMask leftType = left.instructionType; | 680 TypeMask leftType = left.instructionType; |
| 665 TypeMask rightType = right.instructionType; | 681 TypeMask rightType = right.instructionType; |
| 666 | 682 |
| 667 HInstruction makeTrue() => graph.addConstantBool(true, closedWorld); | 683 HInstruction makeTrue() => _graph.addConstantBool(true, _closedWorld); |
| 668 HInstruction makeFalse() => graph.addConstantBool(false, closedWorld); | 684 HInstruction makeFalse() => _graph.addConstantBool(false, _closedWorld); |
| 669 | 685 |
| 670 // Intersection of int and double return conflicting, so | 686 // Intersection of int and double return conflicting, so |
| 671 // we don't optimize on numbers to preserve the runtime semantics. | 687 // we don't optimize on numbers to preserve the runtime semantics. |
| 672 if (!(left.isNumberOrNull(closedWorld) && | 688 if (!(left.isNumberOrNull(_closedWorld) && |
| 673 right.isNumberOrNull(closedWorld))) { | 689 right.isNumberOrNull(_closedWorld))) { |
| 674 if (leftType.isDisjoint(rightType, closedWorld)) { | 690 if (leftType.isDisjoint(rightType, _closedWorld)) { |
| 675 return makeFalse(); | 691 return makeFalse(); |
| 676 } | 692 } |
| 677 } | 693 } |
| 678 | 694 |
| 679 if (left.isNull() && right.isNull()) { | 695 if (left.isNull() && right.isNull()) { |
| 680 return makeTrue(); | 696 return makeTrue(); |
| 681 } | 697 } |
| 682 | 698 |
| 683 HInstruction compareConstant(HConstant constant, HInstruction input) { | 699 HInstruction compareConstant(HConstant constant, HInstruction input) { |
| 684 if (constant.constant.isTrue) { | 700 if (constant.constant.isTrue) { |
| 685 return input; | 701 return input; |
| 686 } else { | 702 } else { |
| 687 return new HNot(input, closedWorld.commonMasks.boolType); | 703 return new HNot(input, _closedWorld.commonMasks.boolType); |
| 688 } | 704 } |
| 689 } | 705 } |
| 690 | 706 |
| 691 if (left.isConstantBoolean() && right.isBoolean(closedWorld)) { | 707 if (left.isConstantBoolean() && right.isBoolean(_closedWorld)) { |
| 692 return compareConstant(left, right); | 708 return compareConstant(left, right); |
| 693 } | 709 } |
| 694 | 710 |
| 695 if (right.isConstantBoolean() && left.isBoolean(closedWorld)) { | 711 if (right.isConstantBoolean() && left.isBoolean(_closedWorld)) { |
| 696 return compareConstant(right, left); | 712 return compareConstant(right, left); |
| 697 } | 713 } |
| 698 | 714 |
| 699 if (identical(left.nonCheck(), right.nonCheck())) { | 715 if (identical(left.nonCheck(), right.nonCheck())) { |
| 700 // Avoid constant-folding `identical(x, x)` when `x` might be double. The | 716 // Avoid constant-folding `identical(x, x)` when `x` might be double. The |
| 701 // dart2js runtime has not always been consistent with the Dart | 717 // dart2js runtime has not always been consistent with the Dart |
| 702 // specification (section 16.0.1), which makes distinctions on NaNs and | 718 // specification (section 16.0.1), which makes distinctions on NaNs and |
| 703 // -0.0 that are hard to implement efficiently. | 719 // -0.0 that are hard to implement efficiently. |
| 704 if (left.isIntegerOrNull(closedWorld)) return makeTrue(); | 720 if (left.isIntegerOrNull(_closedWorld)) return makeTrue(); |
| 705 if (!left.canBePrimitiveNumber(closedWorld)) return makeTrue(); | 721 if (!left.canBePrimitiveNumber(_closedWorld)) return makeTrue(); |
| 706 } | 722 } |
| 707 | 723 |
| 708 return null; | 724 return null; |
| 709 } | 725 } |
| 710 | 726 |
| 711 HInstruction visitIdentity(HIdentity node) { | 727 HInstruction visitIdentity(HIdentity node) { |
| 712 HInstruction newInstruction = handleIdentityCheck(node); | 728 HInstruction newInstruction = handleIdentityCheck(node); |
| 713 return newInstruction == null ? super.visitIdentity(node) : newInstruction; | 729 return newInstruction == null ? super.visitIdentity(node) : newInstruction; |
| 714 } | 730 } |
| 715 | 731 |
| 716 void simplifyCondition( | 732 void simplifyCondition( |
| 717 HBasicBlock block, HInstruction condition, bool value) { | 733 HBasicBlock block, HInstruction condition, bool value) { |
| 718 condition.dominatedUsers(block.first).forEach((user) { | 734 condition.dominatedUsers(block.first).forEach((user) { |
| 719 HInstruction newCondition = graph.addConstantBool(value, closedWorld); | 735 HInstruction newCondition = _graph.addConstantBool(value, _closedWorld); |
| 720 user.changeUse(condition, newCondition); | 736 user.changeUse(condition, newCondition); |
| 721 }); | 737 }); |
| 722 } | 738 } |
| 723 | 739 |
| 724 HInstruction visitIf(HIf node) { | 740 HInstruction visitIf(HIf node) { |
| 725 HInstruction condition = node.condition; | 741 HInstruction condition = node.condition; |
| 726 if (condition.isConstant()) return node; | 742 if (condition.isConstant()) return node; |
| 727 bool isNegated = condition is HNot; | 743 bool isNegated = condition is HNot; |
| 728 | 744 |
| 729 if (isNegated) { | 745 if (isNegated) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 751 | 767 |
| 752 if (!node.isRawCheck) { | 768 if (!node.isRawCheck) { |
| 753 return node; | 769 return node; |
| 754 } else if (type.isTypedef) { | 770 } else if (type.isTypedef) { |
| 755 return node; | 771 return node; |
| 756 } else if (type.isFunctionType) { | 772 } else if (type.isFunctionType) { |
| 757 return node; | 773 return node; |
| 758 } | 774 } |
| 759 | 775 |
| 760 if (type.isObject || type.treatAsDynamic) { | 776 if (type.isObject || type.treatAsDynamic) { |
| 761 return graph.addConstantBool(true, closedWorld); | 777 return _graph.addConstantBool(true, _closedWorld); |
| 762 } | 778 } |
| 763 ResolutionInterfaceType interfaceType = type; | 779 ResolutionInterfaceType interfaceType = type; |
| 764 ClassEntity element = interfaceType.element; | 780 ClassEntity element = interfaceType.element; |
| 765 HInstruction expression = node.expression; | 781 HInstruction expression = node.expression; |
| 766 if (expression.isInteger(closedWorld)) { | 782 if (expression.isInteger(_closedWorld)) { |
| 767 if (element == commonElements.intClass || | 783 if (element == commonElements.intClass || |
| 768 element == commonElements.numClass || | 784 element == commonElements.numClass || |
| 769 commonElements.isNumberOrStringSupertype(element)) { | 785 commonElements.isNumberOrStringSupertype(element)) { |
| 770 return graph.addConstantBool(true, closedWorld); | 786 return _graph.addConstantBool(true, _closedWorld); |
| 771 } else if (element == commonElements.doubleClass) { | 787 } else if (element == commonElements.doubleClass) { |
| 772 // We let the JS semantics decide for that check. Currently | 788 // We let the JS semantics decide for that check. Currently |
| 773 // the code we emit will always return true. | 789 // the code we emit will always return true. |
| 774 return node; | 790 return node; |
| 775 } else { | 791 } else { |
| 776 return graph.addConstantBool(false, closedWorld); | 792 return _graph.addConstantBool(false, _closedWorld); |
| 777 } | 793 } |
| 778 } else if (expression.isDouble(closedWorld)) { | 794 } else if (expression.isDouble(_closedWorld)) { |
| 779 if (element == commonElements.doubleClass || | 795 if (element == commonElements.doubleClass || |
| 780 element == commonElements.numClass || | 796 element == commonElements.numClass || |
| 781 commonElements.isNumberOrStringSupertype(element)) { | 797 commonElements.isNumberOrStringSupertype(element)) { |
| 782 return graph.addConstantBool(true, closedWorld); | 798 return _graph.addConstantBool(true, _closedWorld); |
| 783 } else if (element == commonElements.intClass) { | 799 } else if (element == commonElements.intClass) { |
| 784 // We let the JS semantics decide for that check. Currently | 800 // We let the JS semantics decide for that check. Currently |
| 785 // the code we emit will return true for a double that can be | 801 // the code we emit will return true for a double that can be |
| 786 // represented as a 31-bit integer and for -0.0. | 802 // represented as a 31-bit integer and for -0.0. |
| 787 return node; | 803 return node; |
| 788 } else { | 804 } else { |
| 789 return graph.addConstantBool(false, closedWorld); | 805 return _graph.addConstantBool(false, _closedWorld); |
| 790 } | 806 } |
| 791 } else if (expression.isNumber(closedWorld)) { | 807 } else if (expression.isNumber(_closedWorld)) { |
| 792 if (element == commonElements.numClass) { | 808 if (element == commonElements.numClass) { |
| 793 return graph.addConstantBool(true, closedWorld); | 809 return _graph.addConstantBool(true, _closedWorld); |
| 794 } else { | 810 } else { |
| 795 // We cannot just return false, because the expression may be of | 811 // We cannot just return false, because the expression may be of |
| 796 // type int or double. | 812 // type int or double. |
| 797 } | 813 } |
| 798 } else if (expression.canBePrimitiveNumber(closedWorld) && | 814 } else if (expression.canBePrimitiveNumber(_closedWorld) && |
| 799 element == commonElements.intClass) { | 815 element == commonElements.intClass) { |
| 800 // We let the JS semantics decide for that check. | 816 // We let the JS semantics decide for that check. |
| 801 return node; | 817 return node; |
| 802 // We need the [:hasTypeArguments:] check because we don't have | 818 // We need the [:hasTypeArguments:] check because we don't have |
| 803 // the notion of generics in the backend. For example, [:this:] in | 819 // the notion of generics in the backend. For example, [:this:] in |
| 804 // a class [:A<T>:], is currently always considered to have the | 820 // a class [:A<T>:], is currently always considered to have the |
| 805 // raw type. | 821 // raw type. |
| 806 } else if (!RuntimeTypesSubstitutions.hasTypeArguments(type)) { | 822 } else if (!RuntimeTypesSubstitutions.hasTypeArguments(type)) { |
| 807 TypeMask expressionMask = expression.instructionType; | 823 TypeMask expressionMask = expression.instructionType; |
| 808 assert(TypeMask.assertIsNormalized(expressionMask, closedWorld)); | 824 assert(TypeMask.assertIsNormalized(expressionMask, _closedWorld)); |
| 809 TypeMask typeMask = (element == commonElements.nullClass) | 825 TypeMask typeMask = (element == commonElements.nullClass) |
| 810 ? new TypeMask.subtype(element, closedWorld) | 826 ? new TypeMask.subtype(element, _closedWorld) |
| 811 : new TypeMask.nonNullSubtype(element, closedWorld); | 827 : new TypeMask.nonNullSubtype(element, _closedWorld); |
| 812 if (expressionMask.union(typeMask, closedWorld) == typeMask) { | 828 if (expressionMask.union(typeMask, _closedWorld) == typeMask) { |
| 813 return graph.addConstantBool(true, closedWorld); | 829 return _graph.addConstantBool(true, _closedWorld); |
| 814 } else if (expressionMask.isDisjoint(typeMask, closedWorld)) { | 830 } else if (expressionMask.isDisjoint(typeMask, _closedWorld)) { |
| 815 return graph.addConstantBool(false, closedWorld); | 831 return _graph.addConstantBool(false, _closedWorld); |
| 816 } | 832 } |
| 817 } | 833 } |
| 818 return node; | 834 return node; |
| 819 } | 835 } |
| 820 | 836 |
| 821 HInstruction visitTypeConversion(HTypeConversion node) { | 837 HInstruction visitTypeConversion(HTypeConversion node) { |
| 822 ResolutionDartType type = node.typeExpression; | 838 ResolutionDartType type = node.typeExpression; |
| 823 if (type != null) { | 839 if (type != null) { |
| 824 if (type.isMalformed) { | 840 if (type.isMalformed) { |
| 825 // Malformed types are treated as dynamic statically, but should | 841 // Malformed types are treated as dynamic statically, but should |
| (...skipping 23 matching lines...) Expand all Loading... |
| 849 } | 865 } |
| 850 } | 866 } |
| 851 return removeIfCheckAlwaysSucceeds(node, node.checkedType); | 867 return removeIfCheckAlwaysSucceeds(node, node.checkedType); |
| 852 } | 868 } |
| 853 | 869 |
| 854 HInstruction visitTypeKnown(HTypeKnown node) { | 870 HInstruction visitTypeKnown(HTypeKnown node) { |
| 855 return removeIfCheckAlwaysSucceeds(node, node.knownType); | 871 return removeIfCheckAlwaysSucceeds(node, node.knownType); |
| 856 } | 872 } |
| 857 | 873 |
| 858 HInstruction removeIfCheckAlwaysSucceeds(HCheck node, TypeMask checkedType) { | 874 HInstruction removeIfCheckAlwaysSucceeds(HCheck node, TypeMask checkedType) { |
| 859 if (checkedType.containsAll(closedWorld)) return node; | 875 if (checkedType.containsAll(_closedWorld)) return node; |
| 860 HInstruction input = node.checkedInput; | 876 HInstruction input = node.checkedInput; |
| 861 TypeMask inputType = input.instructionType; | 877 TypeMask inputType = input.instructionType; |
| 862 return inputType.isInMask(checkedType, closedWorld) ? input : node; | 878 return inputType.isInMask(checkedType, _closedWorld) ? input : node; |
| 863 } | 879 } |
| 864 | 880 |
| 865 HInstruction removeCheck(HCheck node) => node.checkedInput; | 881 HInstruction removeCheck(HCheck node) => node.checkedInput; |
| 866 | 882 |
| 867 FieldEntity findConcreteFieldForDynamicAccess( | 883 FieldEntity findConcreteFieldForDynamicAccess( |
| 868 HInstruction receiver, Selector selector) { | 884 HInstruction receiver, Selector selector) { |
| 869 TypeMask receiverType = receiver.instructionType; | 885 TypeMask receiverType = receiver.instructionType; |
| 870 return closedWorld.locateSingleField(selector, receiverType); | 886 return _closedWorld.locateSingleField(selector, receiverType); |
| 871 } | 887 } |
| 872 | 888 |
| 873 HInstruction visitFieldGet(HFieldGet node) { | 889 HInstruction visitFieldGet(HFieldGet node) { |
| 874 if (node.isNullCheck) return node; | 890 if (node.isNullCheck) return node; |
| 875 var receiver = node.receiver; | 891 var receiver = node.receiver; |
| 876 | 892 |
| 877 // HFieldGet of a constructed constant can be replaced with the constant's | 893 // HFieldGet of a constructed constant can be replaced with the constant's |
| 878 // field. | 894 // field. |
| 879 if (receiver is HConstant) { | 895 if (receiver is HConstant) { |
| 880 ConstantValue constant = receiver.constant; | 896 ConstantValue constant = receiver.constant; |
| 881 if (constant.isConstructedObject) { | 897 if (constant.isConstructedObject) { |
| 882 ConstructedConstantValue constructedConstant = constant; | 898 ConstructedConstantValue constructedConstant = constant; |
| 883 Map<FieldEntity, ConstantValue> fields = constructedConstant.fields; | 899 Map<FieldEntity, ConstantValue> fields = constructedConstant.fields; |
| 884 ConstantValue value = fields[node.element]; | 900 ConstantValue value = fields[node.element]; |
| 885 if (value != null) { | 901 if (value != null) { |
| 886 return graph.addConstant(value, closedWorld); | 902 return _graph.addConstant(value, _closedWorld); |
| 887 } | 903 } |
| 888 } | 904 } |
| 889 } | 905 } |
| 890 | 906 |
| 891 return node; | 907 return node; |
| 892 } | 908 } |
| 893 | 909 |
| 894 HInstruction visitGetLength(HGetLength node) { | 910 HInstruction visitGetLength(HGetLength node) { |
| 895 var receiver = node.receiver; | 911 var receiver = node.receiver; |
| 896 if (graph.allocatedFixedLists.contains(receiver)) { | 912 if (_graph.allocatedFixedLists.contains(receiver)) { |
| 897 // TODO(ngeoffray): checking if the second input is an integer | 913 // TODO(ngeoffray): checking if the second input is an integer |
| 898 // should not be necessary but it currently makes it easier for | 914 // should not be necessary but it currently makes it easier for |
| 899 // other optimizations to reason about a fixed length constructor | 915 // other optimizations to reason about a fixed length constructor |
| 900 // that we know takes an int. | 916 // that we know takes an int. |
| 901 if (receiver.inputs[0].isInteger(closedWorld)) { | 917 if (receiver.inputs[0].isInteger(_closedWorld)) { |
| 902 return receiver.inputs[0]; | 918 return receiver.inputs[0]; |
| 903 } | 919 } |
| 904 } else if (receiver.isConstantList() || receiver.isConstantString()) { | 920 } else if (receiver.isConstantList() || receiver.isConstantString()) { |
| 905 return graph.addConstantInt(receiver.constant.length, closedWorld); | 921 return _graph.addConstantInt(receiver.constant.length, _closedWorld); |
| 906 } else { | 922 } else { |
| 907 var type = receiver.instructionType; | 923 var type = receiver.instructionType; |
| 908 if (type.isContainer && type.length != null) { | 924 if (type.isContainer && type.length != null) { |
| 909 HInstruction constant = graph.addConstantInt(type.length, closedWorld); | 925 HInstruction constant = |
| 926 _graph.addConstantInt(type.length, _closedWorld); |
| 910 if (type.isNullable) { | 927 if (type.isNullable) { |
| 911 // If the container can be null, we update all uses of the length | 928 // If the container can be null, we update all uses of the length |
| 912 // access to use the constant instead, but keep the length access in | 929 // access to use the constant instead, but keep the length access in |
| 913 // the graph, to ensure we still have a null check. | 930 // the graph, to ensure we still have a null check. |
| 914 node.block.rewrite(node, constant); | 931 node.block.rewrite(node, constant); |
| 915 return node; | 932 return node; |
| 916 } else { | 933 } else { |
| 917 return constant; | 934 return constant; |
| 918 } | 935 } |
| 919 } | 936 } |
| 920 } | 937 } |
| 921 | 938 |
| 922 if (node.isAssignable && | 939 if (node.isAssignable && |
| 923 isFixedLength(receiver.instructionType, closedWorld)) { | 940 isFixedLength(receiver.instructionType, _closedWorld)) { |
| 924 // The input type has changed to fixed-length so change to an unassignable | 941 // The input type has changed to fixed-length so change to an unassignable |
| 925 // HGetLength to allow more GVN optimizations. | 942 // HGetLength to allow more GVN optimizations. |
| 926 return new HGetLength(receiver, node.instructionType, | 943 return new HGetLength(receiver, node.instructionType, |
| 927 isAssignable: false); | 944 isAssignable: false); |
| 928 } | 945 } |
| 929 return node; | 946 return node; |
| 930 } | 947 } |
| 931 | 948 |
| 932 HInstruction visitIndex(HIndex node) { | 949 HInstruction visitIndex(HIndex node) { |
| 933 if (node.receiver.isConstantList() && node.index.isConstantInteger()) { | 950 if (node.receiver.isConstantList() && node.index.isConstantInteger()) { |
| 934 var instruction = node.receiver; | 951 var instruction = node.receiver; |
| 935 List<ConstantValue> entries = instruction.constant.entries; | 952 List<ConstantValue> entries = instruction.constant.entries; |
| 936 instruction = node.index; | 953 instruction = node.index; |
| 937 int index = instruction.constant.primitiveValue; | 954 int index = instruction.constant.primitiveValue; |
| 938 if (index >= 0 && index < entries.length) { | 955 if (index >= 0 && index < entries.length) { |
| 939 return graph.addConstant(entries[index], closedWorld); | 956 return _graph.addConstant(entries[index], _closedWorld); |
| 940 } | 957 } |
| 941 } | 958 } |
| 942 return node; | 959 return node; |
| 943 } | 960 } |
| 944 | 961 |
| 945 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { | 962 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { |
| 946 propagateConstantValueToUses(node); | 963 propagateConstantValueToUses(node); |
| 947 if (node.isInterceptedCall) { | 964 if (node.isInterceptedCall) { |
| 948 HInstruction folded = handleInterceptedCall(node); | 965 HInstruction folded = handleInterceptedCall(node); |
| 949 if (folded != node) return folded; | 966 if (folded != node) return folded; |
| 950 } | 967 } |
| 951 HInstruction receiver = node.getDartReceiver(closedWorld); | 968 HInstruction receiver = node.getDartReceiver(_closedWorld); |
| 952 FieldEntity field = | 969 FieldEntity field = |
| 953 findConcreteFieldForDynamicAccess(receiver, node.selector); | 970 findConcreteFieldForDynamicAccess(receiver, node.selector); |
| 954 if (field != null) return directFieldGet(receiver, field); | 971 if (field != null) return directFieldGet(receiver, field); |
| 955 | 972 |
| 956 if (node.element == null) { | 973 if (node.element == null) { |
| 957 MemberEntity element = closedWorld.locateSingleElement( | 974 MemberEntity element = _closedWorld.locateSingleElement( |
| 958 node.selector, receiver.instructionType); | 975 node.selector, receiver.instructionType); |
| 959 if (element != null && element.name == node.selector.name) { | 976 if (element != null && element.name == node.selector.name) { |
| 960 node.element = element; | 977 node.element = element; |
| 961 if (element.isFunction) { | 978 if (element.isFunction) { |
| 962 // A property extraction getter, aka a tear-off. | 979 // A property extraction getter, aka a tear-off. |
| 963 node.sideEffects.clearAllDependencies(); | 980 node.sideEffects.clearAllDependencies(); |
| 964 node.sideEffects.clearAllSideEffects(); | 981 node.sideEffects.clearAllSideEffects(); |
| 965 node.setUseGvn(); // We don't care about identity of tear-offs. | 982 node.setUseGvn(); // We don't care about identity of tear-offs. |
| 966 } | 983 } |
| 967 } | 984 } |
| 968 } | 985 } |
| 969 return node; | 986 return node; |
| 970 } | 987 } |
| 971 | 988 |
| 972 HInstruction directFieldGet(HInstruction receiver, FieldEntity field) { | 989 HInstruction directFieldGet(HInstruction receiver, FieldEntity field) { |
| 973 bool isAssignable = !closedWorld.fieldNeverChanges(field); | 990 bool isAssignable = !_closedWorld.fieldNeverChanges(field); |
| 974 | 991 |
| 975 TypeMask type; | 992 TypeMask type; |
| 976 if (backend.nativeData.isNativeClass(field.enclosingClass)) { | 993 if (_nativeData.isNativeClass(field.enclosingClass)) { |
| 977 type = TypeMaskFactory.fromNativeBehavior( | 994 type = TypeMaskFactory.fromNativeBehavior( |
| 978 backend.nativeData.getNativeFieldLoadBehavior(field), closedWorld); | 995 _nativeData.getNativeFieldLoadBehavior(field), _closedWorld); |
| 979 } else { | 996 } else { |
| 980 type = TypeMaskFactory.inferredTypeForElement( | 997 type = TypeMaskFactory.inferredTypeForElement( |
| 981 field as Entity, globalInferenceResults); | 998 field as Entity, _globalInferenceResults); |
| 982 } | 999 } |
| 983 | 1000 |
| 984 return new HFieldGet(field, receiver, type, isAssignable: isAssignable); | 1001 return new HFieldGet(field, receiver, type, isAssignable: isAssignable); |
| 985 } | 1002 } |
| 986 | 1003 |
| 987 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { | 1004 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { |
| 988 if (node.isInterceptedCall) { | 1005 if (node.isInterceptedCall) { |
| 989 HInstruction folded = handleInterceptedCall(node); | 1006 HInstruction folded = handleInterceptedCall(node); |
| 990 if (folded != node) return folded; | 1007 if (folded != node) return folded; |
| 991 } | 1008 } |
| 992 | 1009 |
| 993 HInstruction receiver = node.getDartReceiver(closedWorld); | 1010 HInstruction receiver = node.getDartReceiver(_closedWorld); |
| 994 FieldElement field = | 1011 FieldElement field = |
| 995 findConcreteFieldForDynamicAccess(receiver, node.selector); | 1012 findConcreteFieldForDynamicAccess(receiver, node.selector); |
| 996 if (field == null || !field.isAssignable) return node; | 1013 if (field == null || !field.isAssignable) return node; |
| 997 // Use `node.inputs.last` in case the call follows the interceptor calling | 1014 // Use `node.inputs.last` in case the call follows the interceptor calling |
| 998 // convention, but is not a call on an interceptor. | 1015 // convention, but is not a call on an interceptor. |
| 999 HInstruction value = node.inputs.last; | 1016 HInstruction value = node.inputs.last; |
| 1000 if (compiler.options.enableTypeAssertions) { | 1017 if (_options.enableTypeAssertions) { |
| 1001 ResolutionDartType type = field.type; | 1018 ResolutionDartType type = field.type; |
| 1002 if (!type.treatAsRaw || | 1019 if (!type.treatAsRaw || |
| 1003 type.isTypeVariable || | 1020 type.isTypeVariable || |
| 1004 type.unaliased.isFunctionType) { | 1021 type.unaliased.isFunctionType) { |
| 1005 // We cannot generate the correct type representation here, so don't | 1022 // We cannot generate the correct type representation here, so don't |
| 1006 // inline this access. | 1023 // inline this access. |
| 1007 // TODO(sra): If the input is such that we don't need a type check, we | 1024 // TODO(sra): If the input is such that we don't need a type check, we |
| 1008 // can skip the test an generate the HFieldSet. | 1025 // can skip the test an generate the HFieldSet. |
| 1009 return node; | 1026 return node; |
| 1010 } | 1027 } |
| 1011 HInstruction other = value.convertType( | 1028 HInstruction other = value.convertType( |
| 1012 closedWorld, type, HTypeConversion.CHECKED_MODE_CHECK); | 1029 _closedWorld, type, HTypeConversion.CHECKED_MODE_CHECK); |
| 1013 if (other != value) { | 1030 if (other != value) { |
| 1014 node.block.addBefore(node, other); | 1031 node.block.addBefore(node, other); |
| 1015 value = other; | 1032 value = other; |
| 1016 } | 1033 } |
| 1017 } | 1034 } |
| 1018 return new HFieldSet(field, receiver, value); | 1035 return new HFieldSet(field, receiver, value); |
| 1019 } | 1036 } |
| 1020 | 1037 |
| 1021 HInstruction visitInvokeStatic(HInvokeStatic node) { | 1038 HInstruction visitInvokeStatic(HInvokeStatic node) { |
| 1022 propagateConstantValueToUses(node); | 1039 propagateConstantValueToUses(node); |
| 1023 MemberEntity element = node.element; | 1040 MemberEntity element = node.element; |
| 1024 | 1041 |
| 1025 if (element == compiler.commonElements.identicalFunction) { | 1042 if (element == commonElements.identicalFunction) { |
| 1026 if (node.inputs.length == 2) { | 1043 if (node.inputs.length == 2) { |
| 1027 return new HIdentity(node.inputs[0], node.inputs[1], null, | 1044 return new HIdentity(node.inputs[0], node.inputs[1], null, |
| 1028 closedWorld.commonMasks.boolType) | 1045 _closedWorld.commonMasks.boolType) |
| 1029 ..sourceInformation = node.sourceInformation; | 1046 ..sourceInformation = node.sourceInformation; |
| 1030 } | 1047 } |
| 1031 } else if (element == backend.helpers.checkConcurrentModificationError) { | 1048 } else if (element == _helpers.checkConcurrentModificationError) { |
| 1032 if (node.inputs.length == 2) { | 1049 if (node.inputs.length == 2) { |
| 1033 HInstruction firstArgument = node.inputs[0]; | 1050 HInstruction firstArgument = node.inputs[0]; |
| 1034 if (firstArgument is HConstant) { | 1051 if (firstArgument is HConstant) { |
| 1035 HConstant constant = firstArgument; | 1052 HConstant constant = firstArgument; |
| 1036 if (constant.constant.isTrue) return constant; | 1053 if (constant.constant.isTrue) return constant; |
| 1037 } | 1054 } |
| 1038 } | 1055 } |
| 1039 } else if (element == backend.helpers.checkInt) { | 1056 } else if (element == _helpers.checkInt) { |
| 1040 if (node.inputs.length == 1) { | 1057 if (node.inputs.length == 1) { |
| 1041 HInstruction argument = node.inputs[0]; | 1058 HInstruction argument = node.inputs[0]; |
| 1042 if (argument.isInteger(closedWorld)) return argument; | 1059 if (argument.isInteger(_closedWorld)) return argument; |
| 1043 } | 1060 } |
| 1044 } else if (element == backend.helpers.checkNum) { | 1061 } else if (element == _helpers.checkNum) { |
| 1045 if (node.inputs.length == 1) { | 1062 if (node.inputs.length == 1) { |
| 1046 HInstruction argument = node.inputs[0]; | 1063 HInstruction argument = node.inputs[0]; |
| 1047 if (argument.isNumber(closedWorld)) return argument; | 1064 if (argument.isNumber(_closedWorld)) return argument; |
| 1048 } | 1065 } |
| 1049 } else if (element == backend.helpers.checkString) { | 1066 } else if (element == _helpers.checkString) { |
| 1050 if (node.inputs.length == 1) { | 1067 if (node.inputs.length == 1) { |
| 1051 HInstruction argument = node.inputs[0]; | 1068 HInstruction argument = node.inputs[0]; |
| 1052 if (argument.isString(closedWorld)) return argument; | 1069 if (argument.isString(_closedWorld)) return argument; |
| 1053 } | 1070 } |
| 1054 } | 1071 } |
| 1055 return node; | 1072 return node; |
| 1056 } | 1073 } |
| 1057 | 1074 |
| 1058 HInstruction visitStringConcat(HStringConcat node) { | 1075 HInstruction visitStringConcat(HStringConcat node) { |
| 1059 // Simplify string concat: | 1076 // Simplify string concat: |
| 1060 // | 1077 // |
| 1061 // "" + R -> R | 1078 // "" + R -> R |
| 1062 // L + "" -> L | 1079 // L + "" -> L |
| (...skipping 24 matching lines...) Expand all Loading... |
| 1087 prefix = leftConcat.left; | 1104 prefix = leftConcat.left; |
| 1088 leftString = getString(leftConcat.right); | 1105 leftString = getString(leftConcat.right); |
| 1089 if (leftString == null) return node; | 1106 if (leftString == null) return node; |
| 1090 } | 1107 } |
| 1091 | 1108 |
| 1092 if (leftString.primitiveValue.length + rightString.primitiveValue.length > | 1109 if (leftString.primitiveValue.length + rightString.primitiveValue.length > |
| 1093 MAX_SHARED_CONSTANT_FOLDED_STRING_LENGTH) { | 1110 MAX_SHARED_CONSTANT_FOLDED_STRING_LENGTH) { |
| 1094 if (node.usedBy.length > 1) return node; | 1111 if (node.usedBy.length > 1) return node; |
| 1095 } | 1112 } |
| 1096 | 1113 |
| 1097 HInstruction folded = graph.addConstant( | 1114 HInstruction folded = _graph.addConstant( |
| 1098 constantSystem.createString(new ast.DartString.concat( | 1115 constantSystem.createString(new ast.DartString.concat( |
| 1099 leftString.primitiveValue, rightString.primitiveValue)), | 1116 leftString.primitiveValue, rightString.primitiveValue)), |
| 1100 closedWorld); | 1117 _closedWorld); |
| 1101 if (prefix == null) return folded; | 1118 if (prefix == null) return folded; |
| 1102 return new HStringConcat( | 1119 return new HStringConcat( |
| 1103 prefix, folded, closedWorld.commonMasks.stringType); | 1120 prefix, folded, _closedWorld.commonMasks.stringType); |
| 1104 } | 1121 } |
| 1105 | 1122 |
| 1106 HInstruction visitStringify(HStringify node) { | 1123 HInstruction visitStringify(HStringify node) { |
| 1107 HInstruction input = node.inputs[0]; | 1124 HInstruction input = node.inputs[0]; |
| 1108 if (input.isString(closedWorld)) return input; | 1125 if (input.isString(_closedWorld)) return input; |
| 1109 | 1126 |
| 1110 HInstruction tryConstant() { | 1127 HInstruction tryConstant() { |
| 1111 if (!input.isConstant()) return null; | 1128 if (!input.isConstant()) return null; |
| 1112 HConstant constant = input; | 1129 HConstant constant = input; |
| 1113 if (!constant.constant.isPrimitive) return null; | 1130 if (!constant.constant.isPrimitive) return null; |
| 1114 if (constant.constant.isInt) { | 1131 if (constant.constant.isInt) { |
| 1115 // Only constant-fold int.toString() when Dart and JS results the same. | 1132 // Only constant-fold int.toString() when Dart and JS results the same. |
| 1116 // TODO(18103): We should be able to remove this work-around when issue | 1133 // TODO(18103): We should be able to remove this work-around when issue |
| 1117 // 18103 is resolved by providing the correct string. | 1134 // 18103 is resolved by providing the correct string. |
| 1118 IntConstantValue intConstant = constant.constant; | 1135 IntConstantValue intConstant = constant.constant; |
| 1119 // Very conservative range. | 1136 // Very conservative range. |
| 1120 if (!intConstant.isUInt32()) return null; | 1137 if (!intConstant.isUInt32()) return null; |
| 1121 } | 1138 } |
| 1122 PrimitiveConstantValue primitive = constant.constant; | 1139 PrimitiveConstantValue primitive = constant.constant; |
| 1123 return graph.addConstant( | 1140 return _graph.addConstant( |
| 1124 constantSystem.createString(primitive.toDartString()), closedWorld); | 1141 constantSystem.createString(primitive.toDartString()), _closedWorld); |
| 1125 } | 1142 } |
| 1126 | 1143 |
| 1127 HInstruction tryToString() { | 1144 HInstruction tryToString() { |
| 1128 // If the `toString` method is guaranteed to return a string we can call | 1145 // If the `toString` method is guaranteed to return a string we can call |
| 1129 // it directly. Keep the stringifier for primitives (since they have fast | 1146 // it directly. Keep the stringifier for primitives (since they have fast |
| 1130 // path code in the stringifier) and for classes requiring interceptors | 1147 // path code in the stringifier) and for classes requiring interceptors |
| 1131 // (since SsaInstructionSimplifier runs after SsaSimplifyInterceptors). | 1148 // (since SsaInstructionSimplifier runs after SsaSimplifyInterceptors). |
| 1132 if (input.canBePrimitive(closedWorld)) return null; | 1149 if (input.canBePrimitive(_closedWorld)) return null; |
| 1133 if (input.canBeNull()) return null; | 1150 if (input.canBeNull()) return null; |
| 1134 Selector selector = Selectors.toString_; | 1151 Selector selector = Selectors.toString_; |
| 1135 TypeMask toStringType = TypeMaskFactory.inferredTypeForSelector( | 1152 TypeMask toStringType = TypeMaskFactory.inferredTypeForSelector( |
| 1136 selector, input.instructionType, globalInferenceResults); | 1153 selector, input.instructionType, _globalInferenceResults); |
| 1137 if (!toStringType.containsOnlyString(closedWorld)) return null; | 1154 if (!toStringType.containsOnlyString(_closedWorld)) return null; |
| 1138 // All intercepted classes extend `Interceptor`, so if the receiver can't | 1155 // All intercepted classes extend `Interceptor`, so if the receiver can't |
| 1139 // be a class extending `Interceptor` then it can be called directly. | 1156 // be a class extending `Interceptor` then it can be called directly. |
| 1140 if (new TypeMask.nonNullSubclass(helpers.jsInterceptorClass, closedWorld) | 1157 if (new TypeMask.nonNullSubclass( |
| 1141 .isDisjoint(input.instructionType, closedWorld)) { | 1158 _helpers.jsInterceptorClass, _closedWorld) |
| 1159 .isDisjoint(input.instructionType, _closedWorld)) { |
| 1142 var inputs = <HInstruction>[input, input]; // [interceptor, receiver]. | 1160 var inputs = <HInstruction>[input, input]; // [interceptor, receiver]. |
| 1143 HInstruction result = new HInvokeDynamicMethod( | 1161 HInstruction result = new HInvokeDynamicMethod( |
| 1144 selector, | 1162 selector, |
| 1145 input.instructionType, // receiver mask. | 1163 input.instructionType, // receiver mask. |
| 1146 inputs, | 1164 inputs, |
| 1147 toStringType)..sourceInformation = node.sourceInformation; | 1165 toStringType)..sourceInformation = node.sourceInformation; |
| 1148 return result; | 1166 return result; |
| 1149 } | 1167 } |
| 1150 return null; | 1168 return null; |
| 1151 } | 1169 } |
| 1152 | 1170 |
| 1153 return tryConstant() ?? tryToString() ?? node; | 1171 return tryConstant() ?? tryToString() ?? node; |
| 1154 } | 1172 } |
| 1155 | 1173 |
| 1156 HInstruction visitOneShotInterceptor(HOneShotInterceptor node) { | 1174 HInstruction visitOneShotInterceptor(HOneShotInterceptor node) { |
| 1157 return handleInterceptedCall(node); | 1175 return handleInterceptedCall(node); |
| 1158 } | 1176 } |
| 1159 | 1177 |
| 1160 bool needsSubstitutionForTypeVariableAccess(ClassEntity cls) { | 1178 bool needsSubstitutionForTypeVariableAccess(ClassEntity cls) { |
| 1161 if (closedWorld.isUsedAsMixin(cls)) return true; | 1179 if (_closedWorld.isUsedAsMixin(cls)) return true; |
| 1162 | 1180 |
| 1163 return closedWorld.anyStrictSubclassOf(cls, (ClassEntity subclass) { | 1181 return _closedWorld.anyStrictSubclassOf(cls, (ClassEntity subclass) { |
| 1164 return !backend.rtiSubstitutions.isTrivialSubstitution(subclass, cls); | 1182 return !_rtiSubstitutions.isTrivialSubstitution(subclass, cls); |
| 1165 }); | 1183 }); |
| 1166 } | 1184 } |
| 1167 | 1185 |
| 1168 HInstruction visitTypeInfoExpression(HTypeInfoExpression node) { | 1186 HInstruction visitTypeInfoExpression(HTypeInfoExpression node) { |
| 1169 // Identify the case where the type info expression would be of the form: | 1187 // Identify the case where the type info expression would be of the form: |
| 1170 // | 1188 // |
| 1171 // [getTypeArgumentByIndex(this, 0), .., getTypeArgumentByIndex(this, k)] | 1189 // [getTypeArgumentByIndex(this, 0), .., getTypeArgumentByIndex(this, k)] |
| 1172 // | 1190 // |
| 1173 // and k is the number of type arguments of 'this'. We can simply copy the | 1191 // and k is the number of type arguments of 'this'. We can simply copy the |
| 1174 // list from 'this'. | 1192 // list from 'this'. |
| (...skipping 28 matching lines...) Expand all Loading... |
| 1203 // substitution, even though the general case does, e.g. inlining a | 1221 // substitution, even though the general case does, e.g. inlining a |
| 1204 // method on an exact class. | 1222 // method on an exact class. |
| 1205 return null; | 1223 return null; |
| 1206 } | 1224 } |
| 1207 } else { | 1225 } else { |
| 1208 return null; | 1226 return null; |
| 1209 } | 1227 } |
| 1210 } | 1228 } |
| 1211 | 1229 |
| 1212 if (source == null) return null; | 1230 if (source == null) return null; |
| 1213 return new HTypeInfoReadRaw(source, closedWorld.commonMasks.dynamicType); | 1231 return new HTypeInfoReadRaw(source, _closedWorld.commonMasks.dynamicType); |
| 1214 } | 1232 } |
| 1215 | 1233 |
| 1216 // TODO(sra): Consider fusing type expression trees with no type variables, | 1234 // TODO(sra): Consider fusing type expression trees with no type variables, |
| 1217 // as these could be represented like constants. | 1235 // as these could be represented like constants. |
| 1218 | 1236 |
| 1219 return tryCopyInfo() ?? node; | 1237 return tryCopyInfo() ?? node; |
| 1220 } | 1238 } |
| 1221 | 1239 |
| 1222 HInstruction visitTypeInfoReadVariable(HTypeInfoReadVariable node) { | 1240 HInstruction visitTypeInfoReadVariable(HTypeInfoReadVariable node) { |
| 1223 ResolutionTypeVariableType variable = node.variable; | 1241 ResolutionTypeVariableType variable = node.variable; |
| 1224 HInstruction object = node.object; | 1242 HInstruction object = node.object; |
| 1225 | 1243 |
| 1226 HInstruction finishGroundType(ResolutionInterfaceType groundType) { | 1244 HInstruction finishGroundType(ResolutionInterfaceType groundType) { |
| 1227 ResolutionInterfaceType typeAtVariable = | 1245 ResolutionInterfaceType typeAtVariable = |
| 1228 groundType.asInstanceOf(variable.element.enclosingClass); | 1246 groundType.asInstanceOf(variable.element.enclosingClass); |
| 1229 if (typeAtVariable != null) { | 1247 if (typeAtVariable != null) { |
| 1230 int index = variable.element.index; | 1248 int index = variable.element.index; |
| 1231 ResolutionDartType typeArgument = typeAtVariable.typeArguments[index]; | 1249 ResolutionDartType typeArgument = typeAtVariable.typeArguments[index]; |
| 1232 HInstruction replacement = new HTypeInfoExpression( | 1250 HInstruction replacement = new HTypeInfoExpression( |
| 1233 TypeInfoExpressionKind.COMPLETE, | 1251 TypeInfoExpressionKind.COMPLETE, |
| 1234 typeArgument, | 1252 typeArgument, |
| 1235 const <HInstruction>[], | 1253 const <HInstruction>[], |
| 1236 closedWorld.commonMasks.dynamicType); | 1254 _closedWorld.commonMasks.dynamicType); |
| 1237 return replacement; | 1255 return replacement; |
| 1238 } | 1256 } |
| 1239 return node; | 1257 return node; |
| 1240 } | 1258 } |
| 1241 | 1259 |
| 1242 /// Read the type variable from an allocation of type [createdClass], where | 1260 /// Read the type variable from an allocation of type [createdClass], where |
| 1243 /// [selectTypeArgumentFromObjectCreation] extracts the type argument from | 1261 /// [selectTypeArgumentFromObjectCreation] extracts the type argument from |
| 1244 /// the allocation for factory constructor call. | 1262 /// the allocation for factory constructor call. |
| 1245 HInstruction finishSubstituted(ClassElement createdClass, | 1263 HInstruction finishSubstituted(ClassElement createdClass, |
| 1246 HInstruction selectTypeArgumentFromObjectCreation(int index)) { | 1264 HInstruction selectTypeArgumentFromObjectCreation(int index)) { |
| 1247 HInstruction instructionForTypeVariable(ResolutionTypeVariableType tv) { | 1265 HInstruction instructionForTypeVariable(ResolutionTypeVariableType tv) { |
| 1248 return selectTypeArgumentFromObjectCreation( | 1266 return selectTypeArgumentFromObjectCreation( |
| 1249 createdClass.thisType.typeArguments.indexOf(tv)); | 1267 createdClass.thisType.typeArguments.indexOf(tv)); |
| 1250 } | 1268 } |
| 1251 | 1269 |
| 1252 ResolutionDartType type = createdClass.thisType | 1270 ResolutionDartType type = createdClass.thisType |
| 1253 .asInstanceOf(variable.element.enclosingClass) | 1271 .asInstanceOf(variable.element.enclosingClass) |
| 1254 .typeArguments[variable.element.index]; | 1272 .typeArguments[variable.element.index]; |
| 1255 if (type is ResolutionTypeVariableType) { | 1273 if (type is ResolutionTypeVariableType) { |
| 1256 return instructionForTypeVariable(type); | 1274 return instructionForTypeVariable(type); |
| 1257 } | 1275 } |
| 1258 List<HInstruction> arguments = <HInstruction>[]; | 1276 List<HInstruction> arguments = <HInstruction>[]; |
| 1259 type.forEachTypeVariable((v) { | 1277 type.forEachTypeVariable((v) { |
| 1260 arguments.add(instructionForTypeVariable(v)); | 1278 arguments.add(instructionForTypeVariable(v)); |
| 1261 }); | 1279 }); |
| 1262 HInstruction replacement = new HTypeInfoExpression( | 1280 HInstruction replacement = new HTypeInfoExpression( |
| 1263 TypeInfoExpressionKind.COMPLETE, | 1281 TypeInfoExpressionKind.COMPLETE, |
| 1264 type, | 1282 type, |
| 1265 arguments, | 1283 arguments, |
| 1266 closedWorld.commonMasks.dynamicType); | 1284 _closedWorld.commonMasks.dynamicType); |
| 1267 return replacement; | 1285 return replacement; |
| 1268 } | 1286 } |
| 1269 | 1287 |
| 1270 // Type variable evaluated in the context of a constant can be replaced with | 1288 // Type variable evaluated in the context of a constant can be replaced with |
| 1271 // a ground term type. | 1289 // a ground term type. |
| 1272 if (object is HConstant) { | 1290 if (object is HConstant) { |
| 1273 ConstantValue value = object.constant; | 1291 ConstantValue value = object.constant; |
| 1274 if (value is ConstructedConstantValue) { | 1292 if (value is ConstructedConstantValue) { |
| 1275 return finishGroundType(value.type); | 1293 return finishGroundType(value.type); |
| 1276 } | 1294 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1289 // HTypeInfoExpression(t_0, t_1, t_2, ...)]); | 1307 // HTypeInfoExpression(t_0, t_1, t_2, ...)]); |
| 1290 // | 1308 // |
| 1291 // The `t_i` are the values of the type parameters of ClassElement. | 1309 // The `t_i` are the values of the type parameters of ClassElement. |
| 1292 | 1310 |
| 1293 if (object is HCreate) { | 1311 if (object is HCreate) { |
| 1294 void registerInstantiations() { | 1312 void registerInstantiations() { |
| 1295 // Forwarding the type variable references might cause the HCreate to | 1313 // Forwarding the type variable references might cause the HCreate to |
| 1296 // become dead. This breaks the algorithm for generating the per-type | 1314 // become dead. This breaks the algorithm for generating the per-type |
| 1297 // runtime type information, so we instantiate them here in case the | 1315 // runtime type information, so we instantiate them here in case the |
| 1298 // HCreate becomes dead. | 1316 // HCreate becomes dead. |
| 1299 object.instantiatedTypes?.forEach(registry.registerInstantiation); | 1317 object.instantiatedTypes?.forEach(_registry.registerInstantiation); |
| 1300 } | 1318 } |
| 1301 | 1319 |
| 1302 if (object.hasRtiInput) { | 1320 if (object.hasRtiInput) { |
| 1303 HInstruction typeInfo = object.rtiInput; | 1321 HInstruction typeInfo = object.rtiInput; |
| 1304 if (typeInfo is HTypeInfoExpression) { | 1322 if (typeInfo is HTypeInfoExpression) { |
| 1305 registerInstantiations(); | 1323 registerInstantiations(); |
| 1306 return finishSubstituted( | 1324 return finishSubstituted( |
| 1307 object.element, (int index) => typeInfo.inputs[index]); | 1325 object.element, (int index) => typeInfo.inputs[index]); |
| 1308 } | 1326 } |
| 1309 } else { | 1327 } else { |
| 1310 // Non-generic type (which extends or mixes in a generic type, for | 1328 // Non-generic type (which extends or mixes in a generic type, for |
| 1311 // example CodeUnits extends UnmodifiableListBase<int>). Also used for | 1329 // example CodeUnits extends UnmodifiableListBase<int>). Also used for |
| 1312 // raw-type when the type parameters are elided. | 1330 // raw-type when the type parameters are elided. |
| 1313 registerInstantiations(); | 1331 registerInstantiations(); |
| 1314 return finishSubstituted( | 1332 return finishSubstituted( |
| 1315 object.element, | 1333 object.element, |
| 1316 // If there are type arguments, all type arguments are 'dynamic'. | 1334 // If there are type arguments, all type arguments are 'dynamic'. |
| 1317 (int i) => graph.addConstantNull(closedWorld)); | 1335 (int i) => _graph.addConstantNull(_closedWorld)); |
| 1318 } | 1336 } |
| 1319 } | 1337 } |
| 1320 | 1338 |
| 1321 // TODO(sra): Factory constructors pass type arguments after the value | 1339 // TODO(sra): Factory constructors pass type arguments after the value |
| 1322 // arguments. The [selectTypeArgumentFromObjectCreation] argument of | 1340 // arguments. The [selectTypeArgumentFromObjectCreation] argument of |
| 1323 // [finishSubstituted] indexes into these type arguments. | 1341 // [finishSubstituted] indexes into these type arguments. |
| 1324 | 1342 |
| 1325 return node; | 1343 return node; |
| 1326 } | 1344 } |
| 1327 } | 1345 } |
| 1328 | 1346 |
| 1329 class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase { | 1347 class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase { |
| 1330 final Set<HInstruction> boundsChecked; | 1348 final Set<HInstruction> boundsChecked; |
| 1331 final bool trustPrimitives; | 1349 final bool trustPrimitives; |
| 1332 final JavaScriptBackend backend; | 1350 final BackendHelpers _helpers; |
| 1333 final ClosedWorld closedWorld; | 1351 final ClosedWorld closedWorld; |
| 1334 final String name = "SsaCheckInserter"; | 1352 final String name = "SsaCheckInserter"; |
| 1335 HGraph graph; | 1353 HGraph graph; |
| 1336 | 1354 |
| 1337 SsaCheckInserter( | 1355 SsaCheckInserter(this.trustPrimitives, this._helpers, this.closedWorld, |
| 1338 this.trustPrimitives, this.backend, this.closedWorld, this.boundsChecked); | 1356 this.boundsChecked); |
| 1339 | |
| 1340 BackendHelpers get helpers => backend.helpers; | |
| 1341 | 1357 |
| 1342 void visitGraph(HGraph graph) { | 1358 void visitGraph(HGraph graph) { |
| 1343 this.graph = graph; | 1359 this.graph = graph; |
| 1344 | 1360 |
| 1345 // In --trust-primitives mode we don't add bounds checks. This is better | 1361 // In --trust-primitives mode we don't add bounds checks. This is better |
| 1346 // than trying to remove them later as the limit expression would become | 1362 // than trying to remove them later as the limit expression would become |
| 1347 // dead and require DCE. | 1363 // dead and require DCE. |
| 1348 if (trustPrimitives) return; | 1364 if (trustPrimitives) return; |
| 1349 | 1365 |
| 1350 visitDominatorTree(graph); | 1366 visitDominatorTree(graph); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1393 | 1409 |
| 1394 void visitIndexAssign(HIndexAssign node) { | 1410 void visitIndexAssign(HIndexAssign node) { |
| 1395 if (boundsChecked.contains(node)) return; | 1411 if (boundsChecked.contains(node)) return; |
| 1396 HInstruction index = node.index; | 1412 HInstruction index = node.index; |
| 1397 index = insertBoundsCheck(node, node.receiver, index); | 1413 index = insertBoundsCheck(node, node.receiver, index); |
| 1398 } | 1414 } |
| 1399 | 1415 |
| 1400 void visitInvokeDynamicMethod(HInvokeDynamicMethod node) { | 1416 void visitInvokeDynamicMethod(HInvokeDynamicMethod node) { |
| 1401 MemberEntity element = node.element; | 1417 MemberEntity element = node.element; |
| 1402 if (node.isInterceptedCall) return; | 1418 if (node.isInterceptedCall) return; |
| 1403 if (element != helpers.jsArrayRemoveLast) return; | 1419 if (element != _helpers.jsArrayRemoveLast) return; |
| 1404 if (boundsChecked.contains(node)) return; | 1420 if (boundsChecked.contains(node)) return; |
| 1405 // `0` is the index we want to check, but we want to report `-1`, as if we | 1421 // `0` is the index we want to check, but we want to report `-1`, as if we |
| 1406 // executed `a[a.length-1]` | 1422 // executed `a[a.length-1]` |
| 1407 HBoundsCheck check = insertBoundsCheck( | 1423 HBoundsCheck check = insertBoundsCheck( |
| 1408 node, node.receiver, graph.addConstantInt(0, closedWorld)); | 1424 node, node.receiver, graph.addConstantInt(0, closedWorld)); |
| 1409 HInstruction minusOne = graph.addConstantInt(-1, closedWorld); | 1425 HInstruction minusOne = graph.addConstantInt(-1, closedWorld); |
| 1410 check.inputs.add(minusOne); | 1426 check.inputs.add(minusOne); |
| 1411 minusOne.usedBy.add(check); | 1427 minusOne.usedBy.add(check); |
| 1412 } | 1428 } |
| 1413 } | 1429 } |
| (...skipping 837 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2251 } | 2267 } |
| 2252 } | 2268 } |
| 2253 } | 2269 } |
| 2254 | 2270 |
| 2255 /** | 2271 /** |
| 2256 * Optimization phase that tries to eliminate memory loads (for | 2272 * Optimization phase that tries to eliminate memory loads (for |
| 2257 * example [HFieldGet]), when it knows the value stored in that memory | 2273 * example [HFieldGet]), when it knows the value stored in that memory |
| 2258 * location. | 2274 * location. |
| 2259 */ | 2275 */ |
| 2260 class SsaLoadElimination extends HBaseVisitor implements OptimizationPhase { | 2276 class SsaLoadElimination extends HBaseVisitor implements OptimizationPhase { |
| 2261 final JavaScriptBackend backend; | 2277 final BackendHelpers _helpers; |
| 2262 final Compiler compiler; | 2278 final Compiler compiler; |
| 2263 final ClosedWorld closedWorld; | 2279 final ClosedWorld closedWorld; |
| 2264 final String name = "SsaLoadElimination"; | 2280 final String name = "SsaLoadElimination"; |
| 2265 MemorySet memorySet; | 2281 MemorySet memorySet; |
| 2266 List<MemorySet> memories; | 2282 List<MemorySet> memories; |
| 2267 bool newGvnCandidates = false; | 2283 bool newGvnCandidates = false; |
| 2268 | 2284 |
| 2269 SsaLoadElimination(this.backend, this.compiler, this.closedWorld); | 2285 SsaLoadElimination(this._helpers, this.compiler, this.closedWorld); |
| 2270 | 2286 |
| 2271 void visitGraph(HGraph graph) { | 2287 void visitGraph(HGraph graph) { |
| 2272 memories = new List<MemorySet>(graph.blocks.length); | 2288 memories = new List<MemorySet>(graph.blocks.length); |
| 2273 List<HBasicBlock> blocks = graph.blocks; | 2289 List<HBasicBlock> blocks = graph.blocks; |
| 2274 for (int i = 0; i < blocks.length; i++) { | 2290 for (int i = 0; i < blocks.length; i++) { |
| 2275 HBasicBlock block = blocks[i]; | 2291 HBasicBlock block = blocks[i]; |
| 2276 visitBasicBlock(block); | 2292 visitBasicBlock(block); |
| 2277 if (block.successors.isNotEmpty && block.successors[0].isLoopHeader()) { | 2293 if (block.successors.isNotEmpty && block.successors[0].isLoopHeader()) { |
| 2278 // We've reached the ending block of a loop. Iterate over the | 2294 // We've reached the ending block of a loop. Iterate over the |
| 2279 // blocks of the loop again to take values that flow from that | 2295 // blocks of the loop again to take values that flow from that |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2328 } | 2344 } |
| 2329 | 2345 |
| 2330 void visitFieldGet(HFieldGet instruction) { | 2346 void visitFieldGet(HFieldGet instruction) { |
| 2331 if (instruction.isNullCheck) return; | 2347 if (instruction.isNullCheck) return; |
| 2332 FieldEntity element = instruction.element; | 2348 FieldEntity element = instruction.element; |
| 2333 HInstruction receiver = instruction.getDartReceiver(closedWorld).nonCheck(); | 2349 HInstruction receiver = instruction.getDartReceiver(closedWorld).nonCheck(); |
| 2334 _visitFieldGet(element, receiver, instruction); | 2350 _visitFieldGet(element, receiver, instruction); |
| 2335 } | 2351 } |
| 2336 | 2352 |
| 2337 void visitGetLength(HGetLength instruction) { | 2353 void visitGetLength(HGetLength instruction) { |
| 2338 _visitFieldGet(backend.helpers.jsIndexableLength, | 2354 _visitFieldGet(_helpers.jsIndexableLength, instruction.receiver.nonCheck(), |
| 2339 instruction.receiver.nonCheck(), instruction); | 2355 instruction); |
| 2340 } | 2356 } |
| 2341 | 2357 |
| 2342 void _visitFieldGet( | 2358 void _visitFieldGet( |
| 2343 MemberEntity element, HInstruction receiver, HInstruction instruction) { | 2359 MemberEntity element, HInstruction receiver, HInstruction instruction) { |
| 2344 HInstruction existing = memorySet.lookupFieldValue(element, receiver); | 2360 HInstruction existing = memorySet.lookupFieldValue(element, receiver); |
| 2345 if (existing != null) { | 2361 if (existing != null) { |
| 2346 checkNewGvnCandidates(instruction, existing); | 2362 checkNewGvnCandidates(instruction, existing); |
| 2347 instruction.block.rewriteWithBetterUser(instruction, existing); | 2363 instruction.block.rewriteWithBetterUser(instruction, existing); |
| 2348 instruction.block.remove(instruction); | 2364 instruction.block.remove(instruction); |
| 2349 } else { | 2365 } else { |
| (...skipping 454 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2804 | 2820 |
| 2805 keyedValues.forEach((receiver, values) { | 2821 keyedValues.forEach((receiver, values) { |
| 2806 result.keyedValues[receiver] = | 2822 result.keyedValues[receiver] = |
| 2807 new Map<HInstruction, HInstruction>.from(values); | 2823 new Map<HInstruction, HInstruction>.from(values); |
| 2808 }); | 2824 }); |
| 2809 | 2825 |
| 2810 result.nonEscapingReceivers.addAll(nonEscapingReceivers); | 2826 result.nonEscapingReceivers.addAll(nonEscapingReceivers); |
| 2811 return result; | 2827 return result; |
| 2812 } | 2828 } |
| 2813 } | 2829 } |
| OLD | NEW |