diff options
Diffstat (limited to 'chromium/v8/src/builtins/builtins-number-gen.cc')
-rw-r--r-- | chromium/v8/src/builtins/builtins-number-gen.cc | 1060 |
1 files changed, 93 insertions, 967 deletions
diff --git a/chromium/v8/src/builtins/builtins-number-gen.cc b/chromium/v8/src/builtins/builtins-number-gen.cc index 4513d73abc7..4e8bcae60be 100644 --- a/chromium/v8/src/builtins/builtins-number-gen.cc +++ b/chromium/v8/src/builtins/builtins-number-gen.cc @@ -6,6 +6,7 @@ #include "src/builtins/builtins.h" #include "src/codegen/code-stub-assembler.h" #include "src/ic/binary-op-assembler.h" +#include "src/ic/unary-op-assembler.h" namespace v8 { namespace internal { @@ -13,983 +14,108 @@ namespace internal { // ----------------------------------------------------------------------------- // ES6 section 20.1 Number Objects -class NumberBuiltinsAssembler : public CodeStubAssembler { - public: - explicit NumberBuiltinsAssembler(compiler::CodeAssemblerState* state) - : CodeStubAssembler(state) {} - - protected: - template <typename Descriptor> - void EmitBitwiseOp(Operation op) { - TNode<Object> left = CAST(Parameter(Descriptor::kLeft)); - TNode<Object> right = CAST(Parameter(Descriptor::kRight)); - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - - BinaryOpAssembler binop_asm(state()); - Return(binop_asm.Generate_BitwiseBinaryOp(op, left, right, context)); - } - - template <typename Descriptor> - void RelationalComparisonBuiltin(Operation op) { - TNode<Object> lhs = CAST(Parameter(Descriptor::kLeft)); - TNode<Object> rhs = CAST(Parameter(Descriptor::kRight)); - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - - Return(RelationalComparison(op, lhs, rhs, context)); - } - - template <typename Descriptor> - void UnaryOp(TVariable<Object>* var_input, Label* do_smi, Label* do_double, - TVariable<Float64T>* var_input_double, Label* do_bigint); - - template <typename Descriptor> - void BinaryOp(Label* smis, TVariable<Object>* var_left, - TVariable<Object>* var_right, Label* doubles, - TVariable<Float64T>* var_left_double, - TVariable<Float64T>* var_right_double, Label* bigints); -}; - -// ES6 #sec-number.isfinite -TF_BUILTIN(NumberIsFinite, CodeStubAssembler) { - TNode<Object> number = CAST(Parameter(Descriptor::kNumber)); - - Label return_true(this), return_false(this); - - // Check if {number} is a Smi. - GotoIf(TaggedIsSmi(number), &return_true); - - // Check if {number} is a HeapNumber. - TNode<HeapObject> number_heap_object = CAST(number); - GotoIfNot(IsHeapNumber(number_heap_object), &return_false); - - // Check if {number} contains a finite, non-NaN value. - TNode<Float64T> number_value = LoadHeapNumberValue(number_heap_object); - BranchIfFloat64IsNaN(Float64Sub(number_value, number_value), &return_false, - &return_true); - - BIND(&return_true); - Return(TrueConstant()); - - BIND(&return_false); - Return(FalseConstant()); -} - -TF_BUILTIN(AllocateHeapNumber, CodeStubAssembler) { - TNode<HeapNumber> result = AllocateHeapNumber(); - Return(result); -} - -// ES6 #sec-number.isinteger -TF_BUILTIN(NumberIsInteger, CodeStubAssembler) { - TNode<Object> number = CAST(Parameter(Descriptor::kNumber)); - Return(SelectBooleanConstant(IsInteger(number))); -} - -// ES6 #sec-number.isnan -TF_BUILTIN(NumberIsNaN, CodeStubAssembler) { - TNode<Object> number = CAST(Parameter(Descriptor::kNumber)); - - Label return_true(this), return_false(this); - - // Check if {number} is a Smi. - GotoIf(TaggedIsSmi(number), &return_false); - - // Check if {number} is a HeapNumber. - TNode<HeapObject> number_heap_object = CAST(number); - GotoIfNot(IsHeapNumber(number_heap_object), &return_false); - - // Check if {number} contains a NaN value. - TNode<Float64T> number_value = LoadHeapNumberValue(number_heap_object); - BranchIfFloat64IsNaN(number_value, &return_true, &return_false); - - BIND(&return_true); - Return(TrueConstant()); - - BIND(&return_false); - Return(FalseConstant()); -} - -// ES6 #sec-number.issafeinteger -TF_BUILTIN(NumberIsSafeInteger, CodeStubAssembler) { - TNode<Object> number = CAST(Parameter(Descriptor::kNumber)); - Return(SelectBooleanConstant(IsSafeInteger(number))); -} - -// ES6 #sec-number.parsefloat -TF_BUILTIN(NumberParseFloat, CodeStubAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - - // We might need to loop once for ToString conversion. - TVARIABLE(Object, var_input, CAST(Parameter(Descriptor::kString))); - Label loop(this, &var_input); - Goto(&loop); - BIND(&loop); - { - // Load the current {input} value. - TNode<Object> input = var_input.value(); - - // Check if the {input} is a HeapObject or a Smi. - Label if_inputissmi(this), if_inputisnotsmi(this); - Branch(TaggedIsSmi(input), &if_inputissmi, &if_inputisnotsmi); - - BIND(&if_inputissmi); - { - // The {input} is already a Number, no need to do anything. - Return(input); - } - - BIND(&if_inputisnotsmi); - { - // The {input} is a HeapObject, check if it's already a String. - TNode<HeapObject> input_heap_object = CAST(input); - Label if_inputisstring(this), if_inputisnotstring(this); - TNode<Map> input_map = LoadMap(input_heap_object); - TNode<Uint16T> input_instance_type = LoadMapInstanceType(input_map); - Branch(IsStringInstanceType(input_instance_type), &if_inputisstring, - &if_inputisnotstring); - - BIND(&if_inputisstring); - { - // The {input} is already a String, check if {input} contains - // a cached array index. - Label if_inputcached(this), if_inputnotcached(this); - TNode<Uint32T> input_hash = LoadNameHashField(CAST(input)); - Branch(IsClearWord32(input_hash, - Name::kDoesNotContainCachedArrayIndexMask), - &if_inputcached, &if_inputnotcached); - - BIND(&if_inputcached); - { - // Just return the {input}s cached array index. - TNode<UintPtrT> input_array_index = - DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash); - Return(SmiTag(Signed(input_array_index))); - } - - BIND(&if_inputnotcached); - { - // Need to fall back to the runtime to convert {input} to double. - Return(CallRuntime(Runtime::kStringParseFloat, context, input)); - } - } - - BIND(&if_inputisnotstring); - { - // The {input} is neither a String nor a Smi, check for HeapNumber. - Label if_inputisnumber(this), - if_inputisnotnumber(this, Label::kDeferred); - Branch(IsHeapNumberMap(input_map), &if_inputisnumber, - &if_inputisnotnumber); - - BIND(&if_inputisnumber); - { - // The {input} is already a Number, take care of -0. - Label if_inputiszero(this), if_inputisnotzero(this); - TNode<Float64T> input_value = LoadHeapNumberValue(input_heap_object); - Branch(Float64Equal(input_value, Float64Constant(0.0)), - &if_inputiszero, &if_inputisnotzero); - - BIND(&if_inputiszero); - Return(SmiConstant(0)); - - BIND(&if_inputisnotzero); - Return(input); - } - - BIND(&if_inputisnotnumber); - { - // Need to convert the {input} to String first. - // TODO(bmeurer): This could be more efficient if necessary. - var_input = CallBuiltin(Builtins::kToString, context, input); - Goto(&loop); - } - } - } - } -} - -// ES6 #sec-number.parseint -TF_BUILTIN(ParseInt, CodeStubAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TNode<Object> input = CAST(Parameter(Descriptor::kString)); - TNode<Object> radix = CAST(Parameter(Descriptor::kRadix)); - - // Check if {radix} is treated as 10 (i.e. undefined, 0 or 10). - Label if_radix10(this), if_generic(this, Label::kDeferred); - GotoIf(IsUndefined(radix), &if_radix10); - GotoIf(TaggedEqual(radix, SmiConstant(10)), &if_radix10); - GotoIf(TaggedEqual(radix, SmiConstant(0)), &if_radix10); - Goto(&if_generic); - - BIND(&if_radix10); - { - // Check if we can avoid the ToString conversion on {input}. - Label if_inputissmi(this), if_inputisheapnumber(this), - if_inputisstring(this); - GotoIf(TaggedIsSmi(input), &if_inputissmi); - TNode<Map> input_map = LoadMap(CAST(input)); - GotoIf(IsHeapNumberMap(input_map), &if_inputisheapnumber); - TNode<Uint16T> input_instance_type = LoadMapInstanceType(input_map); - Branch(IsStringInstanceType(input_instance_type), &if_inputisstring, - &if_generic); - - BIND(&if_inputissmi); - { - // Just return the {input}. - Return(input); - } - - BIND(&if_inputisheapnumber); - { - // Check if the {input} value is in Signed32 range. - Label if_inputissigned32(this); - TNode<Float64T> input_value = LoadHeapNumberValue(CAST(input)); - TNode<Int32T> input_value32 = - Signed(TruncateFloat64ToWord32(input_value)); - GotoIf(Float64Equal(input_value, ChangeInt32ToFloat64(input_value32)), - &if_inputissigned32); - - // Check if the absolute {input} value is in the [1,1<<31[ range. - // Take the generic path for the range [0,1[ because the result - // could be -0. - TNode<Float64T> input_value_abs = Float64Abs(input_value); - - GotoIfNot(Float64LessThan(input_value_abs, Float64Constant(1u << 31)), - &if_generic); - Branch(Float64LessThanOrEqual(Float64Constant(1), input_value_abs), - &if_inputissigned32, &if_generic); - - // Return the truncated int32 value, and return the tagged result. - BIND(&if_inputissigned32); - TNode<Number> result = ChangeInt32ToTagged(input_value32); - Return(result); - } - - BIND(&if_inputisstring); - { - // Check if the String {input} has a cached array index. - TNode<Uint32T> input_hash = LoadNameHashField(CAST(input)); - GotoIf(IsSetWord32(input_hash, Name::kDoesNotContainCachedArrayIndexMask), - &if_generic); - - // Return the cached array index as result. - TNode<UintPtrT> input_index = - DecodeWordFromWord32<String::ArrayIndexValueBits>(input_hash); - TNode<Smi> result = SmiTag(Signed(input_index)); - Return(result); - } - } - - BIND(&if_generic); - { - TNode<Object> result = - CallRuntime(Runtime::kStringParseInt, context, input, radix); - Return(result); - } -} - -// ES6 #sec-number.parseint -TF_BUILTIN(NumberParseInt, CodeStubAssembler) { +#define DEF_BINOP(Name, Generator) \ + TF_BUILTIN(Name, CodeStubAssembler) { \ + TNode<Object> lhs = CAST(Parameter(Descriptor::kLeft)); \ + TNode<Object> rhs = CAST(Parameter(Descriptor::kRight)); \ + TNode<Context> context = CAST(Parameter(Descriptor::kContext)); \ + TNode<HeapObject> maybe_feedback_vector = \ + CAST(Parameter(Descriptor::kMaybeFeedbackVector)); \ + TNode<UintPtrT> slot = \ + UncheckedCast<UintPtrT>(Parameter(Descriptor::kSlot)); \ + \ + BinaryOpAssembler binop_asm(state()); \ + TNode<Object> result = binop_asm.Generator(context, lhs, rhs, slot, \ + maybe_feedback_vector, false); \ + \ + Return(result); \ + } +DEF_BINOP(Add_WithFeedback, Generate_AddWithFeedback) +DEF_BINOP(Subtract_WithFeedback, Generate_SubtractWithFeedback) +DEF_BINOP(Multiply_WithFeedback, Generate_MultiplyWithFeedback) +DEF_BINOP(Divide_WithFeedback, Generate_DivideWithFeedback) +DEF_BINOP(Modulus_WithFeedback, Generate_ModulusWithFeedback) +DEF_BINOP(Exponentiate_WithFeedback, Generate_ExponentiateWithFeedback) +DEF_BINOP(BitwiseOr_WithFeedback, Generate_BitwiseOrWithFeedback) +DEF_BINOP(BitwiseXor_WithFeedback, Generate_BitwiseXorWithFeedback) +DEF_BINOP(BitwiseAnd_WithFeedback, Generate_BitwiseAndWithFeedback) +DEF_BINOP(ShiftLeft_WithFeedback, Generate_ShiftLeftWithFeedback) +DEF_BINOP(ShiftRight_WithFeedback, Generate_ShiftRightWithFeedback) +DEF_BINOP(ShiftRightLogical_WithFeedback, + Generate_ShiftRightLogicalWithFeedback) +#undef DEF_BINOP + +#define DEF_UNOP(Name, Generator) \ + TF_BUILTIN(Name, CodeStubAssembler) { \ + TNode<Object> value = CAST(Parameter(Descriptor::kValue)); \ + TNode<Context> context = CAST(Parameter(Descriptor::kContext)); \ + TNode<HeapObject> maybe_feedback_vector = \ + CAST(Parameter(Descriptor::kMaybeFeedbackVector)); \ + TNode<UintPtrT> slot = \ + UncheckedCast<UintPtrT>(Parameter(Descriptor::kSlot)); \ + \ + UnaryOpAssembler a(state()); \ + TNode<Object> result = \ + a.Generator(context, value, slot, maybe_feedback_vector); \ + \ + Return(result); \ + } +DEF_UNOP(BitwiseNot_WithFeedback, Generate_BitwiseNotWithFeedback) +DEF_UNOP(Decrement_WithFeedback, Generate_DecrementWithFeedback) +DEF_UNOP(Increment_WithFeedback, Generate_IncrementWithFeedback) +DEF_UNOP(Negate_WithFeedback, Generate_NegateWithFeedback) +#undef DEF_UNOP + +#define DEF_COMPARE(Name) \ + TF_BUILTIN(Name##_WithFeedback, CodeStubAssembler) { \ + TNode<Object> lhs = CAST(Parameter(Descriptor::kLeft)); \ + TNode<Object> rhs = CAST(Parameter(Descriptor::kRight)); \ + TNode<Context> context = CAST(Parameter(Descriptor::kContext)); \ + TNode<HeapObject> maybe_feedback_vector = \ + CAST(Parameter(Descriptor::kMaybeFeedbackVector)); \ + TNode<UintPtrT> slot = \ + UncheckedCast<UintPtrT>(Parameter(Descriptor::kSlot)); \ + \ + TVARIABLE(Smi, var_type_feedback); \ + TNode<Oddball> result = RelationalComparison(Operation::k##Name, lhs, rhs, \ + context, &var_type_feedback); \ + UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector, slot); \ + \ + Return(result); \ + } +DEF_COMPARE(LessThan) +DEF_COMPARE(LessThanOrEqual) +DEF_COMPARE(GreaterThan) +DEF_COMPARE(GreaterThanOrEqual) +#undef DEF_COMPARE + +TF_BUILTIN(Equal_WithFeedback, CodeStubAssembler) { + TNode<Object> lhs = CAST(Parameter(Descriptor::kLeft)); + TNode<Object> rhs = CAST(Parameter(Descriptor::kRight)); TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TNode<Object> input = CAST(Parameter(Descriptor::kString)); - TNode<Object> radix = CAST(Parameter(Descriptor::kRadix)); - Return(CallBuiltin(Builtins::kParseInt, context, input, radix)); -} + TNode<HeapObject> maybe_feedback_vector = + CAST(Parameter(Descriptor::kMaybeFeedbackVector)); + TNode<UintPtrT> slot = UncheckedCast<UintPtrT>(Parameter(Descriptor::kSlot)); -// ES6 #sec-number.prototype.valueof -TF_BUILTIN(NumberPrototypeValueOf, CodeStubAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); + TVARIABLE(Smi, var_type_feedback); + TNode<Oddball> result = Equal(lhs, rhs, context, &var_type_feedback); + UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector, slot); - TNode<Object> result = ToThisValue(context, receiver, PrimitiveType::kNumber, - "Number.prototype.valueOf"); Return(result); } -class AddStubAssembler : public CodeStubAssembler { - public: - explicit AddStubAssembler(compiler::CodeAssemblerState* state) - : CodeStubAssembler(state) {} - - protected: - TNode<Object> ConvertReceiver(TNode<JSReceiver> js_receiver, - TNode<Context> context) { - // Call ToPrimitive explicitly without hint (whereas ToNumber - // would pass a "number" hint). - Callable callable = CodeFactory::NonPrimitiveToPrimitive(isolate()); - return CallStub(callable, context, js_receiver); - } - - void ConvertNonReceiverAndLoop(TVariable<Object>* var_value, Label* loop, - TNode<Context> context) { - *var_value = - CallBuiltin(Builtins::kNonNumberToNumeric, context, var_value->value()); - Goto(loop); - } - - void ConvertAndLoop(TVariable<Object>* var_value, - TNode<Uint16T> instance_type, Label* loop, - TNode<Context> context) { - Label is_not_receiver(this, Label::kDeferred); - GotoIfNot(IsJSReceiverInstanceType(instance_type), &is_not_receiver); - - *var_value = ConvertReceiver(CAST(var_value->value()), context); - Goto(loop); - - BIND(&is_not_receiver); - ConvertNonReceiverAndLoop(var_value, loop, context); - } -}; - -TF_BUILTIN(Add, AddStubAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TVARIABLE(Object, var_left, CAST(Parameter(Descriptor::kLeft))); - TVARIABLE(Object, var_right, CAST(Parameter(Descriptor::kRight))); - - // Shared entry for floating point addition. - Label do_double_add(this); - TVARIABLE(Float64T, var_left_double); - TVARIABLE(Float64T, var_right_double); - - // We might need to loop several times due to ToPrimitive, ToString and/or - // ToNumeric conversions. - Label loop(this, {&var_left, &var_right}), - string_add_convert_left(this, Label::kDeferred), - string_add_convert_right(this, Label::kDeferred), - do_bigint_add(this, Label::kDeferred); - Goto(&loop); - BIND(&loop); - { - TNode<Object> left = var_left.value(); - TNode<Object> right = var_right.value(); - - Label if_left_smi(this), if_left_heapobject(this); - Branch(TaggedIsSmi(left), &if_left_smi, &if_left_heapobject); - - BIND(&if_left_smi); - { - Label if_right_smi(this), if_right_heapobject(this); - Branch(TaggedIsSmi(right), &if_right_smi, &if_right_heapobject); - - BIND(&if_right_smi); - { - Label if_overflow(this); - TNode<Smi> left_smi = CAST(left); - TNode<Smi> right_smi = CAST(right); - TNode<Smi> result = TrySmiAdd(left_smi, right_smi, &if_overflow); - Return(result); - - BIND(&if_overflow); - { - var_left_double = SmiToFloat64(left_smi); - var_right_double = SmiToFloat64(right_smi); - Goto(&do_double_add); - } - } // if_right_smi - - BIND(&if_right_heapobject); - { - TNode<HeapObject> right_heap_object = CAST(right); - TNode<Map> right_map = LoadMap(right_heap_object); - - Label if_right_not_number(this, Label::kDeferred); - GotoIfNot(IsHeapNumberMap(right_map), &if_right_not_number); - - // {right} is a HeapNumber. - var_left_double = SmiToFloat64(CAST(left)); - var_right_double = LoadHeapNumberValue(right_heap_object); - Goto(&do_double_add); - - BIND(&if_right_not_number); - { - TNode<Uint16T> right_instance_type = LoadMapInstanceType(right_map); - GotoIf(IsStringInstanceType(right_instance_type), - &string_add_convert_left); - GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add); - ConvertAndLoop(&var_right, right_instance_type, &loop, context); - } - } // if_right_heapobject - } // if_left_smi - - BIND(&if_left_heapobject); - { - TNode<HeapObject> left_heap_object = CAST(left); - TNode<Map> left_map = LoadMap(left_heap_object); - Label if_right_smi(this), if_right_heapobject(this); - Branch(TaggedIsSmi(right), &if_right_smi, &if_right_heapobject); - - BIND(&if_right_smi); - { - Label if_left_not_number(this, Label::kDeferred); - GotoIfNot(IsHeapNumberMap(left_map), &if_left_not_number); - - // {left} is a HeapNumber, {right} is a Smi. - var_left_double = LoadHeapNumberValue(left_heap_object); - var_right_double = SmiToFloat64(CAST(right)); - Goto(&do_double_add); - - BIND(&if_left_not_number); - { - TNode<Uint16T> left_instance_type = LoadMapInstanceType(left_map); - GotoIf(IsStringInstanceType(left_instance_type), - &string_add_convert_right); - GotoIf(IsBigIntInstanceType(left_instance_type), &do_bigint_add); - // {left} is neither a Numeric nor a String, and {right} is a Smi. - ConvertAndLoop(&var_left, left_instance_type, &loop, context); - } - } // if_right_smi - - BIND(&if_right_heapobject); - { - TNode<HeapObject> right_heap_object = CAST(right); - TNode<Map> right_map = LoadMap(right_heap_object); - - Label if_left_number(this), if_left_not_number(this, Label::kDeferred); - Branch(IsHeapNumberMap(left_map), &if_left_number, &if_left_not_number); - - BIND(&if_left_number); - { - Label if_right_not_number(this, Label::kDeferred); - GotoIfNot(IsHeapNumberMap(right_map), &if_right_not_number); - - // Both {left} and {right} are HeapNumbers. - var_left_double = LoadHeapNumberValue(CAST(left)); - var_right_double = LoadHeapNumberValue(right_heap_object); - Goto(&do_double_add); - - BIND(&if_right_not_number); - { - TNode<Uint16T> right_instance_type = LoadMapInstanceType(right_map); - GotoIf(IsStringInstanceType(right_instance_type), - &string_add_convert_left); - GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add); - // {left} is a HeapNumber, {right} is neither Number nor String. - ConvertAndLoop(&var_right, right_instance_type, &loop, context); - } - } // if_left_number - - BIND(&if_left_not_number); - { - Label if_left_bigint(this); - TNode<Uint16T> left_instance_type = LoadMapInstanceType(left_map); - GotoIf(IsStringInstanceType(left_instance_type), - &string_add_convert_right); - TNode<Uint16T> right_instance_type = LoadMapInstanceType(right_map); - GotoIf(IsStringInstanceType(right_instance_type), - &string_add_convert_left); - GotoIf(IsBigIntInstanceType(left_instance_type), &if_left_bigint); - Label if_left_not_receiver(this, Label::kDeferred); - Label if_right_not_receiver(this, Label::kDeferred); - GotoIfNot(IsJSReceiverInstanceType(left_instance_type), - &if_left_not_receiver); - // {left} is a JSReceiver, convert it first. - var_left = ConvertReceiver(CAST(var_left.value()), context); - Goto(&loop); - - BIND(&if_left_bigint); - { - // {right} is a HeapObject, but not a String. Jump to - // {do_bigint_add} if {right} is already a Numeric. - GotoIf(IsBigIntInstanceType(right_instance_type), &do_bigint_add); - GotoIf(IsHeapNumberMap(right_map), &do_bigint_add); - ConvertAndLoop(&var_right, right_instance_type, &loop, context); - } - - BIND(&if_left_not_receiver); - GotoIfNot(IsJSReceiverInstanceType(right_instance_type), - &if_right_not_receiver); - // {left} is a Primitive, but {right} is a JSReceiver, so convert - // {right} with priority. - var_right = ConvertReceiver(CAST(var_right.value()), context); - Goto(&loop); - - BIND(&if_right_not_receiver); - // Neither {left} nor {right} are JSReceivers. - ConvertNonReceiverAndLoop(&var_left, &loop, context); - } - } // if_right_heapobject - } // if_left_heapobject - } - BIND(&string_add_convert_left); - { - // Convert {left} to a String and concatenate it with the String {right}. - TailCallBuiltin(Builtins::kStringAddConvertLeft, context, var_left.value(), - var_right.value()); - } - - BIND(&string_add_convert_right); - { - // Convert {right} to a String and concatenate it with the String {left}. - TailCallBuiltin(Builtins::kStringAddConvertRight, context, var_left.value(), - var_right.value()); - } - - BIND(&do_bigint_add); - { - TailCallBuiltin(Builtins::kBigIntAdd, context, var_left.value(), - var_right.value()); - } - - BIND(&do_double_add); - { - TNode<Float64T> value = - Float64Add(var_left_double.value(), var_right_double.value()); - Return(AllocateHeapNumberWithValue(value)); - } -} - -template <typename Descriptor> -void NumberBuiltinsAssembler::UnaryOp(TVariable<Object>* var_input, - Label* do_smi, Label* do_double, - TVariable<Float64T>* var_input_double, - Label* do_bigint) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - *var_input = CAST(Parameter(Descriptor::kValue)); - - // We might need to loop for ToNumeric conversion. - Label loop(this, {var_input}); - Goto(&loop); - BIND(&loop); - TNode<Object> input = var_input->value(); - - Label not_number(this); - GotoIf(TaggedIsSmi(input), do_smi); - TNode<HeapObject> input_heap_object = CAST(input); - GotoIfNot(IsHeapNumber(input_heap_object), ¬_number); - if (var_input_double != nullptr) { - *var_input_double = LoadHeapNumberValue(input_heap_object); - } - Goto(do_double); - - BIND(¬_number); - GotoIf(IsBigInt(input_heap_object), do_bigint); - *var_input = CallBuiltin(Builtins::kNonNumberToNumeric, context, input); - Goto(&loop); -} - -template <typename Descriptor> -void NumberBuiltinsAssembler::BinaryOp(Label* smis, TVariable<Object>* var_left, - TVariable<Object>* var_right, - Label* doubles, - TVariable<Float64T>* var_left_double, - TVariable<Float64T>* var_right_double, - Label* bigints) { - DCHECK_EQ(var_left_double == nullptr, var_right_double == nullptr); - - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - *var_left = CAST(Parameter(Descriptor::kLeft)); - *var_right = CAST(Parameter(Descriptor::kRight)); - - // We might need to loop for ToNumeric conversions. - Label loop(this, {var_left, var_right}); - Goto(&loop); - BIND(&loop); - - Label left_not_smi(this), right_not_smi(this); - Label left_not_number(this), right_not_number(this); - GotoIfNot(TaggedIsSmi(var_left->value()), &left_not_smi); - GotoIf(TaggedIsSmi(var_right->value()), smis); - - // At this point, var_left is a Smi but var_right is not. - TNode<Smi> var_left_smi = CAST(var_left->value()); - TNode<HeapObject> var_right_heap_object = CAST(var_right->value()); - GotoIfNot(IsHeapNumber(var_right_heap_object), &right_not_number); - if (var_left_double != nullptr) { - *var_left_double = SmiToFloat64(var_left_smi); - *var_right_double = LoadHeapNumberValue(var_right_heap_object); - } - Goto(doubles); - - BIND(&left_not_smi); - { - TNode<HeapObject> var_left_heap_object = CAST(var_left->value()); - GotoIfNot(IsHeapNumber(var_left_heap_object), &left_not_number); - GotoIfNot(TaggedIsSmi(var_right->value()), &right_not_smi); - - // At this point, var_left is a HeapNumber and var_right is a Smi. - if (var_left_double != nullptr) { - *var_left_double = LoadHeapNumberValue(var_left_heap_object); - *var_right_double = SmiToFloat64(CAST(var_right->value())); - } - Goto(doubles); - } - - BIND(&right_not_smi); - { - TNode<HeapObject> var_right_heap_object = CAST(var_right->value()); - GotoIfNot(IsHeapNumber(var_right_heap_object), &right_not_number); - if (var_left_double != nullptr) { - *var_left_double = LoadHeapNumberValue(CAST(var_left->value())); - *var_right_double = LoadHeapNumberValue(var_right_heap_object); - } - Goto(doubles); - } - - BIND(&left_not_number); - { - Label left_bigint(this); - GotoIf(IsBigInt(CAST(var_left->value())), &left_bigint); - *var_left = - CallBuiltin(Builtins::kNonNumberToNumeric, context, var_left->value()); - Goto(&loop); - - BIND(&left_bigint); - { - // Jump to {bigints} if {var_right} is already a Numeric. - GotoIf(TaggedIsSmi(var_right->value()), bigints); - TNode<HeapObject> var_right_heap_object = CAST(var_right->value()); - GotoIf(IsBigInt(var_right_heap_object), bigints); - GotoIf(IsHeapNumber(var_right_heap_object), bigints); - *var_right = CallBuiltin(Builtins::kNonNumberToNumeric, context, - var_right->value()); - Goto(&loop); - } - } - - BIND(&right_not_number); - { - GotoIf(IsBigInt(CAST(var_right->value())), bigints); - *var_right = - CallBuiltin(Builtins::kNonNumberToNumeric, context, var_right->value()); - Goto(&loop); - } -} - -TF_BUILTIN(Subtract, NumberBuiltinsAssembler) { - TVARIABLE(Object, var_left); - TVARIABLE(Object, var_right); - TVARIABLE(Float64T, var_left_double); - TVARIABLE(Float64T, var_right_double); - Label do_smi_sub(this), do_double_sub(this), do_bigint_sub(this); - - BinaryOp<Descriptor>(&do_smi_sub, &var_left, &var_right, &do_double_sub, - &var_left_double, &var_right_double, &do_bigint_sub); - - BIND(&do_smi_sub); - { - Label if_overflow(this); - TNode<Smi> var_left_smi = CAST(var_left.value()); - TNode<Smi> var_right_smi = CAST(var_right.value()); - TNode<Smi> result = TrySmiSub(var_left_smi, var_right_smi, &if_overflow); - Return(result); - - BIND(&if_overflow); - { - var_left_double = SmiToFloat64(var_left_smi); - var_right_double = SmiToFloat64(var_right_smi); - Goto(&do_double_sub); - } - } - - BIND(&do_double_sub); - { - TNode<Float64T> value = - Float64Sub(var_left_double.value(), var_right_double.value()); - Return(AllocateHeapNumberWithValue(value)); - } - - BIND(&do_bigint_sub); - { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TailCallBuiltin(Builtins::kBigIntSubtract, context, var_left.value(), - var_right.value()); - } -} - -TF_BUILTIN(BitwiseNot, NumberBuiltinsAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TVARIABLE(Object, var_input); - Label do_number(this), do_bigint(this); - - UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint); - - BIND(&do_number); - { - TailCallBuiltin(Builtins::kBitwiseXor, context, var_input.value(), - SmiConstant(-1)); - } - - BIND(&do_bigint); - { - Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(), - SmiConstant(Operation::kBitwiseNot))); - } -} - -TF_BUILTIN(Decrement, NumberBuiltinsAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TVARIABLE(Object, var_input); - Label do_number(this), do_bigint(this); - - UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint); - - BIND(&do_number); - { - TailCallBuiltin(Builtins::kSubtract, context, var_input.value(), - SmiConstant(1)); - } - - BIND(&do_bigint); - { - Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(), - SmiConstant(Operation::kDecrement))); - } -} - -TF_BUILTIN(Increment, NumberBuiltinsAssembler) { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - TVARIABLE(Object, var_input); - Label do_number(this), do_bigint(this); - - UnaryOp<Descriptor>(&var_input, &do_number, &do_number, nullptr, &do_bigint); - - BIND(&do_number); - { - TailCallBuiltin(Builtins::kAdd, context, var_input.value(), SmiConstant(1)); - } - - BIND(&do_bigint); - { - Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(), - SmiConstant(Operation::kIncrement))); - } -} - -TF_BUILTIN(Negate, NumberBuiltinsAssembler) { - TVARIABLE(Object, var_input); - TVARIABLE(Float64T, var_input_double); - Label do_smi(this), do_double(this), do_bigint(this); - - UnaryOp<Descriptor>(&var_input, &do_smi, &do_double, &var_input_double, - &do_bigint); - - BIND(&do_smi); - { Return(SmiMul(CAST(var_input.value()), SmiConstant(-1))); } - - BIND(&do_double); - { - TNode<Float64T> value = - Float64Mul(var_input_double.value(), Float64Constant(-1)); - Return(AllocateHeapNumberWithValue(value)); - } - - BIND(&do_bigint); - { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(), - SmiConstant(Operation::kNegate))); - } -} - -TF_BUILTIN(Multiply, NumberBuiltinsAssembler) { - TVARIABLE(Object, var_left); - TVARIABLE(Object, var_right); - TVARIABLE(Float64T, var_left_double); - TVARIABLE(Float64T, var_right_double); - Label do_smi_mul(this), do_double_mul(this), do_bigint_mul(this); - - BinaryOp<Descriptor>(&do_smi_mul, &var_left, &var_right, &do_double_mul, - &var_left_double, &var_right_double, &do_bigint_mul); - - BIND(&do_smi_mul); - // The result is not necessarily a smi, in case of overflow. - Return(SmiMul(CAST(var_left.value()), CAST(var_right.value()))); - - BIND(&do_double_mul); - TNode<Float64T> value = - Float64Mul(var_left_double.value(), var_right_double.value()); - Return(AllocateHeapNumberWithValue(value)); - - BIND(&do_bigint_mul); - { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(), - var_right.value(), SmiConstant(Operation::kMultiply))); - } -} - -TF_BUILTIN(Divide, NumberBuiltinsAssembler) { - TVARIABLE(Object, var_left); - TVARIABLE(Object, var_right); - TVARIABLE(Float64T, var_left_double); - TVARIABLE(Float64T, var_right_double); - Label do_smi_div(this), do_double_div(this), do_bigint_div(this); - - BinaryOp<Descriptor>(&do_smi_div, &var_left, &var_right, &do_double_div, - &var_left_double, &var_right_double, &do_bigint_div); - - BIND(&do_smi_div); - { - // TODO(jkummerow): Consider just always doing a double division. - Label bailout(this); - TNode<Smi> dividend = CAST(var_left.value()); - TNode<Smi> divisor = CAST(var_right.value()); - - // Do floating point division if {divisor} is zero. - GotoIf(SmiEqual(divisor, SmiConstant(0)), &bailout); - - // Do floating point division if {dividend} is zero and {divisor} is - // negative. - Label dividend_is_zero(this), dividend_is_not_zero(this); - Branch(SmiEqual(dividend, SmiConstant(0)), ÷nd_is_zero, - ÷nd_is_not_zero); - - BIND(÷nd_is_zero); - { - GotoIf(SmiLessThan(divisor, SmiConstant(0)), &bailout); - Goto(÷nd_is_not_zero); - } - BIND(÷nd_is_not_zero); - - TNode<Int32T> untagged_divisor = SmiToInt32(divisor); - TNode<Int32T> untagged_dividend = SmiToInt32(dividend); - - // Do floating point division if {dividend} is kMinInt (or kMinInt - 1 - // if the Smi size is 31) and {divisor} is -1. - Label divisor_is_minus_one(this), divisor_is_not_minus_one(this); - Branch(Word32Equal(untagged_divisor, Int32Constant(-1)), - &divisor_is_minus_one, &divisor_is_not_minus_one); - - BIND(&divisor_is_minus_one); - { - GotoIf(Word32Equal( - untagged_dividend, - Int32Constant(kSmiValueSize == 32 ? kMinInt : (kMinInt >> 1))), - &bailout); - Goto(&divisor_is_not_minus_one); - } - BIND(&divisor_is_not_minus_one); - - // TODO(epertoso): consider adding a machine instruction that returns - // both the result and the remainder. - TNode<Int32T> untagged_result = - Int32Div(untagged_dividend, untagged_divisor); - TNode<Int32T> truncated = Int32Mul(untagged_result, untagged_divisor); - // Do floating point division if the remainder is not 0. - GotoIf(Word32NotEqual(untagged_dividend, truncated), &bailout); - Return(SmiFromInt32(untagged_result)); - - // Bailout: convert {dividend} and {divisor} to double and do double - // division. - BIND(&bailout); - { - var_left_double = SmiToFloat64(dividend); - var_right_double = SmiToFloat64(divisor); - Goto(&do_double_div); - } - } - - BIND(&do_double_div); - { - TNode<Float64T> value = - Float64Div(var_left_double.value(), var_right_double.value()); - Return(AllocateHeapNumberWithValue(value)); - } - - BIND(&do_bigint_div); - { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(), - var_right.value(), SmiConstant(Operation::kDivide))); - } -} - -TF_BUILTIN(Modulus, NumberBuiltinsAssembler) { - TVARIABLE(Object, var_left); - TVARIABLE(Object, var_right); - TVARIABLE(Float64T, var_left_double); - TVARIABLE(Float64T, var_right_double); - Label do_smi_mod(this), do_double_mod(this), do_bigint_mod(this); - - BinaryOp<Descriptor>(&do_smi_mod, &var_left, &var_right, &do_double_mod, - &var_left_double, &var_right_double, &do_bigint_mod); - - BIND(&do_smi_mod); - Return(SmiMod(CAST(var_left.value()), CAST(var_right.value()))); - - BIND(&do_double_mod); - TNode<Float64T> value = - Float64Mod(var_left_double.value(), var_right_double.value()); - Return(AllocateHeapNumberWithValue(value)); - - BIND(&do_bigint_mod); - { - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(), - var_right.value(), SmiConstant(Operation::kModulus))); - } -} - -TF_BUILTIN(Exponentiate, NumberBuiltinsAssembler) { - TVARIABLE(Object, var_left); - TVARIABLE(Object, var_right); - Label do_number_exp(this), do_bigint_exp(this); - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - - BinaryOp<Descriptor>(&do_number_exp, &var_left, &var_right, &do_number_exp, - nullptr, nullptr, &do_bigint_exp); - - BIND(&do_number_exp); - { Return(MathPowImpl(context, var_left.value(), var_right.value())); } - - BIND(&do_bigint_exp); - Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(), - var_right.value(), SmiConstant(Operation::kExponentiate))); -} - -TF_BUILTIN(ShiftLeft, NumberBuiltinsAssembler) { - EmitBitwiseOp<Descriptor>(Operation::kShiftLeft); -} - -TF_BUILTIN(ShiftRight, NumberBuiltinsAssembler) { - EmitBitwiseOp<Descriptor>(Operation::kShiftRight); -} - -TF_BUILTIN(ShiftRightLogical, NumberBuiltinsAssembler) { - EmitBitwiseOp<Descriptor>(Operation::kShiftRightLogical); -} - -TF_BUILTIN(BitwiseAnd, NumberBuiltinsAssembler) { - EmitBitwiseOp<Descriptor>(Operation::kBitwiseAnd); -} - -TF_BUILTIN(BitwiseOr, NumberBuiltinsAssembler) { - EmitBitwiseOp<Descriptor>(Operation::kBitwiseOr); -} - -TF_BUILTIN(BitwiseXor, NumberBuiltinsAssembler) { - EmitBitwiseOp<Descriptor>(Operation::kBitwiseXor); -} - -TF_BUILTIN(LessThan, NumberBuiltinsAssembler) { - RelationalComparisonBuiltin<Descriptor>(Operation::kLessThan); -} - -TF_BUILTIN(LessThanOrEqual, NumberBuiltinsAssembler) { - RelationalComparisonBuiltin<Descriptor>(Operation::kLessThanOrEqual); -} - -TF_BUILTIN(GreaterThan, NumberBuiltinsAssembler) { - RelationalComparisonBuiltin<Descriptor>(Operation::kGreaterThan); -} - -TF_BUILTIN(GreaterThanOrEqual, NumberBuiltinsAssembler) { - RelationalComparisonBuiltin<Descriptor>(Operation::kGreaterThanOrEqual); -} - -TF_BUILTIN(Equal, CodeStubAssembler) { +TF_BUILTIN(StrictEqual_WithFeedback, CodeStubAssembler) { TNode<Object> lhs = CAST(Parameter(Descriptor::kLeft)); TNode<Object> rhs = CAST(Parameter(Descriptor::kRight)); - TNode<Context> context = CAST(Parameter(Descriptor::kContext)); + TNode<HeapObject> maybe_feedback_vector = + CAST(Parameter(Descriptor::kMaybeFeedbackVector)); + TNode<UintPtrT> slot = UncheckedCast<UintPtrT>(Parameter(Descriptor::kSlot)); - Return(Equal(lhs, rhs, context)); -} + TVARIABLE(Smi, var_type_feedback); + TNode<Oddball> result = StrictEqual(lhs, rhs, &var_type_feedback); + UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector, slot); -TF_BUILTIN(StrictEqual, CodeStubAssembler) { - TNode<Object> lhs = CAST(Parameter(Descriptor::kLeft)); - TNode<Object> rhs = CAST(Parameter(Descriptor::kRight)); - - Return(StrictEqual(lhs, rhs)); + Return(result); } } // namespace internal |