Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of ssa; | 5 part of ssa; |
| 6 | 6 |
| 7 abstract class OptimizationPhase { | 7 abstract class OptimizationPhase { |
| 8 String get name; | 8 String get name; |
| 9 void visitGraph(HGraph graph); | 9 void visitGraph(HGraph graph); |
| 10 } | 10 } |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 138 void visitGraph(HGraph visitee) { | 138 void visitGraph(HGraph visitee) { |
| 139 graph = visitee; | 139 graph = visitee; |
| 140 visitDominatorTree(visitee); | 140 visitDominatorTree(visitee); |
| 141 } | 141 } |
| 142 | 142 |
| 143 visitBasicBlock(HBasicBlock block) { | 143 visitBasicBlock(HBasicBlock block) { |
| 144 HInstruction instruction = block.first; | 144 HInstruction instruction = block.first; |
| 145 while (instruction != null) { | 145 while (instruction != null) { |
| 146 HInstruction next = instruction.next; | 146 HInstruction next = instruction.next; |
| 147 HInstruction replacement = instruction.accept(this); | 147 HInstruction replacement = instruction.accept(this); |
| 148 if (!identical(replacement, instruction)) { | 148 if (replacement != instruction) { |
| 149 if (!replacement.isInBasicBlock()) { | |
| 150 // The constant folding can return an instruction that is already | |
| 151 // part of the graph (like an input), so we only add the replacement | |
| 152 // if necessary. | |
| 153 block.addAfter(instruction, replacement); | |
| 154 } | |
| 155 block.rewrite(instruction, replacement); | 149 block.rewrite(instruction, replacement); |
| 156 block.remove(instruction); | |
| 157 | 150 |
| 158 // If we can replace [instruction] with [replacement], then | 151 // If we can replace [instruction] with [replacement], then |
| 159 // [replacement]'s type can be narrowed. | 152 // [replacement]'s type can be narrowed. |
| 160 types[replacement] = | 153 types[replacement] = |
| 161 types[replacement].intersection(types[instruction], compiler); | 154 types[replacement].intersection(types[instruction], compiler); |
| 162 | 155 |
| 163 // If the replacement instruction does not know its | 156 // If the replacement instruction does not know its |
| 164 // source element, use the source element of the | 157 // source element, use the source element of the |
| 165 // instruction. | 158 // instruction. |
| 166 if (replacement.sourceElement == null) { | 159 if (replacement.sourceElement == null) { |
| 167 replacement.sourceElement = instruction.sourceElement; | 160 replacement.sourceElement = instruction.sourceElement; |
| 168 } | 161 } |
| 169 if (replacement.sourcePosition == null) { | 162 if (replacement.sourcePosition == null) { |
| 170 replacement.sourcePosition = instruction.sourcePosition; | 163 replacement.sourcePosition = instruction.sourcePosition; |
| 171 } | 164 } |
| 165 if (!replacement.isInBasicBlock()) { | |
| 166 // The constant folding can return an instruction that is already | |
| 167 // part of the graph (like an input), so we only add the replacement | |
| 168 // if necessary. | |
| 169 block.addAfter(instruction, replacement); | |
| 170 // Visit the replacement as the next instruction in case it | |
| 171 // can also be constant folded away. | |
| 172 next = replacement; | |
| 173 } | |
| 174 block.remove(instruction); | |
| 172 } | 175 } |
| 173 instruction = next; | 176 instruction = next; |
| 174 } | 177 } |
| 175 } | 178 } |
| 176 | 179 |
| 177 HInstruction visitInstruction(HInstruction node) { | 180 HInstruction visitInstruction(HInstruction node) { |
| 178 return node; | 181 return node; |
| 179 } | 182 } |
| 180 | 183 |
| 181 HInstruction visitBoolify(HBoolify node) { | 184 HInstruction visitBoolify(HBoolify node) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 213 | 216 |
| 214 HInstruction foldUnary(UnaryOperation operation, HInstruction operand) { | 217 HInstruction foldUnary(UnaryOperation operation, HInstruction operand) { |
| 215 if (operand is HConstant) { | 218 if (operand is HConstant) { |
| 216 HConstant receiver = operand; | 219 HConstant receiver = operand; |
| 217 Constant folded = operation.fold(receiver.constant); | 220 Constant folded = operation.fold(receiver.constant); |
| 218 if (folded != null) return graph.addConstant(folded); | 221 if (folded != null) return graph.addConstant(folded); |
| 219 } | 222 } |
| 220 return null; | 223 return null; |
| 221 } | 224 } |
| 222 | 225 |
| 226 HInstruction optimizeLengthInterceptedGetter(HInvokeDynamic node) { | |
| 227 HInstruction actualReceiver = node.inputs[1]; | |
| 228 if (actualReceiver.isIndexablePrimitive(types)) { | |
| 229 if (actualReceiver.isConstantString()) { | |
| 230 HConstant constantInput = actualReceiver; | |
| 231 StringConstant constant = constantInput.constant; | |
| 232 return graph.addConstantInt(constant.length, constantSystem); | |
| 233 } else if (actualReceiver.isConstantList()) { | |
| 234 HConstant constantInput = actualReceiver; | |
| 235 ListConstant constant = constantInput.constant; | |
| 236 return graph.addConstantInt(constant.length, constantSystem); | |
| 237 } | |
| 238 Element element; | |
| 239 bool isAssignable; | |
| 240 if (actualReceiver.isString(types)) { | |
| 241 element = backend.jsStringLength; | |
| 242 isAssignable = false; | |
| 243 } else { | |
| 244 element = backend.jsArrayLength; | |
| 245 isAssignable = !actualReceiver.isFixedArray(types); | |
| 246 } | |
| 247 HFieldGet result = new HFieldGet( | |
| 248 element, actualReceiver, isAssignable: isAssignable); | |
| 249 result.guaranteedType = HType.INTEGER; | |
| 250 types[result] = HType.INTEGER; | |
| 251 return result; | |
| 252 } else if (actualReceiver.isConstantMap()) { | |
| 253 HConstant constantInput = actualReceiver; | |
| 254 MapConstant constant = constantInput.constant; | |
| 255 return graph.addConstantInt(constant.length, constantSystem); | |
| 256 } | |
| 257 return node; | |
| 258 } | |
| 259 | |
| 223 HInstruction handleInterceptorCall(HInvokeDynamic node) { | 260 HInstruction handleInterceptorCall(HInvokeDynamic node) { |
| 224 // We only optimize for intercepted method calls in this method. | 261 // We only optimize for intercepted method calls in this method. |
| 225 if (node.selector.isGetter() || node.selector.isSetter()) return node; | 262 Selector selector = node.selector; |
| 226 | |
| 227 HInstruction input = node.inputs[1]; | |
| 228 if (input.isString(types) | |
| 229 && node.selector.name == const SourceString('toString')) { | |
| 230 return node.inputs[1]; | |
| 231 } | |
| 232 | 263 |
| 233 // Try constant folding the instruction. | 264 // Try constant folding the instruction. |
| 234 Operation operation = node.specializer.operation(constantSystem); | 265 Operation operation = node.specializer.operation(constantSystem); |
| 235 if (operation != null) { | 266 if (operation != null) { |
| 236 HInstruction instruction = node.inputs.length == 2 | 267 HInstruction instruction = node.inputs.length == 2 |
| 237 ? foldUnary(operation, node.inputs[1]) | 268 ? foldUnary(operation, node.inputs[1]) |
| 238 : foldBinary(operation, node.inputs[1], node.inputs[2]); | 269 : foldBinary(operation, node.inputs[1], node.inputs[2]); |
| 239 if (instruction != null) return instruction; | 270 if (instruction != null) return instruction; |
| 240 } | 271 } |
| 241 | 272 |
| 242 // Try converting the instruction to a builtin instruction. | 273 // Try converting the instruction to a builtin instruction. |
| 243 HInstruction instruction = | 274 HInstruction instruction = |
| 244 node.specializer.tryConvertToBuiltin(node, types); | 275 node.specializer.tryConvertToBuiltin(node, types); |
| 245 if (instruction != null) return instruction; | 276 if (instruction != null) return instruction; |
| 246 | 277 |
| 247 // Check if this call does not need to be intercepted. | 278 // Check if this call does not need to be intercepted. |
| 279 HInstruction input = node.inputs[1]; | |
| 248 HType type = types[input]; | 280 HType type = types[input]; |
| 249 var interceptor = node.inputs[0]; | 281 var interceptor = node.inputs[0]; |
| 250 if (interceptor is !HThis && !type.canBePrimitive()) { | 282 if (interceptor is !HThis && !type.canBePrimitive()) { |
| 251 // If the type can be null, and the intercepted method can be in | 283 // If the type can be null, and the intercepted method can be in |
| 252 // the object class, keep the interceptor. | 284 // the object class, keep the interceptor. |
| 253 if (type.canBeNull()) { | 285 if (type.canBeNull()) { |
| 254 Set<ClassElement> interceptedClasses; | 286 Set<ClassElement> interceptedClasses; |
| 255 if (interceptor is HInterceptor) { | 287 if (interceptor is HInterceptor) { |
| 256 interceptedClasses = interceptor.interceptedClasses; | 288 interceptedClasses = interceptor.interceptedClasses; |
| 257 } else if (node is HOneShotInterceptor) { | 289 } else if (node is HOneShotInterceptor) { |
| 258 var oneShotInterceptor = node; | 290 var oneShotInterceptor = node; |
| 259 interceptedClasses = oneShotInterceptor.interceptedClasses; | 291 interceptedClasses = oneShotInterceptor.interceptedClasses; |
| 260 } | 292 } |
| 261 if (interceptedClasses.contains(compiler.objectClass)) return node; | 293 if (interceptedClasses.contains(compiler.objectClass)) return node; |
| 262 } | 294 } |
| 263 // Change the call to a regular invoke dynamic call. | 295 if (selector.isGetter()) { |
| 264 return new HInvokeDynamicMethod( | 296 // Change the call to a regular invoke dynamic call. |
| 265 node.selector, node.inputs.getRange(1, node.inputs.length - 1)); | 297 return new HInvokeDynamicGetter(selector, null, input, false); |
| 298 } else if (selector.isSetter()) { | |
| 299 return new HInvokeDynamicSetter( | |
| 300 selector, null, input, node.inputs[2], false); | |
| 301 } else { | |
| 302 // Change the call to a regular invoke dynamic call. | |
| 303 return new HInvokeDynamicMethod( | |
| 304 selector, node.inputs.getRange(1, node.inputs.length - 1)); | |
| 305 } | |
| 266 } | 306 } |
| 267 | 307 |
| 268 Selector selector = node.selector; | 308 if (selector.isCall()) { |
| 269 SourceString selectorName = selector.name; | 309 Element target; |
| 270 Element target; | 310 if (input.isExtendableArray(types)) { |
| 271 if (input.isExtendableArray(types)) { | 311 if (selector.applies(backend.jsArrayRemoveLast, compiler)) { |
| 272 if (selectorName == backend.jsArrayRemoveLast.name | 312 target = backend.jsArrayRemoveLast; |
| 273 && selector.argumentCount == 0) { | 313 } else if (selector.applies(backend.jsArrayAdd, compiler)) { |
| 274 target = backend.jsArrayRemoveLast; | 314 // The codegen special cases array calls, but does not |
| 275 } else if (selectorName == backend.jsArrayAdd.name | 315 // inlined argument type checks. |
|
kasperl
2013/02/04 13:39:03
but does not inlined -> but do not inline ?
ngeoffray
2013/02/04 14:51:07
Done.
| |
| 276 && selector.argumentCount == 1 | 316 if (!compiler.enableTypeAssertions) { |
| 277 && selector.namedArgumentCount == 0 | 317 target = backend.jsArrayAdd; |
| 278 && !compiler.enableTypeAssertions) { | 318 } |
| 279 target = backend.jsArrayAdd; | 319 } |
| 320 } else if (input.isString(types)) { | |
| 321 if (selector.applies(backend.jsStringSplit, compiler)) { | |
| 322 if (node.inputs[2].isString(types)) { | |
| 323 target = backend.jsStringSplit; | |
| 324 } | |
| 325 } else if (selector.applies(backend.jsStringConcat, compiler)) { | |
| 326 if (node.inputs[2].isString(types)) { | |
| 327 target = backend.jsStringConcat; | |
| 328 } | |
| 329 } else if (selector.applies(backend.jsStringToString, compiler)) { | |
| 330 return node.inputs[1]; | |
|
kasperl
2013/02/04 13:39:03
node.inputs[1] -> input
ngeoffray
2013/02/04 14:51:07
Done.
| |
| 331 } | |
| 280 } | 332 } |
| 281 } else if (input.isString(types)) { | 333 if (target != null) { |
| 282 if (selectorName == backend.jsStringSplit.name | 334 HInvokeDynamicMethod result = new HInvokeDynamicMethod( |
| 283 && selector.argumentCount == 1 | 335 node.selector, node.inputs.getRange(1, node.inputs.length - 1)); |
| 284 && selector.namedArgumentCount == 0 | 336 result.element = target; |
| 285 && node.inputs[2].isString(types)) { | 337 return result; |
| 286 target = backend.jsStringSplit; | |
| 287 } else if (selectorName == backend.jsStringConcat.name | |
| 288 && selector.argumentCount == 1 | |
| 289 && selector.namedArgumentCount == 0 | |
| 290 && node.inputs[2].isString(types)) { | |
| 291 target = backend.jsStringConcat; | |
| 292 } | 338 } |
| 293 } | 339 } else if (selector.isGetter()) { |
| 294 if (target != null) { | 340 if (selector.applies(backend.jsArrayLength, compiler)) { |
| 295 HInvokeDynamicMethod result = new HInvokeDynamicMethod( | 341 return optimizeLengthInterceptedGetter(node); |
| 296 node.selector, node.inputs.getRange(1, node.inputs.length - 1)); | 342 } |
| 297 result.element = target; | |
| 298 return result; | |
| 299 } | 343 } |
| 300 return node; | 344 return node; |
| 301 } | 345 } |
| 302 | 346 |
| 303 bool isFixedSizeListConstructor(HInvokeStatic node) { | 347 bool isFixedSizeListConstructor(HInvokeStatic node) { |
| 304 Element element = node.target.element; | 348 Element element = node.target.element; |
| 305 if (backend.fixedLengthListConstructor == null) { | 349 if (backend.fixedLengthListConstructor == null) { |
| 306 backend.fixedLengthListConstructor = | 350 backend.fixedLengthListConstructor = |
| 307 compiler.listClass.lookupConstructor( | 351 compiler.listClass.lookupConstructor( |
| 308 new Selector.callConstructor(const SourceString("fixedLength"), | 352 new Selector.callConstructor(const SourceString("fixedLength"), |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 338 node.element = element; | 382 node.element = element; |
| 339 } | 383 } |
| 340 // TODO(ngeoffray): If the method has optional parameters, | 384 // TODO(ngeoffray): If the method has optional parameters, |
| 341 // we should pass the default values here. | 385 // we should pass the default values here. |
| 342 } | 386 } |
| 343 } | 387 } |
| 344 } | 388 } |
| 345 return node; | 389 return node; |
| 346 } | 390 } |
| 347 | 391 |
| 348 /** | |
| 349 * Turns a primitive instruction (e.g. [HIndex], [HAdd], ...) into a | |
| 350 * [HInvokeDynamic] because we know the receiver is not a JS | |
| 351 * primitive object. | |
| 352 */ | |
| 353 HInstruction fromPrimitiveInstructionToDynamicInvocation(HInstruction node, | |
| 354 Selector selector) { | |
| 355 HBoundedType type = types[node.inputs[1]]; | |
| 356 HInvokeDynamicMethod result = new HInvokeDynamicMethod( | |
| 357 selector, | |
| 358 node.inputs.getRange(1, node.inputs.length - 1)); | |
| 359 if (type.isExact()) { | |
| 360 HBoundedType concrete = type; | |
| 361 // TODO(johnniwinther): Add lookup by selector to HBoundedType. | |
| 362 Element element = concrete.lookupMember(selector.name); | |
| 363 if (selector.applies(element, compiler)) { | |
| 364 // The target is only valid if the selector applies. | |
| 365 result.element = element; | |
| 366 } | |
| 367 } | |
| 368 return result; | |
| 369 } | |
| 370 | |
| 371 HInstruction visitIntegerCheck(HIntegerCheck node) { | 392 HInstruction visitIntegerCheck(HIntegerCheck node) { |
| 372 HInstruction value = node.value; | 393 HInstruction value = node.value; |
| 373 if (value.isInteger(types)) return value; | 394 if (value.isInteger(types)) return value; |
| 374 if (value.isConstant()) { | 395 if (value.isConstant()) { |
| 375 HConstant constantInstruction = value; | 396 HConstant constantInstruction = value; |
| 376 assert(!constantInstruction.constant.isInt()); | 397 assert(!constantInstruction.constant.isInt()); |
| 377 if (!constantSystem.isInt(constantInstruction.constant)) { | 398 if (!constantSystem.isInt(constantInstruction.constant)) { |
| 378 // -0.0 is a double but will pass the runtime integer check. | 399 // -0.0 is a double but will pass the runtime integer check. |
| 379 node.alwaysFalse = true; | 400 node.alwaysFalse = true; |
| 380 } | 401 } |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 577 // [:new List.fixedLength(int):]. | 598 // [:new List.fixedLength(int):]. |
| 578 HInvokeStatic call = node.receiver; | 599 HInvokeStatic call = node.receiver; |
| 579 if (isFixedSizeListConstructor(call)) { | 600 if (isFixedSizeListConstructor(call)) { |
| 580 return call.inputs[1]; | 601 return call.inputs[1]; |
| 581 } | 602 } |
| 582 } | 603 } |
| 583 } | 604 } |
| 584 return node; | 605 return node; |
| 585 } | 606 } |
| 586 | 607 |
| 587 HInstruction optimizeLengthInterceptedCall(HInvokeDynamicGetter node) { | |
| 588 HInstruction actualReceiver = node.inputs[1]; | |
| 589 if (actualReceiver.isIndexablePrimitive(types)) { | |
| 590 if (actualReceiver.isConstantString()) { | |
| 591 HConstant constantInput = actualReceiver; | |
| 592 StringConstant constant = constantInput.constant; | |
| 593 return graph.addConstantInt(constant.length, constantSystem); | |
| 594 } else if (actualReceiver.isConstantList()) { | |
| 595 HConstant constantInput = actualReceiver; | |
| 596 ListConstant constant = constantInput.constant; | |
| 597 return graph.addConstantInt(constant.length, constantSystem); | |
| 598 } | |
| 599 Element element; | |
| 600 bool isAssignable; | |
| 601 if (actualReceiver.isString(types)) { | |
| 602 element = backend.jsStringLength; | |
| 603 isAssignable = false; | |
| 604 } else { | |
| 605 element = backend.jsArrayLength; | |
| 606 isAssignable = !actualReceiver.isFixedArray(types); | |
| 607 } | |
| 608 HFieldGet result = new HFieldGet( | |
| 609 element, actualReceiver, isAssignable: isAssignable); | |
| 610 result.guaranteedType = HType.INTEGER; | |
| 611 types[result] = HType.INTEGER; | |
| 612 return result; | |
| 613 } else if (actualReceiver.isConstantMap()) { | |
| 614 HConstant constantInput = actualReceiver; | |
| 615 MapConstant constant = constantInput.constant; | |
| 616 return graph.addConstantInt(constant.length, constantSystem); | |
| 617 } | |
| 618 return node; | |
| 619 } | |
| 620 | |
| 621 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { | 608 HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { |
| 622 if (node.selector.name == const SourceString('length') | 609 if (node.isInterceptorCall) return handleInterceptorCall(node); |
| 623 && node.isInterceptorCall) { | |
| 624 return optimizeLengthInterceptedCall(node); | |
| 625 } | |
| 626 | 610 |
| 627 Element field = | 611 Element field = |
| 628 findConcreteFieldForDynamicAccess(node.receiver, node.selector); | 612 findConcreteFieldForDynamicAccess(node.receiver, node.selector); |
| 629 if (field == null) return node; | 613 if (field == null) return node; |
| 630 | 614 |
| 631 Modifiers modifiers = field.modifiers; | 615 Modifiers modifiers = field.modifiers; |
| 632 bool isFinalOrConst = modifiers.isFinal() || modifiers.isConst(); | 616 bool isFinalOrConst = modifiers.isFinal() || modifiers.isConst(); |
| 633 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) { | 617 if (!compiler.resolverWorld.hasInvokedSetter(field, compiler)) { |
| 634 // If no setter is ever used for this field it is only initialized in the | 618 // If no setter is ever used for this field it is only initialized in the |
| 635 // initializer list. | 619 // initializer list. |
| 636 isFinalOrConst = true; | 620 isFinalOrConst = true; |
| 637 } | 621 } |
| 638 HFieldGet result = new HFieldGet( | 622 HFieldGet result = new HFieldGet( |
| 639 field, node.inputs[0], isAssignable: !isFinalOrConst); | 623 field, node.inputs[0], isAssignable: !isFinalOrConst); |
| 640 HType type = backend.optimisticFieldType(field); | 624 HType type = backend.optimisticFieldType(field); |
| 641 if (type != null) { | 625 if (type != null) { |
| 642 result.guaranteedType = type; | 626 result.guaranteedType = type; |
| 643 backend.registerFieldTypesOptimization( | 627 backend.registerFieldTypesOptimization( |
| 644 work.element, field, result.guaranteedType); | 628 work.element, field, result.guaranteedType); |
| 645 } | 629 } |
| 646 return result; | 630 return result; |
| 647 } | 631 } |
| 648 | 632 |
| 649 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { | 633 HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { |
| 634 if (node.isInterceptorCall) return handleInterceptorCall(node); | |
| 635 | |
| 650 Element field = | 636 Element field = |
| 651 findConcreteFieldForDynamicAccess(node.receiver, node.selector); | 637 findConcreteFieldForDynamicAccess(node.receiver, node.selector); |
| 652 if (field == null || !field.isAssignable()) return node; | 638 if (field == null || !field.isAssignable()) return node; |
| 653 HInstruction value = node.inputs[1]; | 639 HInstruction value = node.inputs[1]; |
| 654 if (compiler.enableTypeAssertions) { | 640 if (compiler.enableTypeAssertions) { |
| 655 HInstruction other = value.convertType( | 641 HInstruction other = value.convertType( |
| 656 compiler, | 642 compiler, |
| 657 field.computeType(compiler), | 643 field.computeType(compiler), |
| 658 HTypeConversion.CHECKED_MODE_CHECK); | 644 HTypeConversion.CHECKED_MODE_CHECK); |
| 659 if (other != value) { | 645 if (other != value) { |
| (...skipping 874 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1534 HBasicBlock block = user.block; | 1520 HBasicBlock block = user.block; |
| 1535 block.addAfter(user, interceptor); | 1521 block.addAfter(user, interceptor); |
| 1536 block.rewrite(user, interceptor); | 1522 block.rewrite(user, interceptor); |
| 1537 block.remove(user); | 1523 block.remove(user); |
| 1538 | 1524 |
| 1539 // The interceptor will be removed in the dead code elimination | 1525 // The interceptor will be removed in the dead code elimination |
| 1540 // phase. Note that removing it here would not work because of how | 1526 // phase. Note that removing it here would not work because of how |
| 1541 // the [visitBasicBlock] is implemented. | 1527 // the [visitBasicBlock] is implemented. |
| 1542 } | 1528 } |
| 1543 } | 1529 } |
| OLD | NEW |