From 98300313b34958a4d7119bd82a70af275477638a Mon Sep 17 00:00:00 2001 From: Nico Hartmann Date: Fri, 23 Apr 2021 15:20:15 +0200 Subject: [PATCH] [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 Reviewed-by: Georg Neis Cr-Commit-Position: refs/heads/master@{#74147} --- src/compiler/effect-control-linearizer.cc | 20 ------------ src/compiler/js-call-reducer.cc | 6 ++-- src/compiler/opcodes.h | 5 +-- src/compiler/operation-typer.cc | 3 +- src/compiler/representation-change.h | 6 ++-- src/compiler/simplified-lowering.cc | 28 +++++++++++++++-- src/compiler/simplified-operator.cc | 30 ++++++++++++++++-- src/compiler/simplified-operator.h | 27 ++++++++++++++++- src/compiler/verifier.cc | 4 +-- src/runtime/runtime-test.cc | 6 ++++ src/runtime/runtime.h | 3 +- test/mjsunit/compiler/bigint-asuintn.js | 37 +++++++++++++++++++++++ 12 files changed, 135 insertions(+), 40 deletions(-) create mode 100644 test/mjsunit/compiler/bigint-asuintn.js diff --git a/src/compiler/effect-control-linearizer.cc b/src/compiler/effect-control-linearizer.cc index 924a4f08b0..554396a2dd 100644 --- a/src/compiler/effect-control-linearizer.cc +++ b/src/compiler/effect-control-linearizer.cc @@ -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(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()); diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc index 0bbe926dfa..b1a27b1ca6 100644 --- a/src/compiler/js-call-reducer.cc +++ b/src/compiler/js-call-reducer.cc @@ -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(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); } diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index 471c2e9941..31d0f73d5c 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -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) \ diff --git a/src/compiler/operation-typer.cc b/src/compiler/operation-typer.cc index 8b889c6948..4e3f16f952 100644 --- a/src/compiler/operation-typer.cc +++ b/src/compiler/operation-typer.cc @@ -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(); } diff --git a/src/compiler/representation-change.h b/src/compiler/representation-change.h index 334237ed1f..1c4d05ab0d 100644 --- a/src/compiler/representation-change.h +++ b/src/compiler/representation-change.h @@ -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); } diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc index a71e6270db..495be60af8 100644 --- a/src/compiler/simplified-lowering.cc +++ b/src/compiler/simplified-lowering.cc @@ -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(node, 0, UseInfo::TruncatingWord64()); + case IrOpcode::kSpeculativeBigIntAsUintN: { + const auto p = SpeculativeBigIntAsUintNParametersOf(node->op()); + DCHECK_LE(0, p.bits()); + DCHECK_LE(p.bits(), 64); + + ProcessInput(node, 0, + UseInfo::CheckedBigIntTruncatingWord64(p.feedback())); SetOutput(node, MachineRepresentation::kWord64, Type::BigInt()); + if (lower()) { + 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: diff --git a/src/compiler/simplified-operator.cc b/src/compiler/simplified-operator.cc index 09e3a80ec4..14c81bcc5c 100644 --- a/src/compiler/simplified-operator.cc +++ b/src/compiler/simplified-operator.cc @@ -615,6 +615,27 @@ NumberOperationParameters const& NumberOperationParametersOf( return OpParameter(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(op); +} + size_t hash_value(AllocateParameters info) { return base::hash_combine(info.type(), static_cast(info.allocation_type())); @@ -1296,11 +1317,14 @@ const Operator* SimplifiedOperatorBuilder::RuntimeAbort(AbortReason reason) { static_cast(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>(IrOpcode::kBigIntAsUintN, Operator::kPure, - "BigIntAsUintN", 1, 0, 0, 1, 0, 0, bits); + return zone()->New>( + IrOpcode::kSpeculativeBigIntAsUintN, Operator::kNoProperties, + "SpeculativeBigIntAsUintN", 1, 1, 1, 1, 1, 0, + SpeculativeBigIntAsUintNParameters(bits, feedback)); } const Operator* SimplifiedOperatorBuilder::UpdateInterruptBudget(int delta) { diff --git a/src/compiler/simplified-operator.h b/src/compiler/simplified-operator.h index da98307029..bfe64feb25 100644 --- a/src/compiler/simplified-operator.h +++ b/src/compiler/simplified-operator.h @@ -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(); diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 4335991ed8..b19e558f73 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -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: diff --git a/src/runtime/runtime-test.cc b/src/runtime/runtime-test.cc index 5e6e659f2e..0e48e953e3 100644 --- a/src/runtime/runtime-test.cc +++ b/src/runtime/runtime-test.cc @@ -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 diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 290ff7e1eb..2d9c96a5d8 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -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) \ diff --git a/test/mjsunit/compiler/bigint-asuintn.js b/test/mjsunit/compiler/bigint-asuintn.js new file mode 100644 index 0000000000..fb127bad9d --- /dev/null +++ b/test/mjsunit/compiler/bigint-asuintn.js @@ -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);