%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/maglev/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/maglev/maglev-ir.cc |
// 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.
#include "src/maglev/maglev-ir.h"
#include <limits>
#include "src/base/bounds.h"
#include "src/builtins/builtins-constructor.h"
#include "src/codegen/code-factory.h"
#include "src/codegen/interface-descriptors-inl.h"
#include "src/codegen/interface-descriptors.h"
#include "src/common/globals.h"
#include "src/compiler/heap-refs.h"
#include "src/deoptimizer/deoptimize-reason.h"
#include "src/execution/isolate-inl.h"
#include "src/heap/local-heap.h"
#include "src/heap/parked-scope.h"
#include "src/interpreter/bytecode-flags.h"
#include "src/maglev/maglev-assembler-inl.h"
#include "src/maglev/maglev-assembler.h"
#include "src/maglev/maglev-code-gen-state.h"
#include "src/maglev/maglev-compilation-unit.h"
#include "src/maglev/maglev-graph-labeller.h"
#include "src/maglev/maglev-graph-processor.h"
#include "src/maglev/maglev-ir-inl.h"
#include "src/roots/roots.h"
namespace v8 {
namespace internal {
namespace maglev {
#define __ masm->
const char* OpcodeToString(Opcode opcode) {
#define DEF_NAME(Name) #Name,
static constexpr const char* const names[] = {NODE_BASE_LIST(DEF_NAME)};
#undef DEF_NAME
return names[static_cast<int>(opcode)];
}
BasicBlock* Phi::predecessor_at(int i) {
return merge_state_->predecessor_at(i);
}
namespace {
// Prevent people from accidentally using kScratchRegister here and having their
// code break in arm64.
struct Do_not_use_kScratchRegister_in_arch_independent_code {
} kScratchRegister;
struct Do_not_use_kScratchDoubleRegister_in_arch_independent_code {
} kScratchDoubleRegister;
static_assert(!std::is_same_v<decltype(kScratchRegister), Register>);
static_assert(
!std::is_same_v<decltype(kScratchDoubleRegister), DoubleRegister>);
} // namespace
#ifdef DEBUG
namespace {
template <size_t InputCount, typename Base, typename Derived>
int StaticInputCount(FixedInputNodeTMixin<InputCount, Base, Derived>*) {
return InputCount;
}
int StaticInputCount(NodeBase*) { UNREACHABLE(); }
} // namespace
void NodeBase::CheckCanOverwriteWith(Opcode new_opcode,
OpProperties new_properties) {
DCHECK_IMPLIES(new_properties.can_eager_deopt(),
properties().can_eager_deopt());
DCHECK_IMPLIES(new_properties.can_lazy_deopt(),
properties().can_lazy_deopt());
DCHECK_IMPLIES(new_properties.needs_register_snapshot(),
properties().needs_register_snapshot());
int old_input_count = input_count();
size_t old_sizeof = -1;
switch (opcode()) {
#define CASE(op) \
case Opcode::k##op: \
old_sizeof = sizeof(op); \
break;
NODE_BASE_LIST(CASE);
#undef CASE
}
switch (new_opcode) {
#define CASE(op) \
case Opcode::k##op: { \
DCHECK_EQ(old_input_count, StaticInputCount(static_cast<op*>(this))); \
DCHECK_EQ(sizeof(op), old_sizeof); \
break; \
}
NODE_BASE_LIST(CASE)
#undef CASE
}
}
#endif // DEBUG
bool Phi::is_loop_phi() const { return merge_state()->is_loop(); }
void Phi::RecordUseReprHint(UseRepresentationSet repr_mask,
int current_offset) {
if (is_loop_phi() && merge_state()->loop_info()->Contains(current_offset)) {
same_loop_uses_repr_hint_.Add(repr_mask);
}
if (!repr_mask.is_subset_of(uses_repr_hint_)) {
uses_repr_hint_.Add(repr_mask);
// Propagate in inputs, ignoring unbounded loop backedges.
int bound_inputs = input_count();
if (merge_state()->is_unmerged_loop()) --bound_inputs;
for (int i = 0; i < bound_inputs; i++) {
if (Phi* phi_input = input(i).node()->TryCast<Phi>()) {
phi_input->RecordUseReprHint(repr_mask, current_offset);
}
}
}
}
namespace {
// ---
// Print
// ---
void PrintInputs(std::ostream& os, MaglevGraphLabeller* graph_labeller,
const NodeBase* node) {
if (!node->has_inputs()) return;
os << " [";
for (int i = 0; i < node->input_count(); i++) {
if (i != 0) os << ", ";
graph_labeller->PrintInput(os, node->input(i));
}
os << "]";
}
void PrintResult(std::ostream& os, MaglevGraphLabeller* graph_labeller,
const NodeBase* node) {}
void PrintResult(std::ostream& os, MaglevGraphLabeller* graph_labeller,
const ValueNode* node) {
os << " → " << node->result().operand();
if (node->result().operand().IsAllocated() && node->is_spilled() &&
node->spill_slot() != node->result().operand()) {
os << " (spilled: " << node->spill_slot() << ")";
}
if (node->has_valid_live_range()) {
os << ", live range: [" << node->live_range().start << "-"
<< node->live_range().end << "]";
}
if (!node->has_id()) {
os << ", " << node->use_count() << " uses";
}
}
void PrintTargets(std::ostream& os, MaglevGraphLabeller* graph_labeller,
const NodeBase* node) {}
void PrintTargets(std::ostream& os, MaglevGraphLabeller* graph_labeller,
const UnconditionalControlNode* node) {
os << " b" << graph_labeller->BlockId(node->target());
}
void PrintTargets(std::ostream& os, MaglevGraphLabeller* graph_labeller,
const BranchControlNode* node) {
os << " b" << graph_labeller->BlockId(node->if_true()) << " b"
<< graph_labeller->BlockId(node->if_false());
}
void PrintTargets(std::ostream& os, MaglevGraphLabeller* graph_labeller,
const Switch* node) {
for (int i = 0; i < node->size(); i++) {
const BasicBlockRef& target = node->Cast<Switch>()->targets()[i];
os << " b" << graph_labeller->BlockId(target.block_ptr());
}
if (node->Cast<Switch>()->has_fallthrough()) {
BasicBlock* fallthrough_target = node->Cast<Switch>()->fallthrough();
os << " b" << graph_labeller->BlockId(fallthrough_target);
}
}
class MaybeUnparkForPrint {
public:
MaybeUnparkForPrint() {
LocalHeap* local_heap = LocalHeap::Current();
if (!local_heap) {
local_heap = Isolate::Current()->main_thread_local_heap();
}
DCHECK_NOT_NULL(local_heap);
if (local_heap->IsParked()) {
scope_.emplace(local_heap);
}
}
private:
base::Optional<UnparkedScope> scope_;
};
template <typename NodeT>
void PrintImpl(std::ostream& os, MaglevGraphLabeller* graph_labeller,
const NodeT* node, bool skip_targets) {
MaybeUnparkForPrint unpark;
os << node->opcode();
node->PrintParams(os, graph_labeller);
PrintInputs(os, graph_labeller, node);
PrintResult(os, graph_labeller, node);
if (!skip_targets) {
PrintTargets(os, graph_labeller, node);
}
}
size_t GetInputLocationsArraySize(const DeoptFrame& top_frame) {
static constexpr int kClosureSize = 1;
static constexpr int kReceiverSize = 1;
static constexpr int kContextSize = 1;
size_t size = 0;
const DeoptFrame* frame = &top_frame;
do {
switch (frame->type()) {
case DeoptFrame::FrameType::kInterpretedFrame:
size += kClosureSize + frame->as_interpreted().frame_state()->size(
frame->as_interpreted().unit());
break;
case DeoptFrame::FrameType::kInlinedArgumentsFrame:
size += kClosureSize + frame->as_inlined_arguments().arguments().size();
break;
case DeoptFrame::FrameType::kConstructInvokeStubFrame:
size += kReceiverSize + kContextSize;
break;
case DeoptFrame::FrameType::kBuiltinContinuationFrame:
size +=
frame->as_builtin_continuation().parameters().size() + kContextSize;
break;
}
frame = frame->parent();
} while (frame != nullptr);
return size;
}
bool RootToBoolean(RootIndex index) {
switch (index) {
case RootIndex::kFalseValue:
case RootIndex::kNullValue:
case RootIndex::kUndefinedValue:
case RootIndex::kNanValue:
case RootIndex::kHoleNanValue:
case RootIndex::kMinusZeroValue:
case RootIndex::kempty_string:
#ifdef V8_ENABLE_WEBASSEMBLY
case RootIndex::kWasmNull:
#endif
return false;
default:
return true;
}
}
#ifdef DEBUG
// For all RO roots, check that RootToBoolean returns the same value as
// BooleanValue on that root.
bool CheckToBooleanOnAllRoots(LocalIsolate* local_isolate) {
ReadOnlyRoots roots(local_isolate);
// Use the READ_ONLY_ROOT_LIST macro list rather than a for loop to get nicer
// error messages if there is a failure.
#define DO_CHECK(type, name, CamelName) \
/* Ignore 'undefined' roots that are not the undefined value itself. */ \
if (roots.name() != roots.undefined_value() || \
RootIndex::k##CamelName == RootIndex::kUndefinedValue) { \
DCHECK_EQ(Object::BooleanValue(roots.name(), local_isolate), \
RootToBoolean(RootIndex::k##CamelName)); \
}
READ_ONLY_ROOT_LIST(DO_CHECK)
#undef DO_CHECK
return true;
}
#endif
} // namespace
bool RootConstant::ToBoolean(LocalIsolate* local_isolate) const {
#ifdef DEBUG
// (Ab)use static locals to call CheckToBooleanOnAllRoots once, on first
// call to this function.
static bool check_once = CheckToBooleanOnAllRoots(local_isolate);
DCHECK(check_once);
#endif
// ToBoolean is only supported for RO roots.
DCHECK(RootsTable::IsReadOnly(index_));
return RootToBoolean(index_);
}
bool FromConstantToBool(LocalIsolate* local_isolate, ValueNode* node) {
DCHECK(IsConstantNode(node->opcode()));
switch (node->opcode()) {
#define CASE(Name) \
case Opcode::k##Name: { \
return node->Cast<Name>()->ToBoolean(local_isolate); \
}
CONSTANT_VALUE_NODE_LIST(CASE)
#undef CASE
default:
UNREACHABLE();
}
}
bool FromConstantToBool(MaglevAssembler* masm, ValueNode* node) {
// TODO(leszeks): Getting the main thread local isolate is not what we
// actually want here, but it's all we have, and it happens to work because
// really all we're using it for is ReadOnlyRoots. We should change ToBoolean
// to be able to pass ReadOnlyRoots in directly.
return FromConstantToBool(masm->isolate()->AsLocalIsolate(), node);
}
DeoptInfo::DeoptInfo(Zone* zone, const DeoptFrame top_frame,
compiler::FeedbackSource feedback_to_update)
: top_frame_(top_frame),
feedback_to_update_(feedback_to_update),
input_locations_(zone->AllocateArray<InputLocation>(
GetInputLocationsArraySize(top_frame))) {
// Initialise InputLocations so that they correctly don't have a next use id.
for (size_t i = 0; i < GetInputLocationsArraySize(top_frame); ++i) {
new (&input_locations_[i]) InputLocation();
}
}
bool LazyDeoptInfo::IsResultRegister(interpreter::Register reg) const {
if (top_frame().type() == DeoptFrame::FrameType::kConstructInvokeStubFrame) {
return reg == interpreter::Register::virtual_accumulator();
}
if (V8_LIKELY(result_size() == 1)) {
return reg == result_location_;
}
if (result_size() == 0) {
return false;
}
DCHECK_EQ(result_size(), 2);
return reg == result_location_ ||
reg == interpreter::Register(result_location_.index() + 1);
}
void NodeBase::Print(std::ostream& os, MaglevGraphLabeller* graph_labeller,
bool skip_targets) const {
switch (opcode()) {
#define V(Name) \
case Opcode::k##Name: \
return PrintImpl(os, graph_labeller, this->Cast<Name>(), skip_targets);
NODE_BASE_LIST(V)
#undef V
}
UNREACHABLE();
}
void NodeBase::Print() const {
MaglevGraphLabeller labeller;
Print(std::cout, &labeller);
std::cout << std::endl;
}
void ValueNode::SetHint(compiler::InstructionOperand hint) {
if (!hint_.IsInvalid()) return;
hint_ = hint;
if (result_.operand().IsUnallocated()) {
auto operand = compiler::UnallocatedOperand::cast(result_.operand());
if (operand.HasSameAsInputPolicy()) {
input(operand.input_index()).node()->SetHint(hint);
}
}
if (this->Is<Phi>()) {
for (Input& input : *this) {
if (input.node()->has_id() && input.node()->id() < this->id()) {
input.node()->SetHint(hint);
}
}
}
}
void ValueNode::SetNoSpill() {
DCHECK(!IsConstantNode(opcode()));
#ifdef DEBUG
state_ = kSpill;
#endif // DEBUG
spill_ = compiler::InstructionOperand();
}
void ValueNode::SetConstantLocation() {
DCHECK(IsConstantNode(opcode()));
#ifdef DEBUG
state_ = kSpill;
#endif // DEBUG
spill_ = compiler::ConstantOperand(
compiler::UnallocatedOperand::cast(result().operand())
.virtual_register());
}
// ---
// Check input value representation
// ---
ValueRepresentation ToValueRepresentation(MachineType type) {
switch (type.representation()) {
case MachineRepresentation::kTagged:
case MachineRepresentation::kTaggedSigned:
case MachineRepresentation::kTaggedPointer:
return ValueRepresentation::kTagged;
case MachineRepresentation::kFloat64:
return ValueRepresentation::kFloat64;
case MachineRepresentation::kWord64:
return ValueRepresentation::kWord64;
default:
return ValueRepresentation::kInt32;
}
}
void CheckValueInputIs(const NodeBase* node, int i,
ValueRepresentation expected,
MaglevGraphLabeller* graph_labeller) {
ValueNode* input = node->input(i).node();
DCHECK(!input->Is<Identity>());
ValueRepresentation got = input->properties().value_representation();
// Allow Float64 values to be inputs when HoleyFloat64 is expected.
bool valid =
(got == expected) || (got == ValueRepresentation::kFloat64 &&
expected == ValueRepresentation::kHoleyFloat64);
if (!valid) {
std::ostringstream str;
str << "Type representation error: node ";
if (graph_labeller) {
str << "#" << graph_labeller->NodeId(node) << " : ";
}
str << node->opcode() << " (input @" << i << " = " << input->opcode()
<< ") type " << got << " is not " << expected;
FATAL("%s", str.str().c_str());
}
}
void CheckValueInputIs(const NodeBase* node, int i, Opcode expected,
MaglevGraphLabeller* graph_labeller) {
ValueNode* input = node->input(i).node();
Opcode got = input->opcode();
if (got != expected) {
std::ostringstream str;
str << "Opcode error: node ";
if (graph_labeller) {
str << "#" << graph_labeller->NodeId(node) << " : ";
}
str << node->opcode() << " (input @" << i << " = " << input->opcode()
<< ") opcode " << got << " is not " << expected;
FATAL("%s", str.str().c_str());
}
}
void CheckValueInputIsWord32(const NodeBase* node, int i,
MaglevGraphLabeller* graph_labeller) {
ValueNode* input = node->input(i).node();
DCHECK(!input->Is<Identity>());
ValueRepresentation got = input->properties().value_representation();
if (got != ValueRepresentation::kInt32 &&
got != ValueRepresentation::kUint32) {
std::ostringstream str;
str << "Type representation error: node ";
if (graph_labeller) {
str << "#" << graph_labeller->NodeId(node) << " : ";
}
str << node->opcode() << " (input @" << i << " = " << input->opcode()
<< ") type " << got << " is not Word32 (Int32 or Uint32)";
FATAL("%s", str.str().c_str());
}
}
void GeneratorStore::VerifyInputs(MaglevGraphLabeller* graph_labeller) const {
for (int i = 0; i < input_count(); i++) {
CheckValueInputIs(this, i, ValueRepresentation::kTagged, graph_labeller);
}
}
void UnsafeSmiTag::VerifyInputs(MaglevGraphLabeller* graph_labeller) const {
DCHECK_EQ(input_count(), 1);
CheckValueInputIsWord32(this, 0, graph_labeller);
}
void Phi::VerifyInputs(MaglevGraphLabeller* graph_labeller) const {
switch (value_representation()) {
#define CASE_REPR(repr) \
case ValueRepresentation::k##repr: \
for (int i = 0; i < input_count(); i++) { \
CheckValueInputIs(this, i, ValueRepresentation::k##repr, \
graph_labeller); \
} \
break;
CASE_REPR(Tagged)
CASE_REPR(Int32)
CASE_REPR(Uint32)
CASE_REPR(Float64)
CASE_REPR(HoleyFloat64)
#undef CASE_REPR
case ValueRepresentation::kWord64:
UNREACHABLE();
}
}
void Call::VerifyInputs(MaglevGraphLabeller* graph_labeller) const {
for (int i = 0; i < input_count(); i++) {
CheckValueInputIs(this, i, ValueRepresentation::kTagged, graph_labeller);
}
}
#ifdef V8_COMPRESS_POINTERS
void Call::MarkTaggedInputsAsDecompressing() {
for (int i = 0; i < input_count(); i++) {
input(i).node()->SetTaggedResultNeedsDecompress();
}
}
#endif
void CallWithArrayLike::VerifyInputs(
MaglevGraphLabeller* graph_labeller) const {
for (int i = 0; i < input_count(); i++) {
CheckValueInputIs(this, i, ValueRepresentation::kTagged, graph_labeller);
}
}
#ifdef V8_COMPRESS_POINTERS
void CallWithArrayLike::MarkTaggedInputsAsDecompressing() {
for (int i = 0; i < input_count(); i++) {
input(i).node()->SetTaggedResultNeedsDecompress();
}
}
#endif
void CallWithSpread::VerifyInputs(MaglevGraphLabeller* graph_labeller) const {
for (int i = 0; i < input_count(); i++) {
CheckValueInputIs(this, i, ValueRepresentation::kTagged, graph_labeller);
}
}
#ifdef V8_COMPRESS_POINTERS
void CallWithSpread::MarkTaggedInputsAsDecompressing() {
for (int i = 0; i < input_count(); i++) {
input(i).node()->SetTaggedResultNeedsDecompress();
}
}
#endif
void CallSelf::VerifyInputs(MaglevGraphLabeller* graph_labeller) const {
for (int i = 0; i < input_count(); i++) {
CheckValueInputIs(this, i, ValueRepresentation::kTagged, graph_labeller);
}
}
#ifdef V8_COMPRESS_POINTERS
void CallSelf::MarkTaggedInputsAsDecompressing() {
for (int i = 0; i < input_count(); i++) {
input(i).node()->SetTaggedResultNeedsDecompress();
}
}
#endif
void CallKnownJSFunction::VerifyInputs(
MaglevGraphLabeller* graph_labeller) const {
for (int i = 0; i < input_count(); i++) {
CheckValueInputIs(this, i, ValueRepresentation::kTagged, graph_labeller);
}
}
#ifdef V8_COMPRESS_POINTERS
void CallKnownJSFunction::MarkTaggedInputsAsDecompressing() {
for (int i = 0; i < input_count(); i++) {
input(i).node()->SetTaggedResultNeedsDecompress();
}
}
#endif
void CallKnownApiFunction::VerifyInputs(
MaglevGraphLabeller* graph_labeller) const {
for (int i = 0; i < input_count(); i++) {
CheckValueInputIs(this, i, ValueRepresentation::kTagged, graph_labeller);
}
}
#ifdef V8_COMPRESS_POINTERS
void CallKnownApiFunction::MarkTaggedInputsAsDecompressing() {
for (int i = 0; i < input_count(); i++) {
input(i).node()->SetTaggedResultNeedsDecompress();
}
}
#endif
void Construct::VerifyInputs(MaglevGraphLabeller* graph_labeller) const {
for (int i = 0; i < input_count(); i++) {
CheckValueInputIs(this, i, ValueRepresentation::kTagged, graph_labeller);
}
}
#ifdef V8_COMPRESS_POINTERS
void Construct::MarkTaggedInputsAsDecompressing() {
for (int i = 0; i < input_count(); i++) {
input(i).node()->SetTaggedResultNeedsDecompress();
}
}
#endif
void ConstructWithSpread::VerifyInputs(
MaglevGraphLabeller* graph_labeller) const {
for (int i = 0; i < input_count(); i++) {
CheckValueInputIs(this, i, ValueRepresentation::kTagged, graph_labeller);
}
}
#ifdef V8_COMPRESS_POINTERS
void ConstructWithSpread::MarkTaggedInputsAsDecompressing() {
for (int i = 0; i < input_count(); i++) {
input(i).node()->SetTaggedResultNeedsDecompress();
}
}
#endif
void CallBuiltin::VerifyInputs(MaglevGraphLabeller* graph_labeller) const {
auto descriptor = Builtins::CallInterfaceDescriptorFor(builtin());
int count = input_count();
// Verify context.
if (descriptor.HasContextParameter()) {
CheckValueInputIs(this, count - 1, ValueRepresentation::kTagged,
graph_labeller);
count--;
}
// {all_input_count} includes the feedback slot and vector.
#ifdef DEBUG
int all_input_count = count + (has_feedback() ? 2 : 0);
if (descriptor.AllowVarArgs()) {
DCHECK_GE(all_input_count, descriptor.GetParameterCount());
} else {
DCHECK_EQ(all_input_count, descriptor.GetParameterCount());
}
#endif
int i = 0;
// Check the rest of inputs.
for (; i < count; ++i) {
MachineType type = i < descriptor.GetParameterCount()
? descriptor.GetParameterType(i)
: MachineType::AnyTagged();
CheckValueInputIs(this, i, ToValueRepresentation(type), graph_labeller);
}
}
#ifdef V8_COMPRESS_POINTERS
void CallBuiltin::MarkTaggedInputsAsDecompressing() {
auto descriptor = Builtins::CallInterfaceDescriptorFor(builtin());
int count = input_count();
// Set context.
if (descriptor.HasContextParameter()) {
input(count - 1).node()->SetTaggedResultNeedsDecompress();
count--;
}
int i = 0;
// Set the rest of the tagged inputs.
for (; i < count; ++i) {
MachineType type = i < descriptor.GetParameterCount()
? descriptor.GetParameterType(i)
: MachineType::AnyTagged();
if (type.IsTagged() && !type.IsTaggedSigned()) {
input(i).node()->SetTaggedResultNeedsDecompress();
}
}
}
#endif
void CallCPPBuiltin::VerifyInputs(MaglevGraphLabeller* graph_labeller) const {
for (int i = 0; i < input_count(); i++) {
CheckValueInputIs(this, i, ValueRepresentation::kTagged, graph_labeller);
}
}
#ifdef V8_COMPRESS_POINTERS
void CallCPPBuiltin::MarkTaggedInputsAsDecompressing() {
for (int i = 0; i < input_count(); i++) {
input(i).node()->SetTaggedResultNeedsDecompress();
}
}
#endif
void CallRuntime::VerifyInputs(MaglevGraphLabeller* graph_labeller) const {
for (int i = 0; i < input_count(); i++) {
CheckValueInputIs(this, i, ValueRepresentation::kTagged, graph_labeller);
}
}
#ifdef V8_COMPRESS_POINTERS
void CallRuntime::MarkTaggedInputsAsDecompressing() {
for (int i = 0; i < input_count(); i++) {
input(i).node()->SetTaggedResultNeedsDecompress();
}
}
#endif
void FoldedAllocation::VerifyInputs(MaglevGraphLabeller* graph_labeller) const {
Base::VerifyInputs(graph_labeller);
CheckValueInputIs(this, 0, Opcode::kAllocateRaw, graph_labeller);
}
// ---
// Reify constants
// ---
Handle<Object> ValueNode::Reify(LocalIsolate* isolate) const {
switch (opcode()) {
#define V(Name) \
case Opcode::k##Name: \
return this->Cast<Name>()->DoReify(isolate);
CONSTANT_VALUE_NODE_LIST(V)
#undef V
default:
UNREACHABLE();
}
}
Handle<Object> ExternalConstant::DoReify(LocalIsolate* isolate) const {
UNREACHABLE();
}
Handle<Object> SmiConstant::DoReify(LocalIsolate* isolate) const {
return handle(value_, isolate);
}
Handle<Object> TaggedIndexConstant::DoReify(LocalIsolate* isolate) const {
UNREACHABLE();
}
Handle<Object> Int32Constant::DoReify(LocalIsolate* isolate) const {
return isolate->factory()->NewNumber<AllocationType::kOld>(value());
}
Handle<Object> Float64Constant::DoReify(LocalIsolate* isolate) const {
return isolate->factory()->NewNumber<AllocationType::kOld>(
value_.get_scalar());
}
Handle<Object> Constant::DoReify(LocalIsolate* isolate) const {
return object_.object();
}
Handle<Object> RootConstant::DoReify(LocalIsolate* isolate) const {
return isolate->root_handle(index());
}
// ---
// Load node to registers
// ---
namespace {
template <typename NodeT>
void LoadToRegisterHelper(NodeT* node, MaglevAssembler* masm, Register reg) {
if constexpr (!IsDoubleRepresentation(
NodeT::kProperties.value_representation())) {
return node->DoLoadToRegister(masm, reg);
} else {
UNREACHABLE();
}
}
template <typename NodeT>
void LoadToRegisterHelper(NodeT* node, MaglevAssembler* masm,
DoubleRegister reg) {
if constexpr (IsDoubleRepresentation(
NodeT::kProperties.value_representation())) {
return node->DoLoadToRegister(masm, reg);
} else {
UNREACHABLE();
}
}
} // namespace
void ValueNode::LoadToRegister(MaglevAssembler* masm, Register reg) {
switch (opcode()) {
#define V(Name) \
case Opcode::k##Name: \
return LoadToRegisterHelper(this->Cast<Name>(), masm, reg);
VALUE_NODE_LIST(V)
#undef V
default:
UNREACHABLE();
}
}
void ValueNode::LoadToRegister(MaglevAssembler* masm, DoubleRegister reg) {
switch (opcode()) {
#define V(Name) \
case Opcode::k##Name: \
return LoadToRegisterHelper(this->Cast<Name>(), masm, reg);
VALUE_NODE_LIST(V)
#undef V
default:
UNREACHABLE();
}
}
void ValueNode::DoLoadToRegister(MaglevAssembler* masm, Register reg) {
DCHECK(is_spilled());
DCHECK(!use_double_register());
__ Move(reg,
masm->GetStackSlot(compiler::AllocatedOperand::cast(spill_slot())));
}
void ValueNode::DoLoadToRegister(MaglevAssembler* masm, DoubleRegister reg) {
DCHECK(is_spilled());
DCHECK(use_double_register());
__ LoadFloat64(
reg, masm->GetStackSlot(compiler::AllocatedOperand::cast(spill_slot())));
}
void ExternalConstant::DoLoadToRegister(MaglevAssembler* masm, Register reg) {
__ Move(reg, reference());
}
void SmiConstant::DoLoadToRegister(MaglevAssembler* masm, Register reg) {
__ Move(reg, value());
}
void TaggedIndexConstant::DoLoadToRegister(MaglevAssembler* masm,
Register reg) {
__ Move(reg, value());
}
void Int32Constant::DoLoadToRegister(MaglevAssembler* masm, Register reg) {
__ Move(reg, value());
}
void Float64Constant::DoLoadToRegister(MaglevAssembler* masm,
DoubleRegister reg) {
__ Move(reg, value());
}
void Constant::DoLoadToRegister(MaglevAssembler* masm, Register reg) {
__ Move(reg, object_.object());
}
void RootConstant::DoLoadToRegister(MaglevAssembler* masm, Register reg) {
__ LoadRoot(reg, index());
}
// ---
// Arch agnostic nodes
// ---
void ExternalConstant::SetValueLocationConstraints() { DefineAsConstant(this); }
void ExternalConstant::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {}
void SmiConstant::SetValueLocationConstraints() { DefineAsConstant(this); }
void SmiConstant::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {}
void TaggedIndexConstant::SetValueLocationConstraints() {
DefineAsConstant(this);
}
void TaggedIndexConstant::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {}
void Int32Constant::SetValueLocationConstraints() { DefineAsConstant(this); }
void Int32Constant::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {}
void Float64Constant::SetValueLocationConstraints() { DefineAsConstant(this); }
void Float64Constant::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {}
void Constant::SetValueLocationConstraints() { DefineAsConstant(this); }
void Constant::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {}
void RootConstant::SetValueLocationConstraints() { DefineAsConstant(this); }
void RootConstant::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {}
InitialValue::InitialValue(uint64_t bitfield, interpreter::Register source)
: Base(bitfield), source_(source) {}
void InitialValue::SetValueLocationConstraints() {
result().SetUnallocated(compiler::UnallocatedOperand::FIXED_SLOT,
stack_slot(), kNoVreg);
}
void InitialValue::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
// No-op, the value is already in the appropriate slot.
}
// static
uint32_t InitialValue::stack_slot(uint32_t register_index) {
// TODO(leszeks): Make this nicer.
return (StandardFrameConstants::kExpressionsOffset -
UnoptimizedFrameConstants::kRegisterFileFromFp) /
kSystemPointerSize +
register_index;
}
uint32_t InitialValue::stack_slot() const {
return stack_slot(source_.index());
}
int FunctionEntryStackCheck::MaxCallStackArgs() const { return 0; }
void FunctionEntryStackCheck::SetValueLocationConstraints() {
set_temporaries_needed(2);
// kReturnRegister0 should not be one of the available temporary registers.
RequireSpecificTemporary(kReturnRegister0);
}
void FunctionEntryStackCheck::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
// Stack check. This folds the checks for both the interrupt stack limit
// check and the real stack limit into one by just checking for the
// interrupt limit. The interrupt limit is either equal to the real
// stack limit or tighter. By ensuring we have space until that limit
// after building the frame we can quickly precheck both at once.
const int stack_check_offset = masm->code_gen_state()->stack_check_offset();
// Only NewTarget can be live at this point.
DCHECK_LE(register_snapshot().live_registers.Count(), 1);
Builtin builtin =
register_snapshot().live_tagged_registers.has(
kJavaScriptCallNewTargetRegister)
? Builtin::kMaglevFunctionEntryStackCheck_WithNewTarget
: Builtin::kMaglevFunctionEntryStackCheck_WithoutNewTarget;
ZoneLabelRef done(masm);
Condition cond = __ FunctionEntryStackCheck(stack_check_offset);
if (masm->isolate()->is_short_builtin_calls_enabled()) {
__ JumpIf(cond, *done, Label::kNear);
__ Move(kReturnRegister0, Smi::FromInt(stack_check_offset));
__ MacroAssembler::CallBuiltin(builtin);
masm->DefineLazyDeoptPoint(lazy_deopt_info());
} else {
__ JumpToDeferredIf(
NegateCondition(cond),
[](MaglevAssembler* masm, ZoneLabelRef done,
FunctionEntryStackCheck* node, Builtin builtin,
int stack_check_offset) {
__ Move(kReturnRegister0, Smi::FromInt(stack_check_offset));
__ MacroAssembler::CallBuiltin(builtin);
masm->DefineLazyDeoptPoint(node->lazy_deopt_info());
__ Jump(*done);
},
done, this, builtin, stack_check_offset);
}
__ bind(*done);
}
void RegisterInput::SetValueLocationConstraints() {
DefineAsFixed(this, input());
}
void RegisterInput::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
// Nothing to be done, the value is already in the register.
}
void GetSecondReturnedValue::SetValueLocationConstraints() {
DefineAsFixed(this, kReturnRegister1);
}
void GetSecondReturnedValue::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
// No-op. This is just a hack that binds kReturnRegister1 to a value node.
// kReturnRegister1 is guaranteed to be free in the register allocator, since
// previous node in the basic block is a call.
#ifdef DEBUG
// Check if the previous node is call.
Node* previous = nullptr;
for (Node* node : state.block()->nodes()) {
if (node == this) {
break;
}
previous = node;
}
DCHECK_NE(previous, nullptr);
DCHECK(previous->properties().is_call());
#endif // DEBUG
}
void Deopt::SetValueLocationConstraints() {}
void Deopt::GenerateCode(MaglevAssembler* masm, const ProcessingState& state) {
__ EmitEagerDeopt(this, reason());
}
void Phi::SetValueLocationConstraints() {
for (Input& input : *this) {
UseAny(input);
}
// We have to pass a policy for the result, but it is ignored during register
// allocation. See StraightForwardRegisterAllocator::AllocateRegisters which
// has special handling for Phis.
static const compiler::UnallocatedOperand::ExtendedPolicy kIgnoredPolicy =
compiler::UnallocatedOperand::REGISTER_OR_SLOT_OR_CONSTANT;
result().SetUnallocated(kIgnoredPolicy, kNoVreg);
}
void Phi::GenerateCode(MaglevAssembler* masm, const ProcessingState& state) {}
void Phi::SetUseRequires31BitValue() {
if (uses_require_31_bit_value_) return;
uses_require_31_bit_value_ = true;
auto inputs =
is_loop_phi() ? merge_state_->predecessors_so_far() : input_count();
for (int i = 0; i < inputs; ++i) {
ValueNode* input_node = input(i).node();
DCHECK(input_node);
if (auto phi = input_node->TryCast<Phi>()) {
phi->SetUseRequires31BitValue();
}
}
}
namespace {
constexpr Builtin BuiltinFor(Operation operation) {
switch (operation) {
#define CASE(name) \
case Operation::k##name: \
return Builtin::k##name##_WithFeedback;
OPERATION_LIST(CASE)
#undef CASE
}
}
} // namespace
template <class Derived, Operation kOperation>
void UnaryWithFeedbackNode<Derived, kOperation>::SetValueLocationConstraints() {
using D = UnaryOp_WithFeedbackDescriptor;
UseFixed(operand_input(), D::GetRegisterParameter(D::kValue));
DefineAsFixed(this, kReturnRegister0);
}
template <class Derived, Operation kOperation>
void UnaryWithFeedbackNode<Derived, kOperation>::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
__ CallBuiltin<BuiltinFor(kOperation)>(
masm->native_context().object(), // context
operand_input(), // value
feedback().index(), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
template <class Derived, Operation kOperation>
void BinaryWithFeedbackNode<Derived,
kOperation>::SetValueLocationConstraints() {
using D = BinaryOp_WithFeedbackDescriptor;
UseFixed(left_input(), D::GetRegisterParameter(D::kLeft));
UseFixed(right_input(), D::GetRegisterParameter(D::kRight));
DefineAsFixed(this, kReturnRegister0);
}
template <class Derived, Operation kOperation>
void BinaryWithFeedbackNode<Derived, kOperation>::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
__ CallBuiltin<BuiltinFor(kOperation)>(
masm->native_context().object(), // context
left_input(), // left
right_input(), // right
feedback().index(), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
#define DEF_OPERATION(Name) \
void Name::SetValueLocationConstraints() { \
Base::SetValueLocationConstraints(); \
} \
void Name::GenerateCode(MaglevAssembler* masm, \
const ProcessingState& state) { \
Base::GenerateCode(masm, state); \
}
GENERIC_OPERATIONS_NODE_LIST(DEF_OPERATION)
#undef DEF_OPERATION
void ConstantGapMove::SetValueLocationConstraints() { UNREACHABLE(); }
namespace {
template <typename T>
struct GetRegister;
template <>
struct GetRegister<Register> {
static Register Get(compiler::AllocatedOperand target) {
return target.GetRegister();
}
};
template <>
struct GetRegister<DoubleRegister> {
static DoubleRegister Get(compiler::AllocatedOperand target) {
return target.GetDoubleRegister();
}
};
} // namespace
void ConstantGapMove::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
switch (node_->opcode()) {
#define CASE(Name) \
case Opcode::k##Name: \
return node_->Cast<Name>()->DoLoadToRegister( \
masm, GetRegister<Name::OutputRegister>::Get(target()));
CONSTANT_VALUE_NODE_LIST(CASE)
#undef CASE
default:
UNREACHABLE();
}
}
void GapMove::SetValueLocationConstraints() { UNREACHABLE(); }
void GapMove::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
DCHECK_EQ(source().representation(), target().representation());
MachineRepresentation repr = source().representation();
if (source().IsRegister()) {
Register source_reg = ToRegister(source());
if (target().IsAnyRegister()) {
DCHECK(target().IsRegister());
__ MoveRepr(repr, ToRegister(target()), source_reg);
} else {
__ MoveRepr(repr, masm->ToMemOperand(target()), source_reg);
}
} else if (source().IsDoubleRegister()) {
DoubleRegister source_reg = ToDoubleRegister(source());
if (target().IsAnyRegister()) {
DCHECK(target().IsDoubleRegister());
__ Move(ToDoubleRegister(target()), source_reg);
} else {
__ StoreFloat64(masm->ToMemOperand(target()), source_reg);
}
} else {
DCHECK(source().IsAnyStackSlot());
MemOperand source_op = masm->ToMemOperand(source());
if (target().IsRegister()) {
__ MoveRepr(repr, ToRegister(target()), source_op);
} else if (target().IsDoubleRegister()) {
__ LoadFloat64(ToDoubleRegister(target()), source_op);
} else {
DCHECK(target().IsAnyStackSlot());
__ MoveRepr(repr, masm->ToMemOperand(target()), source_op);
}
}
}
void AssertInt32::SetValueLocationConstraints() {
UseRegister(left_input());
UseRegister(right_input());
}
void AssertInt32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CompareInt32AndAssert(ToRegister(left_input()), ToRegister(right_input()),
ToCondition(condition_), reason_);
}
void CheckUint32IsSmi::SetValueLocationConstraints() { UseRegister(input()); }
void CheckUint32IsSmi::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register reg = ToRegister(input());
// Perform an unsigned comparison against Smi::kMaxValue.
__ Cmp(reg, Smi::kMaxValue);
__ EmitEagerDeoptIf(kUnsignedGreaterThan, DeoptimizeReason::kNotASmi, this);
}
void CheckedSmiUntag::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
}
void CheckedSmiUntag::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register value = ToRegister(input());
// TODO(leszeks): Consider optimizing away this test and using the carry bit
// of the `sarl` for cases where the deopt uses the value from a different
// register.
__ EmitEagerDeoptIfNotSmi(this, value, DeoptimizeReason::kNotASmi);
__ SmiToInt32(value);
}
void UnsafeSmiUntag::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
}
void UnsafeSmiUntag::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register value = ToRegister(input());
__ AssertSmi(value);
__ SmiToInt32(value);
}
void CheckInt32IsSmi::SetValueLocationConstraints() { UseRegister(input()); }
void CheckInt32IsSmi::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
// We shouldn't be emitting this node for 32-bit Smis.
DCHECK(!SmiValuesAre32Bits());
// TODO(leszeks): This basically does a SmiTag and throws the result away.
// Don't throw the result away if we want to actually use it.
Register reg = ToRegister(input());
Label* fail = __ GetDeoptLabel(this, DeoptimizeReason::kNotASmi);
__ CheckInt32IsSmi(reg, fail);
}
void CheckedInt32ToUint32::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
}
void CheckedInt32ToUint32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CompareInt32AndJumpIf(
ToRegister(input()), 0, kLessThan,
__ GetDeoptLabel(this, DeoptimizeReason::kNotUint32));
}
void CheckHoleyFloat64IsSmi::SetValueLocationConstraints() {
UseRegister(input());
set_temporaries_needed(1);
}
void CheckHoleyFloat64IsSmi::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
DoubleRegister value = ToDoubleRegister(input());
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
Label* fail = __ GetDeoptLabel(this, DeoptimizeReason::kNotASmi);
__ TryTruncateDoubleToInt32(scratch, value, fail);
if (!SmiValuesAre32Bits()) {
__ CheckInt32IsSmi(scratch, fail, scratch);
}
}
void CheckedSmiTagInt32::SetValueLocationConstraints() {
UseAndClobberRegister(input());
DefineSameAsFirst(this);
}
void CheckedSmiTagInt32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register reg = ToRegister(input());
Label* fail = __ GetDeoptLabel(this, DeoptimizeReason::kNotASmi);
// None of the mutated input registers should be a register input into the
// eager deopt info.
DCHECK_REGLIST_EMPTY(RegList{reg} &
GetGeneralRegistersUsedAsInputs(eager_deopt_info()));
__ SmiTagInt32AndJumpIfFail(reg, fail);
}
void CheckedSmiSizedInt32::SetValueLocationConstraints() {
UseAndClobberRegister(input());
DefineSameAsFirst(this);
}
void CheckedSmiSizedInt32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
// We shouldn't be emitting this node for 32-bit Smis.
DCHECK(!SmiValuesAre32Bits());
Register reg = ToRegister(input());
Label* fail = __ GetDeoptLabel(this, DeoptimizeReason::kNotASmi);
__ CheckInt32IsSmi(reg, fail);
}
void CheckedSmiTagUint32::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
}
void CheckedSmiTagUint32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register reg = ToRegister(input());
Label* fail = __ GetDeoptLabel(this, DeoptimizeReason::kNotASmi);
// None of the mutated input registers should be a register input into the
// eager deopt info.
DCHECK_REGLIST_EMPTY(RegList{reg} &
GetGeneralRegistersUsedAsInputs(eager_deopt_info()));
__ SmiTagUint32AndJumpIfFail(reg, fail);
}
void UnsafeSmiTag::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
}
void UnsafeSmiTag::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register reg = ToRegister(input());
switch (input().node()->properties().value_representation()) {
case ValueRepresentation::kInt32:
__ UncheckedSmiTagInt32(reg);
break;
case ValueRepresentation::kUint32:
__ UncheckedSmiTagUint32(reg);
break;
default:
UNREACHABLE();
}
}
void CheckedSmiIncrement::SetValueLocationConstraints() {
UseRegister(value_input());
DefineSameAsFirst(this);
}
void CheckedSmiIncrement::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Label* deopt_label = __ GetDeoptLabel(this, DeoptimizeReason::kOverflow);
__ SmiAddConstant(ToRegister(value_input()), 1, deopt_label);
}
void CheckedSmiDecrement::SetValueLocationConstraints() {
UseRegister(value_input());
DefineSameAsFirst(this);
}
void CheckedSmiDecrement::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Label* deopt_label = __ GetDeoptLabel(this, DeoptimizeReason::kOverflow);
__ SmiSubConstant(ToRegister(value_input()), 1, deopt_label);
}
namespace {
void JumpToFailIfNotHeapNumberOrOddball(
MaglevAssembler* masm, Register value,
TaggedToFloat64ConversionType conversion_type, Label* fail) {
switch (conversion_type) {
case TaggedToFloat64ConversionType::kNumberOrOddball:
// Check if HeapNumber or Oddball, jump to fail otherwise.
static_assert(InstanceType::HEAP_NUMBER_TYPE + 1 ==
InstanceType::ODDBALL_TYPE);
if (fail) {
__ CompareObjectTypeRange(value, InstanceType::HEAP_NUMBER_TYPE,
InstanceType::ODDBALL_TYPE);
__ JumpIf(kUnsignedGreaterThan, fail);
} else {
if (v8_flags.debug_code) {
__ CompareObjectTypeRange(value, InstanceType::HEAP_NUMBER_TYPE,
InstanceType::ODDBALL_TYPE);
__ Assert(kUnsignedLessThanEqual, AbortReason::kUnexpectedValue);
}
}
break;
case TaggedToFloat64ConversionType::kOnlyNumber:
// Check if HeapNumber, jump to fail otherwise.
if (fail) {
__ CompareObjectTypeAndJumpIf(value, InstanceType::HEAP_NUMBER_TYPE,
kNotEqual, fail);
} else {
if (v8_flags.debug_code) {
__ CompareObjectTypeAndAssert(value, InstanceType::HEAP_NUMBER_TYPE,
kEqual, AbortReason::kUnexpectedValue);
}
}
break;
}
}
void TryUnboxNumberOrOddball(MaglevAssembler* masm, DoubleRegister dst,
Register clobbered_src,
TaggedToFloat64ConversionType conversion_type,
Label* fail) {
Label is_not_smi, done;
// Check if Smi.
__ JumpIfNotSmi(clobbered_src, &is_not_smi, Label::kNear);
// If Smi, convert to Float64.
__ SmiToInt32(clobbered_src);
__ Int32ToDouble(dst, clobbered_src);
__ Jump(&done, Label::kNear);
__ bind(&is_not_smi);
JumpToFailIfNotHeapNumberOrOddball(masm, clobbered_src, conversion_type,
fail);
static_assert(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset);
__ LoadHeapNumberValue(dst, clobbered_src);
__ bind(&done);
}
} // namespace
void CheckedNumberOrOddballToFloat64::SetValueLocationConstraints() {
UseAndClobberRegister(input());
DefineAsRegister(this);
}
void CheckedNumberOrOddballToFloat64::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
Register value = ToRegister(input());
TryUnboxNumberOrOddball(
masm, ToDoubleRegister(result()), value, conversion_type(),
__ GetDeoptLabel(this, DeoptimizeReason::kNotANumberOrOddball));
}
void UncheckedNumberOrOddballToFloat64::SetValueLocationConstraints() {
UseAndClobberRegister(input());
DefineAsRegister(this);
}
void UncheckedNumberOrOddballToFloat64::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
Register value = ToRegister(input());
TryUnboxNumberOrOddball(masm, ToDoubleRegister(result()), value,
conversion_type(), nullptr);
}
namespace {
void EmitTruncateNumberOrOddballToInt32(
MaglevAssembler* masm, Register value, Register result_reg,
TaggedToFloat64ConversionType conversion_type, Label* not_a_number) {
Label is_not_smi, done;
// Check if Smi.
__ JumpIfNotSmi(value, &is_not_smi, Label::kNear);
// If Smi, convert to Int32.
__ SmiToInt32(value);
__ Jump(&done, Label::kNear);
__ bind(&is_not_smi);
JumpToFailIfNotHeapNumberOrOddball(masm, value, conversion_type,
not_a_number);
static_assert(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset);
MaglevAssembler::ScratchRegisterScope temps(masm);
DoubleRegister double_value = temps.GetDefaultScratchDoubleRegister();
__ LoadHeapNumberValue(double_value, value);
__ TruncateDoubleToInt32(result_reg, double_value);
__ bind(&done);
}
} // namespace
void CheckedObjectToIndex::SetValueLocationConstraints() {
UseRegister(object_input());
DefineAsRegister(this);
set_double_temporaries_needed(1);
}
void CheckedObjectToIndex::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
Register result_reg = ToRegister(result());
ZoneLabelRef done(masm);
__ JumpIfNotSmi(
object,
__ MakeDeferredCode(
[](MaglevAssembler* masm, Register object, Register result_reg,
ZoneLabelRef done, CheckedObjectToIndex* node) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register map = temps.GetDefaultScratchRegister();
Label check_string;
__ LoadMap(map, object);
__ JumpIfNotRoot(
map, RootIndex::kHeapNumberMap, &check_string,
v8_flags.deopt_every_n_times > 0 ? Label::kFar : Label::kNear);
{
DoubleRegister number_value = temps.AcquireDouble();
__ LoadHeapNumberValue(number_value, object);
__ TryChangeFloat64ToIndex(
result_reg, number_value, *done,
__ GetDeoptLabel(node, DeoptimizeReason::kNotInt32));
}
__ bind(&check_string);
__ CompareInstanceTypeRange(map, map, FIRST_STRING_TYPE,
LAST_STRING_TYPE);
// The IC will go generic if it encounters something other than a
// Number or String key.
__ EmitEagerDeoptIf(kUnsignedGreaterThan,
DeoptimizeReason::kNotInt32, node);
{
// TODO(verwaest): Load the cached number from the string hash.
RegisterSnapshot snapshot = node->register_snapshot();
snapshot.live_registers.clear(result_reg);
DCHECK(!snapshot.live_tagged_registers.has(result_reg));
{
SaveRegisterStateForCall save_register_state(masm, snapshot);
AllowExternalCallThatCantCauseGC scope(masm);
__ PrepareCallCFunction(1);
__ Move(arg_reg_1, object);
__ CallCFunction(
ExternalReference::string_to_array_index_function(), 1);
// No need for safepoint since this is a fast C call.
__ Move(result_reg, kReturnRegister0);
}
__ CompareInt32AndJumpIf(
result_reg, 0, kLessThan,
__ GetDeoptLabel(node, DeoptimizeReason::kNotInt32));
__ Jump(*done);
}
},
object, result_reg, done, this));
// If we didn't enter the deferred block, we're a Smi.
__ SmiToInt32(result_reg, object);
__ bind(*done);
}
void CheckedTruncateNumberOrOddballToInt32::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
}
void CheckedTruncateNumberOrOddballToInt32::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
Register value = ToRegister(input());
Register result_reg = ToRegister(result());
DCHECK_EQ(value, result_reg);
Label* deopt_label =
__ GetDeoptLabel(this, DeoptimizeReason::kNotANumberOrOddball);
EmitTruncateNumberOrOddballToInt32(masm, value, result_reg, conversion_type(),
deopt_label);
}
void TruncateNumberOrOddballToInt32::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
}
void TruncateNumberOrOddballToInt32::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
Register value = ToRegister(input());
Register result_reg = ToRegister(result());
DCHECK_EQ(value, result_reg);
EmitTruncateNumberOrOddballToInt32(masm, value, result_reg, conversion_type(),
nullptr);
}
void ChangeInt32ToFloat64::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
}
void ChangeInt32ToFloat64::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ Int32ToDouble(ToDoubleRegister(result()), ToRegister(input()));
}
void ChangeUint32ToFloat64::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
}
void ChangeUint32ToFloat64::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ Uint32ToDouble(ToDoubleRegister(result()), ToRegister(input()));
}
void CheckMaps::SetValueLocationConstraints() {
UseRegister(receiver_input());
set_temporaries_needed(MapCompare::TemporaryCount(maps_.size()));
}
void CheckMaps::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(receiver_input());
// We emit an unconditional deopt if we intersect the map sets and the
// intersection is empty.
DCHECK(!maps().is_empty());
bool maps_include_heap_number = AnyMapIsHeapNumber(maps());
// Exprimentally figured out map limit (with slack) which allows us to use
// near jumps in the code below
constexpr int kMapCountForNearJumps = kTaggedSize == 4 ? 10 : 5;
Label::Distance jump_distance = maps().size() <= kMapCountForNearJumps
? Label::Distance::kNear
: Label::Distance::kFar;
Label done;
if (check_type() == CheckType::kOmitHeapObjectCheck) {
__ AssertNotSmi(object);
} else {
if (maps_include_heap_number) {
// Smis count as matching the HeapNumber map, so we're done.
__ JumpIfSmi(object, &done, jump_distance);
} else {
__ EmitEagerDeoptIfSmi(this, object, DeoptimizeReason::kWrongMap);
}
}
MapCompare map_compare(masm, object, maps_.size());
size_t map_count = maps().size();
for (size_t i = 0; i < map_count - 1; ++i) {
Handle<Map> map = maps().at(i).object();
map_compare.Generate(map, kEqual, &done, jump_distance);
}
Handle<Map> last_map = maps().at(map_count - 1).object();
Label* fail = __ GetDeoptLabel(this, DeoptimizeReason::kWrongMap);
map_compare.Generate(last_map, kNotEqual, fail);
__ bind(&done);
}
int CheckMapsWithMigration::MaxCallStackArgs() const {
DCHECK_EQ(Runtime::FunctionForId(Runtime::kTryMigrateInstance)->nargs, 1);
return 1;
}
void CheckMapsWithMigration::SetValueLocationConstraints() {
UseRegister(receiver_input());
set_temporaries_needed(MapCompare::TemporaryCount(maps_.size()));
}
void CheckMapsWithMigration::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
// We emit an unconditional deopt if we intersect the map sets and the
// intersection is empty.
DCHECK(!maps().is_empty());
MaglevAssembler::ScratchRegisterScope temps(masm);
Register object = ToRegister(receiver_input());
bool maps_include_heap_number = AnyMapIsHeapNumber(maps());
ZoneLabelRef map_checks(masm), done(masm);
if (check_type() == CheckType::kOmitHeapObjectCheck) {
__ AssertNotSmi(object);
} else {
if (maps_include_heap_number) {
// Smis count as matching the HeapNumber map, so we're done.
__ JumpIfSmi(object, *done);
} else {
__ EmitEagerDeoptIfSmi(this, object, DeoptimizeReason::kWrongMap);
}
}
// If we jump from here from the deferred code (below), we need to reload
// the map.
__ bind(*map_checks);
RegisterSnapshot save_registers = register_snapshot();
// Make sure that the object register is not clobbered by the
// Runtime::kMigrateInstance runtime call. It's ok to clobber the register
// where the object map is, since the map is reloaded after the runtime call.
save_registers.live_registers.set(object);
save_registers.live_tagged_registers.set(object);
// We can eager deopt after the snapshot, so make sure the nodes used by the
// deopt are included in it.
// TODO(leszeks): This is a bit of a footgun -- we likely want the snapshot to
// always include eager deopt input registers.
AddDeoptRegistersToSnapshot(&save_registers, eager_deopt_info());
size_t map_count = maps().size();
bool has_migration_targets = false;
MapCompare map_compare(masm, object, maps_.size());
Handle<Map> map_handle;
for (size_t i = 0; i < map_count; ++i) {
map_handle = maps().at(i).object();
const bool last_map = (i == map_count - 1);
if (!last_map) {
map_compare.Generate(map_handle, kEqual, *done);
}
if (map_handle->is_migration_target()) {
has_migration_targets = true;
}
}
if (!has_migration_targets) {
// Emit deopt for the last map.
map_compare.Generate(map_handle, kNotEqual,
__ GetDeoptLabel(this, DeoptimizeReason::kWrongMap));
} else {
map_compare.Generate(
map_handle, kNotEqual,
__ MakeDeferredCode(
[](MaglevAssembler* masm, RegisterSnapshot register_snapshot,
ZoneLabelRef map_checks, MapCompare map_compare,
CheckMapsWithMigration* node) {
Label* deopt =
__ GetDeoptLabel(node, DeoptimizeReason::kWrongMap);
// If the map is not deprecated, we fail the map check.
__ TestInt32AndJumpIfAllClear(
FieldMemOperand(map_compare.GetMap(), Map::kBitField3Offset),
Map::Bits3::IsDeprecatedBit::kMask, deopt);
// Otherwise, try migrating the object.
Register return_val = Register::no_reg();
{
SaveRegisterStateForCall save_register_state(masm,
register_snapshot);
__ Push(map_compare.GetObject());
__ Move(kContextRegister, masm->native_context().object());
__ CallRuntime(Runtime::kTryMigrateInstance);
save_register_state.DefineSafepoint();
// Make sure the return value is preserved across the live
// register restoring pop all.
return_val = kReturnRegister0;
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.GetDefaultScratchRegister();
if (register_snapshot.live_registers.has(return_val)) {
DCHECK(!register_snapshot.live_registers.has(scratch));
__ Move(scratch, return_val);
return_val = scratch;
}
}
// On failure, the returned value is Smi zero.
__ CompareTaggedAndJumpIf(return_val, Smi::zero(), kEqual, deopt);
// Otherwise, the return value is the object (it's always the same
// object we called TryMigrate with). We already have it in a
// register, so we can ignore the return value. We'll need to
// reload the map though since it might have changed; it's done
// right after the map_checks label.
__ Jump(*map_checks);
},
save_registers, map_checks, map_compare, this));
// If the jump to deferred code was not taken, the map was equal to the
// last map.
} // End of the `has_migration_targets` case.
__ bind(*done);
}
int DeleteProperty::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kDeleteProperty>::type;
return D::GetStackParameterCount();
}
void DeleteProperty::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kDeleteProperty>::type;
UseFixed(context(), kContextRegister);
UseFixed(object(), D::GetRegisterParameter(D::kObject));
UseFixed(key(), D::GetRegisterParameter(D::kKey));
DefineAsFixed(this, kReturnRegister0);
}
void DeleteProperty::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kDeleteProperty>(
context(), // context
object(), // object
key(), // key
Smi::FromInt(static_cast<int>(mode())) // language mode
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int ForInPrepare::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kForInPrepare>::type;
return D::GetStackParameterCount();
}
void ForInPrepare::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kForInPrepare>::type;
UseFixed(context(), kContextRegister);
UseFixed(enumerator(), D::GetRegisterParameter(D::kEnumerator));
DefineAsFixed(this, kReturnRegister0);
}
void ForInPrepare::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kForInPrepare>(
context(), // context
enumerator(), // enumerator
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int ForInNext::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kForInNext>::type;
return D::GetStackParameterCount();
}
void ForInNext::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kForInNext>::type;
UseFixed(context(), kContextRegister);
UseFixed(receiver(), D::GetRegisterParameter(D::kReceiver));
UseFixed(cache_array(), D::GetRegisterParameter(D::kCacheArray));
UseFixed(cache_type(), D::GetRegisterParameter(D::kCacheType));
UseFixed(cache_index(), D::GetRegisterParameter(D::kCacheIndex));
DefineAsFixed(this, kReturnRegister0);
}
void ForInNext::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kForInNext>(context(), // context
feedback().index(), // feedback slot
receiver(), // receiver
cache_array(), // cache array
cache_type(), // cache type
cache_index(), // cache index
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int GetIterator::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kGetIteratorWithFeedback>::type;
return D::GetStackParameterCount();
}
void GetIterator::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kGetIteratorWithFeedback>::type;
UseFixed(context(), kContextRegister);
UseFixed(receiver(), D::GetRegisterParameter(D::kReceiver));
DefineAsFixed(this, kReturnRegister0);
}
void GetIterator::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kGetIteratorWithFeedback>(
context(), // context
receiver(), // receiver
TaggedIndex::FromIntptr(load_slot()), // feedback load slot
TaggedIndex::FromIntptr(call_slot()), // feedback call slot
feedback() // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
void Int32Compare::SetValueLocationConstraints() {
UseRegister(left_input());
UseRegister(right_input());
DefineAsRegister(this);
}
void Int32Compare::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register result = ToRegister(this->result());
Label is_true, end;
__ CompareInt32AndJumpIf(ToRegister(left_input()), ToRegister(right_input()),
ConditionFor(operation()), &is_true,
Label::Distance::kNear);
// TODO(leszeks): Investigate loading existing materialisations of roots here,
// if available.
__ LoadRoot(result, RootIndex::kFalseValue);
__ jmp(&end);
{
__ bind(&is_true);
__ LoadRoot(result, RootIndex::kTrueValue);
}
__ bind(&end);
}
void Int32ToBoolean::SetValueLocationConstraints() {
UseRegister(value());
DefineAsRegister(this);
}
void Int32ToBoolean::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register result = ToRegister(this->result());
Label is_true, end;
__ CompareInt32AndJumpIf(ToRegister(value()), 0, kNotEqual, &is_true,
Label::Distance::kNear);
// TODO(leszeks): Investigate loading existing materialisations of roots here,
// if available.
__ LoadRoot(result, flip() ? RootIndex::kTrueValue : RootIndex::kFalseValue);
__ jmp(&end);
{
__ bind(&is_true);
__ LoadRoot(result,
flip() ? RootIndex::kFalseValue : RootIndex::kTrueValue);
}
__ bind(&end);
}
void Float64Compare::SetValueLocationConstraints() {
UseRegister(left_input());
UseRegister(right_input());
DefineAsRegister(this);
}
void Float64Compare::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
DoubleRegister left = ToDoubleRegister(left_input());
DoubleRegister right = ToDoubleRegister(right_input());
Register result = ToRegister(this->result());
Label is_false, end;
__ CompareFloat64AndJumpIf(left, right,
NegateCondition(ConditionForFloat64(operation())),
&is_false, &is_false, Label::Distance::kNear);
// TODO(leszeks): Investigate loading existing materialisations of roots here,
// if available.
__ LoadRoot(result, RootIndex::kTrueValue);
__ Jump(&end);
{
__ bind(&is_false);
__ LoadRoot(result, RootIndex::kFalseValue);
}
__ bind(&end);
}
void Float64ToBoolean::SetValueLocationConstraints() {
UseRegister(value());
set_double_temporaries_needed(1);
DefineAsRegister(this);
}
void Float64ToBoolean::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
DoubleRegister double_scratch = temps.AcquireDouble();
Register result = ToRegister(this->result());
Label is_false, end;
__ Move(double_scratch, 0.0);
__ CompareFloat64AndJumpIf(ToDoubleRegister(value()), double_scratch, kEqual,
&is_false, &is_false, Label::Distance::kNear);
__ LoadRoot(result, flip() ? RootIndex::kFalseValue : RootIndex::kTrueValue);
__ Jump(&end);
{
__ bind(&is_false);
__ LoadRoot(result,
flip() ? RootIndex::kTrueValue : RootIndex::kFalseValue);
}
__ bind(&end);
}
void CheckedHoleyFloat64ToFloat64::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
set_temporaries_needed(1);
}
void CheckedHoleyFloat64ToFloat64::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
__ JumpIfHoleNan(ToDoubleRegister(input()), temps.Acquire(),
__ GetDeoptLabel(this, DeoptimizeReason::kHole));
}
void LoadDoubleField::SetValueLocationConstraints() {
UseRegister(object_input());
DefineAsRegister(this);
set_temporaries_needed(1);
}
void LoadDoubleField::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register tmp = temps.Acquire();
Register object = ToRegister(object_input());
__ AssertNotSmi(object);
__ LoadTaggedField(tmp, object, offset());
__ AssertNotSmi(tmp);
__ LoadHeapNumberValue(ToDoubleRegister(result()), tmp);
}
void LoadTaggedField::SetValueLocationConstraints() {
UseRegister(object_input());
DefineAsRegister(this);
}
void LoadTaggedField::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
__ AssertNotSmi(object);
if (this->decompresses_tagged_result()) {
__ LoadTaggedField(ToRegister(result()), object, offset());
} else {
__ LoadTaggedFieldWithoutDecompressing(ToRegister(result()), object,
offset());
}
}
void LoadTaggedFieldByFieldIndex::SetValueLocationConstraints() {
UseRegister(object_input());
UseAndClobberRegister(index_input());
DefineAsRegister(this);
set_temporaries_needed(1);
set_double_temporaries_needed(1);
}
void LoadTaggedFieldByFieldIndex::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
Register field_index = ToRegister(index_input());
Register result_reg = ToRegister(result());
__ AssertNotSmi(object);
__ AssertSmi(field_index);
ZoneLabelRef done(masm);
// For in-object properties, the field_index is encoded as:
//
// field_index = array_index | is_double_bit | smi_tag
// = array_index << (1+kSmiTagBits)
// + is_double_bit << kSmiTagBits
//
// The value we want is at the field offset:
//
// (array_index << kTaggedSizeLog2) + JSObject::kHeaderSize
//
// We could get field_index from array_index by shifting away the double bit
// and smi tag, followed by shifting back up again, but this means shifting
// twice:
//
// ((field_index >> kSmiTagBits >> 1) << kTaggedSizeLog2
// + JSObject::kHeaderSize
//
// Instead, we can do some rearranging to get the offset with either a single
// small shift, or no shift at all:
//
// (array_index << kTaggedSizeLog2) + JSObject::kHeaderSize
//
// [Split shift to match array_index component of field_index]
// = (
// (array_index << 1+kSmiTagBits)) << (kTaggedSizeLog2-1-kSmiTagBits)
// ) + JSObject::kHeaderSize
//
// [Substitute in field_index]
// = (
// (field_index - is_double_bit << kSmiTagBits)
// << (kTaggedSizeLog2-1-kSmiTagBits)
// ) + JSObject::kHeaderSize
//
// [Fold together the constants]
// = (field_index << (kTaggedSizeLog2-1-kSmiTagBits)
// + (JSObject::kHeaderSize - (is_double_bit << (kTaggedSizeLog2-1)))
//
// Note that this results in:
//
// * No shift when kSmiTagBits == kTaggedSizeLog2 - 1, which is the case
// when pointer compression is on.
// * A shift of 1 when kSmiTagBits == 1 and kTaggedSizeLog2 == 3, which
// is the case when pointer compression is off but Smis are 31 bit.
// * A shift of 2 when kSmiTagBits == 0 and kTaggedSizeLog2 == 3, which
// is the case when pointer compression is off, Smis are 32 bit, and
// the Smi was untagged to int32 already.
//
// These shifts are small enough to encode in the load operand.
//
// For out-of-object properties, the encoding is:
//
// field_index = (-1 - array_index) | is_double_bit | smi_tag
// = (-1 - array_index) << (1+kSmiTagBits)
// + is_double_bit << kSmiTagBits
// = -array_index << (1+kSmiTagBits)
// - 1 << (1+kSmiTagBits) + is_double_bit << kSmiTagBits
// = -array_index << (1+kSmiTagBits)
// - 2 << kSmiTagBits + is_double_bit << kSmiTagBits
// = -array_index << (1+kSmiTagBits)
// (is_double_bit - 2) << kSmiTagBits
//
// The value we want is in the property array at offset:
//
// (array_index << kTaggedSizeLog2) + FixedArray::kHeaderSize
//
// [Split shift to match array_index component of field_index]
// = (array_index << (1+kSmiTagBits)) << (kTaggedSizeLog2-1-kSmiTagBits)
// + FixedArray::kHeaderSize
//
// [Substitute in field_index]
// = (-field_index - (is_double_bit - 2) << kSmiTagBits)
// << (kTaggedSizeLog2-1-kSmiTagBits)
// + FixedArray::kHeaderSize
//
// [Fold together the constants]
// = -field_index << (kTaggedSizeLog2-1-kSmiTagBits)
// + FixedArray::kHeaderSize
// - (is_double_bit - 2) << (kTaggedSizeLog2-1))
//
// This allows us to simply negate the field_index register and do a load with
// otherwise constant offset and the same scale factor as for in-object
// properties.
static constexpr int kSmiTagBitsInValue = SmiValuesAre32Bits() ? 0 : 1;
static_assert(kSmiTagBitsInValue == 32 - kSmiValueSize);
if (SmiValuesAre32Bits()) {
__ SmiUntag(field_index);
}
static constexpr int scale = 1 << (kTaggedSizeLog2 - 1 - kSmiTagBitsInValue);
// Check if field is a mutable double field.
static constexpr int32_t kIsDoubleBitMask = 1 << kSmiTagBitsInValue;
__ TestInt32AndJumpIfAnySet(
field_index, kIsDoubleBitMask,
__ MakeDeferredCode(
[](MaglevAssembler* masm, Register object, Register field_index,
Register result_reg, RegisterSnapshot register_snapshot,
ZoneLabelRef done) {
// The field is a Double field, a.k.a. a mutable HeapNumber.
static constexpr int kIsDoubleBit = 1;
// Check if field is in-object or out-of-object. The is_double bit
// value doesn't matter, since negative values will stay negative.
Label if_outofobject, loaded_field;
__ CompareInt32AndJumpIf(field_index, 0, kLessThan,
&if_outofobject);
// The field is located in the {object} itself.
{
// See giant comment above.
if (SmiValuesAre31Bits() && kTaggedSize != kSystemPointerSize) {
// We haven't untagged, so we need to sign extend.
__ SignExtend32To64Bits(field_index, field_index);
}
__ LoadTaggedFieldByIndex(
result_reg, object, field_index, scale,
JSObject::kHeaderSize -
(kIsDoubleBit << (kTaggedSizeLog2 - 1)));
__ Jump(&loaded_field);
}
__ bind(&if_outofobject);
{
MaglevAssembler::ScratchRegisterScope temps(masm);
Register property_array = temps.Acquire();
// Load the property array.
__ LoadTaggedField(
property_array,
FieldMemOperand(object, JSObject::kPropertiesOrHashOffset));
// See giant comment above. No need to sign extend, negate will
// handle it.
__ NegateInt32(field_index);
__ LoadTaggedFieldByIndex(
result_reg, property_array, field_index, scale,
FixedArray::kHeaderSize -
((2 - kIsDoubleBit) << (kTaggedSizeLog2 - 1)));
__ Jump(&loaded_field);
}
__ bind(&loaded_field);
// We may have transitioned in-place away from double, so check that
// this is a HeapNumber -- otherwise the load is fine and we don't
// need to copy anything anyway.
__ JumpIfSmi(result_reg, *done);
MaglevAssembler::ScratchRegisterScope temps(masm);
Register map = temps.Acquire();
// Hack: The temporary allocated for `map` might alias the result
// register. If it does, use the field_index register as a temporary
// instead (since it's clobbered anyway).
// TODO(leszeks): Extend the result register's lifetime to overlap
// the temporaries, so that this alias isn't possible.
if (map == result_reg) {
DCHECK_NE(map, field_index);
map = field_index;
}
__ LoadMap(map, result_reg);
__ JumpIfNotRoot(map, RootIndex::kHeapNumberMap, *done);
DoubleRegister double_value = temps.AcquireDouble();
__ LoadHeapNumberValue(double_value, result_reg);
__ AllocateHeapNumber(register_snapshot, result_reg, double_value);
__ Jump(*done);
},
object, field_index, result_reg, register_snapshot(), done));
// The field is a proper Tagged field on {object}. The {field_index} is
// shifted to the left by one in the code below.
{
static constexpr int kIsDoubleBit = 0;
// Check if field is in-object or out-of-object. The is_double bit value
// doesn't matter, since negative values will stay negative.
Label if_outofobject;
__ CompareInt32AndJumpIf(field_index, 0, kLessThan, &if_outofobject);
// The field is located in the {object} itself.
{
// See giant comment above.
if (SmiValuesAre31Bits() && kTaggedSize != kSystemPointerSize) {
// We haven't untagged, so we need to sign extend.
__ SignExtend32To64Bits(field_index, field_index);
}
__ SignExtend32To64Bits(field_index, field_index);
__ LoadTaggedFieldByIndex(
result_reg, object, field_index, scale,
JSObject::kHeaderSize - (kIsDoubleBit << (kTaggedSizeLog2 - 1)));
__ Jump(*done);
}
__ bind(&if_outofobject);
{
MaglevAssembler::ScratchRegisterScope temps(masm);
Register property_array = temps.Acquire();
// Load the property array.
__ LoadTaggedField(
property_array,
FieldMemOperand(object, JSObject::kPropertiesOrHashOffset));
// See giant comment above. No need to sign extend, negate will handle it.
__ NegateInt32(field_index);
__ LoadTaggedFieldByIndex(
result_reg, property_array, field_index, scale,
FixedArray::kHeaderSize -
((2 - kIsDoubleBit) << (kTaggedSizeLog2 - 1)));
// Fallthrough to `done`.
}
}
__ bind(*done);
}
void LoadFixedArrayElement::SetValueLocationConstraints() {
UseRegister(elements_input());
UseRegister(index_input());
DefineAsRegister(this);
}
void LoadFixedArrayElement::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register elements = ToRegister(elements_input());
Register index = ToRegister(index_input());
Register result_reg = ToRegister(result());
if (this->decompresses_tagged_result()) {
__ LoadFixedArrayElement(result_reg, elements, index);
} else {
__ LoadFixedArrayElementWithoutDecompressing(result_reg, elements, index);
}
}
void LoadFixedDoubleArrayElement::SetValueLocationConstraints() {
UseRegister(elements_input());
UseRegister(index_input());
DefineAsRegister(this);
}
void LoadFixedDoubleArrayElement::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register elements = ToRegister(elements_input());
Register index = ToRegister(index_input());
DoubleRegister result_reg = ToDoubleRegister(result());
__ LoadFixedDoubleArrayElement(result_reg, elements, index);
}
void LoadHoleyFixedDoubleArrayElement::SetValueLocationConstraints() {
UseRegister(elements_input());
UseRegister(index_input());
DefineAsRegister(this);
}
void LoadHoleyFixedDoubleArrayElement::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
Register elements = ToRegister(elements_input());
Register index = ToRegister(index_input());
DoubleRegister result_reg = ToDoubleRegister(result());
__ LoadFixedDoubleArrayElement(result_reg, elements, index);
}
void LoadHoleyFixedDoubleArrayElementCheckedNotHole::
SetValueLocationConstraints() {
UseRegister(elements_input());
UseRegister(index_input());
DefineAsRegister(this);
set_temporaries_needed(1);
}
void LoadHoleyFixedDoubleArrayElementCheckedNotHole::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register elements = ToRegister(elements_input());
Register index = ToRegister(index_input());
DoubleRegister result_reg = ToDoubleRegister(result());
__ LoadFixedDoubleArrayElement(result_reg, elements, index);
__ JumpIfHoleNan(result_reg, temps.Acquire(),
__ GetDeoptLabel(this, DeoptimizeReason::kHole));
}
void StoreFixedDoubleArrayElement::SetValueLocationConstraints() {
UseRegister(elements_input());
UseRegister(index_input());
UseRegister(value_input());
}
void StoreFixedDoubleArrayElement::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register elements = ToRegister(elements_input());
Register index = ToRegister(index_input());
DoubleRegister value = ToDoubleRegister(value_input());
if (v8_flags.debug_code) {
__ CompareObjectTypeAndAssert(elements, FIXED_DOUBLE_ARRAY_TYPE, kEqual,
AbortReason::kUnexpectedValue);
__ CompareInt32AndAssert(index, 0, kUnsignedGreaterThanEqual,
AbortReason::kUnexpectedNegativeValue);
}
__ StoreFixedDoubleArrayElement(elements, index, value);
}
int StoreMap::MaxCallStackArgs() const {
return WriteBarrierDescriptor::GetStackParameterCount();
}
void StoreMap::SetValueLocationConstraints() {
UseFixed(object_input(), WriteBarrierDescriptor::ObjectRegister());
set_temporaries_needed(1);
}
void StoreMap::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
// TODO(leszeks): Consider making this an arbitrary register and push/popping
// in the deferred path.
Register object = WriteBarrierDescriptor::ObjectRegister();
DCHECK_EQ(object, ToRegister(object_input()));
Register value = temps.Acquire();
__ Move(value, map_.object());
__ StoreTaggedFieldWithWriteBarrier(object, HeapObject::kMapOffset, value,
register_snapshot(),
MaglevAssembler::kValueIsDecompressed,
MaglevAssembler::kValueCannotBeSmi);
}
int StoreTaggedFieldWithWriteBarrier::MaxCallStackArgs() const {
return WriteBarrierDescriptor::GetStackParameterCount();
}
void StoreTaggedFieldWithWriteBarrier::SetValueLocationConstraints() {
UseFixed(object_input(), WriteBarrierDescriptor::ObjectRegister());
UseRegister(value_input());
}
void StoreTaggedFieldWithWriteBarrier::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
// TODO(leszeks): Consider making this an arbitrary register and push/popping
// in the deferred path.
Register object = WriteBarrierDescriptor::ObjectRegister();
DCHECK_EQ(object, ToRegister(object_input()));
Register value = ToRegister(value_input());
__ StoreTaggedFieldWithWriteBarrier(
object, offset(), value, register_snapshot(),
value_input().node()->decompresses_tagged_result()
? MaglevAssembler::kValueIsDecompressed
: MaglevAssembler::kValueIsCompressed,
MaglevAssembler::kValueCanBeSmi);
}
void LoadSignedIntDataViewElement::SetValueLocationConstraints() {
UseRegister(object_input());
UseRegister(index_input());
if (is_little_endian_constant() ||
type_ == ExternalArrayType::kExternalInt8Array) {
UseAny(is_little_endian_input());
} else {
UseRegister(is_little_endian_input());
}
DefineAsRegister(this);
set_temporaries_needed(1);
}
void LoadSignedIntDataViewElement::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
Register index = ToRegister(index_input());
Register result_reg = ToRegister(result());
__ AssertNotSmi(object);
if (v8_flags.debug_code) {
__ CompareObjectTypeAndAssert(object, JS_DATA_VIEW_TYPE,
kUnsignedGreaterThanEqual,
AbortReason::kUnexpectedValue);
}
int element_size = ExternalArrayElementSize(type_);
MaglevAssembler::ScratchRegisterScope temps(masm);
Register data_pointer = temps.Acquire();
// We need to make sure we don't clobber is_little_endian_input by writing to
// the result register.
Register reg_with_result = result_reg;
if (type_ != ExternalArrayType::kExternalInt8Array &&
!is_little_endian_constant() &&
result_reg == ToRegister(is_little_endian_input())) {
reg_with_result = data_pointer;
}
// Load data pointer.
__ LoadExternalPointerField(
data_pointer, FieldMemOperand(object, JSDataView::kDataPointerOffset));
MemOperand element_address = __ DataViewElementOperand(data_pointer, index);
__ LoadSignedField(reg_with_result, element_address, element_size);
// We ignore little endian argument if type is a byte size.
if (type_ != ExternalArrayType::kExternalInt8Array) {
if (is_little_endian_constant()) {
// TODO(v8:7700): Support big endian architectures.
static_assert(V8_TARGET_LITTLE_ENDIAN == 1);
if (!FromConstantToBool(masm, is_little_endian_input().node())) {
DCHECK_EQ(reg_with_result, result_reg);
__ ReverseByteOrder(result_reg, element_size);
}
} else {
ZoneLabelRef is_little_endian(masm), is_big_endian(masm);
DCHECK_NE(reg_with_result, ToRegister(is_little_endian_input()));
__ ToBoolean(ToRegister(is_little_endian_input()),
CheckType::kCheckHeapObject, is_little_endian, is_big_endian,
false);
__ bind(*is_big_endian);
__ ReverseByteOrder(reg_with_result, element_size);
__ bind(*is_little_endian);
if (reg_with_result != result_reg) {
__ Move(result_reg, reg_with_result);
}
}
}
}
void StoreSignedIntDataViewElement::SetValueLocationConstraints() {
UseRegister(object_input());
UseRegister(index_input());
if (ExternalArrayElementSize(type_) > 1) {
UseAndClobberRegister(value_input());
} else {
UseRegister(value_input());
}
if (is_little_endian_constant() ||
type_ == ExternalArrayType::kExternalInt8Array) {
UseAny(is_little_endian_input());
} else {
UseRegister(is_little_endian_input());
}
set_temporaries_needed(1);
}
void StoreSignedIntDataViewElement::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
Register index = ToRegister(index_input());
Register value = ToRegister(value_input());
__ AssertNotSmi(object);
if (v8_flags.debug_code) {
__ CompareObjectTypeAndAssert(object, JS_DATA_VIEW_TYPE,
kUnsignedGreaterThanEqual,
AbortReason::kUnexpectedValue);
}
int element_size = ExternalArrayElementSize(type_);
// We ignore little endian argument if type is a byte size.
if (element_size > 1) {
if (is_little_endian_constant()) {
// TODO(v8:7700): Support big endian architectures.
static_assert(V8_TARGET_LITTLE_ENDIAN == 1);
if (!FromConstantToBool(masm, is_little_endian_input().node())) {
__ ReverseByteOrder(value, element_size);
}
} else {
ZoneLabelRef is_little_endian(masm), is_big_endian(masm);
__ ToBoolean(ToRegister(is_little_endian_input()),
CheckType::kCheckHeapObject, is_little_endian, is_big_endian,
false);
__ bind(*is_big_endian);
__ ReverseByteOrder(value, element_size);
__ bind(*is_little_endian);
}
}
MaglevAssembler::ScratchRegisterScope temps(masm);
Register data_pointer = temps.Acquire();
__ LoadExternalPointerField(
data_pointer, FieldMemOperand(object, JSDataView::kDataPointerOffset));
MemOperand element_address = __ DataViewElementOperand(data_pointer, index);
__ StoreField(element_address, value, element_size);
}
void LoadDoubleDataViewElement::SetValueLocationConstraints() {
UseRegister(object_input());
UseRegister(index_input());
if (is_little_endian_constant()) {
UseAny(is_little_endian_input());
} else {
UseRegister(is_little_endian_input());
}
set_temporaries_needed(1);
DefineAsRegister(this);
}
void LoadDoubleDataViewElement::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register object = ToRegister(object_input());
Register index = ToRegister(index_input());
DoubleRegister result_reg = ToDoubleRegister(result());
Register data_pointer = temps.Acquire();
__ AssertNotSmi(object);
if (v8_flags.debug_code) {
__ CompareObjectTypeAndAssert(object, JS_DATA_VIEW_TYPE,
kUnsignedGreaterThanEqual,
AbortReason::kUnexpectedValue);
}
// Load data pointer.
__ LoadExternalPointerField(
data_pointer, FieldMemOperand(object, JSDataView::kDataPointerOffset));
// TODO(v8:7700): Support big endian architectures.
static_assert(V8_TARGET_LITTLE_ENDIAN == 1);
if (is_little_endian_constant()) {
if (FromConstantToBool(masm, is_little_endian_input().node())) {
__ LoadUnalignedFloat64(result_reg, data_pointer, index);
} else {
__ LoadUnalignedFloat64AndReverseByteOrder(result_reg, data_pointer,
index);
}
} else {
Label done;
ZoneLabelRef is_little_endian(masm), is_big_endian(masm);
// TODO(leszeks): We're likely to be calling this on an existing boolean --
// maybe that's a case we should fast-path here and re-use that boolean
// value?
__ ToBoolean(ToRegister(is_little_endian_input()),
CheckType::kCheckHeapObject, is_little_endian, is_big_endian,
true);
__ bind(*is_little_endian);
__ LoadUnalignedFloat64(result_reg, data_pointer, index);
__ Jump(&done);
// We should swap the bytes if big endian.
__ bind(*is_big_endian);
__ LoadUnalignedFloat64AndReverseByteOrder(result_reg, data_pointer, index);
__ bind(&done);
}
}
void StoreDoubleDataViewElement::SetValueLocationConstraints() {
UseRegister(object_input());
UseRegister(index_input());
UseRegister(value_input());
if (is_little_endian_constant()) {
UseAny(is_little_endian_input());
} else {
UseRegister(is_little_endian_input());
}
set_temporaries_needed(1);
}
void StoreDoubleDataViewElement::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
Register index = ToRegister(index_input());
DoubleRegister value = ToDoubleRegister(value_input());
MaglevAssembler::ScratchRegisterScope temps(masm);
Register data_pointer = temps.Acquire();
__ AssertNotSmi(object);
if (v8_flags.debug_code) {
__ CompareObjectTypeAndAssert(object, JS_DATA_VIEW_TYPE,
kUnsignedGreaterThanEqual,
AbortReason::kUnexpectedValue);
}
// Load data pointer.
__ LoadExternalPointerField(
data_pointer, FieldMemOperand(object, JSDataView::kDataPointerOffset));
// TODO(v8:7700): Support big endian architectures.
static_assert(V8_TARGET_LITTLE_ENDIAN == 1);
if (is_little_endian_constant()) {
if (FromConstantToBool(masm, is_little_endian_input().node())) {
__ StoreUnalignedFloat64(data_pointer, index, value);
} else {
__ ReverseByteOrderAndStoreUnalignedFloat64(data_pointer, index, value);
}
} else {
Label done;
ZoneLabelRef is_little_endian(masm), is_big_endian(masm);
// TODO(leszeks): We're likely to be calling this on an existing boolean --
// maybe that's a case we should fast-path here and re-use that boolean
// value?
__ ToBoolean(ToRegister(is_little_endian_input()),
CheckType::kCheckHeapObject, is_little_endian, is_big_endian,
true);
__ bind(*is_little_endian);
__ StoreUnalignedFloat64(data_pointer, index, value);
__ Jump(&done);
// We should swap the bytes if big endian.
__ bind(*is_big_endian);
__ ReverseByteOrderAndStoreUnalignedFloat64(data_pointer, index, value);
__ bind(&done);
}
}
namespace {
template <typename NodeT, typename Function, typename... Args>
void EmitPolymorphicAccesses(MaglevAssembler* masm, NodeT* node,
Register object, Function&& f, Args&&... args) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register object_map = temps.Acquire();
Label done;
Label is_number;
Label* deopt = __ GetDeoptLabel(node, DeoptimizeReason::kWrongMap);
__ JumpIfSmi(object, &is_number);
__ LoadMap(object_map, object);
for (const PolymorphicAccessInfo& access_info : node->access_infos()) {
Label next;
Label map_found;
auto& maps = access_info.maps();
bool has_number_map = false;
if (HasOnlyStringMaps(base::VectorOf(maps))) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.GetDefaultScratchRegister();
__ CompareInstanceTypeRange(object_map, scratch, FIRST_STRING_TYPE,
LAST_STRING_TYPE);
__ JumpIf(kUnsignedGreaterThan, &next);
// Fallthrough... to map_found.
} else {
for (auto it = maps.begin(); it != maps.end(); ++it) {
if (IsHeapNumberMap(*it->object())) {
if (it == maps.end() - 1) {
__ CompareRootAndJumpIf(object_map, RootIndex::kHeapNumberMap,
kNotEqual, &next);
} else {
__ CompareRootAndJumpIf(object_map, RootIndex::kHeapNumberMap,
kEqual, &map_found);
}
has_number_map = true;
} else {
if (it == maps.end() - 1) {
__ CompareTaggedAndJumpIf(object_map, it->object(), kNotEqual,
&next);
// Fallthrough... to map_found.
} else {
__ CompareTaggedAndJumpIf(object_map, it->object(), kEqual,
&map_found);
}
}
}
}
if (has_number_map) {
DCHECK(!is_number.is_bound());
__ bind(&is_number);
}
__ bind(&map_found);
__ EmitEagerDeoptStress(deopt);
f(masm, node, access_info, object, object_map, std::forward<Args>(args)...);
__ Jump(&done);
__ bind(&next);
}
// A HeapNumberMap was not found, we should eager deopt here in case of a
// number.
if (!is_number.is_bound()) {
__ bind(&is_number);
}
// No map matched!
__ JumpToDeopt(deopt);
__ bind(&done);
}
} // namespace
void LoadPolymorphicTaggedField::SetValueLocationConstraints() {
UseRegister(object_input());
DefineAsRegister(this);
set_temporaries_needed(2);
set_double_temporaries_needed(1);
}
void LoadPolymorphicTaggedField::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
EmitPolymorphicAccesses(
masm, this, object,
[](MaglevAssembler* masm, LoadPolymorphicTaggedField* node,
const PolymorphicAccessInfo& access_info, Register object,
Register map, Register result) {
switch (access_info.kind()) {
case PolymorphicAccessInfo::kNotFound:
__ LoadRoot(result, RootIndex::kUndefinedValue);
break;
case PolymorphicAccessInfo::kConstant: {
Handle<Object> constant = access_info.constant();
if (IsSmi(*constant)) {
__ Move(result, Smi::cast(*constant));
} else {
DCHECK(IsHeapObject(*access_info.constant()));
__ Move(result, Handle<HeapObject>::cast(constant));
}
break;
}
case PolymorphicAccessInfo::kModuleExport: {
Register cell = map; // Reuse scratch.
__ Move(cell, access_info.cell());
__ AssertNotSmi(cell);
__ LoadTaggedField(result, cell, Cell::kValueOffset);
break;
}
case PolymorphicAccessInfo::kDataLoad: {
MaglevAssembler::ScratchRegisterScope temps(masm);
DoubleRegister double_scratch = temps.AcquireDouble();
__ LoadDataField(access_info, result, object, map);
if (access_info.field_index().is_double()) {
__ LoadHeapNumberValue(double_scratch, result);
__ AllocateHeapNumber(node->register_snapshot(), result,
double_scratch);
}
break;
}
case PolymorphicAccessInfo::kStringLength:
__ StringLength(result, object);
__ SmiTag(result);
break;
}
},
ToRegister(result()));
}
void LoadPolymorphicDoubleField::SetValueLocationConstraints() {
UseRegister(object_input());
DefineAsRegister(this);
set_temporaries_needed(1);
}
void LoadPolymorphicDoubleField::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
EmitPolymorphicAccesses(
masm, this, object,
[](MaglevAssembler* masm, LoadPolymorphicDoubleField* node,
const PolymorphicAccessInfo& access_info, Register object,
Register map, DoubleRegister result) {
Register scratch = map;
switch (access_info.kind()) {
case PolymorphicAccessInfo::kDataLoad:
__ LoadDataField(access_info, scratch, object, map);
switch (access_info.field_representation().kind()) {
case Representation::kSmi:
__ SmiToDouble(result, scratch);
break;
case Representation::kDouble:
__ LoadHeapNumberValue(result, scratch);
break;
default:
UNREACHABLE();
}
break;
case PolymorphicAccessInfo::kConstant: {
Handle<Object> constant = access_info.constant();
if (IsSmi(*constant)) {
// Remove the cast to Tagged<Smi> once Smi::cast returns Tagged.
__ Move(scratch, Smi::cast(*constant));
__ SmiToDouble(result, scratch);
} else {
DCHECK(IsHeapNumber(*constant));
__ Move(result, Handle<HeapNumber>::cast(constant)->value());
}
break;
}
case PolymorphicAccessInfo::kStringLength:
__ StringLength(scratch, object);
__ Int32ToDouble(result, scratch);
break;
default:
UNREACHABLE();
}
},
ToDoubleRegister(result()));
}
void LoadEnumCacheLength::SetValueLocationConstraints() {
UseRegister(map_input());
DefineAsRegister(this);
}
void LoadEnumCacheLength::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register map = ToRegister(map_input());
Register result_reg = ToRegister(result());
__ AssertMap(map);
__ LoadBitField<Map::Bits3::EnumLengthBits>(
result_reg, FieldMemOperand(map, Map::kBitField3Offset));
}
int LoadGlobal::MaxCallStackArgs() const {
if (typeof_mode() == TypeofMode::kNotInside) {
using D = CallInterfaceDescriptorFor<Builtin::kLoadGlobalIC>::type;
return D::GetStackParameterCount();
} else {
using D =
CallInterfaceDescriptorFor<Builtin::kLoadGlobalICInsideTypeof>::type;
return D::GetStackParameterCount();
}
}
void LoadGlobal::SetValueLocationConstraints() {
UseFixed(context(), kContextRegister);
DefineAsFixed(this, kReturnRegister0);
}
void LoadGlobal::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
if (typeof_mode() == TypeofMode::kNotInside) {
__ CallBuiltin<Builtin::kLoadGlobalIC>(
context(), // context
name().object(), // name
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
feedback().vector // feedback vector
);
} else {
DCHECK_EQ(typeof_mode(), TypeofMode::kInside);
__ CallBuiltin<Builtin::kLoadGlobalICInsideTypeof>(
context(), // context
name().object(), // name
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
feedback().vector // feedback vector
);
}
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int StoreGlobal::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kStoreGlobalIC>::type;
return D::GetStackParameterCount();
}
void StoreGlobal::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kStoreGlobalIC>::type;
UseFixed(context(), kContextRegister);
UseFixed(value(), D::GetRegisterParameter(D::kValue));
DefineAsFixed(this, kReturnRegister0);
}
void StoreGlobal::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kStoreGlobalIC>(
context(), // context
name().object(), // name
value(), // value
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
void CheckValue::SetValueLocationConstraints() { UseRegister(target_input()); }
void CheckValue::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register target = ToRegister(target_input());
Label* fail = __ GetDeoptLabel(this, DeoptimizeReason::kWrongValue);
__ CompareTaggedAndJumpIf(target, value().object(), kNotEqual, fail);
}
void CheckValueEqualsInt32::SetValueLocationConstraints() {
UseRegister(target_input());
}
void CheckValueEqualsInt32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register target = ToRegister(target_input());
Label* fail = __ GetDeoptLabel(this, DeoptimizeReason::kWrongValue);
__ CompareInt32AndJumpIf(target, value(), kNotEqual, fail);
}
void CheckValueEqualsFloat64::SetValueLocationConstraints() {
UseRegister(target_input());
set_double_temporaries_needed(1);
}
void CheckValueEqualsFloat64::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
DoubleRegister scratch = temps.AcquireDouble();
DoubleRegister target = ToDoubleRegister(target_input());
__ Move(scratch, value());
Label* fail = __ GetDeoptLabel(this, DeoptimizeReason::kWrongValue);
__ CompareFloat64AndJumpIf(scratch, target, kNotEqual, fail, fail);
}
void CheckValueEqualsString::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kStringEqual>::type;
UseFixed(target_input(), D::GetRegisterParameter(D::kLeft));
RequireSpecificTemporary(D::GetRegisterParameter(D::kLength));
}
void CheckValueEqualsString::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
using D = CallInterfaceDescriptorFor<Builtin::kStringEqual>::type;
ZoneLabelRef end(masm);
DCHECK_EQ(D::GetRegisterParameter(D::kLeft), ToRegister(target_input()));
Register target = D::GetRegisterParameter(D::kLeft);
// Maybe the string is internalized already, do a fast reference check first.
__ CompareTaggedAndJumpIf(target, value().object(), kEqual, *end,
Label::kNear);
__ EmitEagerDeoptIfSmi(this, target, DeoptimizeReason::kWrongValue);
__ CompareObjectTypeRange(target, FIRST_STRING_TYPE, LAST_STRING_TYPE);
__ JumpToDeferredIf(
kUnsignedLessThanEqual,
[](MaglevAssembler* masm, CheckValueEqualsString* node,
ZoneLabelRef end) {
Register target = D::GetRegisterParameter(D::kLeft);
Register string_length = D::GetRegisterParameter(D::kLength);
__ StringLength(string_length, target);
Label* fail = __ GetDeoptLabel(node, DeoptimizeReason::kWrongValue);
__ CompareInt32AndJumpIf(string_length, node->value().length(),
kNotEqual, fail);
RegisterSnapshot snapshot = node->register_snapshot();
AddDeoptRegistersToSnapshot(&snapshot, node->eager_deopt_info());
{
SaveRegisterStateForCall save_register_state(masm, snapshot);
__ CallBuiltin<Builtin::kStringEqual>(
node->target_input(), // left
node->value().object(), // right
string_length // length
);
save_register_state.DefineSafepoint();
// Compare before restoring registers, so that the deopt below has the
// correct register set.
__ CompareRoot(kReturnRegister0, RootIndex::kTrueValue);
}
__ EmitEagerDeoptIf(kNotEqual, DeoptimizeReason::kWrongValue, node);
__ Jump(*end);
},
this, end);
__ EmitEagerDeopt(this, DeoptimizeReason::kWrongValue);
__ bind(*end);
}
void CheckDynamicValue::SetValueLocationConstraints() {
UseRegister(first_input());
UseRegister(second_input());
}
void CheckDynamicValue::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register first = ToRegister(first_input());
Register second = ToRegister(second_input());
Label* fail = __ GetDeoptLabel(this, DeoptimizeReason::kWrongValue);
__ CompareTaggedAndJumpIf(first, second, kNotEqual, fail);
}
void CheckSmi::SetValueLocationConstraints() { UseRegister(receiver_input()); }
void CheckSmi::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(receiver_input());
__ EmitEagerDeoptIfNotSmi(this, object, DeoptimizeReason::kNotASmi);
}
void CheckHeapObject::SetValueLocationConstraints() {
UseRegister(receiver_input());
}
void CheckHeapObject::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(receiver_input());
__ EmitEagerDeoptIfSmi(this, object, DeoptimizeReason::kSmi);
}
void CheckSymbol::SetValueLocationConstraints() {
UseRegister(receiver_input());
}
void CheckSymbol::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(receiver_input());
if (check_type() == CheckType::kOmitHeapObjectCheck) {
__ AssertNotSmi(object);
} else {
__ EmitEagerDeoptIfSmi(this, object, DeoptimizeReason::kNotASymbol);
}
__ CompareObjectTypeAndJumpIf(
object, SYMBOL_TYPE, kNotEqual,
__ GetDeoptLabel(this, DeoptimizeReason::kNotASymbol));
}
void CheckInstanceType::SetValueLocationConstraints() {
UseRegister(receiver_input());
if (first_instance_type_ != last_instance_type_) {
set_temporaries_needed(1);
}
}
void CheckInstanceType::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(receiver_input());
if (check_type() == CheckType::kOmitHeapObjectCheck) {
__ AssertNotSmi(object);
} else {
__ EmitEagerDeoptIfSmi(this, object, DeoptimizeReason::kWrongInstanceType);
}
if (first_instance_type_ == last_instance_type_) {
__ CompareObjectTypeAndJumpIf(
object, first_instance_type_, kNotEqual,
__ GetDeoptLabel(this, DeoptimizeReason::kWrongInstanceType));
} else {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register map = temps.Acquire();
__ LoadMap(map, object);
__ CompareInstanceTypeRange(map, map, first_instance_type_,
last_instance_type_);
__ EmitEagerDeoptIf(kUnsignedGreaterThan,
DeoptimizeReason::kWrongInstanceType, this);
}
}
void CheckFixedArrayNonEmpty::SetValueLocationConstraints() {
UseRegister(receiver_input());
set_temporaries_needed(1);
}
void CheckFixedArrayNonEmpty::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(receiver_input());
__ AssertNotSmi(object);
if (v8_flags.debug_code) {
Label ok;
__ CompareObjectTypeAndJumpIf(object, FIXED_ARRAY_TYPE, kEqual, &ok);
__ CompareObjectTypeAndAssert(object, FIXED_DOUBLE_ARRAY_TYPE, kEqual,
AbortReason::kOperandIsNotAFixedArray);
__ bind(&ok);
}
MaglevAssembler::ScratchRegisterScope temps(masm);
Register length = temps.Acquire();
__ LoadTaggedSignedField(length, object, FixedArrayBase::kLengthOffset);
__ CompareSmiAndJumpIf(
length, Smi::zero(), kEqual,
__ GetDeoptLabel(this, DeoptimizeReason::kWrongEnumIndices));
}
void CheckInt32Condition::SetValueLocationConstraints() {
UseRegister(left_input());
UseRegister(right_input());
}
void CheckInt32Condition::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Label* fail = __ GetDeoptLabel(this, reason());
__ CompareInt32AndJumpIf(ToRegister(left_input()), ToRegister(right_input()),
NegateCondition(ToCondition(condition())), fail);
}
void CheckString::SetValueLocationConstraints() {
UseRegister(receiver_input());
}
void CheckString::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(receiver_input());
if (check_type() == CheckType::kOmitHeapObjectCheck) {
__ AssertNotSmi(object);
} else {
__ EmitEagerDeoptIfSmi(this, object, DeoptimizeReason::kNotAString);
}
__ CompareObjectTypeRange(object, FIRST_STRING_TYPE, LAST_STRING_TYPE);
__ EmitEagerDeoptIf(kUnsignedGreaterThan, DeoptimizeReason::kNotAString,
this);
}
void CheckNotHole::SetValueLocationConstraints() {
UseRegister(object_input());
DefineSameAsFirst(this);
}
void CheckNotHole::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
DCHECK_EQ(ToRegister(object_input()), ToRegister(result()));
Register reg = ToRegister(object_input());
__ CompareRoot(reg, RootIndex::kTheHoleValue);
__ EmitEagerDeoptIf(kEqual, DeoptimizeReason::kHole, this);
}
void ConvertHoleToUndefined::SetValueLocationConstraints() {
UseRegister(object_input());
DefineSameAsFirst(this);
}
void ConvertHoleToUndefined::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Label done;
DCHECK_EQ(ToRegister(object_input()), ToRegister(result()));
__ JumpIfNotRoot(ToRegister(object_input()), RootIndex::kTheHoleValue, &done);
__ LoadRoot(ToRegister(result()), RootIndex::kUndefinedValue);
__ bind(&done);
}
int ConvertReceiver::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kToObject>::type;
return D::GetStackParameterCount();
}
void ConvertReceiver::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kToObject>::type;
UseFixed(receiver_input(), D::GetRegisterParameter(D::kInput));
DefineAsFixed(this, kReturnRegister0);
}
void ConvertReceiver::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Label convert_to_object, done;
Register receiver = ToRegister(receiver_input());
__ JumpIfSmi(receiver, &convert_to_object, Label::Distance::kNear);
__ JumpIfJSAnyIsNotPrimitive(receiver, &done);
compiler::JSHeapBroker* broker = masm->compilation_info()->broker();
if (mode_ != ConvertReceiverMode::kNotNullOrUndefined) {
Label convert_global_proxy;
__ JumpIfRoot(receiver, RootIndex::kUndefinedValue, &convert_global_proxy,
Label::Distance::kNear);
__ JumpIfNotRoot(receiver, RootIndex::kNullValue, &convert_to_object,
Label::Distance::kNear);
__ bind(&convert_global_proxy);
// Patch receiver to global proxy.
__ Move(ToRegister(result()),
native_context_.global_proxy_object(broker).object());
__ Jump(&done);
}
__ bind(&convert_to_object);
__ CallBuiltin<Builtin::kToObject>(native_context_.object(),
receiver_input());
__ bind(&done);
}
int CheckDerivedConstructResult::MaxCallStackArgs() const { return 0; }
void CheckDerivedConstructResult::SetValueLocationConstraints() {
UseRegister(construct_result_input());
DefineSameAsFirst(this);
}
void CheckDerivedConstructResult::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register construct_result = ToRegister(construct_result_input());
DCHECK_EQ(construct_result, ToRegister(result()));
// If the result is an object (in the ECMA sense), we should get rid
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
// on page 74.
Label done, do_throw;
__ CompareRoot(construct_result, RootIndex::kUndefinedValue);
__ Assert(kNotEqual, AbortReason::kUnexpectedValue);
// If the result is a smi, it is *not* an object in the ECMA sense.
__ JumpIfSmi(construct_result, &do_throw, Label::Distance::kNear);
// Check if the type of the result is not an object in the ECMA sense.
__ JumpIfJSAnyIsNotPrimitive(construct_result, &done, Label::Distance::kNear);
// Throw away the result of the constructor invocation and use the
// implicit receiver as the result.
__ bind(&do_throw);
__ Jump(__ MakeDeferredCode(
[](MaglevAssembler* masm, CheckDerivedConstructResult* node) {
__ Move(kContextRegister, masm->native_context().object());
__ CallRuntime(Runtime::kThrowConstructorReturnedNonObject);
masm->DefineExceptionHandlerAndLazyDeoptPoint(node);
__ Abort(AbortReason::kUnexpectedReturnFromThrow);
},
this));
__ bind(&done);
}
int CheckConstructResult::MaxCallStackArgs() const { return 0; }
void CheckConstructResult::SetValueLocationConstraints() {
UseRegister(construct_result_input());
UseRegister(implicit_receiver_input());
DefineSameAsFirst(this);
}
void CheckConstructResult::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register construct_result = ToRegister(construct_result_input());
Register result_reg = ToRegister(result());
DCHECK_EQ(construct_result, result_reg);
// If the result is an object (in the ECMA sense), we should get rid
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
// on page 74.
Label done, use_receiver;
// If the result is undefined, we'll use the implicit receiver.
__ JumpIfRoot(construct_result, RootIndex::kUndefinedValue, &use_receiver,
Label::Distance::kNear);
// If the result is a smi, it is *not* an object in the ECMA sense.
__ JumpIfSmi(construct_result, &use_receiver, Label::Distance::kNear);
// Check if the type of the result is not an object in the ECMA sense.
__ JumpIfJSAnyIsNotPrimitive(construct_result, &done, Label::Distance::kNear);
// Throw away the result of the constructor invocation and use the
// implicit receiver as the result.
__ bind(&use_receiver);
Register implicit_receiver = ToRegister(implicit_receiver_input());
__ Move(result_reg, implicit_receiver);
__ bind(&done);
}
int CreateObjectLiteral::MaxCallStackArgs() const {
DCHECK_EQ(Runtime::FunctionForId(Runtime::kCreateObjectLiteral)->nargs, 4);
return 4;
}
void CreateObjectLiteral::SetValueLocationConstraints() {
DefineAsFixed(this, kReturnRegister0);
}
void CreateObjectLiteral::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ Move(kContextRegister, masm->native_context().object());
__ Push(feedback().vector, TaggedIndex::FromIntptr(feedback().index()),
boilerplate_descriptor().object(), Smi::FromInt(flags()));
__ CallRuntime(Runtime::kCreateObjectLiteral, 4);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int CreateShallowArrayLiteral::MaxCallStackArgs() const {
using D =
CallInterfaceDescriptorFor<Builtin::kCreateShallowArrayLiteral>::type;
return D::GetStackParameterCount();
}
void CreateShallowArrayLiteral::SetValueLocationConstraints() {
DefineAsFixed(this, kReturnRegister0);
}
void CreateShallowArrayLiteral::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kCreateShallowArrayLiteral>(
masm->native_context().object(), // context
feedback().vector, // feedback vector
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
constant_elements().object(), // constant elements
Smi::FromInt(flags()) // flags
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int CreateArrayLiteral::MaxCallStackArgs() const {
DCHECK_EQ(Runtime::FunctionForId(Runtime::kCreateArrayLiteral)->nargs, 4);
return 4;
}
void CreateArrayLiteral::SetValueLocationConstraints() {
DefineAsFixed(this, kReturnRegister0);
}
void CreateArrayLiteral::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ Move(kContextRegister, masm->native_context().object());
__ Push(feedback().vector, TaggedIndex::FromIntptr(feedback().index()),
constant_elements().object(), Smi::FromInt(flags()));
__ CallRuntime(Runtime::kCreateArrayLiteral, 4);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int CreateShallowObjectLiteral::MaxCallStackArgs() const {
using D =
CallInterfaceDescriptorFor<Builtin::kCreateShallowObjectLiteral>::type;
return D::GetStackParameterCount();
}
void CreateShallowObjectLiteral::SetValueLocationConstraints() {
DefineAsFixed(this, kReturnRegister0);
}
void CreateShallowObjectLiteral::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kCreateShallowObjectLiteral>(
masm->native_context().object(), // context
feedback().vector, // feedback vector
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
boilerplate_descriptor().object(), // desc
Smi::FromInt(flags()) // flags
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
void AllocateRaw::SetValueLocationConstraints() { DefineAsRegister(this); }
void AllocateRaw::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ Allocate(register_snapshot(), ToRegister(result()), size(),
allocation_type());
}
int CreateClosure::MaxCallStackArgs() const {
DCHECK_EQ(Runtime::FunctionForId(pretenured() ? Runtime::kNewClosure_Tenured
: Runtime::kNewClosure)
->nargs,
2);
return 2;
}
void CreateClosure::SetValueLocationConstraints() {
UseFixed(context(), kContextRegister);
DefineAsFixed(this, kReturnRegister0);
}
void CreateClosure::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Runtime::FunctionId function_id =
pretenured() ? Runtime::kNewClosure_Tenured : Runtime::kNewClosure;
__ Push(shared_function_info().object(), feedback_cell().object());
__ CallRuntime(function_id);
}
int FastCreateClosure::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kFastNewClosure>::type;
return D::GetStackParameterCount();
}
void FastCreateClosure::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kFastNewClosure>::type;
static_assert(D::HasContextParameter());
UseFixed(context(), D::ContextRegister());
DefineAsFixed(this, kReturnRegister0);
}
void FastCreateClosure::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kFastNewClosure>(
context(), // context
shared_function_info().object(), // shared function info
feedback_cell().object() // feedback cell
);
masm->DefineLazyDeoptPoint(lazy_deopt_info());
}
int CreateFunctionContext::MaxCallStackArgs() const {
if (scope_type() == FUNCTION_SCOPE) {
using D = CallInterfaceDescriptorFor<
Builtin::kFastNewFunctionContextFunction>::type;
return D::GetStackParameterCount();
} else {
using D =
CallInterfaceDescriptorFor<Builtin::kFastNewFunctionContextEval>::type;
return D::GetStackParameterCount();
}
}
void CreateFunctionContext::SetValueLocationConstraints() {
DCHECK_LE(slot_count(),
static_cast<uint32_t>(
ConstructorBuiltins::MaximumFunctionContextSlots()));
if (scope_type() == FUNCTION_SCOPE) {
using D = CallInterfaceDescriptorFor<
Builtin::kFastNewFunctionContextFunction>::type;
static_assert(D::HasContextParameter());
UseFixed(context(), D::ContextRegister());
} else {
DCHECK_EQ(scope_type(), ScopeType::EVAL_SCOPE);
using D =
CallInterfaceDescriptorFor<Builtin::kFastNewFunctionContextEval>::type;
static_assert(D::HasContextParameter());
UseFixed(context(), D::ContextRegister());
}
DefineAsFixed(this, kReturnRegister0);
}
void CreateFunctionContext::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
if (scope_type() == FUNCTION_SCOPE) {
__ CallBuiltin<Builtin::kFastNewFunctionContextFunction>(
context(), // context
scope_info().object(), // scope info
slot_count() // slots
);
} else {
__ CallBuiltin<Builtin::kFastNewFunctionContextEval>(
context(), // context
scope_info().object(), // scope info
slot_count() // slots
);
}
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int CreateRegExpLiteral::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kCreateRegExpLiteral>::type;
return D::GetStackParameterCount();
}
void CreateRegExpLiteral::SetValueLocationConstraints() {
DefineAsFixed(this, kReturnRegister0);
}
void CreateRegExpLiteral::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kCreateRegExpLiteral>(
masm->native_context().object(), // context
feedback().vector, // feedback vector
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
pattern().object(), // pattern
Smi::FromInt(flags()) // flags
);
}
int GetTemplateObject::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kGetTemplateObject>::type;
return D::GetStackParameterCount();
}
void GetTemplateObject::SetValueLocationConstraints() {
using D = GetTemplateObjectDescriptor;
UseFixed(description(), D::GetRegisterParameter(D::kDescription));
DefineAsFixed(this, kReturnRegister0);
}
void GetTemplateObject::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kGetTemplateObject>(
masm->native_context().object(), // context
shared_function_info_.object(), // shared function info
description(), // description
feedback().index(), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int HasInPrototypeChain::MaxCallStackArgs() const {
DCHECK_EQ(2, Runtime::FunctionForId(Runtime::kHasInPrototypeChain)->nargs);
return 2;
}
void HasInPrototypeChain::SetValueLocationConstraints() {
UseRegister(object());
DefineAsRegister(this);
set_temporaries_needed(2);
}
void HasInPrototypeChain::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register object_reg = ToRegister(object());
Register result_reg = ToRegister(result());
Label return_false, return_true;
ZoneLabelRef done(masm);
__ JumpIfSmi(object_reg, &return_false, Label::kNear);
// Loop through the prototype chain looking for the {prototype}.
Register map = temps.Acquire();
__ LoadMap(map, object_reg);
Label loop;
{
__ bind(&loop);
Register scratch = temps.Acquire();
// Check if we can determine the prototype directly from the {object_map}.
ZoneLabelRef if_objectisdirect(masm);
Register instance_type = scratch;
__ CompareInstanceTypeRange(map, instance_type, FIRST_TYPE,
LAST_SPECIAL_RECEIVER_TYPE);
__ JumpToDeferredIf(
kUnsignedLessThanEqual,
[](MaglevAssembler* masm, RegisterSnapshot snapshot,
Register object_reg, Register map, Register instance_type,
Register result_reg, HasInPrototypeChain* node,
ZoneLabelRef if_objectisdirect, ZoneLabelRef done) {
Label return_runtime;
// The {object_map} is a special receiver map or a primitive map,
// check if we need to use the if_objectisspecial path in the runtime.
__ JumpIfEqual(instance_type, JS_PROXY_TYPE, &return_runtime);
Register object_bitfield = instance_type;
__ LoadByte(object_bitfield,
FieldMemOperand(map, Map::kBitFieldOffset));
int mask = Map::Bits1::HasNamedInterceptorBit::kMask |
Map::Bits1::IsAccessCheckNeededBit::kMask;
__ TestInt32AndJumpIfAllClear(object_bitfield, mask,
*if_objectisdirect);
__ bind(&return_runtime);
{
snapshot.live_registers.clear(result_reg);
SaveRegisterStateForCall save_register_state(masm, snapshot);
__ Push(object_reg, node->prototype().object());
__ Move(kContextRegister, masm->native_context().object());
__ CallRuntime(Runtime::kHasInPrototypeChain, 2);
masm->DefineExceptionHandlerPoint(node);
save_register_state.DefineSafepointWithLazyDeopt(
node->lazy_deopt_info());
__ Move(result_reg, kReturnRegister0);
}
__ Jump(*done);
},
register_snapshot(), object_reg, map, instance_type, result_reg, this,
if_objectisdirect, done);
instance_type = Register::no_reg();
__ bind(*if_objectisdirect);
// Check the current {object} prototype.
Register object_prototype = scratch;
__ LoadTaggedField(object_prototype, map, Map::kPrototypeOffset);
__ JumpIfRoot(object_prototype, RootIndex::kNullValue, &return_false,
Label::kNear);
__ CompareTaggedAndJumpIf(object_prototype, prototype().object(), kEqual,
&return_true, Label::kNear);
// Continue with the prototype.
__ AssertNotSmi(object_prototype);
__ LoadMap(map, object_prototype);
__ Jump(&loop);
}
__ bind(&return_true);
__ LoadRoot(result_reg, RootIndex::kTrueValue);
__ Jump(*done, Label::kNear);
__ bind(&return_false);
__ LoadRoot(result_reg, RootIndex::kFalseValue);
__ bind(*done);
}
void DebugBreak::SetValueLocationConstraints() {}
void DebugBreak::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ DebugBreak();
}
int Abort::MaxCallStackArgs() const {
DCHECK_EQ(Runtime::FunctionForId(Runtime::kAbort)->nargs, 1);
return 1;
}
void Abort::SetValueLocationConstraints() {}
void Abort::GenerateCode(MaglevAssembler* masm, const ProcessingState& state) {
__ Push(Smi::FromInt(static_cast<int>(reason())));
__ CallRuntime(Runtime::kAbort, 1);
__ Trap();
}
void LogicalNot::SetValueLocationConstraints() {
UseAny(value());
DefineAsRegister(this);
}
void LogicalNot::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
if (v8_flags.debug_code) {
// LogicalNot expects either TrueValue or FalseValue.
Label next;
__ JumpIf(__ IsRootConstant(value(), RootIndex::kFalseValue), &next);
__ JumpIf(__ IsRootConstant(value(), RootIndex::kTrueValue), &next);
__ Abort(AbortReason::kUnexpectedValue);
__ bind(&next);
}
Label return_false, done;
__ JumpIf(__ IsRootConstant(value(), RootIndex::kTrueValue), &return_false);
__ LoadRoot(ToRegister(result()), RootIndex::kTrueValue);
__ Jump(&done);
__ bind(&return_false);
__ LoadRoot(ToRegister(result()), RootIndex::kFalseValue);
__ bind(&done);
}
int LoadNamedGeneric::MaxCallStackArgs() const {
return LoadWithVectorDescriptor::GetStackParameterCount();
}
void LoadNamedGeneric::SetValueLocationConstraints() {
using D = LoadWithVectorDescriptor;
UseFixed(context(), kContextRegister);
UseFixed(object_input(), D::GetRegisterParameter(D::kReceiver));
DefineAsFixed(this, kReturnRegister0);
}
void LoadNamedGeneric::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kLoadIC>(
context(), // context
object_input(), // receiver
name().object(), // name
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int LoadNamedFromSuperGeneric::MaxCallStackArgs() const {
return LoadWithReceiverAndVectorDescriptor::GetStackParameterCount();
}
void LoadNamedFromSuperGeneric::SetValueLocationConstraints() {
using D = LoadWithReceiverAndVectorDescriptor;
UseFixed(context(), kContextRegister);
UseFixed(receiver(), D::GetRegisterParameter(D::kReceiver));
UseFixed(lookup_start_object(),
D::GetRegisterParameter(D::kLookupStartObject));
DefineAsFixed(this, kReturnRegister0);
}
void LoadNamedFromSuperGeneric::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kLoadSuperIC>(
context(), // context
receiver(), // receiver
lookup_start_object(), // lookup start object
name().object(), // name
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int SetNamedGeneric::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kStoreIC>::type;
return D::GetStackParameterCount();
}
void SetNamedGeneric::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kStoreIC>::type;
UseFixed(context(), kContextRegister);
UseFixed(object_input(), D::GetRegisterParameter(D::kReceiver));
UseFixed(value_input(), D::GetRegisterParameter(D::kValue));
DefineAsFixed(this, kReturnRegister0);
}
void SetNamedGeneric::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kStoreIC>(
context(), // context
object_input(), // receiver
name().object(), // name
value_input(), // value
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int DefineNamedOwnGeneric::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kDefineNamedOwnIC>::type;
return D::GetStackParameterCount();
}
void DefineNamedOwnGeneric::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kDefineNamedOwnIC>::type;
UseFixed(context(), kContextRegister);
UseFixed(object_input(), D::GetRegisterParameter(D::kReceiver));
UseFixed(value_input(), D::GetRegisterParameter(D::kValue));
DefineAsFixed(this, kReturnRegister0);
}
void DefineNamedOwnGeneric::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kDefineNamedOwnIC>(
context(), // context
object_input(), // receiver
name().object(), // name
value_input(), // value
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
void UpdateJSArrayLength::SetValueLocationConstraints() {
UseRegister(object_input());
UseAndClobberRegister(index_input());
UseRegister(length_input());
}
void UpdateJSArrayLength::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
Register index = ToRegister(index_input());
Register length = ToRegister(length_input());
Label done;
if (v8_flags.debug_code) {
__ CompareObjectTypeAndAssert(object, JS_ARRAY_TYPE, kEqual,
AbortReason::kUnexpectedValue);
static_assert(Internals::IsValidSmi(FixedArray::kMaxLength),
"MaxLength not a Smi");
__ CompareInt32AndAssert(index, FixedArray::kMaxLength, kUnsignedLessThan,
AbortReason::kUnexpectedValue);
}
__ CompareInt32AndJumpIf(index, length, kUnsignedLessThan, &done);
__ IncrementInt32(index); // This cannot overflow.
__ SmiTag(index);
__ StoreTaggedSignedField(object, JSArray::kLengthOffset, index);
__ bind(&done);
}
void EnsureWritableFastElements::SetValueLocationConstraints() {
UseRegister(elements_input());
UseRegister(object_input());
set_temporaries_needed(1);
DefineSameAsFirst(this);
}
void EnsureWritableFastElements::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
Register elements = ToRegister(elements_input());
DCHECK_EQ(elements, ToRegister(result()));
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
__ EnsureWritableFastElements(register_snapshot(), elements, object, scratch);
}
void MaybeGrowAndEnsureWritableFastElements::SetValueLocationConstraints() {
UseRegister(elements_input());
UseRegister(object_input());
UseRegister(index_input());
UseRegister(elements_length_input());
if (IsSmiOrObjectElementsKind(elements_kind())) {
set_temporaries_needed(1);
}
DefineSameAsFirst(this);
}
void MaybeGrowAndEnsureWritableFastElements::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
Register elements = ToRegister(elements_input());
Register object = ToRegister(object_input());
Register index = ToRegister(index_input());
Register elements_length = ToRegister(elements_length_input());
DCHECK_EQ(elements, ToRegister(result()));
ZoneLabelRef done(masm);
__ CompareInt32AndJumpIf(
index, elements_length, kUnsignedGreaterThanEqual,
__ MakeDeferredCode(
[](MaglevAssembler* masm, ZoneLabelRef done, Register object,
Register index, Register result_reg,
MaybeGrowAndEnsureWritableFastElements* node) {
{
RegisterSnapshot snapshot = node->register_snapshot();
AddDeoptRegistersToSnapshot(&snapshot, node->eager_deopt_info());
snapshot.live_registers.clear(result_reg);
snapshot.live_tagged_registers.clear(result_reg);
SaveRegisterStateForCall save_register_state(masm, snapshot);
using D = GrowArrayElementsDescriptor;
if (index == D::GetRegisterParameter(D::kObject)) {
// That implies that the first parameter move will clobber the
// index value. So we use the result register as temporary.
// TODO(leszeks): Use parallel moves to resolve cases like this.
__ SmiTag(result_reg, index);
index = result_reg;
} else {
__ SmiTag(index);
}
if (IsDoubleElementsKind(node->elements_kind())) {
__ CallBuiltin<Builtin::kGrowFastDoubleElements>(object, index);
} else {
__ CallBuiltin<Builtin::kGrowFastSmiOrObjectElements>(object,
index);
}
save_register_state.DefineSafepoint();
__ Move(result_reg, kReturnRegister0);
}
__ EmitEagerDeoptIfSmi(node, result_reg,
DeoptimizeReason::kCouldNotGrowElements);
__ Jump(*done);
},
done, object, index, elements, this));
if (IsSmiOrObjectElementsKind(elements_kind())) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
__ EnsureWritableFastElements(register_snapshot(), elements, object,
scratch);
}
__ bind(*done);
}
int SetKeyedGeneric::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kKeyedStoreIC>::type;
return D::GetStackParameterCount();
}
void SetKeyedGeneric::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kKeyedStoreIC>::type;
UseFixed(context(), kContextRegister);
UseFixed(object_input(), D::GetRegisterParameter(D::kReceiver));
UseFixed(key_input(), D::GetRegisterParameter(D::kName));
UseFixed(value_input(), D::GetRegisterParameter(D::kValue));
DefineAsFixed(this, kReturnRegister0);
}
void SetKeyedGeneric::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kKeyedStoreIC>(
context(), // context
object_input(), // receiver
key_input(), // name
value_input(), // value
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int DefineKeyedOwnGeneric::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kDefineKeyedOwnIC>::type;
return D::GetStackParameterCount();
}
void DefineKeyedOwnGeneric::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kDefineKeyedOwnIC>::type;
UseFixed(context(), kContextRegister);
UseFixed(object_input(), D::GetRegisterParameter(D::kReceiver));
UseFixed(key_input(), D::GetRegisterParameter(D::kName));
UseFixed(value_input(), D::GetRegisterParameter(D::kValue));
UseFixed(flags_input(), D::GetRegisterParameter(D::kFlags));
DefineAsFixed(this, kReturnRegister0);
}
void DefineKeyedOwnGeneric::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kDefineKeyedOwnIC>(
context(), // context
object_input(), // receiver
key_input(), // name
value_input(), // value
flags_input(), // flags
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int StoreInArrayLiteralGeneric::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kStoreInArrayLiteralIC>::type;
return D::GetStackParameterCount();
}
void StoreInArrayLiteralGeneric::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kStoreInArrayLiteralIC>::type;
UseFixed(context(), kContextRegister);
UseFixed(object_input(), D::GetRegisterParameter(D::kReceiver));
UseFixed(name_input(), D::GetRegisterParameter(D::kName));
UseFixed(value_input(), D::GetRegisterParameter(D::kValue));
DefineAsFixed(this, kReturnRegister0);
}
void StoreInArrayLiteralGeneric::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kStoreInArrayLiteralIC>(
context(), // context
object_input(), // receiver
name_input(), // name
value_input(), // value
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
void GeneratorRestoreRegister::SetValueLocationConstraints() {
UseRegister(array_input());
UseRegister(stale_input());
DefineAsRegister(this);
set_temporaries_needed(1);
}
void GeneratorRestoreRegister::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register temp = temps.Acquire();
Register array = ToRegister(array_input());
Register stale = ToRegister(stale_input());
Register result_reg = ToRegister(result());
// The input and the output can alias, if that happens we use a temporary
// register and a move at the end.
Register value = (array == result_reg ? temp : result_reg);
// Loads the current value in the generator register file.
__ LoadTaggedField(value, array, FixedArray::OffsetOfElementAt(index()));
// And trashs it with StaleRegisterConstant.
DCHECK(stale_input().node()->Is<RootConstant>());
__ StoreTaggedFieldNoWriteBarrier(
array, FixedArray::OffsetOfElementAt(index()), stale);
if (value != result_reg) {
__ Move(result_reg, value);
}
}
int GeneratorStore::MaxCallStackArgs() const {
return WriteBarrierDescriptor::GetStackParameterCount();
}
void GeneratorStore::SetValueLocationConstraints() {
UseAny(context_input());
UseRegister(generator_input());
for (int i = 0; i < num_parameters_and_registers(); i++) {
UseAny(parameters_and_registers(i));
}
RequireSpecificTemporary(WriteBarrierDescriptor::ObjectRegister());
RequireSpecificTemporary(WriteBarrierDescriptor::SlotAddressRegister());
}
void GeneratorStore::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register generator = ToRegister(generator_input());
Register array = WriteBarrierDescriptor::ObjectRegister();
__ LoadTaggedField(array, generator,
JSGeneratorObject::kParametersAndRegistersOffset);
RegisterSnapshot register_snapshot_during_store = register_snapshot();
// Include the array and generator registers in the register snapshot while
// storing parameters and registers, to avoid the write barrier clobbering
// them.
register_snapshot_during_store.live_registers.set(array);
register_snapshot_during_store.live_tagged_registers.set(array);
register_snapshot_during_store.live_registers.set(generator);
register_snapshot_during_store.live_tagged_registers.set(generator);
for (int i = 0; i < num_parameters_and_registers(); i++) {
// Use WriteBarrierDescriptor::SlotAddressRegister() as the temporary for
// the value -- it'll be clobbered by StoreTaggedFieldWithWriteBarrier since
// it's not in the register snapshot, but that's ok, and a clobberable value
// register lets the write barrier emit slightly better code.
Input value_input = parameters_and_registers(i);
Register value = __ FromAnyToRegister(
value_input, WriteBarrierDescriptor::SlotAddressRegister());
// Include the value register in the live set, in case it is used by future
// inputs.
register_snapshot_during_store.live_registers.set(value);
register_snapshot_during_store.live_tagged_registers.set(value);
__ StoreTaggedFieldWithWriteBarrier(
array, FixedArray::OffsetOfElementAt(i), value,
register_snapshot_during_store,
value_input.node()->decompresses_tagged_result()
? MaglevAssembler::kValueIsDecompressed
: MaglevAssembler::kValueIsCompressed,
MaglevAssembler::kValueCanBeSmi);
}
__ StoreTaggedSignedField(generator, JSGeneratorObject::kContinuationOffset,
Smi::FromInt(suspend_id()));
__ StoreTaggedSignedField(generator,
JSGeneratorObject::kInputOrDebugPosOffset,
Smi::FromInt(bytecode_offset()));
// Use WriteBarrierDescriptor::SlotAddressRegister() as the scratch
// register, see comment above. At this point we no longer need to preserve
// the array or generator registers, so use the original register snapshot.
Register context = __ FromAnyToRegister(
context_input(), WriteBarrierDescriptor::SlotAddressRegister());
__ StoreTaggedFieldWithWriteBarrier(
generator, JSGeneratorObject::kContextOffset, context,
register_snapshot(),
context_input().node()->decompresses_tagged_result()
? MaglevAssembler::kValueIsDecompressed
: MaglevAssembler::kValueIsCompressed,
MaglevAssembler::kValueCannotBeSmi);
}
int GetKeyedGeneric::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kKeyedLoadIC>::type;
return D::GetStackParameterCount();
}
void GetKeyedGeneric::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kKeyedLoadIC>::type;
UseFixed(context(), kContextRegister);
UseFixed(object_input(), D::GetRegisterParameter(D::kReceiver));
UseFixed(key_input(), D::GetRegisterParameter(D::kName));
DefineAsFixed(this, kReturnRegister0);
}
void GetKeyedGeneric::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kKeyedLoadIC>(
context(), // context
object_input(), // receiver
key_input(), // name
TaggedIndex::FromIntptr(feedback().index()), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
void Int32ToNumber::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
}
void Int32ToNumber::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(result());
Register value = ToRegister(input());
ZoneLabelRef done(masm);
MaglevAssembler::ScratchRegisterScope temps(masm);
bool input_output_alias = (object == value);
Register res = object;
if (input_output_alias) {
res = temps.GetDefaultScratchRegister();
}
__ SmiTagInt32AndJumpIfFail(
res, value,
__ MakeDeferredCode(
[](MaglevAssembler* masm, Register object, Register value,
ZoneLabelRef done, Int32ToNumber* node) {
MaglevAssembler::ScratchRegisterScope temps(masm);
DoubleRegister double_value =
temps.GetDefaultScratchDoubleRegister();
__ Int32ToDouble(double_value, value);
__ AllocateHeapNumber(node->register_snapshot(), object,
double_value);
__ Jump(*done);
},
object, value, done, this));
if (input_output_alias) {
__ Move(object, res);
}
__ bind(*done);
}
void Uint32ToNumber::SetValueLocationConstraints() {
UseRegister(input());
#ifdef V8_TARGET_ARCH_X64
// We emit slightly more efficient code if result is the same as input.
DefineSameAsFirst(this);
#else
DefineAsRegister(this);
#endif
}
void Uint32ToNumber::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
ZoneLabelRef done(masm);
Register value = ToRegister(input());
Register object = ToRegister(result());
__ SmiTagUint32AndJumpIfFail(
object, value,
__ MakeDeferredCode(
[](MaglevAssembler* masm, Register object, Register value,
ZoneLabelRef done, Uint32ToNumber* node) {
MaglevAssembler::ScratchRegisterScope temps(masm);
DoubleRegister double_value =
temps.GetDefaultScratchDoubleRegister();
__ Uint32ToDouble(double_value, value);
__ AllocateHeapNumber(node->register_snapshot(), object,
double_value);
__ Jump(*done);
},
object, value, done, this));
__ bind(*done);
}
void Float64ToTagged::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
}
void Float64ToTagged::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
DoubleRegister value = ToDoubleRegister(input());
Register object = ToRegister(result());
Label box, done;
if (canonicalize_smi()) {
__ TryTruncateDoubleToInt32(object, value, &box);
__ SmiTagInt32AndJumpIfFail(object, &box);
__ Jump(&done, Label::kNear);
__ bind(&box);
}
__ AllocateHeapNumber(register_snapshot(), object, value);
if (canonicalize_smi()) {
__ bind(&done);
}
}
void HoleyFloat64ToTagged::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
}
void HoleyFloat64ToTagged::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
ZoneLabelRef done(masm);
DoubleRegister value = ToDoubleRegister(input());
Register object = ToRegister(result());
Label box;
if (canonicalize_smi()) {
__ TryTruncateDoubleToInt32(object, value, &box);
__ SmiTagInt32AndJumpIfFail(object, &box);
__ Jump(*done, Label::kNear);
__ bind(&box);
}
// Using return as scratch register.
__ JumpIfHoleNan(
value, ToRegister(result()),
__ MakeDeferredCode(
[](MaglevAssembler* masm, Register object, ZoneLabelRef done) {
// TODO(leszeks): Evaluate whether this is worth deferring.
__ LoadRoot(object, RootIndex::kUndefinedValue);
__ Jump(*done);
},
object, done));
__ AllocateHeapNumber(register_snapshot(), object, value);
__ bind(*done);
}
void Float64Round::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
if (kind_ == Kind::kNearest) {
set_double_temporaries_needed(1);
}
}
void CheckedSmiTagFloat64::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
}
void CheckedSmiTagFloat64::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
DoubleRegister value = ToDoubleRegister(input());
Register object = ToRegister(result());
__ TryTruncateDoubleToInt32(
object, value, __ GetDeoptLabel(this, DeoptimizeReason::kNotASmi));
__ SmiTagInt32AndJumpIfFail(
object, __ GetDeoptLabel(this, DeoptimizeReason::kNotASmi));
}
void StoreFloat64::SetValueLocationConstraints() {
UseRegister(object_input());
UseRegister(value_input());
}
void StoreFloat64::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
DoubleRegister value = ToDoubleRegister(value_input());
__ AssertNotSmi(object);
__ StoreFloat64(FieldMemOperand(object, offset()), value);
}
void StoreTaggedFieldNoWriteBarrier::SetValueLocationConstraints() {
UseRegister(object_input());
UseRegister(value_input());
}
void StoreTaggedFieldNoWriteBarrier::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
Register object = ToRegister(object_input());
Register value = ToRegister(value_input());
__ AssertNotSmi(object);
__ StoreTaggedFieldNoWriteBarrier(object, offset(), value);
}
int StringAt::MaxCallStackArgs() const {
DCHECK_EQ(Runtime::FunctionForId(Runtime::kStringCharCodeAt)->nargs, 2);
return std::max(2, AllocateDescriptor::GetStackParameterCount());
}
void StringAt::SetValueLocationConstraints() {
UseAndClobberRegister(string_input());
UseAndClobberRegister(index_input());
DefineAsRegister(this);
set_temporaries_needed(1);
}
void StringAt::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
Register result_string = ToRegister(result());
Register string = ToRegister(string_input());
Register index = ToRegister(index_input());
Register char_code = string;
ZoneLabelRef done(masm);
Label cached_one_byte_string;
RegisterSnapshot save_registers = register_snapshot();
__ StringCharCodeOrCodePointAt(
BuiltinStringPrototypeCharCodeOrCodePointAt::kCharCodeAt, save_registers,
char_code, string, index, scratch, &cached_one_byte_string);
__ StringFromCharCode(save_registers, &cached_one_byte_string, result_string,
char_code, scratch);
}
void StringLength::SetValueLocationConstraints() {
UseRegister(object_input());
DefineAsRegister(this);
}
void StringLength::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ StringLength(ToRegister(result()), ToRegister(object_input()));
}
void StringConcat::SetValueLocationConstraints() {
using D = StringAdd_CheckNoneDescriptor;
UseFixed(lhs(), D::GetRegisterParameter(D::kLeft));
UseFixed(rhs(), D::GetRegisterParameter(D::kRight));
DefineAsFixed(this, kReturnRegister0);
}
void StringConcat::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kStringAdd_CheckNone>(
masm->native_context().object(), // context
lhs(), // left
rhs() // right
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
DCHECK_EQ(kReturnRegister0, ToRegister(result()));
}
void StringEqual::SetValueLocationConstraints() {
using D = StringEqualDescriptor;
UseFixed(lhs(), D::GetRegisterParameter(D::kLeft));
UseFixed(rhs(), D::GetRegisterParameter(D::kRight));
set_temporaries_needed(1);
RequireSpecificTemporary(D::GetRegisterParameter(D::kLength));
DefineAsFixed(this, kReturnRegister0);
}
void StringEqual::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
using D = StringEqualDescriptor;
Label done, if_equal, if_not_equal;
Register left = ToRegister(lhs());
Register right = ToRegister(rhs());
MaglevAssembler::ScratchRegisterScope temps(masm);
Register left_length = temps.Acquire();
Register right_length = D::GetRegisterParameter(D::kLength);
__ CmpTagged(left, right);
__ JumpIf(kEqual, &if_equal,
// Debug checks in StringLength can make this jump too long for a
// near jump.
v8_flags.debug_code ? Label::kFar : Label::kNear);
__ StringLength(left_length, left);
__ StringLength(right_length, right);
__ CompareInt32AndJumpIf(left_length, right_length, kNotEqual, &if_not_equal,
Label::Distance::kNear);
// The inputs are already in the right registers. The |left| and |right|
// inputs were required to come in in the left/right inputs of the builtin,
// and the |length| input of the builtin is where we loaded the length of the
// right string (which matches the length of the left string when we get
// here).
DCHECK_EQ(right_length, D::GetRegisterParameter(D::kLength));
__ CallBuiltin<Builtin::kStringEqual>(lhs(), rhs(),
D::GetRegisterParameter(D::kLength));
masm->DefineLazyDeoptPoint(this->lazy_deopt_info());
__ Jump(&done, Label::Distance::kNear);
__ bind(&if_equal);
__ LoadRoot(ToRegister(result()), RootIndex::kTrueValue);
__ Jump(&done, Label::Distance::kNear);
__ bind(&if_not_equal);
__ LoadRoot(ToRegister(result()), RootIndex::kFalseValue);
__ bind(&done);
}
void TaggedEqual::SetValueLocationConstraints() {
UseRegister(lhs());
UseRegister(rhs());
DefineAsRegister(this);
}
void TaggedEqual::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Label done, if_equal;
__ CmpTagged(ToRegister(lhs()), ToRegister(rhs()));
__ JumpIf(kEqual, &if_equal, Label::Distance::kNear);
__ LoadRoot(ToRegister(result()), RootIndex::kFalseValue);
__ Jump(&done);
__ bind(&if_equal);
__ LoadRoot(ToRegister(result()), RootIndex::kTrueValue);
__ bind(&done);
}
void TaggedNotEqual::SetValueLocationConstraints() {
UseRegister(lhs());
UseRegister(rhs());
DefineAsRegister(this);
}
void TaggedNotEqual::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Label done, if_equal;
__ CmpTagged(ToRegister(lhs()), ToRegister(rhs()));
__ JumpIf(kEqual, &if_equal, Label::Distance::kNear);
__ LoadRoot(ToRegister(result()), RootIndex::kTrueValue);
__ Jump(&done);
__ bind(&if_equal);
__ LoadRoot(ToRegister(result()), RootIndex::kFalseValue);
__ bind(&done);
}
int TestInstanceOf::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kInstanceOf_WithFeedback>::type;
return D::GetStackParameterCount();
}
void TestInstanceOf::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kInstanceOf_WithFeedback>::type;
UseFixed(context(), kContextRegister);
UseFixed(object(), D::GetRegisterParameter(D::kLeft));
UseFixed(callable(), D::GetRegisterParameter(D::kRight));
DefineAsFixed(this, kReturnRegister0);
}
void TestInstanceOf::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kInstanceOf_WithFeedback>(
context(), // context
object(), // left
callable(), // right
feedback().index(), // feedback slot
feedback().vector // feedback vector
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
void TestTypeOf::SetValueLocationConstraints() {
UseRegister(value());
DefineAsRegister(this);
#ifdef V8_TARGET_ARCH_ARM
set_temporaries_needed(1);
#endif
}
void TestTypeOf::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(value());
Label is_true, is_false, done;
__ TestTypeOf(object, literal_, &is_true, Label::Distance::kNear, true,
&is_false, Label::Distance::kNear, false);
// Fallthrough into true.
__ bind(&is_true);
__ LoadRoot(ToRegister(result()), RootIndex::kTrueValue);
__ Jump(&done, Label::Distance::kNear);
__ bind(&is_false);
__ LoadRoot(ToRegister(result()), RootIndex::kFalseValue);
__ bind(&done);
}
void ToBoolean::SetValueLocationConstraints() {
UseRegister(value());
DefineAsRegister(this);
}
void ToBoolean::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(value());
Register return_value = ToRegister(result());
Label done;
ZoneLabelRef object_is_true(masm), object_is_false(masm);
// TODO(leszeks): We're likely to be calling this on an existing boolean --
// maybe that's a case we should fast-path here and re-use that boolean value?
__ ToBoolean(object, check_type(), object_is_true, object_is_false, true);
__ bind(*object_is_true);
__ LoadRoot(return_value, RootIndex::kTrueValue);
__ Jump(&done);
__ bind(*object_is_false);
__ LoadRoot(return_value, RootIndex::kFalseValue);
__ bind(&done);
}
void ToBooleanLogicalNot::SetValueLocationConstraints() {
UseRegister(value());
DefineAsRegister(this);
}
void ToBooleanLogicalNot::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(value());
Register return_value = ToRegister(result());
Label done;
ZoneLabelRef object_is_true(masm), object_is_false(masm);
__ ToBoolean(object, check_type(), object_is_true, object_is_false, true);
__ bind(*object_is_true);
__ LoadRoot(return_value, RootIndex::kFalseValue);
__ Jump(&done);
__ bind(*object_is_false);
__ LoadRoot(return_value, RootIndex::kTrueValue);
__ bind(&done);
}
int ToName::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kToName>::type;
return D::GetStackParameterCount();
}
void ToName::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kToName>::type;
UseFixed(context(), kContextRegister);
UseFixed(value_input(), D::GetRegisterParameter(D::kInput));
DefineAsFixed(this, kReturnRegister0);
}
void ToName::GenerateCode(MaglevAssembler* masm, const ProcessingState& state) {
__ CallBuiltin<Builtin::kToName>(context(), // context
value_input() // input
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int ToNumberOrNumeric::MaxCallStackArgs() const {
return TypeConversionDescriptor::GetStackParameterCount();
}
void ToNumberOrNumeric::SetValueLocationConstraints() {
UseRegister(value_input());
set_temporaries_needed(1);
DefineAsRegister(this);
}
void ToNumberOrNumeric::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
ZoneLabelRef done(masm);
Label move_and_return;
Register object = ToRegister(value_input());
Register result_reg = ToRegister(result());
__ JumpIfSmi(object, &move_and_return, Label::kNear);
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
__ CompareMapWithRoot(object, RootIndex::kHeapNumberMap, scratch);
__ JumpToDeferredIf(
kNotEqual,
[](MaglevAssembler* masm, Object::Conversion mode, Register object,
Register result_reg, ToNumberOrNumeric* node, ZoneLabelRef done) {
{
RegisterSnapshot snapshot = node->register_snapshot();
snapshot.live_registers.clear(result_reg);
SaveRegisterStateForCall save_register_state(masm, snapshot);
switch (mode) {
case Object::Conversion::kToNumber:
__ CallBuiltin<Builtin::kToNumber>(
masm->native_context().object(), object);
break;
case Object::Conversion::kToNumeric:
__ CallBuiltin<Builtin::kToNumeric>(
masm->native_context().object(), object);
break;
}
masm->DefineExceptionHandlerPoint(node);
save_register_state.DefineSafepointWithLazyDeopt(
node->lazy_deopt_info());
__ Move(result_reg, kReturnRegister0);
}
__ Jump(*done);
},
mode(), object, result_reg, this, done);
__ bind(&move_and_return);
__ Move(result_reg, object);
__ bind(*done);
}
int ToObject::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kToObject>::type;
return D::GetStackParameterCount();
}
void ToObject::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kToObject>::type;
UseFixed(context(), kContextRegister);
UseFixed(value_input(), D::GetRegisterParameter(D::kInput));
DefineAsFixed(this, kReturnRegister0);
}
void ToObject::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register value = ToRegister(value_input());
Label call_builtin, done;
// Avoid the builtin call if {value} is a JSReceiver.
if (check_type() == CheckType::kOmitHeapObjectCheck) {
__ AssertNotSmi(value);
} else {
__ JumpIfSmi(value, &call_builtin, Label::Distance::kNear);
}
__ JumpIfJSAnyIsNotPrimitive(value, &done, Label::Distance::kNear);
__ bind(&call_builtin);
__ CallBuiltin<Builtin::kToObject>(context(), // context
value_input() // input
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
__ bind(&done);
}
int ToString::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kToString>::type;
return D::GetStackParameterCount();
}
void ToString::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kToString>::type;
UseFixed(context(), kContextRegister);
UseFixed(value_input(), D::GetRegisterParameter(D::kO));
DefineAsFixed(this, kReturnRegister0);
}
void ToString::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register value = ToRegister(value_input());
Label call_builtin, done;
// Avoid the builtin call if {value} is a string.
__ JumpIfSmi(value, &call_builtin, Label::Distance::kNear);
__ CompareObjectTypeAndJumpIf(value, FIRST_NONSTRING_TYPE, kUnsignedLessThan,
&done, Label::Distance::kNear);
if (mode() == kConvertSymbol) {
__ CompareObjectTypeAndJumpIf(value, SYMBOL_TYPE, kNotEqual, &call_builtin,
Label::Distance::kNear);
__ Push(value);
__ CallRuntime(Runtime::kSymbolDescriptiveString, 1);
__ Jump(&done, Label::kNear);
}
__ bind(&call_builtin);
__ CallBuiltin<Builtin::kToString>(context(), // context
value_input() // input
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
__ bind(&done);
}
void NumberToString::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kNumberToString>::type;
UseFixed(value_input(), D::GetRegisterParameter(D::kInput));
DefineAsFixed(this, kReturnRegister0);
}
void NumberToString::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kNumberToString>(value_input());
masm->DefineLazyDeoptPoint(this->lazy_deopt_info());
}
int ThrowReferenceErrorIfHole::MaxCallStackArgs() const { return 1; }
void ThrowReferenceErrorIfHole::SetValueLocationConstraints() {
UseAny(value());
}
void ThrowReferenceErrorIfHole::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ JumpToDeferredIf(
__ IsRootConstant(value(), RootIndex::kTheHoleValue),
[](MaglevAssembler* masm, ThrowReferenceErrorIfHole* node) {
__ Push(node->name().object());
__ Move(kContextRegister, masm->native_context().object());
__ CallRuntime(Runtime::kThrowAccessedUninitializedVariable, 1);
masm->DefineExceptionHandlerAndLazyDeoptPoint(node);
__ Abort(AbortReason::kUnexpectedReturnFromThrow);
},
this);
}
int ThrowSuperNotCalledIfHole::MaxCallStackArgs() const { return 0; }
void ThrowSuperNotCalledIfHole::SetValueLocationConstraints() {
UseAny(value());
}
void ThrowSuperNotCalledIfHole::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ JumpToDeferredIf(
__ IsRootConstant(value(), RootIndex::kTheHoleValue),
[](MaglevAssembler* masm, ThrowSuperNotCalledIfHole* node) {
__ Move(kContextRegister, masm->native_context().object());
__ CallRuntime(Runtime::kThrowSuperNotCalled, 0);
masm->DefineExceptionHandlerAndLazyDeoptPoint(node);
__ Abort(AbortReason::kUnexpectedReturnFromThrow);
},
this);
}
int ThrowSuperAlreadyCalledIfNotHole::MaxCallStackArgs() const { return 0; }
void ThrowSuperAlreadyCalledIfNotHole::SetValueLocationConstraints() {
UseAny(value());
}
void ThrowSuperAlreadyCalledIfNotHole::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
__ JumpToDeferredIf(
NegateCondition(__ IsRootConstant(value(), RootIndex::kTheHoleValue)),
[](MaglevAssembler* masm, ThrowSuperAlreadyCalledIfNotHole* node) {
__ Move(kContextRegister, masm->native_context().object());
__ CallRuntime(Runtime::kThrowSuperAlreadyCalledError, 0);
masm->DefineExceptionHandlerAndLazyDeoptPoint(node);
__ Abort(AbortReason::kUnexpectedReturnFromThrow);
},
this);
}
int ThrowIfNotCallable::MaxCallStackArgs() const { return 1; }
void ThrowIfNotCallable::SetValueLocationConstraints() {
UseRegister(value());
set_temporaries_needed(1);
}
void ThrowIfNotCallable::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Label* if_not_callable = __ MakeDeferredCode(
[](MaglevAssembler* masm, ThrowIfNotCallable* node) {
__ Push(node->value());
__ Move(kContextRegister, masm->native_context().object());
__ CallRuntime(Runtime::kThrowCalledNonCallable, 1);
masm->DefineExceptionHandlerAndLazyDeoptPoint(node);
__ Abort(AbortReason::kUnexpectedReturnFromThrow);
},
this);
Register value_reg = ToRegister(value());
__ JumpIfSmi(value_reg, if_not_callable);
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
__ LoadMap(scratch, value_reg);
static_assert(Map::kBitFieldOffsetEnd + 1 - Map::kBitFieldOffset == 1);
__ LoadUnsignedField(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset),
1);
__ TestInt32AndJumpIfAllClear(scratch, Map::Bits1::IsCallableBit::kMask,
if_not_callable);
}
int ThrowIfNotSuperConstructor::MaxCallStackArgs() const { return 2; }
void ThrowIfNotSuperConstructor::SetValueLocationConstraints() {
UseRegister(constructor());
UseRegister(function());
set_temporaries_needed(1);
}
void ThrowIfNotSuperConstructor::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
__ LoadMap(scratch, ToRegister(constructor()));
static_assert(Map::kBitFieldOffsetEnd + 1 - Map::kBitFieldOffset == 1);
__ LoadUnsignedField(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset),
1);
__ TestInt32AndJumpIfAllClear(
scratch, Map::Bits1::IsConstructorBit::kMask,
__ MakeDeferredCode(
[](MaglevAssembler* masm, ThrowIfNotSuperConstructor* node) {
__ Push(ToRegister(node->constructor()),
ToRegister(node->function()));
__ Move(kContextRegister, masm->native_context().object());
__ CallRuntime(Runtime::kThrowNotSuperConstructor, 2);
masm->DefineExceptionHandlerAndLazyDeoptPoint(node);
__ Abort(AbortReason::kUnexpectedReturnFromThrow);
},
this));
}
void TruncateUint32ToInt32::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
}
void TruncateUint32ToInt32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
// No code emitted -- as far as the machine is concerned, int32 is uint32.
DCHECK_EQ(ToRegister(input()), ToRegister(result()));
}
void TruncateFloat64ToInt32::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
}
void TruncateFloat64ToInt32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ TruncateDoubleToInt32(ToRegister(result()), ToDoubleRegister(input()));
}
void CheckedTruncateFloat64ToInt32::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
}
void CheckedTruncateFloat64ToInt32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ TryTruncateDoubleToInt32(
ToRegister(result()), ToDoubleRegister(input()),
__ GetDeoptLabel(this, DeoptimizeReason::kNotInt32));
}
void CheckedTruncateFloat64ToUint32::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
}
void CheckedTruncateFloat64ToUint32::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
__ TryTruncateDoubleToUint32(
ToRegister(result()), ToDoubleRegister(input()),
__ GetDeoptLabel(this, DeoptimizeReason::kNotUint32));
}
void UnsafeTruncateFloat64ToInt32::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
}
void UnsafeTruncateFloat64ToInt32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
#ifdef DEBUG
Label fail, start;
__ Jump(&start);
__ bind(&fail);
__ Abort(AbortReason::kFloat64IsNotAInt32);
__ bind(&start);
__ TryTruncateDoubleToInt32(ToRegister(result()), ToDoubleRegister(input()),
&fail);
#else
// TODO(dmercadier): TruncateDoubleToInt32 does additional work when the
// double doesn't fit in a 32-bit integer. This is not necessary for
// UnsafeTruncateFloat64ToInt32 (since we statically know that it the double
// fits in a 32-bit int) and could be instead just a Cvttsd2si (x64) or Fcvtzs
// (arm64).
__ TruncateDoubleToInt32(ToRegister(result()), ToDoubleRegister(input()));
#endif
}
void CheckedUint32ToInt32::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
}
void CheckedUint32ToInt32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register input_reg = ToRegister(input());
Label* fail = __ GetDeoptLabel(this, DeoptimizeReason::kNotInt32);
__ CompareInt32AndJumpIf(input_reg, 0, kLessThan, fail);
}
void UnsafeTruncateUint32ToInt32::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
}
void UnsafeTruncateUint32ToInt32::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
#ifdef DEBUG
Register input_reg = ToRegister(input());
__ CompareInt32AndAssert(input_reg, 0, kGreaterThanEqual,
AbortReason::kUint32IsNotAInt32);
#endif
// No code emitted -- as far as the machine is concerned, int32 is uint32.
DCHECK_EQ(ToRegister(input()), ToRegister(result()));
}
void Int32ToUint8Clamped::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
}
void Int32ToUint8Clamped::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register value = ToRegister(input());
Register result_reg = ToRegister(result());
DCHECK_EQ(value, result_reg);
Label min, done;
__ CompareInt32AndJumpIf(value, 0, kLessThanEqual, &min);
__ CompareInt32AndJumpIf(value, 255, kLessThanEqual, &done);
__ Move(result_reg, 255);
__ Jump(&done, Label::Distance::kNear);
__ bind(&min);
__ Move(result_reg, 0);
__ bind(&done);
}
void Uint32ToUint8Clamped::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
}
void Uint32ToUint8Clamped::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register value = ToRegister(input());
DCHECK_EQ(value, ToRegister(result()));
Label done;
__ CompareInt32AndJumpIf(value, 255, kUnsignedLessThanEqual, &done,
Label::Distance::kNear);
__ Move(value, 255);
__ bind(&done);
}
void Float64ToUint8Clamped::SetValueLocationConstraints() {
UseRegister(input());
DefineAsRegister(this);
}
void Float64ToUint8Clamped::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
DoubleRegister value = ToDoubleRegister(input());
Register result_reg = ToRegister(result());
Label min, max, done;
__ ToUint8Clamped(result_reg, value, &min, &max, &done);
__ bind(&min);
__ Move(result_reg, 0);
__ Jump(&done, Label::Distance::kNear);
__ bind(&max);
__ Move(result_reg, 255);
__ bind(&done);
}
void CheckNumber::SetValueLocationConstraints() {
UseRegister(receiver_input());
}
void CheckNumber::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Label done;
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.GetDefaultScratchRegister();
Register value = ToRegister(receiver_input());
// If {value} is a Smi or a HeapNumber, we're done.
__ JumpIfSmi(value, &done, Label::Distance::kNear);
if (mode() == Object::Conversion::kToNumeric) {
__ LoadMap(scratch, value);
__ CompareRoot(scratch, RootIndex::kHeapNumberMap);
// Jump to done if it is a HeapNumber.
__ JumpIf(kEqual, &done, Label::Distance::kNear);
// Check if it is a BigInt.
__ CompareRoot(scratch, RootIndex::kBigIntMap);
} else {
__ CompareMapWithRoot(value, RootIndex::kHeapNumberMap, scratch);
}
__ EmitEagerDeoptIf(kNotEqual, DeoptimizeReason::kNotANumber, this);
__ bind(&done);
}
void CheckedInternalizedString::SetValueLocationConstraints() {
UseRegister(object_input());
DefineSameAsFirst(this);
}
void CheckedInternalizedString::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
MaglevAssembler::ScratchRegisterScope temps(masm);
Register instance_type = temps.GetDefaultScratchRegister();
if (check_type() == CheckType::kOmitHeapObjectCheck) {
__ AssertNotSmi(object);
} else {
__ EmitEagerDeoptIfSmi(this, object, DeoptimizeReason::kWrongMap);
}
__ LoadInstanceType(instance_type, object);
__ RecordComment("Test IsInternalizedString");
// Go to the slow path if this is a non-string, or a non-internalised string.
static_assert((kStringTag | kInternalizedTag) == 0);
ZoneLabelRef done(masm);
__ TestInt32AndJumpIfAnySet(
instance_type, kIsNotStringMask | kIsNotInternalizedMask,
__ MakeDeferredCode(
[](MaglevAssembler* masm, ZoneLabelRef done,
CheckedInternalizedString* node, Register object,
Register instance_type) {
__ RecordComment("Deferred Test IsThinString");
// Deopt if this isn't a string.
__ TestInt32AndJumpIfAnySet(
instance_type, kIsNotStringMask,
__ GetDeoptLabel(node, DeoptimizeReason::kWrongMap));
// Deopt if this isn't a thin string.
static_assert(base::bits::CountPopulation(kThinStringTagBit) == 1);
__ TestInt32AndJumpIfAllClear(
instance_type, kThinStringTagBit,
__ GetDeoptLabel(node, DeoptimizeReason::kWrongMap));
// Load internalized string from thin string.
__ LoadTaggedField(object, object, ThinString::kActualOffset);
if (v8_flags.debug_code) {
__ RecordComment("DCHECK IsInternalizedString");
Label checked;
__ LoadInstanceType(instance_type, object);
__ TestInt32AndJumpIfAllClear(
instance_type, kIsNotStringMask | kIsNotInternalizedMask,
&checked);
__ Abort(AbortReason::kUnexpectedValue);
__ bind(&checked);
}
__ Jump(*done);
},
done, this, object, instance_type));
__ bind(*done);
}
void CheckedNumberToUint8Clamped::SetValueLocationConstraints() {
UseRegister(input());
DefineSameAsFirst(this);
set_temporaries_needed(1);
set_double_temporaries_needed(1);
}
void CheckedNumberToUint8Clamped::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register value = ToRegister(input());
Register result_reg = ToRegister(result());
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
DoubleRegister double_value = temps.AcquireDouble();
Label is_not_smi, min, max, done;
// Check if Smi.
__ JumpIfNotSmi(value, &is_not_smi);
// If Smi, convert to Int32.
__ SmiToInt32(value);
// Clamp.
__ CompareInt32AndJumpIf(value, 0, kLessThanEqual, &min);
__ CompareInt32AndJumpIf(value, 255, kGreaterThanEqual, &max);
__ Jump(&done);
__ bind(&is_not_smi);
// Check if HeapNumber, deopt otherwise.
__ CompareMapWithRoot(value, RootIndex::kHeapNumberMap, scratch);
__ EmitEagerDeoptIf(kNotEqual, DeoptimizeReason::kNotANumber, this);
// If heap number, get double value.
__ LoadHeapNumberValue(double_value, value);
// Clamp.
__ ToUint8Clamped(value, double_value, &min, &max, &done);
__ bind(&min);
__ Move(result_reg, 0);
__ Jump(&done, Label::Distance::kNear);
__ bind(&max);
__ Move(result_reg, 255);
__ bind(&done);
}
void StoreFixedArrayElementWithWriteBarrier::SetValueLocationConstraints() {
UseRegister(elements_input());
UseRegister(index_input());
UseRegister(value_input());
RequireSpecificTemporary(WriteBarrierDescriptor::ObjectRegister());
RequireSpecificTemporary(WriteBarrierDescriptor::SlotAddressRegister());
}
void StoreFixedArrayElementWithWriteBarrier::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
Register elements = ToRegister(elements_input());
Register index = ToRegister(index_input());
Register value = ToRegister(value_input());
__ StoreFixedArrayElementWithWriteBarrier(elements, index, value,
register_snapshot());
}
void StoreFixedArrayElementNoWriteBarrier::SetValueLocationConstraints() {
UseRegister(elements_input());
UseRegister(index_input());
UseRegister(value_input());
}
void StoreFixedArrayElementNoWriteBarrier::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
Register elements = ToRegister(elements_input());
Register index = ToRegister(index_input());
Register value = ToRegister(value_input());
__ StoreFixedArrayElementNoWriteBarrier(elements, index, value);
}
// ---
// Arch agnostic call nodes
// ---
int Call::MaxCallStackArgs() const { return num_args(); }
void Call::SetValueLocationConstraints() {
using D = CallTrampolineDescriptor;
UseFixed(function(), D::GetRegisterParameter(D::kFunction));
UseAny(arg(0));
for (int i = 1; i < num_args(); i++) {
UseAny(arg(i));
}
UseFixed(context(), kContextRegister);
DefineAsFixed(this, kReturnRegister0);
}
void Call::GenerateCode(MaglevAssembler* masm, const ProcessingState& state) {
__ PushReverse(base::make_iterator_range(args_begin(), args_end()));
uint32_t arg_count = num_args();
if (target_type_ == TargetType::kAny) {
switch (receiver_mode_) {
case ConvertReceiverMode::kNullOrUndefined:
__ CallBuiltin<Builtin::kCall_ReceiverIsNullOrUndefined>(
context(), function(), arg_count);
break;
case ConvertReceiverMode::kNotNullOrUndefined:
__ CallBuiltin<Builtin::kCall_ReceiverIsNotNullOrUndefined>(
context(), function(), arg_count);
break;
case ConvertReceiverMode::kAny:
__ CallBuiltin<Builtin::kCall_ReceiverIsAny>(context(), function(),
arg_count);
break;
}
} else {
DCHECK_EQ(TargetType::kJSFunction, target_type_);
switch (receiver_mode_) {
case ConvertReceiverMode::kNullOrUndefined:
__ CallBuiltin<Builtin::kCallFunction_ReceiverIsNullOrUndefined>(
context(), function(), arg_count);
break;
case ConvertReceiverMode::kNotNullOrUndefined:
__ CallBuiltin<Builtin::kCallFunction_ReceiverIsNotNullOrUndefined>(
context(), function(), arg_count);
break;
case ConvertReceiverMode::kAny:
__ CallBuiltin<Builtin::kCallFunction_ReceiverIsAny>(
context(), function(), arg_count);
break;
}
}
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int CallSelf::MaxCallStackArgs() const {
int actual_parameter_count = num_args() + 1;
return std::max(expected_parameter_count_, actual_parameter_count);
}
void CallSelf::SetValueLocationConstraints() {
UseAny(receiver());
for (int i = 0; i < num_args(); i++) {
UseAny(arg(i));
}
UseFixed(closure(), kJavaScriptCallTargetRegister);
UseFixed(new_target(), kJavaScriptCallNewTargetRegister);
UseFixed(context(), kContextRegister);
DefineAsFixed(this, kReturnRegister0);
set_temporaries_needed(1);
}
void CallSelf::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
int actual_parameter_count = num_args() + 1;
if (actual_parameter_count < expected_parameter_count_) {
int number_of_undefineds =
expected_parameter_count_ - actual_parameter_count;
__ LoadRoot(scratch, RootIndex::kUndefinedValue);
__ PushReverse(receiver(),
base::make_iterator_range(args_begin(), args_end()),
RepeatValue(scratch, number_of_undefineds));
} else {
__ PushReverse(receiver(),
base::make_iterator_range(args_begin(), args_end()));
}
DCHECK_EQ(kContextRegister, ToRegister(context()));
DCHECK_EQ(kJavaScriptCallTargetRegister, ToRegister(closure()));
__ Move(kJavaScriptCallArgCountRegister, actual_parameter_count);
DCHECK(!shared_function_info().HasBuiltinId());
__ CallSelf();
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int CallKnownJSFunction::MaxCallStackArgs() const {
int actual_parameter_count = num_args() + 1;
return std::max(expected_parameter_count_, actual_parameter_count);
}
void CallKnownJSFunction::SetValueLocationConstraints() {
UseAny(receiver());
for (int i = 0; i < num_args(); i++) {
UseAny(arg(i));
}
UseFixed(closure(), kJavaScriptCallTargetRegister);
UseFixed(new_target(), kJavaScriptCallNewTargetRegister);
UseFixed(context(), kContextRegister);
DefineAsFixed(this, kReturnRegister0);
set_temporaries_needed(1);
}
void CallKnownJSFunction::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
int actual_parameter_count = num_args() + 1;
if (actual_parameter_count < expected_parameter_count_) {
int number_of_undefineds =
expected_parameter_count_ - actual_parameter_count;
__ LoadRoot(scratch, RootIndex::kUndefinedValue);
__ PushReverse(receiver(),
base::make_iterator_range(args_begin(), args_end()),
RepeatValue(scratch, number_of_undefineds));
} else {
__ PushReverse(receiver(),
base::make_iterator_range(args_begin(), args_end()));
}
// From here on, we're going to do a call, so all registers are valid temps,
// except for the ones we're going to write. This is needed in case one of the
// helper methods below wants to use a temp and one of these is in the temp
// list (in particular, this can happen on arm64 where cp is a temp register
// by default).
temps.SetAvailable(MaglevAssembler::GetAllocatableRegisters() -
RegList{kContextRegister, kJavaScriptCallCodeStartRegister,
kJavaScriptCallTargetRegister,
kJavaScriptCallNewTargetRegister,
kJavaScriptCallArgCountRegister});
DCHECK_EQ(kContextRegister, ToRegister(context()));
DCHECK_EQ(kJavaScriptCallTargetRegister, ToRegister(closure()));
__ Move(kJavaScriptCallArgCountRegister, actual_parameter_count);
if (shared_function_info().HasBuiltinId()) {
__ CallBuiltin(shared_function_info().builtin_id());
} else {
__ CallJSFunction(kJavaScriptCallTargetRegister);
}
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int CallKnownApiFunction::MaxCallStackArgs() const {
int actual_parameter_count = num_args() + 1;
return actual_parameter_count;
}
void CallKnownApiFunction::SetValueLocationConstraints() {
if (api_holder_.has_value()) {
UseAny(receiver());
} else {
// This is an "Api holder is receiver" case, ask register allocator to put
// receiver value into the right register.
UseFixed(receiver(), CallApiCallbackOptimizedDescriptor::HolderRegister());
}
for (int i = 0; i < num_args(); i++) {
UseAny(arg(i));
}
UseFixed(context(), kContextRegister);
DefineAsFixed(this, kReturnRegister0);
if (inline_builtin()) {
set_temporaries_needed(2);
}
}
void CallKnownApiFunction::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
__ PushReverse(receiver(),
base::make_iterator_range(args_begin(), args_end()));
// From here on, we're going to do a call, so all registers are valid temps,
// except for the ones we're going to write. This is needed in case one of the
// helper methods below wants to use a temp and one of these is in the temp
// list (in particular, this can happen on arm64 where cp is a temp register
// by default).
temps.SetAvailable(
kAllocatableGeneralRegisters -
RegList{
kContextRegister,
CallApiCallbackOptimizedDescriptor::HolderRegister(),
CallApiCallbackOptimizedDescriptor::ActualArgumentsCountRegister(),
CallApiCallbackOptimizedDescriptor::CallDataRegister(),
CallApiCallbackOptimizedDescriptor::ApiFunctionAddressRegister()});
DCHECK_EQ(kContextRegister, ToRegister(context()));
if (inline_builtin()) {
GenerateCallApiCallbackOptimizedInline(masm, state);
return;
}
if (api_holder_.has_value()) {
__ Move(CallApiCallbackOptimizedDescriptor::HolderRegister(),
api_holder_.value().object());
} else {
// This is an "Api holder is receiver" case, register allocator was asked
// to put receiver value into the right register.
DCHECK_EQ(CallApiCallbackOptimizedDescriptor::HolderRegister(),
ToRegister(receiver()));
}
__ Move(CallApiCallbackOptimizedDescriptor::ActualArgumentsCountRegister(),
num_args()); // not including receiver
if (data_.IsSmi()) {
__ Move(CallApiCallbackOptimizedDescriptor::CallDataRegister(),
Smi::FromInt(data_.AsSmi()));
} else {
__ Move(CallApiCallbackOptimizedDescriptor::CallDataRegister(),
Handle<HeapObject>::cast(data_.object()));
}
compiler::JSHeapBroker* broker = masm->compilation_info()->broker();
ApiFunction function(call_handler_info_.callback(broker));
ExternalReference reference =
ExternalReference::Create(&function, ExternalReference::DIRECT_API_CALL);
__ Move(CallApiCallbackOptimizedDescriptor::ApiFunctionAddressRegister(),
reference);
switch (mode()) {
case kNoProfiling:
__ CallBuiltin(Builtin::kCallApiCallbackOptimizedNoProfiling);
break;
case kNoProfilingInlined:
UNREACHABLE();
case kGeneric:
__ CallBuiltin(Builtin::kCallApiCallbackOptimized);
break;
}
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
void CallKnownApiFunction::GenerateCallApiCallbackOptimizedInline(
MaglevAssembler* masm, const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
Register scratch2 = temps.Acquire();
using FCA = FunctionCallbackArguments;
static_assert(FCA::kArgsLength == 6);
static_assert(FCA::kNewTargetIndex == 5);
static_assert(FCA::kDataIndex == 4);
static_assert(FCA::kReturnValueIndex == 3);
static_assert(FCA::kUnusedIndex == 2);
static_assert(FCA::kIsolateIndex == 1);
static_assert(FCA::kHolderIndex == 0);
// Set up FunctionCallbackInfo's implicit_args on the stack as follows:
//
// Target state:
// sp[0 * kSystemPointerSize]: kHolder <= implicit_args_
// sp[1 * kSystemPointerSize]: kIsolate
// sp[2 * kSystemPointerSize]: undefined (padding, unused)
// sp[3 * kSystemPointerSize]: undefined (kReturnValue)
// sp[4 * kSystemPointerSize]: kData
// sp[5 * kSystemPointerSize]: undefined (kNewTarget)
// Existing state:
// sp[6 * kSystemPointerSize]: <= FCA:::values_
ASM_CODE_COMMENT_STRING(masm, "inlined CallApiCallbackOptimized builtin");
__ LoadRoot(scratch, RootIndex::kUndefinedValue);
// kNewTarget, kData, kReturnValue, kUnused
if (data_.IsSmi()) {
__ Push(scratch, Smi::FromInt(data_.AsSmi()), scratch, scratch);
} else {
__ Push(scratch, Handle<HeapObject>::cast(data_.object()), scratch,
scratch);
}
__ Move(scratch, ExternalReference::isolate_address(masm->isolate()));
// kIsolate, kHolder
if (api_holder_.has_value()) {
__ Push(scratch, api_holder_.value().object());
} else {
// This is an "Api holder is receiver" case, register allocator was asked
// to put receiver value into the right register.
__ Push(scratch, receiver());
}
Register api_function_address =
CallApiCallbackOptimizedDescriptor::ApiFunctionAddressRegister();
compiler::JSHeapBroker* broker = masm->compilation_info()->broker();
ApiFunction function(call_handler_info_.callback(broker));
ExternalReference reference =
ExternalReference::Create(&function, ExternalReference::DIRECT_API_CALL);
__ Move(api_function_address, reference);
Register implicit_args = ReassignRegister(scratch);
// Save a pointer to kHolder (= implicit_args), we use it below to set up
// the FunctionCallbackInfo object.
__ Move(implicit_args, kStackPointerRegister);
// Allocate v8::FunctionCallbackInfo object and a number of bytes to drop
// from the stack after the callback in non-GCed space of the exit frame.
static constexpr int kApiStackSpace = 4;
static_assert((kApiStackSpace - 1) * kSystemPointerSize == FCA::kSize);
const int exit_frame_params_size = 0;
Label done;
// Make it look like as if the code starting from EnterExitFrame was called
// by an instruction right before the |done| label. Depending on the current
// architecture it will either push the "return address" between
// FunctonCallbackInfo and ExitFrame or set the link register to the
// "return address". This is necessary for lazy deopting of current code.
const int kMaybePCOnTheStack = __ PushOrSetReturnAddressTo(&done);
DCHECK(kMaybePCOnTheStack == 0 || kMaybePCOnTheStack == 1);
FrameScope frame_scope(masm, StackFrame::MANUAL);
__ EmitEnterExitFrame(kApiStackSpace, StackFrame::EXIT, api_function_address,
scratch2);
{
ASM_CODE_COMMENT_STRING(masm, "Initialize FunctionCallbackInfo");
// FunctionCallbackInfo::implicit_args_ (points at kHolder as set up
// above).
__ Move(ExitFrameStackSlotOperand(FCA::kImplicitArgsOffset), implicit_args);
// FunctionCallbackInfo::values_ (points at the first varargs argument
// passed on the stack).
__ IncrementAddress(implicit_args,
FCA::kArgsLengthWithReceiver * kSystemPointerSize);
__ Move(ExitFrameStackSlotOperand(FCA::kValuesOffset), implicit_args);
// FunctionCallbackInfo::length_.
scratch = ReassignRegister(implicit_args);
__ Move(scratch, num_args());
__ Move(ExitFrameStackSlotOperand(FCA::kLengthOffset), scratch);
}
Register function_callback_info_arg = arg_reg_1;
__ RecordComment("v8::FunctionCallback's argument.");
__ LoadAddress(function_callback_info_arg,
ExitFrameStackSlotOperand(FCA::kImplicitArgsOffset));
DCHECK(!AreAliased(api_function_address, function_callback_info_arg));
MemOperand return_value_operand = ExitFrameCallerStackSlotOperand(
FCA::kReturnValueIndex + exit_frame_params_size);
const int kStackUnwindSpace =
FCA::kArgsLengthWithReceiver + num_args() + kMaybePCOnTheStack;
const bool with_profiling = false;
ExternalReference no_thunk_ref;
Register no_thunk_arg = no_reg;
CallApiFunctionAndReturn(masm, with_profiling, api_function_address,
no_thunk_ref, no_thunk_arg, kStackUnwindSpace,
nullptr, return_value_operand, &done);
__ bind(&done);
__ RecordComment("end of inlined CallApiCallbackOptimized builtin");
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int CallBuiltin::MaxCallStackArgs() const {
auto descriptor = Builtins::CallInterfaceDescriptorFor(builtin());
if (!descriptor.AllowVarArgs()) {
return descriptor.GetStackParameterCount();
} else {
int all_input_count = InputCountWithoutContext() + (has_feedback() ? 2 : 0);
DCHECK_GE(all_input_count, descriptor.GetRegisterParameterCount());
return all_input_count - descriptor.GetRegisterParameterCount();
}
}
void CallBuiltin::SetValueLocationConstraints() {
auto descriptor = Builtins::CallInterfaceDescriptorFor(builtin());
bool has_context = descriptor.HasContextParameter();
int i = 0;
for (; i < InputsInRegisterCount(); i++) {
UseFixed(input(i), descriptor.GetRegisterParameter(i));
}
for (; i < InputCountWithoutContext(); i++) {
UseAny(input(i));
}
if (has_context) {
UseFixed(input(i), kContextRegister);
}
DefineAsFixed(this, kReturnRegister0);
}
template <typename... Args>
void CallBuiltin::PushArguments(MaglevAssembler* masm, Args... extra_args) {
auto descriptor = Builtins::CallInterfaceDescriptorFor(builtin());
if (descriptor.GetStackArgumentOrder() == StackArgumentOrder::kDefault) {
// In Default order we cannot have extra args (feedback).
DCHECK_EQ(sizeof...(extra_args), 0);
__ Push(base::make_iterator_range(stack_args_begin(), stack_args_end()));
} else {
DCHECK_EQ(descriptor.GetStackArgumentOrder(), StackArgumentOrder::kJS);
__ PushReverse(extra_args..., base::make_iterator_range(stack_args_begin(),
stack_args_end()));
}
}
void CallBuiltin::PassFeedbackSlotInRegister(MaglevAssembler* masm) {
DCHECK(has_feedback());
auto descriptor = Builtins::CallInterfaceDescriptorFor(builtin());
int slot_index = InputCountWithoutContext();
switch (slot_type()) {
case kTaggedIndex:
__ Move(descriptor.GetRegisterParameter(slot_index),
TaggedIndex::FromIntptr(feedback().index()));
break;
case kSmi:
__ Move(descriptor.GetRegisterParameter(slot_index),
Smi::FromInt(feedback().index()));
break;
}
}
void CallBuiltin::PushFeedbackAndArguments(MaglevAssembler* masm) {
DCHECK(has_feedback());
auto descriptor = Builtins::CallInterfaceDescriptorFor(builtin());
int slot_index = InputCountWithoutContext();
int vector_index = slot_index + 1;
// There are three possibilities:
// 1. Feedback slot and vector are in register.
// 2. Feedback slot is in register and vector is on stack.
// 3. Feedback slot and vector are on stack.
if (vector_index < descriptor.GetRegisterParameterCount()) {
PassFeedbackSlotInRegister(masm);
__ Move(descriptor.GetRegisterParameter(vector_index), feedback().vector);
PushArguments(masm);
} else if (vector_index == descriptor.GetRegisterParameterCount()) {
PassFeedbackSlotInRegister(masm);
DCHECK_EQ(descriptor.GetStackArgumentOrder(), StackArgumentOrder::kJS);
// Ensure that the builtin only expects the feedback vector on the stack and
// potentional additional var args are passed through to another builtin.
// This is required to align the stack correctly (e.g. on arm64).
DCHECK_EQ(descriptor.GetStackParameterCount(), 1);
PushArguments(masm);
__ Push(feedback().vector);
} else {
int slot = feedback().index();
Handle<FeedbackVector> vector = feedback().vector;
switch (slot_type()) {
case kTaggedIndex:
PushArguments(masm, TaggedIndex::FromIntptr(slot), vector);
break;
case kSmi:
PushArguments(masm, Smi::FromInt(slot), vector);
break;
}
}
}
void CallBuiltin::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
if (has_feedback()) {
PushFeedbackAndArguments(masm);
} else {
PushArguments(masm);
}
__ CallBuiltin(builtin());
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int CallCPPBuiltin::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<kCEntry_Builtin>::type;
return D::GetStackParameterCount() + num_args();
}
void CallCPPBuiltin::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<kCEntry_Builtin>::type;
UseAny(target());
UseAny(new_target());
UseFixed(context(), kContextRegister);
for (int i = 0; i < num_args(); i++) {
UseAny(arg(i));
}
DefineAsFixed(this, kReturnRegister0);
set_temporaries_needed(1);
RequireSpecificTemporary(D::GetRegisterParameter(D::kArity));
RequireSpecificTemporary(D::GetRegisterParameter(D::kCFunction));
}
void CallCPPBuiltin::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
using D = CallInterfaceDescriptorFor<kCEntry_Builtin>::type;
constexpr Register kArityReg = D::GetRegisterParameter(D::kArity);
constexpr Register kCFunctionReg = D::GetRegisterParameter(D::kCFunction);
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
__ LoadRoot(scratch, RootIndex::kTheHoleValue);
// Push all arguments to the builtin (including the receiver).
static_assert(BuiltinArguments::kNumExtraArgsWithReceiver == 5);
static_assert(BuiltinArguments::kReceiverOffset == 4);
__ PushReverse(base::make_iterator_range(args_begin(), args_end()));
static_assert(BuiltinArguments::kNewTargetOffset == 0);
static_assert(BuiltinArguments::kTargetOffset == 1);
static_assert(BuiltinArguments::kArgcOffset == 2);
static_assert(BuiltinArguments::kPaddingOffset == 3);
// Push stack arguments for CEntry.
Tagged<Smi> tagged_argc = Smi::FromInt(num_args()); // Includes receiver.
__ Push(scratch /* padding */, tagged_argc, target(), new_target());
// Move values to fixed registers after all arguments are pushed. Registers
// for arguments and CEntry registers might overlap.
__ Move(kArityReg, BuiltinArguments::kNumExtraArgs + num_args());
ExternalReference builtin_address =
ExternalReference::Create(Builtins::CppEntryOf(builtin()));
__ Move(kCFunctionReg, builtin_address);
DCHECK_EQ(Builtins::CallInterfaceDescriptorFor(builtin()).GetReturnCount(),
1);
constexpr int kResultSize = 1;
constexpr bool kBuiltinExitFrame = true;
Handle<Code> code = CodeFactory::CEntry(masm->isolate(), kResultSize,
ArgvMode::kStack, kBuiltinExitFrame);
__ Call(code, RelocInfo::CODE_TARGET);
}
int CallRuntime::MaxCallStackArgs() const { return num_args(); }
void CallRuntime::SetValueLocationConstraints() {
UseFixed(context(), kContextRegister);
for (int i = 0; i < num_args(); i++) {
UseAny(arg(i));
}
DefineAsFixed(this, kReturnRegister0);
}
void CallRuntime::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
DCHECK_EQ(ToRegister(context()), kContextRegister);
__ Push(base::make_iterator_range(args_begin(), args_end()));
__ CallRuntime(function_id(), num_args());
// TODO(victorgomes): Not sure if this is needed for all runtime calls.
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int CallWithSpread::MaxCallStackArgs() const {
int argc_no_spread = num_args() - 1;
using D = CallInterfaceDescriptorFor<Builtin::kCallWithSpread>::type;
return argc_no_spread + D::GetStackParameterCount();
}
void CallWithSpread::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kCallWithSpread>::type;
UseFixed(function(), D::GetRegisterParameter(D::kTarget));
UseFixed(spread(), D::GetRegisterParameter(D::kSpread));
UseFixed(context(), kContextRegister);
for (int i = 0; i < num_args() - 1; i++) {
UseAny(arg(i));
}
DefineAsFixed(this, kReturnRegister0);
}
void CallWithSpread::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kCallWithSpread>(
context(), // context
function(), // target
num_args_no_spread(), // arguments count
spread(), // spread
base::make_iterator_range(args_no_spread_begin(),
args_no_spread_end()) // pushed args
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int CallWithArrayLike::MaxCallStackArgs() const {
using D = CallInterfaceDescriptorFor<Builtin::kCallWithArrayLike>::type;
return D::GetStackParameterCount();
}
void CallWithArrayLike::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<Builtin::kCallWithArrayLike>::type;
UseFixed(function(), D::GetRegisterParameter(D::kTarget));
UseAny(receiver());
UseFixed(arguments_list(), D::GetRegisterParameter(D::kArgumentsList));
UseFixed(context(), kContextRegister);
DefineAsFixed(this, kReturnRegister0);
}
void CallWithArrayLike::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
// CallWithArrayLike is a weird builtin that expects a receiver as top of the
// stack, but doesn't explicitly list it as an extra argument. Push it
// manually, and assert that there are no other stack arguments.
static_assert(
CallInterfaceDescriptorFor<
Builtin::kCallWithArrayLike>::type::GetStackParameterCount() == 0);
__ Push(receiver());
__ CallBuiltin<Builtin::kCallWithArrayLike>(
context(), // context
function(), // target
arguments_list() // arguments list
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
// ---
// Arch agnostic construct nodes
// ---
int Construct::MaxCallStackArgs() const {
using D = Construct_WithFeedbackDescriptor;
return num_args() + D::GetStackParameterCount();
}
void Construct::SetValueLocationConstraints() {
using D = Construct_WithFeedbackDescriptor;
UseFixed(function(), D::GetRegisterParameter(D::kTarget));
UseFixed(new_target(), D::GetRegisterParameter(D::kNewTarget));
UseFixed(context(), kContextRegister);
for (int i = 0; i < num_args(); i++) {
UseAny(arg(i));
}
DefineAsFixed(this, kReturnRegister0);
}
void Construct::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kConstruct_WithFeedback>(
context(), // context
function(), // target
new_target(), // new target
num_args(), // actual arguments count
feedback().index(), // feedback slot
feedback().vector, // feedback vector
base::make_iterator_range(args_begin(), args_end()) // args
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
int ConstructWithSpread::MaxCallStackArgs() const {
int argc_no_spread = num_args() - 1;
using D = CallInterfaceDescriptorFor<
Builtin::kConstructWithSpread_WithFeedback>::type;
return argc_no_spread + D::GetStackParameterCount();
}
void ConstructWithSpread::SetValueLocationConstraints() {
using D = CallInterfaceDescriptorFor<
Builtin::kConstructWithSpread_WithFeedback>::type;
UseFixed(function(), D::GetRegisterParameter(D::kTarget));
UseFixed(new_target(), D::GetRegisterParameter(D::kNewTarget));
UseFixed(context(), kContextRegister);
for (int i = 0; i < num_args() - 1; i++) {
UseAny(arg(i));
}
UseFixed(spread(), D::GetRegisterParameter(D::kSpread));
DefineAsFixed(this, kReturnRegister0);
}
void ConstructWithSpread::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CallBuiltin<Builtin::kConstructWithSpread_WithFeedback>(
context(), // context
function(), // target
new_target(), // new target
num_args_no_spread(), // actual arguments count
feedback().index(), // feedback slot
spread(), // spread
feedback().vector, // feedback vector
base::make_iterator_range(args_no_spread_begin(),
args_no_spread_end()) // args
);
masm->DefineExceptionHandlerAndLazyDeoptPoint(this);
}
void SetPendingMessage::SetValueLocationConstraints() {
UseRegister(value());
DefineAsRegister(this);
}
void SetPendingMessage::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register new_message = ToRegister(value());
Register return_value = ToRegister(result());
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.GetDefaultScratchRegister();
MemOperand pending_message_operand = __ ExternalReferenceAsOperand(
ExternalReference::address_of_pending_message(masm->isolate()), scratch);
if (new_message != return_value) {
__ Move(return_value, pending_message_operand);
__ Move(pending_message_operand, new_message);
} else {
__ Move(scratch, pending_message_operand);
__ Move(pending_message_operand, new_message);
__ Move(return_value, scratch);
}
}
void StoreDoubleField::SetValueLocationConstraints() {
UseRegister(object_input());
UseRegister(value_input());
}
void StoreDoubleField::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(object_input());
DoubleRegister value = ToDoubleRegister(value_input());
MaglevAssembler::ScratchRegisterScope temps(masm);
Register tmp = temps.GetDefaultScratchRegister();
__ AssertNotSmi(object);
__ LoadTaggedField(tmp, object, offset());
__ AssertNotSmi(tmp);
__ StoreFloat64(FieldMemOperand(tmp, HeapNumber::kValueOffset), value);
}
int TransitionElementsKindOrCheckMap::MaxCallStackArgs() const {
return std::max(WriteBarrierDescriptor::GetStackParameterCount(), 2);
}
void TransitionElementsKindOrCheckMap::SetValueLocationConstraints() {
UseRegister(object_input());
set_temporaries_needed(1);
}
void TransitionElementsKindOrCheckMap::GenerateCode(
MaglevAssembler* masm, const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register object = ToRegister(object_input());
ZoneLabelRef done(masm);
DCHECK(!AnyMapIsHeapNumber(transition_sources_));
DCHECK(!IsHeapNumberMap(*transition_target_.object()));
if (check_type() == CheckType::kOmitHeapObjectCheck) {
__ AssertNotSmi(object);
} else {
__ EmitEagerDeoptIfSmi(this, object, DeoptimizeReason::kWrongMap);
}
Register map = temps.Acquire();
__ LoadMap(map, object);
for (compiler::MapRef transition_source : transition_sources_) {
bool is_simple = IsSimpleMapChangeTransition(
transition_source.elements_kind(), transition_target_.elements_kind());
// TODO(leszeks): If there are a lot of transition source maps, move the
// source into a register and share the deferred code between maps.
__ CompareTaggedAndJumpIf(
map, transition_source.object(), kEqual,
// We can use `map` as a temporary register, since the deferred
// code will jump to `done`, so we won't use it afterwards.
__ MakeDeferredCode(
[](MaglevAssembler* masm, Register object, Register temp,
RegisterSnapshot register_snapshot,
compiler::MapRef transition_target, bool is_simple,
ZoneLabelRef done) {
if (is_simple) {
__ Move(temp, transition_target.object());
__ StoreTaggedFieldWithWriteBarrier(
object, HeapObject::kMapOffset, temp, register_snapshot,
MaglevAssembler::kValueIsDecompressed,
MaglevAssembler::kValueCannotBeSmi);
} else {
SaveRegisterStateForCall save_state(masm, register_snapshot);
__ Push(object, transition_target.object());
__ Move(kContextRegister, masm->native_context().object());
__ CallRuntime(Runtime::kTransitionElementsKind);
save_state.DefineSafepoint();
}
__ Jump(*done);
},
object, map, register_snapshot(), transition_target_, is_simple,
done));
}
Label* fail = __ GetDeoptLabel(this, DeoptimizeReason::kWrongMap);
__ CompareTaggedAndJumpIf(map, transition_target_.object(), kNotEqual, fail);
__ bind(*done);
}
namespace {
template <bool check_detached, typename ResultReg, typename NodeT>
void GenerateTypedArrayLoad(MaglevAssembler* masm, NodeT* node, Register object,
Register index, ResultReg result_reg,
ElementsKind kind) {
__ AssertNotSmi(object);
if (v8_flags.debug_code) {
MaglevAssembler::ScratchRegisterScope temps(masm);
__ CompareObjectTypeAndAssert(object, JS_TYPED_ARRAY_TYPE, kEqual,
AbortReason::kUnexpectedValue);
}
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
if constexpr (check_detached) {
__ DeoptIfBufferDetached(object, scratch, node);
}
Register data_pointer = scratch;
__ BuildTypedArrayDataPointer(data_pointer, object);
int element_size = ElementsKindSize(kind);
MemOperand operand =
__ TypedArrayElementOperand(data_pointer, index, element_size);
if constexpr (std::is_same_v<ResultReg, Register>) {
if (IsSignedIntTypedArrayElementsKind(kind)) {
__ LoadSignedField(result_reg, operand, element_size);
} else {
DCHECK(IsUnsignedIntTypedArrayElementsKind(kind));
__ LoadUnsignedField(result_reg, operand, element_size);
}
} else {
#ifdef DEBUG
bool result_reg_is_double = std::is_same_v<ResultReg, DoubleRegister>;
DCHECK(result_reg_is_double);
DCHECK(IsFloatTypedArrayElementsKind(kind));
#endif
switch (kind) {
case FLOAT32_ELEMENTS:
__ LoadFloat32(result_reg, operand);
break;
case FLOAT64_ELEMENTS:
__ LoadFloat64(result_reg, operand);
break;
default:
UNREACHABLE();
}
}
}
template <bool check_detached, typename ValueReg, typename NodeT>
void GenerateTypedArrayStore(MaglevAssembler* masm, NodeT* node,
Register object, Register index, ValueReg value,
ElementsKind kind) {
__ AssertNotSmi(object);
if (v8_flags.debug_code) {
MaglevAssembler::ScratchRegisterScope temps(masm);
__ CompareObjectTypeAndAssert(object, JS_TYPED_ARRAY_TYPE, kEqual,
AbortReason::kUnexpectedValue);
}
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
if constexpr (check_detached) {
__ DeoptIfBufferDetached(object, scratch, node);
}
Register data_pointer = scratch;
__ BuildTypedArrayDataPointer(data_pointer, object);
int element_size = ElementsKindSize(kind);
MemOperand operand =
__ TypedArrayElementOperand(data_pointer, index, element_size);
if constexpr (std::is_same_v<ValueReg, Register>) {
int element_size = ElementsKindSize(kind);
__ StoreField(operand, value, element_size);
} else {
#ifdef DEBUG
bool value_is_double = std::is_same_v<ValueReg, DoubleRegister>;
DCHECK(value_is_double);
DCHECK(IsFloatTypedArrayElementsKind(kind));
#endif
switch (kind) {
case FLOAT32_ELEMENTS:
__ StoreFloat32(operand, value);
break;
case FLOAT64_ELEMENTS:
__ StoreFloat64(operand, value);
break;
default:
UNREACHABLE();
}
}
}
} // namespace
#define DEF_LOAD_TYPED_ARRAY(Name, ResultReg, ToResultReg, check_detached) \
void Name::SetValueLocationConstraints() { \
UseRegister(object_input()); \
UseRegister(index_input()); \
DefineAsRegister(this); \
set_temporaries_needed(1); \
} \
void Name::GenerateCode(MaglevAssembler* masm, \
const ProcessingState& state) { \
Register object = ToRegister(object_input()); \
Register index = ToRegister(index_input()); \
ResultReg result_reg = ToResultReg(result()); \
\
GenerateTypedArrayLoad<check_detached>(masm, this, object, index, \
result_reg, elements_kind_); \
}
DEF_LOAD_TYPED_ARRAY(LoadSignedIntTypedArrayElement, Register, ToRegister,
/*check_detached*/ true)
DEF_LOAD_TYPED_ARRAY(LoadSignedIntTypedArrayElementNoDeopt, Register,
ToRegister,
/*check_detached*/ false)
DEF_LOAD_TYPED_ARRAY(LoadUnsignedIntTypedArrayElement, Register, ToRegister,
/*check_detached*/ true)
DEF_LOAD_TYPED_ARRAY(LoadUnsignedIntTypedArrayElementNoDeopt, Register,
ToRegister,
/*check_detached*/ false)
DEF_LOAD_TYPED_ARRAY(LoadDoubleTypedArrayElement, DoubleRegister,
ToDoubleRegister,
/*check_detached*/ true)
DEF_LOAD_TYPED_ARRAY(LoadDoubleTypedArrayElementNoDeopt, DoubleRegister,
ToDoubleRegister, /*check_detached*/ false)
#undef DEF_LOAD_TYPED_ARRAY
#define DEF_STORE_TYPED_ARRAY(Name, ValueReg, ToValueReg, check_detached) \
void Name::SetValueLocationConstraints() { \
UseRegister(object_input()); \
UseRegister(index_input()); \
UseRegister(value_input()); \
set_temporaries_needed(1); \
} \
void Name::GenerateCode(MaglevAssembler* masm, \
const ProcessingState& state) { \
Register object = ToRegister(object_input()); \
Register index = ToRegister(index_input()); \
ValueReg value = ToValueReg(value_input()); \
\
GenerateTypedArrayStore<check_detached>(masm, this, object, index, value, \
elements_kind_); \
}
DEF_STORE_TYPED_ARRAY(StoreIntTypedArrayElement, Register, ToRegister,
/*check_detached*/ true)
DEF_STORE_TYPED_ARRAY(StoreIntTypedArrayElementNoDeopt, Register, ToRegister,
/*check_detached*/ false)
DEF_STORE_TYPED_ARRAY(StoreDoubleTypedArrayElement, DoubleRegister,
ToDoubleRegister,
/*check_detached*/ true)
DEF_STORE_TYPED_ARRAY(StoreDoubleTypedArrayElementNoDeopt, DoubleRegister,
ToDoubleRegister, /*check_detached*/ false)
#undef DEF_STORE_TYPED_ARRAY
// ---
// Arch agnostic control nodes
// ---
void Jump::SetValueLocationConstraints() {}
void Jump::GenerateCode(MaglevAssembler* masm, const ProcessingState& state) {
// Avoid emitting a jump to the next block.
if (target() != state.next_block()) {
__ Jump(target()->label());
}
}
namespace {
void AttemptOnStackReplacement(MaglevAssembler* masm,
ZoneLabelRef no_code_for_osr,
TryOnStackReplacement* node, Register scratch0,
Register scratch1, int32_t loop_depth,
FeedbackSlot feedback_slot,
BytecodeOffset osr_offset) {
// Two cases may cause us to attempt OSR, in the following order:
//
// 1) Presence of cached OSR Turbofan code.
// 2) The OSR urgency exceeds the current loop depth - in that case, call
// into runtime to trigger a Turbofan OSR compilation. A non-zero return
// value means we should deopt into Ignition which will handle all further
// necessary steps (rewriting the stack frame, jumping to OSR'd code).
//
// See also: InterpreterAssembler::OnStackReplacement.
__ AssertFeedbackVector(scratch0);
// Case 1).
Label deopt;
Register maybe_target_code = scratch1;
__ TryLoadOptimizedOsrCode(scratch1, CodeKind::TURBOFAN, scratch0,
feedback_slot, &deopt, Label::kFar);
// Case 2).
{
__ LoadByte(scratch0,
FieldMemOperand(scratch0, FeedbackVector::kOsrStateOffset));
__ DecodeField<FeedbackVector::OsrUrgencyBits>(scratch0);
__ JumpIfByte(kUnsignedLessThanEqual, scratch0, loop_depth,
*no_code_for_osr);
// The osr_urgency exceeds the current loop_depth, signaling an OSR
// request. Call into runtime to compile.
{
// At this point we need a custom register snapshot since additional
// registers may be live at the eager deopt below (the normal
// register_snapshot only contains live registers *after this
// node*).
// TODO(v8:7700): Consider making the snapshot location
// configurable.
RegisterSnapshot snapshot = node->register_snapshot();
AddDeoptRegistersToSnapshot(&snapshot, node->eager_deopt_info());
DCHECK(!snapshot.live_registers.has(maybe_target_code));
SaveRegisterStateForCall save_register_state(masm, snapshot);
if (node->unit()->is_inline()) {
// See comment in
// MaglevGraphBuilder::ShouldEmitOsrInterruptBudgetChecks.
CHECK(!node->unit()->is_osr());
__ Push(Smi::FromInt(osr_offset.ToInt()), node->closure());
__ Move(kContextRegister, masm->native_context().object());
__ CallRuntime(Runtime::kCompileOptimizedOSRFromMaglevInlined, 2);
} else {
__ Push(Smi::FromInt(osr_offset.ToInt()));
__ Move(kContextRegister, masm->native_context().object());
__ CallRuntime(Runtime::kCompileOptimizedOSRFromMaglev, 1);
}
save_register_state.DefineSafepoint();
__ Move(maybe_target_code, kReturnRegister0);
}
// A `0` return value means there is no OSR code available yet. Continue
// execution in Maglev, OSR code will be picked up once it exists and is
// cached on the feedback vector.
__ Cmp(maybe_target_code, 0);
__ JumpIf(kEqual, *no_code_for_osr);
}
__ bind(&deopt);
if (V8_LIKELY(v8_flags.turbofan)) {
// None of the mutated input registers should be a register input into the
// eager deopt info.
DCHECK_REGLIST_EMPTY(
RegList{scratch0, scratch1} &
GetGeneralRegistersUsedAsInputs(node->eager_deopt_info()));
__ EmitEagerDeopt(node, DeoptimizeReason::kPrepareForOnStackReplacement);
} else {
// Continue execution in Maglev. With TF disabled we cannot OSR and thus it
// doesn't make sense to start the process. We do still perform all
// remaining bookkeeping above though, to keep Maglev code behavior roughly
// the same in both configurations.
__ Jump(*no_code_for_osr);
}
}
} // namespace
int TryOnStackReplacement::MaxCallStackArgs() const {
// For the kCompileOptimizedOSRFromMaglev call.
if (unit()->is_inline()) return 2;
return 1;
}
void TryOnStackReplacement::SetValueLocationConstraints() {
UseAny(closure());
set_temporaries_needed(2);
}
void TryOnStackReplacement::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch0 = temps.Acquire();
Register scratch1 = temps.Acquire();
const Register osr_state = scratch1;
__ Move(scratch0, unit_->feedback().object());
__ AssertFeedbackVector(scratch0);
__ LoadByte(osr_state,
FieldMemOperand(scratch0, FeedbackVector::kOsrStateOffset));
ZoneLabelRef no_code_for_osr(masm);
if (v8_flags.maglev_osr) {
// In case we use maglev_osr, we need to explicitly know if there is
// turbofan code waiting for us (i.e., ignore the MaybeHasMaglevOsrCodeBit).
__ DecodeField<
base::BitFieldUnion<FeedbackVector::OsrUrgencyBits,
FeedbackVector::MaybeHasTurbofanOsrCodeBit>>(
osr_state);
}
// The quick initial OSR check. If it passes, we proceed on to more
// expensive OSR logic.
static_assert(FeedbackVector::MaybeHasTurbofanOsrCodeBit::encode(true) >
FeedbackVector::kMaxOsrUrgency);
__ CompareInt32AndJumpIf(
osr_state, loop_depth_, kUnsignedGreaterThan,
__ MakeDeferredCode(AttemptOnStackReplacement, no_code_for_osr, this,
scratch0, scratch1, loop_depth_, feedback_slot_,
osr_offset_));
__ bind(*no_code_for_osr);
}
void JumpLoop::SetValueLocationConstraints() {}
void JumpLoop::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ Jump(target()->label());
}
void BranchIfRootConstant::SetValueLocationConstraints() {
UseRegister(condition_input());
}
void BranchIfRootConstant::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CompareRoot(ToRegister(condition_input()), root_index());
__ Branch(ConditionFor(Operation::kEqual), if_true(), if_false(),
state.next_block());
}
void BranchIfToBooleanTrue::SetValueLocationConstraints() {
// TODO(victorgomes): consider using any input instead.
UseRegister(condition_input());
}
void BranchIfToBooleanTrue::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
// BasicBlocks are zone allocated and so safe to be casted to ZoneLabelRef.
ZoneLabelRef true_label =
ZoneLabelRef::UnsafeFromLabelPointer(if_true()->label());
ZoneLabelRef false_label =
ZoneLabelRef::UnsafeFromLabelPointer(if_false()->label());
bool fallthrough_when_true = (if_true() == state.next_block());
__ ToBoolean(ToRegister(condition_input()), check_type(), true_label,
false_label, fallthrough_when_true);
}
void BranchIfInt32ToBooleanTrue::SetValueLocationConstraints() {
// TODO(victorgomes): consider using any input instead.
UseRegister(condition_input());
}
void BranchIfInt32ToBooleanTrue::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
__ CompareInt32AndBranch(ToRegister(condition_input()), 0, kNotEqual,
if_true(), if_false(), state.next_block());
}
void BranchIfFloat64ToBooleanTrue::SetValueLocationConstraints() {
UseRegister(condition_input());
set_double_temporaries_needed(1);
}
void BranchIfFloat64ToBooleanTrue::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
DoubleRegister double_scratch = temps.AcquireDouble();
__ Move(double_scratch, 0.0);
__ CompareFloat64AndBranch(ToDoubleRegister(condition_input()),
double_scratch, kEqual, if_false(), if_true(),
state.next_block(), if_false());
}
void BranchIfFloat64IsHole::SetValueLocationConstraints() {
UseRegister(condition_input());
set_temporaries_needed(1);
}
void BranchIfFloat64IsHole::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
DoubleRegister input = ToDoubleRegister(condition_input());
// See MaglevAssembler::Branch.
bool fallthrough_when_true = if_true() == state.next_block();
bool fallthrough_when_false = if_false() == state.next_block();
if (fallthrough_when_false) {
if (fallthrough_when_true) {
// If both paths are a fallthrough, do nothing.
DCHECK_EQ(if_true(), if_false());
return;
}
// Jump over the false block if true, otherwise fall through into it.
__ JumpIfHoleNan(input, scratch, if_true()->label(), Label::kFar);
} else {
// Jump to the false block if true.
__ JumpIfNotHoleNan(input, scratch, if_false()->label(), Label::kFar);
// Jump to the true block if it's not the next block.
if (!fallthrough_when_true) {
__ Jump(if_true()->label(), Label::kFar);
}
}
}
void BranchIfFloat64Compare::SetValueLocationConstraints() {
UseRegister(left_input());
UseRegister(right_input());
}
void BranchIfFloat64Compare::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
DoubleRegister left = ToDoubleRegister(left_input());
DoubleRegister right = ToDoubleRegister(right_input());
__ CompareFloat64AndBranch(left, right, ConditionForFloat64(operation_),
if_true(), if_false(), state.next_block(),
if_false());
}
void BranchIfReferenceEqual::SetValueLocationConstraints() {
UseRegister(left_input());
UseRegister(right_input());
}
void BranchIfReferenceEqual::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register left = ToRegister(left_input());
Register right = ToRegister(right_input());
__ CmpTagged(left, right);
__ Branch(kEqual, if_true(), if_false(), state.next_block());
}
void BranchIfInt32Compare::SetValueLocationConstraints() {
UseRegister(left_input());
UseRegister(right_input());
}
void BranchIfInt32Compare::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register left = ToRegister(left_input());
Register right = ToRegister(right_input());
__ CompareInt32AndBranch(left, right, ConditionFor(operation_), if_true(),
if_false(), state.next_block());
}
void BranchIfUndefinedOrNull::SetValueLocationConstraints() {
UseRegister(condition_input());
}
void BranchIfUndefinedOrNull::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register value = ToRegister(condition_input());
__ JumpIfRoot(value, RootIndex::kUndefinedValue, if_true()->label());
__ JumpIfRoot(value, RootIndex::kNullValue, if_true()->label());
auto* next_block = state.next_block();
if (if_false() != next_block) {
__ Jump(if_false()->label());
}
}
void BranchIfUndetectable::SetValueLocationConstraints() {
UseRegister(condition_input());
set_temporaries_needed(1);
}
void BranchIfUndetectable::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register value = ToRegister(condition_input());
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
auto* next_block = state.next_block();
if (next_block == if_true() || next_block != if_false()) {
__ JumpIfNotUndetectable(value, scratch, check_type(), if_false()->label());
if (next_block != if_true()) {
__ Jump(if_true()->label());
}
} else {
__ JumpIfUndetectable(value, scratch, check_type(), if_true()->label());
}
}
void TestUndetectable::SetValueLocationConstraints() {
UseRegister(value());
set_temporaries_needed(1);
DefineAsRegister(this);
}
void TestUndetectable::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register object = ToRegister(value());
Register return_value = ToRegister(result());
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
Label return_false, done;
__ JumpIfNotUndetectable(object, scratch, check_type(), &return_false,
Label::kNear);
__ LoadRoot(return_value, RootIndex::kTrueValue);
__ Jump(&done, Label::kNear);
__ bind(&return_false);
__ LoadRoot(return_value, RootIndex::kFalseValue);
__ bind(&done);
}
void BranchIfTypeOf::SetValueLocationConstraints() {
UseRegister(value_input());
// One temporary for TestTypeOf.
set_temporaries_needed(1);
}
void BranchIfTypeOf::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register value = ToRegister(value_input());
__ TestTypeOf(value, literal_, if_true()->label(), Label::kFar,
if_true() == state.next_block(), if_false()->label(),
Label::kFar, if_false() == state.next_block());
}
void BranchIfJSReceiver::SetValueLocationConstraints() {
UseRegister(condition_input());
}
void BranchIfJSReceiver::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register value = ToRegister(condition_input());
__ JumpIfSmi(value, if_false()->label());
__ JumpIfJSAnyIsNotPrimitive(value, if_true()->label());
__ jmp(if_false()->label());
}
void Switch::SetValueLocationConstraints() {
UseAndClobberRegister(value());
// TODO(victorgomes): Create a arch-agnostic scratch register scope.
set_temporaries_needed(1);
}
void Switch::GenerateCode(MaglevAssembler* masm, const ProcessingState& state) {
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.Acquire();
std::unique_ptr<Label*[]> labels = std::make_unique<Label*[]>(size());
for (int i = 0; i < size(); i++) {
BasicBlock* block = (targets())[i].block_ptr();
block->set_start_block_of_switch_case(true);
labels[i] = block->label();
}
Register val = ToRegister(value());
// Switch requires {val} (the switch's condition) to be 64-bit, but maglev
// usually manipulates/creates 32-bit integers. We thus sign-extend {val} to
// 64-bit to have the correct value for negative numbers.
__ SignExtend32To64Bits(val, val);
__ Switch(scratch, val, value_base(), labels.get(), size());
if (has_fallthrough()) {
DCHECK_EQ(fallthrough(), state.next_block());
} else {
__ Trap();
}
}
// ---
// Print params
// ---
void ExternalConstant::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << reference() << ")";
}
void SmiConstant::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << value() << ")";
}
void TaggedIndexConstant::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << value() << ")";
}
void Int32Constant::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << value() << ")";
}
void Float64Constant::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
if (value().is_nan()) {
os << "(NaN [0x" << std::hex << value().get_bits() << std::dec << "]";
if (value().is_hole_nan()) {
os << ", the hole";
} else if (value().get_bits() ==
base::bit_cast<uint64_t>(
std::numeric_limits<double>::quiet_NaN())) {
os << ", quiet NaN";
}
os << ")";
} else {
os << "(" << value().get_scalar() << ")";
}
}
void Constant::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << *object_.object() << ")";
}
void DeleteProperty::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << LanguageMode2String(mode()) << ")";
}
void InitialValue::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << source().ToString() << ")";
}
void LoadGlobal::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << *name().object() << ")";
}
void StoreGlobal::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << *name().object() << ")";
}
void RegisterInput::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << input() << ")";
}
void RootConstant::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << RootsTable::name(index()) << ")";
}
void CreateFunctionContext::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << *scope_info().object() << ", " << slot_count() << ")";
}
void FastCreateClosure::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << *shared_function_info().object() << ", "
<< feedback_cell().object() << ")";
}
void CreateClosure::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << *shared_function_info().object() << ", "
<< feedback_cell().object();
if (pretenured()) {
os << " [pretenured]";
}
os << ")";
}
void AllocateRaw::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << allocation_type() << ", " << size() << ")";
}
void FoldedAllocation::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(+" << offset() << ")";
}
void Abort::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << GetAbortReason(reason()) << ")";
}
void AssertInt32::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << condition_ << ")";
}
void BuiltinStringPrototypeCharCodeOrCodePointAt::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
switch (mode_) {
case BuiltinStringPrototypeCharCodeOrCodePointAt::kCharCodeAt:
os << "(CharCodeAt)";
break;
case BuiltinStringPrototypeCharCodeOrCodePointAt::kCodePointAt:
os << "(CodePointAt)";
break;
}
}
void CheckMaps::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(";
bool first = true;
for (compiler::MapRef map : maps()) {
if (first) {
first = false;
} else {
os << ", ";
}
os << *map.object();
}
os << ")";
}
void CheckValue::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << *value().object() << ")";
}
void CheckValueEqualsInt32::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << value() << ")";
}
void CheckValueEqualsFloat64::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << value() << ")";
}
void CheckValueEqualsString::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << *value().object() << ")";
}
void CheckInstanceType::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << first_instance_type_;
if (first_instance_type_ != last_instance_type_) {
os << " - " << last_instance_type_;
}
os << ")";
}
void CheckMapsWithMigration::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(";
bool first = true;
for (compiler::MapRef map : maps()) {
if (first) {
first = false;
} else {
os << ", ";
}
os << *map.object();
}
os << ")";
}
void CheckInt32Condition::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << condition() << ", " << reason() << ")";
}
void CheckedNumberOrOddballToFloat64::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << conversion_type() << ")";
}
void UncheckedNumberOrOddballToFloat64::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << conversion_type() << ")";
}
void CheckedTruncateNumberOrOddballToInt32::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << conversion_type() << ")";
}
void TruncateNumberOrOddballToInt32::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << conversion_type() << ")";
}
void LoadTaggedField::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(0x" << std::hex << offset() << std::dec;
// Print compression status only after the result is allocated, since that's
// when we do decompression marking.
if (!result().operand().IsUnallocated()) {
if (decompresses_tagged_result()) {
os << ", decompressed";
} else {
os << ", compressed";
}
}
os << ")";
}
void LoadDoubleField::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(0x" << std::hex << offset() << std::dec << ")";
}
void LoadFixedArrayElement::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
// Print compression status only after the result is allocated, since that's
// when we do decompression marking.
if (!result().operand().IsUnallocated()) {
if (decompresses_tagged_result()) {
os << "(decompressed)";
} else {
os << "(compressed)";
}
}
}
void StoreDoubleField::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(0x" << std::hex << offset() << std::dec << ")";
}
void StoreFloat64::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(0x" << std::hex << offset() << std::dec << ")";
}
void StoreTaggedFieldNoWriteBarrier::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(0x" << std::hex << offset() << std::dec << ")";
}
void StoreMap::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << *map_.object() << ")";
}
void StoreTaggedFieldWithWriteBarrier::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(0x" << std::hex << offset() << std::dec << ")";
}
void LoadNamedGeneric::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << *name_.object() << ")";
}
void LoadNamedFromSuperGeneric::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << *name_.object() << ")";
}
void SetNamedGeneric::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << *name_.object() << ")";
}
void DefineNamedOwnGeneric::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << *name_.object() << ")";
}
void HasInPrototypeChain::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << *prototype_.object() << ")";
}
void GapMove::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << source() << " → " << target() << ")";
}
void ConstantGapMove::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(";
graph_labeller->PrintNodeLabel(os, node_);
os << " → " << target() << ")";
}
void Float64Compare::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << operation() << ")";
}
void Float64ToBoolean::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
if (flip()) {
os << "(flipped)";
}
}
void Int32Compare::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << operation() << ")";
}
void Int32ToBoolean::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
if (flip()) {
os << "(flipped)";
}
}
void Float64Ieee754Unary::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "("
<< ExternalReferenceTable::NameOfIsolateIndependentAddress(
ieee_function_.address())
<< ")";
}
void Float64Round::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
switch (kind_) {
case Kind::kCeil:
os << "(ceil)";
return;
case Kind::kFloor:
os << "(floor)";
return;
case Kind::kNearest:
os << "(nearest)";
return;
}
}
void Phi::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << owner().ToString() << ")";
}
void Call::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << receiver_mode_ << ", ";
switch (target_type_) {
case TargetType::kJSFunction:
os << "JSFunction";
break;
case TargetType::kAny:
os << "Any";
break;
}
os << ")";
}
void CallSelf::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << shared_function_info_.object() << ")";
}
void CallKnownJSFunction::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << shared_function_info_.object() << ")";
}
void CallKnownApiFunction::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(";
switch (mode()) {
case kNoProfiling:
os << "no profiling, ";
break;
case kNoProfilingInlined:
os << "no profiling inlined, ";
break;
case kGeneric:
break;
}
os << function_template_info_.object() << ", ";
if (api_holder_.has_value()) {
os << api_holder_.value().object();
} else {
os << "Api holder is receiver";
}
os << ")";
}
void CallBuiltin::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << Builtins::name(builtin()) << ")";
}
void CallCPPBuiltin::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << Builtins::name(builtin()) << ")";
}
void CallRuntime::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << Runtime::FunctionForId(function_id())->name << ")";
}
void TestTypeOf::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << interpreter::TestTypeOfFlags::ToString(literal_) << ")";
}
void ReduceInterruptBudgetForLoop::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << amount() << ")";
}
void ReduceInterruptBudgetForReturn::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << amount() << ")";
}
void Deopt::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << DeoptimizeReasonToString(reason()) << ")";
}
void BranchIfRootConstant::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << RootsTable::name(root_index_) << ")";
}
void BranchIfFloat64Compare::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << operation_ << ")";
}
void BranchIfInt32Compare::PrintParams(
std::ostream& os, MaglevGraphLabeller* graph_labeller) const {
os << "(" << operation_ << ")";
}
void BranchIfTypeOf::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
os << "(" << interpreter::TestTypeOfFlags::ToString(literal_) << ")";
}
void HandleNoHeapWritesInterrupt::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
ZoneLabelRef done(masm);
Label* deferred = __ MakeDeferredCode(
[](MaglevAssembler* masm, ZoneLabelRef done, Node* node) {
ASM_CODE_COMMENT_STRING(masm, "HandleNoHeapWritesInterrupt");
{
SaveRegisterStateForCall save_register_state(
masm, node->register_snapshot());
__ Move(kContextRegister, masm->native_context().object());
__ CallRuntime(Runtime::kHandleNoHeapWritesInterrupts, 0);
save_register_state.DefineSafepointWithLazyDeopt(
node->lazy_deopt_info());
}
__ Jump(*done);
},
done, this);
MaglevAssembler::ScratchRegisterScope temps(masm);
Register scratch = temps.GetDefaultScratchRegister();
MemOperand check = __ ExternalReferenceAsOperand(
ExternalReference::address_of_no_heap_write_interrupt_request(
masm->isolate()),
scratch);
__ CompareByteAndJumpIf(check, 0, kNotEqual, scratch, deferred, Label::kFar);
__ bind(*done);
}
} // namespace maglev
} // namespace internal
} // namespace v8