[maglev] Encode 'clobbered' as 'free+blocked'

Rather than encoding clobbering with a separate reglist, use the
concept of being "free and blocked" for clobbering. This makes sure that
clobbered registers are not used in later input allocations, and that
two clobbering inputs don't alias. Probably in the future we want to
process clobbering inputs first, before non-clobbering ones, to make
sure that no clobbering input can alias a non-clobbering input (even if
the non-clobbering input is an earlier one).

Also add some documentation to RegisterFrameState to explain these
different states.

Bug: v8:7700
Change-Id: I328e707539be301db50a29f606c15e7eddfe778b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4003160
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Victor Gomes <victorgomes@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84056}
This commit is contained in:
Leszek Swirski 2022-11-03 17:49:42 +01:00 committed by V8 LUCI CQ
parent 0527094a05
commit 8ddac25e16
2 changed files with 50 additions and 30 deletions

View File

@ -597,10 +597,6 @@ void StraightForwardRegisterAllocator::AllocateNode(Node* node) {
AllocateNodeResult(node->Cast<ValueNode>());
}
// Free clobbered registers.
general_registers_.FreeClobbered();
double_registers_.FreeClobbered();
if (v8_flags.trace_maglev_regalloc) {
printing_visitor_->os() << "Updating uses...\n";
}
@ -742,8 +738,6 @@ void StraightForwardRegisterAllocator::DropRegisterValue(
RegisterFrameState<RegisterT>& registers, RegisterT reg) {
// The register should not already be free.
DCHECK(!registers.free().has(reg));
// We are only allowed to allocated blocked registers at the end.
DCHECK(!registers.is_blocked(reg));
ValueNode* node = registers.GetValue(reg);
@ -1073,14 +1067,18 @@ void StraightForwardRegisterAllocator::AssignFixedInput(Input& input) {
}
}
void StraightForwardRegisterAllocator::AddToClobbered(
void StraightForwardRegisterAllocator::MarkAsClobbered(
ValueNode* node, const compiler::AllocatedOperand& location) {
// Ensure node is available on the stack.
Spill(node);
if (node->use_double_register()) {
double_registers_.Clobber(location.GetDoubleRegister());
DoubleRegister reg = location.GetDoubleRegister();
DCHECK(double_registers_.is_blocked(reg));
DropRegisterValue(reg);
double_registers_.AddToFree(reg);
} else {
general_registers_.Clobber(location.GetRegister());
Register reg = location.GetRegister();
DCHECK(general_registers_.is_blocked(reg));
DropRegisterValue(reg);
general_registers_.AddToFree(reg);
}
}
@ -1115,13 +1113,13 @@ void StraightForwardRegisterAllocator::AssignArbitraryRegisterInput(
? double_registers_.ChooseInputRegister(node)
: general_registers_.ChooseInputRegister(node);
if (input.Cloberred()) {
AddToClobbered(node, location);
MarkAsClobbered(node, location);
}
input.SetAllocated(location);
} else {
compiler::AllocatedOperand allocation = AllocateRegister(node);
if (input.Cloberred()) {
AddToClobbered(node, allocation);
MarkAsClobbered(node, allocation);
}
input.SetAllocated(allocation);
DCHECK_NE(location, allocation);
@ -1168,14 +1166,14 @@ void StraightForwardRegisterAllocator::VerifyInputs(NodeBase* node) {
if (input.operand().IsRegister()) {
Register reg =
compiler::AllocatedOperand::cast(input.operand()).GetRegister();
if (general_registers_.GetValue(reg) != input.node()) {
if (general_registers_.GetValueMaybeFreeButBlocked(reg) != input.node()) {
FATAL("Input node n%d is not in expected register %s",
graph_labeller()->NodeId(input.node()), RegisterName(reg));
}
} else if (input.operand().IsDoubleRegister()) {
DoubleRegister reg =
compiler::AllocatedOperand::cast(input.operand()).GetDoubleRegister();
if (double_registers_.GetValue(reg) != input.node()) {
if (double_registers_.GetValueMaybeFreeButBlocked(reg) != input.node()) {
FATAL("Input node n%d is not in expected register %s",
graph_labeller()->NodeId(input.node()), RegisterName(reg));
}
@ -1385,6 +1383,7 @@ RegisterT StraightForwardRegisterAllocator::FreeUnblockedRegister() {
RegisterFrameState<RegisterT>& registers = GetRegisterFrameState<RegisterT>();
RegisterT best = PickRegisterToFree<RegisterT>(registers.blocked());
DCHECK(best.is_valid());
DCHECK(!registers.is_blocked(best));
DropRegisterValue(registers, best);
registers.AddToFree(best);
return best;
@ -1462,6 +1461,7 @@ compiler::AllocatedOperand StraightForwardRegisterAllocator::ForceAllocate(
node->GetMachineRepresentation(),
reg.code());
} else {
DCHECK(!registers.is_blocked(reg));
DropRegisterValue(registers, reg);
}
#ifdef DEBUG

View File

@ -20,6 +20,26 @@ class MaglevCompilationInfo;
class MaglevPrintingVisitor;
class MergePointRegisterState;
// Represents the state of the register frame during register allocation,
// including current register values, and the state of each register.
//
// Register state encodes two orthogonal concepts:
//
// 1. Used/free registers: Which registers currently hold a valid value,
// 2. Blocked/unblocked registers: Which registers can be modified during the
// current allocation.
//
// The combination of these encodes values in different states:
//
// Free + unblocked: Completely unused registers which can be used for
// anything.
// Used + unblocked: Live values that can be spilled if there is register
// pressure.
// Used + blocked: Values that are in a register and are used as an input in
// the current allocation.
// Free + blocked: Unused registers that are reserved as temporaries, or
// inputs that will get clobbered during the execution of the
// node being allocated.
template <typename RegisterT>
class RegisterFrameState {
public:
@ -60,18 +80,6 @@ class RegisterFrameState {
void AddToFree(RegisterT reg) { free_.set(reg); }
void AddToFree(RegTList list) { free_ |= list; }
void Clobber(RegisterT reg) {
DCHECK(!free_.has(reg));
clobbered_.set(reg);
}
void FreeClobbered() {
for (RegisterT reg : clobbered_) {
if (free_.has(reg)) continue; // Already freed.
FreeRegistersUsedBy(GetValue(reg));
}
clobbered_ = kEmptyRegList;
}
void FreeRegistersUsedBy(ValueNode* node) {
RegTList list = node->ClearRegisters<RegisterT>();
DCHECK_EQ(free_ & list, kEmptyRegList);
@ -97,6 +105,19 @@ class RegisterFrameState {
DCHECK_NOT_NULL(node);
return node;
}
#ifdef DEBUG
// Like GetValue, but allow reading freed registers as long as they were also
// blocked. This allows us to DCHECK expected register state against node
// state, even if that node is dead or clobbered by the end of the current
// allocation.
ValueNode* GetValueMaybeFreeButBlocked(RegisterT reg) const {
DCHECK(!free_.has(reg) || blocked_.has(reg));
ValueNode* node = values_[reg.code()];
DCHECK_NOT_NULL(node);
return node;
}
#endif
RegTList blocked() const { return blocked_; }
void block(RegisterT reg) { blocked_.set(reg); }
void unblock(RegisterT reg) { blocked_.clear(reg); }
@ -110,7 +131,6 @@ class RegisterFrameState {
ValueNode* values_[RegisterT::kNumRegisters];
RegTList free_ = kAllocatableRegisters;
RegTList blocked_ = kEmptyRegList;
RegTList clobbered_ = kEmptyRegList;
};
class StraightForwardRegisterAllocator {
@ -148,8 +168,8 @@ class StraightForwardRegisterAllocator {
void UpdateUse(const EagerDeoptInfo& deopt_info);
void UpdateUse(const LazyDeoptInfo& deopt_info);
void AddToClobbered(ValueNode* node,
const compiler::AllocatedOperand& location);
void MarkAsClobbered(ValueNode* node,
const compiler::AllocatedOperand& location);
void AllocateControlNode(ControlNode* node, BasicBlock* block);
void AllocateNode(Node* node);