Commit f920ad11 authored by Bill Budge's avatar Bill Budge Committed by Commit Bot

Revert "[torque] Port builtins-number-gen to Torque"

This reverts commit 4482f988.

Reason for revert: Causes Torque to break UBSAN

https://ci.chromium.org/p/v8/builders/ci/V8%20Linux64%20UBSan/11235?

[764/2199] ACTION //:run_torque(//build/toolchain/linux:clang_x64)
FAILED: gen/torque-generated/bit-fields-tq.h gen/torque-generated/builtin-definitions-tq.h gen/torqu...(too long)
python ../../tools/run.py ./torque -o gen/torque-generated -v8-root ../.. src/builtins/array-copywit...(too long)
../../src/torque/implementation-visitor.cc:778:36: runtime error: 2.14748e+09 is outside the range of representable values of type 'int'

Original change's description:
> [torque] Port builtins-number-gen to Torque
> 
> - Ports everything except Add.
> 
> Builtins generated from this CL are slightly larger, e.g. Subtract
> is 424 bytes on x64, as opposed to 400 bytes for the CSA version.
> See https://crbug.com/v8/10521
> 
> Bug: v8:9891
> 
> Change-Id: Id85779eb26d8e51643d8a04f0a75090bc50ef5b2
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2191644
> Commit-Queue: Bill Budge <bbudge@chromium.org>
> Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
> Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#67910}

TBR=bbudge@chromium.org,jgruber@chromium.org,tebbi@chromium.org

Change-Id: Ib124c893753973243563e32c25bc727a5df2ca53
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: v8:9891
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2209264Reviewed-by: 's avatarBill Budge <bbudge@chromium.org>
Commit-Queue: Bill Budge <bbudge@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67911}
parent 4482f988
......@@ -78,8 +78,7 @@ type JSPrimitive = Numeric|String|Symbol|Boolean|Null|Undefined;
// TheHole or FixedArray.
type JSAny = JSReceiver|JSPrimitive;
type JSAnyNotNumeric = String|Symbol|Boolean|Null|Undefined|JSReceiver;
type JSAnyNotNumber = BigInt|JSAnyNotNumeric;
type JSAnyNotNumber = BigInt|String|Symbol|Boolean|Null|Undefined|JSReceiver;
// This is the intersection of JSAny and HeapObject.
type JSAnyNotSmi = JSAnyNotNumber|HeapNumber;
......@@ -620,11 +619,6 @@ extern macro StringCharCodeAt(String, uintptr): int32;
extern runtime StringCompareSequence(Context, String, String, Number): Boolean;
extern macro StringFromSingleCharCode(int32): String;
extern macro Equal(JSAny, JSAny, Context): Boolean;
macro Equal(implicit context: Context)(left: JSAny, right: JSAny): Boolean {
return Equal(left, right);
}
extern macro StrictEqual(JSAny, JSAny): Boolean;
extern macro SmiLexicographicCompare(Smi, Smi): Smi;
extern runtime ReThrow(Context, JSAny): never;
......@@ -823,7 +817,6 @@ extern operator '+' macro Float64Add(float64, float64): float64;
extern operator '-' macro Float64Sub(float64, float64): float64;
extern operator '*' macro Float64Mul(float64, float64): float64;
extern operator '/' macro Float64Div(float64, float64): float64;
extern operator '%' macro Float64Mod(float64, float64): float64;
extern operator '+' macro NumberAdd(Number, Number): Number;
extern operator '-' macro NumberSub(Number, Number): Number;
......@@ -892,7 +885,6 @@ extern macro TaggedIsNotSmi(Object): bool;
extern macro TaggedIsPositiveSmi(Object): bool;
extern macro IsValidPositiveSmi(intptr): bool;
extern macro IsInteger(JSAny): bool;
extern macro IsInteger(HeapNumber): bool;
extern macro AllocateHeapNumberWithValue(float64): HeapNumber;
......@@ -923,7 +915,6 @@ macro SmiTag<T : type extends uint31>(value: T): SmiTagged<T> {
return %RawDownCast<SmiTagged<T>>(SmiFromUint32(value));
}
extern macro SmiToInt32(Smi): int32;
extern macro SmiToFloat64(Smi): float64;
extern macro TaggedIndexToIntPtr(TaggedIndex): intptr;
extern macro IntPtrToTaggedIndex(intptr): TaggedIndex;
extern macro TaggedIndexToSmi(TaggedIndex): Smi;
......
......@@ -598,15 +598,53 @@ namespace internal {
TFJ(MapIteratorPrototypeNext, 0, kReceiver) \
TFS(MapIteratorToList, kSource) \
\
/* Number */ \
TFC(AllocateHeapNumber, AllocateHeapNumber) \
/* ES #sec-number-constructor */ \
TFJ(NumberConstructor, kDontAdaptArgumentsSentinel) \
/* ES6 #sec-number.isfinite */ \
TFJ(NumberIsFinite, 1, kReceiver, kNumber) \
/* ES6 #sec-number.isinteger */ \
TFJ(NumberIsInteger, 1, kReceiver, kNumber) \
/* ES6 #sec-number.isnan */ \
TFJ(NumberIsNaN, 1, kReceiver, kNumber) \
/* ES6 #sec-number.issafeinteger */ \
TFJ(NumberIsSafeInteger, 1, kReceiver, kNumber) \
/* ES6 #sec-number.parsefloat */ \
TFJ(NumberParseFloat, 1, kReceiver, kString) \
/* ES6 #sec-number.parseint */ \
TFJ(NumberParseInt, 2, kReceiver, kString, kRadix) \
TFS(ParseInt, kString, kRadix) \
CPP(NumberPrototypeToExponential) \
CPP(NumberPrototypeToFixed) \
CPP(NumberPrototypeToLocaleString) \
CPP(NumberPrototypeToPrecision) \
/* ES6 #sec-number.prototype.valueof */ \
TFJ(NumberPrototypeValueOf, 0, kReceiver) \
TFC(Add, BinaryOp) \
TFC(Subtract, BinaryOp) \
TFC(Multiply, BinaryOp) \
TFC(Divide, BinaryOp) \
TFC(Modulus, BinaryOp) \
TFC(Exponentiate, BinaryOp) \
TFC(BitwiseAnd, BinaryOp) \
TFC(BitwiseOr, BinaryOp) \
TFC(BitwiseXor, BinaryOp) \
TFC(ShiftLeft, BinaryOp) \
TFC(ShiftRight, BinaryOp) \
TFC(ShiftRightLogical, BinaryOp) \
TFC(LessThan, Compare) \
TFC(LessThanOrEqual, Compare) \
TFC(GreaterThan, Compare) \
TFC(GreaterThanOrEqual, Compare) \
TFC(Equal, Compare) \
TFC(SameValue, Compare) \
TFC(SameValueNumbersOnly, Compare) \
TFC(StrictEqual, Compare) \
TFS(BitwiseNot, kValue) \
TFS(Decrement, kValue) \
TFS(Increment, kValue) \
TFS(Negate, kValue) \
\
/* Object */ \
/* ES #sec-object-constructor */ \
......
......@@ -13,6 +13,299 @@ 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) {
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));
}
// ES6 #sec-number.prototype.valueof
TF_BUILTIN(NumberPrototypeValueOf, CodeStubAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
TNode<Object> result = ToThisValue(context, receiver, PrimitiveType::kNumber,
"Number.prototype.valueOf");
Return(result);
}
class AddStubAssembler : public CodeStubAssembler {
public:
explicit AddStubAssembler(compiler::CodeAssemblerState* state)
......@@ -245,5 +538,459 @@ TF_BUILTIN(Add, AddStubAssembler) {
}
}
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), &not_number);
if (var_input_double != nullptr) {
*var_input_double = LoadHeapNumberValue(input_heap_object);
}
Goto(do_double);
BIND(&not_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)), &dividend_is_zero,
&dividend_is_not_zero);
BIND(&dividend_is_zero);
{
GotoIf(SmiLessThan(divisor, SmiConstant(0)), &bailout);
Goto(&dividend_is_not_zero);
}
BIND(&dividend_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) {
TNode<Object> lhs = CAST(Parameter(Descriptor::kLeft));
TNode<Object> rhs = CAST(Parameter(Descriptor::kRight));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
Return(Equal(lhs, rhs, context));
}
TF_BUILTIN(StrictEqual, CodeStubAssembler) {
TNode<Object> lhs = CAST(Parameter(Descriptor::kLeft));
TNode<Object> rhs = CAST(Parameter(Descriptor::kRight));
Return(StrictEqual(lhs, rhs));
}
} // namespace internal
} // namespace v8
......@@ -103,9 +103,6 @@ macro Convert<To: type, From: type>(i: From): To labels Overflow {
return i;
}
Convert<Boolean, bool>(b: bool): Boolean {
return b ? True : False;
}
extern macro ConvertElementsKindToInt(ElementsKind): int32;
Convert<int32, ElementsKind>(elementsKind: ElementsKind): int32 {
return ConvertElementsKindToInt(elementsKind);
......
......@@ -2,60 +2,11 @@
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.
#include 'src/ic/binary-op-assembler.h'
extern enum Operation extends uint31 {
// Binary operations.
kAdd,
kSubtract,
kMultiply,
kDivide,
kModulus,
kExponentiate,
kBitwiseAnd,
kBitwiseOr,
kBitwiseXor,
kShiftLeft,
kShiftRight,
kShiftRightLogical,
// Unary operations.
kBitwiseNot,
kNegate,
kIncrement,
kDecrement,
// Compare operations.
kEqual,
kStrictEqual,
kLessThan,
kLessThanOrEqual,
kGreaterThan,
kGreaterThanOrEqual
}
namespace runtime {
extern transitioning runtime
DoubleToStringWithRadix(implicit context: Context)(Number, Number): String;
extern transitioning runtime StringParseFloat(implicit context: Context)(
String): Number;
extern transitioning runtime StringParseInt(implicit context: Context)(
JSAny, JSAny): Number;
extern runtime BigIntUnaryOp(Context, BigInt, SmiTagged<Operation>): BigInt;
extern runtime BigIntBinaryOp(
Context, Numeric, Numeric, SmiTagged<Operation>): BigInt;
} // namespace runtime
// Disambiguate macros with the same name as JS builtins.
// TODO(bbudge) Clean this up with appropriate namespaces.
macro NumberIsNaNImpl(number: Number): bool {
return NumberIsNaN(number);
}
macro StrictEqualImpl(left: JSAny, right: JSAny): Boolean {
return StrictEqual(left, right);
}
namespace number {
extern macro NaNStringConstant(): String;
extern macro ZeroStringConstant(): String;
......@@ -111,535 +62,14 @@ transitioning javascript builtin NumberPrototypeToString(
if (x == -0) {
return ZeroStringConstant();
} else if (NumberIsNaNImpl(x)) {
} else if (NumberIsNaN(x)) {
return NaNStringConstant();
} else if (x == V8_INFINITY) {
return InfinityStringConstant();
} else if (x == MINUS_V8_INFINITY) {
return MinusInfinityStringConstant();
}
return runtime::DoubleToStringWithRadix(x, radixNumber);
}
// ES6 #sec-number.isfinite
javascript builtin NumberIsFinite(
js-implicit context: NativeContext,
receiver: JSAny)(...arguments): Boolean {
typeswitch (arguments[0]) {
case (Smi): {
return True;
}
case (h: HeapNumber): {
const number: float64 = Convert<float64>(h);
const infiniteOrNaN: bool = Float64IsNaN(number - number);
return Convert<Boolean>(!infiniteOrNaN);
}
case (JSAnyNotNumber): {
return False;
}
}
}
// ES6 #sec-number.isinteger
javascript builtin NumberIsInteger(js-implicit context: NativeContext)(
...arguments): Boolean {
return SelectBooleanConstant(IsInteger(arguments[0]));
}
// ES6 #sec-number.isnan
javascript builtin NumberIsNaN(js-implicit context: NativeContext)(
...arguments): Boolean {
typeswitch (arguments[0]) {
case (Smi): {
return False;
}
case (h: HeapNumber): {
const number: float64 = Convert<float64>(h);
return Convert<Boolean>(Float64IsNaN(number));
}
case (JSAnyNotNumber): {
return False;
}
}
}
// ES6 #sec-number.issafeinteger
javascript builtin NumberIsSafeInteger(js-implicit context: NativeContext)(
...arguments): Boolean {
return SelectBooleanConstant(IsSafeInteger(arguments[0]));
}
// ES6 #sec-number.prototype.valueof
transitioning javascript builtin NumberPrototypeValueOf(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
return ToThisValue(
receiver, PrimitiveType::kNumber, 'Number.prototype.valueOf');
}
// ES6 #sec-number.parsefloat
transitioning javascript builtin NumberParseFloat(
js-implicit context: NativeContext)(...arguments): Number {
const input: JSAny = arguments[0];
try {
typeswitch (input) {
case (s: Smi): {
return s;
}
case (h: HeapNumber): {
// The input is already a Number. Take care of -0.
// The sense of comparison is important for the NaN case.
return (Convert<float64>(h) == 0) ? SmiConstant(0) : h;
}
case (s: String): {
goto String(s);
}
case (HeapObject): {
goto String(string::ToString(context, input));
}
}
} label String(s: String) {
// Check if the string is a cached array index.
const hash: NameHash = s.hash_field;
if (!hash.is_not_integer_index_mask &&
hash.array_index_length < kMaxCachedArrayIndexLength) {
const arrayIndex: uint32 = hash.array_index_value;
return SmiFromUint32(arrayIndex);
}
// Fall back to the runtime to convert string to a number.
return runtime::StringParseFloat(s);
}
}
extern macro TruncateFloat64ToWord32(float64): uint32;
transitioning builtin ParseInt(implicit context: Context)(
input: JSAny, radix: JSAny): Number {
try {
// Check if radix should be 10 (i.e. undefined, 0 or 10).
if (radix != Undefined && !TaggedEqual(radix, SmiConstant(10)) &&
!TaggedEqual(radix, SmiConstant(0))) {
goto CallRuntime;
}
typeswitch (input) {
case (s: Smi): {
return s;
}
case (h: HeapNumber): {
// Check if the input value is in Signed32 range.
const asFloat64: float64 = Convert<float64>(h);
const asInt32: int32 = Signed(TruncateFloat64ToWord32(asFloat64));
// The sense of comparison is important for the NaN case.
if (asFloat64 == ChangeInt32ToFloat64(asInt32)) goto Int32(asInt32);
// Check if the absolute value of input is in the [1,1<<31[ range. Call
// the runtime for the range [0,1[ because the result could be -0.
const kMaxAbsValue: float64 = 2147483648.0;
const absInput: float64 = math::Float64Abs(asFloat64);
if (absInput < kMaxAbsValue && absInput >= 1) goto Int32(asInt32);
goto CallRuntime;
}
case (s: String): {
goto String(s);
}
case (HeapObject): {
goto CallRuntime;
}
}
} label Int32(i: int32) {
return ChangeInt32ToTagged(i);
} label String(s: String) {
// Check if the string is a cached array index.
const hash: NameHash = s.hash_field;
if (!hash.is_not_integer_index_mask &&
hash.array_index_length < kMaxCachedArrayIndexLength) {
const arrayIndex: uint32 = hash.array_index_value;
return SmiFromUint32(arrayIndex);
}
// Fall back to the runtime.
goto CallRuntime;
} label CallRuntime {
tail runtime::StringParseInt(input, radix);
}
}
// ES6 #sec-number.parseint
transitioning javascript builtin NumberParseInt(
js-implicit context: NativeContext)(...arguments): Number {
const input: JSAny = arguments[0];
const radix: JSAny = arguments[1];
return ParseInt(input, radix);
}
extern builtin NonNumberToNumeric(implicit context: Context)(JSAny): Numeric;
extern builtin BitwiseXor(implicit context: Context)(Number, Number): Number;
extern builtin Subtract(implicit context: Context)(Number, Number): Number;
extern builtin Add(implicit context: Context)(Number, Number): Number;
extern macro BitwiseOp(int32, int32, constexpr Operation): Number;
extern macro RelationalComparison(
constexpr Operation, JSAny, JSAny, Context): Boolean;
// TODO(bbudge) Use a simpler macro structure that doesn't loop when converting
// non-numbers, if such a code sequence doesn't make the builtin bigger.
// Unary type switch on Number | BigInt.
macro UnaryOp1(implicit context: Context)(value: JSAny): never labels
Number(Number), BigInt(BigInt) {
let x: JSAny = value;
while (true) {
typeswitch (x) {
case (n: Number): {
goto Number(n);
}
case (b: BigInt): {
goto BigInt(b);
}
case (JSAnyNotNumeric): {
x = NonNumberToNumeric(x);
}
}
}
unreachable;
}
// Unary type switch on Smi | HeapNumber | BigInt.
macro UnaryOp2(implicit context: Context)(value: JSAny): never labels
Smi(Smi), HeapNumber(HeapNumber), BigInt(BigInt) {
let x: JSAny = value;
while (true) {
typeswitch (x) {
case (s: Smi): {
goto Smi(s);
}
case (h: HeapNumber): {
goto HeapNumber(h);
}
case (b: BigInt): {
goto BigInt(b);
}
case (JSAnyNotNumeric): {
x = NonNumberToNumeric(x);
}
}
}
unreachable;
}
// Binary type switch on Number | BigInt.
macro BinaryOp1(implicit context: Context)(
leftVal: JSAny, rightVal: JSAny): never labels
Number(Number, Number), AtLeastOneBigInt(Numeric, Numeric) {
let left: JSAny = leftVal;
let right: JSAny = rightVal;
while (true) {
try {
typeswitch (left) {
case (left: Number): {
typeswitch (right) {
case (right: Number): {
goto Number(left, right);
}
case (right: BigInt): {
goto AtLeastOneBigInt(left, right);
}
case (JSAnyNotNumeric): {
goto RightNotNumeric;
}
}
}
case (left: BigInt): {
typeswitch (right) {
case (right: Numeric): {
goto AtLeastOneBigInt(left, right);
}
case (JSAnyNotNumeric): {
goto RightNotNumeric;
}
}
}
case (JSAnyNotNumeric): {
left = NonNumberToNumeric(left);
}
}
} label RightNotNumeric {
right = NonNumberToNumeric(right);
}
}
unreachable;
}
// Binary type switch on Number | BigInt.
macro BinaryOp2(implicit context: Context)(leftVal: JSAny, rightVal: JSAny):
never labels Smis(Smi, Smi), Float64s(float64, float64),
AtLeastOneBigInt(Numeric, Numeric) {
let left: JSAny = leftVal;
let right: JSAny = rightVal;
while (true) {
try {
typeswitch (left) {
case (left: Smi): {
typeswitch (right) {
case (right: Smi): {
goto Smis(left, right);
}
case (right: HeapNumber): {
goto Float64s(SmiToFloat64(left), Convert<float64>(right));
}
case (right: BigInt): {
goto AtLeastOneBigInt(left, right);
}
case (JSAnyNotNumeric): {
goto RightNotNumeric;
}
}
}
case (left: HeapNumber): {
typeswitch (right) {
case (right: Smi): {
goto Float64s(Convert<float64>(left), SmiToFloat64(right));
}
case (right: HeapNumber): {
goto Float64s(Convert<float64>(left), Convert<float64>(right));
}
case (right: BigInt): {
goto AtLeastOneBigInt(left, right);
}
case (JSAnyNotNumeric): {
goto RightNotNumeric;
}
}
}
case (left: BigInt): {
typeswitch (right) {
case (right: Numeric): {
goto AtLeastOneBigInt(left, right);
}
case (JSAnyNotNumeric): {
goto RightNotNumeric;
}
}
}
case (JSAnyNotNumeric): {
left = NonNumberToNumeric(left);
}
}
} label RightNotNumeric {
right = NonNumberToNumeric(right);
}
}
unreachable;
}
builtin Subtract(implicit context: Context)(
left: JSAny, right: JSAny): Numeric {
try {
BinaryOp2(left, right) otherwise Smis, Float64s, AtLeastOneBigInt;
} label Smis(left: Smi, right: Smi) {
try {
return math::TrySmiSub(left, right) otherwise Overflow;
} label Overflow {
goto Float64s(SmiToFloat64(left), SmiToFloat64(right));
}
} label Float64s(left: float64, right: float64) {
return AllocateHeapNumberWithValue(left - right);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail bigint::BigIntSubtract(left, right);
}
}
builtin Multiply(implicit context: Context)(
left: JSAny, right: JSAny): Numeric {
try {
BinaryOp2(left, right) otherwise Smis, Float64s, AtLeastOneBigInt;
} label Smis(left: Smi, right: Smi) {
// The result is not necessarily a smi, in case of overflow.
return SmiMul(left, right);
} label Float64s(left: float64, right: float64) {
return AllocateHeapNumberWithValue(left * right);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail runtime::BigIntBinaryOp(
context, left, right, SmiTag<Operation>(Operation::kMultiply));
}
}
const kSmiValueSize: constexpr int32 generates 'kSmiValueSize';
const kMinInt32: constexpr int32 generates 'kMinInt';
const kMinInt31: constexpr int32 generates 'kMinInt31';
const kMinimumDividend: int32 = (kSmiValueSize == 32) ? kMinInt32 : kMinInt31;
builtin Divide(implicit context: Context)(left: JSAny, right: JSAny): Numeric {
try {
BinaryOp2(left, right) otherwise Smis, Float64s, AtLeastOneBigInt;
} label Smis(left: Smi, right: Smi) {
// TODO(jkummerow): Consider just always doing a double division.
// Bail out if {divisor} is zero.
if (right == 0) goto SmiBailout(left, right);
// Bail out if dividend is zero and divisor is negative.
if (left == 0 && right < 0) goto SmiBailout(left, right);
const dividend: int32 = SmiToInt32(left);
const divisor: int32 = SmiToInt32(right);
// Bail out if dividend is kMinInt31 (or kMinInt32 if Smis are 32 bits)
// and divisor is -1.
if (divisor == -1 && dividend == kMinimumDividend) {
goto SmiBailout(left, right);
}
// TODO(epertoso): consider adding a machine instruction that returns
// both the result and the remainder.
const result: int32 = dividend / divisor;
const truncated: int32 = result * divisor;
if (dividend != truncated) goto SmiBailout(left, right);
return SmiFromInt32(result);
} label SmiBailout(left: Smi, right: Smi) {
goto Float64s(SmiToFloat64(left), SmiToFloat64(right));
} label Float64s(left: float64, right: float64) {
return AllocateHeapNumberWithValue(left / right);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail runtime::BigIntBinaryOp(
context, left, right, SmiTag<Operation>(Operation::kDivide));
}
}
builtin Modulus(implicit context: Context)(left: JSAny, right: JSAny): Numeric {
try {
BinaryOp2(left, right) otherwise Smis, Float64s, AtLeastOneBigInt;
} label Smis(left: Smi, right: Smi) {
return SmiMod(left, right);
} label Float64s(left: float64, right: float64) {
return AllocateHeapNumberWithValue(left % right);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail runtime::BigIntBinaryOp(
context, left, right, SmiTag<Operation>(Operation::kModulus));
}
}
builtin Exponentiate(implicit context: Context)(
left: JSAny, right: JSAny): Numeric {
try {
BinaryOp1(left, right) otherwise Numbers, AtLeastOneBigInt;
} label Numbers(left: Number, right: Number) {
return math::MathPowImpl(left, right);
} label AtLeastOneBigInt(left: Numeric, right: Numeric) {
tail runtime::BigIntBinaryOp(
context, left, right, SmiTag<Operation>(Operation::kExponentiate));
}
}
builtin Negate(implicit context: Context)(value: JSAny): Numeric {
try {
UnaryOp2(value) otherwise Smi, HeapNumber, BigInt;
} label Smi(s: Smi) {
return SmiMul(s, -1);
} label HeapNumber(h: HeapNumber) {
return AllocateHeapNumberWithValue(Convert<float64>(h) * -1);
} label BigInt(b: BigInt) {
tail runtime::BigIntUnaryOp(
context, b, SmiTag<Operation>(Operation::kNegate));
}
}
builtin BitwiseNot(implicit context: Context)(value: JSAny): Numeric {
try {
UnaryOp1(value) otherwise Number, BigInt;
} label Number(n: Number) {
tail BitwiseXor(n, -1);
} label BigInt(b: BigInt) {
return runtime::BigIntUnaryOp(
context, b, SmiTag<Operation>(Operation::kBitwiseNot));
}
}
builtin Decrement(implicit context: Context)(value: JSAny): Numeric {
try {
UnaryOp1(value) otherwise Number, BigInt;
} label Number(n: Number) {
tail Subtract(n, 1);
} label BigInt(b: BigInt) {
return runtime::BigIntUnaryOp(
context, b, SmiTag<Operation>(Operation::kDecrement));
}
}
builtin Increment(implicit context: Context)(value: JSAny): Numeric {
try {
UnaryOp1(value) otherwise Number, BigInt;
} label Number(n: Number) {
tail Add(n, 1);
} label BigInt(b: BigInt) {
return runtime::BigIntUnaryOp(
context, b, SmiTag<Operation>(Operation::kIncrement));
}
}
// Bitwise binary operations.
extern macro BinaryOpAssembler::Generate_BitwiseBinaryOp(
constexpr Operation, JSAny, JSAny, Context): Object;
builtin ShiftLeft(implicit context: Context)(
left: JSAny, right: JSAny): Object {
return Generate_BitwiseBinaryOp(Operation::kShiftLeft, left, right, context);
}
builtin ShiftRight(implicit context: Context)(
left: JSAny, right: JSAny): Object {
return Generate_BitwiseBinaryOp(Operation::kShiftRight, left, right, context);
}
builtin ShiftRightLogical(implicit context: Context)(
left: JSAny, right: JSAny): Object {
return Generate_BitwiseBinaryOp(
Operation::kShiftRightLogical, left, right, context);
}
builtin BitwiseAnd(implicit context: Context)(
left: JSAny, right: JSAny): Object {
return Generate_BitwiseBinaryOp(Operation::kBitwiseAnd, left, right, context);
}
builtin BitwiseOr(implicit context: Context)(
left: JSAny, right: JSAny): Object {
return Generate_BitwiseBinaryOp(Operation::kBitwiseOr, left, right, context);
}
builtin BitwiseXor(implicit context: Context)(
left: JSAny, right: JSAny): Object {
return Generate_BitwiseBinaryOp(Operation::kBitwiseXor, left, right, context);
}
// Relational builtins.
builtin LessThan(implicit context: Context)(left: JSAny, right: JSAny): Object {
return RelationalComparison(Operation::kLessThan, left, right, context);
}
builtin LessThanOrEqual(implicit context: Context)(
left: JSAny, right: JSAny): Object {
return RelationalComparison(
Operation::kLessThanOrEqual, left, right, context);
}
builtin GreaterThan(implicit context: Context)(
left: JSAny, right: JSAny): Object {
return RelationalComparison(Operation::kGreaterThan, left, right, context);
}
builtin GreaterThanOrEqual(implicit context: Context)(
left: JSAny, right: JSAny): Object {
return RelationalComparison(
Operation::kGreaterThanOrEqual, left, right, context);
}
builtin Equal(implicit context: Context)(left: JSAny, right: JSAny): Object {
return Equal(left, right, context);
return runtime::DoubleToStringWithRadix(x, radixNumber);
}
builtin StrictEqual(implicit context: Context)(
left: JSAny, right: JSAny): Object {
return StrictEqualImpl(left, right);
}
} // namespace number
......@@ -191,6 +191,11 @@ void AbortDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void AllocateHeapNumberDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
data->InitializePlatformSpecific(0, nullptr);
}
void CompareDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {r1, r0};
......
......@@ -191,6 +191,11 @@ void AbortDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void AllocateHeapNumberDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
data->InitializePlatformSpecific(0, nullptr);
}
void CompareDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// x1: left operand
......
......@@ -195,6 +195,12 @@ void AbortDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void AllocateHeapNumberDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// register state
data->InitializePlatformSpecific(0, nullptr);
}
void CompareDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {edx, eax};
......
......@@ -83,6 +83,7 @@ void CallDescriptors::InitializeOncePerProcess() {
DCHECK(ContextOnlyDescriptor{}.HasContextParameter());
DCHECK(!NoContextDescriptor{}.HasContextParameter());
DCHECK(!AllocateDescriptor{}.HasContextParameter());
DCHECK(!AllocateHeapNumberDescriptor{}.HasContextParameter());
DCHECK(!AbortDescriptor{}.HasContextParameter());
DCHECK(!WasmFloat32ToNumberDescriptor{}.HasContextParameter());
DCHECK(!WasmFloat64ToNumberDescriptor{}.HasContextParameter());
......
......@@ -23,6 +23,7 @@ namespace internal {
#define INTERFACE_DESCRIPTOR_LIST(V) \
V(Abort) \
V(Allocate) \
V(AllocateHeapNumber) \
V(ApiCallback) \
V(ApiGetter) \
V(ArgumentsAdaptor) \
......@@ -1056,6 +1057,13 @@ class AbortDescriptor : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR(AbortDescriptor, CallInterfaceDescriptor)
};
class AllocateHeapNumberDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS_NO_CONTEXT()
DEFINE_PARAMETER_TYPES()
DECLARE_DESCRIPTOR(AllocateHeapNumberDescriptor, CallInterfaceDescriptor)
};
class ArrayConstructorDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_JS_PARAMETERS(kAllocationSite)
......
......@@ -217,6 +217,12 @@ void AbortDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void AllocateHeapNumberDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// register state
data->InitializePlatformSpecific(0, nullptr);
}
void CompareDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {a1, a0};
......
......@@ -217,6 +217,12 @@ void AbortDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void AllocateHeapNumberDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
// register state
data->InitializePlatformSpecific(0, nullptr);
}
void CompareDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {a1, a0};
......
......@@ -191,6 +191,11 @@ void AbortDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void AllocateHeapNumberDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
data->InitializePlatformSpecific(0, nullptr);
}
void CompareDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {r4, r3};
......
......@@ -191,6 +191,11 @@ void AbortDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void AllocateHeapNumberDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
data->InitializePlatformSpecific(0, nullptr);
}
void CompareDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {r3, r2};
......
......@@ -233,6 +233,11 @@ void AbortDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void AllocateHeapNumberDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
data->InitializePlatformSpecific(0, nullptr);
}
void CompareDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {rdx, rax};
......
......@@ -151,8 +151,6 @@ constexpr int kMaxInt16 = (1 << 15) - 1;
constexpr int kMinInt16 = -(1 << 15);
constexpr int kMaxUInt16 = (1 << 16) - 1;
constexpr int kMinUInt16 = 0;
constexpr int kMaxInt31 = kMaxInt / 2;
constexpr int kMinInt31 = kMinInt / 2;
constexpr uint32_t kMaxUInt32 = 0xFFFFFFFFu;
constexpr int kMinUInt32 = 0;
......
......@@ -5,16 +5,8 @@
@abstract
@generateCppClass
extern class Name extends PrimitiveHeapObject {
hash_field: NameHash;
hash_field: uint32;
}
bitfield struct NameHash extends uint32 {
hash_not_commputed: bool: 1 bit;
is_not_integer_index_mask: bool: 1 bit;
array_index_value: uint32: 24 bit;
array_index_length: uint32: 6 bit;
}
// This is the same as Name, but with the information that there are no other
// kinds of names.
type AnyName = PrivateSymbol|PublicSymbol|String;
......@@ -37,12 +29,5 @@ extern class Symbol extends Name {
type PublicSymbol extends Symbol;
type PrivateSymbol extends Symbol;
const kNameEmptyHashField: NameHash = NameHash{
hash_not_commputed: true,
is_not_integer_index_mask: true,
array_index_value: 0,
array_index_length: 0
};
const kMaxCachedArrayIndexLength: constexpr uint32
generates 'Name::kMaxCachedArrayIndexLength';
const kNameEmptyHashField:
constexpr uint32 generates 'Name::kEmptyHashField';
......@@ -72,6 +72,7 @@ struct WasmModule;
V(WasmThrow) \
V(WasmRethrow) \
V(WasmTraceMemory) \
V(AllocateHeapNumber) \
V(ArgumentsAdaptorTrampoline) \
V(BigIntToI32Pair) \
V(BigIntToI64) \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment