pass: de-prioritize base pointer in register allocation (#184)
Updates #156
This commit is contained in:
committed by
GitHub
parent
f295bde84c
commit
c32f24fb1e
25
pass/reg.go
25
pass/reg.go
@@ -74,7 +74,7 @@ func Liveness(fn *ir.Function) error {
|
||||
|
||||
// AllocateRegisters performs register allocation.
|
||||
func AllocateRegisters(fn *ir.Function) error {
|
||||
// Populate allocators (one per kind).
|
||||
// Initialize one allocator per kind.
|
||||
as := map[reg.Kind]*Allocator{}
|
||||
for _, i := range fn.Instructions() {
|
||||
for _, r := range i.Registers() {
|
||||
@@ -86,7 +86,28 @@ func AllocateRegisters(fn *ir.Function) error {
|
||||
}
|
||||
as[k] = a
|
||||
}
|
||||
as[k].Add(r.ID())
|
||||
}
|
||||
}
|
||||
|
||||
// De-prioritize the base pointer register. This can be used as a general
|
||||
// purpose register, but it's callee-save so needs to be saved/restored if
|
||||
// it is clobbered. For this reason we prefer to avoid using it unless
|
||||
// forced to by register pressure.
|
||||
for k, a := range as {
|
||||
f := reg.FamilyOfKind(k)
|
||||
for _, r := range f.Registers() {
|
||||
if (r.Info() & reg.BasePointer) != 0 {
|
||||
// Negative priority penalizes this register relative to all
|
||||
// others (having default zero priority).
|
||||
a.SetPriority(r.ID(), -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate registers to be allocated.
|
||||
for _, i := range fn.Instructions() {
|
||||
for _, r := range i.Registers() {
|
||||
as[r.Kind()].Add(r.ID())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,51 @@ func ConstructLiveness(t *testing.T, ctx *build.Context) *ir.Function {
|
||||
return BuildFunction(t, ctx, pass.LabelTarget, pass.CFG, pass.Liveness)
|
||||
}
|
||||
|
||||
func TestAllocateRegistersBasePointerDeprioritized(t *testing.T) {
|
||||
// Construct a function that requires n general-purpose registers all live
|
||||
// at once. Choose n to be the maximal possible number of registers without
|
||||
// touching the base pointer.
|
||||
n := 14
|
||||
|
||||
ctx := build.NewContext()
|
||||
ctx.Function("sum")
|
||||
ctx.SignatureExpr("func() uint64")
|
||||
|
||||
x := make([]reg.GPVirtual, n)
|
||||
for i := 0; i < n; i++ {
|
||||
x[i] = ctx.GP64()
|
||||
ctx.MOVQ(operand.U64(i), x[i])
|
||||
}
|
||||
|
||||
for i := 1; i < n; i++ {
|
||||
ctx.ADDQ(x[i], x[0])
|
||||
}
|
||||
|
||||
ctx.Store(x[0], ctx.ReturnIndex(0))
|
||||
ctx.RET()
|
||||
|
||||
// Build and compile the function up to register allocation.
|
||||
fn := BuildFunction(t, ctx, pass.LabelTarget, pass.CFG, pass.Liveness, pass.AllocateRegisters, pass.BindRegisters)
|
||||
|
||||
// Verify this function uses n registers, but not the base pointer.
|
||||
ps := map[reg.Physical]bool{}
|
||||
for _, i := range fn.Instructions() {
|
||||
for _, r := range i.OutputRegisters() {
|
||||
ps[reg.ToPhysical(r)] = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(ps) != n {
|
||||
t.Fatalf("expected function to require %d registers", n)
|
||||
}
|
||||
|
||||
for p := range ps {
|
||||
if (p.Info() & reg.BasePointer) != 0 {
|
||||
t.Fatal("base pointer used")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureBasePointerCalleeSavedFrameless(t *testing.T) {
|
||||
// Construct a function that writes to the base pointer.
|
||||
ctx := build.NewContext()
|
||||
|
||||
Reference in New Issue
Block a user