mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.ssa] cmd/compile: allocate the flag register in a separate pass
Spilling/restoring flag values is a pain to do during regalloc. Instead, allocate the flag register in a separate pass. Regalloc then operates normally on any flag recomputation instructions. Change-Id: Ia1c3d9e6eff678861193093c0b48a00f90e4156b Reviewed-on: https://go-review.googlesource.com/17694 Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
09ffa0c4c7
commit
c140df0326
6 changed files with 162 additions and 50 deletions
|
|
@ -38,12 +38,6 @@
|
|||
// x3 can then be used wherever x is referenced again.
|
||||
// If the spill (x2) is never used, it will be removed at the end of regalloc.
|
||||
//
|
||||
// Flags values are special. Instead of attempting to spill and restore the flags
|
||||
// register, we recalculate it if needed.
|
||||
// There are more efficient schemes (see the discussion in CL 13844),
|
||||
// but flag restoration is empirically rare, and this approach is simple
|
||||
// and architecture-independent.
|
||||
//
|
||||
// Phi values are special, as always. We define two kinds of phis, those
|
||||
// where the merge happens in a register (a "register" phi) and those where
|
||||
// the merge happens in a stack location (a "stack" phi).
|
||||
|
|
@ -173,7 +167,6 @@ var registers = [...]Register{
|
|||
Register{30, "X14"},
|
||||
Register{31, "X15"},
|
||||
Register{32, "SB"}, // pseudo-register for global base pointer (aka %rip)
|
||||
Register{33, "FLAGS"},
|
||||
|
||||
// TODO: make arch-dependent
|
||||
}
|
||||
|
|
@ -226,7 +219,7 @@ type regAllocState struct {
|
|||
f *Func
|
||||
|
||||
// For each value, whether it needs a register or not.
|
||||
// Cached value of !v.Type.IsMemory() && !v.Type.IsVoid().
|
||||
// Cached value of !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags().
|
||||
needReg []bool
|
||||
|
||||
// for each block, its primary predecessor.
|
||||
|
|
@ -435,40 +428,9 @@ func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool) *Val
|
|||
c = s.curBlock.NewValue1(v.Line, OpCopy, v.Type, s.regs[r2].c)
|
||||
} else if v.rematerializeable() {
|
||||
// Rematerialize instead of loading from the spill location.
|
||||
c = s.curBlock.NewValue0(v.Line, v.Op, v.Type)
|
||||
c.Aux = v.Aux
|
||||
c.AuxInt = v.AuxInt
|
||||
c.AddArgs(v.Args...)
|
||||
c = v.copyInto(s.curBlock)
|
||||
} else {
|
||||
switch {
|
||||
// It is difficult to spill and reload flags on many architectures.
|
||||
// Instead, we regenerate the flags register by issuing the same instruction again.
|
||||
// This requires (possibly) spilling and reloading that instruction's args.
|
||||
case v.Type.IsFlags():
|
||||
if logSpills {
|
||||
fmt.Println("regalloc: regenerating flags")
|
||||
}
|
||||
ns := s.nospill
|
||||
// Place v's arguments in registers, spilling and loading as needed
|
||||
args := make([]*Value, 0, len(v.Args))
|
||||
regspec := opcodeTable[v.Op].reg
|
||||
for _, i := range regspec.inputs {
|
||||
// Extract the original arguments to v
|
||||
a := s.orig[v.Args[i.idx].ID]
|
||||
if a.Type.IsFlags() {
|
||||
s.f.Fatalf("cannot load flags value with flags arg: %v has unwrapped arg %v", v.LongString(), a.LongString())
|
||||
}
|
||||
cc := s.allocValToReg(a, i.regs, true)
|
||||
args = append(args, cc)
|
||||
}
|
||||
s.nospill = ns
|
||||
// Recalculate v
|
||||
c = s.curBlock.NewValue0(v.Line, v.Op, v.Type)
|
||||
c.Aux = v.Aux
|
||||
c.AuxInt = v.AuxInt
|
||||
c.resetArgs()
|
||||
c.AddArgs(args...)
|
||||
|
||||
// Load v from its spill location.
|
||||
case vi.spill2 != nil:
|
||||
if logSpills {
|
||||
|
|
@ -506,7 +468,7 @@ func (s *regAllocState) init(f *Func) {
|
|||
s.orig = make([]*Value, f.NumValues())
|
||||
for _, b := range f.Blocks {
|
||||
for _, v := range b.Values {
|
||||
if v.Type.IsMemory() || v.Type.IsVoid() {
|
||||
if v.Type.IsMemory() || v.Type.IsVoid() || v.Type.IsFlags() {
|
||||
continue
|
||||
}
|
||||
s.needReg[v.ID] = true
|
||||
|
|
@ -818,6 +780,10 @@ func (s *regAllocState) regalloc(f *Func) {
|
|||
// by the register specification (most constrained first).
|
||||
args = append(args[:0], v.Args...)
|
||||
for _, i := range regspec.inputs {
|
||||
if i.regs == flagRegMask {
|
||||
// TODO: remove flag input from regspec.inputs.
|
||||
continue
|
||||
}
|
||||
args[i.idx] = s.allocValToReg(v.Args[i.idx], i.regs, true)
|
||||
}
|
||||
|
||||
|
|
@ -834,8 +800,11 @@ func (s *regAllocState) regalloc(f *Func) {
|
|||
// Pick register for output.
|
||||
var r register
|
||||
var mask regMask
|
||||
if len(regspec.outputs) > 0 {
|
||||
if s.needReg[v.ID] {
|
||||
mask = regspec.outputs[0] &^ s.reserved()
|
||||
if mask>>33&1 != 0 {
|
||||
s.f.Fatalf("bad mask %s\n", v.LongString())
|
||||
}
|
||||
}
|
||||
if mask != 0 {
|
||||
r = s.allocReg(mask)
|
||||
|
|
@ -858,7 +827,7 @@ func (s *regAllocState) regalloc(f *Func) {
|
|||
// f()
|
||||
// }
|
||||
// It would be good to have both spill and restore inside the IF.
|
||||
if !v.Type.IsFlags() {
|
||||
if s.needReg[v.ID] {
|
||||
spill := b.NewValue1(v.Line, OpStoreReg, v.Type, v)
|
||||
s.setOrig(spill, v)
|
||||
s.values[v.ID].spill = spill
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue