| // Copyright 2016 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/ic/binary-op-assembler.h" |
| |
| #include "src/common/globals.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| namespace { |
| |
| inline bool IsBigInt64OpSupported(BinaryOpAssembler* assembler, Operation op) { |
| return assembler->Is64() && op != Operation::kExponentiate && |
| op != Operation::kShiftLeft && op != Operation::kShiftRight && |
| op != Operation::kShiftRightLogical; |
| } |
| |
| } // namespace |
| |
| TNode<Object> BinaryOpAssembler::Generate_AddWithFeedback( |
| const LazyNode<Context>& context, TNode<Object> lhs, TNode<Object> rhs, |
| TNode<UintPtrT> slot_id, const LazyNode<HeapObject>& maybe_feedback_vector, |
| UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) { |
| // Shared entry for floating point addition. |
| Label do_fadd(this), if_lhsisnotnumber(this, Label::kDeferred), |
| check_rhsisoddball(this, Label::kDeferred), |
| call_with_oddball_feedback(this), call_with_any_feedback(this), |
| call_add_stub(this), end(this), bigint(this, Label::kDeferred), |
| bigint64(this); |
| TVARIABLE(Float64T, var_fadd_lhs); |
| TVARIABLE(Float64T, var_fadd_rhs); |
| TVARIABLE(Smi, var_type_feedback); |
| TVARIABLE(Object, var_result); |
| |
| // Check if the {lhs} is a Smi or a HeapObject. |
| Label if_lhsissmi(this); |
| // If rhs is known to be an Smi we want to fast path Smi operation. This is |
| // for AddSmi operation. For the normal Add operation, we want to fast path |
| // both Smi and Number operations, so this path should not be marked as |
| // Deferred. |
| Label if_lhsisnotsmi(this, |
| rhs_known_smi ? Label::kDeferred : Label::kNonDeferred); |
| Branch(TaggedIsNotSmi(lhs), &if_lhsisnotsmi, &if_lhsissmi); |
| |
| BIND(&if_lhsissmi); |
| { |
| Comment("lhs is Smi"); |
| TNode<Smi> lhs_smi = CAST(lhs); |
| if (!rhs_known_smi) { |
| // Check if the {rhs} is also a Smi. |
| Label if_rhsissmi(this), if_rhsisnotsmi(this); |
| Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); |
| |
| BIND(&if_rhsisnotsmi); |
| { |
| // Check if the {rhs} is a HeapNumber. |
| TNode<HeapObject> rhs_heap_object = CAST(rhs); |
| GotoIfNot(IsHeapNumber(rhs_heap_object), &check_rhsisoddball); |
| |
| var_fadd_lhs = SmiToFloat64(lhs_smi); |
| var_fadd_rhs = LoadHeapNumberValue(rhs_heap_object); |
| Goto(&do_fadd); |
| } |
| |
| BIND(&if_rhsissmi); |
| } |
| |
| { |
| Comment("perform smi operation"); |
| // If rhs is known to be an Smi we want to fast path Smi operation. This |
| // is for AddSmi operation. For the normal Add operation, we want to fast |
| // path both Smi and Number operations, so this path should not be marked |
| // as Deferred. |
| TNode<Smi> rhs_smi = CAST(rhs); |
| Label if_overflow(this, |
| rhs_known_smi ? Label::kDeferred : Label::kNonDeferred); |
| TNode<Smi> smi_result = TrySmiAdd(lhs_smi, rhs_smi, &if_overflow); |
| // Not overflowed. |
| { |
| var_type_feedback = SmiConstant(BinaryOperationFeedback::kSignedSmall); |
| UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), |
| slot_id, update_feedback_mode); |
| var_result = smi_result; |
| Goto(&end); |
| } |
| |
| BIND(&if_overflow); |
| { |
| var_fadd_lhs = SmiToFloat64(lhs_smi); |
| var_fadd_rhs = SmiToFloat64(rhs_smi); |
| Goto(&do_fadd); |
| } |
| } |
| } |
| |
| BIND(&if_lhsisnotsmi); |
| { |
| // Check if {lhs} is a HeapNumber. |
| TNode<HeapObject> lhs_heap_object = CAST(lhs); |
| GotoIfNot(IsHeapNumber(lhs_heap_object), &if_lhsisnotnumber); |
| |
| if (!rhs_known_smi) { |
| // Check if the {rhs} is Smi. |
| Label if_rhsissmi(this), if_rhsisnotsmi(this); |
| Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); |
| |
| BIND(&if_rhsisnotsmi); |
| { |
| // Check if the {rhs} is a HeapNumber. |
| TNode<HeapObject> rhs_heap_object = CAST(rhs); |
| GotoIfNot(IsHeapNumber(rhs_heap_object), &check_rhsisoddball); |
| |
| var_fadd_lhs = LoadHeapNumberValue(lhs_heap_object); |
| var_fadd_rhs = LoadHeapNumberValue(rhs_heap_object); |
| Goto(&do_fadd); |
| } |
| |
| BIND(&if_rhsissmi); |
| } |
| { |
| var_fadd_lhs = LoadHeapNumberValue(lhs_heap_object); |
| var_fadd_rhs = SmiToFloat64(CAST(rhs)); |
| Goto(&do_fadd); |
| } |
| } |
| |
| BIND(&do_fadd); |
| { |
| var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumber); |
| UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id, |
| update_feedback_mode); |
| TNode<Float64T> value = |
| Float64Add(var_fadd_lhs.value(), var_fadd_rhs.value()); |
| TNode<HeapNumber> result = AllocateHeapNumberWithValue(value); |
| var_result = result; |
| Goto(&end); |
| } |
| |
| BIND(&if_lhsisnotnumber); |
| { |
| // No checks on rhs are done yet. We just know lhs is not a number or Smi. |
| Label if_lhsisoddball(this), if_lhsisnotoddball(this); |
| TNode<Uint16T> lhs_instance_type = LoadInstanceType(CAST(lhs)); |
| TNode<BoolT> lhs_is_oddball = |
| InstanceTypeEqual(lhs_instance_type, ODDBALL_TYPE); |
| Branch(lhs_is_oddball, &if_lhsisoddball, &if_lhsisnotoddball); |
| |
| BIND(&if_lhsisoddball); |
| { |
| GotoIf(TaggedIsSmi(rhs), &call_with_oddball_feedback); |
| |
| // Check if {rhs} is a HeapNumber. |
| Branch(IsHeapNumber(CAST(rhs)), &call_with_oddball_feedback, |
| &check_rhsisoddball); |
| } |
| |
| BIND(&if_lhsisnotoddball); |
| { |
| // Check if the {rhs} is a smi, and exit the string and bigint check early |
| // if it is. |
| GotoIf(TaggedIsSmi(rhs), &call_with_any_feedback); |
| TNode<HeapObject> rhs_heap_object = CAST(rhs); |
| |
| Label lhs_is_string(this), lhs_is_bigint(this); |
| GotoIf(IsStringInstanceType(lhs_instance_type), &lhs_is_string); |
| GotoIf(IsBigIntInstanceType(lhs_instance_type), &lhs_is_bigint); |
| Goto(&call_with_any_feedback); |
| |
| BIND(&lhs_is_bigint); |
| { |
| GotoIfNot(IsBigInt(rhs_heap_object), &call_with_any_feedback); |
| if (Is64()) { |
| GotoIfLargeBigInt(CAST(lhs), &bigint); |
| GotoIfLargeBigInt(CAST(rhs), &bigint); |
| Goto(&bigint64); |
| } else { |
| Goto(&bigint); |
| } |
| } |
| |
| BIND(&lhs_is_string); |
| { |
| TNode<Uint16T> rhs_instance_type = LoadInstanceType(rhs_heap_object); |
| |
| // Exit unless {rhs} is a string. Since {lhs} is a string we no longer |
| // need an Oddball check. |
| GotoIfNot(IsStringInstanceType(rhs_instance_type), |
| &call_with_any_feedback); |
| |
| var_type_feedback = SmiConstant(BinaryOperationFeedback::kString); |
| UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), |
| slot_id, update_feedback_mode); |
| var_result = |
| CallBuiltin(Builtin::kStringAdd_CheckNone, context(), lhs, rhs); |
| |
| Goto(&end); |
| } |
| } |
| } |
| |
| BIND(&check_rhsisoddball); |
| { |
| // Check if rhs is an oddball. At this point we know lhs is either a |
| // Smi or number or oddball and rhs is not a number or Smi. |
| TNode<Uint16T> rhs_instance_type = LoadInstanceType(CAST(rhs)); |
| TNode<BoolT> rhs_is_oddball = |
| InstanceTypeEqual(rhs_instance_type, ODDBALL_TYPE); |
| GotoIf(rhs_is_oddball, &call_with_oddball_feedback); |
| Goto(&call_with_any_feedback); |
| } |
| |
| if (Is64()) { |
| BIND(&bigint64); |
| { |
| // Both {lhs} and {rhs} are of BigInt type and can fit in 64-bit |
| // registers. |
| Label if_overflow(this); |
| TVARIABLE(UintPtrT, lhs_raw); |
| TVARIABLE(UintPtrT, rhs_raw); |
| BigIntToRawBytes(CAST(lhs), &lhs_raw, &lhs_raw); |
| BigIntToRawBytes(CAST(rhs), &rhs_raw, &rhs_raw); |
| var_result = BigIntFromInt64( |
| TryIntPtrAdd(UncheckedCast<IntPtrT>(lhs_raw.value()), |
| UncheckedCast<IntPtrT>(rhs_raw.value()), &if_overflow)); |
| |
| var_type_feedback = SmiConstant(BinaryOperationFeedback::kBigInt64); |
| UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), |
| slot_id, update_feedback_mode); |
| Goto(&end); |
| |
| BIND(&if_overflow); |
| Goto(&bigint); |
| } |
| } |
| |
| BIND(&bigint); |
| { |
| // Both {lhs} and {rhs} are of BigInt type. |
| Label bigint_too_big(this); |
| var_result = CallBuiltin(Builtin::kBigIntAddNoThrow, context(), lhs, rhs); |
| // Check for sentinel that signals BigIntTooBig exception. |
| GotoIf(TaggedIsSmi(var_result.value()), &bigint_too_big); |
| |
| var_type_feedback = SmiConstant(BinaryOperationFeedback::kBigInt); |
| UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id, |
| update_feedback_mode); |
| Goto(&end); |
| |
| BIND(&bigint_too_big); |
| { |
| // Update feedback to prevent deopt loop. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| maybe_feedback_vector(), slot_id, update_feedback_mode); |
| ThrowRangeError(context(), MessageTemplate::kBigIntTooBig); |
| } |
| } |
| |
| BIND(&call_with_oddball_feedback); |
| { |
| var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumberOrOddball); |
| Goto(&call_add_stub); |
| } |
| |
| BIND(&call_with_any_feedback); |
| { |
| var_type_feedback = SmiConstant(BinaryOperationFeedback::kAny); |
| Goto(&call_add_stub); |
| } |
| |
| BIND(&call_add_stub); |
| { |
| UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id, |
| update_feedback_mode); |
| var_result = CallBuiltin(Builtin::kAdd, context(), lhs, rhs); |
| Goto(&end); |
| } |
| |
| BIND(&end); |
| return var_result.value(); |
| } |
| |
| TNode<Object> BinaryOpAssembler::Generate_BinaryOperationWithFeedback( |
| const LazyNode<Context>& context, TNode<Object> lhs, TNode<Object> rhs, |
| TNode<UintPtrT> slot_id, const LazyNode<HeapObject>& maybe_feedback_vector, |
| const SmiOperation& smiOperation, const FloatOperation& floatOperation, |
| Operation op, UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) { |
| Label do_float_operation(this), end(this), call_stub(this), |
| check_rhsisoddball(this, Label::kDeferred), call_with_any_feedback(this), |
| if_lhsisnotnumber(this, Label::kDeferred), |
| if_both_bigint(this, Label::kDeferred), if_both_bigint64(this); |
| TVARIABLE(Float64T, var_float_lhs); |
| TVARIABLE(Float64T, var_float_rhs); |
| TVARIABLE(Smi, var_type_feedback); |
| TVARIABLE(Object, var_result); |
| |
| Label if_lhsissmi(this); |
| // If rhs is known to be an Smi (in the SubSmi, MulSmi, DivSmi, ModSmi |
| // bytecode handlers) we want to fast path Smi operation. For the normal |
| // operation, we want to fast path both Smi and Number operations, so this |
| // path should not be marked as Deferred. |
| Label if_lhsisnotsmi(this, |
| rhs_known_smi ? Label::kDeferred : Label::kNonDeferred); |
| Branch(TaggedIsNotSmi(lhs), &if_lhsisnotsmi, &if_lhsissmi); |
| |
| // Check if the {lhs} is a Smi or a HeapObject. |
| BIND(&if_lhsissmi); |
| { |
| Comment("lhs is Smi"); |
| TNode<Smi> lhs_smi = CAST(lhs); |
| if (!rhs_known_smi) { |
| // Check if the {rhs} is also a Smi. |
| Label if_rhsissmi(this), if_rhsisnotsmi(this); |
| Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); |
| |
| BIND(&if_rhsisnotsmi); |
| { |
| // Check if {rhs} is a HeapNumber. |
| TNode<HeapObject> rhs_heap_object = CAST(rhs); |
| GotoIfNot(IsHeapNumber(rhs_heap_object), &check_rhsisoddball); |
| |
| // Perform a floating point operation. |
| var_float_lhs = SmiToFloat64(lhs_smi); |
| var_float_rhs = LoadHeapNumberValue(rhs_heap_object); |
| Goto(&do_float_operation); |
| } |
| |
| BIND(&if_rhsissmi); |
| } |
| |
| { |
| Comment("perform smi operation"); |
| var_result = smiOperation(lhs_smi, CAST(rhs), &var_type_feedback); |
| UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), |
| slot_id, update_feedback_mode); |
| Goto(&end); |
| } |
| } |
| |
| BIND(&if_lhsisnotsmi); |
| { |
| Comment("lhs is not Smi"); |
| // Check if the {lhs} is a HeapNumber. |
| TNode<HeapObject> lhs_heap_object = CAST(lhs); |
| GotoIfNot(IsHeapNumber(lhs_heap_object), &if_lhsisnotnumber); |
| |
| if (!rhs_known_smi) { |
| // Check if the {rhs} is a Smi. |
| Label if_rhsissmi(this), if_rhsisnotsmi(this); |
| Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); |
| |
| BIND(&if_rhsisnotsmi); |
| { |
| // Check if the {rhs} is a HeapNumber. |
| TNode<HeapObject> rhs_heap_object = CAST(rhs); |
| GotoIfNot(IsHeapNumber(rhs_heap_object), &check_rhsisoddball); |
| |
| // Perform a floating point operation. |
| var_float_lhs = LoadHeapNumberValue(lhs_heap_object); |
| var_float_rhs = LoadHeapNumberValue(rhs_heap_object); |
| Goto(&do_float_operation); |
| } |
| |
| BIND(&if_rhsissmi); |
| } |
| |
| { |
| // Perform floating point operation. |
| var_float_lhs = LoadHeapNumberValue(lhs_heap_object); |
| var_float_rhs = SmiToFloat64(CAST(rhs)); |
| Goto(&do_float_operation); |
| } |
| } |
| |
| BIND(&do_float_operation); |
| { |
| var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumber); |
| UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id, |
| update_feedback_mode); |
| TNode<Float64T> lhs_value = var_float_lhs.value(); |
| TNode<Float64T> rhs_value = var_float_rhs.value(); |
| TNode<Float64T> value = floatOperation(lhs_value, rhs_value); |
| var_result = AllocateHeapNumberWithValue(value); |
| Goto(&end); |
| } |
| |
| BIND(&if_lhsisnotnumber); |
| { |
| // No checks on rhs are done yet. We just know lhs is not a number or Smi. |
| Label if_left_bigint(this), if_left_oddball(this); |
| TNode<Uint16T> lhs_instance_type = LoadInstanceType(CAST(lhs)); |
| GotoIf(IsBigIntInstanceType(lhs_instance_type), &if_left_bigint); |
| TNode<BoolT> lhs_is_oddball = |
| InstanceTypeEqual(lhs_instance_type, ODDBALL_TYPE); |
| Branch(lhs_is_oddball, &if_left_oddball, &call_with_any_feedback); |
| |
| BIND(&if_left_oddball); |
| { |
| Label if_rhsissmi(this), if_rhsisnotsmi(this); |
| Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); |
| |
| BIND(&if_rhsissmi); |
| { |
| var_type_feedback = |
| SmiConstant(BinaryOperationFeedback::kNumberOrOddball); |
| Goto(&call_stub); |
| } |
| |
| BIND(&if_rhsisnotsmi); |
| { |
| // Check if {rhs} is a HeapNumber. |
| GotoIfNot(IsHeapNumber(CAST(rhs)), &check_rhsisoddball); |
| |
| var_type_feedback = |
| SmiConstant(BinaryOperationFeedback::kNumberOrOddball); |
| Goto(&call_stub); |
| } |
| } |
| |
| BIND(&if_left_bigint); |
| { |
| GotoIf(TaggedIsSmi(rhs), &call_with_any_feedback); |
| GotoIfNot(IsBigInt(CAST(rhs)), &call_with_any_feedback); |
| if (IsBigInt64OpSupported(this, op)) { |
| GotoIfLargeBigInt(CAST(lhs), &if_both_bigint); |
| GotoIfLargeBigInt(CAST(rhs), &if_both_bigint); |
| Goto(&if_both_bigint64); |
| } else { |
| Goto(&if_both_bigint); |
| } |
| } |
| } |
| |
| BIND(&check_rhsisoddball); |
| { |
| // Check if rhs is an oddball. At this point we know lhs is either a |
| // Smi or number or oddball and rhs is not a number or Smi. |
| TNode<Uint16T> rhs_instance_type = LoadInstanceType(CAST(rhs)); |
| TNode<BoolT> rhs_is_oddball = |
| InstanceTypeEqual(rhs_instance_type, ODDBALL_TYPE); |
| GotoIfNot(rhs_is_oddball, &call_with_any_feedback); |
| |
| var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumberOrOddball); |
| Goto(&call_stub); |
| } |
| |
| if (IsBigInt64OpSupported(this, op)) { |
| BIND(&if_both_bigint64); |
| var_type_feedback = SmiConstant(BinaryOperationFeedback::kBigInt64); |
| UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id, |
| update_feedback_mode); |
| |
| TVARIABLE(UintPtrT, lhs_raw); |
| TVARIABLE(UintPtrT, rhs_raw); |
| BigIntToRawBytes(CAST(lhs), &lhs_raw, &lhs_raw); |
| BigIntToRawBytes(CAST(rhs), &rhs_raw, &rhs_raw); |
| |
| switch (op) { |
| case Operation::kSubtract: { |
| var_result = BigIntFromInt64(TryIntPtrSub( |
| UncheckedCast<IntPtrT>(lhs_raw.value()), |
| UncheckedCast<IntPtrT>(rhs_raw.value()), &if_both_bigint)); |
| Goto(&end); |
| break; |
| } |
| case Operation::kMultiply: { |
| var_result = BigIntFromInt64(TryIntPtrMul( |
| UncheckedCast<IntPtrT>(lhs_raw.value()), |
| UncheckedCast<IntPtrT>(rhs_raw.value()), &if_both_bigint)); |
| Goto(&end); |
| break; |
| } |
| case Operation::kDivide: { |
| // No need to check overflow because INT_MIN is excluded |
| // from the range of small BigInts. |
| Label if_div_zero(this); |
| var_result = BigIntFromInt64(TryIntPtrDiv( |
| UncheckedCast<IntPtrT>(lhs_raw.value()), |
| UncheckedCast<IntPtrT>(rhs_raw.value()), &if_div_zero)); |
| Goto(&end); |
| |
| BIND(&if_div_zero); |
| { |
| // Update feedback to prevent deopt loop. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| maybe_feedback_vector(), slot_id, |
| update_feedback_mode); |
| ThrowRangeError(context(), MessageTemplate::kBigIntDivZero); |
| } |
| break; |
| } |
| case Operation::kModulus: { |
| Label if_div_zero(this); |
| var_result = BigIntFromInt64(TryIntPtrMod( |
| UncheckedCast<IntPtrT>(lhs_raw.value()), |
| UncheckedCast<IntPtrT>(rhs_raw.value()), &if_div_zero)); |
| Goto(&end); |
| |
| BIND(&if_div_zero); |
| { |
| // Update feedback to prevent deopt loop. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| maybe_feedback_vector(), slot_id, |
| update_feedback_mode); |
| ThrowRangeError(context(), MessageTemplate::kBigIntDivZero); |
| } |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| BIND(&if_both_bigint); |
| { |
| var_type_feedback = SmiConstant(BinaryOperationFeedback::kBigInt); |
| UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id, |
| update_feedback_mode); |
| switch (op) { |
| case Operation::kSubtract: { |
| var_result = |
| CallBuiltin(Builtin::kBigIntSubtractNoThrow, context(), lhs, rhs); |
| |
| // Check for sentinel that signals BigIntTooBig exception. |
| GotoIfNot(TaggedIsSmi(var_result.value()), &end); |
| |
| // Update feedback to prevent deopt loop. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| maybe_feedback_vector(), slot_id, update_feedback_mode); |
| ThrowRangeError(context(), MessageTemplate::kBigIntTooBig); |
| break; |
| } |
| case Operation::kMultiply: { |
| Label termination_requested(this, Label::kDeferred); |
| var_result = |
| CallBuiltin(Builtin::kBigIntMultiplyNoThrow, context(), lhs, rhs); |
| |
| GotoIfNot(TaggedIsSmi(var_result.value()), &end); |
| |
| // Check for sentinel that signals TerminationRequested exception. |
| GotoIf(TaggedEqual(var_result.value(), SmiConstant(1)), |
| &termination_requested); |
| |
| // Handles BigIntTooBig exception. |
| // Update feedback to prevent deopt loop. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| maybe_feedback_vector(), slot_id, update_feedback_mode); |
| ThrowRangeError(context(), MessageTemplate::kBigIntTooBig); |
| |
| BIND(&termination_requested); |
| TerminateExecution(context()); |
| break; |
| } |
| case Operation::kDivide: { |
| Label termination_requested(this, Label::kDeferred); |
| var_result = |
| CallBuiltin(Builtin::kBigIntDivideNoThrow, context(), lhs, rhs); |
| |
| GotoIfNot(TaggedIsSmi(var_result.value()), &end); |
| |
| // Check for sentinel that signals TerminationRequested exception. |
| GotoIf(TaggedEqual(var_result.value(), SmiConstant(1)), |
| &termination_requested); |
| |
| // Handles BigIntDivZero exception. |
| // Update feedback to prevent deopt loop. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| maybe_feedback_vector(), slot_id, update_feedback_mode); |
| ThrowRangeError(context(), MessageTemplate::kBigIntDivZero); |
| |
| BIND(&termination_requested); |
| TerminateExecution(context()); |
| break; |
| } |
| case Operation::kModulus: { |
| Label termination_requested(this, Label::kDeferred); |
| var_result = |
| CallBuiltin(Builtin::kBigIntModulusNoThrow, context(), lhs, rhs); |
| |
| GotoIfNot(TaggedIsSmi(var_result.value()), &end); |
| |
| // Check for sentinel that signals TerminationRequested exception. |
| GotoIf(TaggedEqual(var_result.value(), SmiConstant(1)), |
| &termination_requested); |
| |
| // Handles BigIntDivZero exception. |
| // Update feedback to prevent deopt loop. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| maybe_feedback_vector(), slot_id, update_feedback_mode); |
| ThrowRangeError(context(), MessageTemplate::kBigIntDivZero); |
| |
| BIND(&termination_requested); |
| TerminateExecution(context()); |
| break; |
| } |
| case Operation::kExponentiate: { |
| // TODO(panq): replace the runtime with builtin once it is implemented. |
| var_result = CallRuntime(Runtime::kBigIntBinaryOp, context(), lhs, rhs, |
| SmiConstant(op)); |
| Goto(&end); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| BIND(&call_with_any_feedback); |
| { |
| var_type_feedback = SmiConstant(BinaryOperationFeedback::kAny); |
| Goto(&call_stub); |
| } |
| |
| BIND(&call_stub); |
| { |
| UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector(), slot_id, |
| update_feedback_mode); |
| TNode<Object> result; |
| switch (op) { |
| case Operation::kSubtract: |
| result = CallBuiltin(Builtin::kSubtract, context(), lhs, rhs); |
| break; |
| case Operation::kMultiply: |
| result = CallBuiltin(Builtin::kMultiply, context(), lhs, rhs); |
| break; |
| case Operation::kDivide: |
| result = CallBuiltin(Builtin::kDivide, context(), lhs, rhs); |
| break; |
| case Operation::kModulus: |
| result = CallBuiltin(Builtin::kModulus, context(), lhs, rhs); |
| break; |
| case Operation::kExponentiate: |
| result = CallBuiltin(Builtin::kExponentiate, context(), lhs, rhs); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| var_result = result; |
| Goto(&end); |
| } |
| |
| BIND(&end); |
| return var_result.value(); |
| } |
| |
| TNode<Object> BinaryOpAssembler::Generate_SubtractWithFeedback( |
| const LazyNode<Context>& context, TNode<Object> lhs, TNode<Object> rhs, |
| TNode<UintPtrT> slot_id, const LazyNode<HeapObject>& maybe_feedback_vector, |
| UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) { |
| auto smiFunction = [=](TNode<Smi> lhs, TNode<Smi> rhs, |
| TVariable<Smi>* var_type_feedback) { |
| Label end(this); |
| TVARIABLE(Number, var_result); |
| // If rhs is known to be an Smi (for SubSmi) we want to fast path Smi |
| // operation. For the normal Sub operation, we want to fast path both |
| // Smi and Number operations, so this path should not be marked as Deferred. |
| Label if_overflow(this, |
| rhs_known_smi ? Label::kDeferred : Label::kNonDeferred); |
| var_result = TrySmiSub(lhs, rhs, &if_overflow); |
| *var_type_feedback = SmiConstant(BinaryOperationFeedback::kSignedSmall); |
| Goto(&end); |
| |
| BIND(&if_overflow); |
| { |
| *var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumber); |
| TNode<Float64T> value = Float64Sub(SmiToFloat64(lhs), SmiToFloat64(rhs)); |
| var_result = AllocateHeapNumberWithValue(value); |
| Goto(&end); |
| } |
| |
| BIND(&end); |
| return var_result.value(); |
| }; |
| auto floatFunction = [=](TNode<Float64T> lhs, TNode<Float64T> rhs) { |
| return Float64Sub(lhs, rhs); |
| }; |
| return Generate_BinaryOperationWithFeedback( |
| context, lhs, rhs, slot_id, maybe_feedback_vector, smiFunction, |
| floatFunction, Operation::kSubtract, update_feedback_mode, rhs_known_smi); |
| } |
| |
| TNode<Object> BinaryOpAssembler::Generate_MultiplyWithFeedback( |
| const LazyNode<Context>& context, TNode<Object> lhs, TNode<Object> rhs, |
| TNode<UintPtrT> slot_id, const LazyNode<HeapObject>& maybe_feedback_vector, |
| UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) { |
| auto smiFunction = [=](TNode<Smi> lhs, TNode<Smi> rhs, |
| TVariable<Smi>* var_type_feedback) { |
| TNode<Number> result = SmiMul(lhs, rhs); |
| *var_type_feedback = SelectSmiConstant( |
| TaggedIsSmi(result), BinaryOperationFeedback::kSignedSmall, |
| BinaryOperationFeedback::kNumber); |
| return result; |
| }; |
| auto floatFunction = [=](TNode<Float64T> lhs, TNode<Float64T> rhs) { |
| return Float64Mul(lhs, rhs); |
| }; |
| return Generate_BinaryOperationWithFeedback( |
| context, lhs, rhs, slot_id, maybe_feedback_vector, smiFunction, |
| floatFunction, Operation::kMultiply, update_feedback_mode, rhs_known_smi); |
| } |
| |
| TNode<Object> BinaryOpAssembler::Generate_DivideWithFeedback( |
| const LazyNode<Context>& context, TNode<Object> dividend, |
| TNode<Object> divisor, TNode<UintPtrT> slot_id, |
| const LazyNode<HeapObject>& maybe_feedback_vector, |
| UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) { |
| auto smiFunction = [=](TNode<Smi> lhs, TNode<Smi> rhs, |
| TVariable<Smi>* var_type_feedback) { |
| TVARIABLE(Object, var_result); |
| // If rhs is known to be an Smi (for DivSmi) we want to fast path Smi |
| // operation. For the normal Div operation, we want to fast path both |
| // Smi and Number operations, so this path should not be marked as Deferred. |
| Label bailout(this, rhs_known_smi ? Label::kDeferred : Label::kNonDeferred), |
| end(this); |
| var_result = TrySmiDiv(lhs, rhs, &bailout); |
| *var_type_feedback = SmiConstant(BinaryOperationFeedback::kSignedSmall); |
| Goto(&end); |
| |
| BIND(&bailout); |
| { |
| *var_type_feedback = |
| SmiConstant(BinaryOperationFeedback::kSignedSmallInputs); |
| TNode<Float64T> value = Float64Div(SmiToFloat64(lhs), SmiToFloat64(rhs)); |
| var_result = AllocateHeapNumberWithValue(value); |
| Goto(&end); |
| } |
| |
| BIND(&end); |
| return var_result.value(); |
| }; |
| auto floatFunction = [=](TNode<Float64T> lhs, TNode<Float64T> rhs) { |
| return Float64Div(lhs, rhs); |
| }; |
| return Generate_BinaryOperationWithFeedback( |
| context, dividend, divisor, slot_id, maybe_feedback_vector, smiFunction, |
| floatFunction, Operation::kDivide, update_feedback_mode, rhs_known_smi); |
| } |
| |
| TNode<Object> BinaryOpAssembler::Generate_ModulusWithFeedback( |
| const LazyNode<Context>& context, TNode<Object> dividend, |
| TNode<Object> divisor, TNode<UintPtrT> slot_id, |
| const LazyNode<HeapObject>& maybe_feedback_vector, |
| UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) { |
| auto smiFunction = [=](TNode<Smi> lhs, TNode<Smi> rhs, |
| TVariable<Smi>* var_type_feedback) { |
| TNode<Number> result = SmiMod(lhs, rhs); |
| *var_type_feedback = SelectSmiConstant( |
| TaggedIsSmi(result), BinaryOperationFeedback::kSignedSmall, |
| BinaryOperationFeedback::kNumber); |
| return result; |
| }; |
| auto floatFunction = [=](TNode<Float64T> lhs, TNode<Float64T> rhs) { |
| return Float64Mod(lhs, rhs); |
| }; |
| return Generate_BinaryOperationWithFeedback( |
| context, dividend, divisor, slot_id, maybe_feedback_vector, smiFunction, |
| floatFunction, Operation::kModulus, update_feedback_mode, rhs_known_smi); |
| } |
| |
| TNode<Object> BinaryOpAssembler::Generate_ExponentiateWithFeedback( |
| const LazyNode<Context>& context, TNode<Object> base, |
| TNode<Object> exponent, TNode<UintPtrT> slot_id, |
| const LazyNode<HeapObject>& maybe_feedback_vector, |
| UpdateFeedbackMode update_feedback_mode, bool rhs_known_smi) { |
| auto smiFunction = [=](TNode<Smi> base, TNode<Smi> exponent, |
| TVariable<Smi>* var_type_feedback) { |
| *var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumber); |
| return AllocateHeapNumberWithValue( |
| Float64Pow(SmiToFloat64(base), SmiToFloat64(exponent))); |
| }; |
| auto floatFunction = [=](TNode<Float64T> base, TNode<Float64T> exponent) { |
| return Float64Pow(base, exponent); |
| }; |
| return Generate_BinaryOperationWithFeedback( |
| context, base, exponent, slot_id, maybe_feedback_vector, smiFunction, |
| floatFunction, Operation::kExponentiate, update_feedback_mode, |
| rhs_known_smi); |
| } |
| |
| TNode<Object> BinaryOpAssembler::Generate_BitwiseBinaryOpWithOptionalFeedback( |
| Operation bitwise_op, TNode<Object> left, TNode<Object> right, |
| const LazyNode<Context>& context, TNode<UintPtrT>* slot, |
| const LazyNode<HeapObject>* maybe_feedback_vector, |
| UpdateFeedbackMode update_feedback_mode) { |
| TVARIABLE(Object, result); |
| TVARIABLE(Smi, var_left_feedback); |
| TVARIABLE(Smi, var_right_feedback); |
| TVARIABLE(Word32T, var_left_word32); |
| TVARIABLE(Word32T, var_right_word32); |
| TVARIABLE(BigInt, var_left_bigint); |
| Label done(this); |
| Label if_left_number(this), do_number_op(this); |
| Label if_left_bigint(this), if_left_bigint64(this); |
| Label if_left_number_right_bigint(this, Label::kDeferred); |
| |
| TaggedToWord32OrBigIntWithFeedback( |
| context(), left, &if_left_number, &var_left_word32, &if_left_bigint, |
| IsBigInt64OpSupported(this, bitwise_op) ? &if_left_bigint64 : nullptr, |
| &var_left_bigint, slot ? &var_left_feedback : nullptr); |
| |
| BIND(&if_left_number); |
| TaggedToWord32OrBigIntWithFeedback( |
| context(), right, &do_number_op, &var_right_word32, |
| &if_left_number_right_bigint, nullptr, nullptr, |
| slot ? &var_right_feedback : nullptr); |
| |
| BIND(&if_left_number_right_bigint); |
| { |
| if (slot) { |
| // Ensure that the feedback is updated before we throw. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| (*maybe_feedback_vector)(), *slot, update_feedback_mode); |
| } |
| ThrowTypeError(context(), MessageTemplate::kBigIntMixedTypes); |
| } |
| |
| BIND(&do_number_op); |
| { |
| result = BitwiseOp(var_left_word32.value(), var_right_word32.value(), |
| bitwise_op); |
| |
| if (slot) { |
| TNode<Smi> result_type = SelectSmiConstant( |
| TaggedIsSmi(result.value()), BinaryOperationFeedback::kSignedSmall, |
| BinaryOperationFeedback::kNumber); |
| TNode<Smi> input_feedback = |
| SmiOr(var_left_feedback.value(), var_right_feedback.value()); |
| TNode<Smi> feedback = SmiOr(result_type, input_feedback); |
| UpdateFeedback(feedback, (*maybe_feedback_vector)(), *slot, |
| update_feedback_mode); |
| } |
| Goto(&done); |
| } |
| |
| // BigInt cases. |
| { |
| TVARIABLE(BigInt, var_right_bigint); |
| Label if_both_bigint(this), if_both_bigint64(this); |
| Label if_bigint_mix(this, Label::kDeferred); |
| |
| BIND(&if_left_bigint); |
| TaggedToBigInt(context(), right, &if_bigint_mix, &if_both_bigint, nullptr, |
| &var_right_bigint, slot ? &var_right_feedback : nullptr); |
| |
| if (IsBigInt64OpSupported(this, bitwise_op)) { |
| BIND(&if_left_bigint64); |
| TaggedToBigInt(context(), right, &if_bigint_mix, &if_both_bigint, |
| &if_both_bigint64, &var_right_bigint, |
| slot ? &var_right_feedback : nullptr); |
| |
| BIND(&if_both_bigint64); |
| if (slot) { |
| // {feedback} is Any if {left} or {right} is non-number. |
| TNode<Smi> feedback = |
| SmiOr(var_left_feedback.value(), var_right_feedback.value()); |
| UpdateFeedback(feedback, (*maybe_feedback_vector)(), *slot, |
| update_feedback_mode); |
| } |
| |
| TVARIABLE(UintPtrT, left_raw); |
| TVARIABLE(UintPtrT, right_raw); |
| BigIntToRawBytes(var_left_bigint.value(), &left_raw, &left_raw); |
| BigIntToRawBytes(var_right_bigint.value(), &right_raw, &right_raw); |
| |
| switch (bitwise_op) { |
| case Operation::kBitwiseAnd: { |
| result = BigIntFromInt64(UncheckedCast<IntPtrT>( |
| WordAnd(left_raw.value(), right_raw.value()))); |
| Goto(&done); |
| break; |
| } |
| case Operation::kBitwiseOr: { |
| result = BigIntFromInt64(UncheckedCast<IntPtrT>( |
| WordOr(left_raw.value(), right_raw.value()))); |
| Goto(&done); |
| break; |
| } |
| case Operation::kBitwiseXor: { |
| result = BigIntFromInt64(UncheckedCast<IntPtrT>( |
| WordXor(left_raw.value(), right_raw.value()))); |
| Goto(&done); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| BIND(&if_both_bigint); |
| { |
| if (slot) { |
| // Ensure that the feedback is updated even if the runtime call below |
| // would throw. |
| TNode<Smi> feedback = |
| SmiOr(var_left_feedback.value(), var_right_feedback.value()); |
| UpdateFeedback(feedback, (*maybe_feedback_vector)(), *slot, |
| update_feedback_mode); |
| } |
| |
| switch (bitwise_op) { |
| case Operation::kBitwiseAnd: { |
| result = |
| CallBuiltin(Builtin::kBigIntBitwiseAndNoThrow, context(), |
| var_left_bigint.value(), var_right_bigint.value()); |
| // Check for sentinel that signals BigIntTooBig exception. |
| GotoIfNot(TaggedIsSmi(result.value()), &done); |
| |
| if (slot) { |
| // Update feedback to prevent deopt loop. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| (*maybe_feedback_vector)(), *slot, |
| update_feedback_mode); |
| } |
| ThrowRangeError(context(), MessageTemplate::kBigIntTooBig); |
| break; |
| } |
| case Operation::kBitwiseOr: { |
| result = |
| CallBuiltin(Builtin::kBigIntBitwiseOrNoThrow, context(), |
| var_left_bigint.value(), var_right_bigint.value()); |
| // Check for sentinel that signals BigIntTooBig exception. |
| GotoIfNot(TaggedIsSmi(result.value()), &done); |
| |
| if (slot) { |
| // Update feedback to prevent deopt loop. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| (*maybe_feedback_vector)(), *slot, |
| update_feedback_mode); |
| } |
| ThrowRangeError(context(), MessageTemplate::kBigIntTooBig); |
| break; |
| } |
| case Operation::kBitwiseXor: { |
| result = |
| CallBuiltin(Builtin::kBigIntBitwiseXorNoThrow, context(), |
| var_left_bigint.value(), var_right_bigint.value()); |
| // Check for sentinel that signals BigIntTooBig exception. |
| GotoIfNot(TaggedIsSmi(result.value()), &done); |
| |
| if (slot) { |
| // Update feedback to prevent deopt loop. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| (*maybe_feedback_vector)(), *slot, |
| update_feedback_mode); |
| } |
| ThrowRangeError(context(), MessageTemplate::kBigIntTooBig); |
| break; |
| } |
| case Operation::kShiftLeft: { |
| result = |
| CallBuiltin(Builtin::kBigIntShiftLeftNoThrow, context(), |
| var_left_bigint.value(), var_right_bigint.value()); |
| // Check for sentinel that signals BigIntTooBig exception. |
| GotoIfNot(TaggedIsSmi(result.value()), &done); |
| |
| if (slot) { |
| // Update feedback to prevent deopt loop. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| (*maybe_feedback_vector)(), *slot, |
| update_feedback_mode); |
| } |
| ThrowRangeError(context(), MessageTemplate::kBigIntTooBig); |
| break; |
| } |
| case Operation::kShiftRight: { |
| result = |
| CallBuiltin(Builtin::kBigIntShiftRightNoThrow, context(), |
| var_left_bigint.value(), var_right_bigint.value()); |
| // Check for sentinel that signals BigIntTooBig exception. |
| GotoIfNot(TaggedIsSmi(result.value()), &done); |
| |
| if (slot) { |
| // Update feedback to prevent deopt loop. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| (*maybe_feedback_vector)(), *slot, |
| update_feedback_mode); |
| } |
| ThrowRangeError(context(), MessageTemplate::kBigIntTooBig); |
| break; |
| } |
| case Operation::kShiftRightLogical: { |
| if (slot) { |
| // Ensure that the feedback is updated before we throw. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| (*maybe_feedback_vector)(), *slot, |
| update_feedback_mode); |
| } |
| // BigInt does not support logical right shift. |
| ThrowTypeError(context(), MessageTemplate::kBigIntShr); |
| break; |
| } |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| BIND(&if_bigint_mix); |
| { |
| if (slot) { |
| // Ensure that the feedback is updated before we throw. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| (*maybe_feedback_vector)(), *slot, update_feedback_mode); |
| } |
| ThrowTypeError(context(), MessageTemplate::kBigIntMixedTypes); |
| } |
| } |
| |
| BIND(&done); |
| return result.value(); |
| } |
| |
| TNode<Object> |
| BinaryOpAssembler::Generate_BitwiseBinaryOpWithSmiOperandAndOptionalFeedback( |
| Operation bitwise_op, TNode<Object> left, TNode<Object> right, |
| const LazyNode<Context>& context, TNode<UintPtrT>* slot, |
| const LazyNode<HeapObject>* maybe_feedback_vector, |
| UpdateFeedbackMode update_feedback_mode) { |
| TNode<Smi> right_smi = CAST(right); |
| TVARIABLE(Object, result); |
| TVARIABLE(Smi, var_left_feedback); |
| TVARIABLE(Word32T, var_left_word32); |
| TVARIABLE(BigInt, var_left_bigint); |
| TVARIABLE(Smi, feedback); |
| // Check if the {lhs} is a Smi or a HeapObject. |
| Label if_lhsissmi(this), if_lhsisnotsmi(this, Label::kDeferred); |
| Label do_number_op(this), if_bigint_mix(this), done(this); |
| |
| Branch(TaggedIsSmi(left), &if_lhsissmi, &if_lhsisnotsmi); |
| |
| BIND(&if_lhsissmi); |
| { |
| TNode<Smi> left_smi = CAST(left); |
| result = BitwiseSmiOp(left_smi, right_smi, bitwise_op); |
| if (slot) { |
| if (IsBitwiseOutputKnownSmi(bitwise_op)) { |
| feedback = SmiConstant(BinaryOperationFeedback::kSignedSmall); |
| } else { |
| feedback = SelectSmiConstant(TaggedIsSmi(result.value()), |
| BinaryOperationFeedback::kSignedSmall, |
| BinaryOperationFeedback::kNumber); |
| } |
| } |
| Goto(&done); |
| } |
| |
| BIND(&if_lhsisnotsmi); |
| { |
| TNode<HeapObject> left_pointer = CAST(left); |
| TaggedPointerToWord32OrBigIntWithFeedback( |
| context(), left_pointer, &do_number_op, &var_left_word32, |
| &if_bigint_mix, nullptr, &var_left_bigint, &var_left_feedback); |
| BIND(&do_number_op); |
| { |
| result = |
| BitwiseOp(var_left_word32.value(), SmiToInt32(right_smi), bitwise_op); |
| if (slot) { |
| TNode<Smi> result_type = SelectSmiConstant( |
| TaggedIsSmi(result.value()), BinaryOperationFeedback::kSignedSmall, |
| BinaryOperationFeedback::kNumber); |
| feedback = SmiOr(result_type, var_left_feedback.value()); |
| } |
| Goto(&done); |
| } |
| |
| BIND(&if_bigint_mix); |
| { |
| if (slot) { |
| // Ensure that the feedback is updated before we throw. |
| UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny), |
| (*maybe_feedback_vector)(), *slot, update_feedback_mode); |
| } |
| ThrowTypeError(context(), MessageTemplate::kBigIntMixedTypes); |
| } |
| } |
| |
| BIND(&done); |
| UpdateFeedback(feedback.value(), (*maybe_feedback_vector)(), *slot, |
| update_feedback_mode); |
| return result.value(); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |