[bigint] Support BigInts in -,~,++,-- unary ops

and add the implementations for BitwiseNot, Increment, Decrement.
This CL teaches the respective bytecode handlers about BigInts,
and collects kBigInt type feedback for them (which TF discards
for now, substituting "any").

Bug: v8:6791
Change-Id: I4e802b301b9702d8270bda400edd7e885e6b11b9
Reviewed-on: https://chromium-review.googlesource.com/706101
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Adam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48479}
This commit is contained in:
Jakob Kummerow 2017-10-11 17:50:06 -07:00 committed by Commit Bot
parent 28ef8dc700
commit e34debaf2b
13 changed files with 330 additions and 418 deletions

View File

@ -3594,7 +3594,7 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
// TODO(ignition): Think about adding proper PostInc/PostDec bytecodes
// instead of this ToNumber + Inc/Dec dance.
builder()
->ToNumber(feedback_index(count_slot))
->ToNumeric(feedback_index(count_slot))
.StoreAccumulatorInRegister(old_value);
}

View File

@ -1328,92 +1328,6 @@ Node* InterpreterAssembler::TaggedToWord32OrBigIntWithFeedback(
return var_result.value();
}
Node* InterpreterAssembler::TruncateTaggedToWord32WithFeedback(
Node* context, Node* value, Variable* var_type_feedback) {
// We might need to loop once due to ToNumber conversion.
Variable var_value(this, MachineRepresentation::kTagged),
var_result(this, MachineRepresentation::kWord32);
Variable* loop_vars[] = {&var_value, var_type_feedback};
Label loop(this, 2, loop_vars), done_loop(this, &var_result);
var_value.Bind(value);
var_type_feedback->Bind(SmiConstant(BinaryOperationFeedback::kNone));
Goto(&loop);
BIND(&loop);
{
// Load the current {value}.
value = var_value.value();
// Check if the {value} is a Smi or a HeapObject.
Label if_valueissmi(this), if_valueisnotsmi(this);
Branch(TaggedIsSmi(value), &if_valueissmi, &if_valueisnotsmi);
BIND(&if_valueissmi);
{
// Convert the Smi {value}.
var_result.Bind(SmiToWord32(value));
var_type_feedback->Bind(
SmiOr(var_type_feedback->value(),
SmiConstant(BinaryOperationFeedback::kSignedSmall)));
Goto(&done_loop);
}
BIND(&if_valueisnotsmi);
{
// Check if {value} is a HeapNumber.
Label if_valueisheapnumber(this),
if_valueisnotheapnumber(this, Label::kDeferred);
Node* value_map = LoadMap(value);
Branch(IsHeapNumberMap(value_map), &if_valueisheapnumber,
&if_valueisnotheapnumber);
BIND(&if_valueisheapnumber);
{
// Truncate the floating point value.
var_result.Bind(TruncateHeapNumberValueToWord32(value));
var_type_feedback->Bind(
SmiOr(var_type_feedback->value(),
SmiConstant(BinaryOperationFeedback::kNumber)));
Goto(&done_loop);
}
BIND(&if_valueisnotheapnumber);
{
// We do not require an Or with earlier feedback here because once we
// convert the value to a number, we cannot reach this path. We can
// only reach this path on the first pass when the feedback is kNone.
CSA_ASSERT(this, SmiEqual(var_type_feedback->value(),
SmiConstant(BinaryOperationFeedback::kNone)));
Label if_valueisoddball(this),
if_valueisnotoddball(this, Label::kDeferred);
Node* is_oddball = Word32Equal(LoadMapInstanceType(value_map),
Int32Constant(ODDBALL_TYPE));
Branch(is_oddball, &if_valueisoddball, &if_valueisnotoddball);
BIND(&if_valueisoddball);
{
// Convert Oddball to a Number and perform checks again.
var_value.Bind(LoadObjectField(value, Oddball::kToNumberOffset));
var_type_feedback->Bind(
SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
Goto(&loop);
}
BIND(&if_valueisnotoddball);
{
// Convert the {value} to a Number first.
var_value.Bind(
CallBuiltin(Builtins::kNonNumberToNumber, context, value));
var_type_feedback->Bind(SmiConstant(BinaryOperationFeedback::kAny));
Goto(&loop);
}
}
}
}
BIND(&done_loop);
return var_result.value();
}
void InterpreterAssembler::UpdateInterruptBudgetOnReturn() {
// TODO(rmcilroy): Investigate whether it is worth supporting self
// optimization of primitive functions like FullCodegen.

View File

@ -221,12 +221,7 @@ class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler {
// Dispatch bytecode as wide operand variant.
void DispatchWide(OperandScale operand_scale);
// Truncate tagged |value| to word32 and store the type feedback in
// |var_type_feedback|.
compiler::Node* TruncateTaggedToWord32WithFeedback(
compiler::Node* context, compiler::Node* value,
Variable* var_type_feedback);
// Truncate tagged |value| to word32, or find that it is a BigInt and jump,
// Truncate tagged |value| to word32, or find that it is a BigInt and jump
// to {bigint}, and store the type feedback in |var_type_feedback|.
compiler::Node* TaggedToWord32OrBigIntWithFeedback(
compiler::Node* context, compiler::Node* value,

View File

@ -1115,18 +1115,30 @@ IGNITION_HANDLER(BitwiseNot, InterpreterAssembler) {
Node* feedback_vector = LoadFeedbackVector();
Node* context = GetContext();
Variable var_type_feedback(this, MachineRepresentation::kTaggedSigned);
Node* truncated_value =
TruncateTaggedToWord32WithFeedback(context, operand, &var_type_feedback);
VARIABLE(var_feedback, MachineRepresentation::kTaggedSigned);
VARIABLE(var_bigint, MachineRepresentation::kTagged);
Label if_bigint(this);
Node* truncated_value = TaggedToWord32OrBigIntWithFeedback(
context, operand, &var_feedback, &if_bigint, &var_bigint);
// Number case.
Node* value = Word32Not(truncated_value);
Node* result = ChangeInt32ToTagged(value);
Node* result_type = SelectSmiConstant(TaggedIsSmi(result),
BinaryOperationFeedback::kSignedSmall,
BinaryOperationFeedback::kNumber);
UpdateFeedback(SmiOr(result_type, var_type_feedback.value()), feedback_vector,
UpdateFeedback(SmiOr(result_type, var_feedback.value()), feedback_vector,
slot_index);
SetAccumulator(result);
Dispatch();
// BigInt case.
BIND(&if_bigint);
UpdateFeedback(SmiConstant(BinaryOperationFeedback::kBigInt), feedback_vector,
slot_index);
SetAccumulator(CallRuntime(Runtime::kBigIntUnaryOp, context,
var_bigint.value(), SmiConstant(Token::BIT_NOT)));
Dispatch();
}
// ShiftLeftSmi <imm>
@ -1156,78 +1168,187 @@ IGNITION_HANDLER(ShiftRightLogicalSmi, InterpreterBitwiseBinaryOpAssembler) {
BitwiseBinaryOpWithSmi(Token::SHR);
}
class UnaryNumericOpAssembler : public InterpreterAssembler {
public:
UnaryNumericOpAssembler(CodeAssemblerState* state, Bytecode bytecode,
OperandScale operand_scale)
: InterpreterAssembler(state, bytecode, operand_scale) {}
// {smi_op} signature: (Node* smi_value, Variable* var_feedback,
// Label* do_float_op, Variable* var_float) => Node* tagged result.
// {float_op} signature: (Node* float_value) => Node* float_result.
// {bigint_op} signature: (Node* bigint_value) => Node* tagged_result.
void UnaryOpWithFeedback(
std::function<Node*(Node*, Variable*, Label*, Variable*)> smi_op,
std::function<Node*(Node*)> float_op,
std::function<Node*(Node*)> bigint_op) {
VARIABLE(var_value, MachineRepresentation::kTagged, GetAccumulator());
Node* slot_index = BytecodeOperandIdx(0);
Node* feedback_vector = LoadFeedbackVector();
VARIABLE(var_result, MachineRepresentation::kTagged);
VARIABLE(var_float_value, MachineRepresentation::kFloat64);
VARIABLE(var_feedback, MachineRepresentation::kTaggedSigned,
SmiConstant(BinaryOperationFeedback::kNone));
Variable* loop_vars[] = {&var_value, &var_feedback};
Label start(this, arraysize(loop_vars), loop_vars), end(this);
Label do_float_op(this, &var_float_value);
Goto(&start);
// We might have to try again after ToNumeric conversion.
BIND(&start);
{
Label if_smi(this), if_heapnumber(this), if_bigint(this);
Label if_oddball(this), if_other(this);
Node* value = var_value.value();
GotoIf(TaggedIsSmi(value), &if_smi);
Node* map = LoadMap(value);
GotoIf(IsHeapNumberMap(map), &if_heapnumber);
Node* instance_type = LoadMapInstanceType(map);
GotoIf(IsBigIntInstanceType(instance_type), &if_bigint);
Branch(Word32Equal(instance_type, Int32Constant(ODDBALL_TYPE)),
&if_oddball, &if_other);
BIND(&if_smi);
{
var_result.Bind(
smi_op(value, &var_feedback, &do_float_op, &var_float_value));
Goto(&end);
}
BIND(&if_heapnumber);
{
var_float_value.Bind(LoadHeapNumberValue(value));
Goto(&do_float_op);
}
BIND(&if_bigint);
{
var_result.Bind(bigint_op(value));
var_feedback.Bind(SmiOr(var_feedback.value(),
SmiConstant(BinaryOperationFeedback::kBigInt)));
Goto(&end);
}
BIND(&if_oddball);
{
// We do not require an Or with earlier feedback here because once we
// convert the value to a number, we cannot reach this path. We can
// only reach this path on the first pass when the feedback is kNone.
CSA_ASSERT(this, SmiEqual(var_feedback.value(),
SmiConstant(BinaryOperationFeedback::kNone)));
var_feedback.Bind(
SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
var_value.Bind(LoadObjectField(value, Oddball::kToNumberOffset));
Goto(&start);
}
BIND(&if_other);
{
// We do not require an Or with earlier feedback here because once we
// convert the value to a number, we cannot reach this path. We can
// only reach this path on the first pass when the feedback is kNone.
CSA_ASSERT(this, SmiEqual(var_feedback.value(),
SmiConstant(BinaryOperationFeedback::kNone)));
var_feedback.Bind(SmiConstant(BinaryOperationFeedback::kAny));
// TODO(jkummerow): This should call kNonNumericToNumeric.
var_value.Bind(
CallBuiltin(Builtins::kNonNumberToNumber, GetContext(), value));
Goto(&start);
}
}
BIND(&do_float_op);
{
var_feedback.Bind(SmiOr(var_feedback.value(),
SmiConstant(BinaryOperationFeedback::kNumber)));
var_result.Bind(
AllocateHeapNumberWithValue(float_op(var_float_value.value())));
Goto(&end);
}
BIND(&end);
UpdateFeedback(var_feedback.value(), feedback_vector, slot_index);
SetAccumulator(var_result.value());
Dispatch();
}
void IncDecWithFeedback(Token::Value op) {
DCHECK(op == Token::INC || op == Token::DEC);
UnaryOpWithFeedback(
[=](Node* smi_value, Variable* var_feedback, Label* do_float_op,
Variable* var_float) {
// Try fast Smi operation first.
Node* value = BitcastTaggedToWord(smi_value);
Node* one = BitcastTaggedToWord(SmiConstant(1));
Node* pair = op == Token::INC ? IntPtrAddWithOverflow(value, one)
: IntPtrSubWithOverflow(value, one);
Node* overflow = Projection(1, pair);
// Check if the Smi operation overflowed.
Label if_overflow(this), if_notoverflow(this);
Branch(overflow, &if_overflow, &if_notoverflow);
BIND(&if_overflow);
{
var_float->Bind(SmiToFloat64(smi_value));
Goto(do_float_op);
}
BIND(&if_notoverflow);
var_feedback->Bind(
SmiOr(var_feedback->value(),
SmiConstant(BinaryOperationFeedback::kSignedSmall)));
return BitcastWordToTaggedSigned(Projection(0, pair));
},
[=](Node* float_value) {
return op == Token::INC
? Float64Add(float_value, Float64Constant(1.0))
: Float64Sub(float_value, Float64Constant(1.0));
},
[=](Node* bigint_value) {
return CallRuntime(Runtime::kBigIntUnaryOp, GetContext(),
bigint_value, SmiConstant(op));
});
}
};
// Negate <feedback_slot>
//
// Perform arithmetic negation on the accumulator.
IGNITION_HANDLER(Negate, InterpreterAssembler) {
Node* operand = GetAccumulator();
IGNITION_HANDLER(Negate, UnaryNumericOpAssembler) {
UnaryOpWithFeedback(
[=](Node* operand, Variable* var_feedback, Label* do_float_op,
Variable* var_float) {
VARIABLE(var_result, MachineRepresentation::kTagged);
Label if_zero(this), if_min_smi(this), end(this);
// Return -0 if operand is 0.
GotoIf(SmiEqual(operand, SmiConstant(0)), &if_zero);
Label end(this);
VARIABLE(var_type_feedback, MachineRepresentation::kTaggedSigned);
VARIABLE(var_result, MachineRepresentation::kTagged);
// Special-case the minimum Smi to avoid overflow.
GotoIf(SmiEqual(operand, SmiConstant(Smi::kMinValue)), &if_min_smi);
Label if_smi(this), if_heapnumber(this), if_notnumber(this, Label::kDeferred);
GotoIf(TaggedIsSmi(operand), &if_smi);
Branch(IsHeapNumber(operand), &if_heapnumber, &if_notnumber);
// Else simply subtract operand from 0.
var_feedback->Bind(SmiConstant(BinaryOperationFeedback::kSignedSmall));
var_result.Bind(SmiSub(SmiConstant(0), operand));
Goto(&end);
BIND(&if_smi);
{
Label if_zero(this), if_min_smi(this);
// Return -0 if operand is 0.
GotoIf(SmiEqual(operand, SmiConstant(0)), &if_zero);
BIND(&if_zero);
var_feedback->Bind(SmiConstant(BinaryOperationFeedback::kNumber));
var_result.Bind(MinusZeroConstant());
Goto(&end);
// Special-case the minimum smi to avoid overflow.
GotoIf(SmiEqual(operand, SmiConstant(Smi::kMinValue)), &if_min_smi);
BIND(&if_min_smi);
var_float->Bind(SmiToFloat64(operand));
Goto(do_float_op);
// Else simply subtract operand from 0.
var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kSignedSmall));
var_result.Bind(SmiSub(SmiConstant(0), operand));
Goto(&end);
BIND(&if_zero);
var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kNumber));
var_result.Bind(MinusZeroConstant());
Goto(&end);
BIND(&if_min_smi);
var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kNumber));
var_result.Bind(AllocateHeapNumberWithValue(
Float64Constant(-static_cast<double>(Smi::kMinValue))));
Goto(&end);
}
BIND(&if_heapnumber);
{
Node* result = Float64Neg(LoadHeapNumberValue(operand));
var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kNumber));
var_result.Bind(AllocateHeapNumberWithValue(result));
Goto(&end);
}
BIND(&if_notnumber);
{
Node* instance_type = LoadInstanceType(operand);
Node* is_oddball = Word32Equal(instance_type, Int32Constant(ODDBALL_TYPE));
var_type_feedback.Bind(
SelectSmiConstant(is_oddball, BinaryOperationFeedback::kNumberOrOddball,
BinaryOperationFeedback::kAny));
Node* context = GetContext();
Node* result =
CallBuiltin(Builtins::kMultiply, context, operand, SmiConstant(-1));
var_result.Bind(result);
Goto(&end);
}
BIND(&end);
Node* slot_index = BytecodeOperandIdx(0);
Node* feedback_vector = LoadFeedbackVector();
UpdateFeedback(var_type_feedback.value(), feedback_vector, slot_index);
SetAccumulator(var_result.value());
Dispatch();
BIND(&end);
return var_result.value();
},
[=](Node* float_value) { return Float64Neg(float_value); },
[=](Node* bigint_value) {
return CallRuntime(Runtime::kBigIntUnaryOp, GetContext(), bigint_value,
SmiConstant(Token::SUB));
});
}
// ToName <dst>
@ -1271,247 +1392,15 @@ IGNITION_HANDLER(ToObject, InterpreterAssembler) {
// Inc
//
// Increments value in the accumulator by one.
IGNITION_HANDLER(Inc, InterpreterAssembler) {
Node* value = GetAccumulator();
Node* context = GetContext();
Node* slot_index = BytecodeOperandIdx(0);
Node* feedback_vector = LoadFeedbackVector();
// Shared entry for floating point increment.
Label do_finc(this), end(this);
Variable var_finc_value(this, MachineRepresentation::kFloat64);
// We might need to try again due to ToNumber conversion.
Variable value_var(this, MachineRepresentation::kTagged);
Variable result_var(this, MachineRepresentation::kTagged);
Variable var_type_feedback(this, MachineRepresentation::kTaggedSigned);
Variable* loop_vars[] = {&value_var, &var_type_feedback};
Label start(this, 2, loop_vars);
value_var.Bind(value);
var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kNone));
Goto(&start);
BIND(&start);
{
value = value_var.value();
Label if_issmi(this), if_isnotsmi(this);
Branch(TaggedIsSmi(value), &if_issmi, &if_isnotsmi);
BIND(&if_issmi);
{
// Try fast Smi addition first.
Node* one = SmiConstant(1);
Node* pair = IntPtrAddWithOverflow(BitcastTaggedToWord(value),
BitcastTaggedToWord(one));
Node* overflow = Projection(1, pair);
// Check if the Smi addition overflowed.
Label if_overflow(this), if_notoverflow(this);
Branch(overflow, &if_overflow, &if_notoverflow);
BIND(&if_notoverflow);
var_type_feedback.Bind(
SmiOr(var_type_feedback.value(),
SmiConstant(BinaryOperationFeedback::kSignedSmall)));
result_var.Bind(BitcastWordToTaggedSigned(Projection(0, pair)));
Goto(&end);
BIND(&if_overflow);
{
var_finc_value.Bind(SmiToFloat64(value));
Goto(&do_finc);
}
}
BIND(&if_isnotsmi);
{
// Check if the value is a HeapNumber.
Label if_valueisnumber(this), if_valuenotnumber(this, Label::kDeferred);
Node* value_map = LoadMap(value);
Branch(IsHeapNumberMap(value_map), &if_valueisnumber, &if_valuenotnumber);
BIND(&if_valueisnumber);
{
// Load the HeapNumber value.
var_finc_value.Bind(LoadHeapNumberValue(value));
Goto(&do_finc);
}
BIND(&if_valuenotnumber);
{
// We do not require an Or with earlier feedback here because once we
// convert the value to a number, we cannot reach this path. We can
// only reach this path on the first pass when the feedback is kNone.
CSA_ASSERT(this, SmiEqual(var_type_feedback.value(),
SmiConstant(BinaryOperationFeedback::kNone)));
Label if_valueisoddball(this), if_valuenotoddball(this);
Node* instance_type = LoadMapInstanceType(value_map);
Node* is_oddball =
Word32Equal(instance_type, Int32Constant(ODDBALL_TYPE));
Branch(is_oddball, &if_valueisoddball, &if_valuenotoddball);
BIND(&if_valueisoddball);
{
// Convert Oddball to Number and check again.
value_var.Bind(LoadObjectField(value, Oddball::kToNumberOffset));
var_type_feedback.Bind(
SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
Goto(&start);
}
BIND(&if_valuenotoddball);
{
// Convert to a Number first and try again.
var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kAny));
value_var.Bind(
CallBuiltin(Builtins::kNonNumberToNumber, context, value));
Goto(&start);
}
}
}
}
BIND(&do_finc);
{
Node* finc_value = var_finc_value.value();
Node* one = Float64Constant(1.0);
Node* finc_result = Float64Add(finc_value, one);
var_type_feedback.Bind(
SmiOr(var_type_feedback.value(),
SmiConstant(BinaryOperationFeedback::kNumber)));
result_var.Bind(AllocateHeapNumberWithValue(finc_result));
Goto(&end);
}
BIND(&end);
UpdateFeedback(var_type_feedback.value(), feedback_vector, slot_index);
SetAccumulator(result_var.value());
Dispatch();
IGNITION_HANDLER(Inc, UnaryNumericOpAssembler) {
IncDecWithFeedback(Token::INC);
}
// Dec
//
// Decrements value in the accumulator by one.
IGNITION_HANDLER(Dec, InterpreterAssembler) {
Node* value = GetAccumulator();
Node* context = GetContext();
Node* slot_index = BytecodeOperandIdx(0);
Node* feedback_vector = LoadFeedbackVector();
// Shared entry for floating point decrement.
Label do_fdec(this), end(this);
Variable var_fdec_value(this, MachineRepresentation::kFloat64);
// We might need to try again due to ToNumber conversion.
Variable value_var(this, MachineRepresentation::kTagged);
Variable result_var(this, MachineRepresentation::kTagged);
Variable var_type_feedback(this, MachineRepresentation::kTaggedSigned);
Variable* loop_vars[] = {&value_var, &var_type_feedback};
Label start(this, 2, loop_vars);
var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kNone));
value_var.Bind(value);
Goto(&start);
BIND(&start);
{
value = value_var.value();
Label if_issmi(this), if_isnotsmi(this);
Branch(TaggedIsSmi(value), &if_issmi, &if_isnotsmi);
BIND(&if_issmi);
{
// Try fast Smi subtraction first.
Node* one = SmiConstant(1);
Node* pair = IntPtrSubWithOverflow(BitcastTaggedToWord(value),
BitcastTaggedToWord(one));
Node* overflow = Projection(1, pair);
// Check if the Smi subtraction overflowed.
Label if_overflow(this), if_notoverflow(this);
Branch(overflow, &if_overflow, &if_notoverflow);
BIND(&if_notoverflow);
var_type_feedback.Bind(
SmiOr(var_type_feedback.value(),
SmiConstant(BinaryOperationFeedback::kSignedSmall)));
result_var.Bind(BitcastWordToTaggedSigned(Projection(0, pair)));
Goto(&end);
BIND(&if_overflow);
{
var_fdec_value.Bind(SmiToFloat64(value));
Goto(&do_fdec);
}
}
BIND(&if_isnotsmi);
{
// Check if the value is a HeapNumber.
Label if_valueisnumber(this), if_valuenotnumber(this, Label::kDeferred);
Node* value_map = LoadMap(value);
Branch(IsHeapNumberMap(value_map), &if_valueisnumber, &if_valuenotnumber);
BIND(&if_valueisnumber);
{
// Load the HeapNumber value.
var_fdec_value.Bind(LoadHeapNumberValue(value));
Goto(&do_fdec);
}
BIND(&if_valuenotnumber);
{
// We do not require an Or with earlier feedback here because once we
// convert the value to a number, we cannot reach this path. We can
// only reach this path on the first pass when the feedback is kNone.
CSA_ASSERT(this, SmiEqual(var_type_feedback.value(),
SmiConstant(BinaryOperationFeedback::kNone)));
Label if_valueisoddball(this), if_valuenotoddball(this);
Node* instance_type = LoadMapInstanceType(value_map);
Node* is_oddball =
Word32Equal(instance_type, Int32Constant(ODDBALL_TYPE));
Branch(is_oddball, &if_valueisoddball, &if_valuenotoddball);
BIND(&if_valueisoddball);
{
// Convert Oddball to Number and check again.
value_var.Bind(LoadObjectField(value, Oddball::kToNumberOffset));
var_type_feedback.Bind(
SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
Goto(&start);
}
BIND(&if_valuenotoddball);
{
// Convert to a Number first and try again.
var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kAny));
value_var.Bind(
CallBuiltin(Builtins::kNonNumberToNumber, context, value));
Goto(&start);
}
}
}
}
BIND(&do_fdec);
{
Node* fdec_value = var_fdec_value.value();
Node* one = Float64Constant(1.0);
Node* fdec_result = Float64Sub(fdec_value, one);
var_type_feedback.Bind(
SmiOr(var_type_feedback.value(),
SmiConstant(BinaryOperationFeedback::kNumber)));
result_var.Bind(AllocateHeapNumberWithValue(fdec_result));
Goto(&end);
}
BIND(&end);
UpdateFeedback(var_type_feedback.value(), feedback_vector, slot_index);
SetAccumulator(result_var.value());
Dispatch();
IGNITION_HANDLER(Dec, UnaryNumericOpAssembler) {
IncDecWithFeedback(Token::DEC);
}
// LogicalNot

View File

@ -34,8 +34,18 @@ Handle<BigInt> BigInt::UnaryMinus(Handle<BigInt> x) {
return result;
}
Handle<BigInt> BigInt::BitwiseNot(Handle<BigInt> x) {
UNIMPLEMENTED(); // TODO(jkummerow): Implement.
MaybeHandle<BigInt> BigInt::BitwiseNot(Handle<BigInt> x) {
Handle<BigInt> result;
int length = x->length();
if (x->sign()) {
// ~(-x) == ~(~(x-1)) == x-1
result = AbsoluteSubOne(x, length);
} else {
// ~x == -x-1 == -(x+1)
result = AbsoluteAddOne(x, true);
}
result->RightTrim();
return result;
}
MaybeHandle<BigInt> BigInt::Exponentiate(Handle<BigInt> base,
@ -237,6 +247,34 @@ Handle<BigInt> BigInt::BitwiseOr(Handle<BigInt> x, Handle<BigInt> y) {
return result;
}
MaybeHandle<BigInt> BigInt::Increment(Handle<BigInt> x) {
int length = x->length();
Handle<BigInt> result;
if (x->sign()) {
result = AbsoluteSubOne(x, length);
result->set_sign(true);
} else {
result = AbsoluteAddOne(x, false);
}
result->RightTrim();
return result;
}
MaybeHandle<BigInt> BigInt::Decrement(Handle<BigInt> x) {
int length = x->length();
Handle<BigInt> result;
if (x->sign()) {
result = AbsoluteAddOne(x, true);
} else if (x->is_zero()) {
// TODO(jkummerow): Consider caching a canonical -1n BigInt.
result = x->GetIsolate()->factory()->NewBigIntFromInt(-1);
} else {
result = AbsoluteSubOne(x, length);
}
result->RightTrim();
return result;
}
MaybeHandle<String> BigInt::ToString(Handle<BigInt> bigint, int radix) {
Isolate* isolate = bigint->GetIsolate();
if (bigint->is_zero()) {
@ -343,17 +381,31 @@ Handle<BigInt> BigInt::AbsoluteSub(Handle<BigInt> x, Handle<BigInt> y,
return result;
}
// Adds 1 to the absolute value of {x}, stores the result in {result_storage}
// and sets its sign to {sign}.
// Adds 1 to the absolute value of {x} and sets the result's sign to {sign}.
// {result_storage} is optional; if present, it will be used to store the
// result, otherwise a new BigInt will be allocated for the result.
// {result_storage} and {x} may refer to the same BigInt for in-place
// modification.
Handle<BigInt> BigInt::AbsoluteAddOne(Handle<BigInt> x, bool sign,
BigInt* result_storage) {
DCHECK(result_storage != nullptr);
int input_length = x->length();
int result_length = result_storage->length();
// The addition will overflow into a new digit if all existing digits are
// at maximum.
bool will_overflow = true;
for (int i = 0; i < input_length; i++) {
if (!digit_ismax(x->digit(i))) {
will_overflow = false;
break;
}
}
int result_length = input_length + will_overflow;
Isolate* isolate = x->GetIsolate();
Handle<BigInt> result(result_storage, isolate);
if (result_storage == nullptr) {
result = isolate->factory()->NewBigIntRaw(result_length);
} else {
DCHECK(result->length() == result_length);
}
digit_t carry = 1;
for (int i = 0; i < input_length; i++) {
digit_t new_carry = 0;

View File

@ -23,7 +23,7 @@ class BigInt : public HeapObject {
// https://tc39.github.io/proposal-bigint/#sec-numeric-types
// Sections 1.1.1 through 1.1.19.
static Handle<BigInt> UnaryMinus(Handle<BigInt> x);
static Handle<BigInt> BitwiseNot(Handle<BigInt> x);
static MaybeHandle<BigInt> BitwiseNot(Handle<BigInt> x);
static MaybeHandle<BigInt> Exponentiate(Handle<BigInt> base,
Handle<BigInt> exponent);
static Handle<BigInt> Multiply(Handle<BigInt> x, Handle<BigInt> y);
@ -42,6 +42,9 @@ class BigInt : public HeapObject {
static Handle<BigInt> BitwiseXor(Handle<BigInt> x, Handle<BigInt> y);
static Handle<BigInt> BitwiseOr(Handle<BigInt> x, Handle<BigInt> y);
static MaybeHandle<BigInt> Increment(Handle<BigInt> x);
static MaybeHandle<BigInt> Decrement(Handle<BigInt> x);
// Other parts of the public interface.
bool ToBoolean() { return !is_zero(); }
uint32_t Hash() {
@ -93,7 +96,7 @@ class BigInt : public HeapObject {
static Handle<BigInt> AbsoluteSub(Handle<BigInt> x, Handle<BigInt> y,
bool result_sign);
static Handle<BigInt> AbsoluteAddOne(Handle<BigInt> x, bool sign,
BigInt* result_storage);
BigInt* result_storage = nullptr);
static Handle<BigInt> AbsoluteSubOne(Handle<BigInt> x, int result_length);
enum ExtraDigitsHandling { kCopy, kSkip };

View File

@ -84,5 +84,31 @@ RUNTIME_FUNCTION(Runtime_BigIntBinaryOp) {
RETURN_RESULT_OR_FAILURE(isolate, result);
}
RUNTIME_FUNCTION(Runtime_BigIntUnaryOp) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(BigInt, x, 0);
CONVERT_SMI_ARG_CHECKED(opcode, 1);
MaybeHandle<BigInt> result;
switch (opcode) {
case Token::BIT_NOT:
result = BigInt::BitwiseNot(x);
break;
case Token::SUB:
result = BigInt::UnaryMinus(x);
break;
case Token::INC:
result = BigInt::Increment(x);
break;
case Token::DEC:
result = BigInt::Decrement(x);
break;
default:
UNREACHABLE();
}
RETURN_RESULT_OR_FAILURE(isolate, result);
}
} // namespace internal
} // namespace v8

View File

@ -71,7 +71,8 @@ namespace internal {
#define FOR_EACH_INTRINSIC_BIGINT(F) \
F(BigIntBinaryOp, 3, 1) \
F(BigIntEqual, 2, 1) \
F(BigIntToBoolean, 1, 1)
F(BigIntToBoolean, 1, 1) \
F(BigIntUnaryOp, 2, 1)
#define FOR_EACH_INTRINSIC_CLASSES(F) \
F(ThrowUnsupportedSuperError, 0, 1) \

View File

@ -249,7 +249,7 @@ bytecodes: [
/* 55 E> */ B(Add), R(1), U8(0),
B(Star), R(1),
B(Ldar), R(0),
B(ToNumber), U8(1),
B(ToNumeric), U8(1),
B(Star), R(2),
B(Inc), U8(1),
B(Star), R(0),

View File

@ -36,7 +36,7 @@ bytecodes: [
/* 30 E> */ B(StackCheck),
/* 42 S> */ B(LdaSmi), I8(1),
B(Star), R(0),
/* 45 S> */ B(ToNumber), U8(0),
/* 45 S> */ B(ToNumeric), U8(0),
B(Star), R(1),
B(Inc), U8(0),
B(Star), R(0),
@ -79,7 +79,7 @@ bytecodes: [
/* 30 E> */ B(StackCheck),
/* 42 S> */ B(LdaSmi), I8(1),
B(Star), R(0),
/* 45 S> */ B(ToNumber), U8(0),
/* 45 S> */ B(ToNumeric), U8(0),
B(Star), R(1),
B(Dec), U8(0),
B(Star), R(0),
@ -103,7 +103,7 @@ bytecodes: [
/* 42 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(41), R(1),
B(Mov), R(1), R(0),
/* 54 S> */ B(LdaNamedProperty), R(1), U8(1), U8(1),
B(ToNumber), U8(5),
B(ToNumeric), U8(5),
B(Star), R(2),
B(Inc), U8(5),
/* 66 E> */ B(StaNamedProperty), R(1), U8(1), U8(3),
@ -155,7 +155,7 @@ bytecodes: [
B(Mov), R(2), R(1),
/* 72 S> */ B(Ldar), R(0),
/* 81 E> */ B(LdaKeyedProperty), R(2), U8(1),
B(ToNumber), U8(5),
B(ToNumeric), U8(5),
B(Star), R(4),
B(Dec), U8(5),
/* 86 E> */ B(StaKeyedProperty), R(2), R(0), U8(3),
@ -237,7 +237,7 @@ bytecodes: [
/* 53 S> */ B(CreateClosure), U8(0), U8(0), U8(2),
B(Star), R(0),
/* 78 S> */ B(LdaCurrentContextSlot), U8(4),
B(ToNumber), U8(1),
B(ToNumeric), U8(1),
B(Star), R(2),
B(Dec), U8(1),
/* 86 E> */ B(StaCurrentContextSlot), U8(4),
@ -264,7 +264,7 @@ bytecodes: [
/* 55 S> */ B(CreateArrayLiteral), U8(0), U8(0), U8(37),
B(Star), R(1),
/* 63 S> */ B(Ldar), R(0),
B(ToNumber), U8(1),
B(ToNumeric), U8(1),
B(Star), R(3),
B(Inc), U8(1),
B(Star), R(0),

View File

@ -40,7 +40,7 @@ bytecode array length: 16
bytecodes: [
/* 26 E> */ B(StackCheck),
/* 31 S> */ B(LdaGlobal), U8(0), U8(0),
B(ToNumber), U8(4),
B(ToNumeric), U8(4),
B(Star), R(0),
B(Dec), U8(4),
/* 44 E> */ B(StaGlobalSloppy), U8(0), U8(2),
@ -87,7 +87,7 @@ bytecode array length: 16
bytecodes: [
/* 27 E> */ B(StackCheck),
/* 32 S> */ B(LdaGlobal), U8(0), U8(0),
B(ToNumber), U8(4),
B(ToNumeric), U8(4),
B(Star), R(0),
B(Inc), U8(4),
/* 50 E> */ B(StaGlobalSloppy), U8(0), U8(2),

View File

@ -259,7 +259,7 @@ bytecodes: [
/* 34 S> */ B(LdaUndefined),
/* 34 E> */ B(StaCurrentContextSlot), U8(4),
/* 39 S> */ B(LdaModuleVariable), I8(1), U8(1),
B(ToNumber), U8(1),
B(ToNumeric), U8(1),
B(Star), R(4),
B(Inc), U8(1),
/* 42 E> */ B(StaModuleVariable), I8(1), U8(1),
@ -339,7 +339,7 @@ bytecodes: [
/* 34 S> */ B(LdaUndefined),
/* 34 E> */ B(StaCurrentContextSlot), U8(4),
/* 39 S> */ B(LdaModuleVariable), I8(1), U8(1),
B(ToNumber), U8(1),
B(ToNumeric), U8(1),
B(Star), R(4),
B(Inc), U8(1),
/* 42 E> */ B(StaModuleVariable), I8(1), U8(1),
@ -419,7 +419,7 @@ bytecodes: [
/* 36 S> */ B(LdaUndefined),
/* 36 E> */ B(StaCurrentContextSlot), U8(4),
/* 41 S> */ B(LdaModuleVariable), I8(1), U8(1),
B(ToNumber), U8(1),
B(ToNumeric), U8(1),
B(Star), R(4),
B(Inc), U8(1),
/* 44 E> */ B(CallRuntime), U16(Runtime::kThrowConstAssignError), R(0), U8(0),

View File

@ -426,3 +426,35 @@ const six = BigInt(6);
assertThrows("three >>> {valueOf: function() { return 1; }}", TypeError);
assertThrows("({valueOf: function() { return 1; }}) >>> one", TypeError);
}
// Unary ops.
{
assertTrue(~minus_one === zero);
assertTrue(-minus_one === one);
assertTrue(~~two === two);
assertTrue(-(-two) === two);
let a = minus_one;
assertTrue(a++ === minus_one);
assertTrue(a === zero);
assertTrue(a++ === zero);
assertTrue(a === one);
assertTrue(++a === two);
assertTrue(a === two);
assertTrue(--a === one);
assertTrue(a === one);
assertTrue(a-- === one);
assertTrue(a === zero);
assertTrue(a-- === zero);
assertTrue(a === minus_one);
a = {valueOf() { return minus_one }};
assertTrue(a++ === minus_one);
assertTrue(a++ === zero);
assertTrue(a === one);
a = {valueOf() { return one }};
assertTrue(a-- === one);
assertTrue(a-- === zero);
assertTrue(a === minus_one);
}