Reland "[turbofan] Rematerialize BigInt64 in deopt"

This is a reland of commit 80fb281561

This CL fixes the signedness of rematerialized Numbers from immediates, which was introduced by the original change. Besides, BigInt truncation to zero bits is lowered to Int64Constant instead of NumberConstant of zero, which will flow into the state values directly.

Original change's description:
> [turbofan] Rematerialize BigInt64 in deopt
>
> This CL introduces two MachineTypes - SignedBigInt64 and UnsignedBigInt64, which are represented as Word64 but will be rematerialized to BigInt in deoptimization. This will avoid unnecessary conversions for BigInt64s when they are passed to StateValues.
>
> Bug: v8:9407
> Change-Id: I65fdee3e028ed8f9920b1c20ff78993c7784de48
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3858238
> Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
> Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
> Commit-Queue: Qifan Pan <panq@google.com>
> Cr-Commit-Position: refs/heads/main@{#83230}

Bug: v8:9407, chromium:1364319, chromium:1364400
Change-Id: I0b4e077b52f64af46018b6c045893bbd56153f32
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3899258
Commit-Queue: Qifan Pan <panq@google.com>
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83384}
This commit is contained in:
Qifan Pan 2022-09-16 17:12:01 +02:00 committed by V8 LUCI CQ
parent 4e23f53cdf
commit 98c6c367b5
15 changed files with 513 additions and 41 deletions

View File

