[turbofan] Decompose CheckBigInt64 and make it precise

This CL solves two problems:

- Eliminating redundant CheckBigInt/CheckBigInt64 by decomposing
  CheckBigInt64 to CheckBigInt and CheckedBigIntToBigInt64.
- Having precise checks for SignedBigInt64 to make the range of BigInt64
  consistent in CheckedBigInt64Ops and CheckedBigIntToBigInt64.
  Otherwise, there would be semantic difference between the subgraphs
  where we keep CheckBigInt64 inbetween two CheckedBigInt64Ops (e.g.,
  the variant assert_types) and the subgraphs where we eliminate the
  checks.

Bug: v8:9407
Change-Id: I79a5c99e12eb3f3ffc7b5cbfc51191e6792f634b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3960333
Commit-Queue: Qifan Pan <panq@google.com>
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83899}
This commit is contained in:
Qifan Pan 2022-10-25 10:15:55 +02:00 committed by V8 LUCI CQ
parent 100fbd89f2
commit 7eb43bf494
13 changed files with 87 additions and 63 deletions

View File

@ -84,7 +84,7 @@ class EffectControlLinearizer {
Node* LowerCheckReceiverOrNullOrUndefined(Node* node, Node* frame_state);
Node* LowerCheckString(Node* node, Node* frame_state);
Node* LowerCheckBigInt(Node* node, Node* frame_state);
Node* LowerCheckBigInt64(Node* node, Node* frame_state);
Node* LowerCheckedBigIntToBigInt64(Node* node, Node* frame_state);
Node* LowerCheckSymbol(Node* node, Node* frame_state);
void LowerCheckIf(Node* node, Node* frame_state);
Node* LowerCheckedInt32Add(Node* node, Node* frame_state);
@ -1013,8 +1013,8 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckBigInt:
result = LowerCheckBigInt(node, frame_state);
break;
case IrOpcode::kCheckBigInt64:
result = LowerCheckBigInt64(node, frame_state);
case IrOpcode::kCheckedBigIntToBigInt64:
result = LowerCheckedBigIntToBigInt64(node, frame_state);
break;
case IrOpcode::kCheckInternalizedString:
result = LowerCheckInternalizedString(node, frame_state);
@ -2948,28 +2948,17 @@ Node* EffectControlLinearizer::LowerCheckBigInt(Node* node, Node* frame_state) {
return value;
}
Node* EffectControlLinearizer::LowerCheckBigInt64(Node* node,
Node* frame_state) {
Node* EffectControlLinearizer::LowerCheckedBigIntToBigInt64(Node* node,
Node* frame_state) {
DCHECK(machine()->Is64());
auto done = __ MakeLabel();
auto if_not_zero = __ MakeLabel();
auto if_may_be_out_of_range = __ MakeDeferredLabel();
Node* value = node->InputAt(0);
const CheckParameters& params = CheckParametersOf(node->op());
// Check for Smi.
Node* smi_check = ObjectIsSmi(value);
__ DeoptimizeIf(DeoptimizeReason::kSmi, params.feedback(), smi_check,
frame_state);
// Check for BigInt.
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
Node* bi_check = __ TaggedEqual(value_map, __ BigIntMapConstant());
__ DeoptimizeIfNot(DeoptimizeReason::kWrongInstanceType, params.feedback(),
bi_check, frame_state);
// Check for BigInt64.
Node* bitfield = __ LoadField(AccessBuilder::ForBigIntBitfield(), value);
__ GotoIfNot(__ Word32Equal(bitfield, __ Int32Constant(0)), &if_not_zero);
__ Goto(&done);
@ -2980,19 +2969,32 @@ Node* EffectControlLinearizer::LowerCheckBigInt64(Node* node,
Node* length =
__ Word32And(bitfield, __ Int32Constant(BigInt::LengthBits::kMask));
__ DeoptimizeIfNot(
DeoptimizeReason::kWrongInstanceType, params.feedback(),
DeoptimizeReason::kNotABigInt64, params.feedback(),
__ Word32Equal(length, __ Int32Constant(uint32_t{1}
<< BigInt::LengthBits::kShift)),
frame_state);
Node* lsd =
__ LoadField(AccessBuilder::ForBigIntLeastSignificantDigit64(), value);
// Accepted small BigInts are in the range [-2^63 + 1, 2^63 - 1].
// Excluding -2^63 from the range makes the check simpler and faster.
Node* bi64_check = __ Uint64LessThanOrEqual(
Node* magnitude_check = __ Uint64LessThanOrEqual(
lsd, __ Int64Constant(std::numeric_limits<int64_t>::max()));
__ DeoptimizeIfNot(DeoptimizeReason::kWrongInstanceType, params.feedback(),
bi64_check, frame_state);
__ Branch(magnitude_check, &done, &if_may_be_out_of_range);
__ Bind(&if_may_be_out_of_range);
Node* sign =
__ Word32And(bitfield, __ Int32Constant(BigInt::SignBits::kMask));
Node* sign_check =
__ Word32Equal(sign, __ Int32Constant(BigInt::SignBits::kMask));
__ DeoptimizeIfNot(DeoptimizeReason::kNotABigInt64, params.feedback(),
sign_check, frame_state);
Node* min_check = __ Word64Equal(
lsd, __ Int64Constant(std::numeric_limits<int64_t>::min()));
__ DeoptimizeIfNot(DeoptimizeReason::kNotABigInt64, params.feedback(),
min_check, frame_state);
__ Goto(&done);
}

View File

@ -400,7 +400,7 @@
#define SIMPLIFIED_BIGINT_UNOP_LIST(V) \
V(BigIntNegate) \
V(CheckBigInt) \
V(CheckBigInt64)
V(CheckedBigIntToBigInt64)
#define SIMPLIFIED_SPECULATIVE_NUMBER_UNOP_LIST(V) V(SpeculativeToNumber)

View File

@ -585,7 +585,9 @@ Type OperationTyper::SpeculativeBigIntAsUintN(Type) {
Type OperationTyper::CheckBigInt(Type type) { return Type::BigInt(); }
Type OperationTyper::CheckBigInt64(Type type) { return Type::SignedBigInt64(); }
Type OperationTyper::CheckedBigIntToBigInt64(Type type) {
return Type::SignedBigInt64();
}
Type OperationTyper::NumberAdd(Type lhs, Type rhs) {
DCHECK(lhs.Is(Type::Number()));

View File

@ -20,7 +20,7 @@ Reduction RedundancyElimination::Reduce(Node* node) {
if (node_checks_.Get(node)) return NoChange();
switch (node->opcode()) {
case IrOpcode::kCheckBigInt:
case IrOpcode::kCheckBigInt64:
case IrOpcode::kCheckedBigIntToBigInt64:
case IrOpcode::kCheckBounds:
case IrOpcode::kCheckClosure:
case IrOpcode::kCheckEqualsInternalizedString:
@ -38,15 +38,14 @@ Reduction RedundancyElimination::Reduce(Node* node) {
case IrOpcode::kCheckSymbol:
// These are not really check nodes, but behave the same in that they can be
// folded together if repeated with identical inputs.
case IrOpcode::kBigIntAdd:
case IrOpcode::kBigIntSubtract:
case IrOpcode::kStringCharCodeAt:
case IrOpcode::kStringCodePointAt:
case IrOpcode::kStringFromCodePointAt:
case IrOpcode::kStringSubstring:
#define SIMPLIFIED_CHECKED_OP(Opcode) case IrOpcode::k##Opcode:
SIMPLIFIED_CHECKED_OP_LIST(SIMPLIFIED_CHECKED_OP)
#undef SIMPLIFIED_CHECKED_OP
#define SIMPLIFIED_OP(Opcode) case IrOpcode::k##Opcode:
SIMPLIFIED_CHECKED_OP_LIST(SIMPLIFIED_OP)
SIMPLIFIED_BIGINT_BINOP_LIST(SIMPLIFIED_OP)
#undef SIMPLIFIED_OP
return ReduceCheckNode(node);
case IrOpcode::kSpeculativeNumberEqual:
case IrOpcode::kSpeculativeNumberLessThan:
@ -165,7 +164,7 @@ bool CheckSubsumes(Node const* a, Node const* b) {
case IrOpcode::kCheckString:
case IrOpcode::kCheckNumber:
case IrOpcode::kCheckBigInt:
case IrOpcode::kCheckBigInt64:
case IrOpcode::kCheckedBigIntToBigInt64:
break;
case IrOpcode::kCheckedInt32ToTaggedSigned:
case IrOpcode::kCheckedInt64ToInt32:

View File

@ -505,7 +505,15 @@ Node* RepresentationChanger::GetTaggedPointerRepresentationFor(
if (output_type.Is(Type::SignedBigInt64())) {
return node;
}
op = simplified()->CheckBigInt64(use_info.feedback());
if (!output_type.Is(Type::BigInt())) {
node = InsertConversion(
node, simplified()->CheckBigInt(use_info.feedback()), use_node);
}
op = simplified()->CheckedBigIntToBigInt64(use_info.feedback());
} else if (output_rep == MachineRepresentation::kTaggedPointer ||
!output_type.Maybe(Type::SignedSmall())) {
DCHECK_NE(output_rep, MachineRepresentation::kTaggedSigned);
return node;
} else {
return TypeError(node, output_rep, output_type,
MachineRepresentation::kTaggedPointer);

View File

@ -283,8 +283,9 @@ void SimplifiedLoweringVerifier::VisitNode(Node* node,
CheckAndSet(node, input_type, InputTruncation(node, 0));
break;
}
case IrOpcode::kCheckBigInt64: {
case IrOpcode::kCheckedBigIntToBigInt64: {
Type input_type = InputType(node, 0);
CHECK(input_type.Is(Type::BigInt()));
input_type =
Type::Intersect(input_type, Type::SignedBigInt64(), graph_zone());
CheckAndSet(node, input_type, InputTruncation(node, 0));

View File

@ -2963,24 +2963,6 @@ class RepresentationSelector {
}
return;
}
case IrOpcode::kCheckBigInt: {
if (InputIs(node, Type::BigInt())) {
VisitNoop<T>(node, truncation);
} else {
VisitUnop<T>(node, UseInfo::AnyTagged(),
MachineRepresentation::kTaggedPointer);
}
return;
}
case IrOpcode::kCheckBigInt64: {
if (InputIs(node, Type::BigInt())) {
VisitNoop<T>(node, truncation);
} else {
VisitUnop<T>(node, UseInfo::AnyTagged(),
MachineRepresentation::kTaggedPointer);
}
return;
}
case IrOpcode::kSpeculativeBigIntAsIntN:
case IrOpcode::kSpeculativeBigIntAsUintN: {
const bool is_asuintn =

View File

@ -847,7 +847,7 @@ bool operator==(CheckMinusZeroParameters const& lhs,
V(CheckSmi, 1, 1) \
V(CheckString, 1, 1) \
V(CheckBigInt, 1, 1) \
V(CheckBigInt64, 1, 1) \
V(CheckedBigIntToBigInt64, 1, 1) \
V(CheckedInt32ToTaggedSigned, 1, 1) \
V(CheckedInt64ToInt32, 1, 1) \
V(CheckedInt64ToTaggedSigned, 1, 1) \

View File

@ -935,7 +935,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* CheckedTaggedToTaggedPointer(const FeedbackSource& feedback);
const Operator* CheckedTaggedToTaggedSigned(const FeedbackSource& feedback);
const Operator* CheckBigInt(const FeedbackSource& feedback);
const Operator* CheckBigInt64(const FeedbackSource& feedback);
const Operator* CheckedBigIntToBigInt64(const FeedbackSource& feedback);
const Operator* CheckedTruncateTaggedToWord32(CheckTaggedInputMode,
const FeedbackSource& feedback);
const Operator* CheckedUint32Div();

View File

@ -1654,8 +1654,8 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::BigInt());
break;
case IrOpcode::kCheckBigInt64:
CheckValueInputIs(node, 0, Type::Any());
case IrOpcode::kCheckedBigIntToBigInt64:
CheckValueInputIs(node, 0, Type::BigInt());
CheckTypeIs(node, Type::SignedBigInt64());
break;
case IrOpcode::kFastApiCall:

View File

@ -40,6 +40,7 @@ namespace internal {
V(NaN, "NaN") \
V(NoCache, "no cache") \
V(NotABigInt, "not a BigInt") \
V(NotABigInt64, "not a BigInt64") \
V(NotAHeapNumber, "not a heap number") \
V(NotAJavaScriptObject, "not a JavaScript object") \
V(NotAJavaScriptObjectOrNullOrUndefined, \

View File

@ -4,7 +4,7 @@
// Flags: --allow-natives-syntax --turbofan --no-always-turbofan
(function OptimizeAndTest() {
(function OptimizeAndTestNegativeLimit() {
function f(x, y) {
return x + y;
}
@ -12,24 +12,54 @@
assertEquals(1n, f(0n, 1n));
assertEquals(5n, f(2n, 3n));
%OptimizeFunctionOnNextCall(f);
assertEquals(9n, f(4n, 5n));
assertEquals(-(2n ** 63n), f(-(2n ** 63n), 0n));
assertOptimized(f);
// Re-prepare the function before the first deopt to ensure type feedback is
// not cleared by an umtimely gc.
%PrepareFunctionForOptimization(f);
assertOptimized(f);
// CheckBigInt64 should trigger deopt.
assertEquals(-(2n ** 63n), f(-(2n ** 63n), 0n));
// CheckBigInt64 should trigger deopt on INT_MIN - 1.
assertEquals(-(2n ** 63n) - 1n, f(-(2n ** 63n) - 1n, 0n));
if (%Is64Bit()) {
assertUnoptimized(f);
assertEquals(1n, f(0n, 1n));
assertEquals(5n, f(2n, 3n));
%OptimizeFunctionOnNextCall(f);
assertEquals(9n, f(4n, 5n));
assertEquals(-(2n ** 63n), f(-(2n ** 63n), 0n));
assertOptimized(f);
// Ensure there is no deopt loop.
assertEquals(-(2n ** 63n), f(-(2n ** 63n), 0n));
assertEquals(-(2n ** 63n) - 1n, f(-(2n ** 63n) - 1n, 0n));
assertOptimized(f);
}
})();
(function OptimizeAndTestPositiveLimit() {
function f(x, y) {
return x + y;
}
%PrepareFunctionForOptimization(f);
assertEquals(1n, f(0n, 1n));
assertEquals(5n, f(2n, 3n));
%OptimizeFunctionOnNextCall(f);
assertEquals(2n ** 63n - 1n, f(2n ** 63n - 1n, 0n));
assertOptimized(f);
// Re-prepare the function before the first deopt to ensure type feedback is
// not cleared by an umtimely gc.
%PrepareFunctionForOptimization(f);
assertOptimized(f);
// CheckBigInt64 should trigger deopt on INT_MAX + 1.
assertEquals(2n ** 63n, f(2n ** 63n, 0n));
if (%Is64Bit()) {
assertUnoptimized(f);
assertEquals(1n, f(0n, 1n));
assertEquals(5n, f(2n, 3n));
%OptimizeFunctionOnNextCall(f);
assertEquals(2n ** 63n - 1n, f(2n ** 63n - 1n, 0n));
assertOptimized(f);
// Ensure there is no deopt loop.
assertEquals(2n ** 63n, f(2n ** 63n, 0n));
assertOptimized(f);
}
})();

View File

@ -1339,7 +1339,6 @@
# which in turn can lead to different deopt behavior.
'compiler/number-abs': [SKIP],
'compiler/number-toboolean': [SKIP],
'compiler/bigint64-div-no-deopt-loop': [SKIP],
# Type assertions block the propagation of word64 truncation useinfo,
# leading to differences in representation selection.
'compiler/bigint-multiply-truncate': [SKIP],