[liftoff] Outline spilling and reloading code

The assumption is that {PopToRegister} most often finds a register stack
slot (this is backed by data). Hence put all spilling-related code
behind no-inline and preserve_most functions.
Also, annotate some methods that are supposed to be inlined with
V8_INLINE. This is not needed (they were already inlined before), but
this documents the intend better.

This saves some binary size and seems to also slightly improve
performance.

R=ahaas@chromium.org

Bug: v8:13565, v8:13673
Change-Id: Ib4b8bd361ee19c29221263f6383034933fe7dff5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4212407
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/main@{#85629}
This commit is contained in:
Clemens Backes 2023-02-02 14:45:56 +01:00 committed by V8 LUCI CQ
parent c07d8535f8
commit 99936e2de9
2 changed files with 56 additions and 34 deletions

View File

@ -678,22 +678,11 @@ LiftoffAssembler::~LiftoffAssembler() {
}
}
LiftoffRegister LiftoffAssembler::LoadToRegister(VarState slot,
LiftoffRegList pinned) {
if (slot.is_reg()) return slot.reg();
LiftoffRegister reg = GetUnusedRegister(reg_class_for(slot.kind()), pinned);
return LoadToRegister(slot, reg);
}
LiftoffRegister LiftoffAssembler::LoadToRegister(VarState slot,
LiftoffRegister reg) {
if (slot.is_const()) {
LoadConstant(reg, slot.constant());
} else {
DCHECK(slot.is_stack());
Fill(reg, slot.offset(), slot.kind());
}
return reg;
void LiftoffAssembler::LoadToRegister(VarState slot, LiftoffRegList pinned,
LiftoffRegister* out_reg) {
DCHECK(!slot.is_reg());
*out_reg = GetUnusedRegister(reg_class_for(slot.kind()), pinned);
LoadToFixedRegister(slot, *out_reg);
}
LiftoffRegister LiftoffAssembler::LoadI64HalfIntoRegister(VarState slot,
@ -718,9 +707,7 @@ LiftoffRegister LiftoffAssembler::PeekToRegister(int index,
LiftoffRegList pinned) {
DCHECK_LT(index, cache_state_.stack_state.size());
VarState& slot = cache_state_.stack_state.end()[-1 - index];
if (slot.is_reg()) {
return slot.reg();
}
if (V8_LIKELY(slot.is_reg())) return slot.reg();
LiftoffRegister reg = LoadToRegister(slot, pinned);
cache_state_.inc_used(reg);
slot.MakeRegister(reg);
@ -1372,11 +1359,10 @@ bool LiftoffAssembler::ValidateCacheState() const {
}
#endif
LiftoffRegister LiftoffAssembler::SpillOneRegister(LiftoffRegList candidates) {
// Spill one cached value to free a register.
LiftoffRegister spill_reg = cache_state_.GetNextSpillReg(candidates);
SpillRegister(spill_reg);
return spill_reg;
void LiftoffAssembler::SpillOneRegister(LiftoffRegList candidates,
LiftoffRegister* spilled_reg) {
*spilled_reg = cache_state_.GetNextSpillReg(candidates);
SpillRegister(*spilled_reg);
}
LiftoffRegister LiftoffAssembler::SpillAdjacentFpRegisters(

View File

@ -469,15 +469,41 @@ class LiftoffAssembler : public MacroAssembler {
explicit LiftoffAssembler(std::unique_ptr<AssemblerBuffer>);
~LiftoffAssembler() override;
LiftoffRegister LoadToRegister(VarState slot, LiftoffRegList pinned);
// Load a cache slot to a free register.
V8_INLINE LiftoffRegister LoadToRegister(VarState slot,
LiftoffRegList pinned) {
if (V8_LIKELY(slot.is_reg())) return slot.reg();
// TODO(clemensb): Remove this hack once https://reviews.llvm.org/D141020 is
// available.
std::aligned_storage_t<sizeof(LiftoffRegister), alignof(LiftoffRegister)>
reg_storage;
LiftoffRegister* out_reg = reinterpret_cast<LiftoffRegister*>(&reg_storage);
LoadToRegister(slot, pinned, out_reg);
return *out_reg;
}
LiftoffRegister LoadToRegister(VarState slot, LiftoffRegister dst);
// Slow path called for the method above.
// TODO(clemensb): Use a return value instead of output parameter once
// https://reviews.llvm.org/D141020 is available.
V8_NOINLINE V8_PRESERVE_MOST void LoadToRegister(VarState slot,
LiftoffRegList pinned,
LiftoffRegister* dst);
LiftoffRegister PopToRegister(LiftoffRegList pinned = {}) {
// Load a non-register cache slot to a given (fixed) register.
void LoadToFixedRegister(VarState slot, LiftoffRegister reg) {
DCHECK(slot.is_const() || slot.is_stack());
if (slot.is_const()) {
LoadConstant(reg, slot.constant());
} else {
Fill(reg, slot.offset(), slot.kind());
}
}
V8_INLINE LiftoffRegister PopToRegister(LiftoffRegList pinned = {}) {
DCHECK(!cache_state_.stack_state.empty());
VarState slot = cache_state_.stack_state.back();
cache_state_.stack_state.pop_back();
if (slot.is_reg()) {
if (V8_LIKELY(slot.is_reg())) {
cache_state_.dec_used(slot.reg());
return slot.reg();
}
@ -488,7 +514,7 @@ class LiftoffAssembler : public MacroAssembler {
DCHECK(!cache_state_.stack_state.empty());
VarState slot = cache_state_.stack_state.back();
cache_state_.stack_state.pop_back();
if (slot.is_reg()) {
if (V8_LIKELY(slot.is_reg())) {
cache_state_.dec_used(slot.reg());
if (slot.reg() == reg) return;
if (cache_state_.is_used(reg)) SpillRegister(reg);
@ -496,7 +522,7 @@ class LiftoffAssembler : public MacroAssembler {
return;
}
if (cache_state_.is_used(reg)) SpillRegister(reg);
LoadToRegister(slot, reg);
LoadToFixedRegister(slot, reg);
}
// Use this to pop a value into a register that has no other uses, so it
@ -576,7 +602,7 @@ class LiftoffAssembler : public MacroAssembler {
cache_state_.stack_state.emplace_back(kind, NextSpillOffset(kind));
}
void SpillRegister(LiftoffRegister);
V8_NOINLINE V8_PRESERVE_MOST void SpillRegister(LiftoffRegister);
uint32_t GetNumUses(LiftoffRegister reg) const {
return cache_state_.get_use_count(reg);
@ -621,13 +647,19 @@ class LiftoffAssembler : public MacroAssembler {
LiftoffRegister GetUnusedRegister(LiftoffRegList candidates) {
DCHECK(!cache_state_.frozen);
DCHECK(!candidates.is_empty());
if (cache_state_.has_unused_register(candidates)) {
if (V8_LIKELY(cache_state_.has_unused_register(candidates))) {
return cache_state_.unused_register(candidates);
}
if (cache_state_.has_volatile_register(candidates)) {
return cache_state_.take_volatile_register(candidates);
}
return SpillOneRegister(candidates);
// TODO(clemensb): Remove this hack once https://reviews.llvm.org/D141020 is
// available.
std::aligned_storage_t<sizeof(LiftoffRegister), alignof(LiftoffRegister)>
reg_storage;
LiftoffRegister* out_reg = reinterpret_cast<LiftoffRegister*>(&reg_storage);
SpillOneRegister(candidates, out_reg);
return *out_reg;
}
// Performs operations on locals and the top {arity} value stack entries
@ -1655,7 +1687,11 @@ class LiftoffAssembler : public MacroAssembler {
private:
LiftoffRegister LoadI64HalfIntoRegister(VarState slot, RegPairHalf half);
V8_NOINLINE LiftoffRegister SpillOneRegister(LiftoffRegList candidates);
// Spill one of the candidate registers.
// TODO(clemensb): Use return value instead of output parameter once
// https://reviews.llvm.org/D141020 is available.
V8_NOINLINE V8_PRESERVE_MOST void SpillOneRegister(
LiftoffRegList candidates, LiftoffRegister* spilled_reg);
// Spill one or two fp registers to get a pair of adjacent fp registers.
LiftoffRegister SpillAdjacentFpRegisters(LiftoffRegList pinned);