[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}
This commit is contained in:
Qifan Pan 2022-09-08 12:31:03 +02:00 committed by V8 LUCI CQ
parent 8199d70fd6
commit 80fb281561
13 changed files with 474 additions and 39 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
};
@ -193,6 +195,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 =
@ -1246,16 +1256,25 @@ void CodeGenerator::AddTranslationForOperand(Instruction* instr,
DCHECK(type.representation() != MachineRepresentation::kNone ||
constant.ToInt32() == FrameStateDescriptor::kImpossibleValue);
if (type == MachineType::Uint32()) {
literal = DeoptimizationLiteral(
static_cast<uint32_t>(constant.ToInt32()));
literal =
DeoptimizationLiteral(static_cast<double>(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:
@ -2983,7 +2984,10 @@ class RepresentationSelector {
node, InsertTypeOverrideForVerifier(Type::UnsignedBigInt63(),
jsgraph_->ZeroConstant()));
} 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,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);
}