@ -79,6 +79,10 @@ std::ostream& operator<<(std::ostream& os, MachineSemantic type) {
return os << "kTypeInt64";
case MachineSemantic::kUint64:
return os << "kTypeUint64";
case MachineSemantic::kSignedBigInt64:
return os << "kTypeSignedBigInt64";
case MachineSemantic::kUnsignedBigInt64:
return os << "kTypeUnsignedBigInt64";
case MachineSemantic::kNumber:
return os << "kTypeNumber";
case MachineSemantic::kAny:

View File

@ -86,6 +86,8 @@ enum class MachineSemantic : uint8_t {
kUint32,
kInt64,
kUint64,
kSignedBigInt64,
kUnsignedBigInt64,
kNumber,
kAny
};
@ -189,6 +191,14 @@ class MachineType {
return MachineType(MachineRepresentation::kWord64,
MachineSemantic::kUint64);
}
constexpr static MachineType SignedBigInt64() {
return MachineType(MachineRepresentation::kWord64,
MachineSemantic::kSignedBigInt64);
}
constexpr static MachineType UnsignedBigInt64() {
return MachineType(MachineRepresentation::kWord64,
MachineSemantic::kUnsignedBigInt64);
}
constexpr static MachineType Float32() {
return MachineType(MachineRepresentation::kFloat32,
MachineSemantic::kNumber);

View File

@ -1171,6 +1171,12 @@ void CodeGenerator::AddTranslationForOperand(Instruction* instr,
translations_.StoreUint32StackSlot(LocationOperand::cast(op)->index());
} else if (type == MachineType::Int64()) {
translations_.StoreInt64StackSlot(LocationOperand::cast(op)->index());
} else if (type == MachineType::SignedBigInt64()) {
translations_.StoreSignedBigInt64StackSlot(
LocationOperand::cast(op)->index());
} else if (type == MachineType::UnsignedBigInt64()) {
translations_.StoreUnsignedBigInt64StackSlot(
LocationOperand::cast(op)->index());
} else {
#if defined(V8_COMPRESS_POINTERS)
CHECK(MachineRepresentation::kTagged == type.representation() ||
@ -1199,6 +1205,10 @@ void CodeGenerator::AddTranslationForOperand(Instruction* instr,
translations_.StoreUint32Register(converter.ToRegister(op));
} else if (type == MachineType::Int64()) {
translations_.StoreInt64Register(converter.ToRegister(op));
} else if (type == MachineType::SignedBigInt64()) {
translations_.StoreSignedBigInt64Register(converter.ToRegister(op));
} else if (type == MachineType::UnsignedBigInt64()) {
translations_.StoreUnsignedBigInt64Register(converter.ToRegister(op));
} else {
#if defined(V8_COMPRESS_POINTERS)
CHECK(MachineRepresentation::kTagged == type.representation() ||
@ -1229,7 +1239,7 @@ void CodeGenerator::AddTranslationForOperand(Instruction* instr,
DCHECK_EQ(4, kSystemPointerSize);
Smi smi(static_cast<Address>(constant.ToInt32()));
DCHECK(smi.IsSmi());
literal = DeoptimizationLiteral(smi.value());
literal = DeoptimizationLiteral(static_cast<double>(smi.value()));
} else if (type.representation() == MachineRepresentation::kBit) {
if (constant.ToInt32() == 0) {
literal =
@ -1247,15 +1257,24 @@ void CodeGenerator::AddTranslationForOperand(Instruction* instr,
constant.ToInt32() == FrameStateDescriptor::kImpossibleValue);
if (type == MachineType::Uint32()) {
literal = DeoptimizationLiteral(
static_cast<uint32_t>(constant.ToInt32()));
static_cast<double>(static_cast<uint32_t>(constant.ToInt32())));
} else {
literal = DeoptimizationLiteral(constant.ToInt32());
literal =
DeoptimizationLiteral(static_cast<double>(constant.ToInt32()));
}
}
break;
case Constant::kInt64:
DCHECK_EQ(8, kSystemPointerSize);
if (type.representation() == MachineRepresentation::kWord64) {
if (type == MachineType::SignedBigInt64()) {
literal = DeoptimizationLiteral(constant.ToInt64());
} else if (type == MachineType::UnsignedBigInt64()) {
literal =
DeoptimizationLiteral(static_cast<uint64_t>(constant.ToInt64()));
} else if (type.representation() == MachineRepresentation::kWord64) {
CHECK_EQ(
constant.ToInt64(),
static_cast<int64_t>(static_cast<double>(constant.ToInt64())));
literal =
DeoptimizationLiteral(static_cast<double>(constant.ToInt64()));
} else {
@ -1264,7 +1283,7 @@ void CodeGenerator::AddTranslationForOperand(Instruction* instr,
DCHECK_EQ(MachineRepresentation::kTagged, type.representation());
Smi smi(static_cast<Address>(constant.ToInt64()));
DCHECK(smi.IsSmi());
literal = DeoptimizationLiteral(smi.value());
literal = DeoptimizationLiteral(static_cast<double>(smi.value()));
}
break;
case Constant::kFloat32:
@ -1325,6 +1344,12 @@ Handle<Object> DeoptimizationLiteral::Reify(Isolate* isolate) const {
case DeoptimizationLiteralKind::kNumber: {
return isolate->factory()->NewNumber(number_);
}
case DeoptimizationLiteralKind::kSignedBigInt64: {
return BigInt::FromInt64(isolate, signed_bigint64_);
}
case DeoptimizationLiteralKind::kUnsignedBigInt64: {
return BigInt::FromUint64(isolate, unsigned_bigint64_);
}
case DeoptimizationLiteralKind::kInvalid: {
UNREACHABLE();
}

View File

@ -55,26 +55,52 @@ class InstructionOperandIterator {
size_t pos_;
};
enum class DeoptimizationLiteralKind { kObject, kNumber, kInvalid };
enum class DeoptimizationLiteralKind {
kObject,
kNumber,
kSignedBigInt64,
kUnsignedBigInt64,
kInvalid
};
// Either a non-null Handle<Object> or a double.
// A non-null Handle<Object>, a double, an int64_t, or a uint64_t.
class DeoptimizationLiteral {
public:
DeoptimizationLiteral()
: kind_(DeoptimizationLiteralKind::kInvalid), object_(), number_(0) {}
: kind_(DeoptimizationLiteralKind::kInvalid), object_() {}
explicit DeoptimizationLiteral(Handle<Object> object)
: kind_(DeoptimizationLiteralKind::kObject), object_(object) {
CHECK(!object_.is_null());
}
explicit DeoptimizationLiteral(double number)
: kind_(DeoptimizationLiteralKind::kNumber), number_(number) {}
explicit DeoptimizationLiteral(int64_t signed_bigint64)
: kind_(DeoptimizationLiteralKind::kSignedBigInt64),
signed_bigint64_(signed_bigint64) {}
explicit DeoptimizationLiteral(uint64_t unsigned_bigint64)
: kind_(DeoptimizationLiteralKind::kUnsignedBigInt64),
unsigned_bigint64_(unsigned_bigint64) {}
Handle<Object> object() const { return object_; }
bool operator==(const DeoptimizationLiteral& other) const {
return kind_ == other.kind_ && object_.equals(other.object_) &&
base::bit_cast<uint64_t>(number_) ==
if (kind_ != other.kind_) {
return false;
}
switch (kind_) {
case DeoptimizationLiteralKind::kObject:
return object_.equals(other.object_);
case DeoptimizationLiteralKind::kNumber:
return base::bit_cast<uint64_t>(number_) ==
base::bit_cast<uint64_t>(other.number_);
case DeoptimizationLiteralKind::kSignedBigInt64:
return signed_bigint64_ == other.signed_bigint64_;
case DeoptimizationLiteralKind::kUnsignedBigInt64:
return unsigned_bigint64_ == other.unsigned_bigint64_;
case DeoptimizationLiteralKind::kInvalid:
return true;
}
UNREACHABLE();
}
Handle<Object> Reify(Isolate* isolate) const;
@ -91,8 +117,12 @@ class DeoptimizationLiteral {
private:
DeoptimizationLiteralKind kind_;
Handle<Object> object_;
double number_ = 0;
union {
Handle<Object> object_;
double number_;
int64_t signed_bigint64_;
uint64_t unsigned_bigint64_;
};
};
// These structs hold pc offsets for generated instructions and is only used

View File

@ -26,6 +26,10 @@ bool IsNonTruncatingMachineTypeFor(const MachineType& mt, const Type& type) {
if (type.IsNone()) return true;
// TODO(nicohartmann@): Add more cases here.
if (type.Is(Type::BigInt())) {
if (mt.representation() == MachineRepresentation::kWord64) {
return type.Is(Type::SignedBigInt64()) ||
type.Is(Type::UnsignedBigInt64());
}
return mt.representation() == MachineRepresentation::kTaggedPointer ||
mt.representation() == MachineRepresentation::kTagged;
}

View File

@ -223,6 +223,11 @@ bool IsSomePositiveOrderedNumber(Type type) {
return type.Is(Type::OrderedNumber()) && (type.IsNone() || type.Min() > 0);
}
inline bool IsLargeBigInt(Type type) {
return type.Is(Type::BigInt()) && !type.Is(Type::SignedBigInt64()) &&
!type.Is(Type::UnsignedBigInt64());
}
class JSONGraphWriterWithVerifierTypes : public JSONGraphWriter {
public:
JSONGraphWriterWithVerifierTypes(std::ostream& os, const Graph* graph,
@ -1307,6 +1312,14 @@ class RepresentationSelector {
return MachineType::AnyTagged();
}
if (rep == MachineRepresentation::kWord64) {
if (type.Is(Type::SignedBigInt64())) {
return MachineType::SignedBigInt64();
}
if (type.Is(Type::UnsignedBigInt64())) {
return MachineType::UnsignedBigInt64();
}
if (type.Is(Type::BigInt())) {
return MachineType::AnyTagged();
}
@ -1328,13 +1341,11 @@ class RepresentationSelector {
void VisitStateValues(Node* node) {
if (propagate<T>()) {
for (int i = 0; i < node->InputCount(); i++) {
// When lowering 64 bit BigInts to Word64 representation, we have to
// make sure they are rematerialized before deoptimization. By
// propagating a AnyTagged use, the RepresentationChanger is going to
// insert the necessary conversions.
// TODO(nicohartmann): Remove, once the deoptimizer can rematerialize
// truncated BigInts.
if (TypeOf(node->InputAt(i)).Is(Type::BigInt())) {
// BigInt64s are rematerialized in deoptimization. The other BigInts
// must be rematerialized before deoptimization. By propagating an
// AnyTagged use, the RepresentationChanger is going to insert the
// necessary conversions.
if (IsLargeBigInt(TypeOf(node->InputAt(i)))) {
EnqueueInput<T>(node, i, UseInfo::AnyTagged());
} else {
EnqueueInput<T>(node, i, UseInfo::Any());
@ -1346,9 +1357,7 @@ class RepresentationSelector {
zone->New<ZoneVector<MachineType>>(node->InputCount(), zone);
for (int i = 0; i < node->InputCount(); i++) {
Node* input = node->InputAt(i);
// TODO(nicohartmann): Remove, once the deoptimizer can rematerialize
// truncated BigInts.
if (TypeOf(input).Is(Type::BigInt())) {
if (IsLargeBigInt(TypeOf(input))) {
ConvertInput(node, i, UseInfo::AnyTagged());
}
@ -1377,9 +1386,7 @@ class RepresentationSelector {
// state-values node).
Node* accumulator = node.stack();
if (propagate<T>()) {
// TODO(nicohartmann): Remove, once the deoptimizer can rematerialize
// truncated BigInts.
if (TypeOf(accumulator).Is(Type::BigInt())) {
if (IsLargeBigInt(TypeOf(accumulator))) {
EnqueueInput<T>(node, FrameState::kFrameStateStackInput,
UseInfo::AnyTagged());
} else {
@ -1387,9 +1394,7 @@ class RepresentationSelector {
UseInfo::Any());
}
} else if (lower<T>()) {
// TODO(nicohartmann): Remove, once the deoptimizer can rematerialize
// truncated BigInts.
if (TypeOf(accumulator).Is(Type::BigInt())) {
if (IsLargeBigInt(TypeOf(accumulator))) {
ConvertInput(node, FrameState::kFrameStateStackInput,
UseInfo::AnyTagged());
}
@ -1424,9 +1429,7 @@ class RepresentationSelector {
void VisitObjectState(Node* node) {
if (propagate<T>()) {
for (int i = 0; i < node->InputCount(); i++) {
// TODO(nicohartmann): Remove, once the deoptimizer can rematerialize
// truncated BigInts.
if (TypeOf(node->InputAt(i)).Is(Type::BigInt())) {
if (IsLargeBigInt(TypeOf(node->InputAt(i)))) {
EnqueueInput<T>(node, i, UseInfo::AnyTagged());
} else {
EnqueueInput<T>(node, i, UseInfo::Any());
@ -1440,9 +1443,7 @@ class RepresentationSelector {
Node* input = node->InputAt(i);
(*types)[i] =
DeoptMachineTypeOf(GetInfo(input)->representation(), TypeOf(input));
// TODO(nicohartmann): Remove, once the deoptimizer can rematerialize
// truncated BigInts.
if (TypeOf(node->InputAt(i)).Is(Type::BigInt())) {
if (IsLargeBigInt(TypeOf(input))) {
ConvertInput(node, i, UseInfo::AnyTagged());
}
}
@ -1981,7 +1982,7 @@ class RepresentationSelector {
case wasm::kI32:
return MachineType::Int32();
case wasm::kI64:
return MachineType::Int64();
return MachineType::SignedBigInt64();
case wasm::kF32:
return MachineType::Float32();
case wasm::kF64:
@ -2979,11 +2980,14 @@ class RepresentationSelector {
is_asuintn ? Type::UnsignedBigInt64() : Type::SignedBigInt64());
if (lower<T>()) {
if (p.bits() == 0) {
DeferReplacement(
node, InsertTypeOverrideForVerifier(Type::UnsignedBigInt63(),
jsgraph_->ZeroConstant()));
DeferReplacement(node, InsertTypeOverrideForVerifier(
Type::UnsignedBigInt63(),
jsgraph_->Int64Constant(0)));
} else if (p.bits() == 64) {
DeferReplacement(node, node->InputAt(0));
DeferReplacement(node, InsertTypeOverrideForVerifier(
is_asuintn ? Type::UnsignedBigInt64()
: Type::SignedBigInt64(),
node->InputAt(0)));
} else {
if (is_asuintn) {
const uint64_t mask = (1ULL << p.bits()) - 1ULL;

View File

@ -145,6 +145,22 @@ void TranslationArrayPrintSingleFrame(
break;
}
case TranslationOpcode::SIGNED_BIGINT64_REGISTER: {
DCHECK_EQ(TranslationOpcodeOperandCount(opcode), 1);
int reg_code = iterator.NextUnsigned();
os << "{input=" << converter.NameOfCPURegister(reg_code)
<< " (signed bigint64)}";
break;
}
case TranslationOpcode::UNSIGNED_BIGINT64_REGISTER: {
DCHECK_EQ(TranslationOpcodeOperandCount(opcode), 1);
int reg_code = iterator.NextUnsigned();
os << "{input=" << converter.NameOfCPURegister(reg_code)
<< " (unsigned bigint64)}";
break;
}
case TranslationOpcode::UINT32_REGISTER: {
DCHECK_EQ(TranslationOpcodeOperandCount(opcode), 1);
int reg_code = iterator.NextUnsigned();
@ -195,6 +211,20 @@ void TranslationArrayPrintSingleFrame(
break;
}
case TranslationOpcode::SIGNED_BIGINT64_STACK_SLOT: {
DCHECK_EQ(TranslationOpcodeOperandCount(opcode), 1);
int input_slot_index = iterator.Next();
os << "{input=" << input_slot_index << " (signed bigint64)}";
break;
}
case TranslationOpcode::UNSIGNED_BIGINT64_STACK_SLOT: {
DCHECK_EQ(TranslationOpcodeOperandCount(opcode), 1);
int input_slot_index = iterator.Next();
os << "{input=" << input_slot_index << " (unsigned bigint64)}";
break;
}
case TranslationOpcode::UINT32_STACK_SLOT: {
DCHECK_EQ(TranslationOpcodeOperandCount(opcode), 1);
int input_slot_index = iterator.Next();
@ -329,6 +359,14 @@ TranslatedValue TranslatedValue::NewInt64ToBigInt(TranslatedState* container,
return slot;
}
// static
TranslatedValue TranslatedValue::NewUint64ToBigInt(TranslatedState* container,
uint64_t value) {
TranslatedValue slot(container, kUint64ToBigInt);
slot.uint64_value_ = value;
return slot;
}
// static
TranslatedValue TranslatedValue::NewUInt32(TranslatedState* container,
uint32_t value) {
@ -375,6 +413,11 @@ int64_t TranslatedValue::int64_value() const {
return int64_value_;
}
uint64_t TranslatedValue::uint64_value() const {
DCHECK(kUint64ToBigInt == kind());
return uint64_value_;
}
uint32_t TranslatedValue::uint32_value() const {
DCHECK(kind() == kUInt32 || kind() == kBoolBit);
return uint32_value_;
@ -547,6 +590,9 @@ Handle<Object> TranslatedValue::GetValue() {
case TranslatedValue::kInt64ToBigInt:
heap_object = BigInt::FromInt64(isolate(), int64_value());
break;
case TranslatedValue::kUint64ToBigInt:
heap_object = BigInt::FromUint64(isolate(), uint64_value());
break;
case TranslatedValue::kUInt32:
number = uint32_value();
heap_object = isolate()->factory()->NewHeapNumber(number);
@ -562,7 +608,8 @@ Handle<Object> TranslatedValue::GetValue() {
default:
UNREACHABLE();
}
DCHECK(!IsSmiDouble(number) || kind() == TranslatedValue::kInt64ToBigInt);
DCHECK(!IsSmiDouble(number) || kind() == TranslatedValue::kInt64ToBigInt ||
kind() == TranslatedValue::kUint64ToBigInt);
set_initialized_storage(heap_object);
return storage_;
}
@ -868,6 +915,8 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame(
case TranslationOpcode::REGISTER:
case TranslationOpcode::INT32_REGISTER:
case TranslationOpcode::INT64_REGISTER:
case TranslationOpcode::SIGNED_BIGINT64_REGISTER:
case TranslationOpcode::UNSIGNED_BIGINT64_REGISTER:
case TranslationOpcode::UINT32_REGISTER:
case TranslationOpcode::BOOL_REGISTER:
case TranslationOpcode::FLOAT_REGISTER:
@ -875,6 +924,8 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame(
case TranslationOpcode::STACK_SLOT:
case TranslationOpcode::INT32_STACK_SLOT:
case TranslationOpcode::INT64_STACK_SLOT:
case TranslationOpcode::SIGNED_BIGINT64_STACK_SLOT:
case TranslationOpcode::UNSIGNED_BIGINT64_STACK_SLOT:
case TranslationOpcode::UINT32_STACK_SLOT:
case TranslationOpcode::BOOL_STACK_SLOT:
case TranslationOpcode::FLOAT_STACK_SLOT:
@ -1089,6 +1140,42 @@ int TranslatedState::CreateNextTranslatedValue(
return translated_value.GetChildrenCount();
}
case TranslationOpcode::SIGNED_BIGINT64_REGISTER: {
int input_reg = iterator->NextUnsigned();
if (registers == nullptr) {
TranslatedValue translated_value = TranslatedValue::NewInvalid(this);
frame.Add(translated_value);
return translated_value.GetChildrenCount();
}
intptr_t value = registers->GetRegister(input_reg);
if (trace_file != nullptr) {
PrintF(trace_file, "%" V8PRIdPTR " ; %s (signed bigint64)", value,
converter.NameOfCPURegister(input_reg));
}
TranslatedValue translated_value =
TranslatedValue::NewInt64ToBigInt(this, value);
frame.Add(translated_value);
return translated_value.GetChildrenCount();
}
case TranslationOpcode::UNSIGNED_BIGINT64_REGISTER: {
int input_reg = iterator->NextUnsigned();
if (registers == nullptr) {
TranslatedValue translated_value = TranslatedValue::NewInvalid(this);
frame.Add(translated_value);
return translated_value.GetChildrenCount();
}
intptr_t value = registers->GetRegister(input_reg);
if (trace_file != nullptr) {
PrintF(trace_file, "%" V8PRIdPTR " ; %s (unsigned bigint64)", value,
converter.NameOfCPURegister(input_reg));
}
TranslatedValue translated_value =
TranslatedValue::NewUint64ToBigInt(this, value);
frame.Add(translated_value);
return translated_value.GetChildrenCount();
}
case TranslationOpcode::UINT32_REGISTER: {
int input_reg = iterator->NextUnsigned();
if (registers == nullptr) {
@ -1205,6 +1292,36 @@ int TranslatedState::CreateNextTranslatedValue(
return translated_value.GetChildrenCount();
}
case TranslationOpcode::SIGNED_BIGINT64_STACK_SLOT: {
int slot_offset =
OptimizedFrame::StackSlotOffsetRelativeToFp(iterator->Next());
uint64_t value = GetUInt64Slot(fp, slot_offset);
if (trace_file != nullptr) {
PrintF(trace_file, "%" V8PRIdPTR " ; (signed bigint64) [fp %c %3d] ",
static_cast<intptr_t>(value), slot_offset < 0 ? '-' : '+',
std::abs(slot_offset));
}
TranslatedValue translated_value =
TranslatedValue::NewInt64ToBigInt(this, value);
frame.Add(translated_value);
return translated_value.GetChildrenCount();
}
case TranslationOpcode::UNSIGNED_BIGINT64_STACK_SLOT: {
int slot_offset =
OptimizedFrame::StackSlotOffsetRelativeToFp(iterator->Next());
uint64_t value = GetUInt64Slot(fp, slot_offset);
if (trace_file != nullptr) {
PrintF(trace_file, "%" V8PRIdPTR " ; (unsigned bigint64) [fp %c %3d] ",
static_cast<intptr_t>(value), slot_offset < 0 ? '-' : '+',
std::abs(slot_offset));
}
TranslatedValue translated_value =
TranslatedValue::NewUint64ToBigInt(this, value);
frame.Add(translated_value);
return translated_value.GetChildrenCount();
}
case TranslationOpcode::UINT32_STACK_SLOT: {
int slot_offset =
OptimizedFrame::StackSlotOffsetRelativeToFp(iterator->Next());

View File

@ -73,6 +73,7 @@ class TranslatedValue {
kInt32,
kInt64,
kInt64ToBigInt,
kUint64ToBigInt,
kUInt32,
kBoolBit,
kFloat,
@ -111,6 +112,8 @@ class TranslatedValue {
static TranslatedValue NewInt64(TranslatedState* container, int64_t value);
static TranslatedValue NewInt64ToBigInt(TranslatedState* container,
int64_t value);
static TranslatedValue NewUint64ToBigInt(TranslatedState* container,
uint64_t value);
static TranslatedValue NewUInt32(TranslatedState* container, uint32_t value);
static TranslatedValue NewBool(TranslatedState* container, uint32_t value);
static TranslatedValue NewTagged(TranslatedState* container, Object literal);
@ -152,7 +155,9 @@ class TranslatedValue {
uint32_t uint32_value_;
// kind is kInt32.
int32_t int32_value_;
// kind is kInt64.
// kind is kUint64ToBigInt.
uint64_t uint64_value_;
// kind is kInt64 or kInt64ToBigInt.
int64_t int64_value_;
// kind is kFloat
Float32 float_value_;
@ -167,6 +172,7 @@ class TranslatedValue {
int32_t int32_value() const;
int64_t int64_value() const;
uint32_t uint32_value() const;
uint64_t uint64_value() const;
Float32 float_value() const;
Float64 double_value() const;
int object_length() const;

View File

@ -287,6 +287,20 @@ void TranslationArrayBuilder::StoreInt64Register(Register reg) {
DCHECK_EQ(TranslationOpcodeOperandCount(opcode), 1);
}
void TranslationArrayBuilder::StoreSignedBigInt64Register(Register reg) {
auto opcode = TranslationOpcode::SIGNED_BIGINT64_REGISTER;
AddOpcode(opcode);
AddRegister(reg);
DCHECK_EQ(TranslationOpcodeOperandCount(opcode), 1);
}
void TranslationArrayBuilder::StoreUnsignedBigInt64Register(Register reg) {
auto opcode = TranslationOpcode::UNSIGNED_BIGINT64_REGISTER;
AddOpcode(opcode);
AddRegister(reg);
DCHECK_EQ(TranslationOpcodeOperandCount(opcode), 1);
}
void TranslationArrayBuilder::StoreUint32Register(Register reg) {
auto opcode = TranslationOpcode::UINT32_REGISTER;
AddOpcode(opcode);
@ -334,6 +348,20 @@ void TranslationArrayBuilder::StoreInt64StackSlot(int index) {
DCHECK_EQ(TranslationOpcodeOperandCount(opcode), 1);
}
void TranslationArrayBuilder::StoreSignedBigInt64StackSlot(int index) {
auto opcode = TranslationOpcode::SIGNED_BIGINT64_STACK_SLOT;
AddOpcode(opcode);
Add(index);
DCHECK_EQ(TranslationOpcodeOperandCount(opcode), 1);
}
void TranslationArrayBuilder::StoreUnsignedBigInt64StackSlot(int index) {
auto opcode = TranslationOpcode::UNSIGNED_BIGINT64_STACK_SLOT;
AddOpcode(opcode);
Add(index);
DCHECK_EQ(TranslationOpcodeOperandCount(opcode), 1);
}
void TranslationArrayBuilder::StoreUint32StackSlot(int index) {
auto opcode = TranslationOpcode::UINT32_STACK_SLOT;
AddOpcode(opcode);

View File

@ -90,6 +90,8 @@ class TranslationArrayBuilder {
void StoreRegister(Register reg);
void StoreInt32Register(Register reg);
void StoreInt64Register(Register reg);
void StoreSignedBigInt64Register(Register reg);
void StoreUnsignedBigInt64Register(Register reg);
void StoreUint32Register(Register reg);
void StoreBoolRegister(Register reg);
void StoreFloatRegister(FloatRegister reg);
@ -97,6 +99,8 @@ class TranslationArrayBuilder {
void StoreStackSlot(int index);
void StoreInt32StackSlot(int index);
void StoreInt64StackSlot(int index);
void StoreSignedBigInt64StackSlot(int index);
void StoreUnsignedBigInt64StackSlot(int index);
void StoreUint32StackSlot(int index);
void StoreBoolStackSlot(int index);
void StoreFloatStackSlot(int index);

View File

@ -30,6 +30,10 @@ namespace internal {
V(INT32_STACK_SLOT, 1) \
V(INT64_REGISTER, 1) \
V(INT64_STACK_SLOT, 1) \
V(SIGNED_BIGINT64_REGISTER, 1) \
V(SIGNED_BIGINT64_STACK_SLOT, 1) \
V(UNSIGNED_BIGINT64_REGISTER, 1) \
V(UNSIGNED_BIGINT64_STACK_SLOT, 1) \
V(INTERPRETED_FRAME, 5) \
V(JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME, 3) \
V(JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME, 3) \

View File

@ -0,0 +1,156 @@
// 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 OptimizeAndTestAsUintN() {
function f(x) {
// Will be lowered to Int64Constant(-1) and stored as an immediate.
let y = BigInt.asUintN(64, -1n);
try {
return x + y;
} catch(_) {
return y;
}
}
%PrepareFunctionForOptimization(f);
assertEquals(2n ** 64n, f(1n));
assertEquals(2n ** 64n + 1n, f(2n));
%OptimizeFunctionOnNextCall(f);
assertEquals(2n ** 64n, f(1n));
assertOptimized(f);
// Should be rematerialized to 2n ** 64n - 1n in code generation.
assertEquals(2n ** 64n - 1n, f(0));
if (%Is64Bit()) {
assertUnoptimized(f);
}
})();
(function OptimizeAndTestAsUintN() {
function f(x) {
// Will be lowered to Int64Sub because exponentiation is not truncated and
// stored in a register.
let y = BigInt.asUintN(64, -(2n ** 0n));
try {
return x + y;
} catch(_) {
return y;
}
}
%PrepareFunctionForOptimization(f);
assertEquals(2n ** 64n, f(1n));
assertEquals(2n ** 64n + 1n, f(2n));
%OptimizeFunctionOnNextCall(f);
assertEquals(2n ** 64n, f(1n));
assertOptimized(f);
// Should be rematerialized to 2n ** 64n - 1n in deoptimization.
assertEquals(2n ** 64n - 1n, f(0));
if (%Is64Bit()) {
assertUnoptimized(f);
}
})();
(function OptimizeAndTestAsUintN() {
function f(x) {
// Will be lowered to Int64Sub because exponentiation is not truncated and
// stored in a stack slot.
let y = BigInt.asUintN(64, -(2n ** 0n));
try {
// The recursion is used to make sure `y` is stored on the stack.
return (x < 3n) ? (x + y) : f(x - 1n);
} catch(_) {
return y;
}
}
%PrepareFunctionForOptimization(f);
assertEquals(2n ** 64n, f(1n));
assertEquals(2n ** 64n + 1n, f(2n));
%OptimizeFunctionOnNextCall(f);
assertEquals(2n ** 64n, f(1n));
assertOptimized(f);
// Should be rematerialized to 2n ** 64n - 1n in deoptimization.
assertEquals(2n ** 64n - 1n, f(0));
if (%Is64Bit()) {
assertUnoptimized(f);
}
})();
(function OptimizeAndTestAsIntN() {
function f(x) {
// Will be lowered to Int64Constant(-1) and stored as an immediate.
let y = BigInt.asIntN(64, -1n);
try {
return x + y;
} catch (_) {
return y;
}
}
%PrepareFunctionForOptimization(f);
assertEquals(0n, f(1n));
assertEquals(1n, f(2n));
%OptimizeFunctionOnNextCall(f);
assertEquals(0n, f(1n));
assertOptimized(f);
// Should be rematerialized to -1n in code generation.
assertEquals(-1n, f(0));
if (%Is64Bit()) {
assertUnoptimized(f);
}
})();
(function OptimizeAndTestAsIntN() {
function f(x) {
// Will be lowered to Int64Sub because exponentiation is not truncated and
// stored in a register.
let y = BigInt.asIntN(64, -(2n ** 0n));
try {
return x + y;
} catch(_) {
return y;
}
}
%PrepareFunctionForOptimization(f);
assertEquals(0n, f(1n));
assertEquals(1n, f(2n));
%OptimizeFunctionOnNextCall(f);
assertEquals(0n, f(1n));
assertOptimized(f);
// Should be rematerialized to -1n in deoptimization.
assertEquals(-1n, f(0));
if (%Is64Bit()) {
assertUnoptimized(f);
}
})();
(function OptimizeAndTestAsIntN() {
function f(x) {
// Will be lowered to Int64Sub because exponentiation is not truncated and
// stored in a stack slot.
let y = BigInt.asIntN(64, -(2n ** 0n));
try {
// The recursion is used to make sure `y` is stored on the stack.
return (x < 3n) ? (x + y) : f(x - 1n);
} catch(_) {
return y;
}
}
%PrepareFunctionForOptimization(f);
assertEquals(0n, f(1n));
assertEquals(1n, f(2n));
%OptimizeFunctionOnNextCall(f);
assertEquals(0n, f(1n));
assertOptimized(f);
// Should be rematerialized to -1n in deoptimization.
assertEquals(-1n, f(0));
if (%Is64Bit()) {
assertUnoptimized(f);
}
})();

View File

@ -0,0 +1,15 @@
// 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
function f() {
return "abcd".charCodeAt(BigInt.asUintN(0, -1307n));
}
%PrepareFunctionForOptimization(f);
try { f(); } catch(e) {}
try { f(); } catch(e) {}
%OptimizeFunctionOnNextCall(f);
assertThrows(f, TypeError);

View File

@ -0,0 +1,22 @@
// 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
function foo(deopt, x) {
x = x >>> 0;
return deopt ? Math.max(x) : x;
}
function bar(deopt) {
return foo(deopt, 4294967295);
};
%PrepareFunctionForOptimization(bar);
%PrepareFunctionForOptimization(foo);
bar(false);
%OptimizeFunctionOnNextCall(bar);
// bar will bailout because of insufficient type feedback for generic named
// access. The HeapNumber should be correctly rematerialized in deoptimzer.
assertEquals(4294967295, bar(true));

View File

@ -0,0 +1,43 @@
// 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 --turbo-inline-js-wasm-calls
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let builder = new WasmModuleBuilder();
builder
.addFunction("f", kSig_l_v) // () -> i64
.addBody([
kExprI64Const, 0,
kExprI64Const, 1,
kExprI64Sub, // -1
])
.exportFunc();
let module = builder.instantiate();
function f(x) {
let y = module.exports.f();
try {
return x + y;
} catch(_) {
return y;
}
}
%PrepareFunctionForOptimization(f);
assertEquals(0n, f(1n));
assertEquals(1n, f(2n));
%OptimizeFunctionOnNextCall(f);
assertEquals(0n, f(1n));
assertOptimized(f);
// After optimization, the result of the js wasm call is stored in word64 and
// passed to StateValues without conversion. Rematerialization will happen
// in deoptimizer.
assertEquals(-1n, f(0));
if (%Is64Bit()) {
assertUnoptimized(f);
}