From bbca76a5e2df6c5adf0ee9327f3a7af21fb1c19a Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 9 Feb 2016 13:34:37 +0100 Subject: [PATCH] dart2js cps: More aggressive operator specialization. Inline more unsafe operator calls, and insert more checks accordingly. When "num" is the only receiver that does not respond with NSM, insert a check and use the builtin operator. Previously we only did this when the receiver was a number or null. For example: if (typeof x !== "number") return x.$lt(); result = x < 4; When the argument might have the wrong type, insert an argument check and use the builtin operator. Previously we never did this if the argument might have the wrong type. For example: if (typeof x !== "number") return H.iae(x); result = 4 < x; If both receiver and argument need checks, we join the two checks and use a one-shot interceptor to throw the error: if (typeof x !== "number" || typeof y !== "number") return J.$lt(x, y); result = x < y; NullCheck has been generalized to a ReceiverCheck instruction to support "x.$lt()" above, that throws even if x is a non-null value. BUG= R=sigmund@google.com Review URL: https://codereview.chromium.org/1668913002 . --- .../cps_ir/backward_null_check_remover.dart | 8 +- pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart | 138 +++++--- .../lib/src/cps_ir/cps_ir_nodes_sexpr.dart | 5 +- .../lib/src/cps_ir/cps_ir_tracer.dart | 7 +- pkg/compiler/lib/src/cps_ir/gvn.dart | 2 +- pkg/compiler/lib/src/cps_ir/inline.dart | 33 +- .../lib/src/cps_ir/redundant_join.dart | 2 +- .../lib/src/cps_ir/type_propagation.dart | 297 ++++++++++++++---- .../lib/src/cps_ir/update_refinements.dart | 13 +- .../lib/src/js_backend/codegen/codegen.dart | 7 +- .../optimization/pull_into_initializers.dart | 2 +- .../optimization/statement_rewriter.dart | 2 +- .../lib/src/tree_ir/tree_ir_builder.dart | 8 +- .../lib/src/tree_ir/tree_ir_nodes.dart | 19 +- .../lib/src/tree_ir/tree_ir_tracer.dart | 4 +- .../cps_ir/expected/argument_refinement_1.js | 4 +- .../cps_ir/expected/argument_refinement_10.js | 4 +- .../cps_ir/expected/argument_refinement_11.js | 4 +- .../cps_ir/expected/argument_refinement_12.js | 4 +- .../cps_ir/expected/argument_refinement_13.js | 4 +- .../cps_ir/expected/argument_refinement_19.js | 12 +- .../cps_ir/expected/argument_refinement_2.js | 4 +- .../cps_ir/expected/argument_refinement_20.js | 12 +- .../cps_ir/expected/argument_refinement_21.js | 7 +- .../cps_ir/expected/argument_refinement_7.js | 4 +- .../cps_ir/expected/argument_refinement_8.js | 4 +- .../cps_ir/expected/argument_refinement_9.js | 4 +- .../expected/argument_refinement_num_1.js | 4 +- .../expected/argument_refinement_num_10.js | 4 +- .../expected/argument_refinement_num_11.js | 4 +- .../expected/argument_refinement_num_12.js | 4 +- .../expected/argument_refinement_num_13.js | 4 +- .../expected/argument_refinement_num_2.js | 4 +- .../expected/argument_refinement_num_7.js | 4 +- .../expected/argument_refinement_num_8.js | 4 +- .../expected/argument_refinement_num_9.js | 4 +- .../dart2js/cps_ir/expected/operators2_3.js | 8 +- .../dart2js/cps_ir/expected/operators2_6.js | 7 +- .../dart2js/cps_ir/expected/operators2_7.js | 5 +- .../dart2js/cps_ir/expected/operators2_8.js | 7 +- .../dart2js/cps_ir/input/operators2_3.dart | 5 +- .../dart2js/cps_ir/input/operators2_6.dart | 5 +- .../dart2js/cps_ir/input/operators2_7.dart | 5 +- .../dart2js/cps_ir/input/operators2_8.dart | 5 +- 44 files changed, 516 insertions(+), 181 deletions(-) diff --git a/pkg/compiler/lib/src/cps_ir/backward_null_check_remover.dart b/pkg/compiler/lib/src/cps_ir/backward_null_check_remover.dart index b55a433ffa2..6a2c16fac20 100644 --- a/pkg/compiler/lib/src/cps_ir/backward_null_check_remover.dart +++ b/pkg/compiler/lib/src/cps_ir/backward_null_check_remover.dart @@ -19,11 +19,11 @@ import 'cps_fragment.dart'; /// /// print(x.length); /// -/// `x.length` will throw when x is null, so the original [NullCheck] is not +/// `x.length` will throw when x is null, so the original [ReceiverCheck] is not /// needed. This changes the error message, but at least for now we are /// willing to accept this. /// -/// Note that code motion may not occur after this pass, since the [NullCheck] +/// Note that code motion may not occur after this pass, since the [ReceiverCheck] /// nodes are not there to restrict it. // // TODO(asgerf): It would be nice with a clear specification of when we allow @@ -53,7 +53,7 @@ class BackwardNullCheckRemover extends BlockVisitor implements Pass { /// Returns a reference to an operand of [prim], where [prim] throws if null /// is passed into that operand. Reference getNullCheckedOperand(Primitive prim) { - if (prim is NullCheck) return prim.value; + if (prim is ReceiverCheck) return prim.value; if (prim is GetLength) return prim.object; if (prim is GetField) return prim.object; if (prim is GetIndex) return prim.object; @@ -71,7 +71,7 @@ class BackwardNullCheckRemover extends BlockVisitor implements Pass { /// It has been determined that the null check in [prim] made redundant by /// [newNullCheck]. Eliminate [prim] if it is not needed any more. void tryEliminateRedundantNullCheck(Primitive prim, Primitive newNullCheck) { - if (prim is NullCheck) { + if (prim is ReceiverCheck && prim.isNullCheck) { Primitive value = prim.value.definition; LetPrim let = prim.parent; prim..replaceUsesWith(value)..destroy(); diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart index 4395c00995b..7cb92608d54 100644 --- a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart @@ -260,7 +260,7 @@ abstract class Primitive extends Variable { // TODO(johnniwinther): Require source information for all primitives. SourceInformation get sourceInformation => null; - /// If this is a [Refinement], [BoundsCheck] or [NullCheck] node, returns the + /// If this is a [Refinement], [BoundsCheck] or [ReceiverCheck] node, returns the /// value being refined, the indexable object being checked, or the value /// that was checked to be non-null, respectively. /// @@ -805,7 +805,7 @@ class Refinement extends Primitive { class BoundsCheck extends Primitive { final Reference object; Reference index; - Reference length; // FIXME write docs for length + Reference length; int checks; final SourceInformation sourceInformation; @@ -879,53 +879,99 @@ class BoundsCheck extends Primitive { Primitive get effectiveDefinition => object.definition.effectiveDefinition; } -/// Throw an exception if [value] is `null`. +/// Throw a [NoSuchMethodError] if [value] cannot respond to [selector]. /// /// Returns [value] so this can be used to restrict code motion. /// -/// In the simplest form this compiles to `value.toString;`. +/// The check can take one of three forms: /// -/// [selector] holds the selector that is the cause of the null check. This is -/// usually a method that was inlined where [value] the receiver. +/// value.toString; +/// value.selectorName; +/// value.selectorName(); (should only be used if check always fails) /// -/// If [selector] is set and [useSelector] is true, `toString` is replaced with -/// the (possibly minified) invocation name of the selector. This can be -/// shorter and generate a more meaningful error message, but is expensive if -/// [value] is non-null and does not have that property at runtime. +/// The first two forms are used when it is known that only null fails the +/// check. Additionally, the check may be guarded by a [condition], allowing +/// for three more forms: /// -/// If [condition] is set, it is assumed that [condition] is true if and only -/// if [value] is null. The check then compiles to: +/// if (condition) value.toString; (this form is valid but unused) +/// if (condition) value.selectorName; +/// if (condition) value.selectorName(); /// -/// if (condition) value.toString; (or .selector if non-null) +/// The condition must be true if and only if the check should fail. It should +/// ideally be of a form understood by JS engines, e.g. a `typeof` test. /// -/// The latter form is useful when [condition] is a form understood by the JS -/// runtime, such as a `typeof` test. -class NullCheck extends Primitive { +/// If [useSelector] is false, the first form instead becomes `value.toString;`. +/// This form is faster when the value is non-null and the accessed property has +/// been removed by tree shaking. +/// +/// [selector] may not be one of the selectors implemented by the null object. +class ReceiverCheck extends Primitive { final Reference value; final Selector selector; - final bool useSelector; - final Reference condition; final SourceInformation sourceInformation; + final Reference condition; + final int _flags; - NullCheck(Primitive value, this.sourceInformation, - {Primitive condition, - this.selector, - this.useSelector: false}) - : this.value = new Reference(value), - this.condition = - condition == null ? null : new Reference(condition); + static const int _USE_SELECTOR = 1 << 0; + static const int _NULL_CHECK = 1 << 1; - NullCheck.guarded(Primitive condition, Primitive value, this.selector, - this.sourceInformation) - : this.condition = new Reference(condition), - this.value = new Reference(value), - this.useSelector = true; + /// True if the selector name should be used in the check; otherwise + /// `toString` will be used. + bool get useSelector => _flags & _USE_SELECTOR != 0; + + /// True if null is the only possible input that cannot respond to [selector]. + bool get isNullCheck => _flags & _NULL_CHECK != 0; + + + /// Constructor for creating checks in arbitrary configurations. + /// + /// Consider using one of the named constructors instead. + /// + /// [useSelector] and [isNullCheck] are mandatory named arguments. + ReceiverCheck(Primitive value, this.selector, this.sourceInformation, + {Primitive condition, bool useSelector, bool isNullCheck}) + : value = new Reference(value), + condition = _optionalReference(condition), + _flags = (useSelector ? _USE_SELECTOR : 0) | + (isNullCheck ? _NULL_CHECK : 0); + + /// Simplified constructor for building null checks. + /// + /// Null must be the only possible input value that does not respond to + /// [selector]. + ReceiverCheck.nullCheck( + Primitive value, + Selector selector, + SourceInformation sourceInformation, + {Primitive condition}) + : this(value, + selector, + sourceInformation, + condition: condition, + useSelector: condition != null, + isNullCheck: true); + + /// Simplified constructor for building the general check of form: + /// + /// if (condition) value.selectorName(); + /// + ReceiverCheck.generalCheck( + Primitive value, + Selector selector, + SourceInformation sourceInformation, + Primitive condition) + : this(value, + selector, + sourceInformation, + condition: condition, + useSelector: true, + isNullCheck: false); bool get isSafeForElimination => false; bool get isSafeForReordering => false; bool get hasValue => true; - accept(Visitor visitor) => visitor.visitNullCheck(this); + accept(Visitor visitor) => visitor.visitReceiverCheck(this); void setParentPointers() { value.parent = this; @@ -935,6 +981,10 @@ class NullCheck extends Primitive { } Primitive get effectiveDefinition => value.definition.effectiveDefinition; + + String get nullCheckString => isNullCheck ? 'null-check' : 'general-check'; + String get useSelectorString => useSelector ? 'use-selector' : 'no-selector'; + String get flagString => '$nullCheckString $useSelectorString'; } /// An "is" type test. @@ -1930,6 +1980,16 @@ class Yield extends UnsafePrimitive { } } +Reference _reference(Primitive definition) { + return new Reference(definition); +} + +Reference _optionalReference(Primitive definition) { + return definition == null + ? null + : new Reference(definition); +} + List> _referenceList(Iterable definitions) { return definitions.map((e) => new Reference(e)).toList(); } @@ -2079,7 +2139,7 @@ abstract class Visitor implements BlockVisitor { T visitSetIndex(SetIndex node); T visitRefinement(Refinement node); T visitBoundsCheck(BoundsCheck node); - T visitNullCheck(NullCheck node); + T visitReceiverCheck(ReceiverCheck node); T visitForeignCode(ForeignCode node); } @@ -2402,8 +2462,8 @@ class DeepRecursiveVisitor implements Visitor { } } - processNullCheck(NullCheck node) {} - visitNullCheck(NullCheck node) { + processNullCheck(ReceiverCheck node) {} + visitReceiverCheck(ReceiverCheck node) { processNullCheck(node); processReference(node.value); if (node.condition != null) { @@ -2763,11 +2823,13 @@ class DefinitionCopyingVisitor extends Visitor { } } - Definition visitNullCheck(NullCheck node) { - return new NullCheck(getCopy(node.value), node.sourceInformation, + Definition visitReceiverCheck(ReceiverCheck node) { + return new ReceiverCheck(getCopy(node.value), + node.selector, + node.sourceInformation, condition: node.condition == null ? null : getCopy(node.condition), - selector: node.selector, - useSelector: node.useSelector); + useSelector: node.useSelector, + isNullCheck: node.isNullCheck); } Definition visitForeignCode(ForeignCode node) { diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart index 37836bf7717..952f881fcc9 100644 --- a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart @@ -448,10 +448,11 @@ class SExpressionStringifier extends Indentation implements Visitor { return '(BoundsCheck $object $index $length ${node.checkString})'; } - String visitNullCheck(NullCheck node) { + String visitReceiverCheck(ReceiverCheck node) { String value = access(node.value); String condition = optionalAccess(node.condition); - return '(NullCheck $value $condition (${node.selector ?? ""}))'; + return '(ReceiverCheck $value ${node.selector} $condition ' + '${node.flagString}))'; } } diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart index db1592db321..dd69e3e497e 100644 --- a/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart +++ b/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart @@ -393,10 +393,11 @@ class IRTracer extends TracerUtil implements cps_ir.Visitor { return 'BoundsCheck $object $index $length ${node.checkString}'; } - visitNullCheck(cps_ir.NullCheck node) { + visitReceiverCheck(cps_ir.ReceiverCheck node) { String value = formatReference(node.value); String condition = formatReference(node.condition); - return 'NullCheck $value condition:$condition selector:${node.selector}'; + return 'ReceiverCheck $value $condition ${node.selector} ' + '${node.flagString}'; } } @@ -681,7 +682,7 @@ class BlockCollector implements cps_ir.Visitor { unexpectedNode(node); } - visitNullCheck(cps_ir.NullCheck node) { + visitReceiverCheck(cps_ir.ReceiverCheck node) { unexpectedNode(node); } } diff --git a/pkg/compiler/lib/src/cps_ir/gvn.dart b/pkg/compiler/lib/src/cps_ir/gvn.dart index a4a591f3ae2..46f5d936ca7 100644 --- a/pkg/compiler/lib/src/cps_ir/gvn.dart +++ b/pkg/compiler/lib/src/cps_ir/gvn.dart @@ -192,7 +192,7 @@ class GVN extends TrampolineRecursiveVisitor implements Pass { bool isHoistablePrimitive(Primitive prim) { if (prim.isSafeForElimination) return true; - if (prim is NullCheck || + if (prim is ReceiverCheck || prim is BoundsCheck || prim is GetLength || prim is GetField || diff --git a/pkg/compiler/lib/src/cps_ir/inline.dart b/pkg/compiler/lib/src/cps_ir/inline.dart index 554e93e3923..972241e1389 100644 --- a/pkg/compiler/lib/src/cps_ir/inline.dart +++ b/pkg/compiler/lib/src/cps_ir/inline.dart @@ -405,6 +405,14 @@ class InliningVisitor extends TrampolineRecursiveVisitor { return null; } + if (isBlacklisted(target)) return null; + + if (invoke.callingConvention == CallingConvention.OneShotIntercepted) { + // One-shot interceptor calls with a known target are only inserted on + // uncommon code paths, so they should not be inlined. + return null; + } + Reference dartReceiver = invoke.dartReceiverReference; TypeMask abstractReceiver = dartReceiver == null ? null : abstractType(dartReceiver); @@ -512,7 +520,9 @@ class InliningVisitor extends TrampolineRecursiveVisitor { CpsFragment fragment, Primitive dartReceiver, TypeMask abstractReceiver) { - Selector selector = invoke is InvokeMethod ? invoke.selector : null; + if (invoke is! InvokeMethod) return dartReceiver; + InvokeMethod invokeMethod = invoke; + Selector selector = invokeMethod.selector; if (typeSystem.isDefinitelyNum(abstractReceiver, allowNull: true)) { Primitive condition = _fragment.letPrim( new ApplyBuiltinOperator(BuiltinOperator.IsNotNumber, @@ -520,15 +530,16 @@ class InliningVisitor extends TrampolineRecursiveVisitor { invoke.sourceInformation)); condition.type = typeSystem.boolType; Primitive check = _fragment.letPrim( - new NullCheck.guarded( - condition, dartReceiver, selector, invoke.sourceInformation)); + new ReceiverCheck.nullCheck(dartReceiver, selector, + invoke.sourceInformation, + condition: condition)); check.type = abstractReceiver.nonNullable(); return check; } Primitive check = _fragment.letPrim( - new NullCheck(dartReceiver, invoke.sourceInformation, - selector: selector)); + new ReceiverCheck.nullCheck(dartReceiver, selector, + invoke.sourceInformation)); check.type = abstractReceiver.nonNullable(); return check; } @@ -571,4 +582,16 @@ class InliningVisitor extends TrampolineRecursiveVisitor { } return tryInlining(node, node.target, null); } + + bool isBlacklisted(FunctionElement target) { + ClassElement enclosingClass = target.enclosingClass; + if (target.isOperator && + (enclosingClass == backend.helpers.jsNumberClass || + enclosingClass == backend.helpers.jsDoubleClass || + enclosingClass == backend.helpers.jsIntClass)) { + // These should be handled by operator specialization. + return true; + } + return false; + } } diff --git a/pkg/compiler/lib/src/cps_ir/redundant_join.dart b/pkg/compiler/lib/src/cps_ir/redundant_join.dart index 808c1c73eed..f1191ff5e17 100644 --- a/pkg/compiler/lib/src/cps_ir/redundant_join.dart +++ b/pkg/compiler/lib/src/cps_ir/redundant_join.dart @@ -212,7 +212,7 @@ class RedundantJoinEliminator extends TrampolineRecursiveVisitor implements Pass /// After lifting LetConts in the main pass above, parameter objects can have /// multiple bindings. Each reference implicitly refers to the binding that /// is currently in scope. -/// +/// /// This returns the IR to its normal form after redundant joins have been /// eliminated. class AlphaRenamer extends TrampolineRecursiveVisitor { diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart index 6c896e1cf8e..c231aa92830 100644 --- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart +++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart @@ -702,6 +702,17 @@ class ConstantPropagationLattice { return nonConstant(value.type.nonNullable()); } + AbstractConstantValue intersectWithType(AbstractConstantValue value, + TypeMask type) { + if (value.isNothing || typeSystem.areDisjoint(value.type, type)) { + return nothing; + } else if (value.isConstant) { + return value; + } else { + return nonConstant(typeSystem.intersection(value.type, type)); + } + } + /// If [value] is an integer constant, returns its value, otherwise `null`. int intValue(AbstractConstantValue value) { if (value.isConstant && value.constant.isInt) { @@ -798,11 +809,17 @@ class TransformingVisitor extends DeepRecursiveVisitor { final List stack = []; + TypeCheckOperator checkIsNumber; + TransformingVisitor(this.compiler, this.functionCompiler, this.lattice, this.analyzer, - this.internalError); + this.internalError) { + checkIsNumber = new ClassTypeCheckOperator( + helpers.jsNumberClass, + BuiltinOperator.IsNotNumber); + } void transform(FunctionDefinition root) { // If one of the parameters has no value, the function is unreachable. @@ -1116,41 +1133,99 @@ class TransformingVisitor extends DeepRecursiveVisitor { /// /// Returns `true` if the node was replaced. specializeOperatorCall(InvokeMethod node) { + if (!backend.isInterceptedSelector(node.selector)) return null; + if (node.dartArgumentsLength > 1) return null; + if (node.callingConvention == CallingConvention.OneShotIntercepted) { + return null; + } + bool trustPrimitives = compiler.trustPrimitives; - /// Throws a [NoSuchMethodError] if the receiver is null, where [guard] - /// is a predicate that is true if and only if the receiver is null. - /// - /// See [NullCheck.guarded]. - Primitive guardReceiver(CpsFragment cps, BuiltinOperator guard) { - if (guard == null || getValue(node.dartReceiver).isDefinitelyNotNull) { - return node.dartReceiver; + /// Check that the receiver and argument satisfy the given type checks, and + /// throw a [NoSuchMethodError] or [ArgumentError] if the check fails. + CpsFragment makeGuard(TypeCheckOperator receiverGuard, + [TypeCheckOperator argumentGuard]) { + CpsFragment cps = new CpsFragment(node.sourceInformation); + + // Make no guards if trusting primitives. + if (trustPrimitives) return cps; + + // Determine which guards are needed. + ChecksNeeded receiverChecks = + receiverGuard.getChecksNeeded(node.dartReceiver, classWorld); + bool needReceiverGuard = receiverChecks != ChecksNeeded.None; + bool needArgumentGuard = + argumentGuard != null && + argumentGuard.needsCheck(node.dartArgument(0), classWorld); + + if (!needReceiverGuard && !needArgumentGuard) return cps; + + // If we only need the receiver check, emit the specialized receiver + // check instruction. Examples: + // + // if (typeof receiver !== "number") return receiver.$lt; + // if (typeof receiver !== "number") return receiver.$lt(); + // + if (!needArgumentGuard) { + Primitive condition = receiverGuard.makeCheck(cps, node.dartReceiver); + cps.letPrim(new ReceiverCheck( + node.dartReceiver, + node.selector, + node.sourceInformation, + condition: condition, + useSelector: true, + isNullCheck: receiverChecks == ChecksNeeded.Null + )); + return cps; } - if (!trustPrimitives) { - // TODO(asgerf): Perhaps a separate optimization should decide that - // the guarded check is better based on the type? - Primitive check = cps.applyBuiltin(guard, [node.dartReceiver]); - return cps.letPrim(new NullCheck.guarded(check, node.dartReceiver, - node.selector, node.sourceInformation)); - } else { - // Refine the receiver to be non-null for use in the operator. - // This restricts code motion and improves the type computed for the - // built-in operator that depends on it. - // This must be done even if trusting primitives. - return cps.letPrim( - new Refinement(node.dartReceiver, typeSystem.nonNullType)); + + // TODO(asgerf): We should consider specialized instructions for + // argument checks and receiver+argument checks, to avoid breaking up + // basic blocks. + + // Emit as `H.iae(x)` if only the argument check may fail. For example: + // + // if (typeof argument !== "number") return H.iae(argument); + // + if (!needReceiverGuard) { + cps.ifTruthy(argumentGuard.makeCheck(cps, node.dartArgument(0))) + .invokeStaticThrower(helpers.throwIllegalArgumentException, + [node.dartArgument(0)]); + return cps; } + + // Both receiver and argument check is needed. Emit as a combined check + // using a one-shot interceptor to produce the exact error message in + // the error case. For example: + // + // if (typeof receiver !== "number" || typeof argument !== "number") + // return J.$lt(receiver, argument); + // + Continuation fail = cps.letCont(); + cps.ifTruthy(receiverGuard.makeCheck(cps, node.dartReceiver)) + .invokeContinuation(fail); + cps.ifTruthy(argumentGuard.makeCheck(cps, node.dartArgument(0))) + .invokeContinuation(fail); + + cps.insideContinuation(fail) + ..invokeMethod(node.dartReceiver, node.selector, node.mask, + [node.dartArgument(0)], CallingConvention.OneShotIntercepted) + ..put(new Unreachable()); + + return cps; } /// Replaces the call with [operator], using the receiver and first argument /// as operands (in that order). /// - /// If [guard] is given, the receiver is checked using [guardReceiver], - /// unless it is known not to be null. - CpsFragment makeBinary(BuiltinOperator operator, {BuiltinOperator guard}) { - CpsFragment cps = new CpsFragment(node.sourceInformation); - Primitive left = guardReceiver(cps, guard); - Primitive right = node.dartArgument(0); + /// If [guard] is given, the receiver and argument are both checked using + /// that operator. + CpsFragment makeBinary(BuiltinOperator operator, + {TypeCheckOperator guard: TypeCheckOperator.none}) { + CpsFragment cps = makeGuard(guard, guard); + Primitive left = guard.makeRefinement(cps, node.dartReceiver, classWorld); + Primitive right = + guard.makeRefinement(cps, node.dartArgument(0), classWorld); Primitive result = cps.applyBuiltin(operator, [left, right]); result.hint = node.hint; node.replaceUsesWith(result); @@ -1159,15 +1234,20 @@ class TransformingVisitor extends DeepRecursiveVisitor { /// Like [makeBinary] but for unary operators with the receiver as the /// argument. - CpsFragment makeUnary(BuiltinOperator operator, {BuiltinOperator guard}) { - CpsFragment cps = new CpsFragment(node.sourceInformation); - Primitive argument = guardReceiver(cps, guard); + CpsFragment makeUnary(BuiltinOperator operator, + {TypeCheckOperator guard: TypeCheckOperator.none}) { + CpsFragment cps = makeGuard(guard); + Primitive argument = + guard.makeRefinement(cps, node.dartReceiver, classWorld); Primitive result = cps.applyBuiltin(operator, [argument]); result.hint = node.hint; node.replaceUsesWith(result); return cps; } + TypeMask successType = + typeSystem.receiverTypeFor(node.selector, node.dartReceiver.type); + if (node.selector.isOperator && node.dartArgumentsLength == 1) { Primitive leftArg = node.dartReceiver; Primitive rightArg = node.dartArgument(0); @@ -1198,13 +1278,17 @@ class TransformingVisitor extends DeepRecursiveVisitor { return makeBinary(BuiltinOperator.Identical); } } else { - if (lattice.isDefinitelyNum(left, allowNull: true) && - lattice.isDefinitelyNum(right, allowNull: trustPrimitives)) { + if (typeSystem.isDefinitelyNum(successType)) { // Try to insert a numeric operator. BuiltinOperator operator = NumBinaryBuiltins[opname]; if (operator != null) { - return makeBinary(operator, guard: BuiltinOperator.IsNotNumber); + return makeBinary(operator, guard: checkIsNumber); } + + // The following specializations only apply to integers. + // The Math.floor test is quite large, so we only apply these in cases + // where the guard does not involve Math.floor. + // Shift operators are not in [NumBinaryBuiltins] because Dart shifts // behave different to JS shifts, especially in the handling of the // shift count. @@ -1212,8 +1296,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { if (opname == '<<' && lattice.isDefinitelyInt(left, allowNull: true) && lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) { - return makeBinary(BuiltinOperator.NumShl, - guard: BuiltinOperator.IsNotNumber); + return makeBinary(BuiltinOperator.NumShl, guard: checkIsNumber); } // Try to insert a shift-right operator. JavaScript's right shift is // consistent with Dart's only for left operands in the unsigned @@ -1221,8 +1304,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { if (opname == '>>' && lattice.isDefinitelyUint32(left, allowNull: true) && lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) { - return makeBinary(BuiltinOperator.NumShr, - guard: BuiltinOperator.IsNotNumber); + return makeBinary(BuiltinOperator.NumShr, guard: checkIsNumber); } // Try to use remainder for '%'. Both operands must be non-negative // and the divisor must be non-zero. @@ -1231,14 +1313,14 @@ class TransformingVisitor extends DeepRecursiveVisitor { lattice.isDefinitelyUint(right) && lattice.isDefinitelyIntInRange(right, min: 1)) { return makeBinary(BuiltinOperator.NumRemainder, - guard: BuiltinOperator.IsNotNumber); + guard: checkIsNumber); } if (opname == '~/' && lattice.isDefinitelyUint32(left, allowNull: true) && lattice.isDefinitelyIntInRange(right, min: 2)) { return makeBinary(BuiltinOperator.NumTruncatingDivideToSigned32, - guard: BuiltinOperator.IsNotNumber); + guard: checkIsNumber); } } if (lattice.isDefinitelyString(left, allowNull: trustPrimitives) && @@ -1250,18 +1332,13 @@ class TransformingVisitor extends DeepRecursiveVisitor { } } if (node.selector.isOperator && node.dartArgumentsLength == 0) { - Primitive argument = node.dartReceiver; - AbstractConstantValue value = getValue(argument); - - if (lattice.isDefinitelyNum(value, allowNull: true)) { + if (typeSystem.isDefinitelyNum(successType)) { String opname = node.selector.name; if (opname == '~') { - return makeUnary(BuiltinOperator.NumBitNot, - guard: BuiltinOperator.IsNotNumber); + return makeUnary(BuiltinOperator.NumBitNot, guard: checkIsNumber); } if (opname == 'unary-') { - return makeUnary(BuiltinOperator.NumNegate, - guard: BuiltinOperator.IsNotNumber); + return makeUnary(BuiltinOperator.NumNegate, guard: checkIsNumber); } } } @@ -1277,7 +1354,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { lattice.isDefinitelyInt(argValue) && isIntNotZero(argValue)) { return makeBinary(BuiltinOperator.NumRemainder, - guard: BuiltinOperator.IsNotNumber); + guard: checkIsNumber); } } } else if (name == 'codeUnitAt') { @@ -1391,6 +1468,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { case '[]': Primitive index = node.dartArgument(0); + // TODO(asgerf): Consider inserting a guard and specialize anyway. if (!lattice.isDefinitelyInt(getValue(index))) return null; CpsFragment cps = new CpsFragment(node.sourceInformation); receiver = makeBoundsCheck(cps, receiver, index); @@ -2282,9 +2360,12 @@ class TransformingVisitor extends DeepRecursiveVisitor { } } - visitNullCheck(NullCheck node) { - if (!getValue(node.value.definition).isNullable) { - node.replaceUsesWith(node.value.definition); + visitReceiverCheck(ReceiverCheck node) { + Primitive input = node.value.definition; + if (!input.type.isNullable && + (node.isNullCheck || + !input.type.needsNoSuchMethodHandling(node.selector, classWorld))) { + node.replaceUsesWith(input); return new CpsFragment(); } return null; @@ -3128,16 +3209,9 @@ class TypePropagationVisitor implements Visitor { @override void visitRefinement(Refinement node) { - AbstractConstantValue value = getValue(node.value.definition); - if (value.isNothing || - typeSystem.areDisjoint(value.type, node.refineType)) { - setValue(node, nothing); - } else if (value.isConstant) { - setValue(node, value); - } else { - setValue(node, - nonConstant(value.type.intersection(node.refineType, classWorld))); - } + setValue(node, lattice.intersectWithType( + getValue(node.value.definition), + node.refineType)); } @override @@ -3146,8 +3220,19 @@ class TypePropagationVisitor implements Visitor { } @override - void visitNullCheck(NullCheck node) { - setValue(node, lattice.nonNullable(getValue(node.value.definition))); + void visitReceiverCheck(ReceiverCheck node) { + AbstractConstantValue value = getValue(node.value.definition); + if (node.isNullCheck) { + // Avoid expensive TypeMask operations for null checks. + setValue(node, lattice.nonNullable(value)); + } else if (value.isConstant && + !value.type.needsNoSuchMethodHandling(node.selector, classWorld)) { + // Preserve constants, unless the check fails for the constant. + setValue(node, value); + } else { + setValue(node, + nonConstant(typeSystem.receiverTypeFor(node.selector, value.type))); + } } } @@ -3269,3 +3354,89 @@ class ResetAnalysisInfo extends TrampolineRecursiveVisitor { clear(node.variable); } } + +enum ChecksNeeded { + /// No check is needed. + None, + + /// Only null may fail the check. + Null, + + /// Full check required. + Complete, +} + +/// Generates runtime checks against a some type criteria, and determines at +/// compile-time if the check is needed. +/// +/// This class only generates the condition for determining if a check should +/// fail. Throwing the appropriate error in response to a failure is handled +/// elsewhere. +abstract class TypeCheckOperator { + const TypeCheckOperator(); + static const TypeCheckOperator none = const NoTypeCheckOperator(); + + /// Determines to what extent a runtime check is needed. + /// + /// Sometimes a check can be slightly improved if it is known that null is the + /// only possible input that fails the check. + ChecksNeeded getChecksNeeded(Primitive value, World world); + + /// Make an expression that returns `true` if [value] should fail the check. + /// + /// The result should be used in a check of the form: + /// + /// if (makeCheck(value)) throw Error(value); + /// + Primitive makeCheck(CpsFragment cps, Primitive value); + + /// Refine [value] after a succesful check. + Primitive makeRefinement(CpsFragment cps, Primitive value, World world); + + bool needsCheck(Primitive value, World world) { + return getChecksNeeded(value, world) != ChecksNeeded.None; + } +} + +/// Check that always passes. +class NoTypeCheckOperator extends TypeCheckOperator { + const NoTypeCheckOperator(); + + ChecksNeeded getChecksNeeded(Primitive value, World world) { + return ChecksNeeded.None; + } + + Primitive makeCheck(CpsFragment cps, Primitive value) { + return cps.makeFalse(); + } + + Primitive makeRefinement(CpsFragment cps, Primitive value, World world) { + return value; + } +} + +/// Checks using a built-in operator that a value is an instance of a given +/// class. +class ClassTypeCheckOperator extends TypeCheckOperator { + ClassElement classElement; + BuiltinOperator negatedOperator; + + ClassTypeCheckOperator(this.classElement, this.negatedOperator); + + ChecksNeeded getChecksNeeded(Primitive value, World world) { + TypeMask type = value.type; + if (type.satisfies(classElement, world)) { + return type.isNullable ? ChecksNeeded.Null : ChecksNeeded.None; + } else { + return ChecksNeeded.Complete; + } + } + + Primitive makeCheck(CpsFragment cps, Primitive value) { + return cps.applyBuiltin(negatedOperator, [value]); + } + + Primitive makeRefinement(CpsFragment cps, Primitive value, World world) { + return cps.refine(value, new TypeMask.nonNullSubclass(classElement, world)); + } +} diff --git a/pkg/compiler/lib/src/cps_ir/update_refinements.dart b/pkg/compiler/lib/src/cps_ir/update_refinements.dart index 48e3242457e..3500521ac01 100644 --- a/pkg/compiler/lib/src/cps_ir/update_refinements.dart +++ b/pkg/compiler/lib/src/cps_ir/update_refinements.dart @@ -3,6 +3,7 @@ library dart2js.cps_ir.update_refinements; import 'cps_ir_nodes.dart'; import 'optimizers.dart' show Pass; import 'type_mask_system.dart'; +import '../world.dart'; /// Updates all references to use the most refined version in scope. /// @@ -19,6 +20,7 @@ class UpdateRefinements extends TrampolineRecursiveVisitor implements Pass { String get passName => 'Update refinements'; final TypeMaskSystem typeSystem; + World get classWorld => typeSystem.classWorld; Map refinementFor = {}; @@ -34,20 +36,21 @@ class UpdateRefinements extends TrampolineRecursiveVisitor implements Pass { return next; } - visitNullCheck(NullCheck node) { + visitReceiverCheck(ReceiverCheck node) { if (refine(node.value)) { + // Update the type if the input has changed. Primitive value = node.value.definition; - if (value.type.isNullable) { - // Update the type if the input has changed. - node.type = value.type.nonNullable(); + if (value.type.needsNoSuchMethodHandling(node.selector, classWorld)) { + node.type = typeSystem.receiverTypeFor(node.selector, value.type); } else { + // Check is no longer needed. node..replaceUsesWith(value)..destroy(); LetPrim letPrim = node.parent; letPrim.remove(); return; } } - // Use the NullCheck as a refinement. + // Use the ReceiverCheck as a refinement. Primitive value = node.effectiveDefinition; Primitive old = refinementFor[value]; refinementFor[value] = node; diff --git a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart index 1815ee2a8b7..eb1e6994d4e 100644 --- a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart +++ b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart @@ -1031,7 +1031,7 @@ class CodeGenerator extends tree_ir.StatementVisitor } @override - visitNullCheck(tree_ir.NullCheck node) { + visitReceiverCheck(tree_ir.ReceiverCheck node) { js.Expression value = visitExpression(node.value); // TODO(sra): Try to use the selector even when [useSelector] is false. The // reason we use 'toString' is that it is always defined so avoids a slow @@ -1041,9 +1041,12 @@ class CodeGenerator extends tree_ir.StatementVisitor // hook for that selector. We don't know these things here, but the decision // could be deferred by creating a deferred property that was resolved after // codegen. - js.Expression access = node.selector != null && node.useSelector + js.Expression access = node.useSelector ? js.js('#.#', [value, glue.invocationName(node.selector)]) : js.js('#.toString', [value]); + if (node.useInvoke) { + access = new js.Call(access, []); + } if (node.condition != null) { js.Expression condition = visitExpression(node.condition); js.Statement body = isNullReturn(node.next) diff --git a/pkg/compiler/lib/src/tree_ir/optimization/pull_into_initializers.dart b/pkg/compiler/lib/src/tree_ir/optimization/pull_into_initializers.dart index 1ada85b35d7..3785f7802f8 100644 --- a/pkg/compiler/lib/src/tree_ir/optimization/pull_into_initializers.dart +++ b/pkg/compiler/lib/src/tree_ir/optimization/pull_into_initializers.dart @@ -168,7 +168,7 @@ class PullIntoInitializers extends RecursiveTransformer return node; } - Statement visitNullCheck(NullCheck node) { + Statement visitReceiverCheck(ReceiverCheck node) { if (node.condition != null) { node.condition = visitExpression(node.condition); // The value occurs in conditional context, so don't pull from that. diff --git a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart index 1c92ff5eeb3..368513776e3 100644 --- a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart +++ b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart @@ -1261,7 +1261,7 @@ class StatementRewriter extends Transformer implements Pass { } @override - Statement visitNullCheck(NullCheck node) { + Statement visitReceiverCheck(ReceiverCheck node) { inEmptyEnvironment(() { node.next = visitStatement(node.next); }); diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart index 15dc292cb6a..a9c28318480 100644 --- a/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart +++ b/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart @@ -699,12 +699,16 @@ class Builder implements cps_ir.Visitor/**/ { } } - visitNullCheck(cps_ir.NullCheck node) => (Statement next) { - return new NullCheck( + visitReceiverCheck(cps_ir.ReceiverCheck node) => (Statement next) { + // The CPS IR uses 'isNullCheck' because the semantics are important. + // In the Tree IR, syntax is more important, so the receiver check uses + // "useInvoke" to denote if an invocation should be emitted. + return new ReceiverCheck( condition: getVariableUseOrNull(node.condition), value: getVariableUse(node.value), selector: node.selector, useSelector: node.useSelector, + useInvoke: !node.isNullCheck, next: next, sourceInformation: node.sourceInformation); }; diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart index 2b4d3011163..ec2e34abdac 100644 --- a/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart +++ b/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart @@ -950,23 +950,24 @@ class Yield extends Statement { } } -class NullCheck extends Statement { +class ReceiverCheck extends Statement { Expression condition; Expression value; Selector selector; bool useSelector; + bool useInvoke; Statement next; SourceInformation sourceInformation; - NullCheck({this.condition, this.value, this.selector, this.useSelector, - this.next, this.sourceInformation}); + ReceiverCheck({this.condition, this.value, this.selector, this.useSelector, + this.useInvoke, this.next, this.sourceInformation}); accept(StatementVisitor visitor) { - return visitor.visitNullCheck(this); + return visitor.visitReceiverCheck(this); } accept1(StatementVisitor1 visitor, arg) { - return visitor.visitNullCheck(this, arg); + return visitor.visitReceiverCheck(this, arg); } } @@ -1059,7 +1060,7 @@ abstract class StatementVisitor { S visitUnreachable(Unreachable node); S visitForeignStatement(ForeignStatement node); S visitYield(Yield node); - S visitNullCheck(NullCheck node); + S visitReceiverCheck(ReceiverCheck node); } abstract class StatementVisitor1 { @@ -1077,7 +1078,7 @@ abstract class StatementVisitor1 { S visitUnreachable(Unreachable node, A arg); S visitForeignStatement(ForeignStatement node, A arg); S visitYield(Yield node, A arg); - S visitNullCheck(NullCheck node, A arg); + S visitReceiverCheck(ReceiverCheck node, A arg); } abstract class RecursiveVisitor implements StatementVisitor, ExpressionVisitor { @@ -1286,7 +1287,7 @@ abstract class RecursiveVisitor implements StatementVisitor, ExpressionVisitor { visitStatement(node.next); } - visitNullCheck(NullCheck node) { + visitReceiverCheck(ReceiverCheck node) { if (node.condition != null) visitExpression(node.condition); visitExpression(node.value); visitStatement(node.next); @@ -1546,7 +1547,7 @@ class RecursiveTransformer extends Transformer { return node; } - visitNullCheck(NullCheck node) { + visitReceiverCheck(ReceiverCheck node) { if (node.condition != null) { node.condition = visitExpression(node.condition); } diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_tracer.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_tracer.dart index 6d2ce7eac42..01aed83b869 100644 --- a/pkg/compiler/lib/src/tree_ir/tree_ir_tracer.dart +++ b/pkg/compiler/lib/src/tree_ir/tree_ir_tracer.dart @@ -179,7 +179,7 @@ class BlockCollector extends StatementVisitor { visitStatement(node.next); } - visitNullCheck(NullCheck node) { + visitReceiverCheck(ReceiverCheck node) { _addStatement(node); visitStatement(node.next); } @@ -345,7 +345,7 @@ class TreeTracer extends TracerUtil with StatementVisitor { } @override - visitNullCheck(NullCheck node) { + visitReceiverCheck(ReceiverCheck node) { printStatement(null, 'NullCheck ${expr(node.value)}'); } } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_1.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_1.js index 758c03c8d23..be268f4a7d3 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_1.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_1.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$sub$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$sub$n(x, y); + P.print(x - y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_10.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_10.js index 8679a25c40d..9c4c3f3cba1 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_10.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_10.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$gt$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$gt$n(x, y); + P.print(x > y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_11.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_11.js index b5ff041ab2c..23f5fca882a 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_11.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_11.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$lt$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$lt$n(x, y); + P.print(x < y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_12.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_12.js index 525a5d002c5..0fd94d22512 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_12.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_12.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$ge$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$ge$n(x, y); + P.print(x >= y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_13.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_13.js index f434bb78d5a..481395cb7da 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_13.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_13.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$le$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$le$n(x, y); + P.print(x <= y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_19.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_19.js index c614b758b3d..5125dcbb0bc 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_19.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_19.js @@ -10,12 +10,14 @@ // } function() { - var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null), v0 = typeof y === "number"; - P.print(J.$div$n(x, 2)); + var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); + if (typeof x !== "number") + return x.$div(); + P.print(x / 2); P.print(true); - P.print(v0); - if (!v0) - throw H.wrapException(H.argumentErrorValue(y)); + P.print(typeof y === "number"); + if (typeof y !== "number") + return H.iae(y); P.print(x + y); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_2.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_2.js index 33c7bf9920c..0de632e3976 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_2.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_2.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$div$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$div$n(x, y); + P.print(x / y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_20.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_20.js index 915d2371cee..13b3053e386 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_20.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_20.js @@ -10,12 +10,14 @@ // } function() { - var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null), v0 = typeof y === "number"; - P.print(J.$div$n(x, 2)); + var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); + if (typeof x !== "number") + return x.$div(); + P.print(x / 2); P.print(true); - P.print(v0); - if (!v0) - throw H.wrapException(H.argumentErrorValue(y)); + P.print(typeof y === "number"); + if (typeof y !== "number") + return H.iae(y); P.print(x * y); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_21.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_21.js index 2c4e7c7b2b6..26cfc957632 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_21.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_21.js @@ -10,9 +10,12 @@ // } function() { - var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null), v0 = typeof y === "number"; - P.print(J.$div$n(x, 2)); + var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null), v0; + if (typeof x !== "number") + return x.$div(); + P.print(x / 2); P.print(true); + v0 = typeof y === "number"; P.print(v0); if (!v0) throw H.wrapException(H.argumentErrorValue(y)); diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_7.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_7.js index 6e152f78354..cb58cd33ef7 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_7.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_7.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$and$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$and$n(x, y); + P.print((x & y) >>> 0); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_8.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_8.js index 438d6dc7213..7c0109d9411 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_8.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_8.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$or$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$or$n(x, y); + P.print((x | y) >>> 0); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_9.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_9.js index 43418b58a98..c1c6becaeb0 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_9.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_9.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$xor$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$xor$n(x, y); + P.print((x ^ y) >>> 0); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_1.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_1.js index 758c03c8d23..be268f4a7d3 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_1.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_1.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$sub$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$sub$n(x, y); + P.print(x - y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_10.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_10.js index b5ff041ab2c..23f5fca882a 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_10.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_10.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$lt$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$lt$n(x, y); + P.print(x < y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_11.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_11.js index 8679a25c40d..9c4c3f3cba1 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_11.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_11.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$gt$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$gt$n(x, y); + P.print(x > y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_12.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_12.js index f434bb78d5a..481395cb7da 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_12.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_12.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$le$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$le$n(x, y); + P.print(x <= y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_13.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_13.js index 525a5d002c5..0fd94d22512 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_13.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_13.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$ge$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$ge$n(x, y); + P.print(x >= y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_2.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_2.js index 33c7bf9920c..0de632e3976 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_2.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_2.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$div$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$div$n(x, y); + P.print(x / y); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_7.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_7.js index 6e152f78354..cb58cd33ef7 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_7.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_7.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$and$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$and$n(x, y); + P.print((x & y) >>> 0); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_8.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_8.js index 438d6dc7213..7c0109d9411 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_8.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_8.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$or$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$or$n(x, y); + P.print((x | y) >>> 0); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_9.js b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_9.js index 43418b58a98..c1c6becaeb0 100644 --- a/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_9.js +++ b/tests/compiler/dart2js/cps_ir/expected/argument_refinement_num_9.js @@ -13,7 +13,9 @@ function() { var x = P.int_parse("1233", null, null), y = P.int_parse("1234", null, null); P.print(typeof x === "number"); P.print(typeof y === "number"); - P.print(J.$xor$n(x, y)); + if (typeof x !== "number" || typeof y !== "number") + return J.$xor$n(x, y); + P.print((x ^ y) >>> 0); P.print(true); P.print(true); } diff --git a/tests/compiler/dart2js/cps_ir/expected/operators2_3.js b/tests/compiler/dart2js/cps_ir/expected/operators2_3.js index bf578b27148..0ec84408c9f 100644 --- a/tests/compiler/dart2js/cps_ir/expected/operators2_3.js +++ b/tests/compiler/dart2js/cps_ir/expected/operators2_3.js @@ -1,12 +1,14 @@ // Expectation for test: // // Method to test: function(foo) -// foo(a) => a % 13; +// import 'package:expect/expect.dart'; +// +// @NoInline() foo(a) => a % 13; +// // main() { // print(foo(5)); // print(foo(-100)); // } function(a) { - var result = a % 13; - return result === 0 ? 0 : result > 0 ? result : result + 13; + return C.JSInt_methods.$mod(a, 13); } diff --git a/tests/compiler/dart2js/cps_ir/expected/operators2_6.js b/tests/compiler/dart2js/cps_ir/expected/operators2_6.js index 79fe43a2d9c..d9cfe873d84 100644 --- a/tests/compiler/dart2js/cps_ir/expected/operators2_6.js +++ b/tests/compiler/dart2js/cps_ir/expected/operators2_6.js @@ -1,11 +1,14 @@ // Expectation for test: // // Method to test: function(foo) -// foo(a) => a ~/ 13; +// import 'package:expect/expect.dart'; +// +// @NoInline() foo(a) => a ~/ 13; +// // main() { // print(foo(5)); // print(foo(-100)); // } function(a) { - return (a | 0) === a && (13 | 0) === 13 ? a / 13 | 0 : C.JSNumber_methods.toInt$0(a / 13); + return C.JSInt_methods.$tdiv(a, 13); } diff --git a/tests/compiler/dart2js/cps_ir/expected/operators2_7.js b/tests/compiler/dart2js/cps_ir/expected/operators2_7.js index 9faff59c99f..403b8312c7d 100644 --- a/tests/compiler/dart2js/cps_ir/expected/operators2_7.js +++ b/tests/compiler/dart2js/cps_ir/expected/operators2_7.js @@ -1,6 +1,9 @@ // Expectation for test: // // Method to test: function(foo) -// foo(a) => a ~/ 13; +// import 'package:expect/expect.dart'; +// +// @NoInline() foo(a) => a ~/ 13; +// // main() { // print(foo.toString()); // print(foo(5)); diff --git a/tests/compiler/dart2js/cps_ir/expected/operators2_8.js b/tests/compiler/dart2js/cps_ir/expected/operators2_8.js index 2f1ce60fb82..b322ba0c2fb 100644 --- a/tests/compiler/dart2js/cps_ir/expected/operators2_8.js +++ b/tests/compiler/dart2js/cps_ir/expected/operators2_8.js @@ -1,11 +1,14 @@ // Expectation for test: // // Method to test: function(foo) -// foo(a) => a ~/ 13; +// import 'package:expect/expect.dart'; +// +// @NoInline() foo(a) => a ~/ 13; +// // main() { // print(foo(5)); // print(foo(8000000000)); // } function(a) { - return (a | 0) === a && (13 | 0) === 13 ? a / 13 | 0 : C.JSNumber_methods.toInt$0(a / 13); + return C.JSInt_methods.$tdiv(a, 13); } diff --git a/tests/compiler/dart2js/cps_ir/input/operators2_3.dart b/tests/compiler/dart2js/cps_ir/input/operators2_3.dart index f1dc5acd9c9..2cd9a5340a3 100644 --- a/tests/compiler/dart2js/cps_ir/input/operators2_3.dart +++ b/tests/compiler/dart2js/cps_ir/input/operators2_3.dart @@ -1,5 +1,8 @@ // Method to test: function(foo) -foo(a) => a % 13; +import 'package:expect/expect.dart'; + +@NoInline() foo(a) => a % 13; + main() { print(foo(5)); print(foo(-100)); diff --git a/tests/compiler/dart2js/cps_ir/input/operators2_6.dart b/tests/compiler/dart2js/cps_ir/input/operators2_6.dart index 4b763a62310..88bd1374b18 100644 --- a/tests/compiler/dart2js/cps_ir/input/operators2_6.dart +++ b/tests/compiler/dart2js/cps_ir/input/operators2_6.dart @@ -1,5 +1,8 @@ // Method to test: function(foo) -foo(a) => a ~/ 13; +import 'package:expect/expect.dart'; + +@NoInline() foo(a) => a ~/ 13; + main() { print(foo(5)); print(foo(-100)); diff --git a/tests/compiler/dart2js/cps_ir/input/operators2_7.dart b/tests/compiler/dart2js/cps_ir/input/operators2_7.dart index 225701be571..f8fe827ab2c 100644 --- a/tests/compiler/dart2js/cps_ir/input/operators2_7.dart +++ b/tests/compiler/dart2js/cps_ir/input/operators2_7.dart @@ -1,5 +1,8 @@ // Method to test: function(foo) -foo(a) => a ~/ 13; +import 'package:expect/expect.dart'; + +@NoInline() foo(a) => a ~/ 13; + main() { print(foo.toString()); print(foo(5)); diff --git a/tests/compiler/dart2js/cps_ir/input/operators2_8.dart b/tests/compiler/dart2js/cps_ir/input/operators2_8.dart index 05e3bb0aa32..ec9b8b363f1 100644 --- a/tests/compiler/dart2js/cps_ir/input/operators2_8.dart +++ b/tests/compiler/dart2js/cps_ir/input/operators2_8.dart @@ -1,5 +1,8 @@ // Method to test: function(foo) -foo(a) => a ~/ 13; +import 'package:expect/expect.dart'; + +@NoInline() foo(a) => a ~/ 13; + main() { print(foo(5)); print(foo(8000000000));