wazevo(arm64): places spill slots below clobbered regs (#1833)

Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
This commit is contained in:
Takeshi Yoneda
2023-11-10 11:27:13 +09:00
committed by GitHub
parent b16e47721b
commit 9bc1ae6816
5 changed files with 89 additions and 97 deletions

View File

@@ -485,7 +485,7 @@ func (m *machine) getVRegSpillSlotOffsetFromSP(id regalloc.VRegID, size byte) in
m.spillSlots[id] = offset
m.spillSlotSize += int64(size)
}
return offset + m.clobberedRegSlotSize() + 16 // spill slot starts above the clobbered registers and the frame size.
return offset + 16 // spill slot starts above the clobbered registers and the frame size.
}
func (m *machine) clobberedRegSlotSize() int64 {

View File

@@ -41,6 +41,39 @@ func (m *machine) SetupPrologue() {
panic(fmt.Sprintf("BUG: spillSlotSize=%d, spillSlots=%v\n", m.spillSlotSize, m.spillSlots))
}
if regs := m.clobberedRegs; len(regs) > 0 {
//
// (high address) (high address)
// +-----------------+ +-----------------+
// | ....... | | ....... |
// | ret Y | | ret Y |
// | ....... | | ....... |
// | ret 0 | | ret 0 |
// | arg X | | arg X |
// | ....... | | ....... |
// | arg 1 | | arg 1 |
// | arg 0 | | arg 0 |
// | size_of_arg_ret | | size_of_arg_ret |
// | ReturnAddress | | ReturnAddress |
// SP----> +-----------------+ ====> +-----------------+
// (low address) | clobbered M |
// | ............ |
// | clobbered 0 |
// +-----------------+ <----- SP
// (low address)
//
_amode := addressModePreOrPostIndex(spVReg,
-16, // stack pointer must be 16-byte aligned.
true, // Decrement before store.
)
for _, vr := range regs {
// TODO: pair stores to reduce the number of instructions.
store := m.allocateInstr()
store.asStore(operandNR(vr), _amode, regTypeToRegisterSizeInBits(vr.RegType()))
cur = linkInstr(cur, store)
}
}
if size := m.spillSlotSize; size > 0 {
// Check if size is 16-byte aligned.
if size&0xf != 0 {
@@ -64,53 +97,17 @@ func (m *machine) SetupPrologue() {
// | size_of_arg_ret |
// | ReturnAddress |
// +------------------+
// | spill slot M |
// | clobbered M |
// | ............ |
// | clobbered 0 |
// | spill slot N |
// | ............ |
// | spill slot 2 |
// | spill slot 1 |
// | spill slot 0 |
// SP----> +------------------+
// (low address)
}
if regs := m.clobberedRegs; len(regs) > 0 {
//
// (high address) (high address)
// +-----------------+ +-----------------+
// | ....... | | ....... |
// | ret Y | | ret Y |
// | ....... | | ....... |
// | ret 0 | | ret 0 |
// | arg X | | arg X |
// | ....... | | ....... |
// | arg 1 | | arg 1 |
// | arg 0 | | arg 0 |
// | size_of_arg_ret | | size_of_arg_ret |
// | ReturnAddress | | ReturnAddress |
// +-----------------+ ====> +-----------------+
// | ........... | | ........... |
// | spill slot M | | spill slot M |
// | ............ | | ............ |
// | spill slot 2 | | spill slot 2 |
// | spill slot 1 | | spill slot 1 |
// SP----> +-----------------+ | spill slot 1 |
// (low address) | clobbered N |
// | ............ |
// | clobbered 0 |
// +-----------------+ <----- SP
// (low address)
//
_amode := addressModePreOrPostIndex(spVReg,
-16, // stack pointer must be 16-byte aligned.
true, // Decrement before store.
)
for _, vr := range regs {
// TODO: pair stores to reduce the number of instructions.
store := m.allocateInstr()
store.asStore(operandNR(vr), _amode, regTypeToRegisterSizeInBits(vr.RegType()))
cur = linkInstr(cur, store)
}
}
// We push the frame size into the stack to make it possible to unwind stack:
//
//
@@ -127,15 +124,14 @@ func (m *machine) SetupPrologue() {
// | size_of_arg_ret | | size_of_arg_ret |
// | ReturnAddress | | ReturnAddress |
// +-----------------+ ==> +-----------------+ <----+
// | ........... | | ........... | |
// | spill slot M | | spill slot M | |
// | clobbered M | | clobbered M | |
// | ............ | | ............ | |
// | spill slot 2 | | spill slot 2 | |
// | spill slot 1 | | spill slot 1 | | frame size
// | spill slot 1 | | spill slot 1 | |
// | clobbered N | | clobbered N | |
// | clobbered 2 | | clobbered 2 | |
// | clobbered 1 | | clobbered 1 | | frame size
// | clobbered 0 | | clobbered 0 | |
// | spill slot N | | spill slot N | |
// | ............ | | ............ | |
// | clobbered 0 | | clobbered 0 | <----+
// | spill slot 0 | | spill slot 0 | <----+
// SP---> +-----------------+ | xxxxxx | ;; unused space to make it 16-byte aligned.
// | frame_size |
// +-----------------+ <---- SP
@@ -215,6 +211,35 @@ func (m *machine) setupEpilogueAfter(cur *instruction) {
// We've stored the frame size in the prologue, and now that we are about to return from this function, we won't need it anymore.
cur = m.addsAddOrSubStackPointer(cur, spVReg, 16, true)
if s := m.spillSlotSize; s > 0 {
// Adjust SP to the original value:
//
// (high address) (high address)
// +-----------------+ +-----------------+
// | ....... | | ....... |
// | ret Y | | ret Y |
// | ....... | | ....... |
// | ret 0 | | ret 0 |
// | arg X | | arg X |
// | ....... | | ....... |
// | arg 1 | | arg 1 |
// | arg 0 | | arg 0 |
// | xxxxx | | xxxxx |
// | ReturnAddress | | ReturnAddress |
// +-----------------+ ====> +-----------------+
// | clobbered M | | clobbered M |
// | ............ | | ............ |
// | clobbered 1 | | clobbered 1 |
// | clobbered 0 | | clobbered 0 |
// | spill slot N | +-----------------+ <---- SP
// | ............ |
// | spill slot 0 |
// SP---> +-----------------+
// (low address)
//
cur = m.addsAddOrSubStackPointer(cur, spVReg, s, true)
}
// First we need to restore the clobbered registers.
if len(m.clobberedRegs) > 0 {
// (high address)
@@ -227,17 +252,13 @@ func (m *machine) setupEpilogueAfter(cur *instruction) {
// | ....... | | ....... |
// | arg 1 | | arg 1 |
// | arg 0 | | arg 0 |
// | xxxxx | | xxxxx |
// | ReturnAddress | | ReturnAddress |
// +-----------------+ ========> +-----------------+
// | ........... | | ........... |
// | spill slot M | | spill slot M |
// | ............ | | ............ |
// | spill slot 2 | | spill slot 2 |
// | spill slot 1 | | spill slot 1 |
// | clobbered 0 | SP---> +-----------------+
// +-----------------+ ========> +-----------------+ <---- SP
// | clobbered M |
// | clobbered 1 |
// | ........... |
// | clobbered N |
// | clobbered 0 |
// SP---> +-----------------+
// (low address)
@@ -260,33 +281,6 @@ func (m *machine) setupEpilogueAfter(cur *instruction) {
}
}
if s := m.spillSlotSize; s > 0 {
// Adjust SP to the original value:
//
// (high address) (high address)
// +-----------------+ +-----------------+
// | ....... | | ....... |
// | ret Y | | ret Y |
// | ....... | | ....... |
// | ret 0 | | ret 0 |
// | arg X | | arg X |
// | ....... | | ....... |
// | arg 1 | | arg 1 |
// | arg 0 | | arg 0 |
// | xxxxx | | xxxxx |
// | ReturnAddress | | ReturnAddress |
// +-----------------+ ====> +-----------------+ <---- SP
// | ........... | (low address)
// | spill slot M |
// | ............ |
// | spill slot 2 |
// | spill slot 1 |
// SP---> +-----------------+
// (low address)
//
cur = m.addsAddOrSubStackPointer(cur, spVReg, s, true)
}
// Reload the return address (lr).
//
// +-----------------+ +-----------------+

View File

@@ -62,11 +62,11 @@ func TestMachine_SetupPrologue(t *testing.T) {
clobberedRegs: []regalloc.VReg{v18VReg, v19VReg, x18VReg, x25VReg},
exp: `
stp x30, xzr, [sp, #-0x10]!
sub sp, sp, #0x140
str q18, [sp, #-0x10]!
str q19, [sp, #-0x10]!
str x18, [sp, #-0x10]!
str x25, [sp, #-0x10]!
sub sp, sp, #0x140
orr x27, xzr, #0x180
str x27, [sp, #-0x10]!
udf
@@ -80,11 +80,11 @@ func TestMachine_SetupPrologue(t *testing.T) {
orr x27, xzr, #0x1e0
sub sp, sp, x27
stp x30, x27, [sp, #-0x10]!
sub sp, sp, #0x140
str q18, [sp, #-0x10]!
str q19, [sp, #-0x10]!
str x18, [sp, #-0x10]!
str x25, [sp, #-0x10]!
sub sp, sp, #0x140
orr x27, xzr, #0x180
str x27, [sp, #-0x10]!
udf
@@ -177,11 +177,11 @@ func TestMachine_SetupEpilogue(t *testing.T) {
{
exp: `
add sp, sp, #0x10
add sp, sp, #0xa0
ldr x25, [sp], #0x10
ldr x18, [sp], #0x10
ldr q27, [sp], #0x10
ldr q18, [sp], #0x10
add sp, sp, #0xa0
ldr x30, [sp], #0x10
ret
`,
@@ -191,11 +191,11 @@ func TestMachine_SetupEpilogue(t *testing.T) {
{
exp: `
add sp, sp, #0x10
add sp, sp, #0xa0
ldr x25, [sp], #0x10
ldr x18, [sp], #0x10
ldr q27, [sp], #0x10
ldr q18, [sp], #0x10
add sp, sp, #0xa0
ldr x30, [sp], #0x10
add sp, sp, #0x150
ret

View File

@@ -62,7 +62,6 @@ func TestRegAllocFunctionImpl_ReversePostOrderBlockIterator(t *testing.T) {
func TestRegAllocFunctionImpl_ReloadRegisterAfter(t *testing.T) {
ctx, _, m := newSetupWithMockContext()
m.clobberedRegs = make([]regalloc.VReg, 3) // This will make the beginning of the spill slot at (3 + 1(frame size))* 16 bytes = 64.
ctx.typeOf = map[regalloc.VReg]ssa.Type{x1VReg: ssa.TypeI64, v1VReg: ssa.TypeF64}
i1, i2 := m.allocateNop(), m.allocateNop()
@@ -86,14 +85,13 @@ func TestRegAllocFunctionImpl_ReloadRegisterAfter(t *testing.T) {
m.rootInstr = i1
require.Equal(t, `
ldr d1, [sp, #0x48]
ldr x1, [sp, #0x40]
ldr d1, [sp, #0x18]
ldr x1, [sp, #0x10]
`, m.Format())
}
func TestRegAllocFunctionImpl_StoreRegisterBefore(t *testing.T) {
ctx, _, m := newSetupWithMockContext()
m.clobberedRegs = make([]regalloc.VReg, 3) // This will make the beginning of the spill slot at (3 + 1(frame size))* 16 bytes = 64.
ctx.typeOf = map[regalloc.VReg]ssa.Type{x1VReg: ssa.TypeI64, v1VReg: ssa.TypeF64}
i1, i2 := m.allocateNop(), m.allocateNop()
@@ -117,8 +115,8 @@ func TestRegAllocFunctionImpl_StoreRegisterBefore(t *testing.T) {
m.rootInstr = i1
require.Equal(t, `
str x1, [sp, #0x40]
str d1, [sp, #0x48]
str x1, [sp, #0x10]
str d1, [sp, #0x18]
`, m.Format())
}

View File

@@ -108,17 +108,17 @@ func TestMachine_ret0OffsetFromSP(t *testing.T) {
}
func TestMachine_getVRegSpillSlotOffsetFromSP(t *testing.T) {
m := &machine{clobberedRegs: make([]regalloc.VReg, 10), spillSlots: make(map[regalloc.VRegID]int64)}
m := &machine{spillSlots: make(map[regalloc.VRegID]int64)}
id := regalloc.VRegID(1)
offset := m.getVRegSpillSlotOffsetFromSP(id, 8)
require.Equal(t, int64(160)+16, offset)
require.Equal(t, int64(16), offset)
require.Equal(t, int64(8), m.spillSlotSize)
_, ok := m.spillSlots[id]
require.True(t, ok)
id = 100
offset = m.getVRegSpillSlotOffsetFromSP(id, 16)
require.Equal(t, int64(160)+16+8, offset)
require.Equal(t, int64(16+8), offset)
require.Equal(t, int64(24), m.spillSlotSize)
_, ok = m.spillSlots[id]
require.True(t, ok)