| // 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. | |
| // Register allocation. | |
| // | |
| // We use a version of a linear scan register allocator. We treat the | |
| // whole function as a single long basic block and run through | |
| // it using a greedy register allocator. Then all merge edges | |
| // (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 | |
| // at least one of its predecessors have been scheduled. The most recent | |
| // 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 | |
| // | |
| // During the normal course of the allocator, we might throw a still-live | |
| // value out of all registers. When that value is subsequently used, we must | |
| // load it from a slot on the stack. We must also issue an instruction to | |
| // initialize that stack location with a copy of v. | |
| // | |
| // pre-regalloc: | |
| // (1) v = Op ... | |
| // (2) x = Op ... | |
| // (3) ... = Op v ... | |
| // | |
| // post-regalloc: | |
| // (1) v = Op ... : AX // computes v, store result in AX | |
| // s = StoreReg v // spill v to a stack slot | |
| // (2) x = Op ... : AX // some other op uses AX | |
| // c = LoadReg s : CX // restore v from stack slot | |
| // (3) ... = Op c ... // use the restored value | |
| // | |
| // Allocation occurs normally until we reach (3) and we realize we have | |
| // a use of v and it isn't in any register. At that point, we allocate | |
| // a spill (a StoreReg) for v. We can't determine the correct place for | |
| // the spill at this point, so we allocate the spill as blockless initially. | |
| // The restore is then generated to load v back into a register so it can | |
| // be used. Subsequent uses of v will use the restored value c instead. | |
| // | |
| // What remains is the question of where to schedule the spill. | |
| // During allocation, we keep track of the dominator of all restores of v. | |
| // The spill of v must dominate that block. The spill must also be issued at | |
| // a point where v is still in a register. | |
| // | |
| // To find the right place, start at b, the block which dominates all restores. | |
| // - If b is v.Block, then issue the spill right after v. | |
| // It is known to be in a register at that point, and dominates any restores. | |
| // - Otherwise, if v is in a register at the start of b, | |
| // put the spill of v at the start of b. | |
| // - Otherwise, set b = immediate dominator of b, and repeat. | |
| // | |
| // Phi values are special, as always. We define two kinds of phis, those | |
| // where the merge happens in a register (a "register" phi) and those where | |
| // the merge happens in a stack location (a "stack" phi). | |
| // | |
| // A register phi must have the phi and all of its inputs allocated to the | |
| // same register. Register phis are spilled similarly to regular ops. | |
| // | |
| // A stack phi must have the phi and all of its inputs allocated to the same | |
| // stack location. Stack phis start out life already spilled - each phi | |
| // 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 | |
| // same register. This affinity graph will be used to prefer certain | |
| // registers for allocation. This affinity helps eliminate moves that | |
| // are required for phi implementations and helps generate allocations | |
| // for 2-register architectures. | |
| // Note: regalloc generates a not-quite-SSA output. If we have: | |
| // | |
| // 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 an extra phi at the start of b2 (x5=phi(x3,x4):BX) to keep | |
| // 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 | |
| // 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? | |
| package ssa | |
| import ( | |
| "cmd/compile/internal/ssa/block" | |
| "cmd/compile/internal/ssa/ssaop" | |
| "cmd/internal/src" | |
| ) | |
| const ( | |
| moveSpills = iota | |
| LogSpills | |
| RegDebug | |
| StackDebug | |
| ) | |
| func RegMaskAt(i ssaop.Register) ssaop.RegMask { | |
| if i < 64 { | |
| return ssaop.RegMask{V1: 1 << i} | |
| } | |
| return ssaop.RegMask{V2: 1 << (i - 64)} | |
| } | |
| type Use struct { | |
| // distance from start of the block to a use of a value | |
| // Dist == 0 used by first instruction in block | |
| // Dist == len(b.Values)-1 used by last instruction in block | |
| // Dist == len(b.Values) used by block's control value | |
| // Dist > len(b.Values) used by a subsequent block | |
| Dist int32 | |
| Pos src.XPos // source position of the use | |
| Next *Use // linked list of uses of a value in nondecreasing dist order | |
| } | |
| // A ValState records the register allocation state for a (pre-regalloc) value. | |
| type ValState struct { | |
| Regs ssaop.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 (if any) | |
| RestoreMin int32 // minimum of all restores' blocks' sdom.entry | |
| RestoreMax int32 // maximum of all restores' blocks' sdom.exit | |
| NeedReg bool // cached value of !v.Type.IsMemory() && !v.Type.IsVoid() && !.v.Type.IsFlags() | |
| Rematerializeable bool // cached value of v.rematerializeable() | |
| } | |
| // NeedRegister reports whether v needs a register. | |
| func (v *Value) NeedRegister() bool { | |
| return !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() && !v.Type.IsTuple() | |
| } | |
| // Rematerializeable reports whether the register allocator should recompute | |
| // a value instead of spilling/restoring it. | |
| func (v *Value) Rematerializeable() bool { | |
| if !ssaop.OpcodeTable[v.Op].Rematerializeable { | |
| return false | |
| } | |
| for _, a := range v.Args { | |
| // Fixed-register allocations (SP, SB, etc.) are always available. | |
| // Any other argument of an opcode makes it not rematerializeable. | |
| if !ssaop.OpcodeTable[a.Op].FixedReg { | |
| return false | |
| } | |
| } | |
| return true | |
| } | |
| // ComputeUnavoidableCalls computes the containsUnavoidableCall fields in the loop nest. | |
| func (loopnest *LoopNest) ComputeUnavoidableCalls() { | |
| f := loopnest.F | |
| hasCall := f.Cache.AllocBoolSlice(f.NumBlocks()) | |
| defer f.Cache.FreeBoolSlice(hasCall) | |
| for _, b := range f.Blocks { | |
| if b.containsCall() { | |
| hasCall[b.ID] = true | |
| } | |
| } | |
| found := f.Cache.AllocSparseSet(f.NumBlocks()) | |
| defer f.Cache.FreeSparseSet(found) | |
| // Run dfs to find path through the loop that avoids all calls. | |
| // Such path either escapes the loop or returns back to the header. | |
| // It isn't enough to have exit not dominated by any call, for example: | |
| // ... some loop | |
| // call1 call2 | |
| // \ / | |
| // block | |
| // ... | |
| // block is not dominated by any single call, but we don't have call-free path to it. | |
| loopLoop: | |
| for _, l := range loopnest.Loops { | |
| found.Clear() | |
| tovisit := make([]*Block, 0, 8) | |
| tovisit = append(tovisit, l.Header) | |
| for len(tovisit) > 0 { | |
| cur := tovisit[len(tovisit)-1] | |
| tovisit = tovisit[:len(tovisit)-1] | |
| if hasCall[cur.ID] { | |
| continue | |
| } | |
| for _, s := range cur.Succs { | |
| nb := s.Block() | |
| if nb == l.Header { | |
| // Found a call-free path around the loop. | |
| continue loopLoop | |
| } | |
| if found.Contains(nb.ID) { | |
| // Already found via another path. | |
| continue | |
| } | |
| nl := loopnest.B2L[nb.ID] | |
| if nl == nil || (nl.Depth <= l.Depth && nl != l) { | |
| // Left the loop. | |
| continue | |
| } | |
| tovisit = append(tovisit, nb) | |
| found.Add(nb.ID) | |
| } | |
| } | |
| // No call-free path was found. | |
| l.ContainsUnavoidableCall = true | |
| } | |
| } | |
| func (b *Block) containsCall() bool { | |
| if b.Kind == block.BlockDefer { | |
| return true | |
| } | |
| for _, v := range b.Values { | |
| if ssaop.OpcodeTable[v.Op].Call { | |
| return true | |
| } | |
| } | |
| return false | |
| } |