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

[maglev] Fix result regalloc clobbering inputs

Consider the following

  * A ValueNode has inputs A and B
  * Input A has later uses, input B doesn't
  * The ValueNode's result must be in the same register as A

It can then happen that UpdateUses frees B, and the result allocation
emits a gap move from A's register to B's old register (now free) to
preserve the value of A when the ValueNode writes into its register.
This gap move is emmitted before the ValueNode start, which means that
it clobbers B.

Now, UpdateUses only clears registers _after_ node result allocation, so
that the known free registers are still the ones before updating uses.

Done naively, this would have bad consequences -- in the case where A
has no later uses, it would still force the regalloc to save its value
thinking that it is still live. So, this patch also introduces a concept
of "AllocationStage" where we're either allocating at the start or end
of a Node. Inputs are allocated at the start, results at the end. When
walking registers during an allocation, nodes whose lifetimes end at the
current node are considered to be dead at the "end" allocation stage,
and we are allowed to a) use their registers, and b) drop them without
preserving their value.

Bug: v8:7700
Change-Id: I5ca764ed04b12269f189577e81eb7e2a27cd1b09
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3625978
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80371}
parent 6bc97b49
This diff is collapsed.
......@@ -91,8 +91,12 @@ class StraightForwardRegisterAllocator {
~StraightForwardRegisterAllocator();
private:
enum class AllocationStage { kAtStart, kAtEnd };
RegisterFrameState<Register> general_registers_;
RegisterFrameState<DoubleRegister> double_registers_;
RegList free_general_registers_before_node_;
DoubleRegList free_double_registers_before_node_;
struct SpillSlotInfo {
SpillSlotInfo(uint32_t slot_index, NodeIdT freed_at_position)
......@@ -142,24 +146,30 @@ class StraightForwardRegisterAllocator {
void FreeRegistersUsedBy(ValueNode* node);
template <typename RegisterT>
void FreeSomeRegister(RegisterFrameState<RegisterT>& registers);
void FreeSomeGeneralRegister();
void FreeSomeDoubleRegister();
void FreeSomeRegister(RegisterFrameState<RegisterT>& registers,
AllocationStage stage);
void FreeSomeGeneralRegister(AllocationStage stage);
void FreeSomeDoubleRegister(AllocationStage stage);
template <typename RegisterT>
void DropRegisterValue(RegisterFrameState<RegisterT>& registers,
RegisterT reg);
void DropRegisterValue(Register reg);
void DropRegisterValue(DoubleRegister reg);
RegisterT reg, AllocationStage stage);
void DropRegisterValue(Register reg, AllocationStage stage);
void DropRegisterValue(DoubleRegister reg, AllocationStage stage);
compiler::AllocatedOperand AllocateRegister(ValueNode* node);
compiler::AllocatedOperand AllocateRegister(ValueNode* node,
AllocationStage stage);
template <typename RegisterT>
compiler::AllocatedOperand ForceAllocate(
RegisterFrameState<RegisterT>& registers, RegisterT reg, ValueNode* node);
compiler::AllocatedOperand ForceAllocate(Register reg, ValueNode* node);
compiler::AllocatedOperand ForceAllocate(DoubleRegister reg, ValueNode* node);
compiler::AllocatedOperand ForceAllocate(const Input& input, ValueNode* node);
RegisterFrameState<RegisterT>& registers, RegisterT reg, ValueNode* node,
AllocationStage stage);
compiler::AllocatedOperand ForceAllocate(Register reg, ValueNode* node,
AllocationStage stage);
compiler::AllocatedOperand ForceAllocate(DoubleRegister reg, ValueNode* node,
AllocationStage stage);
compiler::AllocatedOperand ForceAllocate(const Input& input, ValueNode* node,
AllocationStage stage);
template <typename Function>
void ForEachMergePointRegisterState(
......
......@@ -43,3 +43,52 @@ assertEquals(5, foo());
%OptimizeMaglevOnNextCall(foo);
assertEquals(5, foo());
function nested_factory_mutable() {
var x = 1;
x = 2;
return (function() {
var z = 3;
z = 4;
return function foo(){
// Add two values from different contexts to force an
// LdaImmutableCurrentContextSlot
return x+z;
}
})();
}
foo = nested_factory_mutable();
%PrepareFunctionForOptimization(foo);
assertEquals(6, foo());
assertEquals(6, foo());
%OptimizeMaglevOnNextCall(foo);
assertEquals(6, foo());
function nested_factory_immutable() {
var x = 1;
return (function() {
var z = 3;
z = 4;
return function foo(){
// Add two values from different contexts to force an
// LdaImmutableCurrentContextSlot
return x+z;
}
})();
}
foo = nested_factory_immutable();
%PrepareFunctionForOptimization(foo);
assertEquals(5, foo());
assertEquals(5, foo());
%OptimizeMaglevOnNextCall(foo);
assertEquals(5, foo());
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