cmd/compile: teach regalloc about temporary registers

Temporary registers are sometimes needed for an architecture backend
which needs to use several machine instructions to implement a single
SSA instruction.

Mark such instructions so that regalloc can reserve the temporary register
for it. That way we don't have to reserve a fixed register like we do now.

Convert the temp-register-using instructions on amd64 to use this
new mechanism. Other archs can follow as needed.

Change-Id: I1d0c8588afdad5cd18b4398eb5a0f755be5dead7
Reviewed-on: https://go-review.googlesource.com/c/go/+/398556
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Keith Randall <khr@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
Keith Randall 2022-04-05 15:07:29 -07:00
parent 249e51e5d9
commit 5f7abeca5a
9 changed files with 95 additions and 44 deletions

View file

@ -852,6 +852,9 @@ func (s *regAllocState) isGReg(r register) bool {
return s.f.Config.hasGReg && s.GReg == r
}
// Dummy value used to represent the value being held in a temporary register.
var tmpVal Value
func (s *regAllocState) regalloc(f *Func) {
regValLiveSet := f.newSparseSet(f.NumValues()) // set of values that may be live in register
defer f.retSparseSet(regValLiveSet)
@ -1266,6 +1269,7 @@ func (s *regAllocState) regalloc(f *Func) {
// Process all the non-phi values.
for idx, v := range oldSched {
tmpReg := noRegister
if s.f.pass.debug > regDebug {
fmt.Printf(" processing %s\n", v.LongString())
}
@ -1550,6 +1554,20 @@ func (s *regAllocState) regalloc(f *Func) {
}
ok:
// Pick a temporary register if needed.
// It should be distinct from all the input registers, so we
// allocate it after all the input registers, but before
// the input registers are freed via advanceUses below.
// (Not all instructions need that distinct part, but it is conservative.)
if opcodeTable[v.Op].needIntTemp {
m := s.allocatable & s.f.Config.gpRegMask
if m&^desired.avoid != 0 {
m &^= desired.avoid
}
tmpReg = s.allocReg(m, &tmpVal)
s.nospill |= regMask(1) << tmpReg
}
// Now that all args are in regs, we're ready to issue the value itself.
// Before we pick a register for the output value, allow input registers
// to be deallocated. We do this here so that the output can use the
@ -1574,6 +1592,11 @@ func (s *regAllocState) regalloc(f *Func) {
outRegs := noRegisters // TODO if this is costly, hoist and clear incrementally below.
maxOutIdx := -1
var used regMask
if tmpReg != noRegister {
// Ensure output registers are distinct from the temporary register.
// (Not all instructions need that distinct part, but it is conservative.)
used |= regMask(1) << tmpReg
}
for _, out := range regspec.outputs {
mask := out.regs & s.allocatable &^ used
if mask == 0 {
@ -1655,6 +1678,13 @@ func (s *regAllocState) regalloc(f *Func) {
s.assignReg(r, v, v)
}
}
if tmpReg != noRegister {
// Remember the temp register allocation, if any.
if s.f.tempRegs == nil {
s.f.tempRegs = map[ID]*Register{}
}
s.f.tempRegs[v.ID] = &s.registers[tmpReg]
}
}
// deallocate dead args, if we have not done so