Commit 15609882 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[bigint] Support BigInts in <<,>>,>>>,&,|,^ binary ops

This CL teaches the respective bytecode handlers and standalone
stubs about BigInts, and collects "kBigInt" type feedback for them.
Just like for other binary ops, that feedback is converted to "any"
for TurboFan for now.

Bug: v8:6791
Change-Id: I0709cc77dc248dad506207c7b35b63c80b1ef96a
Reviewed-on: https://chromium-review.googlesource.com/699424Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48471}
parent 6576495f
......@@ -6,6 +6,7 @@
#include "src/builtins/builtins.h"
#include "src/code-stub-assembler.h"
#include "src/ic/binary-op-assembler.h"
#include "src/parsing/token.h"
namespace v8 {
namespace internal {
......@@ -20,29 +21,64 @@ class NumberBuiltinsAssembler : public CodeStubAssembler {
protected:
template <typename Descriptor>
void BitwiseOp(std::function<Node*(Node* lhs, Node* rhs)> body,
Signedness signed_result = kSigned) {
void BitwiseOp(Token::Value op) {
Node* left = Parameter(Descriptor::kLeft);
Node* right = Parameter(Descriptor::kRight);
Node* context = Parameter(Descriptor::kContext);
Node* lhs_value = TruncateTaggedToWord32(context, left);
Node* rhs_value = TruncateTaggedToWord32(context, right);
Node* value = body(lhs_value, rhs_value);
Node* result = signed_result == kSigned ? ChangeInt32ToTagged(value)
: ChangeUint32ToTagged(value);
VARIABLE(var_left_bigint, MachineRepresentation::kTagged, left);
VARIABLE(var_right_bigint, MachineRepresentation::kTagged, right);
Label if_left_bigint(this), do_bigint_op(this);
Node* left32 = TaggedToWord32OrBigInt(context, left, &if_left_bigint,
&var_left_bigint);
Node* right32 = TaggedToWord32OrBigInt(context, right, &do_bigint_op,
&var_right_bigint);
// Number case.
Node* result;
switch (op) {
case Token::BIT_AND:
result = ChangeInt32ToTagged(Word32And(left32, right32));
break;
case Token::BIT_OR:
result = ChangeInt32ToTagged(Word32Or(left32, right32));
break;
case Token::BIT_XOR:
result = ChangeInt32ToTagged(Word32Xor(left32, right32));
break;
case Token::SHL:
result = ChangeInt32ToTagged(
Word32Shl(left32, Word32And(right32, Int32Constant(0x1f))));
break;
case Token::SAR:
result = ChangeInt32ToTagged(
Word32Sar(left32, Word32And(right32, Int32Constant(0x1f))));
break;
case Token::SHR:
result = ChangeUint32ToTagged(
Word32Shr(left32, Word32And(right32, Int32Constant(0x1f))));
break;
default:
UNREACHABLE();
}
Return(result);
}
template <typename Descriptor>
void BitwiseShiftOp(std::function<Node*(Node* lhs, Node* shift_count)> body,
Signedness signed_result = kSigned) {
BitwiseOp<Descriptor>(
[=](Node* lhs, Node* rhs) {
Node* shift_count = Word32And(rhs, Int32Constant(0x1f));
return body(lhs, shift_count);
},
signed_result);
// BigInt cases.
BIND(&if_left_bigint);
GotoIf(TaggedIsSmi(right), &do_bigint_op);
Node* right_map = LoadMap(right);
GotoIf(IsHeapNumberMap(right_map), &do_bigint_op);
Node* right_type = LoadMapInstanceType(right_map);
GotoIf(IsBigIntInstanceType(right_type), &do_bigint_op);
// TODO(jkummerow): This should use kNonNumericToNumeric.
var_right_bigint.Bind(
CallBuiltin(Builtins::kNonNumberToNumber, context, right));
Goto(&do_bigint_op);
BIND(&do_bigint_op);
Return(CallRuntime(Runtime::kBigIntBinaryOp, context,
var_left_bigint.value(), var_right_bigint.value(),
SmiConstant(op)));
}
template <typename Descriptor>
......@@ -58,6 +94,47 @@ class NumberBuiltinsAssembler : public CodeStubAssembler {
void BinaryOp(Label* smis, Variable* var_left, Variable* var_right,
Label* doubles, Variable* var_left_double,
Variable* var_right_double, Label* bigints);
// Similar to CodeStubAssembler::TruncateTaggedToWord32, but BigInt aware.
// Jumps to {bigint} with {var_bigint_result} populated if it encounters
// a BigInt.
Node* TaggedToWord32OrBigInt(Node* context, Node* value, Label* bigint,
Variable* var_bigint_result) {
// We might need to loop once due to ToNumeric conversion.
VARIABLE(var_value, MachineRepresentation::kTagged, value);
VARIABLE(var_result, MachineRepresentation::kWord32);
Label loop(this, &var_value), done(this, &var_result);
Goto(&loop);
BIND(&loop);
{
value = var_value.value();
Label not_smi(this), is_heap_number(this), is_bigint(this);
GotoIf(TaggedIsNotSmi(value), &not_smi);
// {value} is a Smi.
var_result.Bind(SmiToWord32(value));
Goto(&done);
BIND(&not_smi);
Node* map = LoadMap(value);
GotoIf(IsHeapNumberMap(map), &is_heap_number);
Node* instance_type = LoadMapInstanceType(map);
GotoIf(IsBigIntInstanceType(instance_type), &is_bigint);
// Neither HeapNumber nor BigInt -> convert to Numeric.
// TODO(jkummerow): This should call "NonNumericToNumeric".
var_value.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, value));
Goto(&loop);
BIND(&is_heap_number);
var_result.Bind(TruncateHeapNumberValueToWord32(value));
Goto(&done);
BIND(&is_bigint);
var_bigint_result->Bind(value);
Goto(bigint);
}
BIND(&done);
return var_result.value();
}
};
// ES6 #sec-number.isfinite
......@@ -824,36 +901,27 @@ TF_BUILTIN(Modulus, NumberBuiltinsAssembler) {
}
TF_BUILTIN(ShiftLeft, NumberBuiltinsAssembler) {
BitwiseShiftOp<Descriptor>([=](Node* lhs, Node* shift_count) {
return Word32Shl(lhs, shift_count);
});
BitwiseOp<Descriptor>(Token::SHL);
}
TF_BUILTIN(ShiftRight, NumberBuiltinsAssembler) {
BitwiseShiftOp<Descriptor>([=](Node* lhs, Node* shift_count) {
return Word32Sar(lhs, shift_count);
});
BitwiseOp<Descriptor>(Token::SAR);
}
TF_BUILTIN(ShiftRightLogical, NumberBuiltinsAssembler) {
BitwiseShiftOp<Descriptor>(
[=](Node* lhs, Node* shift_count) { return Word32Shr(lhs, shift_count); },
kUnsigned);
BitwiseOp<Descriptor>(Token::SHR);
}
TF_BUILTIN(BitwiseAnd, NumberBuiltinsAssembler) {
BitwiseOp<Descriptor>(
[=](Node* lhs, Node* rhs) { return Word32And(lhs, rhs); });
BitwiseOp<Descriptor>(Token::BIT_AND);
}
TF_BUILTIN(BitwiseOr, NumberBuiltinsAssembler) {
BitwiseOp<Descriptor>(
[=](Node* lhs, Node* rhs) { return Word32Or(lhs, rhs); });
BitwiseOp<Descriptor>(Token::BIT_OR);
}
TF_BUILTIN(BitwiseXor, NumberBuiltinsAssembler) {
BitwiseOp<Descriptor>(
[=](Node* lhs, Node* rhs) { return Word32Xor(lhs, rhs); });
BitwiseOp<Descriptor>(Token::BIT_XOR);
}
TF_BUILTIN(LessThan, NumberBuiltinsAssembler) {
......
......@@ -1257,6 +1257,77 @@ void InterpreterAssembler::DispatchWide(OperandScale operand_scale) {
DispatchToBytecodeHandlerEntry(target_code_entry, next_bytecode_offset);
}
// Like NumberBuiltinsAssembler::TaggedToWord32OrBigInt, but with feedback.
Node* InterpreterAssembler::TaggedToWord32OrBigIntWithFeedback(
Node* context, Node* value, Variable* var_type_feedback, Label* bigint,
Variable* var_bigint_result) {
// We might need to loop once due to ToNumeric conversion.
VARIABLE(var_value, MachineRepresentation::kTagged, value);
VARIABLE(var_result, MachineRepresentation::kWord32);
Variable* loop_vars[] = {&var_value, var_type_feedback};
Label loop(this, arraysize(loop_vars), loop_vars), done(this, &var_result);
var_type_feedback->Bind(SmiConstant(BinaryOperationFeedback::kNone));
Goto(&loop);
BIND(&loop);
{
value = var_value.value();
Label not_smi(this), is_heap_number(this), is_oddball(this),
is_bigint(this);
GotoIf(TaggedIsNotSmi(value), &not_smi);
// {value} is a Smi.
var_result.Bind(SmiToWord32(value));
var_type_feedback->Bind(
SmiOr(var_type_feedback->value(),
SmiConstant(BinaryOperationFeedback::kSignedSmall)));
Goto(&done);
BIND(&not_smi);
Node* map = LoadMap(value);
GotoIf(IsHeapNumberMap(map), &is_heap_number);
Node* instance_type = LoadMapInstanceType(map);
GotoIf(IsBigIntInstanceType(instance_type), &is_bigint);
// Not HeapNumber or BigInt.
{
// We do not require an Or with earlier feedback here because once we
// convert the value to a Numeric, 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)));
GotoIf(Word32Equal(instance_type, Int32Constant(ODDBALL_TYPE)),
&is_oddball);
// Not an oddball either -> convert to Numeric.
// TODO(jkummerow): This should use "NonNumericToNumeric".
var_value.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, value));
var_type_feedback->Bind(SmiConstant(BinaryOperationFeedback::kAny));
Goto(&loop);
BIND(&is_oddball);
var_value.Bind(LoadObjectField(value, Oddball::kToNumberOffset));
var_type_feedback->Bind(
SmiConstant(BinaryOperationFeedback::kNumberOrOddball));
Goto(&loop);
}
BIND(&is_heap_number);
var_result.Bind(TruncateHeapNumberValueToWord32(value));
var_type_feedback->Bind(
SmiOr(var_type_feedback->value(),
SmiConstant(BinaryOperationFeedback::kNumber)));
Goto(&done);
BIND(&is_bigint);
var_bigint_result->Bind(value);
var_type_feedback->Bind(
SmiOr(var_type_feedback->value(),
SmiConstant(BinaryOperationFeedback::kBigInt)));
Goto(bigint);
}
BIND(&done);
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.
......
......@@ -226,6 +226,11 @@ class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler {
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,
// to {bigint}, and store the type feedback in |var_type_feedback|.
compiler::Node* TaggedToWord32OrBigIntWithFeedback(
compiler::Node* context, compiler::Node* value,
Variable* var_type_feedback, Label* bigint, Variable* var_bigint_result);
// Abort with the given bailout reason.
void Abort(BailoutReason bailout_reason);
......
This diff is collapsed.
......@@ -60,6 +60,24 @@ RUNTIME_FUNCTION(Runtime_BigIntBinaryOp) {
case Token::MOD:
result = BigInt::Remainder(left, right);
break;
case Token::BIT_AND:
result = BigInt::BitwiseAnd(left, right);
break;
case Token::BIT_OR:
result = BigInt::BitwiseOr(left, right);
break;
case Token::BIT_XOR:
result = BigInt::BitwiseXor(left, right);
break;
case Token::SHL:
result = BigInt::LeftShift(left, right);
break;
case Token::SAR:
result = BigInt::SignedRightShift(left, right);
break;
case Token::SHR:
result = BigInt::UnsignedRightShift(left, right);
break;
default:
UNREACHABLE();
}
......
......@@ -382,3 +382,47 @@ const six = BigInt(6);
assertThrows("three % zero", RangeError);
assertThrows("three % 0", TypeError);
}
// Bitwise binary ops.
{
assertTrue((three & one) === one);
assertThrows("three & 1", TypeError);
assertThrows("1 & three", TypeError);
assertThrows("three & true", TypeError);
assertThrows("true & three", TypeError);
assertThrows("three & {valueOf: function() { return 1; }}", TypeError);
assertThrows("({valueOf: function() { return 1; }}) & three", TypeError);
assertTrue((two | one) === three);
assertThrows("two | 0", TypeError);
assertThrows("0 | two", TypeError);
assertThrows("two | undefined", TypeError);
assertThrows("undefined | two", TypeError);
assertTrue((three ^ one) === two);
assertThrows("three ^ 1", TypeError);
assertThrows("1 ^ three", TypeError);
assertThrows("three ^ 2.5", TypeError);
assertThrows("2.5 ^ three", TypeError);
}
// Shift ops.
{
assertTrue(one << one === two);
assertThrows("one << 1", TypeError);
assertThrows("1 << one", TypeError);
assertThrows("one << true", TypeError);
assertThrows("true << one", TypeError);
assertTrue(three >> one === one);
assertThrows("three >> 1", TypeError);
assertThrows("0xbeef >> one", TypeError);
assertThrows("three >> 1.5", TypeError);
assertThrows("23.45 >> three", TypeError);
assertThrows("three >>> one", TypeError);
assertThrows("three >>> 1", TypeError);
assertThrows("0xbeef >>> one", TypeError);
assertThrows("three >>> {valueOf: function() { return 1; }}", TypeError);
assertThrows("({valueOf: function() { return 1; }}) >>> one", TypeError);
}
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