33class RegisterReuseMap {
36 if ([[maybe_unused]]
auto previous = Lookup(src)) {
40 map_.emplace_back(src);
41 map_.emplace_back(dst);
45 for (
auto it =
map_.begin(),
end =
map_.end(); it !=
end; it += 2) {
46 if (it->is_gp_pair() == src.is_gp_pair() &&
47 it->is_fp_pair() == src.is_fp_pair() && *it == src)
55 base::SmallVector<LiftoffRegister, 8>
map_;
58enum MergeKeepStackSlots :
bool {
59 kKeepStackSlots =
true,
60 kTurnStackSlotsIntoRegisters =
false
62enum MergeAllowConstants :
bool {
63 kConstantsAllowed =
true,
64 kConstantsNotAllowed =
false
66enum MergeAllowRegisters :
bool {
67 kRegistersAllowed =
true,
68 kRegistersNotAllowed =
false
70enum ReuseRegisters :
bool {
71 kReuseRegisters =
true,
72 kNoReuseRegisters =
false
80void InitMergeRegion(LiftoffAssembler::CacheState* target_state,
82 MergeKeepStackSlots keep_stack_slots,
83 MergeAllowConstants allow_constants,
84 MergeAllowRegisters allow_registers,
85 ReuseRegisters reuse_registers, LiftoffRegList used_regs,
86 int new_stack_offset, ParallelMove& parallel_move) {
87 RegisterReuseMap register_reuse_map;
88 for (
const VarState* source_end = source + count; source < source_end;
90 if (source->is_stack() && keep_stack_slots) {
96 if (new_stack_offset) {
99 if (new_stack_offset != source->offset()) {
100 target->set_offset(new_stack_offset);
101 parallel_move.TransferToStack(new_stack_offset, *source);
106 if (source->is_const() && allow_constants) {
108 DCHECK(!new_stack_offset);
111 std::optional<LiftoffRegister>
reg;
112 bool needs_reg_transfer =
true;
113 if (allow_registers) {
115 if (source->is_reg() && target_state->is_free(source->reg())) {
117 needs_reg_transfer =
false;
121 if (!
reg && reuse_registers) {
122 reg = register_reuse_map.Lookup(source->reg());
126 if (!
reg && target_state->has_unused_register(rc, used_regs)) {
127 reg = target_state->unused_register(rc, used_regs);
131 int target_offset = source->offset();
132 if (new_stack_offset) {
135 target_offset = new_stack_offset;
138 if (needs_reg_transfer) parallel_move.LoadIntoRegister(*
reg, *source);
139 if (reuse_registers) register_reuse_map.Add(source->reg(), *
reg);
140 target_state->inc_used(*
reg);
141 *target =
VarState(source->kind(), *
reg, target_offset);
144 *target =
VarState(source->kind(), target_offset);
145 parallel_move.TransferToStack(target_offset, *source);
153 uint32_t num_locals, uint32_t arity, uint32_t stack_depth) {
182 uint32_t target_height =
num_locals + stack_depth + arity;
184 target.stack_state.resize(target_height);
187 VarState* target_begin = target.stack_state.data();
191 const VarState* locals_source = source_begin;
193 const VarState* discarded_source = stack_prefix_source + stack_depth;
195 VarState* locals_target = target_begin;
204 if (src.is_reg()) used_regs.
set(src.reg());
210 MergeAllowRegisters allow_registers =
211 arity <= 1 ? kRegistersAllowed : kRegistersNotAllowed;
212 if (allow_registers) {
214 if (src.is_reg()) used_regs.
set(src.reg());
225 MergeKeepStackSlots keep_merge_stack_slots =
228 : kTurnStackSlotsIntoRegisters;
231 int merge_region_stack_offset = discarded_source == source_begin
233 : discarded_source[-1].
offset();
234 InitMergeRegion(&target, merge_source, merge_target, arity,
235 keep_merge_stack_slots, kConstantsNotAllowed,
236 allow_registers, kNoReuseRegisters, used_regs,
237 merge_region_stack_offset, parallel_move);
243 InitMergeRegion(&target, locals_source, locals_target,
num_locals,
244 kKeepStackSlots, kConstantsNotAllowed, kRegistersAllowed,
245 kNoReuseRegisters, used_regs, 0, parallel_move);
248 DCHECK_EQ(used_regs, target.used_registers & used_regs);
255 InitMergeRegion(&target, stack_prefix_source, stack_prefix_target,
256 stack_depth, kKeepStackSlots, kConstantsAllowed,
257 kRegistersAllowed, kReuseRegisters, used_regs, 0,
266 *
this = std::move(source);
275int GetSafepointIndexForStackSlot(
const VarState& slot) {
291 for (
const auto& slot : stack_state) {
294 if (spill_location == SpillLocation::kTopOfStack && slot.is_reg()) {
301 DCHECK_IMPLIES(slot.is_reg(), spill_location == SpillLocation::kStackSlots);
303 slots->push_back(GetSafepointIndexForStackSlot(slot));
316 if (!slot.is_stack())
continue;
324 for (
const auto& slot : stack_state) {
326 if (slot.is_stack()) {
364 std::unique_ptr<AssemblerBuffer> buffer)
405 if (dropped->is_reg()) {
413 slot !=
end; ++slot) {
417 if (slot->offset() != stack_offset) {
418 if (slot->is_stack()) {
421 slot->set_offset(stack_offset);
447 Move(dst_reg, slot.reg(), slot.kind());
450 slot.MakeRegister(dst_reg);
452 Spill(slot.offset(), slot.reg(), slot.kind());
460 if (!slot.is_const())
continue;
466 slot.MakeRegister(
reg);
468 Spill(slot.offset(), slot.constant());
478 return a.is_stack() && b.
is_stack() &&
487 for (
size_t idx = 0,
end = v.
size(); idx <
end; idx += 1 + idx / 16) {
488 if (SlotInterference(a, v[idx]))
return true;
502 DCHECK(!SlotInterference(target.stack_state[
i],
509 target.ClearCachedInstanceRegister();
513 target.ClearCachedMemStartRegister();
525 uint32_t target_stack_height = target.stack_height();
526 DCHECK_LE(target_stack_height, stack_height);
528 uint32_t stack_base = stack_height - arity;
529 uint32_t target_stack_base = target_stack_height - arity;
531 for (uint32_t
i = 0;
i < target_stack_base; ++
i) {
534 target.stack_state[
i],
536 target_stack_base -
i - 1)));
538 target.stack_state[
i],
541 for (uint32_t
i = 0;
i < arity; ++
i) {
542 parallel_move.Transfer(target.stack_state[target_stack_base +
i],
545 target.stack_state[target_stack_base +
i],
554 bool reload_instance_data =
false;
558 target.cached_instance_data !=
no_reg) {
561 target.ClearCachedInstanceRegister();
565 parallel_move.MoveRegister(
570 reload_instance_data =
true;
574 bool reload_mem_start =
false;
581 target.cached_mem_start !=
no_reg) {
584 target.ClearCachedMemStartRegister();
594 reload_mem_start =
true;
599 parallel_move.Execute();
601 if (reload_instance_data) {
604 if (reload_mem_start) {
614 if (target.cached_mem_index == 0) {
623 WasmTrustedInstanceData::kProtectedMemoryBasesAndSizesOffset));
634 switch (slot->
loc()) {
657 if (!slot.
is_reg())
continue;
666 Register reg, std::initializer_list<Register*> possible_uses,
687 for (
Register* use : possible_uses) {
688 if (
reg != *use)
continue;
689 if (replacement ==
no_reg) {
708 uint32_t call_desc_input_idx =
709 static_cast<uint32_t
>(call_descriptor->
InputCount());
710 uint32_t num_params =
static_cast<uint32_t
>(
sig->parameter_count());
711 for (uint32_t
i = num_params;
i > 0; --
i) {
712 const uint32_t param =
i - 1;
715 const int num_lowered_params = is_gp_pair ? 2 : 1;
716 const VarState& slot = slots[param];
720 for (
int lowered_idx = 0; lowered_idx < num_lowered_params; ++lowered_idx) {
723 --call_desc_input_idx;
751 std::initializer_list<VarState> params) {
755 PrepareStackTransfers(
sig, call_descriptor, params.begin(), &
stack_slots,
756 ¶llel_move, ¶m_regs);
759 if (param_slots > 0) {
774 uint32_t num_params =
static_cast<uint32_t
>(
sig->parameter_count());
787 param_regs.
set(instance_reg);
788 if (target_instance_data ==
no_reg) {
791 if (target_instance_data !=
no_reg && target_instance_data != instance_reg) {
800 PrepareStackTransfers(
sig, call_descriptor,
802 ¶llel_move, ¶m_regs);
832 if (!slot->
is_reg())
continue;
842 [](
const VarState& slot) { return slot.is_stack() || slot.is_const(); }));
844 if (param_slots > 0) {
851 if (target_instance_data ==
no_reg) {
867 int call_desc_return_idx = 0;
871 const int num_lowered_params = 1 + needs_gp_pair;
872 const ValueKind lowered_kind = needs_gp_pair ?
kI32 : return_kind;
880 for (
int pair_idx = 0; pair_idx < num_lowered_params; ++pair_idx) {
897 pinned.
set(reg_pair[0]);
900 if (num_lowered_params == 1) {
907 int return_slots =
static_cast<int>(call_descriptor->
ReturnSlotCount());
923 }
else if (dst.
is_gp()) {
933 for (
auto tuple : tuples) {
934 if (tuple.dst == tuple.src)
continue;
935 parallel_move.
MoveRegister(tuple.dst, tuple.src, tuple.kind);
963 if (slot.
reg() != return_reg) {
986 int call_desc_return_idx = 0;
991 if (
v8_flags.experimental_wasm_growable_stacks) {
996 for (
size_t i = 0;
i <
sig->return_count(); ++
i) {
999 int num_lowered_params = 1 + needs_gp_pair;
1000 for (
int pair_idx = 0; pair_idx < num_lowered_params; ++pair_idx) {
1009 ValueKind lowered_kind = needs_gp_pair ?
kI32 : return_kind;
1016 call_desc_return_idx = 0;
1017 for (
size_t i = 0;
i <
sig->return_count(); ++
i) {
1020 int num_lowered_params = 1 + needs_gp_pair;
1021 for (
int pair_idx = 0; pair_idx < num_lowered_params; ++pair_idx) {
1028 ValueKind lowered_kind = needs_gp_pair ?
kI32 : return_kind;
1033 if (needs_gp_pair) {
1048void LiftoffRegList::Print()
const {
1049 std::ostringstream os;
1050 os << *
this <<
"\n";
1051 PrintF(
"%s", os.str().c_str());
1055#ifdef ENABLE_SLOW_DCHECKS
1056bool LiftoffAssembler::ValidateCacheState()
const {
1058 LiftoffRegList used_regs;
1064 if (!var.is_reg())
continue;
1065 LiftoffRegister
reg = var.reg();
1067 ++register_use_count[
reg.low().liftoff_code()];
1068 ++register_use_count[
reg.high().liftoff_code()];
1070 ++register_use_count[
reg.liftoff_code()];
1074 for (Register cache_reg :
1076 if (cache_reg !=
no_reg) {
1077 DCHECK(!used_regs.has(cache_reg));
1078 int liftoff_code = LiftoffRegister{cache_reg}.liftoff_code();
1079 used_regs.set(cache_reg);
1080 DCHECK_EQ(0, register_use_count[liftoff_code]);
1081 register_use_count[liftoff_code] = 1;
1085 sizeof(register_use_count)) == 0 &&
1087 if (valid)
return true;
1088 std::ostringstream os;
1089 os <<
"Error in LiftoffAssembler::ValidateCacheState().\n";
1090 os <<
"expected: used_regs " << used_regs <<
", counts "
1094 os <<
"Use --trace-wasm-decoder and --trace-liftoff to debug.";
1095 FATAL(
"%s", os.str().c_str());
1125 if (last_fp.
fp().
code() % 2 == 0) {
1126 pinned.
set(last_fp);
1146 if (first_reg.
fp().
code() % 2 == 0) {
1152 low_reg = second_reg;
1180 if (--remaining_uses == 0)
break;
1198 switch (slot.
loc()) {
1200 return os <<
"s0x" << std::hex << slot.
offset() << std::dec;
1202 return os << slot.
reg();
constexpr size_t size() const
Simd128Register Simd128Register Simd128Register Simd128Register rc
static constexpr int kFixedFrameSizeAboveFp
bool IsAnyRegister() const
int32_t AsRegister() const
bool IsCallerFrameSlot() const
int32_t GetLocation() const
NO_INLINE_FOR_ARM64_MSVC bool IsRegister() const
int32_t AsCallerFrameSlot() const
void set_abort_hard(bool v)
constexpr int8_t code() const
static constexpr Register from_code(int code)
void DefineTaggedRegister(int reg_code)
void DefineTaggedStackSlot(int index)
size_t ReturnSlotCount() const
size_t ParameterSlotCount() const
size_t ReturnCount() const
size_t InputCount() const
LinkageLocation GetReturnLocation(size_t index) const
LinkageLocation GetInputLocation(size_t index) const
int GetOffsetToReturns() const
void ClearRegister(Register reg, std::initializer_list< Register * > possible_uses, LiftoffRegList pinned)
static constexpr ValueKind kSmiKind
static constexpr uint32_t kInlineLocalKinds
ValueKind * more_local_kinds_
int ool_spill_space_size_
V8_NOINLINE V8_PRESERVE_MOST LiftoffRegister LoadToRegister_Slow(VarState slot, LiftoffRegList pinned)
void DropValues(int count)
void RecordUsedSpillOffset(int offset)
void FillI64Half(Register, int offset, RegPairHalf)
LiftoffAssembler(Zone *, std::unique_ptr< AssemblerBuffer >)
V8_NODISCARD CacheState MergeIntoNewState(uint32_t num_locals, uint32_t arity, uint32_t stack_depth)
void MergeStackWith(CacheState &target, uint32_t arity, JumpDirection)
void LoadFullPointer(Register dst, Register src_addr, int32_t offset_imm)
int max_used_spill_offset_
int TopSpillOffset() const
static V8_INLINE int NextSpillOffset(ValueKind kind, int top_spill_offset)
V8_INLINE LiftoffRegister LoadToRegister(VarState slot, LiftoffRegList pinned)
Signature< ValueKind > ValueKindSig
void PrepareCall(const ValueKindSig *, compiler::CallDescriptor *, Register *target=nullptr, Register target_instance=no_reg)
void StoreCallerFrameSlot(LiftoffRegister, uint32_t caller_slot_idx, ValueKind, Register frame_pointer)
void Spill(VarState *slot)
~LiftoffAssembler() override
LiftoffRegister LoadI64HalfIntoRegister(VarState slot, RegPairHalf half, LiftoffRegList pinned)
void PrepareForBranch(uint32_t arity, LiftoffRegList pinned)
void LoadConstant(LiftoffRegister, WasmValue)
uint32_t num_locals() const
void LoadFromInstance(Register dst, Register instance, int offset, int size)
void SpillLoopArgs(int num)
void LoadProtectedPointer(Register dst, Register src_addr, int32_t offset)
void LoadInstanceDataFromFrame(Register dst)
void DropExceptionValueAtOffset(int offset)
LiftoffRegister SpillAdjacentFpRegisters(LiftoffRegList pinned)
void ParallelRegisterMove(base::Vector< const ParallelRegisterMoveTuple >)
void MoveStackValue(uint32_t dst_offset, uint32_t src_offset, ValueKind)
void Move(LiftoffRegister dst, LiftoffRegister src, ValueKind)
CacheState * cache_state()
void set_num_locals(uint32_t num_locals)
int OolSpillCount() const
void PushRegister(ValueKind kind, LiftoffRegister reg)
void FinishCall(const ValueKindSig *, compiler::CallDescriptor *)
void MoveToReturnLocations(const FunctionSig *, compiler::CallDescriptor *)
void LoadToFixedRegister(VarState slot, LiftoffRegister reg)
LiftoffRegister GetUnusedRegister(RegClass rc, std::initializer_list< LiftoffRegister > try_first, LiftoffRegList pinned)
void MergeFullStackWith(CacheState &target)
V8_NOINLINE V8_PRESERVE_MOST LiftoffRegister SpillOneRegister(LiftoffRegList candidates)
static constexpr ValueKind kIntPtrKind
void PrepareBuiltinCall(const ValueKindSig *sig, compiler::CallDescriptor *call_descriptor, std::initializer_list< VarState > params)
void LoadReturnStackSlot(LiftoffRegister, int offset, ValueKind)
Register LoadOldFramePointer()
int GetTotalFrameSlotCountForGC() const
V8_NOINLINE V8_PRESERVE_MOST void MoveToReturnLocationsMultiReturn(const FunctionSig *, compiler::CallDescriptor *)
V8_NOINLINE V8_PRESERVE_MOST void SpillRegister(LiftoffRegister)
static constexpr int StaticStackFrameSize()
constexpr Register set(Register reg)
LiftoffRegister GetLastRegSet() const
LiftoffRegList MaskOut(const LiftoffRegList mask) const
constexpr bool HasAdjacentFpRegsSet() const
bool has(LiftoffRegister reg) const
constexpr bool is_empty() const
LiftoffRegister GetFirstRegSet() const
constexpr LiftoffRegList SpreadSetBitsToAdjacentFpRegs() const
constexpr DoubleRegister fp() const
static LiftoffRegister from_liftoff_code(int code)
static LiftoffRegister ForFpPair(DoubleRegister low)
bool overlaps(const LiftoffRegister other) const
constexpr bool is_gp() const
LiftoffRegister high() const
constexpr RegClass reg_class() const
constexpr bool is_gp_pair() const
static LiftoffRegister from_external_code(RegClass rc, ValueKind kind, int code)
static LiftoffRegister ForPair(Register low, Register high)
constexpr bool is_fp_pair() const
LiftoffRegister low() const
DoubleRegister low_fp() const
constexpr int liftoff_code() const
constexpr bool is_pair() const
constexpr Register gp() const
WasmValue constant() const
LiftoffRegister reg() const
int32_t i32_const() const
static constexpr int ToTagged(int offset)
void MoveRegister(LiftoffRegister dst, LiftoffRegister src, ValueKind kind)
V8_INLINE void LoadIntoRegister(LiftoffRegister dst, const VarState &src)
void LoadI64HalfIntoRegister(LiftoffRegister dst, const VarState &src, RegPairHalf half)
#define ASM_CODE_COMMENT(asm)
ZoneVector< OpIndex > candidates
DirectHandle< Object > new_target
ZoneVector< RpoNumber > & result
Register cached_instance_data
base::SmallVector< int32_t, 1 > stack_slots
InstructionOperand source
constexpr Vector< T > VectorOf(T *start, size_t size)
void * Malloc(size_t size)
static constexpr RegClass reg_class_for(ValueKind kind)
constexpr DoubleRegister kFpReturnRegisters[]
constexpr Register kGpParamRegisters[]
static constexpr bool needs_gp_reg_pair(ValueKind kind)
static constexpr bool needs_fp_reg_pair(ValueKind kind)
LiftoffAssembler::VarState VarState
static constexpr int kAfterMaxLiftoffRegCode
static constexpr bool kNeedS128RegPair
constexpr Register kGpReturnRegisters[]
constexpr bool is_object_reference(ValueKind kind)
constexpr int value_kind_size(ValueKind kind)
static constexpr LiftoffRegList kGpCacheRegList
std::ostream & operator<<(std::ostream &os, LiftoffVarState slot)
static constexpr LiftoffRegList kFpCacheRegList
constexpr bool is_reference(ValueKind kind)
static constexpr bool kNeedI64RegPair
constexpr Register no_reg
void PrintF(const char *format,...)
kWasmInternalFunctionIndirectPointerTag instance_data
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in name
kWasmInternalFunctionIndirectPointerTag kProtectedInstanceDataOffset sig
constexpr int kSystemPointerSize
V8_EXPORT_PRIVATE FlagValues v8_flags
constexpr int kUInt32Size
auto PrintCollection(const T &collection) -> PrintIteratorRange< typename std::common_type< decltype(std::begin(collection)), decltype(std::end(collection))>::type >
#define DCHECK_LE(v1, v2)
#define DCHECK_NOT_NULL(val)
#define DCHECK_IMPLIES(v1, v2)
#define DCHECK_NE(v1, v2)
#define DCHECK(condition)
#define DCHECK_LT(v1, v2)
#define DCHECK_EQ(v1, v2)
#define DCHECK_GT(v1, v2)
uint32_t register_use_count[kAfterMaxLiftoffRegCode]
void ClearCachedInstanceRegister()
Register cached_instance_data
static constexpr int kNoCachedMemIndex
uint32_t stack_height() const
LiftoffRegister GetNextSpillReg(LiftoffRegList candidates)
LiftoffRegister unused_register(RegClass rc, LiftoffRegList pinned={}) const
LiftoffRegList last_spilled_regs
Register cached_mem_start
void dec_used(LiftoffRegister reg)
LiftoffRegister take_volatile_register(LiftoffRegList candidates)
bool is_used(LiftoffRegister reg) const
void DefineSafepoint(SafepointTableBuilder::Safepoint &safepoint)
SmallZoneVector< VarState, 16 > stack_state
void Split(const CacheState &source)
void GetTaggedSlotsForOOLCode(ZoneVector< int > *slots, LiftoffRegList *spills, SpillLocation spill_location)
void reset_used_registers()
void ClearAllCacheRegisters()
void DefineSafepointWithCalleeSavedRegisters(SafepointTableBuilder::Safepoint &safepoint)
void clear_used(LiftoffRegister reg)
bool has_unused_register(RegClass rc, LiftoffRegList pinned={}) const
uint32_t get_use_count(LiftoffRegister reg) const
LiftoffRegList used_registers
void Steal(CacheState &source)
void ClearCachedMemStartRegister()
void inc_used(LiftoffRegister reg)
bool has_volatile_register(LiftoffRegList candidates)
#define OFFSET_OF_DATA_START(Type)
#define V8_LIKELY(condition)
#define V8_UNLIKELY(condition)