Commit 30ee0690 authored by Qifan Pan's avatar Qifan Pan Committed by V8 LUCI CQ

Reland "[TurboFan] Support BigIntMultiply"

This is a reland of commit ccde4205

Added a test case for terminating optimized bigint multiply and attached frame_state to the runtime call to provide deopt information to determine the throw location

Original change's description:
> [TurboFan] Support BigIntMultiply
>
> Bug: v8:9407
> Change-Id: Iab0a4ca8dd5d83444d1addd6043a5c8e3a8577a7
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3773773
> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
> Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#82140}

Bug: v8:9407
Change-Id: Ia691d758265148da1de291365d41c7c1d1f98ddd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3810391
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82232}
parent 6ea78398
......@@ -712,6 +712,8 @@ extern transitioning runtime ThrowCalledNonCallable(implicit context: Context)(
extern transitioning macro ThrowIfNotJSReceiver(implicit context: Context)(
JSAny, constexpr MessageTemplate, constexpr string): void;
extern macro TerminateExecution(implicit context: Context)(): never;
extern macro ArraySpeciesCreate(Context, JSAny, Number): JSReceiver;
extern macro ArrayCreate(implicit context: Context)(Number): JSArray;
extern macro BuildAppendJSArray(
......
......@@ -63,6 +63,20 @@ class BigIntBuiltinsAssembler : public CodeStubAssembler {
std::make_pair(MachineType::AnyTagged(), y));
}
TNode<BoolT> CppAbsoluteMulAndCanonicalize(TNode<BigInt> result,
TNode<BigInt> x, TNode<BigInt> y) {
TNode<ExternalReference> mutable_big_int_absolute_mul_and_canonicalize =
ExternalConstant(
ExternalReference::
mutable_big_int_absolute_mul_and_canonicalize_function());
TNode<BoolT> success = UncheckedCast<BoolT>(CallCFunction(
mutable_big_int_absolute_mul_and_canonicalize, MachineType::Bool(),
std::make_pair(MachineType::AnyTagged(), result),
std::make_pair(MachineType::AnyTagged(), x),
std::make_pair(MachineType::AnyTagged(), y)));
return success;
}
TNode<Int32T> CppAbsoluteCompare(TNode<BigInt> x, TNode<BigInt> y) {
TNode<ExternalReference> mutable_big_int_absolute_compare =
ExternalConstant(
......
......@@ -13,6 +13,8 @@ extern macro BigIntBuiltinsAssembler::CppAbsoluteAddAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppAbsoluteSubAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): void;
extern macro BigIntBuiltinsAssembler::CppAbsoluteMulAndCanonicalize(
MutableBigInt, BigIntBase, BigIntBase): bool;
extern macro BigIntBuiltinsAssembler::CppAbsoluteCompare(
BigIntBase, BigIntBase): int32;
......@@ -204,6 +206,66 @@ builtin BigIntSubtract(implicit context: Context)(
}
}
macro BigIntMultiplyImpl(implicit context: Context)(x: BigInt, y: BigInt):
BigInt labels BigIntTooBig, TerminationRequested {
const xlength = ReadBigIntLength(x);
const ylength = ReadBigIntLength(y);
// case: 0n * y
if (xlength == 0) {
return x;
}
// case: x * 0n
if (ylength == 0) {
return y;
}
// case: x * y
const xsign = ReadBigIntSign(x);
const ysign = ReadBigIntSign(y);
const resultSign = (xsign != ysign) ? kNegativeSign : kPositiveSign;
const result = AllocateEmptyBigIntNoThrow(resultSign, xlength + ylength)
otherwise BigIntTooBig;
if (!CppAbsoluteMulAndCanonicalize(result, x, y)) {
goto TerminationRequested;
}
return Convert<BigInt>(result);
}
builtin BigIntMultiplyNoThrow(implicit context: Context)(
x: BigInt, y: BigInt): Numeric {
try {
return BigIntMultiplyImpl(x, y) otherwise BigIntTooBig,
TerminationRequested;
} label BigIntTooBig {
// Smi sentinal 0 is used to signal BigIntTooBig exception.
return Convert<Smi>(0);
} label TerminationRequested {
// Smi sentinal 1 is used to signal TerminateExecution exception.
return Convert<Smi>(1);
}
}
builtin BigIntMultiply(implicit context: Context)(
xNum: Numeric, yNum: Numeric): BigInt {
try {
const x = Cast<BigInt>(xNum) otherwise MixedTypes;
const y = Cast<BigInt>(yNum) otherwise MixedTypes;
return BigIntMultiplyImpl(x, y) otherwise BigIntTooBig,
TerminationRequested;
} label MixedTypes {
ThrowTypeError(MessageTemplate::kBigIntMixedTypes);
} label BigIntTooBig {
ThrowRangeError(MessageTemplate::kBigIntTooBig);
} label TerminationRequested {
TerminateExecution();
}
}
builtin BigIntUnaryMinus(implicit context: Context)(bigint: BigInt): BigInt {
const length = ReadBigIntLength(bigint);
......
......@@ -614,8 +614,7 @@ builtin Multiply(implicit context: Context)(
} 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));
tail bigint::BigIntMultiply(left, right);
}
}
......
......@@ -6105,6 +6105,11 @@ void CodeStubAssembler::ThrowTypeError(TNode<Context> context,
Unreachable();
}
void CodeStubAssembler::TerminateExecution(TNode<Context> context) {
CallRuntime(Runtime::kTerminateExecution, context);
Unreachable();
}
TNode<HeapObject> CodeStubAssembler::GetPendingMessage() {
TNode<ExternalReference> pending_message = ExternalConstant(
ExternalReference::address_of_pending_message(isolate()));
......
......@@ -2529,6 +2529,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
base::Optional<TNode<Object>> arg1 = base::nullopt,
base::Optional<TNode<Object>> arg2 = base::nullopt);
void TerminateExecution(TNode<Context> context);
TNode<HeapObject> GetPendingMessage();
void SetPendingMessage(TNode<HeapObject> message);
TNode<BoolT> IsExecutionTerminating();
......
......@@ -1031,6 +1031,9 @@ FUNCTION_REFERENCE(mutable_big_int_absolute_compare_function,
FUNCTION_REFERENCE(mutable_big_int_absolute_sub_and_canonicalize_function,
MutableBigInt_AbsoluteSubAndCanonicalize)
FUNCTION_REFERENCE(mutable_big_int_absolute_mul_and_canonicalize_function,
MutableBigInt_AbsoluteMulAndCanonicalize)
FUNCTION_REFERENCE(check_object_type, CheckObjectType)
#ifdef V8_INTL_SUPPORT
......
......@@ -175,6 +175,8 @@ class StatsCounter;
"MutableBigInt_AbsoluteCompare") \
V(mutable_big_int_absolute_sub_and_canonicalize_function, \
"MutableBigInt_AbsoluteSubAndCanonicalize") \
V(mutable_big_int_absolute_mul_and_canonicalize_function, \
"MutableBigInt_AbsoluteMulAndCanonicalize") \
V(new_deoptimizer_function, "Deoptimizer::New()") \
V(orderedhashmap_gethash_raw, "orderedhashmap_gethash_raw") \
V(printf_function, "printf") \
......
......@@ -174,6 +174,7 @@ class EffectControlLinearizer {
Node* LowerStringLessThanOrEqual(Node* node);
Node* LowerBigIntAdd(Node* node, Node* frame_state);
Node* LowerBigIntSubtract(Node* node, Node* frame_state);
Node* LowerBigIntMultiply(Node* node, Node* frame_state);
Node* LowerBigIntNegate(Node* node);
Node* LowerCheckFloat64Hole(Node* node, Node* frame_state);
Node* LowerCheckNotTaggedHole(Node* node, Node* frame_state);
......@@ -1237,6 +1238,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kBigIntSubtract:
result = LowerBigIntSubtract(node, frame_state);
break;
case IrOpcode::kBigIntMultiply:
result = LowerBigIntMultiply(node, frame_state);
break;
case IrOpcode::kBigIntNegate:
result = LowerBigIntNegate(node);
break;
......@@ -4341,6 +4345,50 @@ Node* EffectControlLinearizer::LowerBigIntSubtract(Node* node,
return value;
}
Node* EffectControlLinearizer::LowerBigIntMultiply(Node* node,
Node* frame_state) {
Node* lhs = node->InputAt(0);
Node* rhs = node->InputAt(1);
Callable const callable =
Builtins::CallableFor(isolate(), Builtin::kBigIntMultiplyNoThrow);
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());
auto if_termreq = __ MakeDeferredLabel();
auto done = __ MakeLabel();
// Check for exception sentinel: Smi 1 is returned to signal
// TerminationRequested.
__ GotoIf(__ TaggedEqual(value, __ SmiConstant(1)), &if_termreq);
// Check for exception sentinel: Smi 0 is returned to signal
// BigIntTooBig.
__ DeoptimizeIf(DeoptimizeReason::kBigIntTooBig, FeedbackSource{},
ObjectIsSmi(value), frame_state);
__ Goto(&done);
__ Bind(&if_termreq);
{
Runtime::FunctionId id = Runtime::kTerminateExecution;
auto call_descriptor = Linkage::GetRuntimeCallDescriptor(
graph()->zone(), id, 0, Operator::kNoDeopt,
CallDescriptor::kNeedsFrameState);
__ Call(call_descriptor, __ CEntryStubConstant(1),
__ ExternalConstant(ExternalReference::Create(id)),
__ Int32Constant(0), __ NoContextConstant(), frame_state);
__ Goto(&done);
}
__ Bind(&done);
return value;
}
Node* EffectControlLinearizer::LowerBigIntNegate(Node* node) {
Callable const callable =
Builtins::CallableFor(isolate(), Builtin::kBigIntUnaryMinus);
......
......@@ -151,12 +151,13 @@ class JSSpeculativeBinopBuilder final {
}
const Operator* SpeculativeBigIntOp(BigIntOperationHint hint) {
DCHECK(jsgraph()->machine()->Is64());
switch (op_->opcode()) {
case IrOpcode::kJSAdd:
return simplified()->SpeculativeBigIntAdd(hint);
case IrOpcode::kJSSubtract:
return simplified()->SpeculativeBigIntSubtract(hint);
case IrOpcode::kJSMultiply:
return simplified()->SpeculativeBigIntMultiply(hint);
default:
break;
}
......@@ -205,7 +206,6 @@ class JSSpeculativeBinopBuilder final {
}
Node* TryBuildBigIntBinop() {
DCHECK(jsgraph()->machine()->Is64());
BigIntOperationHint hint;
if (GetBinaryBigIntOperationHint(&hint)) {
const Operator* op = SpeculativeBigIntOp(hint);
......@@ -404,13 +404,12 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
return LoweringResult::SideEffectFree(node, node, control);
}
if (op->opcode() == IrOpcode::kJSAdd ||
op->opcode() == IrOpcode::kJSSubtract) {
if (jsgraph()->machine()->Is64()) {
op->opcode() == IrOpcode::kJSSubtract ||
op->opcode() == IrOpcode::kJSMultiply) {
if (Node* node = b.TryBuildBigIntBinop()) {
return LoweringResult::SideEffectFree(node, node, control);
}
}
}
break;
}
default:
......
......@@ -334,7 +334,8 @@
#define SIMPLIFIED_BIGINT_BINOP_LIST(V) \
V(BigIntAdd) \
V(BigIntSubtract)
V(BigIntSubtract) \
V(BigIntMultiply)
#define SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(V) \
V(SpeculativeNumberAdd) \
......@@ -499,7 +500,8 @@
#define SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(V) \
V(SpeculativeBigIntAdd) \
V(SpeculativeBigIntSubtract)
V(SpeculativeBigIntSubtract) \
V(SpeculativeBigIntMultiply)
#define SIMPLIFIED_SPECULATIVE_BIGINT_UNOP_LIST(V) \
V(SpeculativeBigIntAsIntN) \
......
......@@ -1144,6 +1144,14 @@ Type OperationTyper::BigIntSubtract(Type lhs, Type rhs) {
return Type::BigInt();
}
Type OperationTyper::BigIntMultiply(Type lhs, Type rhs) {
DCHECK(lhs.Is(Type::BigInt()));
DCHECK(rhs.Is(Type::BigInt()));
if (lhs.IsNone() || rhs.IsNone()) return Type::None();
return Type::BigInt();
}
Type OperationTyper::BigIntNegate(Type type) {
DCHECK(type.Is(Type::BigInt()));
......@@ -1161,6 +1169,11 @@ Type OperationTyper::SpeculativeBigIntSubtract(Type lhs, Type rhs) {
return Type::BigInt();
}
Type OperationTyper::SpeculativeBigIntMultiply(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();
......
......@@ -3225,6 +3225,15 @@ class RepresentationSelector {
}
return;
}
case IrOpcode::kSpeculativeBigIntMultiply: {
VisitBinop<T>(node,
UseInfo::CheckedBigIntAsTaggedPointer(FeedbackSource{}),
MachineRepresentation::kTaggedPointer);
if (lower<T>()) {
ChangeOp(node, lowering->simplified()->BigIntMultiply());
}
return;
}
case IrOpcode::kSpeculativeBigIntNegate: {
if (truncation.IsUsedAsWord64()) {
VisitUnop<T>(node,
......
......@@ -804,6 +804,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(BigIntMultiply, Operator::kNoProperties, 2, 1) \
V(StringCharCodeAt, Operator::kNoProperties, 2, 1) \
V(StringCodePointAt, Operator::kNoProperties, 2, 1) \
V(StringFromCodePointAt, Operator::kNoProperties, 2, 1) \
......@@ -1606,6 +1607,14 @@ const Operator* SimplifiedOperatorBuilder::SpeculativeBigIntSubtract(
1, 1, 1, 1, 0, hint);
}
const Operator* SimplifiedOperatorBuilder::SpeculativeBigIntMultiply(
BigIntOperationHint hint) {
return zone()->New<Operator1<BigIntOperationHint>>(
IrOpcode::kSpeculativeBigIntMultiply,
Operator::kFoldable | Operator::kNoThrow, "SpeculativeBigIntMultiply", 2,
1, 1, 1, 1, 0, hint);
}
const Operator* SimplifiedOperatorBuilder::SpeculativeBigIntNegate(
BigIntOperationHint hint) {
return zone()->New<Operator1<BigIntOperationHint>>(
......
......@@ -778,6 +778,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* BigIntAdd();
const Operator* BigIntSubtract();
const Operator* BigIntMultiply();
const Operator* BigIntNegate();
const Operator* SpeculativeSafeIntegerAdd(NumberOperationHint hint);
......@@ -802,6 +803,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* SpeculativeBigIntAdd(BigIntOperationHint hint);
const Operator* SpeculativeBigIntSubtract(BigIntOperationHint hint);
const Operator* SpeculativeBigIntMultiply(BigIntOperationHint hint);
const Operator* SpeculativeBigIntNegate(BigIntOperationHint hint);
const Operator* SpeculativeBigIntAsIntN(int bits,
const FeedbackSource& feedback);
......
......@@ -967,6 +967,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
break;
case IrOpcode::kSpeculativeBigIntAdd:
case IrOpcode::kSpeculativeBigIntSubtract:
case IrOpcode::kSpeculativeBigIntMultiply:
CheckTypeIs(node, Type::BigInt());
break;
case IrOpcode::kSpeculativeBigIntNegate:
......@@ -979,6 +980,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
break;
case IrOpcode::kBigIntAdd:
case IrOpcode::kBigIntSubtract:
case IrOpcode::kBigIntMultiply:
CheckValueInputIs(node, 0, Type::BigInt());
CheckValueInputIs(node, 1, Type::BigInt());
CheckTypeIs(node, Type::BigInt());
......
......@@ -1623,5 +1623,29 @@ void MutableBigInt_AbsoluteSubAndCanonicalize(Address result_addr,
MutableBigInt::Canonicalize(result);
}
// Returns true if it succeeded to obtain the result of multiplication.
// Returns false if the computation is interrupted.
bool MutableBigInt_AbsoluteMulAndCanonicalize(Address result_addr,
Address x_addr, Address y_addr) {
BigInt x = BigInt::cast(Object(x_addr));
BigInt y = BigInt::cast(Object(y_addr));
MutableBigInt result = MutableBigInt::cast(Object(result_addr));
Isolate* isolate;
if (!GetIsolateFromHeapObject(x, &isolate)) {
// We should always get the isolate from the BigInt.
UNREACHABLE();
}
bigint::Status status = isolate->bigint_processor()->Multiply(
GetRWDigits(result), GetDigits(x), GetDigits(y));
if (status == bigint::Status::kInterrupted) {
return false;
}
MutableBigInt::Canonicalize(result);
return true;
}
} // namespace internal
} // namespace v8
......@@ -26,6 +26,8 @@ void MutableBigInt_AbsoluteAddAndCanonicalize(Address result_addr,
int32_t MutableBigInt_AbsoluteCompare(Address x_addr, Address y_addr);
void MutableBigInt_AbsoluteSubAndCanonicalize(Address result_addr,
Address x_addr, Address y_addr);
bool MutableBigInt_AbsoluteMulAndCanonicalize(Address result_addr,
Address x_addr, Address y_addr);
class BigInt;
class ValueDeserializer;
......
......@@ -83,6 +83,12 @@ RUNTIME_FUNCTION(Runtime_ThrowSymbolAsyncIteratorInvalid) {
isolate, NewTypeError(MessageTemplate::kSymbolAsyncIteratorInvalid));
}
RUNTIME_FUNCTION(Runtime_TerminateExecution) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
return isolate->TerminateExecution();
}
#define THROW_ERROR(isolate, args, call) \
HandleScope scope(isolate); \
DCHECK_LE(1, args.length()); \
......
......@@ -132,6 +132,7 @@ bool Runtime::NeedsExactContext(FunctionId id) {
case Runtime::kThrowThrowMethodMissing:
case Runtime::kThrowTypeError:
case Runtime::kThrowUnsupportedSuperError:
case Runtime::kTerminateExecution:
#if V8_ENABLE_WEBASSEMBLY
case Runtime::kThrowWasmError:
case Runtime::kThrowWasmStackOverflow:
......@@ -170,6 +171,7 @@ bool Runtime::IsNonReturning(FunctionId id) {
case Runtime::kThrowSymbolAsyncIteratorInvalid:
case Runtime::kThrowTypeError:
case Runtime::kThrowConstAssignError:
case Runtime::kTerminateExecution:
#if V8_ENABLE_WEBASSEMBLY
case Runtime::kThrowWasmError:
case Runtime::kThrowWasmStackOverflow:
......
......@@ -262,6 +262,7 @@ namespace internal {
F(ThrowThrowMethodMissing, 0, 1) \
F(ThrowTypeError, -1 /* >= 1 */, 1) \
F(ThrowTypeErrorIfStrict, -1 /* >= 1 */, 1) \
F(TerminateExecution, 0, 1) \
F(Typeof, 1, 1) \
F(UnwindAndFindExceptionHandler, 0, 1)
......
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --turbofan --no-always-turbofan
function TestMultiply(a, b) {
return a * b;
}
function OptimizeAndTest(fn) {
%PrepareFunctionForOptimization(fn);
assertEquals(fn(3n, 4n), 12n);
assertEquals(fn(5n, 6n), 30n);
%OptimizeFunctionOnNextCall(fn);
assertEquals(fn(7n, 8n), 56n);
assertOptimized(fn);
assertEquals(fn(7, 8), 56);
assertUnoptimized(fn);
}
OptimizeAndTest(TestMultiply);
......@@ -222,6 +222,22 @@ TEST_F(ThreadTerminationTest, TerminateBigIntMultiplication) {
"fail();");
}
TEST_F(ThreadTerminationTest, TerminateOptimizedBigIntMultiplication) {
i::FLAG_allow_natives_syntax = true;
TestTerminatingSlowOperation(
"function foo(a, b) { return a * b; }"
"%PrepareFunctionForOptimization(foo);"
"foo(1n, 2n);"
"foo(1n, 2n);"
"%OptimizeFunctionOnNextCall(foo);"
"foo(1n, 2n);"
"var a = 5n ** 555555n;"
"var b = 3n ** 3333333n;"
"terminate();"
"foo(a, b);"
"fail();");
}
TEST_F(ThreadTerminationTest, TerminateBigIntDivision) {
TestTerminatingSlowOperation(
"var a = 2n ** 2222222n;"
......
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