2015-06-05 13:04:29 -04:00
|
|
|
// Copyright 2015 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
// Register allocation.
|
|
|
|
|
//
|
2016-03-01 23:21:55 +00:00
|
|
|
// We use a version of a linear scan register allocator. We treat the
|
2015-08-11 12:51:33 -07:00
|
|
|
// whole function as a single long basic block and run through
|
2016-03-01 23:21:55 +00:00
|
|
|
// it using a greedy register allocator. Then all merge edges
|
2015-08-11 12:51:33 -07:00
|
|
|
// (those targeting a block with len(Preds)>1) are processed to
|
|
|
|
|
// shuffle data into the place that the target of the edge expects.
|
|
|
|
|
//
|
|
|
|
|
// The greedy allocator moves values into registers just before they
|
|
|
|
|
// are used, spills registers only when necessary, and spills the
|
|
|
|
|
// value whose next use is farthest in the future.
|
|
|
|
|
//
|
|
|
|
|
// The register allocator requires that a block is not scheduled until
|
2016-03-01 23:21:55 +00:00
|
|
|
// at least one of its predecessors have been scheduled. The most recent
|
2015-08-11 12:51:33 -07:00
|
|
|
// such predecessor provides the starting register state for a block.
|
|
|
|
|
//
|
|
|
|
|
// It also requires that there are no critical edges (critical =
|
|
|
|
|
// comes from a block with >1 successor and goes to a block with >1
|
|
|
|
|
// predecessor). This makes it easy to add fixup code on merge edges -
|
|
|
|
|
// the source of a merge edge has only one successor, so we can add
|
|
|
|
|
// fixup code to the end of that block.
|
|
|
|
|
|
|
|
|
|
// Spilling
|
|
|
|
|
//
|
|
|
|
|
// For every value, we generate a spill immediately after the value itself.
|
|
|
|
|
// x = Op y z : AX
|
|
|
|
|
// x2 = StoreReg x
|
2016-03-01 23:21:55 +00:00
|
|
|
// While AX still holds x, any uses of x will use that value. When AX is needed
|
2015-08-11 12:51:33 -07:00
|
|
|
// for another value, we simply reuse AX. Spill code has already been generated
|
2016-03-01 23:21:55 +00:00
|
|
|
// so there is no code generated at "spill" time. When x is referenced
|
2015-08-11 12:51:33 -07:00
|
|
|
// subsequently, we issue a load to restore x to a register using x2 as
|
|
|
|
|
// its argument:
|
|
|
|
|
// x3 = Restore x2 : CX
|
|
|
|
|
// 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.
|
|
|
|
|
//
|
2016-03-01 23:21:55 +00:00
|
|
|
// Phi values are special, as always. We define two kinds of phis, those
|
2015-08-11 12:51:33 -07:00
|
|
|
// where the merge happens in a register (a "register" phi) and those where
|
|
|
|
|
// the merge happens in a stack location (a "stack" phi).
|
|
|
|
|
//
|
|
|
|
|
// A register phi must have the phi and all of its inputs allocated to the
|
2016-03-01 23:21:55 +00:00
|
|
|
// same register. Register phis are spilled similarly to regular ops:
|
2015-08-11 12:51:33 -07:00
|
|
|
// b1: y = ... : AX b2: z = ... : AX
|
|
|
|
|
// goto b3 goto b3
|
|
|
|
|
// b3: x = phi(y, z) : AX
|
|
|
|
|
// x2 = StoreReg x
|
|
|
|
|
//
|
|
|
|
|
// A stack phi must have the phi and all of its inputs allocated to the same
|
2016-03-01 23:21:55 +00:00
|
|
|
// stack location. Stack phis start out life already spilled - each phi
|
2015-08-11 12:51:33 -07:00
|
|
|
// input must be a store (using StoreReg) at the end of the corresponding
|
|
|
|
|
// predecessor block.
|
|
|
|
|
// b1: y = ... : AX b2: z = ... : BX
|
|
|
|
|
// y2 = StoreReg y z2 = StoreReg z
|
|
|
|
|
// goto b3 goto b3
|
|
|
|
|
// b3: x = phi(y2, z2)
|
|
|
|
|
// The stack allocator knows that StoreReg args of stack-allocated phis
|
|
|
|
|
// must be allocated to the same stack slot as the phi that uses them.
|
|
|
|
|
// x is now a spilled value and a restore must appear before its first use.
|
|
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
|
|
|
|
|
|
// Use an affinity graph to mark two values which should use the
|
2016-03-01 23:21:55 +00:00
|
|
|
// same register. This affinity graph will be used to prefer certain
|
|
|
|
|
// registers for allocation. This affinity helps eliminate moves that
|
2015-08-11 12:51:33 -07:00
|
|
|
// are required for phi implementations and helps generate allocations
|
|
|
|
|
// for 2-register architectures.
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Note: regalloc generates a not-quite-SSA output. If we have:
|
2015-08-11 12:51:33 -07:00
|
|
|
//
|
|
|
|
|
// b1: x = ... : AX
|
|
|
|
|
// x2 = StoreReg x
|
|
|
|
|
// ... AX gets reused for something else ...
|
|
|
|
|
// if ... goto b3 else b4
|
|
|
|
|
//
|
|
|
|
|
// b3: x3 = LoadReg x2 : BX b4: x4 = LoadReg x2 : CX
|
|
|
|
|
// ... use x3 ... ... use x4 ...
|
|
|
|
|
//
|
|
|
|
|
// b2: ... use x3 ...
|
|
|
|
|
//
|
|
|
|
|
// If b3 is the primary predecessor of b2, then we use x3 in b2 and
|
|
|
|
|
// add a x4:CX->BX copy at the end of b4.
|
|
|
|
|
// But the definition of x3 doesn't dominate b2. We should really
|
|
|
|
|
// insert a dummy phi at the start of b2 (x5=phi(x3,x4):BX) to keep
|
2016-03-01 23:21:55 +00:00
|
|
|
// SSA form. For now, we ignore this problem as remaining in strict
|
|
|
|
|
// SSA form isn't needed after regalloc. We'll just leave the use
|
2015-08-11 12:51:33 -07:00
|
|
|
// of x3 not dominated by the definition of x3, and the CX->BX copy
|
|
|
|
|
// will have no use (so don't run deadcode after regalloc!).
|
|
|
|
|
// TODO: maybe we should introduce these extra phis?
|
|
|
|
|
|
2016-03-21 11:32:04 -04:00
|
|
|
// Additional not-quite-SSA output occurs when spills are sunk out
|
|
|
|
|
// of loops to the targets of exit edges from the loop. Before sinking,
|
|
|
|
|
// there is one spill site (one StoreReg) targeting stack slot X, after
|
|
|
|
|
// sinking there may be multiple spill sites targeting stack slot X,
|
|
|
|
|
// with no phi functions at any join points reachable by the multiple
|
|
|
|
|
// spill sites.
|
|
|
|
|
|
2015-05-05 16:19:12 -07:00
|
|
|
package ssa
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
import (
|
2015-10-22 13:07:38 -07:00
|
|
|
"cmd/internal/obj"
|
2015-08-11 12:51:33 -07:00
|
|
|
"fmt"
|
|
|
|
|
"unsafe"
|
|
|
|
|
)
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2016-03-10 17:52:57 -06:00
|
|
|
const (
|
2016-03-21 11:32:04 -04:00
|
|
|
moveSpills = iota
|
|
|
|
|
logSpills
|
2016-03-10 17:52:57 -06:00
|
|
|
regDebug
|
|
|
|
|
stackDebug
|
|
|
|
|
)
|
2015-08-11 12:51:33 -07:00
|
|
|
|
2016-03-02 15:18:40 -08:00
|
|
|
// distance is a measure of how far into the future values are used.
|
|
|
|
|
// distance is measured in units of instructions.
|
|
|
|
|
const (
|
|
|
|
|
likelyDistance = 1
|
|
|
|
|
normalDistance = 10
|
|
|
|
|
unlikelyDistance = 100
|
|
|
|
|
)
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// regalloc performs register allocation on f. It sets f.RegAlloc
|
2015-08-11 12:51:33 -07:00
|
|
|
// to the resulting allocation.
|
|
|
|
|
func regalloc(f *Func) {
|
|
|
|
|
var s regAllocState
|
|
|
|
|
s.init(f)
|
|
|
|
|
s.regalloc(f)
|
2015-05-05 16:19:12 -07:00
|
|
|
}
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
type register uint8
|
|
|
|
|
|
|
|
|
|
const noRegister register = 255
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2015-06-06 16:03:33 -07:00
|
|
|
type regMask uint64
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
func (m regMask) String() string {
|
|
|
|
|
s := ""
|
2016-03-21 22:57:26 -07:00
|
|
|
for r := register(0); m != 0; r++ {
|
2015-08-11 12:51:33 -07:00
|
|
|
if m>>r&1 == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2016-03-21 22:57:26 -07:00
|
|
|
m &^= regMask(1) << r
|
2015-08-11 12:51:33 -07:00
|
|
|
if s != "" {
|
|
|
|
|
s += " "
|
|
|
|
|
}
|
|
|
|
|
s += fmt.Sprintf("r%d", r)
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-05 16:19:12 -07:00
|
|
|
// countRegs returns the number of set bits in the register mask.
|
|
|
|
|
func countRegs(r regMask) int {
|
|
|
|
|
n := 0
|
|
|
|
|
for r != 0 {
|
|
|
|
|
n += int(r & 1)
|
|
|
|
|
r >>= 1
|
|
|
|
|
}
|
|
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pickReg picks an arbitrary register from the register mask.
|
|
|
|
|
func pickReg(r regMask) register {
|
|
|
|
|
// pick the lowest one
|
|
|
|
|
if r == 0 {
|
|
|
|
|
panic("can't pick a register from an empty set")
|
|
|
|
|
}
|
|
|
|
|
for i := register(0); ; i++ {
|
|
|
|
|
if r&1 != 0 {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
r >>= 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
type use struct {
|
2015-11-05 14:59:47 -08:00
|
|
|
dist int32 // distance from start of the block to a use of a value
|
|
|
|
|
next *use // linked list of uses of a value in nondecreasing dist order
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
type valState struct {
|
2015-12-17 10:01:24 -08:00
|
|
|
regs regMask // the set of registers holding a Value (usually just one)
|
|
|
|
|
uses *use // list of uses in this block
|
|
|
|
|
spill *Value // spilled copy of the Value
|
|
|
|
|
spillUsed bool
|
2016-03-21 11:32:04 -04:00
|
|
|
spillUsedShuffle bool // true if used in shuffling, after ordinary uses
|
2016-01-18 20:00:15 -08:00
|
|
|
needReg bool // cached value of !v.Type.IsMemory() && !v.Type.IsVoid() && !.v.Type.IsFlags()
|
|
|
|
|
rematerializeable bool // cached value of v.rematerializeable()
|
|
|
|
|
desired register // register we want value to be in, if any
|
|
|
|
|
avoid regMask // registers to avoid if we can
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
type regState struct {
|
|
|
|
|
v *Value // Original (preregalloc) Value stored in this register.
|
2015-11-05 14:59:47 -08:00
|
|
|
c *Value // A Value equal to v which is currently in a register. Might be v or a copy of it.
|
2015-08-11 12:51:33 -07:00
|
|
|
// If a register is unused, v==c==nil
|
|
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
type regAllocState struct {
|
|
|
|
|
f *Func
|
|
|
|
|
|
2016-03-21 22:57:26 -07:00
|
|
|
registers []Register
|
|
|
|
|
numRegs register
|
|
|
|
|
SPReg register
|
|
|
|
|
SBReg register
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
// for each block, its primary predecessor.
|
|
|
|
|
// A predecessor of b is primary if it is the closest
|
|
|
|
|
// predecessor that appears before b in the layout order.
|
|
|
|
|
// We record the index in the Preds list where the primary predecessor sits.
|
|
|
|
|
primary []int32
|
|
|
|
|
|
2015-11-05 14:59:47 -08:00
|
|
|
// live values at the end of each block. live[b.ID] is a list of value IDs
|
|
|
|
|
// which are live at the end of b, together with a count of how many instructions
|
|
|
|
|
// forward to the next use.
|
|
|
|
|
live [][]liveInfo
|
2015-08-11 12:51:33 -07:00
|
|
|
|
|
|
|
|
// current state of each (preregalloc) Value
|
|
|
|
|
values []valState
|
|
|
|
|
|
2015-10-29 13:41:02 -07:00
|
|
|
// For each Value, map from its value ID back to the
|
|
|
|
|
// preregalloc Value it was derived from.
|
|
|
|
|
orig []*Value
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
// current state of each register
|
|
|
|
|
regs []regState
|
|
|
|
|
|
|
|
|
|
// registers that contain values which can't be kicked out
|
|
|
|
|
nospill regMask
|
|
|
|
|
|
|
|
|
|
// mask of registers currently in use
|
|
|
|
|
used regMask
|
|
|
|
|
|
|
|
|
|
// current block we're working on
|
|
|
|
|
curBlock *Block
|
2015-11-05 14:59:47 -08:00
|
|
|
|
|
|
|
|
// cache of use records
|
|
|
|
|
freeUseRecords *use
|
2015-12-17 10:01:24 -08:00
|
|
|
|
|
|
|
|
// endRegs[blockid] is the register state at the end of each block.
|
|
|
|
|
// encoded as a set of endReg records.
|
|
|
|
|
endRegs [][]endReg
|
|
|
|
|
|
|
|
|
|
// startRegs[blockid] is the register state at the start of merge blocks.
|
|
|
|
|
// saved state does not include the state of phi ops in the block.
|
|
|
|
|
startRegs [][]startReg
|
|
|
|
|
|
|
|
|
|
// spillLive[blockid] is the set of live spills at the end of each block
|
|
|
|
|
spillLive [][]ID
|
2016-03-10 14:42:52 -05:00
|
|
|
|
|
|
|
|
loopnest *loopnest
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
|
|
|
|
|
2016-03-21 11:32:04 -04:00
|
|
|
type spillToSink struct {
|
|
|
|
|
spill *Value // Spill instruction to move (a StoreReg)
|
|
|
|
|
dests int32 // Bitmask indicating exit blocks from loop in which spill/val is defined. 1<<i set means val is live into loop.exitBlocks[i]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (sts *spillToSink) spilledValue() *Value {
|
|
|
|
|
return sts.spill.Args[0]
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-17 10:01:24 -08:00
|
|
|
type endReg struct {
|
|
|
|
|
r register
|
|
|
|
|
v *Value // pre-regalloc value held in this register (TODO: can we use ID here?)
|
|
|
|
|
c *Value // cached version of the value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type startReg struct {
|
|
|
|
|
r register
|
|
|
|
|
vid ID // pre-regalloc value needed in this register
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// freeReg frees up register r. Any current user of r is kicked out.
|
2015-08-11 12:51:33 -07:00
|
|
|
func (s *regAllocState) freeReg(r register) {
|
|
|
|
|
v := s.regs[r].v
|
|
|
|
|
if v == nil {
|
|
|
|
|
s.f.Fatalf("tried to free an already free register %d\n", r)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mark r as unused.
|
2016-03-10 17:52:57 -06:00
|
|
|
if s.f.pass.debug > regDebug {
|
2016-03-21 22:57:26 -07:00
|
|
|
fmt.Printf("freeReg %s (dump %s/%s)\n", s.registers[r].Name(), v, s.regs[r].c)
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
s.regs[r] = regState{}
|
|
|
|
|
s.values[v.ID].regs &^= regMask(1) << r
|
|
|
|
|
s.used &^= regMask(1) << r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// freeRegs frees up all registers listed in m.
|
|
|
|
|
func (s *regAllocState) freeRegs(m regMask) {
|
|
|
|
|
for m&s.used != 0 {
|
|
|
|
|
s.freeReg(pickReg(m & s.used))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-29 13:41:02 -07:00
|
|
|
// setOrig records that c's original value is the same as
|
|
|
|
|
// v's original value.
|
|
|
|
|
func (s *regAllocState) setOrig(c *Value, v *Value) {
|
|
|
|
|
for int(c.ID) >= len(s.orig) {
|
|
|
|
|
s.orig = append(s.orig, nil)
|
|
|
|
|
}
|
|
|
|
|
if s.orig[c.ID] != nil {
|
|
|
|
|
s.f.Fatalf("orig value set twice %s %s", c, v)
|
|
|
|
|
}
|
|
|
|
|
s.orig[c.ID] = s.orig[v.ID]
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
// assignReg assigns register r to hold c, a copy of v.
|
|
|
|
|
// r must be unused.
|
|
|
|
|
func (s *regAllocState) assignReg(r register, v *Value, c *Value) {
|
2016-03-10 17:52:57 -06:00
|
|
|
if s.f.pass.debug > regDebug {
|
2016-03-21 22:57:26 -07:00
|
|
|
fmt.Printf("assignReg %s %s/%s\n", s.registers[r].Name(), v, c)
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
if s.regs[r].v != nil {
|
|
|
|
|
s.f.Fatalf("tried to assign register %d to %s/%s but it is already used by %s", r, v, c, s.regs[r].v)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update state.
|
|
|
|
|
s.regs[r] = regState{v, c}
|
|
|
|
|
s.values[v.ID].regs |= regMask(1) << r
|
|
|
|
|
s.used |= regMask(1) << r
|
2016-03-21 22:57:26 -07:00
|
|
|
s.f.setHome(c, &s.registers[r])
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
|
2016-01-18 20:00:15 -08:00
|
|
|
// allocReg chooses a register for v from the set of registers in mask.
|
|
|
|
|
// If there is no unused register, a Value will be kicked out of
|
|
|
|
|
// a register to make room.
|
|
|
|
|
func (s *regAllocState) allocReg(v *Value, mask regMask) register {
|
2015-08-11 12:51:33 -07:00
|
|
|
mask &^= s.nospill
|
|
|
|
|
if mask == 0 {
|
|
|
|
|
s.f.Fatalf("no register available")
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-18 20:00:15 -08:00
|
|
|
// Pick an unused register if one is available.
|
|
|
|
|
if mask&^s.used != 0 {
|
|
|
|
|
mask &^= s.used
|
|
|
|
|
|
|
|
|
|
// Use desired register if we can.
|
|
|
|
|
d := s.values[v.ID].desired
|
|
|
|
|
if d != noRegister && mask>>d&1 != 0 {
|
|
|
|
|
mask = regMask(1) << d
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Avoid avoidable registers if we can.
|
|
|
|
|
if mask&^s.values[v.ID].avoid != 0 {
|
|
|
|
|
mask &^= s.values[v.ID].avoid
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pickReg(mask)
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
2016-01-18 20:00:15 -08:00
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Pick a value to spill. Spill the value with the
|
2015-08-11 12:51:33 -07:00
|
|
|
// farthest-in-the-future use.
|
|
|
|
|
// TODO: Prefer registers with already spilled Values?
|
|
|
|
|
// TODO: Modify preference using affinity graph.
|
2015-11-05 14:59:47 -08:00
|
|
|
// TODO: if a single value is in multiple registers, spill one of them
|
|
|
|
|
// before spilling a value in just a single register.
|
2015-09-11 16:40:05 -04:00
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// SP and SB are allocated specially. No regular value should
|
2015-09-11 16:40:05 -04:00
|
|
|
// be allocated to them.
|
2016-03-21 22:57:26 -07:00
|
|
|
mask &^= 1<<s.SPReg | 1<<s.SBReg
|
2015-09-11 16:40:05 -04:00
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Find a register to spill. We spill the register containing the value
|
2015-11-05 14:59:47 -08:00
|
|
|
// whose next use is as far in the future as possible.
|
|
|
|
|
// https://en.wikipedia.org/wiki/Page_replacement_algorithm#The_theoretically_optimal_page_replacement_algorithm
|
2016-01-18 20:00:15 -08:00
|
|
|
var r register
|
2015-08-11 12:51:33 -07:00
|
|
|
maxuse := int32(-1)
|
2016-03-21 22:57:26 -07:00
|
|
|
for t := register(0); t < s.numRegs; t++ {
|
2015-08-11 12:51:33 -07:00
|
|
|
if mask>>t&1 == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
v := s.regs[t].v
|
2015-11-05 14:59:47 -08:00
|
|
|
if n := s.values[v.ID].uses.dist; n > maxuse {
|
|
|
|
|
// v's next use is farther in the future than any value
|
2016-03-01 23:21:55 +00:00
|
|
|
// we've seen so far. A new best spill candidate.
|
2015-08-11 12:51:33 -07:00
|
|
|
r = t
|
|
|
|
|
maxuse = n
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if maxuse == -1 {
|
|
|
|
|
s.f.Unimplementedf("couldn't find register to spill")
|
|
|
|
|
}
|
|
|
|
|
s.freeReg(r)
|
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// allocValToReg allocates v to a register selected from regMask and
|
|
|
|
|
// returns the register copy of v. Any previous user is kicked out and spilled
|
|
|
|
|
// (if necessary). Load code is added at the current pc. If nospill is set the
|
|
|
|
|
// allocated register is marked nospill so the assignment cannot be
|
|
|
|
|
// undone until the caller allows it by clearing nospill. Returns a
|
|
|
|
|
// *Value which is either v or a copy of v allocated to the chosen register.
|
2016-02-23 21:09:39 -05:00
|
|
|
func (s *regAllocState) allocValToReg(v *Value, mask regMask, nospill bool, line int32) *Value {
|
2015-08-11 12:51:33 -07:00
|
|
|
vi := &s.values[v.ID]
|
|
|
|
|
|
|
|
|
|
// Check if v is already in a requested register.
|
|
|
|
|
if mask&vi.regs != 0 {
|
|
|
|
|
r := pickReg(mask & vi.regs)
|
|
|
|
|
if s.regs[r].v != v || s.regs[r].c == nil {
|
|
|
|
|
panic("bad register state")
|
|
|
|
|
}
|
|
|
|
|
if nospill {
|
|
|
|
|
s.nospill |= regMask(1) << r
|
|
|
|
|
}
|
|
|
|
|
return s.regs[r].c
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-05 14:59:47 -08:00
|
|
|
if v.Op != OpSP {
|
2016-03-21 22:57:26 -07:00
|
|
|
mask &^= 1 << s.SPReg // dont' spill SP
|
2015-11-05 14:59:47 -08:00
|
|
|
}
|
|
|
|
|
if v.Op != OpSB {
|
2016-03-21 22:57:26 -07:00
|
|
|
mask &^= 1 << s.SBReg // don't spill SB
|
2015-11-05 14:59:47 -08:00
|
|
|
}
|
2015-10-22 13:07:38 -07:00
|
|
|
mask &^= s.reserved()
|
2015-08-11 12:51:33 -07:00
|
|
|
|
|
|
|
|
// Allocate a register.
|
2016-01-18 20:00:15 -08:00
|
|
|
r := s.allocReg(v, mask)
|
2015-08-11 12:51:33 -07:00
|
|
|
|
|
|
|
|
// Allocate v to the new register.
|
|
|
|
|
var c *Value
|
|
|
|
|
if vi.regs != 0 {
|
|
|
|
|
// Copy from a register that v is already in.
|
|
|
|
|
r2 := pickReg(vi.regs)
|
|
|
|
|
if s.regs[r2].v != v {
|
|
|
|
|
panic("bad register state")
|
|
|
|
|
}
|
2016-02-23 21:09:39 -05:00
|
|
|
c = s.curBlock.NewValue1(line, OpCopy, v.Type, s.regs[r2].c)
|
2015-10-19 10:57:03 -07:00
|
|
|
} else if v.rematerializeable() {
|
|
|
|
|
// Rematerialize instead of loading from the spill location.
|
2015-12-09 15:58:18 -08:00
|
|
|
c = v.copyInto(s.curBlock)
|
2015-08-11 12:51:33 -07:00
|
|
|
} else {
|
[dev.ssa] cmd/compile: support spilling and loading flags
This CL takes a simple approach to spilling and loading flags.
We never spill. When a load is needed, we recalculate,
loading the arguments as needed.
This is simple and architecture-independent.
It is not very efficient, but as of this CL,
there are fewer than 200 flag spills during make.bash.
This was tested by manually reverting CLs 13813 and 13843,
causing SETcc, MOV, and LEA instructions to clobber flags,
which dramatically increases the number of flags spills.
With that done, all stdlib tests that used to pass
still pass.
For future reference, here are some other, more efficient
amd64-only schemes that we could adapt in the future if needed.
(1) Spill exactly the flags needed.
For example, if we know that the flags will be needed
by a SETcc or Jcc op later, we could use SETcc to
extract just the relevant flag. When needed,
we could use TESTB and change the op to JNE/SETNE.
(Alternatively, we could leave the op unaltered
and prepare an appropriate CMPB instruction
to produce the desired flag.)
However, this requires separate handling for every
instruction that uses the flags register,
including (say) SBBQcarrymask.
We could enable this on an ad hoc basis for common cases
and fall back to recalculation for other cases.
(2) Spill all flags with PUSHF and POPF
This modifies SP, which the runtime won't like.
It also requires coordination with stackalloc to
make sure that we have a stack slot ready for use.
(3) Spill almost all flags with LAHF, SETO, and SAHF
See http://blog.freearrow.com/archives/396
for details. This would handle all the flags we currently
use. However, LAHF and SAHF are not universally available
and it requires arranging for AX to be free.
Change-Id: Ie36600fd8e807ef2bee83e2e2ae3685112a7f276
Reviewed-on: https://go-review.googlesource.com/13844
Reviewed-by: Keith Randall <khr@golang.org>
2015-08-22 19:38:12 -07:00
|
|
|
switch {
|
2015-08-11 12:51:33 -07:00
|
|
|
// Load v from its spill location.
|
[dev.ssa] cmd/compile: support spilling and loading flags
This CL takes a simple approach to spilling and loading flags.
We never spill. When a load is needed, we recalculate,
loading the arguments as needed.
This is simple and architecture-independent.
It is not very efficient, but as of this CL,
there are fewer than 200 flag spills during make.bash.
This was tested by manually reverting CLs 13813 and 13843,
causing SETcc, MOV, and LEA instructions to clobber flags,
which dramatically increases the number of flags spills.
With that done, all stdlib tests that used to pass
still pass.
For future reference, here are some other, more efficient
amd64-only schemes that we could adapt in the future if needed.
(1) Spill exactly the flags needed.
For example, if we know that the flags will be needed
by a SETcc or Jcc op later, we could use SETcc to
extract just the relevant flag. When needed,
we could use TESTB and change the op to JNE/SETNE.
(Alternatively, we could leave the op unaltered
and prepare an appropriate CMPB instruction
to produce the desired flag.)
However, this requires separate handling for every
instruction that uses the flags register,
including (say) SBBQcarrymask.
We could enable this on an ad hoc basis for common cases
and fall back to recalculation for other cases.
(2) Spill all flags with PUSHF and POPF
This modifies SP, which the runtime won't like.
It also requires coordination with stackalloc to
make sure that we have a stack slot ready for use.
(3) Spill almost all flags with LAHF, SETO, and SAHF
See http://blog.freearrow.com/archives/396
for details. This would handle all the flags we currently
use. However, LAHF and SAHF are not universally available
and it requires arranging for AX to be free.
Change-Id: Ie36600fd8e807ef2bee83e2e2ae3685112a7f276
Reviewed-on: https://go-review.googlesource.com/13844
Reviewed-by: Keith Randall <khr@golang.org>
2015-08-22 19:38:12 -07:00
|
|
|
case vi.spill != nil:
|
2016-03-10 17:52:57 -06:00
|
|
|
if s.f.pass.debug > logSpills {
|
2016-04-08 13:33:43 -04:00
|
|
|
s.f.Config.Warnl(vi.spill.Line, "load spill for %v from %v", v, vi.spill)
|
2015-08-30 21:39:25 -05:00
|
|
|
}
|
2016-02-23 21:09:39 -05:00
|
|
|
c = s.curBlock.NewValue1(line, OpLoadReg, v.Type, vi.spill)
|
2015-08-11 12:51:33 -07:00
|
|
|
vi.spillUsed = true
|
[dev.ssa] cmd/compile: support spilling and loading flags
This CL takes a simple approach to spilling and loading flags.
We never spill. When a load is needed, we recalculate,
loading the arguments as needed.
This is simple and architecture-independent.
It is not very efficient, but as of this CL,
there are fewer than 200 flag spills during make.bash.
This was tested by manually reverting CLs 13813 and 13843,
causing SETcc, MOV, and LEA instructions to clobber flags,
which dramatically increases the number of flags spills.
With that done, all stdlib tests that used to pass
still pass.
For future reference, here are some other, more efficient
amd64-only schemes that we could adapt in the future if needed.
(1) Spill exactly the flags needed.
For example, if we know that the flags will be needed
by a SETcc or Jcc op later, we could use SETcc to
extract just the relevant flag. When needed,
we could use TESTB and change the op to JNE/SETNE.
(Alternatively, we could leave the op unaltered
and prepare an appropriate CMPB instruction
to produce the desired flag.)
However, this requires separate handling for every
instruction that uses the flags register,
including (say) SBBQcarrymask.
We could enable this on an ad hoc basis for common cases
and fall back to recalculation for other cases.
(2) Spill all flags with PUSHF and POPF
This modifies SP, which the runtime won't like.
It also requires coordination with stackalloc to
make sure that we have a stack slot ready for use.
(3) Spill almost all flags with LAHF, SETO, and SAHF
See http://blog.freearrow.com/archives/396
for details. This would handle all the flags we currently
use. However, LAHF and SAHF are not universally available
and it requires arranging for AX to be free.
Change-Id: Ie36600fd8e807ef2bee83e2e2ae3685112a7f276
Reviewed-on: https://go-review.googlesource.com/13844
Reviewed-by: Keith Randall <khr@golang.org>
2015-08-22 19:38:12 -07:00
|
|
|
default:
|
|
|
|
|
s.f.Fatalf("attempt to load unspilled value %v", v.LongString())
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
}
|
2015-10-29 13:41:02 -07:00
|
|
|
s.setOrig(c, v)
|
2015-08-11 12:51:33 -07:00
|
|
|
s.assignReg(r, v, c)
|
|
|
|
|
if nospill {
|
|
|
|
|
s.nospill |= regMask(1) << r
|
|
|
|
|
}
|
|
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *regAllocState) init(f *Func) {
|
2016-03-21 22:57:26 -07:00
|
|
|
s.registers = f.Config.registers
|
|
|
|
|
s.numRegs = register(len(s.registers))
|
|
|
|
|
if s.numRegs > noRegister || s.numRegs > register(unsafe.Sizeof(regMask(0))*8) {
|
2015-08-11 12:51:33 -07:00
|
|
|
panic("too many registers")
|
|
|
|
|
}
|
2016-03-21 22:57:26 -07:00
|
|
|
for r := register(0); r < s.numRegs; r++ {
|
|
|
|
|
if s.registers[r].Name() == "SP" {
|
|
|
|
|
s.SPReg = r
|
|
|
|
|
}
|
|
|
|
|
if s.registers[r].Name() == "SB" {
|
|
|
|
|
s.SBReg = r
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
|
|
|
|
|
s.f = f
|
2016-03-21 22:57:26 -07:00
|
|
|
s.regs = make([]regState, s.numRegs)
|
2015-08-11 12:51:33 -07:00
|
|
|
s.values = make([]valState, f.NumValues())
|
2015-10-29 13:41:02 -07:00
|
|
|
s.orig = make([]*Value, f.NumValues())
|
|
|
|
|
for _, b := range f.Blocks {
|
|
|
|
|
for _, v := range b.Values {
|
2015-12-17 10:01:24 -08:00
|
|
|
if !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() {
|
|
|
|
|
s.values[v.ID].needReg = true
|
|
|
|
|
s.values[v.ID].rematerializeable = v.rematerializeable()
|
2016-01-18 20:00:15 -08:00
|
|
|
s.values[v.ID].desired = noRegister
|
2015-12-17 10:01:24 -08:00
|
|
|
s.orig[v.ID] = v
|
2015-11-05 14:59:47 -08:00
|
|
|
}
|
2015-10-29 13:41:02 -07:00
|
|
|
}
|
|
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
s.computeLive()
|
2015-05-18 16:44:20 -07:00
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Compute block order. This array allows us to distinguish forward edges
|
2015-08-11 12:51:33 -07:00
|
|
|
// from backward edges and compute how far they go.
|
|
|
|
|
blockOrder := make([]int32, f.NumBlocks())
|
|
|
|
|
for i, b := range f.Blocks {
|
|
|
|
|
blockOrder[b.ID] = int32(i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compute primary predecessors.
|
|
|
|
|
s.primary = make([]int32, f.NumBlocks())
|
2015-05-05 16:19:12 -07:00
|
|
|
for _, b := range f.Blocks {
|
2015-08-11 12:51:33 -07:00
|
|
|
best := -1
|
|
|
|
|
for i, p := range b.Preds {
|
|
|
|
|
if blockOrder[p.ID] >= blockOrder[b.ID] {
|
|
|
|
|
continue // backward edge
|
|
|
|
|
}
|
|
|
|
|
if best == -1 || blockOrder[p.ID] > blockOrder[b.Preds[best].ID] {
|
|
|
|
|
best = i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
s.primary[b.ID] = int32(best)
|
|
|
|
|
}
|
2015-12-17 10:01:24 -08:00
|
|
|
|
|
|
|
|
s.endRegs = make([][]endReg, f.NumBlocks())
|
|
|
|
|
s.startRegs = make([][]startReg, f.NumBlocks())
|
|
|
|
|
s.spillLive = make([][]ID, f.NumBlocks())
|
2015-11-05 14:59:47 -08:00
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2015-11-05 14:59:47 -08:00
|
|
|
// Adds a use record for id at distance dist from the start of the block.
|
|
|
|
|
// All calls to addUse must happen with nonincreasing dist.
|
|
|
|
|
func (s *regAllocState) addUse(id ID, dist int32) {
|
|
|
|
|
r := s.freeUseRecords
|
|
|
|
|
if r != nil {
|
|
|
|
|
s.freeUseRecords = r.next
|
|
|
|
|
} else {
|
|
|
|
|
r = &use{}
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
r.dist = dist
|
|
|
|
|
r.next = s.values[id].uses
|
|
|
|
|
s.values[id].uses = r
|
|
|
|
|
if r.next != nil && dist > r.next.dist {
|
|
|
|
|
s.f.Fatalf("uses added in wrong order")
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2015-11-05 14:59:47 -08:00
|
|
|
// advanceUses advances the uses of v's args from the state before v to the state after v.
|
|
|
|
|
// Any values which have no more uses are deallocated from registers.
|
|
|
|
|
func (s *regAllocState) advanceUses(v *Value) {
|
|
|
|
|
for _, a := range v.Args {
|
2015-12-17 10:01:24 -08:00
|
|
|
if !s.values[a.ID].needReg {
|
2015-08-11 12:51:33 -07:00
|
|
|
continue
|
|
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
ai := &s.values[a.ID]
|
|
|
|
|
r := ai.uses
|
|
|
|
|
ai.uses = r.next
|
|
|
|
|
if r.next == nil {
|
|
|
|
|
// Value is dead, free all registers that hold it.
|
|
|
|
|
s.freeRegs(ai.regs)
|
|
|
|
|
}
|
|
|
|
|
r.next = s.freeUseRecords
|
|
|
|
|
s.freeUseRecords = r
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2016-04-10 08:26:43 -07:00
|
|
|
// liveAfterCurrentInstruction reports whether v is live after
|
|
|
|
|
// the current instruction is completed. v must be used by the
|
|
|
|
|
// current instruction.
|
|
|
|
|
func (s *regAllocState) liveAfterCurrentInstruction(v *Value) bool {
|
|
|
|
|
u := s.values[v.ID].uses
|
|
|
|
|
d := u.dist
|
|
|
|
|
for u != nil && u.dist == d {
|
|
|
|
|
u = u.next
|
|
|
|
|
}
|
|
|
|
|
return u != nil && u.dist > d
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-17 10:01:24 -08:00
|
|
|
// Sets the state of the registers to that encoded in regs.
|
|
|
|
|
func (s *regAllocState) setState(regs []endReg) {
|
2015-08-11 12:51:33 -07:00
|
|
|
s.freeRegs(s.used)
|
2015-12-17 10:01:24 -08:00
|
|
|
for _, x := range regs {
|
|
|
|
|
s.assignReg(x.r, x.v, x.c)
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
}
|
2015-05-18 16:44:20 -07:00
|
|
|
|
2015-12-17 10:01:24 -08:00
|
|
|
// compatRegs returns the set of registers which can store a type t.
|
|
|
|
|
func (s *regAllocState) compatRegs(t Type) regMask {
|
2015-10-22 13:07:38 -07:00
|
|
|
var m regMask
|
2016-03-02 15:18:40 -08:00
|
|
|
if t.IsFloat() || t == TypeInt128 {
|
2015-10-22 13:07:38 -07:00
|
|
|
m = 0xffff << 16 // X0-X15
|
|
|
|
|
} else {
|
|
|
|
|
m = 0xffef << 0 // AX-R15, except SP
|
2015-08-25 22:49:59 -05:00
|
|
|
}
|
2015-10-22 13:07:38 -07:00
|
|
|
return m &^ s.reserved()
|
2015-08-25 22:49:59 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-21 11:32:04 -04:00
|
|
|
// loopForBlock returns the loop containing block b,
|
|
|
|
|
// provided that the loop is "interesting" for purposes
|
|
|
|
|
// of improving register allocation (= is inner, and does
|
|
|
|
|
// not contain a call)
|
|
|
|
|
func (s *regAllocState) loopForBlock(b *Block) *loop {
|
|
|
|
|
loop := s.loopnest.b2l[b.ID]
|
|
|
|
|
|
|
|
|
|
// Minor for-the-time-being optimization: nothing happens
|
|
|
|
|
// unless a loop is both inner and call-free, therefore
|
|
|
|
|
// don't bother with other loops.
|
|
|
|
|
if loop != nil && (loop.containsCall || !loop.isInner) {
|
|
|
|
|
loop = nil
|
|
|
|
|
}
|
|
|
|
|
return loop
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
func (s *regAllocState) regalloc(f *Func) {
|
2016-01-28 22:19:46 -06:00
|
|
|
liveSet := f.newSparseSet(f.NumValues())
|
|
|
|
|
defer f.retSparseSet(liveSet)
|
2015-08-11 12:51:33 -07:00
|
|
|
var oldSched []*Value
|
|
|
|
|
var phis []*Value
|
2015-11-05 14:59:47 -08:00
|
|
|
var phiRegs []register
|
|
|
|
|
var args []*Value
|
2015-08-11 12:51:33 -07:00
|
|
|
|
2016-03-21 11:32:04 -04:00
|
|
|
// statistics
|
|
|
|
|
var nSpills int // # of spills remaining
|
|
|
|
|
var nSpillsInner int // # of spills remaining in inner loops
|
|
|
|
|
var nSpillsSunk int // # of sunk spills remaining
|
|
|
|
|
var nSpillsChanged int // # of sunk spills lost because of register use change
|
|
|
|
|
var nSpillsSunkUnused int // # of spills not sunk because they were removed completely
|
|
|
|
|
var nSpillsNotSunkLateUse int // # of spills not sunk because of very late use (in shuffle)
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
if f.Entry != f.Blocks[0] {
|
|
|
|
|
f.Fatalf("entry block must be first")
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-21 11:32:04 -04:00
|
|
|
// Get loop nest so that spills in inner loops can be
|
|
|
|
|
// tracked. When the last block of a loop is processed,
|
|
|
|
|
// attempt to move spills out of the loop.
|
|
|
|
|
s.loopnest.findExits()
|
|
|
|
|
|
|
|
|
|
// Spills are moved from one block's slice of values to another's.
|
|
|
|
|
// This confuses register allocation if it occurs before it is
|
|
|
|
|
// complete, so candidates are recorded, then rechecked and
|
|
|
|
|
// moved after all allocation (register and stack) is complete.
|
|
|
|
|
// Because movement is only within a stack slot's lifetime, it
|
|
|
|
|
// is safe to do this.
|
|
|
|
|
var toSink []spillToSink
|
|
|
|
|
// Will be used to figure out live inputs to exit blocks of inner loops.
|
|
|
|
|
entryCandidates := newSparseMap(f.NumValues())
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
for _, b := range f.Blocks {
|
|
|
|
|
s.curBlock = b
|
2016-03-21 11:32:04 -04:00
|
|
|
loop := s.loopForBlock(b)
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2015-11-05 14:59:47 -08:00
|
|
|
// Initialize liveSet and uses fields for this block.
|
|
|
|
|
// Walk backwards through the block doing liveness analysis.
|
|
|
|
|
liveSet.clear()
|
2016-03-02 15:18:40 -08:00
|
|
|
d := int32(len(b.Values))
|
2016-03-09 19:27:57 -08:00
|
|
|
if b.Kind == BlockCall || b.Kind == BlockDefer {
|
2016-03-02 15:18:40 -08:00
|
|
|
d += unlikelyDistance
|
|
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
for _, e := range s.live[b.ID] {
|
2016-03-02 15:18:40 -08:00
|
|
|
s.addUse(e.ID, d+e.dist) // pseudo-uses from beyond end of block
|
2015-11-05 14:59:47 -08:00
|
|
|
liveSet.add(e.ID)
|
|
|
|
|
}
|
2015-12-17 10:01:24 -08:00
|
|
|
if v := b.Control; v != nil && s.values[v.ID].needReg {
|
|
|
|
|
s.addUse(v.ID, int32(len(b.Values))) // psuedo-use by control value
|
|
|
|
|
liveSet.add(v.ID)
|
2015-11-05 14:59:47 -08:00
|
|
|
}
|
|
|
|
|
for i := len(b.Values) - 1; i >= 0; i-- {
|
|
|
|
|
v := b.Values[i]
|
2015-12-17 10:01:24 -08:00
|
|
|
liveSet.remove(v.ID)
|
2015-11-05 14:59:47 -08:00
|
|
|
if v.Op == OpPhi {
|
2015-12-17 10:01:24 -08:00
|
|
|
// Remove v from the live set, but don't add
|
2016-03-01 23:21:55 +00:00
|
|
|
// any inputs. This is the state the len(b.Preds)>1
|
2015-12-17 10:01:24 -08:00
|
|
|
// case below desires; it wants to process phis specially.
|
|
|
|
|
continue
|
2015-11-05 14:59:47 -08:00
|
|
|
}
|
|
|
|
|
for _, a := range v.Args {
|
2015-12-17 10:01:24 -08:00
|
|
|
if !s.values[a.ID].needReg {
|
2015-11-05 14:59:47 -08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
s.addUse(a.ID, int32(i))
|
|
|
|
|
liveSet.add(a.ID)
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-10 17:52:57 -06:00
|
|
|
if s.f.pass.debug > regDebug {
|
2015-11-05 14:59:47 -08:00
|
|
|
fmt.Printf("uses for %s:%s\n", s.f.Name, b)
|
|
|
|
|
for i := range s.values {
|
|
|
|
|
vi := &s.values[i]
|
|
|
|
|
u := vi.uses
|
|
|
|
|
if u == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf(" v%d:", i)
|
2015-11-05 14:59:47 -08:00
|
|
|
for u != nil {
|
|
|
|
|
fmt.Printf(" %d", u.dist)
|
|
|
|
|
u = u.next
|
|
|
|
|
}
|
|
|
|
|
fmt.Println()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
// Make a copy of the block schedule so we can generate a new one in place.
|
|
|
|
|
// We make a separate copy for phis and regular values.
|
|
|
|
|
nphi := 0
|
|
|
|
|
for _, v := range b.Values {
|
|
|
|
|
if v.Op != OpPhi {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
nphi++
|
|
|
|
|
}
|
|
|
|
|
phis = append(phis[:0], b.Values[:nphi]...)
|
|
|
|
|
oldSched = append(oldSched[:0], b.Values[nphi:]...)
|
2015-05-05 16:19:12 -07:00
|
|
|
b.Values = b.Values[:0]
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
// Initialize start state of block.
|
|
|
|
|
if b == f.Entry {
|
|
|
|
|
// Regalloc state is empty to start.
|
|
|
|
|
if nphi > 0 {
|
|
|
|
|
f.Fatalf("phis in entry block")
|
2015-06-06 16:03:33 -07:00
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
} else if len(b.Preds) == 1 {
|
|
|
|
|
// Start regalloc state with the end state of the previous block.
|
2015-12-17 10:01:24 -08:00
|
|
|
s.setState(s.endRegs[b.Preds[0].ID])
|
2015-08-11 12:51:33 -07:00
|
|
|
if nphi > 0 {
|
|
|
|
|
f.Fatalf("phis in single-predecessor block")
|
|
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
// Drop any values which are no longer live.
|
|
|
|
|
// This may happen because at the end of p, a value may be
|
|
|
|
|
// live but only used by some other successor of p.
|
2016-03-21 22:57:26 -07:00
|
|
|
for r := register(0); r < s.numRegs; r++ {
|
2015-11-05 14:59:47 -08:00
|
|
|
v := s.regs[r].v
|
|
|
|
|
if v != nil && !liveSet.contains(v.ID) {
|
|
|
|
|
s.freeReg(r)
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
} else {
|
2016-03-01 23:21:55 +00:00
|
|
|
// This is the complicated case. We have more than one predecessor,
|
2015-08-11 12:51:33 -07:00
|
|
|
// which means we may have Phi ops.
|
|
|
|
|
|
|
|
|
|
// Copy phi ops into new schedule.
|
|
|
|
|
b.Values = append(b.Values, phis...)
|
|
|
|
|
|
|
|
|
|
// Start with the final register state of the primary predecessor
|
|
|
|
|
idx := s.primary[b.ID]
|
|
|
|
|
if idx < 0 {
|
|
|
|
|
f.Fatalf("block with no primary predecessor %s", b)
|
|
|
|
|
}
|
|
|
|
|
p := b.Preds[idx]
|
2015-12-17 10:01:24 -08:00
|
|
|
s.setState(s.endRegs[p.ID])
|
|
|
|
|
|
2016-03-10 17:52:57 -06:00
|
|
|
if s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf("starting merge block %s with end state of %s:\n", b, p)
|
|
|
|
|
for _, x := range s.endRegs[p.ID] {
|
2016-03-21 22:57:26 -07:00
|
|
|
fmt.Printf(" %s: orig:%s cache:%s\n", s.registers[x.r].Name(), x.v, x.c)
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
|
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Decide on registers for phi ops. Use the registers determined
|
2015-08-11 12:51:33 -07:00
|
|
|
// by the primary predecessor if we can.
|
|
|
|
|
// TODO: pick best of (already processed) predecessors?
|
|
|
|
|
// Majority vote? Deepest nesting level?
|
|
|
|
|
phiRegs = phiRegs[:0]
|
2015-12-17 10:01:24 -08:00
|
|
|
var phiUsed regMask
|
2015-08-11 12:51:33 -07:00
|
|
|
for _, v := range phis {
|
2015-12-17 10:01:24 -08:00
|
|
|
if !s.values[v.ID].needReg {
|
2015-08-11 12:51:33 -07:00
|
|
|
phiRegs = append(phiRegs, noRegister)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2015-12-17 10:01:24 -08:00
|
|
|
a := v.Args[idx]
|
|
|
|
|
m := s.values[a.ID].regs &^ phiUsed
|
2015-08-11 12:51:33 -07:00
|
|
|
if m != 0 {
|
2016-03-24 20:57:53 +11:00
|
|
|
r := pickReg(m)
|
2015-12-17 10:01:24 -08:00
|
|
|
s.freeReg(r)
|
|
|
|
|
phiUsed |= regMask(1) << r
|
|
|
|
|
phiRegs = append(phiRegs, r)
|
2015-08-11 12:51:33 -07:00
|
|
|
} else {
|
2015-12-17 10:01:24 -08:00
|
|
|
phiRegs = append(phiRegs, noRegister)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Second pass - deallocate any phi inputs which are now dead.
|
|
|
|
|
for _, v := range phis {
|
|
|
|
|
if !s.values[v.ID].needReg {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
a := v.Args[idx]
|
|
|
|
|
if !liveSet.contains(a.ID) {
|
|
|
|
|
// Input is dead beyond the phi, deallocate
|
|
|
|
|
// anywhere else it might live.
|
|
|
|
|
s.freeRegs(s.values[a.ID].regs)
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
}
|
2015-12-17 10:01:24 -08:00
|
|
|
|
|
|
|
|
// Third pass - pick registers for phis whose inputs
|
|
|
|
|
// were not in a register.
|
2015-08-11 12:51:33 -07:00
|
|
|
for i, v := range phis {
|
2015-12-17 10:01:24 -08:00
|
|
|
if !s.values[v.ID].needReg {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if phiRegs[i] != noRegister {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
m := s.compatRegs(v.Type) &^ phiUsed &^ s.used
|
|
|
|
|
if m != 0 {
|
|
|
|
|
r := pickReg(m)
|
|
|
|
|
phiRegs[i] = r
|
|
|
|
|
phiUsed |= regMask(1) << r
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Set registers for phis. Add phi spill code.
|
2015-12-17 10:01:24 -08:00
|
|
|
for i, v := range phis {
|
|
|
|
|
if !s.values[v.ID].needReg {
|
2015-08-11 12:51:33 -07:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
r := phiRegs[i]
|
|
|
|
|
if r == noRegister {
|
2015-12-17 10:01:24 -08:00
|
|
|
// stack-based phi
|
|
|
|
|
// Spills will be inserted in all the predecessors below.
|
|
|
|
|
s.values[v.ID].spill = v // v starts life spilled
|
|
|
|
|
s.values[v.ID].spillUsed = true // use is guaranteed
|
|
|
|
|
continue
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
// register-based phi
|
|
|
|
|
s.assignReg(r, v, v)
|
|
|
|
|
// Spill the phi in case we need to restore it later.
|
|
|
|
|
spill := b.NewValue1(v.Line, OpStoreReg, v.Type, v)
|
2015-10-29 13:41:02 -07:00
|
|
|
s.setOrig(spill, v)
|
2015-08-11 12:51:33 -07:00
|
|
|
s.values[v.ID].spill = spill
|
|
|
|
|
s.values[v.ID].spillUsed = false
|
2016-03-21 11:32:04 -04:00
|
|
|
if loop != nil {
|
|
|
|
|
loop.spills = append(loop.spills, v)
|
|
|
|
|
nSpillsInner++
|
|
|
|
|
}
|
|
|
|
|
nSpills++
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
|
2015-12-17 10:01:24 -08:00
|
|
|
// Save the starting state for use by merge edges.
|
|
|
|
|
var regList []startReg
|
2016-03-21 22:57:26 -07:00
|
|
|
for r := register(0); r < s.numRegs; r++ {
|
2015-12-17 10:01:24 -08:00
|
|
|
v := s.regs[r].v
|
|
|
|
|
if v == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if phiUsed>>r&1 != 0 {
|
|
|
|
|
// Skip registers that phis used, we'll handle those
|
|
|
|
|
// specially during merge edge processing.
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
regList = append(regList, startReg{r, v.ID})
|
|
|
|
|
}
|
|
|
|
|
s.startRegs[b.ID] = regList
|
|
|
|
|
|
2016-03-10 17:52:57 -06:00
|
|
|
if s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf("after phis\n")
|
|
|
|
|
for _, x := range s.startRegs[b.ID] {
|
2016-03-21 22:57:26 -07:00
|
|
|
fmt.Printf(" %s: v%d\n", s.registers[x.r].Name(), x.vid)
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-18 20:00:15 -08:00
|
|
|
// Compute preferred registers for each value using a backwards pass.
|
|
|
|
|
// Note that we do this phase after startRegs is set above, so that
|
|
|
|
|
// we get the right behavior for a block which branches to itself.
|
|
|
|
|
for _, succ := range b.Succs {
|
|
|
|
|
// TODO: prioritize likely successor.
|
|
|
|
|
for _, x := range s.startRegs[succ.ID] {
|
|
|
|
|
v := s.orig[x.vid]
|
|
|
|
|
s.values[v.ID].desired = x.r
|
|
|
|
|
}
|
|
|
|
|
// Process phi ops in succ
|
|
|
|
|
i := -1
|
|
|
|
|
for j, p := range succ.Preds {
|
|
|
|
|
if p == b {
|
|
|
|
|
i = j
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if i == -1 {
|
|
|
|
|
s.f.Fatalf("can't find predecssor %s of %s\n", b, succ)
|
|
|
|
|
}
|
|
|
|
|
for _, v := range succ.Values {
|
|
|
|
|
if v.Op != OpPhi {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if !s.values[v.ID].needReg {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
r, ok := s.f.getHome(v.ID).(*Register)
|
|
|
|
|
if !ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
a := s.orig[v.Args[i].ID]
|
|
|
|
|
s.values[a.ID].desired = register(r.Num)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set avoid fields to help desired register availability.
|
|
|
|
|
liveSet.clear()
|
|
|
|
|
for _, e := range s.live[b.ID] {
|
|
|
|
|
liveSet.add(e.ID)
|
|
|
|
|
}
|
|
|
|
|
if v := b.Control; v != nil && s.values[v.ID].needReg {
|
|
|
|
|
liveSet.add(v.ID)
|
|
|
|
|
}
|
|
|
|
|
for i := len(oldSched) - 1; i >= 0; i-- {
|
|
|
|
|
v := oldSched[i]
|
|
|
|
|
liveSet.remove(v.ID)
|
|
|
|
|
|
|
|
|
|
r := s.values[v.ID].desired
|
|
|
|
|
if r != noRegister {
|
|
|
|
|
m := regMask(1) << r
|
|
|
|
|
// All live values should avoid this register so
|
|
|
|
|
// it will be available at this point.
|
|
|
|
|
for _, w := range liveSet.contents() {
|
|
|
|
|
s.values[w].avoid |= m
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, a := range v.Args {
|
|
|
|
|
if !s.values[a.ID].needReg {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
liveSet.add(a.ID)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
// Process all the non-phi values.
|
2015-12-17 10:01:24 -08:00
|
|
|
for _, v := range oldSched {
|
2016-03-10 17:52:57 -06:00
|
|
|
if s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf(" processing %s\n", v.LongString())
|
|
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
if v.Op == OpPhi {
|
|
|
|
|
f.Fatalf("phi %s not at start of block", v)
|
|
|
|
|
}
|
|
|
|
|
if v.Op == OpSP {
|
2016-03-21 22:57:26 -07:00
|
|
|
s.assignReg(s.SPReg, v, v)
|
2015-05-05 16:19:12 -07:00
|
|
|
b.Values = append(b.Values, v)
|
2015-11-05 14:59:47 -08:00
|
|
|
s.advanceUses(v)
|
2015-05-05 16:19:12 -07:00
|
|
|
continue
|
|
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
if v.Op == OpSB {
|
2016-03-21 22:57:26 -07:00
|
|
|
s.assignReg(s.SBReg, v, v)
|
2015-05-27 14:52:22 -07:00
|
|
|
b.Values = append(b.Values, v)
|
2015-11-05 14:59:47 -08:00
|
|
|
s.advanceUses(v)
|
2015-05-27 14:52:22 -07:00
|
|
|
continue
|
|
|
|
|
}
|
2015-11-02 08:10:26 -08:00
|
|
|
if v.Op == OpArg {
|
2016-03-01 23:21:55 +00:00
|
|
|
// Args are "pre-spilled" values. We don't allocate
|
|
|
|
|
// any register here. We just set up the spill pointer to
|
2015-11-02 08:10:26 -08:00
|
|
|
// point at itself and any later user will restore it to use it.
|
|
|
|
|
s.values[v.ID].spill = v
|
|
|
|
|
s.values[v.ID].spillUsed = true // use is guaranteed
|
|
|
|
|
b.Values = append(b.Values, v)
|
2015-11-05 14:59:47 -08:00
|
|
|
s.advanceUses(v)
|
2015-11-02 08:10:26 -08:00
|
|
|
continue
|
|
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
regspec := opcodeTable[v.Op].reg
|
|
|
|
|
if len(regspec.inputs) == 0 && len(regspec.outputs) == 0 {
|
|
|
|
|
// No register allocation required (or none specified yet)
|
|
|
|
|
s.freeRegs(regspec.clobbers)
|
|
|
|
|
b.Values = append(b.Values, v)
|
|
|
|
|
continue
|
2015-05-05 16:19:12 -07:00
|
|
|
}
|
|
|
|
|
|
2015-12-17 10:01:24 -08:00
|
|
|
if s.values[v.ID].rematerializeable {
|
2015-10-19 10:57:03 -07:00
|
|
|
// Value is rematerializeable, don't issue it here.
|
|
|
|
|
// It will get issued just before each use (see
|
|
|
|
|
// allocValueToReg).
|
2016-03-15 20:45:50 -07:00
|
|
|
for _, a := range v.Args {
|
|
|
|
|
a.Uses--
|
|
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
s.advanceUses(v)
|
2015-10-19 10:57:03 -07:00
|
|
|
continue
|
|
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Move arguments to registers. Process in an ordering defined
|
2015-11-05 14:59:47 -08:00
|
|
|
// by the register specification (most constrained first).
|
|
|
|
|
args = append(args[:0], v.Args...)
|
2015-08-11 12:51:33 -07:00
|
|
|
for _, i := range regspec.inputs {
|
2015-12-09 15:58:18 -08:00
|
|
|
if i.regs == flagRegMask {
|
|
|
|
|
// TODO: remove flag input from regspec.inputs.
|
|
|
|
|
continue
|
|
|
|
|
}
|
2016-02-23 21:09:39 -05:00
|
|
|
args[i.idx] = s.allocValToReg(v.Args[i.idx], i.regs, true, v.Line)
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2016-04-10 08:26:43 -07:00
|
|
|
// If the output clobbers the input register, and the input register is
|
|
|
|
|
// live beyond the instruction, make another copy of the input register so
|
|
|
|
|
// we don't have to reload the value from the spill location.
|
|
|
|
|
if opcodeTable[v.Op].resultInArg0 &&
|
|
|
|
|
s.liveAfterCurrentInstruction(v.Args[0]) &&
|
|
|
|
|
countRegs(s.values[v.Args[0].ID].regs) == 1 {
|
|
|
|
|
|
|
|
|
|
if opcodeTable[v.Op].commutative &&
|
|
|
|
|
(!s.liveAfterCurrentInstruction(v.Args[1]) ||
|
|
|
|
|
countRegs(s.values[v.Args[1].ID].regs) > 1) {
|
|
|
|
|
// Input #1 is dead after the instruction, or we have
|
|
|
|
|
// more than one copy of it in a register. Either way,
|
|
|
|
|
// use that input as the one that is clobbered.
|
|
|
|
|
args[0], args[1] = args[1], args[0]
|
|
|
|
|
} else {
|
|
|
|
|
m := s.compatRegs(v.Args[0].Type)
|
|
|
|
|
m &^= s.values[v.Args[0].ID].regs // a register not already holding v.Args[0]
|
|
|
|
|
s.allocValToReg(v.Args[0], m, true, v.Line)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
// Now that all args are in regs, we're ready to issue the value itself.
|
2015-11-05 14:59:47 -08:00
|
|
|
// Before we pick a register for the output value, allow input registers
|
2015-08-11 12:51:33 -07:00
|
|
|
// to be deallocated. We do this here so that the output can use the
|
|
|
|
|
// same register as a dying input.
|
|
|
|
|
s.nospill = 0
|
2015-11-05 14:59:47 -08:00
|
|
|
s.advanceUses(v) // frees any registers holding args that are no longer live
|
2015-08-11 12:51:33 -07:00
|
|
|
|
|
|
|
|
// Dump any registers which will be clobbered
|
|
|
|
|
s.freeRegs(regspec.clobbers)
|
|
|
|
|
|
|
|
|
|
// Pick register for output.
|
2015-12-17 10:01:24 -08:00
|
|
|
if s.values[v.ID].needReg {
|
2016-03-10 13:05:56 -08:00
|
|
|
mask := regspec.outputs[0] &^ s.reserved()
|
2015-12-09 15:58:18 -08:00
|
|
|
if mask>>33&1 != 0 {
|
|
|
|
|
s.f.Fatalf("bad mask %s\n", v.LongString())
|
|
|
|
|
}
|
2016-03-10 13:05:56 -08:00
|
|
|
if opcodeTable[v.Op].resultInArg0 {
|
2016-04-10 08:26:43 -07:00
|
|
|
// Output must use the same register as input 0.
|
2016-03-10 13:05:56 -08:00
|
|
|
r := register(s.f.getHome(args[0].ID).(*Register).Num)
|
2016-04-10 08:26:43 -07:00
|
|
|
mask = regMask(1) << r
|
2016-03-10 13:05:56 -08:00
|
|
|
}
|
2016-01-18 20:00:15 -08:00
|
|
|
r := s.allocReg(v, mask)
|
2015-08-11 12:51:33 -07:00
|
|
|
s.assignReg(r, v, v)
|
|
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2015-08-11 12:51:33 -07:00
|
|
|
// Issue the Value itself.
|
2015-11-05 14:59:47 -08:00
|
|
|
for i, a := range args {
|
2016-03-15 20:45:50 -07:00
|
|
|
v.SetArg(i, a) // use register version of arguments
|
2015-11-05 14:59:47 -08:00
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
b.Values = append(b.Values, v)
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Issue a spill for this value. We issue spills unconditionally,
|
2015-08-11 12:51:33 -07:00
|
|
|
// then at the end of regalloc delete the ones we never use.
|
2015-11-05 14:59:47 -08:00
|
|
|
// TODO: schedule the spill at a point that dominates all restores.
|
|
|
|
|
// The restore may be off in an unlikely branch somewhere and it
|
|
|
|
|
// would be better to have the spill in that unlikely branch as well.
|
|
|
|
|
// v := ...
|
|
|
|
|
// if unlikely {
|
|
|
|
|
// f()
|
|
|
|
|
// }
|
|
|
|
|
// It would be good to have both spill and restore inside the IF.
|
2015-12-17 10:01:24 -08:00
|
|
|
if s.values[v.ID].needReg {
|
[dev.ssa] cmd/compile: support spilling and loading flags
This CL takes a simple approach to spilling and loading flags.
We never spill. When a load is needed, we recalculate,
loading the arguments as needed.
This is simple and architecture-independent.
It is not very efficient, but as of this CL,
there are fewer than 200 flag spills during make.bash.
This was tested by manually reverting CLs 13813 and 13843,
causing SETcc, MOV, and LEA instructions to clobber flags,
which dramatically increases the number of flags spills.
With that done, all stdlib tests that used to pass
still pass.
For future reference, here are some other, more efficient
amd64-only schemes that we could adapt in the future if needed.
(1) Spill exactly the flags needed.
For example, if we know that the flags will be needed
by a SETcc or Jcc op later, we could use SETcc to
extract just the relevant flag. When needed,
we could use TESTB and change the op to JNE/SETNE.
(Alternatively, we could leave the op unaltered
and prepare an appropriate CMPB instruction
to produce the desired flag.)
However, this requires separate handling for every
instruction that uses the flags register,
including (say) SBBQcarrymask.
We could enable this on an ad hoc basis for common cases
and fall back to recalculation for other cases.
(2) Spill all flags with PUSHF and POPF
This modifies SP, which the runtime won't like.
It also requires coordination with stackalloc to
make sure that we have a stack slot ready for use.
(3) Spill almost all flags with LAHF, SETO, and SAHF
See http://blog.freearrow.com/archives/396
for details. This would handle all the flags we currently
use. However, LAHF and SAHF are not universally available
and it requires arranging for AX to be free.
Change-Id: Ie36600fd8e807ef2bee83e2e2ae3685112a7f276
Reviewed-on: https://go-review.googlesource.com/13844
Reviewed-by: Keith Randall <khr@golang.org>
2015-08-22 19:38:12 -07:00
|
|
|
spill := b.NewValue1(v.Line, OpStoreReg, v.Type, v)
|
2015-10-29 13:41:02 -07:00
|
|
|
s.setOrig(spill, v)
|
[dev.ssa] cmd/compile: support spilling and loading flags
This CL takes a simple approach to spilling and loading flags.
We never spill. When a load is needed, we recalculate,
loading the arguments as needed.
This is simple and architecture-independent.
It is not very efficient, but as of this CL,
there are fewer than 200 flag spills during make.bash.
This was tested by manually reverting CLs 13813 and 13843,
causing SETcc, MOV, and LEA instructions to clobber flags,
which dramatically increases the number of flags spills.
With that done, all stdlib tests that used to pass
still pass.
For future reference, here are some other, more efficient
amd64-only schemes that we could adapt in the future if needed.
(1) Spill exactly the flags needed.
For example, if we know that the flags will be needed
by a SETcc or Jcc op later, we could use SETcc to
extract just the relevant flag. When needed,
we could use TESTB and change the op to JNE/SETNE.
(Alternatively, we could leave the op unaltered
and prepare an appropriate CMPB instruction
to produce the desired flag.)
However, this requires separate handling for every
instruction that uses the flags register,
including (say) SBBQcarrymask.
We could enable this on an ad hoc basis for common cases
and fall back to recalculation for other cases.
(2) Spill all flags with PUSHF and POPF
This modifies SP, which the runtime won't like.
It also requires coordination with stackalloc to
make sure that we have a stack slot ready for use.
(3) Spill almost all flags with LAHF, SETO, and SAHF
See http://blog.freearrow.com/archives/396
for details. This would handle all the flags we currently
use. However, LAHF and SAHF are not universally available
and it requires arranging for AX to be free.
Change-Id: Ie36600fd8e807ef2bee83e2e2ae3685112a7f276
Reviewed-on: https://go-review.googlesource.com/13844
Reviewed-by: Keith Randall <khr@golang.org>
2015-08-22 19:38:12 -07:00
|
|
|
s.values[v.ID].spill = spill
|
|
|
|
|
s.values[v.ID].spillUsed = false
|
2016-03-21 11:32:04 -04:00
|
|
|
if loop != nil {
|
|
|
|
|
loop.spills = append(loop.spills, v)
|
|
|
|
|
nSpillsInner++
|
|
|
|
|
}
|
|
|
|
|
nSpills++
|
[dev.ssa] cmd/compile: support spilling and loading flags
This CL takes a simple approach to spilling and loading flags.
We never spill. When a load is needed, we recalculate,
loading the arguments as needed.
This is simple and architecture-independent.
It is not very efficient, but as of this CL,
there are fewer than 200 flag spills during make.bash.
This was tested by manually reverting CLs 13813 and 13843,
causing SETcc, MOV, and LEA instructions to clobber flags,
which dramatically increases the number of flags spills.
With that done, all stdlib tests that used to pass
still pass.
For future reference, here are some other, more efficient
amd64-only schemes that we could adapt in the future if needed.
(1) Spill exactly the flags needed.
For example, if we know that the flags will be needed
by a SETcc or Jcc op later, we could use SETcc to
extract just the relevant flag. When needed,
we could use TESTB and change the op to JNE/SETNE.
(Alternatively, we could leave the op unaltered
and prepare an appropriate CMPB instruction
to produce the desired flag.)
However, this requires separate handling for every
instruction that uses the flags register,
including (say) SBBQcarrymask.
We could enable this on an ad hoc basis for common cases
and fall back to recalculation for other cases.
(2) Spill all flags with PUSHF and POPF
This modifies SP, which the runtime won't like.
It also requires coordination with stackalloc to
make sure that we have a stack slot ready for use.
(3) Spill almost all flags with LAHF, SETO, and SAHF
See http://blog.freearrow.com/archives/396
for details. This would handle all the flags we currently
use. However, LAHF and SAHF are not universally available
and it requires arranging for AX to be free.
Change-Id: Ie36600fd8e807ef2bee83e2e2ae3685112a7f276
Reviewed-on: https://go-review.googlesource.com/13844
Reviewed-by: Keith Randall <khr@golang.org>
2015-08-22 19:38:12 -07:00
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2016-03-02 15:18:40 -08:00
|
|
|
// Load control value into reg.
|
2015-12-17 10:01:24 -08:00
|
|
|
if v := b.Control; v != nil && s.values[v.ID].needReg {
|
2016-03-10 17:52:57 -06:00
|
|
|
if s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf(" processing control %s\n", v.LongString())
|
|
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
// TODO: regspec for block control values, instead of using
|
|
|
|
|
// register set from the control op's output.
|
2016-02-23 21:09:39 -05:00
|
|
|
s.allocValToReg(v, opcodeTable[v.Op].reg.outputs[0], false, b.Line)
|
2015-11-05 14:59:47 -08:00
|
|
|
// Remove this use from the uses list.
|
2015-12-17 10:01:24 -08:00
|
|
|
vi := &s.values[v.ID]
|
|
|
|
|
u := vi.uses
|
|
|
|
|
vi.uses = u.next
|
|
|
|
|
if u.next == nil {
|
|
|
|
|
s.freeRegs(vi.regs) // value is dead
|
|
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
u.next = s.freeUseRecords
|
|
|
|
|
s.freeUseRecords = u
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
|
2016-03-02 15:18:40 -08:00
|
|
|
// If we are approaching a merge point and we are the primary
|
|
|
|
|
// predecessor of it, find live values that we use soon after
|
|
|
|
|
// the merge point and promote them to registers now.
|
2016-03-10 14:42:52 -05:00
|
|
|
if len(b.Succs) == 1 {
|
2016-03-02 15:18:40 -08:00
|
|
|
// For this to be worthwhile, the loop must have no calls in it.
|
|
|
|
|
top := b.Succs[0]
|
2016-03-10 14:42:52 -05:00
|
|
|
loop := s.loopnest.b2l[top.ID]
|
|
|
|
|
if loop == nil || loop.header != top || loop.containsCall {
|
|
|
|
|
goto badloop
|
2016-03-02 15:18:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: sort by distance, pick the closest ones?
|
|
|
|
|
for _, live := range s.live[b.ID] {
|
|
|
|
|
if live.dist >= unlikelyDistance {
|
|
|
|
|
// Don't preload anything live after the loop.
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
vid := live.ID
|
|
|
|
|
vi := &s.values[vid]
|
|
|
|
|
if vi.regs != 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
v := s.orig[vid]
|
|
|
|
|
m := s.compatRegs(v.Type) &^ s.used
|
|
|
|
|
if m != 0 {
|
|
|
|
|
s.allocValToReg(v, m, false, b.Line)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
badloop:
|
|
|
|
|
;
|
|
|
|
|
|
2015-12-17 10:01:24 -08:00
|
|
|
// Save end-of-block register state.
|
2016-02-10 17:43:31 -05:00
|
|
|
// First count how many, this cuts allocations in half.
|
|
|
|
|
k := 0
|
2016-03-21 22:57:26 -07:00
|
|
|
for r := register(0); r < s.numRegs; r++ {
|
2016-02-10 17:43:31 -05:00
|
|
|
v := s.regs[r].v
|
|
|
|
|
if v == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
k++
|
|
|
|
|
}
|
|
|
|
|
regList := make([]endReg, 0, k)
|
2016-03-21 22:57:26 -07:00
|
|
|
for r := register(0); r < s.numRegs; r++ {
|
2015-12-17 10:01:24 -08:00
|
|
|
v := s.regs[r].v
|
|
|
|
|
if v == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
regList = append(regList, endReg{r, v, s.regs[r].c})
|
|
|
|
|
}
|
|
|
|
|
s.endRegs[b.ID] = regList
|
|
|
|
|
|
|
|
|
|
// Check. TODO: remove
|
|
|
|
|
{
|
|
|
|
|
liveSet.clear()
|
|
|
|
|
for _, x := range s.live[b.ID] {
|
|
|
|
|
liveSet.add(x.ID)
|
|
|
|
|
}
|
2016-03-21 22:57:26 -07:00
|
|
|
for r := register(0); r < s.numRegs; r++ {
|
2015-12-17 10:01:24 -08:00
|
|
|
v := s.regs[r].v
|
|
|
|
|
if v == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if !liveSet.contains(v.ID) {
|
|
|
|
|
s.f.Fatalf("val %s is in reg but not live at end of %s", v, b)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If a value is live at the end of the block and
|
|
|
|
|
// isn't in a register, remember that its spill location
|
2016-03-01 23:21:55 +00:00
|
|
|
// is live. We need to remember this information so that
|
2016-01-18 20:00:15 -08:00
|
|
|
// the liveness analysis in stackalloc is correct.
|
2015-12-17 10:01:24 -08:00
|
|
|
for _, e := range s.live[b.ID] {
|
|
|
|
|
if s.values[e.ID].regs != 0 {
|
|
|
|
|
// in a register, we'll use that source for the merge.
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
spill := s.values[e.ID].spill
|
|
|
|
|
if spill == nil {
|
|
|
|
|
// rematerializeable values will have spill==nil.
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
s.spillLive[b.ID] = append(s.spillLive[b.ID], spill.ID)
|
|
|
|
|
s.values[e.ID].spillUsed = true
|
|
|
|
|
}
|
2015-06-06 16:03:33 -07:00
|
|
|
|
2016-03-21 11:32:04 -04:00
|
|
|
// Keep track of values that are spilled in the loop, but whose spill
|
|
|
|
|
// is not used in the loop. It may be possible to move ("sink") the
|
|
|
|
|
// spill out of the loop into one or more exit blocks.
|
|
|
|
|
if loop != nil {
|
|
|
|
|
loop.scratch++ // increment count of blocks in this loop that have been processed
|
|
|
|
|
if loop.scratch == loop.nBlocks { // just processed last block of loop, if it is an inner loop.
|
|
|
|
|
// This check is redundant with code at the top of the loop.
|
|
|
|
|
// This is definitive; the one at the top of the loop is an optimization.
|
|
|
|
|
if loop.isInner && // Common case, easier, most likely to be profitable
|
|
|
|
|
!loop.containsCall && // Calls force spills, also lead to puzzling spill info.
|
|
|
|
|
len(loop.exits) <= 32 { // Almost no inner loops have more than 32 exits,
|
|
|
|
|
// and this allows use of a bitvector and a sparseMap.
|
|
|
|
|
|
|
|
|
|
// TODO: exit calculation is messed up for non-inner loops
|
|
|
|
|
// because of multilevel exits that are not part of the "exit"
|
|
|
|
|
// count.
|
|
|
|
|
|
|
|
|
|
// Compute the set of spill-movement candidates live at entry to exit blocks.
|
|
|
|
|
// isLoopSpillCandidate filters for
|
|
|
|
|
// (1) defined in appropriate loop
|
|
|
|
|
// (2) needs a register
|
|
|
|
|
// (3) spill not already used (in the loop)
|
|
|
|
|
// Condition (3) === "in a register at all loop exits"
|
|
|
|
|
|
|
|
|
|
entryCandidates.clear()
|
|
|
|
|
|
|
|
|
|
for whichExit, ss := range loop.exits {
|
|
|
|
|
// Start with live at end.
|
|
|
|
|
for _, li := range s.live[ss.ID] {
|
|
|
|
|
if s.isLoopSpillCandidate(loop, s.orig[li.ID]) {
|
|
|
|
|
entryCandidates.setBit(li.ID, uint(whichExit))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Control can also be live.
|
|
|
|
|
if ss.Control != nil && s.isLoopSpillCandidate(loop, ss.Control) {
|
|
|
|
|
entryCandidates.setBit(ss.Control.ID, uint(whichExit))
|
|
|
|
|
}
|
|
|
|
|
// Walk backwards, filling in locally live values, removing those defined.
|
|
|
|
|
for i := len(ss.Values) - 1; i >= 0; i-- {
|
|
|
|
|
v := ss.Values[i]
|
|
|
|
|
entryCandidates.remove(v.ID) // Cannot be an issue, only keeps the sets smaller.
|
|
|
|
|
for _, a := range v.Args {
|
|
|
|
|
if s.isLoopSpillCandidate(loop, a) {
|
|
|
|
|
entryCandidates.setBit(a.ID, uint(whichExit))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, e := range loop.spills {
|
|
|
|
|
whichblocks := entryCandidates.get(e.ID)
|
|
|
|
|
oldSpill := s.values[e.ID].spill
|
|
|
|
|
if whichblocks != 0 && whichblocks != -1 { // -1 = not in map.
|
|
|
|
|
toSink = append(toSink, spillToSink{spill: oldSpill, dests: whichblocks})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // loop is inner etc
|
|
|
|
|
loop.scratch = 0 // Don't leave a mess, just in case.
|
|
|
|
|
loop.spills = nil
|
|
|
|
|
} // if scratch == nBlocks
|
|
|
|
|
} // if loop is not nil
|
|
|
|
|
|
2015-11-05 14:59:47 -08:00
|
|
|
// Clear any final uses.
|
|
|
|
|
// All that is left should be the pseudo-uses added for values which
|
|
|
|
|
// are live at the end of b.
|
|
|
|
|
for _, e := range s.live[b.ID] {
|
|
|
|
|
u := s.values[e.ID].uses
|
|
|
|
|
if u == nil {
|
|
|
|
|
f.Fatalf("live at end, no uses v%d", e.ID)
|
|
|
|
|
}
|
|
|
|
|
if u.next != nil {
|
|
|
|
|
f.Fatalf("live at end, too many uses v%d", e.ID)
|
|
|
|
|
}
|
|
|
|
|
s.values[e.ID].uses = nil
|
|
|
|
|
u.next = s.freeUseRecords
|
|
|
|
|
s.freeUseRecords = u
|
|
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Erase any spills we never used
|
|
|
|
|
for i := range s.values {
|
|
|
|
|
vi := s.values[i]
|
|
|
|
|
if vi.spillUsed {
|
2016-03-10 17:52:57 -06:00
|
|
|
if s.f.pass.debug > logSpills {
|
2016-04-08 13:33:43 -04:00
|
|
|
s.f.Config.Warnl(vi.spill.Line, "spilled value at %v remains", vi.spill)
|
2015-08-31 20:42:04 -05:00
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
spill := vi.spill
|
|
|
|
|
if spill == nil {
|
|
|
|
|
// Constants, SP, SB, ...
|
|
|
|
|
continue
|
2015-05-05 16:19:12 -07:00
|
|
|
}
|
2016-03-21 11:32:04 -04:00
|
|
|
loop := s.loopForBlock(spill.Block)
|
|
|
|
|
if loop != nil {
|
|
|
|
|
nSpillsInner--
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-15 20:45:50 -07:00
|
|
|
spill.Args[0].Uses--
|
2016-01-28 13:46:30 -08:00
|
|
|
f.freeValue(spill)
|
2016-03-21 11:32:04 -04:00
|
|
|
nSpills--
|
2015-05-05 16:19:12 -07:00
|
|
|
}
|
2016-03-21 11:32:04 -04:00
|
|
|
|
2015-05-05 16:19:12 -07:00
|
|
|
for _, b := range f.Blocks {
|
2015-08-11 12:51:33 -07:00
|
|
|
i := 0
|
2015-05-05 16:19:12 -07:00
|
|
|
for _, v := range b.Values {
|
2015-08-11 12:51:33 -07:00
|
|
|
if v.Op == OpInvalid {
|
2015-08-05 11:01:59 -07:00
|
|
|
continue
|
2015-05-05 16:19:12 -07:00
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
b.Values[i] = v
|
|
|
|
|
i++
|
2015-05-05 16:19:12 -07:00
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
b.Values = b.Values[:i]
|
|
|
|
|
// TODO: zero b.Values[i:], recycle Values
|
|
|
|
|
// Not important now because this is the last phase that manipulates Values
|
2015-05-05 16:19:12 -07:00
|
|
|
}
|
2015-08-11 12:51:33 -07:00
|
|
|
|
2016-03-21 11:32:04 -04:00
|
|
|
// Must clear these out before any potential recycling, though that's
|
|
|
|
|
// not currently implemented.
|
|
|
|
|
for i, ts := range toSink {
|
|
|
|
|
vsp := ts.spill
|
|
|
|
|
if vsp.Op == OpInvalid { // This spill was completely eliminated
|
|
|
|
|
toSink[i].spill = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-17 10:01:24 -08:00
|
|
|
// Anything that didn't get a register gets a stack location here.
|
|
|
|
|
// (StoreReg, stack-based phis, inputs, ...)
|
|
|
|
|
stacklive := stackalloc(s.f, s.spillLive)
|
|
|
|
|
|
|
|
|
|
// Fix up all merge edges.
|
|
|
|
|
s.shuffle(stacklive)
|
2016-03-21 11:32:04 -04:00
|
|
|
|
|
|
|
|
// Insert moved spills (that have not been marked invalid above)
|
|
|
|
|
// at start of appropriate block and remove the originals from their
|
|
|
|
|
// location within loops. Notice that this can break SSA form;
|
|
|
|
|
// if a spill is sunk to multiple exits, there will be no phi for that
|
|
|
|
|
// spill at a join point downstream of those two exits, though the
|
|
|
|
|
// two spills will target the same stack slot. Notice also that this
|
|
|
|
|
// takes place after stack allocation, so the stack allocator does
|
|
|
|
|
// not need to process these malformed flow graphs.
|
|
|
|
|
sinking:
|
|
|
|
|
for _, ts := range toSink {
|
|
|
|
|
vsp := ts.spill
|
|
|
|
|
if vsp == nil { // This spill was completely eliminated
|
|
|
|
|
nSpillsSunkUnused++
|
|
|
|
|
continue sinking
|
|
|
|
|
}
|
|
|
|
|
e := ts.spilledValue()
|
|
|
|
|
if s.values[e.ID].spillUsedShuffle {
|
|
|
|
|
nSpillsNotSunkLateUse++
|
|
|
|
|
continue sinking
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// move spills to a better (outside of loop) block.
|
|
|
|
|
// This would be costly if it occurred very often, but it doesn't.
|
|
|
|
|
b := vsp.Block
|
|
|
|
|
loop := s.loopnest.b2l[b.ID]
|
|
|
|
|
dests := ts.dests
|
|
|
|
|
|
|
|
|
|
// Pre-check to be sure that spilled value is still in expected register on all exits where live.
|
|
|
|
|
check_val_still_in_reg:
|
|
|
|
|
for i := uint(0); i < 32 && dests != 0; i++ {
|
|
|
|
|
|
|
|
|
|
if dests&(1<<i) == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
dests ^= 1 << i
|
|
|
|
|
d := loop.exits[i]
|
|
|
|
|
if len(d.Preds) > 1 {
|
|
|
|
|
panic("Should be impossible given critical edges removed")
|
|
|
|
|
}
|
|
|
|
|
p := d.Preds[0] // block in loop exiting to d.
|
|
|
|
|
|
|
|
|
|
endregs := s.endRegs[p.ID]
|
|
|
|
|
for _, regrec := range endregs {
|
|
|
|
|
if regrec.v == e && regrec.r != noRegister && regrec.c == e { // TODO: regrec.c != e implies different spill possible.
|
|
|
|
|
continue check_val_still_in_reg
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// If here, the register assignment was lost down at least one exit and it can't be sunk
|
|
|
|
|
if s.f.pass.debug > moveSpills {
|
|
|
|
|
s.f.Config.Warnl(e.Line, "lost register assignment for spill %v in %v at exit %v to %v",
|
|
|
|
|
vsp, b, p, d)
|
|
|
|
|
}
|
|
|
|
|
nSpillsChanged++
|
|
|
|
|
continue sinking
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nSpillsSunk++
|
|
|
|
|
nSpillsInner--
|
|
|
|
|
// don't update nSpills, since spill is only moved, and if it is duplicated, the spills-on-a-path is not increased.
|
|
|
|
|
|
|
|
|
|
dests = ts.dests
|
|
|
|
|
|
|
|
|
|
// remove vsp from b.Values
|
|
|
|
|
i := 0
|
|
|
|
|
for _, w := range b.Values {
|
|
|
|
|
if vsp == w {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
b.Values[i] = w
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
b.Values = b.Values[:i]
|
|
|
|
|
|
|
|
|
|
for i := uint(0); i < 32 && dests != 0; i++ {
|
|
|
|
|
|
|
|
|
|
if dests&(1<<i) == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dests ^= 1 << i
|
|
|
|
|
|
|
|
|
|
d := loop.exits[i]
|
|
|
|
|
vspnew := d.NewValue1(e.Line, OpStoreReg, e.Type, e)
|
|
|
|
|
|
|
|
|
|
if s.f.pass.debug > moveSpills {
|
|
|
|
|
s.f.Config.Warnl(e.Line, "moved spill %v in %v for %v to %v in %v",
|
|
|
|
|
vsp, b, e, vspnew, d)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f.setHome(vspnew, f.getHome(vsp.ID)) // copy stack home
|
|
|
|
|
|
|
|
|
|
// shuffle vspnew to the beginning of its block
|
|
|
|
|
copy(d.Values[1:], d.Values[0:len(d.Values)-1])
|
|
|
|
|
d.Values[0] = vspnew
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if f.pass.stats > 0 {
|
|
|
|
|
f.logStat("spills_info",
|
|
|
|
|
nSpills, "spills", nSpillsInner, "inner_spills_remaining", nSpillsSunk, "inner_spills_sunk", nSpillsSunkUnused, "inner_spills_unused", nSpillsNotSunkLateUse, "inner_spills_shuffled", nSpillsChanged, "inner_spills_changed")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// isLoopSpillCandidate indicates whether the spill for v satisfies preliminary
|
|
|
|
|
// spill-sinking conditions just after the last block of loop has been processed.
|
|
|
|
|
// In particular:
|
|
|
|
|
// v needs a register.
|
|
|
|
|
// v's spill is not (YET) used.
|
|
|
|
|
// v's definition is within loop.
|
|
|
|
|
// The spill may be used in the future, either by an outright use
|
|
|
|
|
// in the code, or by shuffling code inserted after stack allocation.
|
|
|
|
|
// Outright uses cause sinking; shuffling (within the loop) inhibits it.
|
|
|
|
|
func (s *regAllocState) isLoopSpillCandidate(loop *loop, v *Value) bool {
|
|
|
|
|
return s.values[v.ID].needReg && !s.values[v.ID].spillUsed && s.loopnest.b2l[v.Block.ID] == loop
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// lateSpillUse notes a late (after stack allocation) use of spill c
|
|
|
|
|
// This will inhibit spill sinking.
|
|
|
|
|
func (s *regAllocState) lateSpillUse(c *Value) {
|
|
|
|
|
// TODO investigate why this is necessary.
|
|
|
|
|
// It appears that an outside-the-loop use of
|
|
|
|
|
// an otherwise sinkable spill makes the spill
|
|
|
|
|
// a candidate for shuffling, when it would not
|
|
|
|
|
// otherwise have been the case (spillUsed was not
|
|
|
|
|
// true when isLoopSpillCandidate was called, yet
|
|
|
|
|
// it was shuffled). Such shuffling cuts the amount
|
|
|
|
|
// of spill sinking by more than half (in make.bash)
|
|
|
|
|
v := s.orig[c.ID]
|
|
|
|
|
if v != nil {
|
|
|
|
|
s.values[v.ID].spillUsedShuffle = true
|
|
|
|
|
}
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// shuffle fixes up all the merge edges (those going into blocks of indegree > 1).
|
|
|
|
|
func (s *regAllocState) shuffle(stacklive [][]ID) {
|
|
|
|
|
var e edgeState
|
|
|
|
|
e.s = s
|
|
|
|
|
e.cache = map[ID][]*Value{}
|
|
|
|
|
e.contents = map[Location]contentRecord{}
|
2016-03-10 17:52:57 -06:00
|
|
|
if s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf("shuffle %s\n", s.f.Name)
|
|
|
|
|
fmt.Println(s.f.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, b := range s.f.Blocks {
|
|
|
|
|
if len(b.Preds) <= 1 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
e.b = b
|
|
|
|
|
for i, p := range b.Preds {
|
|
|
|
|
e.p = p
|
|
|
|
|
e.setup(i, s.endRegs[p.ID], s.startRegs[b.ID], stacklive[p.ID])
|
|
|
|
|
e.process()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type edgeState struct {
|
|
|
|
|
s *regAllocState
|
|
|
|
|
p, b *Block // edge goes from p->b.
|
|
|
|
|
|
|
|
|
|
// for each pre-regalloc value, a list of equivalent cached values
|
2016-03-03 09:53:03 -08:00
|
|
|
cache map[ID][]*Value
|
|
|
|
|
cachedVals []ID // (superset of) keys of the above map, for deterministic iteration
|
2015-12-17 10:01:24 -08:00
|
|
|
|
|
|
|
|
// map from location to the value it contains
|
|
|
|
|
contents map[Location]contentRecord
|
|
|
|
|
|
|
|
|
|
// desired destination locations
|
|
|
|
|
destinations []dstRecord
|
|
|
|
|
extra []dstRecord
|
|
|
|
|
|
|
|
|
|
usedRegs regMask // registers currently holding something
|
|
|
|
|
uniqueRegs regMask // registers holding the only copy of a value
|
|
|
|
|
finalRegs regMask // registers holding final target
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type contentRecord struct {
|
|
|
|
|
vid ID // pre-regalloc value
|
|
|
|
|
c *Value // cached value
|
|
|
|
|
final bool // this is a satisfied destination
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type dstRecord struct {
|
|
|
|
|
loc Location // register or stack slot
|
|
|
|
|
vid ID // pre-regalloc value it should contain
|
|
|
|
|
splice **Value // place to store reference to the generating instruction
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// setup initializes the edge state for shuffling.
|
|
|
|
|
func (e *edgeState) setup(idx int, srcReg []endReg, dstReg []startReg, stacklive []ID) {
|
2016-03-10 17:52:57 -06:00
|
|
|
if e.s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf("edge %s->%s\n", e.p, e.b)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear state.
|
2016-03-03 09:53:03 -08:00
|
|
|
for _, vid := range e.cachedVals {
|
|
|
|
|
delete(e.cache, vid)
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
2016-03-03 09:53:03 -08:00
|
|
|
e.cachedVals = e.cachedVals[:0]
|
2015-12-17 10:01:24 -08:00
|
|
|
for k := range e.contents {
|
|
|
|
|
delete(e.contents, k)
|
|
|
|
|
}
|
2016-01-04 13:34:54 -08:00
|
|
|
e.usedRegs = 0
|
|
|
|
|
e.uniqueRegs = 0
|
|
|
|
|
e.finalRegs = 0
|
2015-12-17 10:01:24 -08:00
|
|
|
|
|
|
|
|
// Live registers can be sources.
|
|
|
|
|
for _, x := range srcReg {
|
2016-03-21 22:57:26 -07:00
|
|
|
e.set(&e.s.registers[x.r], x.v.ID, x.c, false)
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
|
|
|
|
// So can all of the spill locations.
|
|
|
|
|
for _, spillID := range stacklive {
|
|
|
|
|
v := e.s.orig[spillID]
|
|
|
|
|
spill := e.s.values[v.ID].spill
|
|
|
|
|
e.set(e.s.f.getHome(spillID), v.ID, spill, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Figure out all the destinations we need.
|
|
|
|
|
dsts := e.destinations[:0]
|
|
|
|
|
for _, x := range dstReg {
|
2016-03-21 22:57:26 -07:00
|
|
|
dsts = append(dsts, dstRecord{&e.s.registers[x.r], x.vid, nil})
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
|
|
|
|
// Phis need their args to end up in a specific location.
|
|
|
|
|
for _, v := range e.b.Values {
|
|
|
|
|
if v.Op != OpPhi {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
loc := e.s.f.getHome(v.ID)
|
|
|
|
|
if loc == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
dsts = append(dsts, dstRecord{loc, v.Args[idx].ID, &v.Args[idx]})
|
|
|
|
|
}
|
|
|
|
|
e.destinations = dsts
|
|
|
|
|
|
2016-03-10 17:52:57 -06:00
|
|
|
if e.s.f.pass.debug > regDebug {
|
2016-03-03 09:53:03 -08:00
|
|
|
for _, vid := range e.cachedVals {
|
|
|
|
|
a := e.cache[vid]
|
2015-12-17 10:01:24 -08:00
|
|
|
for _, c := range a {
|
|
|
|
|
fmt.Printf("src %s: v%d cache=%s\n", e.s.f.getHome(c.ID).Name(), vid, c)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, d := range e.destinations {
|
|
|
|
|
fmt.Printf("dst %s: v%d\n", d.loc.Name(), d.vid)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// process generates code to move all the values to the right destination locations.
|
|
|
|
|
func (e *edgeState) process() {
|
|
|
|
|
dsts := e.destinations
|
|
|
|
|
|
|
|
|
|
// Process the destinations until they are all satisfied.
|
|
|
|
|
for len(dsts) > 0 {
|
|
|
|
|
i := 0
|
|
|
|
|
for _, d := range dsts {
|
|
|
|
|
if !e.processDest(d.loc, d.vid, d.splice) {
|
|
|
|
|
// Failed - save for next iteration.
|
|
|
|
|
dsts[i] = d
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if i < len(dsts) {
|
2016-03-01 23:21:55 +00:00
|
|
|
// Made some progress. Go around again.
|
2015-12-17 10:01:24 -08:00
|
|
|
dsts = dsts[:i]
|
|
|
|
|
|
|
|
|
|
// Append any extras destinations we generated.
|
|
|
|
|
dsts = append(dsts, e.extra...)
|
|
|
|
|
e.extra = e.extra[:0]
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// We made no progress. That means that any
|
2015-12-17 10:01:24 -08:00
|
|
|
// remaining unsatisfied moves are in simple cycles.
|
|
|
|
|
// For example, A -> B -> C -> D -> A.
|
|
|
|
|
// A ----> B
|
|
|
|
|
// ^ |
|
|
|
|
|
// | |
|
|
|
|
|
// | v
|
|
|
|
|
// D <---- C
|
|
|
|
|
|
|
|
|
|
// To break the cycle, we pick an unused register, say R,
|
|
|
|
|
// and put a copy of B there.
|
|
|
|
|
// A ----> B
|
|
|
|
|
// ^ |
|
|
|
|
|
// | |
|
|
|
|
|
// | v
|
|
|
|
|
// D <---- C <---- R=copyofB
|
|
|
|
|
// When we resume the outer loop, the A->B move can now proceed,
|
|
|
|
|
// and eventually the whole cycle completes.
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Copy any cycle location to a temp register. This duplicates
|
2015-12-17 10:01:24 -08:00
|
|
|
// one of the cycle entries, allowing the just duplicated value
|
|
|
|
|
// to be overwritten and the cycle to proceed.
|
|
|
|
|
loc := dsts[0].loc
|
|
|
|
|
vid := e.contents[loc].vid
|
|
|
|
|
c := e.contents[loc].c
|
|
|
|
|
r := e.findRegFor(c.Type)
|
2016-03-10 17:52:57 -06:00
|
|
|
if e.s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf("breaking cycle with v%d in %s:%s\n", vid, loc.Name(), c)
|
|
|
|
|
}
|
|
|
|
|
if _, isReg := loc.(*Register); isReg {
|
|
|
|
|
c = e.p.NewValue1(c.Line, OpCopy, c.Type, c)
|
|
|
|
|
} else {
|
2016-03-21 11:32:04 -04:00
|
|
|
e.s.lateSpillUse(c)
|
2015-12-17 10:01:24 -08:00
|
|
|
c = e.p.NewValue1(c.Line, OpLoadReg, c.Type, c)
|
|
|
|
|
}
|
|
|
|
|
e.set(r, vid, c, false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// processDest generates code to put value vid into location loc. Returns true
|
2015-12-17 10:01:24 -08:00
|
|
|
// if progress was made.
|
|
|
|
|
func (e *edgeState) processDest(loc Location, vid ID, splice **Value) bool {
|
|
|
|
|
occupant := e.contents[loc]
|
|
|
|
|
if occupant.vid == vid {
|
|
|
|
|
// Value is already in the correct place.
|
|
|
|
|
e.contents[loc] = contentRecord{vid, occupant.c, true}
|
|
|
|
|
if splice != nil {
|
2016-03-15 20:45:50 -07:00
|
|
|
(*splice).Uses--
|
2015-12-17 10:01:24 -08:00
|
|
|
*splice = occupant.c
|
2016-03-15 20:45:50 -07:00
|
|
|
occupant.c.Uses++
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
2016-03-01 23:21:55 +00:00
|
|
|
// Note: if splice==nil then c will appear dead. This is
|
2015-12-17 10:01:24 -08:00
|
|
|
// non-SSA formed code, so be careful after this pass not to run
|
|
|
|
|
// deadcode elimination.
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if we're allowed to clobber the destination location.
|
|
|
|
|
if len(e.cache[occupant.vid]) == 1 && !e.s.values[occupant.vid].rematerializeable {
|
|
|
|
|
// We can't overwrite the last copy
|
|
|
|
|
// of a value that needs to survive.
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy from a source of v, register preferred.
|
|
|
|
|
v := e.s.orig[vid]
|
|
|
|
|
var c *Value
|
|
|
|
|
var src Location
|
2016-03-10 17:52:57 -06:00
|
|
|
if e.s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf("moving v%d to %s\n", vid, loc.Name())
|
|
|
|
|
fmt.Printf("sources of v%d:", vid)
|
|
|
|
|
}
|
|
|
|
|
for _, w := range e.cache[vid] {
|
|
|
|
|
h := e.s.f.getHome(w.ID)
|
2016-03-10 17:52:57 -06:00
|
|
|
if e.s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf(" %s:%s", h.Name(), w)
|
|
|
|
|
}
|
|
|
|
|
_, isreg := h.(*Register)
|
|
|
|
|
if src == nil || isreg {
|
|
|
|
|
c = w
|
|
|
|
|
src = h
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-10 17:52:57 -06:00
|
|
|
if e.s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
if src != nil {
|
|
|
|
|
fmt.Printf(" [use %s]\n", src.Name())
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Printf(" [no source]\n")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_, dstReg := loc.(*Register)
|
|
|
|
|
var x *Value
|
|
|
|
|
if c == nil {
|
|
|
|
|
if !e.s.values[vid].rematerializeable {
|
|
|
|
|
e.s.f.Fatalf("can't find source for %s->%s: v%d\n", e.p, e.b, vid)
|
|
|
|
|
}
|
|
|
|
|
if dstReg {
|
|
|
|
|
x = v.copyInto(e.p)
|
|
|
|
|
} else {
|
2016-03-01 23:21:55 +00:00
|
|
|
// Rematerialize into stack slot. Need a free
|
2015-12-17 10:01:24 -08:00
|
|
|
// register to accomplish this.
|
|
|
|
|
e.erase(loc) // see pre-clobber comment below
|
|
|
|
|
r := e.findRegFor(v.Type)
|
|
|
|
|
x = v.copyInto(e.p)
|
|
|
|
|
e.set(r, vid, x, false)
|
2016-01-04 13:34:54 -08:00
|
|
|
// Make sure we spill with the size of the slot, not the
|
|
|
|
|
// size of x (which might be wider due to our dropping
|
|
|
|
|
// of narrowing conversions).
|
|
|
|
|
x = e.p.NewValue1(x.Line, OpStoreReg, loc.(LocalSlot).Type, x)
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Emit move from src to dst.
|
|
|
|
|
_, srcReg := src.(*Register)
|
|
|
|
|
if srcReg {
|
|
|
|
|
if dstReg {
|
|
|
|
|
x = e.p.NewValue1(c.Line, OpCopy, c.Type, c)
|
|
|
|
|
} else {
|
2016-01-04 13:34:54 -08:00
|
|
|
x = e.p.NewValue1(c.Line, OpStoreReg, loc.(LocalSlot).Type, c)
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if dstReg {
|
2016-03-21 11:32:04 -04:00
|
|
|
e.s.lateSpillUse(c)
|
2015-12-17 10:01:24 -08:00
|
|
|
x = e.p.NewValue1(c.Line, OpLoadReg, c.Type, c)
|
|
|
|
|
} else {
|
2016-03-01 23:21:55 +00:00
|
|
|
// mem->mem. Use temp register.
|
2015-12-17 10:01:24 -08:00
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Pre-clobber destination. This avoids the
|
2015-12-17 10:01:24 -08:00
|
|
|
// following situation:
|
|
|
|
|
// - v is currently held in R0 and stacktmp0.
|
|
|
|
|
// - We want to copy stacktmp1 to stacktmp0.
|
|
|
|
|
// - We choose R0 as the temporary register.
|
|
|
|
|
// During the copy, both R0 and stacktmp0 are
|
2016-03-01 23:21:55 +00:00
|
|
|
// clobbered, losing both copies of v. Oops!
|
2015-12-17 10:01:24 -08:00
|
|
|
// Erasing the destination early means R0 will not
|
|
|
|
|
// be chosen as the temp register, as it will then
|
|
|
|
|
// be the last copy of v.
|
|
|
|
|
e.erase(loc)
|
|
|
|
|
|
|
|
|
|
r := e.findRegFor(c.Type)
|
2016-03-21 11:32:04 -04:00
|
|
|
e.s.lateSpillUse(c)
|
2015-12-17 10:01:24 -08:00
|
|
|
t := e.p.NewValue1(c.Line, OpLoadReg, c.Type, c)
|
|
|
|
|
e.set(r, vid, t, false)
|
2016-01-04 13:34:54 -08:00
|
|
|
x = e.p.NewValue1(c.Line, OpStoreReg, loc.(LocalSlot).Type, t)
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
e.set(loc, vid, x, true)
|
|
|
|
|
if splice != nil {
|
2016-03-15 20:45:50 -07:00
|
|
|
(*splice).Uses--
|
2015-12-17 10:01:24 -08:00
|
|
|
*splice = x
|
2016-03-15 20:45:50 -07:00
|
|
|
x.Uses++
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set changes the contents of location loc to hold the given value and its cached representative.
|
|
|
|
|
func (e *edgeState) set(loc Location, vid ID, c *Value, final bool) {
|
|
|
|
|
e.s.f.setHome(c, loc)
|
|
|
|
|
e.erase(loc)
|
|
|
|
|
e.contents[loc] = contentRecord{vid, c, final}
|
|
|
|
|
a := e.cache[vid]
|
2016-03-03 09:53:03 -08:00
|
|
|
if len(a) == 0 {
|
|
|
|
|
e.cachedVals = append(e.cachedVals, vid)
|
|
|
|
|
}
|
2015-12-17 10:01:24 -08:00
|
|
|
a = append(a, c)
|
|
|
|
|
e.cache[vid] = a
|
|
|
|
|
if r, ok := loc.(*Register); ok {
|
|
|
|
|
e.usedRegs |= regMask(1) << uint(r.Num)
|
|
|
|
|
if final {
|
|
|
|
|
e.finalRegs |= regMask(1) << uint(r.Num)
|
|
|
|
|
}
|
|
|
|
|
if len(a) == 1 {
|
|
|
|
|
e.uniqueRegs |= regMask(1) << uint(r.Num)
|
|
|
|
|
}
|
|
|
|
|
if len(a) == 2 {
|
|
|
|
|
if t, ok := e.s.f.getHome(a[0].ID).(*Register); ok {
|
|
|
|
|
e.uniqueRegs &^= regMask(1) << uint(t.Num)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-10 17:52:57 -06:00
|
|
|
if e.s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf("%s\n", c.LongString())
|
|
|
|
|
fmt.Printf("v%d now available in %s:%s\n", vid, loc.Name(), c)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// erase removes any user of loc.
|
|
|
|
|
func (e *edgeState) erase(loc Location) {
|
|
|
|
|
cr := e.contents[loc]
|
|
|
|
|
if cr.c == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
vid := cr.vid
|
|
|
|
|
|
|
|
|
|
if cr.final {
|
|
|
|
|
// Add a destination to move this value back into place.
|
|
|
|
|
// Make sure it gets added to the tail of the destination queue
|
|
|
|
|
// so we make progress on other moves first.
|
|
|
|
|
e.extra = append(e.extra, dstRecord{loc, cr.vid, nil})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove c from the list of cached values.
|
|
|
|
|
a := e.cache[vid]
|
|
|
|
|
for i, c := range a {
|
|
|
|
|
if e.s.f.getHome(c.ID) == loc {
|
2016-03-10 17:52:57 -06:00
|
|
|
if e.s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf("v%d no longer available in %s:%s\n", vid, loc.Name(), c)
|
|
|
|
|
}
|
|
|
|
|
a[i], a = a[len(a)-1], a[:len(a)-1]
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
e.cache[vid] = a
|
|
|
|
|
|
|
|
|
|
// Update register masks.
|
|
|
|
|
if r, ok := loc.(*Register); ok {
|
|
|
|
|
e.usedRegs &^= regMask(1) << uint(r.Num)
|
|
|
|
|
if cr.final {
|
|
|
|
|
e.finalRegs &^= regMask(1) << uint(r.Num)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(a) == 1 {
|
|
|
|
|
if r, ok := e.s.f.getHome(a[0].ID).(*Register); ok {
|
|
|
|
|
e.uniqueRegs |= regMask(1) << uint(r.Num)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// findRegFor finds a register we can use to make a temp copy of type typ.
|
|
|
|
|
func (e *edgeState) findRegFor(typ Type) Location {
|
|
|
|
|
// Which registers are possibilities.
|
|
|
|
|
var m regMask
|
|
|
|
|
if typ.IsFloat() {
|
|
|
|
|
m = e.s.compatRegs(e.s.f.Config.fe.TypeFloat64())
|
|
|
|
|
} else {
|
|
|
|
|
m = e.s.compatRegs(e.s.f.Config.fe.TypeInt64())
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Pick a register. In priority order:
|
2015-12-17 10:01:24 -08:00
|
|
|
// 1) an unused register
|
|
|
|
|
// 2) a non-unique register not holding a final value
|
|
|
|
|
// 3) a non-unique register
|
|
|
|
|
x := m &^ e.usedRegs
|
|
|
|
|
if x != 0 {
|
2016-03-21 22:57:26 -07:00
|
|
|
return &e.s.registers[pickReg(x)]
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
|
|
|
|
x = m &^ e.uniqueRegs &^ e.finalRegs
|
|
|
|
|
if x != 0 {
|
2016-03-21 22:57:26 -07:00
|
|
|
return &e.s.registers[pickReg(x)]
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
|
|
|
|
x = m &^ e.uniqueRegs
|
|
|
|
|
if x != 0 {
|
2016-03-21 22:57:26 -07:00
|
|
|
return &e.s.registers[pickReg(x)]
|
2015-12-17 10:01:24 -08:00
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// No register is available. Allocate a temp location to spill a register to.
|
2015-12-17 10:01:24 -08:00
|
|
|
// The type of the slot is immaterial - it will not be live across
|
2016-03-01 23:21:55 +00:00
|
|
|
// any safepoint. Just use a type big enough to hold any register.
|
2015-12-17 10:01:24 -08:00
|
|
|
typ = e.s.f.Config.fe.TypeInt64()
|
|
|
|
|
t := LocalSlot{e.s.f.Config.fe.Auto(typ), typ, 0}
|
|
|
|
|
// TODO: reuse these slots.
|
|
|
|
|
|
|
|
|
|
// Pick a register to spill.
|
2016-03-03 09:53:03 -08:00
|
|
|
for _, vid := range e.cachedVals {
|
|
|
|
|
a := e.cache[vid]
|
2015-12-17 10:01:24 -08:00
|
|
|
for _, c := range a {
|
|
|
|
|
if r, ok := e.s.f.getHome(c.ID).(*Register); ok && m>>uint(r.Num)&1 != 0 {
|
|
|
|
|
x := e.p.NewValue1(c.Line, OpStoreReg, c.Type, c)
|
|
|
|
|
e.set(t, vid, x, false)
|
2016-03-10 17:52:57 -06:00
|
|
|
if e.s.f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Printf(" SPILL %s->%s %s\n", r.Name(), t.Name(), x.LongString())
|
|
|
|
|
}
|
2016-03-01 23:21:55 +00:00
|
|
|
// r will now be overwritten by the caller. At some point
|
2015-12-17 10:01:24 -08:00
|
|
|
// later, the newly saved value will be moved back to its
|
|
|
|
|
// final destination in processDest.
|
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-04 13:34:54 -08:00
|
|
|
fmt.Printf("m:%d unique:%d final:%d\n", m, e.uniqueRegs, e.finalRegs)
|
2016-03-03 09:53:03 -08:00
|
|
|
for _, vid := range e.cachedVals {
|
|
|
|
|
a := e.cache[vid]
|
2016-01-04 13:34:54 -08:00
|
|
|
for _, c := range a {
|
|
|
|
|
fmt.Printf("v%d: %s %s\n", vid, c, e.s.f.getHome(c.ID).Name())
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-12-17 10:01:24 -08:00
|
|
|
e.s.f.Fatalf("can't find empty register on edge %s->%s", e.p, e.b)
|
|
|
|
|
return nil
|
2015-05-05 16:19:12 -07:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 10:57:03 -07:00
|
|
|
func (v *Value) rematerializeable() bool {
|
2016-01-31 11:39:39 -08:00
|
|
|
if !opcodeTable[v.Op].rematerializeable {
|
2015-10-19 10:57:03 -07:00
|
|
|
return false
|
|
|
|
|
}
|
2016-01-31 11:39:39 -08:00
|
|
|
for _, a := range v.Args {
|
2015-12-17 10:01:24 -08:00
|
|
|
// SP and SB (generated by OpSP and OpSB) are always available.
|
2016-01-31 11:39:39 -08:00
|
|
|
if a.Op != OpSP && a.Op != OpSB {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2015-10-19 10:57:03 -07:00
|
|
|
}
|
2016-01-31 11:39:39 -08:00
|
|
|
return true
|
2015-10-19 10:57:03 -07:00
|
|
|
}
|
|
|
|
|
|
2015-11-05 14:59:47 -08:00
|
|
|
type liveInfo struct {
|
|
|
|
|
ID ID // ID of variable
|
|
|
|
|
dist int32 // # of instructions before next use
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// computeLive computes a map from block ID to a list of value IDs live at the end
|
2016-03-01 23:21:55 +00:00
|
|
|
// of that block. Together with the value ID is a count of how many instructions
|
|
|
|
|
// to the next use of that value. The resulting map is stored at s.live.
|
2015-05-05 16:19:12 -07:00
|
|
|
// TODO: this could be quadratic if lots of variables are live across lots of
|
2016-03-01 23:21:55 +00:00
|
|
|
// basic blocks. Figure out a way to make this function (or, more precisely, the user
|
2015-05-05 16:19:12 -07:00
|
|
|
// of this function) require only linear size & time.
|
2015-11-05 14:59:47 -08:00
|
|
|
func (s *regAllocState) computeLive() {
|
|
|
|
|
f := s.f
|
|
|
|
|
s.live = make([][]liveInfo, f.NumBlocks())
|
2015-05-05 16:19:12 -07:00
|
|
|
var phis []*Value
|
|
|
|
|
|
2015-11-05 14:59:47 -08:00
|
|
|
live := newSparseMap(f.NumValues())
|
|
|
|
|
t := newSparseMap(f.NumValues())
|
2015-07-22 20:40:18 -07:00
|
|
|
|
|
|
|
|
// Instead of iterating over f.Blocks, iterate over their postordering.
|
|
|
|
|
// Liveness information flows backward, so starting at the end
|
|
|
|
|
// increases the probability that we will stabilize quickly.
|
|
|
|
|
// TODO: Do a better job yet. Here's one possibility:
|
|
|
|
|
// Calculate the dominator tree and locate all strongly connected components.
|
|
|
|
|
// If a value is live in one block of an SCC, it is live in all.
|
|
|
|
|
// Walk the dominator tree from end to beginning, just once, treating SCC
|
|
|
|
|
// components as single blocks, duplicated calculated liveness information
|
|
|
|
|
// out to all of them.
|
2016-03-10 14:42:52 -05:00
|
|
|
s.loopnest = loopnestfor(f)
|
|
|
|
|
po := s.loopnest.po
|
2015-05-05 16:19:12 -07:00
|
|
|
for {
|
|
|
|
|
changed := false
|
|
|
|
|
|
2015-07-22 20:40:18 -07:00
|
|
|
for _, b := range po {
|
2015-11-05 14:59:47 -08:00
|
|
|
// Start with known live values at the end of the block.
|
|
|
|
|
// Add len(b.Values) to adjust from end-of-block distance
|
|
|
|
|
// to beginning-of-block distance.
|
|
|
|
|
live.clear()
|
2016-03-02 15:18:40 -08:00
|
|
|
d := int32(len(b.Values))
|
2016-03-09 19:27:57 -08:00
|
|
|
if b.Kind == BlockCall || b.Kind == BlockDefer {
|
2016-03-02 15:18:40 -08:00
|
|
|
// Because we keep no values in registers across a call,
|
|
|
|
|
// make every use past a call very far away.
|
|
|
|
|
d += unlikelyDistance
|
|
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
for _, e := range s.live[b.ID] {
|
2016-03-02 15:18:40 -08:00
|
|
|
live.set(e.ID, e.dist+d)
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mark control value as live
|
2015-12-17 10:01:24 -08:00
|
|
|
if b.Control != nil && s.values[b.Control.ID].needReg {
|
2015-11-05 14:59:47 -08:00
|
|
|
live.set(b.Control.ID, int32(len(b.Values)))
|
2015-08-04 14:22:29 -07:00
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
|
|
|
|
|
// Propagate backwards to the start of the block
|
|
|
|
|
// Assumes Values have been scheduled.
|
|
|
|
|
phis := phis[:0]
|
|
|
|
|
for i := len(b.Values) - 1; i >= 0; i-- {
|
|
|
|
|
v := b.Values[i]
|
2015-11-05 14:59:47 -08:00
|
|
|
live.remove(v.ID)
|
2015-05-05 16:19:12 -07:00
|
|
|
if v.Op == OpPhi {
|
|
|
|
|
// save phi ops for later
|
|
|
|
|
phis = append(phis, v)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
for _, a := range v.Args {
|
2015-12-17 10:01:24 -08:00
|
|
|
if s.values[a.ID].needReg {
|
2015-11-05 14:59:47 -08:00
|
|
|
live.set(a.ID, int32(i))
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
}
|
|
|
|
|
|
2015-11-05 14:59:47 -08:00
|
|
|
// For each predecessor of b, expand its list of live-at-end values.
|
|
|
|
|
// invariant: live contains the values live at the start of b (excluding phi inputs)
|
2015-05-05 16:19:12 -07:00
|
|
|
for i, p := range b.Preds {
|
2015-11-05 14:59:47 -08:00
|
|
|
// Compute additional distance for the edge.
|
|
|
|
|
// Note: delta must be at least 1 to distinguish the control
|
|
|
|
|
// value use from the first user in a successor block.
|
2016-03-02 15:18:40 -08:00
|
|
|
delta := int32(normalDistance)
|
2015-11-05 14:59:47 -08:00
|
|
|
if len(p.Succs) == 2 {
|
|
|
|
|
if p.Succs[0] == b && p.Likely == BranchLikely ||
|
|
|
|
|
p.Succs[1] == b && p.Likely == BranchUnlikely {
|
2016-03-02 15:18:40 -08:00
|
|
|
delta = likelyDistance
|
2015-11-05 14:59:47 -08:00
|
|
|
}
|
|
|
|
|
if p.Succs[0] == b && p.Likely == BranchUnlikely ||
|
|
|
|
|
p.Succs[1] == b && p.Likely == BranchLikely {
|
2016-03-02 15:18:40 -08:00
|
|
|
delta = unlikelyDistance
|
2015-08-11 12:51:33 -07:00
|
|
|
}
|
|
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
|
|
|
|
|
// Start t off with the previously known live values at the end of p.
|
2015-05-05 16:19:12 -07:00
|
|
|
t.clear()
|
2015-11-05 14:59:47 -08:00
|
|
|
for _, e := range s.live[p.ID] {
|
|
|
|
|
t.set(e.ID, e.dist)
|
|
|
|
|
}
|
|
|
|
|
update := false
|
|
|
|
|
|
|
|
|
|
// Add new live values from scanning this block.
|
|
|
|
|
for _, e := range live.contents() {
|
|
|
|
|
d := e.val + delta
|
|
|
|
|
if !t.contains(e.key) || d < t.get(e.key) {
|
|
|
|
|
update = true
|
|
|
|
|
t.set(e.key, d)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Also add the correct arg from the saved phi values.
|
|
|
|
|
// All phis are at distance delta (we consider them
|
|
|
|
|
// simultaneously happening at the start of the block).
|
2015-05-05 16:19:12 -07:00
|
|
|
for _, v := range phis {
|
2015-11-05 14:59:47 -08:00
|
|
|
id := v.Args[i].ID
|
2015-12-17 10:01:24 -08:00
|
|
|
if s.values[id].needReg && !t.contains(id) || delta < t.get(id) {
|
2015-11-05 14:59:47 -08:00
|
|
|
update = true
|
|
|
|
|
t.set(id, delta)
|
|
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
|
|
|
|
|
if !update {
|
2015-05-05 16:19:12 -07:00
|
|
|
continue
|
|
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
// The live set has changed, update it.
|
|
|
|
|
l := s.live[p.ID][:0]
|
2016-02-10 17:43:31 -05:00
|
|
|
if cap(l) < t.size() {
|
|
|
|
|
l = make([]liveInfo, 0, t.size())
|
2016-01-30 17:37:38 -05:00
|
|
|
}
|
2015-11-05 14:59:47 -08:00
|
|
|
for _, e := range t.contents() {
|
|
|
|
|
l = append(l, liveInfo{e.key, e.val})
|
|
|
|
|
}
|
|
|
|
|
s.live[p.ID] = l
|
2015-05-05 16:19:12 -07:00
|
|
|
changed = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !changed {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-10 17:52:57 -06:00
|
|
|
if f.pass.debug > regDebug {
|
2015-12-17 10:01:24 -08:00
|
|
|
fmt.Println("live values at end of each block")
|
|
|
|
|
for _, b := range f.Blocks {
|
|
|
|
|
fmt.Printf(" %s:", b)
|
|
|
|
|
for _, x := range s.live[b.ID] {
|
|
|
|
|
fmt.Printf(" v%d", x.ID)
|
|
|
|
|
}
|
|
|
|
|
fmt.Println()
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-05 16:19:12 -07:00
|
|
|
}
|
2015-10-22 13:07:38 -07:00
|
|
|
|
|
|
|
|
// reserved returns a mask of reserved registers.
|
|
|
|
|
func (s *regAllocState) reserved() regMask {
|
|
|
|
|
var m regMask
|
|
|
|
|
if obj.Framepointer_enabled != 0 {
|
|
|
|
|
m |= 1 << 5 // BP
|
|
|
|
|
}
|
|
|
|
|
if s.f.Config.ctxt.Flag_dynlink {
|
|
|
|
|
m |= 1 << 15 // R15
|
|
|
|
|
}
|
|
|
|
|
return m
|
|
|
|
|
}
|