|
| 1 | +// Copyright 2021 The Go Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style |
| 3 | +// license that can be found in the LICENSE file. |
| 4 | + |
| 5 | +package liveness |
| 6 | + |
| 7 | +import ( |
| 8 | + "fmt" |
| 9 | + |
| 10 | + "cmd/compile/internal/base" |
| 11 | + "cmd/compile/internal/bitvec" |
| 12 | + "cmd/compile/internal/ir" |
| 13 | + "cmd/compile/internal/objw" |
| 14 | + "cmd/compile/internal/ssa" |
| 15 | + "cmd/internal/obj" |
| 16 | + "cmd/internal/objabi" |
| 17 | +) |
| 18 | + |
| 19 | +// Argument liveness tracking. |
| 20 | +// |
| 21 | +// For arguments passed in registers, this file tracks if their spill slots |
| 22 | +// are live for runtime traceback. An argument spill slot is live at a PC |
| 23 | +// if we know that an actual value has stored into it at or before this point. |
| 24 | +// |
| 25 | +// Stack args are always live and not tracked in this code. Stack args are |
| 26 | +// laid out before register spill slots, so we emit the smallest offset that |
| 27 | +// needs tracking. Slots before that offset are always live. That offset is |
| 28 | +// usually the offset of the first spill slot. But if the first spill slot is |
| 29 | +// always live (e.g. if it is address-taken), it will be the offset of a later |
| 30 | +// one. |
| 31 | +// |
| 32 | +// The liveness information is emitted as a FUNCDATA and a PCDATA. |
| 33 | +// |
| 34 | +// FUNCDATA format: |
| 35 | +// - start (smallest) offset that needs tracking (1 byte) |
| 36 | +// - a list of bitmaps. |
| 37 | +// In a bitmap bit i is set if the i-th spill slot is live. |
| 38 | +// |
| 39 | +// At a PC where the liveness info changes, a PCDATA indicates the |
| 40 | +// byte offset of the liveness map in the FUNCDATA. PCDATA -1 is a |
| 41 | +// special case indicating all slots are live (for binary size |
| 42 | +// saving). |
| 43 | + |
| 44 | +const allLiveIdx = -1 |
| 45 | + |
| 46 | +// name and offset |
| 47 | +type nameOff struct { |
| 48 | + n *ir.Name |
| 49 | + off int64 |
| 50 | +} |
| 51 | + |
| 52 | +func (a nameOff) FrameOffset() int64 { return a.n.FrameOffset() + a.off } |
| 53 | +func (a nameOff) String() string { return fmt.Sprintf("%v+%d", a.n, a.off) } |
| 54 | + |
| 55 | +type blockArgEffects struct { |
| 56 | + livein bitvec.BitVec // variables live at block entry |
| 57 | + liveout bitvec.BitVec // variables live at block exit |
| 58 | +} |
| 59 | + |
| 60 | +type argLiveness struct { |
| 61 | + fn *ir.Func |
| 62 | + f *ssa.Func |
| 63 | + args []nameOff // name and offset of spill slots |
| 64 | + idx map[nameOff]int32 // index in args |
| 65 | + |
| 66 | + be []blockArgEffects // indexed by block ID |
| 67 | + |
| 68 | + bvset bvecSet // Set of liveness bitmaps, used for uniquifying. |
| 69 | + |
| 70 | + // Liveness map indices at each Value (where it changes) and Block entry. |
| 71 | + // During the computation the indices are temporarily index to bvset. |
| 72 | + // At the end they will be index (offset) to the output funcdata (changed |
| 73 | + // in (*argLiveness).emit). |
| 74 | + blockIdx map[ssa.ID]int |
| 75 | + valueIdx map[ssa.ID]int |
| 76 | +} |
| 77 | + |
| 78 | +// ArgLiveness computes the liveness information of register argument spill slots. |
| 79 | +// An argument's spill slot is "live" if we know it contains a meaningful value, |
| 80 | +// that is, we have stored the register value to it. |
| 81 | +// Returns the liveness map indices at each Block entry and at each Value (where |
| 82 | +// it changes). |
| 83 | +func ArgLiveness(fn *ir.Func, f *ssa.Func, pp *objw.Progs) (blockIdx, valueIdx map[ssa.ID]int) { |
| 84 | + if f.OwnAux.ABIInfo().InRegistersUsed() == 0 || base.Flag.N != 0 { |
| 85 | + // No register args. Nothing to emit. |
| 86 | + // Or if -N is used we spill everything upfront so it is always live. |
| 87 | + return nil, nil |
| 88 | + } |
| 89 | + |
| 90 | + lv := &argLiveness{ |
| 91 | + fn: fn, |
| 92 | + f: f, |
| 93 | + idx: make(map[nameOff]int32), |
| 94 | + be: make([]blockArgEffects, f.NumBlocks()), |
| 95 | + blockIdx: make(map[ssa.ID]int), |
| 96 | + valueIdx: make(map[ssa.ID]int), |
| 97 | + } |
| 98 | + // Gather all register arg spill slots. |
| 99 | + for _, a := range f.OwnAux.ABIInfo().InParams() { |
| 100 | + n, ok := a.Name.(*ir.Name) |
| 101 | + if !ok || len(a.Registers) == 0 { |
| 102 | + continue |
| 103 | + } |
| 104 | + _, offs := a.RegisterTypesAndOffsets() |
| 105 | + for _, off := range offs { |
| 106 | + if n.FrameOffset()+off > 0xff { |
| 107 | + // We only print a limited number of args, with stack |
| 108 | + // offsets no larger than 255. |
| 109 | + continue |
| 110 | + } |
| 111 | + lv.args = append(lv.args, nameOff{n, off}) |
| 112 | + } |
| 113 | + } |
| 114 | + if len(lv.args) > 10 { |
| 115 | + lv.args = lv.args[:10] // We print no more than 10 args. |
| 116 | + } |
| 117 | + |
| 118 | + // We spill address-taken or non-SSA-able value upfront, so they are always live. |
| 119 | + alwaysLive := func(n *ir.Name) bool { return n.Addrtaken() || !f.Frontend().CanSSA(n.Type()) } |
| 120 | + |
| 121 | + // We'll emit the smallest offset for the slots that need liveness info. |
| 122 | + // No need to include a slot with a lower offset if it is always live. |
| 123 | + for len(lv.args) > 0 && alwaysLive(lv.args[0].n) { |
| 124 | + lv.args = lv.args[1:] |
| 125 | + } |
| 126 | + if len(lv.args) == 0 { |
| 127 | + return // everything is always live |
| 128 | + } |
| 129 | + |
| 130 | + for i, a := range lv.args { |
| 131 | + lv.idx[a] = int32(i) |
| 132 | + } |
| 133 | + |
| 134 | + nargs := int32(len(lv.args)) |
| 135 | + bulk := bitvec.NewBulk(nargs, int32(len(f.Blocks)*2)) |
| 136 | + for _, b := range f.Blocks { |
| 137 | + be := &lv.be[b.ID] |
| 138 | + be.livein = bulk.Next() |
| 139 | + be.liveout = bulk.Next() |
| 140 | + |
| 141 | + // initialize to all 1s, so we can AND them |
| 142 | + be.livein.Not() |
| 143 | + be.liveout.Not() |
| 144 | + } |
| 145 | + |
| 146 | + entrybe := &lv.be[f.Entry.ID] |
| 147 | + entrybe.livein.Clear() |
| 148 | + for i, a := range lv.args { |
| 149 | + if alwaysLive(a.n) { |
| 150 | + entrybe.livein.Set(int32(i)) |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + // Visit blocks in reverse-postorder, compute block effects. |
| 155 | + po := f.Postorder() |
| 156 | + for i := len(po) - 1; i >= 0; i-- { |
| 157 | + b := po[i] |
| 158 | + be := &lv.be[b.ID] |
| 159 | + |
| 160 | + // A slot is live at block entry if it is live in all predecessors. |
| 161 | + for _, pred := range b.Preds { |
| 162 | + pb := pred.Block() |
| 163 | + be.livein.And(be.livein, lv.be[pb.ID].liveout) |
| 164 | + } |
| 165 | + |
| 166 | + be.liveout.Copy(be.livein) |
| 167 | + for _, v := range b.Values { |
| 168 | + lv.valueEffect(v, be.liveout) |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + // Coalesce identical live vectors. Compute liveness indices at each PC |
| 173 | + // where it changes. |
| 174 | + live := bitvec.New(nargs) |
| 175 | + addToSet := func(bv bitvec.BitVec) (int, bool) { |
| 176 | + if bv.Count() == int(nargs) { // special case for all live |
| 177 | + return allLiveIdx, false |
| 178 | + } |
| 179 | + return lv.bvset.add(bv) |
| 180 | + } |
| 181 | + for _, b := range lv.f.Blocks { |
| 182 | + be := &lv.be[b.ID] |
| 183 | + lv.blockIdx[b.ID], _ = addToSet(be.livein) |
| 184 | + |
| 185 | + live.Copy(be.livein) |
| 186 | + var lastv *ssa.Value |
| 187 | + for i, v := range b.Values { |
| 188 | + if lv.valueEffect(v, live) { |
| 189 | + // Record that liveness changes but not emit a map now. |
| 190 | + // For a sequence of StoreRegs we only need to emit one |
| 191 | + // at last. |
| 192 | + lastv = v |
| 193 | + } |
| 194 | + if lastv != nil && (mayFault(v) || i == len(b.Values)-1) { |
| 195 | + // Emit the liveness map if it may fault or at the end of |
| 196 | + // the block. We may need a traceback if the instruction |
| 197 | + // may cause a panic. |
| 198 | + var added bool |
| 199 | + lv.valueIdx[lastv.ID], added = addToSet(live) |
| 200 | + if added { |
| 201 | + // live is added to bvset and we cannot modify it now. |
| 202 | + // Make a copy. |
| 203 | + t := live |
| 204 | + live = bitvec.New(nargs) |
| 205 | + live.Copy(t) |
| 206 | + } |
| 207 | + lastv = nil |
| 208 | + } |
| 209 | + } |
| 210 | + |
| 211 | + // Sanity check. |
| 212 | + if !live.Eq(be.liveout) { |
| 213 | + panic("wrong arg liveness map at block end") |
| 214 | + } |
| 215 | + } |
| 216 | + |
| 217 | + // Emit funcdata symbol, update indices to offsets in the symbol data. |
| 218 | + lsym := lv.emit() |
| 219 | + fn.LSym.Func().ArgLiveInfo = lsym |
| 220 | + |
| 221 | + //lv.print() |
| 222 | + |
| 223 | + p := pp.Prog(obj.AFUNCDATA) |
| 224 | + p.From.SetConst(objabi.FUNCDATA_ArgLiveInfo) |
| 225 | + p.To.Type = obj.TYPE_MEM |
| 226 | + p.To.Name = obj.NAME_EXTERN |
| 227 | + p.To.Sym = lsym |
| 228 | + |
| 229 | + return lv.blockIdx, lv.valueIdx |
| 230 | +} |
| 231 | + |
| 232 | +// valueEffect applies the effect of v to live, return whether it is changed. |
| 233 | +func (lv *argLiveness) valueEffect(v *ssa.Value, live bitvec.BitVec) bool { |
| 234 | + if v.Op != ssa.OpStoreReg { // TODO: include other store instructions? |
| 235 | + return false |
| 236 | + } |
| 237 | + n, off := ssa.AutoVar(v) |
| 238 | + if n.Class != ir.PPARAM { |
| 239 | + return false |
| 240 | + } |
| 241 | + i, ok := lv.idx[nameOff{n, off}] |
| 242 | + if !ok || live.Get(i) { |
| 243 | + return false |
| 244 | + } |
| 245 | + live.Set(i) |
| 246 | + return true |
| 247 | +} |
| 248 | + |
| 249 | +func mayFault(v *ssa.Value) bool { |
| 250 | + switch v.Op { |
| 251 | + case ssa.OpLoadReg, ssa.OpStoreReg, ssa.OpCopy, ssa.OpPhi, |
| 252 | + ssa.OpVarDef, ssa.OpVarKill, ssa.OpVarLive, ssa.OpKeepAlive, |
| 253 | + ssa.OpSelect0, ssa.OpSelect1, ssa.OpSelectN, ssa.OpMakeResult, |
| 254 | + ssa.OpConvert, ssa.OpInlMark, ssa.OpGetG: |
| 255 | + return false |
| 256 | + } |
| 257 | + if len(v.Args) == 0 { |
| 258 | + return false // assume constant op cannot fault |
| 259 | + } |
| 260 | + return true // conservatively assume all other ops could fault |
| 261 | +} |
| 262 | + |
| 263 | +func (lv *argLiveness) print() { |
| 264 | + fmt.Println("argument liveness:", lv.f.Name) |
| 265 | + live := bitvec.New(int32(len(lv.args))) |
| 266 | + for _, b := range lv.f.Blocks { |
| 267 | + be := &lv.be[b.ID] |
| 268 | + |
| 269 | + fmt.Printf("%v: live in: ", b) |
| 270 | + lv.printLivenessVec(be.livein) |
| 271 | + if idx, ok := lv.blockIdx[b.ID]; ok { |
| 272 | + fmt.Printf(" #%d", idx) |
| 273 | + } |
| 274 | + fmt.Println() |
| 275 | + |
| 276 | + for _, v := range b.Values { |
| 277 | + if lv.valueEffect(v, live) { |
| 278 | + fmt.Printf(" %v: ", v) |
| 279 | + lv.printLivenessVec(live) |
| 280 | + if idx, ok := lv.valueIdx[v.ID]; ok { |
| 281 | + fmt.Printf(" #%d", idx) |
| 282 | + } |
| 283 | + fmt.Println() |
| 284 | + } |
| 285 | + } |
| 286 | + |
| 287 | + fmt.Printf("%v: live out: ", b) |
| 288 | + lv.printLivenessVec(be.liveout) |
| 289 | + fmt.Println() |
| 290 | + } |
| 291 | + fmt.Println("liveness maps data:", lv.fn.LSym.Func().ArgLiveInfo.P) |
| 292 | +} |
| 293 | + |
| 294 | +func (lv *argLiveness) printLivenessVec(bv bitvec.BitVec) { |
| 295 | + for i, a := range lv.args { |
| 296 | + if bv.Get(int32(i)) { |
| 297 | + fmt.Printf("%v ", a) |
| 298 | + } |
| 299 | + } |
| 300 | +} |
| 301 | + |
| 302 | +func (lv *argLiveness) emit() *obj.LSym { |
| 303 | + livenessMaps := lv.bvset.extractUnique() |
| 304 | + |
| 305 | + // stack offsets of register arg spill slots |
| 306 | + argOffsets := make([]uint8, len(lv.args)) |
| 307 | + for i, a := range lv.args { |
| 308 | + off := a.FrameOffset() |
| 309 | + if off > 0xff { |
| 310 | + panic("offset too large") |
| 311 | + } |
| 312 | + argOffsets[i] = uint8(off) |
| 313 | + } |
| 314 | + |
| 315 | + idx2off := make([]int, len(livenessMaps)) |
| 316 | + |
| 317 | + lsym := base.Ctxt.Lookup(lv.fn.LSym.Name + ".argliveinfo") |
| 318 | + lsym.Set(obj.AttrContentAddressable, true) |
| 319 | + |
| 320 | + off := objw.Uint8(lsym, 0, argOffsets[0]) // smallest offset that needs liveness info. |
| 321 | + for idx, live := range livenessMaps { |
| 322 | + idx2off[idx] = off |
| 323 | + off = objw.BitVec(lsym, off, live) |
| 324 | + } |
| 325 | + |
| 326 | + // Update liveness indices to offsets. |
| 327 | + for i, x := range lv.blockIdx { |
| 328 | + if x != allLiveIdx { |
| 329 | + lv.blockIdx[i] = idx2off[x] |
| 330 | + } |
| 331 | + } |
| 332 | + for i, x := range lv.valueIdx { |
| 333 | + if x != allLiveIdx { |
| 334 | + lv.valueIdx[i] = idx2off[x] |
| 335 | + } |
| 336 | + } |
| 337 | + |
| 338 | + return lsym |
| 339 | +} |
0 commit comments