[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:
Santiago Aboy Solanes 2020-09-28 13:47:22 +01:00 committed by Commit Bot
parent fedbf2dcb9
commit 630fbf672a
10 changed files with 120 additions and 22 deletions

View File

@ -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));

View File

@ -189,6 +189,7 @@ namespace internal {
\
/* Type conversions */ \
TFC(ToNumber, TypeConversion) \
TFC(PlainPrimitiveToNumber, TypeConversionNoContext) \
TFC(ToNumberConvertBigInt, TypeConversion) \
TFC(Typeof, Typeof) \
TFC(GetSuperConstructor, Typeof) \

View File

@ -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), &not_smi);
TNode<Smi> input_smi = CAST(input);
var_result = input_smi;
Goto(&end);
BIND(&not_smi);
{
Label not_heap_number(this, Label::kDeferred);
TNode<HeapObject> input_ho = CAST(input);
GotoIfNot(IsHeapNumber(input_ho), &not_heap_number);
TNode<HeapNumber> input_hn = CAST(input_ho);
var_result = input_hn;
Goto(&end);
BIND(&not_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);

View File

@ -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 {

View File

@ -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);

View File

@ -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:

View File

@ -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(),

View File

@ -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)

View File

@ -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()))

View File

@ -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) \