[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:
parent
0527094a05
commit
8ddac25e16
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user