Commit 3b5a26f8 authored by Nico Hartmann's avatar Nico Hartmann Committed by Commit Bot

[turbofan] Optimize BigInt subtraction

This CL implements torque builtins for BigInt subtraction and extends
the compilation pipeline to lower calls to the generic subtraction
to SpeculativeBigIntSubtract and later to BigIntSubtract with
necessary checks in case of BigInt feedback.

The CL also implements lowering of these operators to native machine
word operations on 64 bit architectures if they are used in a
truncating context (aka BigInt.asUintN).

Bug: v8:9407
Change-Id: Idf5da14c380bc7c12375e7f084a3e1c455303f5f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1895566Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65037}
parent 6a57b93c
......@@ -183,6 +183,51 @@ namespace bigint {
}
}
macro BigIntSubtractImpl(implicit context: Context)(x: BigInt, y: BigInt):
BigInt labels BigIntTooBig {
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
if (xsign != ysign) {
// x - (-y) == x + y
// (-x) - y == -(x + y)
return MutableBigIntAbsoluteAdd(x, y, xsign) otherwise BigIntTooBig;
}
// x - y == -(y - x)
// (-x) - (-y) == y - x == -(x - y)
if (MutableBigIntAbsoluteCompare(x, y) >= 0) {
return MutableBigIntAbsoluteSub(x, y, xsign);
}
return MutableBigIntAbsoluteSub(y, x, InvertSign(xsign));
}
builtin BigIntSubtractNoThrow(implicit context:
Context)(x: BigInt, y: BigInt): Numeric {
try {
return BigIntSubtractImpl(x, y) otherwise BigIntTooBig;
}
label BigIntTooBig {
// Smi sentinal is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
}
}
builtin BigIntSubtract(implicit context:
Context)(xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntSubtractImpl(x, y) otherwise BigIntTooBig;
}
label MixedTypes {
ThrowTypeError(kBigIntMixedTypes);
}
label BigIntTooBig {
ThrowRangeError(kBigIntTooBig);
}
}
builtin BigIntUnaryMinus(implicit context: Context)(bigint: BigInt): BigInt {
const length = ReadBigIntLength(bigint);
......
......@@ -712,8 +712,8 @@ TF_BUILTIN(Subtract, NumberBuiltinsAssembler) {
BIND(&do_bigint_sub);
{
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
Return(CallRuntime(Runtime::kBigIntBinaryOp, context, var_left.value(),
var_right.value(), SmiConstant(Operation::kSubtract)));
TailCallBuiltin(Builtins::kBigIntSubtract, context, var_left.value(),
var_right.value());
}
}
......
......@@ -167,6 +167,7 @@ class EffectControlLinearizer {
Node* LowerStringLessThan(Node* node);
Node* LowerStringLessThanOrEqual(Node* node);
Node* LowerBigIntAdd(Node* node, Node* frame_state);
Node* LowerBigIntSubtract(Node* node, Node* frame_state);
Node* LowerBigIntNegate(Node* node);
Node* LowerCheckFloat64Hole(Node* node, Node* frame_state);
Node* LowerCheckNotTaggedHole(Node* node, Node* frame_state);
......@@ -1148,6 +1149,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kBigIntAdd:
result = LowerBigIntAdd(node, frame_state);
break;
case IrOpcode::kBigIntSubtract:
result = LowerBigIntSubtract(node, frame_state);
break;
case IrOpcode::kBigIntNegate:
result = LowerBigIntNegate(node);
break;
......@@ -4209,6 +4213,27 @@ Node* EffectControlLinearizer::LowerBigIntAdd(Node* node, Node* frame_state) {
return value;
}
Node* EffectControlLinearizer::LowerBigIntSubtract(Node* node,
Node* frame_state) {
Node* lhs = node->InputAt(0);
Node* rhs = node->InputAt(1);
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kBigIntSubtractNoThrow);
auto call_descriptor = Linkage::GetStubCallDescriptor(
graph()->zone(), callable.descriptor(),
callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags,
Operator::kFoldable | Operator::kNoThrow);
Node* value = __ Call(call_descriptor, __ HeapConstant(callable.code()), lhs,
rhs, __ NoContextConstant());
// Check for exception sentinel: Smi is returned to signal BigIntTooBig.
__ DeoptimizeIf(DeoptimizeReason::kBigIntTooBig, FeedbackSource{},
ObjectIsSmi(value), frame_state);
return value;
}
Node* EffectControlLinearizer::LowerBigIntNegate(Node* node) {
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kBigIntUnaryMinus);
......
......@@ -157,6 +157,8 @@ class JSSpeculativeBinopBuilder final {
switch (op_->opcode()) {
case IrOpcode::kJSAdd:
return simplified()->SpeculativeBigIntAdd(hint);
case IrOpcode::kJSSubtract:
return simplified()->SpeculativeBigIntSubtract(hint);
default:
break;
}
......@@ -397,7 +399,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
if (Node* node = b.TryBuildNumberBinop()) {
return LoweringResult::SideEffectFree(node, node, control);
}
if (op->opcode() == IrOpcode::kJSAdd) {
if (op->opcode() == IrOpcode::kJSAdd ||
op->opcode() == IrOpcode::kJSSubtract) {
if (Node* node = b.TryBuildBigIntBinop()) {
return LoweringResult::SideEffectFree(node, node, control);
}
......
......@@ -318,7 +318,9 @@
V(NumberMin) \
V(NumberPow)
#define SIMPLIFIED_BIGINT_BINOP_LIST(V) V(BigIntAdd)
#define SIMPLIFIED_BIGINT_BINOP_LIST(V) \
V(BigIntAdd) \
V(BigIntSubtract)
#define SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(V) \
V(SpeculativeNumberAdd) \
......@@ -475,7 +477,10 @@
V(AssertType) \
V(DateNow)
#define SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(V) V(SpeculativeBigIntAdd)
#define SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(V) \
V(SpeculativeBigIntAdd) \
V(SpeculativeBigIntSubtract)
#define SIMPLIFIED_SPECULATIVE_BIGINT_UNOP_LIST(V) V(SpeculativeBigIntNegate)
#define SIMPLIFIED_OP_LIST(V) \
......
......@@ -1125,6 +1125,11 @@ Type OperationTyper::BigIntAdd(Type lhs, Type rhs) {
return Type::BigInt();
}
Type OperationTyper::BigIntSubtract(Type lhs, Type rhs) {
if (lhs.IsNone() || rhs.IsNone()) return Type::None();
return Type::BigInt();
}
Type OperationTyper::BigIntNegate(Type type) {
if (type.IsNone()) return type;
return Type::BigInt();
......@@ -1135,6 +1140,11 @@ Type OperationTyper::SpeculativeBigIntAdd(Type lhs, Type rhs) {
return Type::BigInt();
}
Type OperationTyper::SpeculativeBigIntSubtract(Type lhs, Type rhs) {
if (lhs.IsNone() || rhs.IsNone()) return Type::None();
return Type::BigInt();
}
Type OperationTyper::SpeculativeBigIntNegate(Type type) {
if (type.IsNone()) return type;
return Type::BigInt();
......
......@@ -2719,6 +2719,25 @@ class RepresentationSelector {
}
return;
}
case IrOpcode::kSpeculativeBigIntSubtract: {
if (truncation.IsUsedAsWord64()) {
VisitBinop(node,
UseInfo::CheckedBigIntTruncatingWord64(FeedbackSource{}),
MachineRepresentation::kWord64);
if (lower()) {
ChangeToPureOp(node, lowering->machine()->Int64Sub());
}
} else {
VisitBinop(node,
UseInfo::CheckedBigIntAsTaggedPointer(FeedbackSource{}),
MachineRepresentation::kTaggedPointer);
if (lower()) {
NodeProperties::ChangeOp(node,
lowering->simplified()->BigIntSubtract());
}
}
return;
}
case IrOpcode::kSpeculativeBigIntNegate: {
if (truncation.IsUsedAsWord64()) {
VisitUnop(node,
......
......@@ -790,6 +790,7 @@ bool operator==(CheckMinusZeroParameters const& lhs,
#define EFFECT_DEPENDENT_OP_LIST(V) \
V(BigIntAdd, Operator::kNoProperties, 2, 1) \
V(BigIntSubtract, Operator::kNoProperties, 2, 1) \
V(StringCharCodeAt, Operator::kNoProperties, 2, 1) \
V(StringCodePointAt, Operator::kNoProperties, 2, 1) \
V(StringFromCodePointAt, Operator::kNoProperties, 2, 1) \
......@@ -1459,6 +1460,14 @@ const Operator* SimplifiedOperatorBuilder::SpeculativeBigIntAdd(
"SpeculativeBigIntAdd", 2, 1, 1, 1, 1, 0, hint);
}
const Operator* SimplifiedOperatorBuilder::SpeculativeBigIntSubtract(
BigIntOperationHint hint) {
return new (zone()) Operator1<BigIntOperationHint>(
IrOpcode::kSpeculativeBigIntSubtract,
Operator::kFoldable | Operator::kNoThrow, "SpeculativeBigIntSubtract", 2,
1, 1, 1, 1, 0, hint);
}
const Operator* SimplifiedOperatorBuilder::SpeculativeBigIntNegate(
BigIntOperationHint hint) {
return new (zone()) Operator1<BigIntOperationHint>(
......
......@@ -666,6 +666,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* NumberSilenceNaN();
const Operator* BigIntAdd();
const Operator* BigIntSubtract();
const Operator* BigIntNegate();
const Operator* SpeculativeSafeIntegerAdd(NumberOperationHint hint);
......@@ -688,6 +689,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* SpeculativeNumberEqual(NumberOperationHint hint);
const Operator* SpeculativeBigIntAdd(BigIntOperationHint hint);
const Operator* SpeculativeBigIntSubtract(BigIntOperationHint hint);
const Operator* SpeculativeBigIntNegate(BigIntOperationHint hint);
const Operator* BigIntAsUintN(int bits);
......
......@@ -1001,6 +1001,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kSpeculativeBigIntAdd:
case IrOpcode::kSpeculativeBigIntSubtract:
CheckTypeIs(node, Type::BigInt());
break;
case IrOpcode::kSpeculativeBigIntNegate:
......@@ -1011,6 +1012,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckTypeIs(node, Type::BigInt());
break;
case IrOpcode::kBigIntAdd:
case IrOpcode::kBigIntSubtract:
CheckValueInputIs(node, 0, Type::BigInt());
CheckValueInputIs(node, 1, Type::BigInt());
CheckTypeIs(node, Type::BigInt());
......
......@@ -236,7 +236,7 @@ TNode<Object> BinaryOpAssembler::Generate_BinaryOperationWithFeedback(
Label do_float_operation(this), end(this), call_stub(this),
check_rhsisoddball(this, Label::kDeferred), call_with_any_feedback(this),
if_lhsisnotnumber(this, Label::kDeferred),
if_bigint(this, Label::kDeferred);
if_both_bigint(this, Label::kDeferred);
TVARIABLE(Float64T, var_float_lhs);
TVARIABLE(Float64T, var_float_rhs);
TVARIABLE(Smi, var_type_feedback);
......@@ -364,7 +364,7 @@ TNode<Object> BinaryOpAssembler::Generate_BinaryOperationWithFeedback(
BIND(&if_left_bigint);
{
GotoIf(TaggedIsSmi(rhs), &call_with_any_feedback);
Branch(IsBigInt(CAST(rhs)), &if_bigint, &call_with_any_feedback);
Branch(IsBigInt(CAST(rhs)), &if_both_bigint, &call_with_any_feedback);
}
}
......@@ -373,7 +373,6 @@ TNode<Object> BinaryOpAssembler::Generate_BinaryOperationWithFeedback(
// Check if rhs is an oddball. At this point we know lhs is either a
// Smi or number or oddball and rhs is not a number or Smi.
TNode<Uint16T> rhs_instance_type = LoadInstanceType(CAST(rhs));
GotoIf(IsBigIntInstanceType(rhs_instance_type), &if_bigint);
TNode<BoolT> rhs_is_oddball =
InstanceTypeEqual(rhs_instance_type, ODDBALL_TYPE);
GotoIfNot(rhs_is_oddball, &call_with_any_feedback);
......@@ -382,18 +381,31 @@ TNode<Object> BinaryOpAssembler::Generate_BinaryOperationWithFeedback(
Goto(&call_stub);
}
// This handles the case where at least one input is a BigInt.
BIND(&if_bigint);
BIND(&if_both_bigint);
{
var_type_feedback = SmiConstant(BinaryOperationFeedback::kBigInt);
if (op == Operation::kAdd) {
var_result = CallBuiltin(Builtins::kBigIntAdd, context, lhs, rhs);
if (op == Operation::kSubtract) {
Label bigint_too_big(this);
var_result =
CallBuiltin(Builtins::kBigIntSubtractNoThrow, context, lhs, rhs);
// Check for sentinel that signals BigIntTooBig exception.
GotoIf(TaggedIsSmi(var_result.value()), &bigint_too_big);
Goto(&end);
BIND(&bigint_too_big);
{
// Update feedback to prevent deopt loop.
UpdateFeedback(SmiConstant(BinaryOperationFeedback::kAny),
maybe_feedback_vector, slot_id);
ThrowRangeError(context, MessageTemplate::kBigIntTooBig);
}
} else {
var_result = CallRuntime(Runtime::kBigIntBinaryOp, context, lhs, rhs,
SmiConstant(op));
}
Goto(&end);
}
}
BIND(&call_with_any_feedback);
{
......
......@@ -39,6 +39,10 @@ function TestInt64LoweredOperations() {
assertEquals(14n, BigInt.asUintN(32, 8n + 6n));
assertEquals(813n, BigInt.asUintN(10, 1013n + -200n));
assertEquals(15n, BigInt.asUintN(4, -319n + 302n));
assertEquals(32n, BigInt.asUintN(64, (2n ** 100n + 64n) - 32n));
assertEquals(2n ** 64n - 32n, BigInt.asUintN(64, 32n - (2n ** 100n + 64n)));
assertEquals(11n, BigInt.asUintN(4, 800n - 789n));
assertEquals(5n, BigInt.asUintN(4, 789n - 800n));
for (let i = 0; i < 2; ++i) {
let x = 32n; // x = 32n
......@@ -46,7 +50,7 @@ function TestInt64LoweredOperations() {
x = BigInt.asUintN(64, x + 3n); // x = 35n
const y = x + -8n + x; // x = 35n, y = 62n
x = BigInt.asUintN(6, y + x); // x = 33n, y = 62n
x = -9n + y + -x; // x = 20n
x = -9n + y - x; // x = 20n
x = BigInt.asUintN(10000 * i, x); // x = 20n
} else {
x = x + 400n; // x = 432n
......
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