[TurboFan] Streamline BigInt.asUintN lowering
This CL applies the following changes: - JSCallReducer no longer generates a CheckBigInt in front of the generated BigIntAsUintN. - This results in a slight change of the semantics of the latter, which now includes the necessary type check. Typer and Verifier are changed accordingly. - The BigIntAsUintN operator is now effectful, since it can now deopt. - IrOpcode::kBigIntAsUintN is now lowered in SimplifedLowering instead of EffectControlLinearizer, the necessary type check is introduced by the RepresentationChanger. - Adds a small mjsunit test to check the correct deoptimization behavior of optimized BigInt.asUintN. ==> Remove UseInfo::TruncatingWord64()! Drive-by: Fix an issue in ChangeUnaryToPureBinaryOp when the new_input is at index 1. Drive-by: Introduce an %Is64Bit() intrinsic to allow tests to distinguish 32 and 64 bit architectures. Bug: v8:11682 Change-Id: I448f892d3bd2280d731ae5b248c833de8faf1bd5 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2843816 Commit-Queue: Nico Hartmann <nicohartmann@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#74147}
This commit is contained in:
parent
508248f745
commit
98300313b3
@ -113,7 +113,6 @@ class EffectControlLinearizer {
|
||||
Node* LowerCheckedTaggedToFloat64(Node* node, Node* frame_state);
|
||||
Node* LowerCheckedTaggedToTaggedSigned(Node* node, Node* frame_state);
|
||||
Node* LowerCheckedTaggedToTaggedPointer(Node* node, Node* frame_state);
|
||||
Node* LowerBigIntAsUintN(Node* node, Node* frame_state);
|
||||
Node* LowerChangeUint64ToBigInt(Node* node);
|
||||
Node* LowerTruncateBigIntToUint64(Node* node);
|
||||
Node* LowerChangeTaggedToFloat64(Node* node);
|
||||
@ -1061,9 +1060,6 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
|
||||
case IrOpcode::kCheckedTaggedToTaggedPointer:
|
||||
result = LowerCheckedTaggedToTaggedPointer(node, frame_state);
|
||||
break;
|
||||
case IrOpcode::kBigIntAsUintN:
|
||||
result = LowerBigIntAsUintN(node, frame_state);
|
||||
break;
|
||||
case IrOpcode::kChangeUint64ToBigInt:
|
||||
result = LowerChangeUint64ToBigInt(node);
|
||||
break;
|
||||
@ -2936,22 +2932,6 @@ Node* EffectControlLinearizer::LowerCheckBigInt(Node* node, Node* frame_state) {
|
||||
return value;
|
||||
}
|
||||
|
||||
Node* EffectControlLinearizer::LowerBigIntAsUintN(Node* node,
|
||||
Node* frame_state) {
|
||||
DCHECK(machine()->Is64());
|
||||
|
||||
const int bits = OpParameter<int>(node->op());
|
||||
DCHECK(0 <= bits && bits <= 64);
|
||||
|
||||
if (bits == 64) {
|
||||
// Reduce to nop.
|
||||
return node->InputAt(0);
|
||||
} else {
|
||||
const uint64_t msk = (1ULL << bits) - 1ULL;
|
||||
return __ Word64And(node->InputAt(0), __ Int64Constant(msk));
|
||||
}
|
||||
}
|
||||
|
||||
Node* EffectControlLinearizer::LowerChangeUint64ToBigInt(Node* node) {
|
||||
DCHECK(machine()->Is64());
|
||||
|
||||
|
@ -7829,9 +7829,9 @@ Reduction JSCallReducer::ReduceBigIntAsUintN(Node* node) {
|
||||
NumberMatcher matcher(bits);
|
||||
if (matcher.IsInteger() && matcher.IsInRange(0, 64)) {
|
||||
const int bits_value = static_cast<int>(matcher.ResolvedValue());
|
||||
value = effect = graph()->NewNode(simplified()->CheckBigInt(p.feedback()),
|
||||
value, effect, control);
|
||||
value = graph()->NewNode(simplified()->BigIntAsUintN(bits_value), value);
|
||||
value = effect = graph()->NewNode(
|
||||
simplified()->SpeculativeBigIntAsUintN(bits_value, p.feedback()), value,
|
||||
effect, control);
|
||||
ReplaceWithValue(node, value, effect);
|
||||
return Replace(value);
|
||||
}
|
||||
|
@ -385,7 +385,6 @@
|
||||
V(NumberSilenceNaN)
|
||||
|
||||
#define SIMPLIFIED_BIGINT_UNOP_LIST(V) \
|
||||
V(BigIntAsUintN) \
|
||||
V(BigIntNegate) \
|
||||
V(CheckBigInt)
|
||||
|
||||
@ -497,7 +496,9 @@
|
||||
V(SpeculativeBigIntAdd) \
|
||||
V(SpeculativeBigIntSubtract)
|
||||
|
||||
#define SIMPLIFIED_SPECULATIVE_BIGINT_UNOP_LIST(V) V(SpeculativeBigIntNegate)
|
||||
#define SIMPLIFIED_SPECULATIVE_BIGINT_UNOP_LIST(V) \
|
||||
V(SpeculativeBigIntAsUintN) \
|
||||
V(SpeculativeBigIntNegate)
|
||||
|
||||
#define SIMPLIFIED_OP_LIST(V) \
|
||||
SIMPLIFIED_CHANGE_OP_LIST(V) \
|
||||
|
@ -576,8 +576,7 @@ Type OperationTyper::NumberSilenceNaN(Type type) {
|
||||
return type;
|
||||
}
|
||||
|
||||
Type OperationTyper::BigIntAsUintN(Type type) {
|
||||
DCHECK(type.Is(Type::BigInt()));
|
||||
Type OperationTyper::SpeculativeBigIntAsUintN(Type type) {
|
||||
return Type::BigInt();
|
||||
}
|
||||
|
||||
|
@ -180,10 +180,10 @@ class UseInfo {
|
||||
static UseInfo TruncatingWord32() {
|
||||
return UseInfo(MachineRepresentation::kWord32, Truncation::Word32());
|
||||
}
|
||||
static UseInfo TruncatingWord64() {
|
||||
return UseInfo(MachineRepresentation::kWord64, Truncation::Word64());
|
||||
}
|
||||
static UseInfo CheckedBigIntTruncatingWord64(const FeedbackSource& feedback) {
|
||||
// Note that Trunction::Word64() can safely use kIdentifyZero, because
|
||||
// TypeCheckKind::kBigInt will make sure we deopt for anything other than
|
||||
// type BigInt anyway.
|
||||
return UseInfo(MachineRepresentation::kWord64, Truncation::Word64(),
|
||||
TypeCheckKind::kBigInt, feedback);
|
||||
}
|
||||
|
@ -836,7 +836,13 @@ class RepresentationSelector {
|
||||
} else {
|
||||
DCHECK_EQ(0, node->op()->ControlInputCount());
|
||||
}
|
||||
node->InsertInput(jsgraph_->zone(), new_input_index, new_input);
|
||||
if (new_input_index == 0) {
|
||||
node->InsertInput(jsgraph_->zone(), 0, new_input);
|
||||
} else {
|
||||
DCHECK_EQ(new_input_index, 1);
|
||||
DCHECK_EQ(node->InputCount(), 1);
|
||||
node->AppendInput(jsgraph_->zone(), new_input);
|
||||
}
|
||||
ChangeOp(node, new_op);
|
||||
}
|
||||
|
||||
@ -2830,9 +2836,25 @@ class RepresentationSelector {
|
||||
}
|
||||
return;
|
||||
}
|
||||
case IrOpcode::kBigIntAsUintN: {
|
||||
ProcessInput<T>(node, 0, UseInfo::TruncatingWord64());
|
||||
case IrOpcode::kSpeculativeBigIntAsUintN: {
|
||||
const auto p = SpeculativeBigIntAsUintNParametersOf(node->op());
|
||||
DCHECK_LE(0, p.bits());
|
||||
DCHECK_LE(p.bits(), 64);
|
||||
|
||||
ProcessInput<T>(node, 0,
|
||||
UseInfo::CheckedBigIntTruncatingWord64(p.feedback()));
|
||||
SetOutput<T>(node, MachineRepresentation::kWord64, Type::BigInt());
|
||||
if (lower<T>()) {
|
||||
if (p.bits() == 0) {
|
||||
DeferReplacement(node, jsgraph_->ZeroConstant());
|
||||
} else if (p.bits() == 64) {
|
||||
DeferReplacement(node, node->InputAt(0));
|
||||
} else {
|
||||
const uint64_t mask = (1ULL << p.bits()) - 1ULL;
|
||||
ChangeUnaryToPureBinaryOp(node, lowering->machine()->Word64And(), 1,
|
||||
jsgraph_->Int64Constant(mask));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case IrOpcode::kNumberAcos:
|
||||
|
@ -615,6 +615,27 @@ NumberOperationParameters const& NumberOperationParametersOf(
|
||||
return OpParameter<NumberOperationParameters>(op);
|
||||
}
|
||||
|
||||
bool operator==(SpeculativeBigIntAsUintNParameters const& lhs,
|
||||
SpeculativeBigIntAsUintNParameters const& rhs) {
|
||||
return lhs.bits() == rhs.bits() && lhs.feedback() == rhs.feedback();
|
||||
}
|
||||
|
||||
size_t hash_value(SpeculativeBigIntAsUintNParameters const& p) {
|
||||
FeedbackSource::Hash feedback_hash;
|
||||
return base::hash_combine(p.bits(), feedback_hash(p.feedback()));
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
SpeculativeBigIntAsUintNParameters const& p) {
|
||||
return os << p.bits() << ", " << p.feedback();
|
||||
}
|
||||
|
||||
SpeculativeBigIntAsUintNParameters const& SpeculativeBigIntAsUintNParametersOf(
|
||||
Operator const* op) {
|
||||
DCHECK_EQ(IrOpcode::kSpeculativeBigIntAsUintN, op->opcode());
|
||||
return OpParameter<SpeculativeBigIntAsUintNParameters>(op);
|
||||
}
|
||||
|
||||
size_t hash_value(AllocateParameters info) {
|
||||
return base::hash_combine(info.type(),
|
||||
static_cast<int>(info.allocation_type()));
|
||||
@ -1296,11 +1317,14 @@ const Operator* SimplifiedOperatorBuilder::RuntimeAbort(AbortReason reason) {
|
||||
static_cast<int>(reason)); // parameter
|
||||
}
|
||||
|
||||
const Operator* SimplifiedOperatorBuilder::BigIntAsUintN(int bits) {
|
||||
const Operator* SimplifiedOperatorBuilder::SpeculativeBigIntAsUintN(
|
||||
int bits, const FeedbackSource& feedback) {
|
||||
CHECK(0 <= bits && bits <= 64);
|
||||
|
||||
return zone()->New<Operator1<int>>(IrOpcode::kBigIntAsUintN, Operator::kPure,
|
||||
"BigIntAsUintN", 1, 0, 0, 1, 0, 0, bits);
|
||||
return zone()->New<Operator1<SpeculativeBigIntAsUintNParameters>>(
|
||||
IrOpcode::kSpeculativeBigIntAsUintN, Operator::kNoProperties,
|
||||
"SpeculativeBigIntAsUintN", 1, 1, 1, 1, 1, 0,
|
||||
SpeculativeBigIntAsUintNParameters(bits, feedback));
|
||||
}
|
||||
|
||||
const Operator* SimplifiedOperatorBuilder::UpdateInterruptBudget(int delta) {
|
||||
|
@ -606,6 +606,30 @@ bool operator==(NumberOperationParameters const&,
|
||||
const NumberOperationParameters& NumberOperationParametersOf(const Operator* op)
|
||||
V8_WARN_UNUSED_RESULT;
|
||||
|
||||
class SpeculativeBigIntAsUintNParameters {
|
||||
public:
|
||||
SpeculativeBigIntAsUintNParameters(int bits, const FeedbackSource& feedback)
|
||||
: bits_(bits), feedback_(feedback) {
|
||||
DCHECK_GE(bits_, 0);
|
||||
DCHECK_LE(bits_, 64);
|
||||
}
|
||||
|
||||
int bits() const { return bits_; }
|
||||
const FeedbackSource& feedback() const { return feedback_; }
|
||||
|
||||
private:
|
||||
int bits_;
|
||||
FeedbackSource feedback_;
|
||||
};
|
||||
|
||||
size_t hash_value(SpeculativeBigIntAsUintNParameters const&);
|
||||
V8_EXPORT_PRIVATE std::ostream& operator<<(
|
||||
std::ostream&, const SpeculativeBigIntAsUintNParameters&);
|
||||
bool operator==(SpeculativeBigIntAsUintNParameters const&,
|
||||
SpeculativeBigIntAsUintNParameters const&);
|
||||
const SpeculativeBigIntAsUintNParameters& SpeculativeBigIntAsUintNParametersOf(
|
||||
const Operator* op) V8_WARN_UNUSED_RESULT;
|
||||
|
||||
int FormalParameterCountOf(const Operator* op) V8_WARN_UNUSED_RESULT;
|
||||
|
||||
class AllocateParameters {
|
||||
@ -813,7 +837,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
|
||||
const Operator* SpeculativeBigIntAdd(BigIntOperationHint hint);
|
||||
const Operator* SpeculativeBigIntSubtract(BigIntOperationHint hint);
|
||||
const Operator* SpeculativeBigIntNegate(BigIntOperationHint hint);
|
||||
const Operator* BigIntAsUintN(int bits);
|
||||
const Operator* SpeculativeBigIntAsUintN(int bits,
|
||||
const FeedbackSource& feedback);
|
||||
|
||||
const Operator* ReferenceEqual();
|
||||
const Operator* SameValue();
|
||||
|
@ -954,8 +954,8 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
||||
case IrOpcode::kSpeculativeBigIntNegate:
|
||||
CheckTypeIs(node, Type::BigInt());
|
||||
break;
|
||||
case IrOpcode::kBigIntAsUintN:
|
||||
CheckValueInputIs(node, 0, Type::BigInt());
|
||||
case IrOpcode::kSpeculativeBigIntAsUintN:
|
||||
CheckValueInputIs(node, 0, Type::Any());
|
||||
CheckTypeIs(node, Type::BigInt());
|
||||
break;
|
||||
case IrOpcode::kBigIntAdd:
|
||||
|
@ -1348,5 +1348,11 @@ RUNTIME_FUNCTION(Runtime_NewRegExpWithBacktrackLimit) {
|
||||
isolate, JSRegExp::New(isolate, pattern, flags, backtrack_limit));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_Is64Bit) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK_EQ(0, args.length());
|
||||
return isolate->heap()->ToBoolean(kSystemPointerSize == 8);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -552,7 +552,8 @@ namespace internal {
|
||||
I(DeoptimizeNow, 0, 1) \
|
||||
F(PromiseSpeciesProtector, 0, 1) \
|
||||
F(IsConcatSpreadableProtector, 0, 1) \
|
||||
F(RegExpSpeciesProtector, 0, 1)
|
||||
F(RegExpSpeciesProtector, 0, 1) \
|
||||
F(Is64Bit, 0, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_TYPEDARRAY(F, I) \
|
||||
F(ArrayBufferDetach, 1, 1) \
|
||||
|
37
test/mjsunit/compiler/bigint-asuintn.js
Normal file
37
test/mjsunit/compiler/bigint-asuintn.js
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2021 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 --opt --no-always-opt
|
||||
|
||||
|
||||
function f(x) {
|
||||
return BigInt.asUintN(3, x);
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(f);
|
||||
assertEquals(7n, f(7n));
|
||||
assertEquals(1n, f(9n));
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
assertEquals(7n, f(7n));
|
||||
assertEquals(1n, f(9n));
|
||||
assertOptimized(f);
|
||||
|
||||
// BigInt.asUintN throws TypeError for non-BigInt arguments.
|
||||
assertThrows(() => f(2), TypeError);
|
||||
if(%Is64Bit()) {
|
||||
// On 64 bit architectures TurboFan optimizes BigInt.asUintN to native code
|
||||
// that deoptimizes on non-BigInt arguments.
|
||||
assertUnoptimized(f);
|
||||
|
||||
// The next time the function is optimized, speculation should be disabled
|
||||
// so the builtin call is kept, which won't deoptimize again.
|
||||
%PrepareFunctionForOptimization(f);
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
}
|
||||
assertEquals(7n, f(7n));
|
||||
assertOptimized(f);
|
||||
|
||||
// Re-optimized still throws but does not deopt-loop.
|
||||
assertThrows(() => f(2), TypeError);
|
||||
assertOptimized(f);
|
Loading…
Reference in New Issue
Block a user