| 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 23 matching lines...) Expand all Loading... |
| 34 JavaScriptItemCompilationContext context = work.compilationContext; | 34 JavaScriptItemCompilationContext context = work.compilationContext; |
| 35 HTypeMap types = context.types; | 35 HTypeMap types = context.types; |
| 36 measure(() { | 36 measure(() { |
| 37 List<OptimizationPhase> phases = <OptimizationPhase>[ | 37 List<OptimizationPhase> phases = <OptimizationPhase>[ |
| 38 // Run trivial constant folding first to optimize | 38 // Run trivial constant folding first to optimize |
| 39 // some patterns useful for type conversion. | 39 // some patterns useful for type conversion. |
| 40 new SsaConstantFolder(constantSystem, backend, work, types), | 40 new SsaConstantFolder(constantSystem, backend, work, types), |
| 41 new SsaTypeConversionInserter(compiler), | 41 new SsaTypeConversionInserter(compiler), |
| 42 new SsaTypePropagator(compiler, types), | 42 new SsaTypePropagator(compiler, types), |
| 43 new SsaConstantFolder(constantSystem, backend, work, types), | 43 new SsaConstantFolder(constantSystem, backend, work, types), |
| 44 // The constant folder affects the types of instructions, so |
| 45 // we run the type propagator again. Note that this would |
| 46 // not be necessary if types were directly stored on |
| 47 // instructions. |
| 48 new SsaTypePropagator(compiler, types), |
| 44 new SsaCheckInserter(backend, work, types, context.boundsChecked), | 49 new SsaCheckInserter(backend, work, types, context.boundsChecked), |
| 45 new SsaRedundantPhiEliminator(), | 50 new SsaRedundantPhiEliminator(), |
| 46 new SsaDeadPhiEliminator(), | 51 new SsaDeadPhiEliminator(), |
| 47 new SsaConstantFolder(constantSystem, backend, work, types), | 52 new SsaConstantFolder(constantSystem, backend, work, types), |
| 48 new SsaTypePropagator(compiler, types), | 53 new SsaTypePropagator(compiler, types), |
| 49 new SsaReceiverSpecialization(compiler), | 54 new SsaReceiverSpecialization(compiler), |
| 50 new SsaGlobalValueNumberer(compiler, types), | 55 new SsaGlobalValueNumberer(compiler, types), |
| 51 new SsaCodeMotion(), | 56 new SsaCodeMotion(), |
| 52 new SsaValueRangeAnalyzer(constantSystem, types, work), | 57 new SsaValueRangeAnalyzer(constantSystem, types, work), |
| 53 // Previous optimizations may have generated new | 58 // Previous optimizations may have generated new |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 213 } | 218 } |
| 214 return null; | 219 return null; |
| 215 } | 220 } |
| 216 | 221 |
| 217 HInstruction handleInterceptorCall(HInvokeDynamicMethod node) { | 222 HInstruction handleInterceptorCall(HInvokeDynamicMethod node) { |
| 218 HInstruction input = node.inputs[1]; | 223 HInstruction input = node.inputs[1]; |
| 219 if (input.isString(types) | 224 if (input.isString(types) |
| 220 && node.selector.name == const SourceString('toString')) { | 225 && node.selector.name == const SourceString('toString')) { |
| 221 return node.inputs[1]; | 226 return node.inputs[1]; |
| 222 } | 227 } |
| 223 // Check if this call does not need to be intercepted. | |
| 224 HType type = types[input]; | |
| 225 var interceptor = node.inputs[0]; | |
| 226 if (interceptor is !HThis && !type.canBePrimitive()) { | |
| 227 // If the type can be null, and the intercepted method can be in | |
| 228 // the object class, keep the interceptor. | |
| 229 if (type.canBeNull() | |
| 230 && interceptor.interceptedClasses.contains(compiler.objectClass)) { | |
| 231 return node; | |
| 232 } | |
| 233 // Change the call to a regular invoke dynamic call. | |
| 234 return new HInvokeDynamicMethod( | |
| 235 node.selector, node.inputs.getRange(1, node.inputs.length - 1)); | |
| 236 } | |
| 237 | 228 |
| 238 // Try constant folding the instruction. | 229 // Try constant folding the instruction. |
| 239 Operation operation = node.specializer.operation(constantSystem); | 230 Operation operation = node.specializer.operation(constantSystem); |
| 240 if (operation != null) { | 231 if (operation != null) { |
| 241 HInstruction instruction = node.inputs.length == 2 | 232 HInstruction instruction = node.inputs.length == 2 |
| 242 ? foldUnary(operation, node.inputs[1]) | 233 ? foldUnary(operation, node.inputs[1]) |
| 243 : foldBinary(operation, node.inputs[1], node.inputs[2]); | 234 : foldBinary(operation, node.inputs[1], node.inputs[2]); |
| 244 if (instruction != null) return instruction; | 235 if (instruction != null) return instruction; |
| 245 } | 236 } |
| 246 | 237 |
| 247 // Try converting the instruction to a builtin instruction. | 238 // Try converting the instruction to a builtin instruction. |
| 248 HInstruction instruction = | 239 HInstruction instruction = |
| 249 node.specializer.tryConvertToBuiltin(node, types); | 240 node.specializer.tryConvertToBuiltin(node, types); |
| 250 if (instruction != null) return instruction; | 241 if (instruction != null) return instruction; |
| 251 | 242 |
| 243 // Check if this call does not need to be intercepted. |
| 244 HType type = types[input]; |
| 245 var interceptor = node.inputs[0]; |
| 246 if (interceptor is !HThis && !type.canBePrimitive()) { |
| 247 // If the type can be null, and the intercepted method can be in |
| 248 // the object class, keep the interceptor. |
| 249 if (type.canBeNull() |
| 250 && interceptor.interceptedClasses.contains(compiler.objectClass)) { |
| 251 return node; |
| 252 } |
| 253 // Change the call to a regular invoke dynamic call. |
| 254 return new HInvokeDynamicMethod( |
| 255 node.selector, node.inputs.getRange(1, node.inputs.length - 1)); |
| 256 } |
| 257 |
| 252 Selector selector = node.selector; | 258 Selector selector = node.selector; |
| 253 SourceString selectorName = selector.name; | 259 SourceString selectorName = selector.name; |
| 254 Element target; | 260 Element target; |
| 255 if (input.isExtendableArray(types)) { | 261 if (input.isExtendableArray(types)) { |
| 256 if (selectorName == backend.jsArrayRemoveLast.name | 262 if (selectorName == backend.jsArrayRemoveLast.name |
| 257 && selector.argumentCount == 0) { | 263 && selector.argumentCount == 0) { |
| 258 target = backend.jsArrayRemoveLast; | 264 target = backend.jsArrayRemoveLast; |
| 259 } else if (selectorName == backend.jsArrayAdd.name | 265 } else if (selectorName == backend.jsArrayAdd.name |
| 260 && selector.argumentCount == 1 | 266 && selector.argumentCount == 1 |
| 261 && selector.namedArgumentCount == 0 | 267 && selector.namedArgumentCount == 0 |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 377 } | 383 } |
| 378 return null; | 384 return null; |
| 379 } | 385 } |
| 380 | 386 |
| 381 HInstruction visitInvokeBinary(HInvokeBinary node) { | 387 HInstruction visitInvokeBinary(HInvokeBinary node) { |
| 382 HInstruction left = node.left; | 388 HInstruction left = node.left; |
| 383 HInstruction right = node.right; | 389 HInstruction right = node.right; |
| 384 BinaryOperation operation = node.operation(constantSystem); | 390 BinaryOperation operation = node.operation(constantSystem); |
| 385 HConstant folded = foldBinary(operation, left, right); | 391 HConstant folded = foldBinary(operation, left, right); |
| 386 if (folded != null) return folded; | 392 if (folded != null) return folded; |
| 387 | |
| 388 if (!left.canBePrimitive(types) | |
| 389 && operation.isUserDefinable() | |
| 390 // The equals operation is being optimized in visitEquals. | |
| 391 && node is! HEquals) { | |
| 392 Selector selector = new Selector.binaryOperator(operation.name); | |
| 393 return fromPrimitiveInstructionToDynamicInvocation(node, selector); | |
| 394 } | |
| 395 return node; | 393 return node; |
| 396 } | 394 } |
| 397 | 395 |
| 398 bool allUsersAreBoolifies(HInstruction instruction) { | 396 bool allUsersAreBoolifies(HInstruction instruction) { |
| 399 List<HInstruction> users = instruction.usedBy; | 397 List<HInstruction> users = instruction.usedBy; |
| 400 int length = users.length; | 398 int length = users.length; |
| 401 for (int i = 0; i < length; i++) { | 399 for (int i = 0; i < length; i++) { |
| 402 if (users[i] is! HBoolify) return false; | 400 if (users[i] is! HBoolify) return false; |
| 403 } | 401 } |
| 404 return true; | 402 return true; |
| 405 } | 403 } |
| 406 | 404 |
| 407 HInstruction visitRelational(HRelational node) { | 405 HInstruction visitRelational(HRelational node) { |
| 408 if (allUsersAreBoolifies(node)) { | 406 if (allUsersAreBoolifies(node)) { |
| 409 Interceptors interceptors = backend.builder.interceptors; | 407 Interceptors interceptors = backend.builder.interceptors; |
| 410 HStatic oldTarget = node.target; | |
| 411 Element boolifiedInterceptor = | |
| 412 interceptors.getBoolifiedVersionOf(oldTarget.element); | |
| 413 if (boolifiedInterceptor != null) { | |
| 414 HStatic boolifiedTarget = new HStatic(boolifiedInterceptor); | |
| 415 // We don't remove the [oldTarget] in case it is used by other | |
| 416 // instructions. If it is unused it will be treated as dead code and | |
| 417 // discarded. | |
| 418 oldTarget.block.addAfter(oldTarget, boolifiedTarget); | |
| 419 // Remove us as user from the [oldTarget]. | |
| 420 oldTarget.removeUser(node); | |
| 421 // Replace old target with boolified target. | |
| 422 assert(node.target == node.inputs[0]); | |
| 423 node.inputs[0] = boolifiedTarget; | |
| 424 boolifiedTarget.usedBy.add(node); | |
| 425 node.usesBoolifiedInterceptor = true; | |
| 426 types[node] = HType.BOOLEAN; | |
| 427 } | |
| 428 // This node stays the same, but the Boolify node will go away. | 408 // This node stays the same, but the Boolify node will go away. |
| 429 } | 409 } |
| 430 // Note that we still have to call [super] to make sure that we end up | 410 // Note that we still have to call [super] to make sure that we end up |
| 431 // in the remaining optimizations. | 411 // in the remaining optimizations. |
| 432 return super.visitRelational(node); | 412 return super.visitRelational(node); |
| 433 } | 413 } |
| 434 | 414 |
| 435 HInstruction handleIdentityCheck(HRelational node) { | 415 HInstruction handleIdentityCheck(HRelational node) { |
| 436 HInstruction left = node.left; | 416 HInstruction left = node.left; |
| 437 HInstruction right = node.right; | 417 HInstruction right = node.right; |
| (...skipping 25 matching lines...) Expand all Loading... |
| 463 } | 443 } |
| 464 | 444 |
| 465 return null; | 445 return null; |
| 466 } | 446 } |
| 467 | 447 |
| 468 HInstruction visitIdentity(HIdentity node) { | 448 HInstruction visitIdentity(HIdentity node) { |
| 469 HInstruction newInstruction = handleIdentityCheck(node); | 449 HInstruction newInstruction = handleIdentityCheck(node); |
| 470 return newInstruction == null ? super.visitIdentity(node) : newInstruction; | 450 return newInstruction == null ? super.visitIdentity(node) : newInstruction; |
| 471 } | 451 } |
| 472 | 452 |
| 473 HInstruction foldBuiltinEqualsCheck(HEquals node) { | |
| 474 // TODO(floitsch): cache interceptors. | |
| 475 HInstruction newInstruction = handleIdentityCheck(node); | |
| 476 if (newInstruction == null) { | |
| 477 HStatic target = new HStatic( | |
| 478 backend.builder.interceptors.getTripleEqualsInterceptor()); | |
| 479 node.block.addBefore(node, target); | |
| 480 return new HIdentity(target, node.left, node.right); | |
| 481 } else { | |
| 482 return newInstruction; | |
| 483 } | |
| 484 } | |
| 485 | |
| 486 HInstruction visitEquals(HEquals node) { | |
| 487 HInstruction left = node.left; | |
| 488 HInstruction right = node.right; | |
| 489 | |
| 490 if (node.isBuiltin(types)) { | |
| 491 return foldBuiltinEqualsCheck(node); | |
| 492 } | |
| 493 | |
| 494 if (left.isConstant() && right.isConstant()) { | |
| 495 return super.visitEquals(node); | |
| 496 } | |
| 497 | |
| 498 HType leftType = types[left]; | |
| 499 if (leftType.isExact()) { | |
| 500 HBoundedType type = leftType; | |
| 501 Element element = type.lookupMember(const SourceString('==')); | |
| 502 if (element != null) { | |
| 503 // If the left-hand side is guaranteed to be a non-primitive | |
| 504 // type and and it defines operator==, we emit a call to that | |
| 505 // operator. | |
| 506 return super.visitEquals(node); | |
| 507 } else if (right.isConstantNull()) { | |
| 508 return graph.addConstantBool(false, constantSystem); | |
| 509 } else { | |
| 510 // We can just emit an identity check because the type does | |
| 511 // not implement operator=. | |
| 512 return foldBuiltinEqualsCheck(node); | |
| 513 } | |
| 514 } | |
| 515 | |
| 516 if (right.isConstantNull()) { | |
| 517 if (leftType.isPrimitive()) { | |
| 518 return graph.addConstantBool(false, constantSystem); | |
| 519 } | |
| 520 } | |
| 521 | |
| 522 // All other cases are dealt with by the [visitRelational] and | |
| 523 // [visitInvokeBinary], which are visited by invoking the [super]'s | |
| 524 // visit method. | |
| 525 return super.visitEquals(node); | |
| 526 } | |
| 527 | |
| 528 HInstruction visitTypeGuard(HTypeGuard node) { | 453 HInstruction visitTypeGuard(HTypeGuard node) { |
| 529 HInstruction value = node.guarded; | 454 HInstruction value = node.guarded; |
| 530 // If the intersection of the types is still the incoming type then | 455 // If the intersection of the types is still the incoming type then |
| 531 // the incoming type was a subtype of the guarded type, and no check | 456 // the incoming type was a subtype of the guarded type, and no check |
| 532 // is required. | 457 // is required. |
| 533 HType combinedType = types[value].intersection(node.guardedType, compiler); | 458 HType combinedType = types[value].intersection(node.guardedType, compiler); |
| 534 return (combinedType == types[value]) ? value : node; | 459 return (combinedType == types[value]) ? value : node; |
| 535 } | 460 } |
| 536 | 461 |
| 537 HInstruction visitIs(HIs node) { | 462 HInstruction visitIs(HIs node) { |
| (...skipping 978 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1516 || otherIntercepted.contains(backend.jsDoubleClass)) { | 1441 || otherIntercepted.contains(backend.jsDoubleClass)) { |
| 1517 interceptor.interceptedClasses.addAll(user.interceptedClasses); | 1442 interceptor.interceptedClasses.addAll(user.interceptedClasses); |
| 1518 } | 1443 } |
| 1519 user.interceptedClasses = interceptor.interceptedClasses; | 1444 user.interceptedClasses = interceptor.interceptedClasses; |
| 1520 } | 1445 } |
| 1521 } | 1446 } |
| 1522 } | 1447 } |
| 1523 | 1448 |
| 1524 // TODO(ngeoffray): Also implement it for non-intercepted calls. | 1449 // TODO(ngeoffray): Also implement it for non-intercepted calls. |
| 1525 } | 1450 } |
| OLD | NEW |