[CSA] Create a PlainPrimitiveToNumberBuiltin
Previously ToNumber could be called with an empty context. In a previous CL (https://crrev.com/c/v8/v8/+/2078580) we added DCHECKS to make sure that some paths were not using the empty context. Now we are doing the next step of adding a primitive to separate the cases. Small update from delphick@ to get the builtin descriptor right. Bug: v8:6949, v8:10933 Change-Id: Ie40b169f680f60a08eb26fac1fcfcef7d6169e65 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2428863 Commit-Queue: Santiago Aboy Solanes <solanes@chromium.org> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Cr-Commit-Position: refs/heads/master@{#70162}
This commit is contained in:
parent
fedbf2dcb9
commit
630fbf672a
@ -14,15 +14,18 @@ namespace internal {
|
||||
|
||||
// ES6 section 7.1.3 ToNumber ( argument )
|
||||
TF_BUILTIN(ToNumber, CodeStubAssembler) {
|
||||
// TODO(solanes, v8:6949): Changing this to a TNode<Context> crashes with the
|
||||
// empty context. Context might not be needed, but it is propagated all over
|
||||
// the place and hard to pull out.
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
TNode<Object> input = CAST(Parameter(Descriptor::kArgument));
|
||||
|
||||
Return(ToNumber(context, input));
|
||||
}
|
||||
|
||||
TF_BUILTIN(PlainPrimitiveToNumber, CodeStubAssembler) {
|
||||
TNode<Object> input = CAST(Parameter(Descriptor::kArgument));
|
||||
|
||||
Return(PlainPrimitiveToNumber(input));
|
||||
}
|
||||
|
||||
// Like ToNumber, but also converts BigInts.
|
||||
TF_BUILTIN(ToNumberConvertBigInt, CodeStubAssembler) {
|
||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
|
@ -189,6 +189,7 @@ namespace internal {
|
||||
\
|
||||
/* Type conversions */ \
|
||||
TFC(ToNumber, TypeConversion) \
|
||||
TFC(PlainPrimitiveToNumber, TypeConversionNoContext) \
|
||||
TFC(ToNumberConvertBigInt, TypeConversion) \
|
||||
TFC(Typeof, Typeof) \
|
||||
TFC(GetSuperConstructor, Typeof) \
|
||||
|
@ -6755,6 +6755,12 @@ TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input) {
|
||||
return result.value();
|
||||
}
|
||||
|
||||
// TODO(solanes, v8:6949): Refactor this to check for JSReceivers first. If we
|
||||
// have a JSReceiver, extract the primitive and fallthrough. Otherwise, continue
|
||||
// asking for the other instance types. This will make it so that we can remove
|
||||
// the loop (which was looping at most once). Also, see if we can make use of
|
||||
// PlainPrimitiveNonNumberToNumber to de-duplicate code, maybe changing it to a
|
||||
// TryPlainPrimitiveNonNumberToNumber with a Label* as a parameter.
|
||||
TNode<Numeric> CodeStubAssembler::NonNumberToNumberOrNumeric(
|
||||
TNode<Context> context, TNode<HeapObject> input, Object::Conversion mode,
|
||||
BigIntHandling bigint_handling) {
|
||||
@ -6792,7 +6798,6 @@ TNode<Numeric> CodeStubAssembler::NonNumberToNumberOrNumeric(
|
||||
}
|
||||
|
||||
BIND(&if_inputisbigint);
|
||||
CSA_ASSERT(this, Word32And(TaggedIsNotSmi(context), IsContext(context)));
|
||||
if (mode == Object::Conversion::kToNumeric) {
|
||||
var_result = CAST(input);
|
||||
Goto(&end);
|
||||
@ -6817,9 +6822,9 @@ TNode<Numeric> CodeStubAssembler::NonNumberToNumberOrNumeric(
|
||||
|
||||
BIND(&if_inputisreceiver);
|
||||
{
|
||||
CSA_ASSERT(this, Word32And(TaggedIsNotSmi(context), IsContext(context)));
|
||||
// The {input} is a JSReceiver, we need to convert it to a Primitive first
|
||||
// using the ToPrimitive type conversion, preferably yielding a Number.
|
||||
// The {input} is a JSReceiver, we need to convert it to a Primitive
|
||||
// first using the ToPrimitive type conversion, preferably yielding a
|
||||
// Number.
|
||||
Callable callable = CodeFactory::NonPrimitiveToPrimitive(
|
||||
isolate(), ToPrimitiveHint::kNumber);
|
||||
TNode<Object> result = CallStub(callable, context, input);
|
||||
@ -6832,15 +6837,16 @@ TNode<Numeric> CodeStubAssembler::NonNumberToNumberOrNumeric(
|
||||
|
||||
BIND(&if_done);
|
||||
{
|
||||
// The ToPrimitive conversion already gave us a Number/Numeric, so we're
|
||||
// done.
|
||||
// The ToPrimitive conversion already gave us a Number/Numeric, so
|
||||
// we're done.
|
||||
var_result = CAST(result);
|
||||
Goto(&end);
|
||||
}
|
||||
|
||||
BIND(&if_notdone);
|
||||
{
|
||||
// We now have a Primitive {result}, but it's not yet a Number/Numeric.
|
||||
// We now have a Primitive {result}, but it's not yet a
|
||||
// Number/Numeric.
|
||||
var_input = CAST(result);
|
||||
Goto(&loop);
|
||||
}
|
||||
@ -6848,7 +6854,6 @@ TNode<Numeric> CodeStubAssembler::NonNumberToNumberOrNumeric(
|
||||
|
||||
BIND(&if_inputisother);
|
||||
{
|
||||
CSA_ASSERT(this, Word32And(TaggedIsNotSmi(context), IsContext(context)));
|
||||
// The {input} is something else (e.g. Symbol), let the runtime figure
|
||||
// out the correct exception.
|
||||
// Note: We cannot tail call to the runtime here, as js-to-wasm
|
||||
@ -6873,14 +6878,46 @@ TNode<Numeric> CodeStubAssembler::NonNumberToNumberOrNumeric(
|
||||
}
|
||||
|
||||
TNode<Number> CodeStubAssembler::NonNumberToNumber(
|
||||
SloppyTNode<Context> context, SloppyTNode<HeapObject> input,
|
||||
TNode<Context> context, SloppyTNode<HeapObject> input,
|
||||
BigIntHandling bigint_handling) {
|
||||
return CAST(NonNumberToNumberOrNumeric(
|
||||
context, input, Object::Conversion::kToNumber, bigint_handling));
|
||||
}
|
||||
|
||||
TNode<Number> CodeStubAssembler::PlainPrimitiveNonNumberToNumber(
|
||||
TNode<HeapObject> input) {
|
||||
CSA_ASSERT(this, Word32BinaryNot(IsHeapNumber(input)));
|
||||
TVARIABLE(Number, var_result);
|
||||
Label done(this);
|
||||
|
||||
// Dispatch on the {input} instance type.
|
||||
TNode<Uint16T> input_instance_type = LoadInstanceType(input);
|
||||
Label if_inputisstring(this), if_inputisoddball(this);
|
||||
GotoIf(IsStringInstanceType(input_instance_type), &if_inputisstring);
|
||||
CSA_ASSERT(this, InstanceTypeEqual(input_instance_type, ODDBALL_TYPE));
|
||||
Goto(&if_inputisoddball);
|
||||
|
||||
BIND(&if_inputisstring);
|
||||
{
|
||||
// The {input} is a String, use the fast stub to convert it to a Number.
|
||||
TNode<String> string_input = CAST(input);
|
||||
var_result = StringToNumber(string_input);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&if_inputisoddball);
|
||||
{
|
||||
// The {input} is an Oddball, we just need to load the Number value of it.
|
||||
var_result = LoadObjectField<Number>(input, Oddball::kToNumberOffset);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&done);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
TNode<Numeric> CodeStubAssembler::NonNumberToNumeric(
|
||||
SloppyTNode<Context> context, SloppyTNode<HeapObject> input) {
|
||||
TNode<Context> context, SloppyTNode<HeapObject> input) {
|
||||
return NonNumberToNumberOrNumeric(context, input,
|
||||
Object::Conversion::kToNumeric);
|
||||
}
|
||||
@ -6909,7 +6946,7 @@ TNode<Number> CodeStubAssembler::ToNumber_Inline(SloppyTNode<Context> context,
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
TNode<Number> CodeStubAssembler::ToNumber(SloppyTNode<Context> context,
|
||||
TNode<Number> CodeStubAssembler::ToNumber(TNode<Context> context,
|
||||
SloppyTNode<Object> input,
|
||||
BigIntHandling bigint_handling) {
|
||||
TVARIABLE(Number, var_result);
|
||||
@ -6942,6 +6979,37 @@ TNode<Number> CodeStubAssembler::ToNumber(SloppyTNode<Context> context,
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
TNode<Number> CodeStubAssembler::PlainPrimitiveToNumber(TNode<Object> input) {
|
||||
TVARIABLE(Number, var_result);
|
||||
Label end(this);
|
||||
|
||||
Label not_smi(this, Label::kDeferred);
|
||||
GotoIfNot(TaggedIsSmi(input), ¬_smi);
|
||||
TNode<Smi> input_smi = CAST(input);
|
||||
var_result = input_smi;
|
||||
Goto(&end);
|
||||
|
||||
BIND(¬_smi);
|
||||
{
|
||||
Label not_heap_number(this, Label::kDeferred);
|
||||
TNode<HeapObject> input_ho = CAST(input);
|
||||
GotoIfNot(IsHeapNumber(input_ho), ¬_heap_number);
|
||||
|
||||
TNode<HeapNumber> input_hn = CAST(input_ho);
|
||||
var_result = input_hn;
|
||||
Goto(&end);
|
||||
|
||||
BIND(¬_heap_number);
|
||||
{
|
||||
var_result = PlainPrimitiveNonNumberToNumber(input_ho);
|
||||
Goto(&end);
|
||||
}
|
||||
}
|
||||
|
||||
BIND(&end);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
TNode<BigInt> CodeStubAssembler::ToBigInt(TNode<Context> context,
|
||||
TNode<Object> input) {
|
||||
TVARIABLE(BigInt, var_result);
|
||||
|
@ -2464,20 +2464,23 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
|
||||
// Convert a Non-Number object to a Number.
|
||||
TNode<Number> NonNumberToNumber(
|
||||
SloppyTNode<Context> context, SloppyTNode<HeapObject> input,
|
||||
TNode<Context> context, SloppyTNode<HeapObject> input,
|
||||
BigIntHandling bigint_handling = BigIntHandling::kThrow);
|
||||
// Convert a Non-Number object to a Numeric.
|
||||
TNode<Numeric> NonNumberToNumeric(SloppyTNode<Context> context,
|
||||
TNode<Numeric> NonNumberToNumeric(TNode<Context> context,
|
||||
SloppyTNode<HeapObject> input);
|
||||
// Convert any object to a Number.
|
||||
// Conforms to ES#sec-tonumber if {bigint_handling} == kThrow.
|
||||
// With {bigint_handling} == kConvertToNumber, matches behavior of
|
||||
// tc39.github.io/proposal-bigint/#sec-number-constructor-number-value.
|
||||
TNode<Number> ToNumber(
|
||||
SloppyTNode<Context> context, SloppyTNode<Object> input,
|
||||
TNode<Context> context, SloppyTNode<Object> input,
|
||||
BigIntHandling bigint_handling = BigIntHandling::kThrow);
|
||||
TNode<Number> ToNumber_Inline(SloppyTNode<Context> context,
|
||||
SloppyTNode<Object> input);
|
||||
// Convert any plain primitive to a Number. No need to handle BigInts since
|
||||
// they are not plain primitives.
|
||||
TNode<Number> PlainPrimitiveToNumber(TNode<Object> input);
|
||||
|
||||
// Try to convert an object to a BigInt. Throws on failure (e.g. for Numbers).
|
||||
// https://tc39.github.io/proposal-bigint/#sec-to-bigint
|
||||
@ -3655,6 +3658,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<UnionT<FixedArray, PropertyArray>> array, TNode<TIndex> index,
|
||||
TNode<Object> value, WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
|
||||
int additional_offset = 0);
|
||||
|
||||
// Converts {input} to a number. {input} must be a plain primitve.
|
||||
TNode<Number> PlainPrimitiveNonNumberToNumber(TNode<HeapObject> input);
|
||||
};
|
||||
|
||||
class V8_EXPORT_PRIVATE CodeStubArguments {
|
||||
|
@ -287,6 +287,12 @@ void TypeConversionDescriptor::InitializePlatformSpecific(
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void TypeConversionNoContextDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
Register registers[] = {TypeConversionDescriptor::ArgumentRegister()};
|
||||
data->InitializePlatformSpecific(arraysize(registers), registers);
|
||||
}
|
||||
|
||||
void TypeConversionStackParameterDescriptor::InitializePlatformSpecific(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
data->InitializePlatformSpecific(0, nullptr);
|
||||
|
@ -93,6 +93,7 @@ namespace internal {
|
||||
V(StringAtAsString) \
|
||||
V(StringSubstring) \
|
||||
V(TypeConversion) \
|
||||
V(TypeConversionNoContext) \
|
||||
V(TypeConversionStackParameter) \
|
||||
V(Typeof) \
|
||||
V(UnaryOp_WithFeedback) \
|
||||
@ -909,6 +910,13 @@ class TypeConversionDescriptor final : public CallInterfaceDescriptor {
|
||||
static const Register ArgumentRegister();
|
||||
};
|
||||
|
||||
class TypeConversionNoContextDescriptor final : public CallInterfaceDescriptor {
|
||||
public:
|
||||
DEFINE_PARAMETERS_NO_CONTEXT(kArgument)
|
||||
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged())
|
||||
DECLARE_DESCRIPTOR(TypeConversionNoContextDescriptor, CallInterfaceDescriptor)
|
||||
};
|
||||
|
||||
class TypeConversionStackParameterDescriptor final
|
||||
: public CallInterfaceDescriptor {
|
||||
public:
|
||||
|
@ -716,9 +716,9 @@ Node* GraphAssembler::UnsafePointerAdd(Node* base, Node* external) {
|
||||
}
|
||||
|
||||
TNode<Number> JSGraphAssembler::PlainPrimitiveToNumber(TNode<Object> value) {
|
||||
return AddNode<Number>(graph()->NewNode(PlainPrimitiveToNumberOperator(),
|
||||
ToNumberBuiltinConstant(), value,
|
||||
NoContextConstant(), effect()));
|
||||
return AddNode<Number>(graph()->NewNode(
|
||||
PlainPrimitiveToNumberOperator(), PlainPrimitiveToNumberBuiltinConstant(),
|
||||
value, effect()));
|
||||
}
|
||||
|
||||
Node* GraphAssembler::BitcastWordToTaggedSigned(Node* value) {
|
||||
@ -959,7 +959,8 @@ void GraphAssembler::InitializeEffectControl(Node* effect, Node* control) {
|
||||
|
||||
Operator const* JSGraphAssembler::PlainPrimitiveToNumberOperator() {
|
||||
if (!to_number_operator_.is_set()) {
|
||||
Callable callable = Builtins::CallableFor(isolate(), Builtins::kToNumber);
|
||||
Callable callable =
|
||||
Builtins::CallableFor(isolate(), Builtins::kPlainPrimitiveToNumber);
|
||||
CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
|
||||
auto call_descriptor = Linkage::GetStubCallDescriptor(
|
||||
graph()->zone(), callable.descriptor(),
|
||||
|
@ -127,6 +127,7 @@ class BasicBlock;
|
||||
V(One, Number) \
|
||||
V(TheHole, Oddball) \
|
||||
V(ToNumberBuiltin, Code) \
|
||||
V(PlainPrimitiveToNumberBuiltin, Code) \
|
||||
V(True, Boolean) \
|
||||
V(Undefined, Oddball) \
|
||||
V(Zero, Number)
|
||||
|
@ -129,6 +129,9 @@ DEFINE_GETTER(BooleanMapConstant, HeapConstant(factory()->boolean_map()))
|
||||
DEFINE_GETTER(ToNumberBuiltinConstant,
|
||||
HeapConstant(BUILTIN_CODE(isolate(), ToNumber)))
|
||||
|
||||
DEFINE_GETTER(PlainPrimitiveToNumberBuiltinConstant,
|
||||
HeapConstant(BUILTIN_CODE(isolate(), PlainPrimitiveToNumber)))
|
||||
|
||||
DEFINE_GETTER(EmptyFixedArrayConstant,
|
||||
HeapConstant(factory()->empty_fixed_array()))
|
||||
|
||||
|
@ -85,6 +85,7 @@ class V8_EXPORT_PRIVATE JSGraph : public MachineGraph {
|
||||
V(BigIntMapConstant) \
|
||||
V(BooleanMapConstant) \
|
||||
V(ToNumberBuiltinConstant) \
|
||||
V(PlainPrimitiveToNumberBuiltinConstant) \
|
||||
V(EmptyFixedArrayConstant) \
|
||||
V(EmptyStringConstant) \
|
||||
V(FixedArrayMapConstant) \
|
||||
|
Loading…
Reference in New Issue
Block a user