Commit cc692a98 authored by Leszek Swirski's avatar Leszek Swirski Committed by V8 LUCI CQ

[maglev] Fix phi injection for empty blocks

Empty blocks were skipped when performing register merges (since they
don't have a state), but we were still doing phi value injection only
when visiting the empty block. This meant that empty blocks have
inconsistent register state with the nodes they are trying to use.

Fix this by removing the skipping code, and adding a register merge
state to empty blocks.

Bug: v8:7700
Change-Id: I305b1474c1f956c5c5775c62e3cd1d0c70b5cfdc
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3698553Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81129}
parent 76a07814
...@@ -57,11 +57,22 @@ class BasicBlock { ...@@ -57,11 +57,22 @@ class BasicBlock {
return empty_block_predecessor_; return empty_block_predecessor_;
} }
MergePointRegisterState& empty_block_register_state() {
DCHECK(is_empty_block());
return *empty_block_register_state_;
}
void set_empty_block_register_state(MergePointRegisterState* register_state) {
DCHECK(is_empty_block());
empty_block_register_state_ = register_state;
}
void set_empty_block_predecessor(BasicBlock* predecessor) { void set_empty_block_predecessor(BasicBlock* predecessor) {
DCHECK(nodes_.is_empty()); DCHECK(nodes_.is_empty());
DCHECK(control_node()->Is<Jump>()); DCHECK(control_node()->Is<Jump>());
DCHECK_NULL(state_); DCHECK_NULL(state_);
is_empty_block_ = true; is_empty_block_ = true;
empty_block_register_state_ = nullptr;
empty_block_predecessor_ = predecessor; empty_block_predecessor_ = predecessor;
} }
...@@ -87,7 +98,7 @@ class BasicBlock { ...@@ -87,7 +98,7 @@ class BasicBlock {
DCHECK(has_state()); DCHECK(has_state());
return state_; return state_;
} }
bool has_state() const { return state_ != nullptr && !is_empty_block(); } bool has_state() const { return !is_empty_block() && state_ != nullptr; }
private: private:
bool is_empty_block_ = false; bool is_empty_block_ = false;
...@@ -95,8 +106,9 @@ class BasicBlock { ...@@ -95,8 +106,9 @@ class BasicBlock {
ControlNode* control_node_; ControlNode* control_node_;
union { union {
MergePointInterpreterFrameState* state_; MergePointInterpreterFrameState* state_;
BasicBlock* empty_block_predecessor_; MergePointRegisterState* empty_block_register_state_;
}; };
BasicBlock* empty_block_predecessor_;
Label label_; Label label_;
}; };
......
...@@ -239,6 +239,8 @@ void StraightForwardRegisterAllocator::AllocateRegisters(Graph* graph) { ...@@ -239,6 +239,8 @@ void StraightForwardRegisterAllocator::AllocateRegisters(Graph* graph) {
// Restore mergepoint state. // Restore mergepoint state.
if (block->has_state()) { if (block->has_state()) {
InitializeRegisterValues(block->state()->register_state()); InitializeRegisterValues(block->state()->register_state());
} else if (block->is_empty_block()) {
InitializeRegisterValues(block->empty_block_register_state());
} }
if (FLAG_trace_maglev_regalloc) { if (FLAG_trace_maglev_regalloc) {
...@@ -572,18 +574,46 @@ void StraightForwardRegisterAllocator::DropRegisterValue( ...@@ -572,18 +574,46 @@ void StraightForwardRegisterAllocator::DropRegisterValue(
DropRegisterValue<DoubleRegister>(double_registers_, reg, stage); DropRegisterValue<DoubleRegister>(double_registers_, reg, stage);
} }
void StraightForwardRegisterAllocator::InitializeConditionalBranchRegisters( void StraightForwardRegisterAllocator::InitializeBranchTargetPhis(
ConditionalControlNode* control_node, BasicBlock* target) { int predecessor_id, BasicBlock* target) {
if (target->is_empty_block()) { DCHECK(!target->is_empty_block());
// Jumping over an empty block, so we're in fact merging.
Jump* jump = target->control_node()->Cast<Jump>(); if (!target->has_phi()) return;
target = jump->target(); Phi::List* phis = target->phis();
return MergeRegisterValues(control_node, target, jump->predecessor_id()); for (Phi* phi : *phis) {
Input& input = phi->input(predecessor_id);
input.InjectLocation(input.node()->allocation());
// Write the node to the phi's register (if any), to make sure
// register state is accurate for MergeRegisterValues later.
if (phi->result().operand().IsAnyRegister()) {
DCHECK(!phi->result().operand().IsDoubleRegister());
Register reg = phi->result().AssignedGeneralRegister();
if (!general_registers_.free().has(reg)) {
// Drop the value currently in the register, using AtStart to treat
// pre-jump gap moves as if they were inputs.
DropRegisterValue(general_registers_, reg, AllocationStage::kAtStart);
} else {
general_registers_.RemoveFromFree(reg);
}
general_registers_.SetValue(reg, input.node());
}
} }
for (Phi* phi : *phis) UpdateUse(&phi->input(predecessor_id));
}
void StraightForwardRegisterAllocator::InitializeConditionalBranchTarget(
ConditionalControlNode* control_node, BasicBlock* target) {
DCHECK(!target->has_phi());
if (target->has_state()) { if (target->has_state()) {
// Not a fall-through branch, copy the state over. // Not a fall-through branch, copy the state over.
return InitializeBranchTargetRegisterValues(control_node, target); return InitializeBranchTargetRegisterValues(control_node, target);
} }
if (target->is_empty_block()) {
return InitializeEmptyBlockRegisterValues(control_node, target);
}
// Clear dead fall-through registers. // Clear dead fall-through registers.
DCHECK_EQ(control_node->id() + 1, target->first_id()); DCHECK_EQ(control_node->id() + 1, target->first_id());
ClearDeadFallthroughRegisters<Register>(general_registers_, control_node, ClearDeadFallthroughRegisters<Register>(general_registers_, control_node,
...@@ -605,49 +635,19 @@ void StraightForwardRegisterAllocator::AllocateControlNode(ControlNode* node, ...@@ -605,49 +635,19 @@ void StraightForwardRegisterAllocator::AllocateControlNode(ControlNode* node,
if (node->properties().is_call()) SpillAndClearRegisters(); if (node->properties().is_call()) SpillAndClearRegisters();
// Inject allocation into target phis.
if (auto unconditional = node->TryCast<UnconditionalControlNode>()) {
BasicBlock* target = unconditional->target();
if (target->has_phi()) {
Phi::List* phis = target->phis();
for (Phi* phi : *phis) {
Input& input = phi->input(block->predecessor_id());
input.InjectLocation(input.node()->allocation());
// Write the node to the phi's register (if any), to make sure
// register state is accurate for MergeRegisterValues later.
if (phi->result().operand().IsAnyRegister()) {
DCHECK(!phi->result().operand().IsDoubleRegister());
Register reg = phi->result().AssignedGeneralRegister();
if (!general_registers_.free().has(reg)) {
// Drop the value currently in the register, using AtStart to treat
// pre-jump gap moves as if they were inputs.
DropRegisterValue(general_registers_, reg,
AllocationStage::kAtStart);
} else {
general_registers_.RemoveFromFree(reg);
}
general_registers_.SetValue(reg, input.node());
}
}
for (Phi* phi : *phis) UpdateUse(&phi->input(block->predecessor_id()));
}
}
// Merge register values. Values only flowing into phis and not being
// independently live will be killed as part of the merge.
if (node->Is<JumpToInlined>()) { if (node->Is<JumpToInlined>()) {
// Do nothing. // Do nothing.
// TODO(leszeks): DCHECK any useful invariants here. // TODO(leszeks): DCHECK any useful invariants here.
} else if (auto unconditional = node->TryCast<UnconditionalControlNode>()) { } else if (auto unconditional = node->TryCast<UnconditionalControlNode>()) {
// Empty blocks are immediately merged at the control of their predecessor. InitializeBranchTargetPhis(block->predecessor_id(),
if (!block->is_empty_block()) { unconditional->target());
MergeRegisterValues(unconditional, unconditional->target(), // Merge register values. Values only flowing into phis and not being
block->predecessor_id()); // independently live will be killed as part of the merge.
} MergeRegisterValues(unconditional, unconditional->target(),
block->predecessor_id());
} else if (auto conditional = node->TryCast<ConditionalControlNode>()) { } else if (auto conditional = node->TryCast<ConditionalControlNode>()) {
InitializeConditionalBranchRegisters(conditional, conditional->if_true()); InitializeConditionalBranchTarget(conditional, conditional->if_true());
InitializeConditionalBranchRegisters(conditional, conditional->if_false()); InitializeConditionalBranchTarget(conditional, conditional->if_false());
} }
if (FLAG_trace_maglev_regalloc) { if (FLAG_trace_maglev_regalloc) {
...@@ -1089,9 +1089,9 @@ void StraightForwardRegisterAllocator::InitializeRegisterValues( ...@@ -1089,9 +1089,9 @@ void StraightForwardRegisterAllocator::InitializeRegisterValues(
ForEachMergePointRegisterState(target_state, fill); ForEachMergePointRegisterState(target_state, fill);
} }
void StraightForwardRegisterAllocator::EnsureInRegister(
MergePointRegisterState& target_state, ValueNode* incoming) {
#ifdef DEBUG #ifdef DEBUG
bool StraightForwardRegisterAllocator::IsInRegister(
MergePointRegisterState& target_state, ValueNode* incoming) {
bool found = false; bool found = false;
auto find = [&found, &incoming](auto reg, RegisterState& state) { auto find = [&found, &incoming](auto reg, RegisterState& state) {
ValueNode* node; ValueNode* node;
...@@ -1104,9 +1104,9 @@ void StraightForwardRegisterAllocator::EnsureInRegister( ...@@ -1104,9 +1104,9 @@ void StraightForwardRegisterAllocator::EnsureInRegister(
} else { } else {
target_state.ForEachGeneralRegister(find); target_state.ForEachGeneralRegister(find);
} }
DCHECK(found); return found;
#endif
} }
#endif
void StraightForwardRegisterAllocator::InitializeBranchTargetRegisterValues( void StraightForwardRegisterAllocator::InitializeBranchTargetRegisterValues(
ControlNode* source, BasicBlock* target) { ControlNode* source, BasicBlock* target) {
...@@ -1123,9 +1123,33 @@ void StraightForwardRegisterAllocator::InitializeBranchTargetRegisterValues( ...@@ -1123,9 +1123,33 @@ void StraightForwardRegisterAllocator::InitializeBranchTargetRegisterValues(
ForEachMergePointRegisterState(target_state, init); ForEachMergePointRegisterState(target_state, init);
} }
void StraightForwardRegisterAllocator::InitializeEmptyBlockRegisterValues(
ControlNode* source, BasicBlock* target) {
DCHECK(target->is_empty_block());
MergePointRegisterState* register_state =
compilation_info_->zone()->New<MergePointRegisterState>();
DCHECK(!register_state->is_initialized());
auto init = [&](auto& registers, auto reg, RegisterState& state) {
ValueNode* node = nullptr;
if (!registers.free().has(reg)) {
node = registers.GetValue(reg);
if (!IsLiveAtTarget(node, source, target)) node = nullptr;
}
state = {node, initialized_node};
};
ForEachMergePointRegisterState(*register_state, init);
target->set_empty_block_register_state(register_state);
}
void StraightForwardRegisterAllocator::MergeRegisterValues(ControlNode* control, void StraightForwardRegisterAllocator::MergeRegisterValues(ControlNode* control,
BasicBlock* target, BasicBlock* target,
int predecessor_id) { int predecessor_id) {
if (target->is_empty_block()) {
return InitializeEmptyBlockRegisterValues(control, target);
}
MergePointRegisterState& target_state = target->state()->register_state(); MergePointRegisterState& target_state = target->state()->register_state();
if (!target_state.is_initialized()) { if (!target_state.is_initialized()) {
// This is the first block we're merging, initialize the values. // This is the first block we're merging, initialize the values.
...@@ -1167,7 +1191,7 @@ void StraightForwardRegisterAllocator::MergeRegisterValues(ControlNode* control, ...@@ -1167,7 +1191,7 @@ void StraightForwardRegisterAllocator::MergeRegisterValues(ControlNode* control,
// If there's a value in the incoming state, that value is either // If there's a value in the incoming state, that value is either
// already spilled or in another place in the merge state. // already spilled or in another place in the merge state.
if (incoming != nullptr && incoming->is_loadable()) { if (incoming != nullptr && incoming->is_loadable()) {
EnsureInRegister(target_state, incoming); DCHECK(IsInRegister(target_state, incoming));
} }
return; return;
} }
...@@ -1177,7 +1201,20 @@ void StraightForwardRegisterAllocator::MergeRegisterValues(ControlNode* control, ...@@ -1177,7 +1201,20 @@ void StraightForwardRegisterAllocator::MergeRegisterValues(ControlNode* control,
// If the register is unallocated at the merge point, and the incoming // If the register is unallocated at the merge point, and the incoming
// value isn't spilled, that means we must have seen it already in a // value isn't spilled, that means we must have seen it already in a
// different register. // different register.
EnsureInRegister(target_state, incoming); // This maybe not be true for conversion nodes, as they can split and take
// over the liveness of the node they are converting.
DCHECK_IMPLIES(!IsInRegister(target_state, incoming),
incoming->properties().is_conversion());
return;
}
if (node != nullptr && !node->is_loadable()) {
// If we have a node already, but can't load it here, we must be in a
// liveness hole for it, so nuke the merge state.
// This can only happen for conversion nodes, as they can split and take
// over the liveness of the node they are converting.
DCHECK(node->properties().is_conversion());
state = {nullptr, initialized_node};
return; return;
} }
......
...@@ -95,8 +95,6 @@ class StraightForwardRegisterAllocator { ...@@ -95,8 +95,6 @@ class StraightForwardRegisterAllocator {
RegisterFrameState<Register> general_registers_; RegisterFrameState<Register> general_registers_;
RegisterFrameState<DoubleRegister> double_registers_; RegisterFrameState<DoubleRegister> double_registers_;
RegList free_general_registers_before_node_;
DoubleRegList free_double_registers_before_node_;
struct SpillSlotInfo { struct SpillSlotInfo {
SpillSlotInfo(uint32_t slot_index, NodeIdT freed_at_position) SpillSlotInfo(uint32_t slot_index, NodeIdT freed_at_position)
...@@ -182,13 +180,17 @@ class StraightForwardRegisterAllocator { ...@@ -182,13 +180,17 @@ class StraightForwardRegisterAllocator {
MergePointRegisterState& merge_point_state, Function&& f); MergePointRegisterState& merge_point_state, Function&& f);
void InitializeRegisterValues(MergePointRegisterState& target_state); void InitializeRegisterValues(MergePointRegisterState& target_state);
void EnsureInRegister(MergePointRegisterState& target_state, #ifdef DEBUG
ValueNode* incoming); bool IsInRegister(MergePointRegisterState& target_state, ValueNode* incoming);
#endif
void InitializeBranchTargetRegisterValues(ControlNode* source, void InitializeBranchTargetRegisterValues(ControlNode* source,
BasicBlock* target); BasicBlock* target);
void InitializeConditionalBranchRegisters(ConditionalControlNode* source, void InitializeEmptyBlockRegisterValues(ControlNode* source,
BasicBlock* target); BasicBlock* target);
void InitializeBranchTargetPhis(int predecessor_id, BasicBlock* target);
void InitializeConditionalBranchTarget(ConditionalControlNode* source,
BasicBlock* target);
void MergeRegisterValues(ControlNode* control, BasicBlock* target, void MergeRegisterValues(ControlNode* control, BasicBlock* target,
int predecessor_id); int predecessor_id);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment