[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:
Nico Hartmann 2021-04-23 15:20:15 +02:00 committed by Commit Bot
parent 508248f745
commit 98300313b3
12 changed files with 135 additions and 40 deletions

View File

@ -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());

View File

@ -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);
}

View File

@ -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) \

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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:

View File

@ -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) {

View File

@ -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();

View File

@ -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:

View File

@ -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

View File

@ -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) \

View 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);