| | |
| | |
| | |
| |
|
| | package ssa |
| |
|
| | import ( |
| | "cmd/compile/internal/abi" |
| | "cmd/compile/internal/abt" |
| | "cmd/compile/internal/ir" |
| | "cmd/compile/internal/types" |
| | "cmd/internal/dwarf" |
| | "cmd/internal/obj" |
| | "cmd/internal/src" |
| | "cmp" |
| | "encoding/hex" |
| | "fmt" |
| | "internal/buildcfg" |
| | "math/bits" |
| | "slices" |
| | "strings" |
| | ) |
| |
|
| | type SlotID int32 |
| | type VarID int32 |
| |
|
| | |
| | |
| | |
| | type FuncDebug struct { |
| | |
| | Slots []LocalSlot |
| | |
| | Vars []*ir.Name |
| | |
| | VarSlots [][]SlotID |
| | |
| | LocationLists [][]byte |
| | |
| | |
| | RegOutputParams []*ir.Name |
| | |
| | OptDcl []*ir.Name |
| | |
| | |
| | EntryID ID |
| |
|
| | |
| | |
| | |
| | |
| | GetPC func(block, value ID) int64 |
| | } |
| |
|
| | type BlockDebug struct { |
| | |
| | |
| | startState, endState abt.T |
| | |
| | |
| | |
| | lastCheckedTime, lastChangedTime int32 |
| | |
| | relevant bool |
| | |
| | |
| | |
| | everProcessed bool |
| | } |
| |
|
| | |
| | type liveSlot struct { |
| | VarLoc |
| | } |
| |
|
| | func (ls *liveSlot) String() string { |
| | return fmt.Sprintf("0x%x.%d.%d", ls.Registers, ls.stackOffsetValue(), int32(ls.StackOffset)&1) |
| | } |
| |
|
| | |
| | |
| | |
| | type StackOffset int32 |
| |
|
| | func (s StackOffset) onStack() bool { |
| | return s != 0 |
| | } |
| |
|
| | func (s StackOffset) stackOffsetValue() int32 { |
| | return int32(s) >> 1 |
| | } |
| |
|
| | |
| | type stateAtPC struct { |
| | |
| | slots []VarLoc |
| | |
| | registers [][]SlotID |
| | } |
| |
|
| | |
| | func (state *stateAtPC) reset(live abt.T) { |
| | slots, registers := state.slots, state.registers |
| | clear(slots) |
| | for i := range registers { |
| | registers[i] = registers[i][:0] |
| | } |
| | for it := live.Iterator(); !it.Done(); { |
| | k, d := it.Next() |
| | live := d.(*liveSlot) |
| | slots[k] = live.VarLoc |
| | if live.VarLoc.Registers == 0 { |
| | continue |
| | } |
| |
|
| | mask := uint64(live.VarLoc.Registers) |
| | for { |
| | if mask == 0 { |
| | break |
| | } |
| | reg := uint8(bits.TrailingZeros64(mask)) |
| | mask &^= 1 << reg |
| |
|
| | registers[reg] = append(registers[reg], SlotID(k)) |
| | } |
| | } |
| | state.slots, state.registers = slots, registers |
| | } |
| |
|
| | func (s *debugState) LocString(loc VarLoc) string { |
| | if loc.absent() { |
| | return "<nil>" |
| | } |
| |
|
| | var storage []string |
| | if loc.onStack() { |
| | storage = append(storage, fmt.Sprintf("@%+d", loc.stackOffsetValue())) |
| | } |
| |
|
| | mask := uint64(loc.Registers) |
| | for { |
| | if mask == 0 { |
| | break |
| | } |
| | reg := uint8(bits.TrailingZeros64(mask)) |
| | mask &^= 1 << reg |
| |
|
| | storage = append(storage, s.registers[reg].String()) |
| | } |
| | return strings.Join(storage, ",") |
| | } |
| |
|
| | |
| | type VarLoc struct { |
| | |
| | |
| | Registers RegisterSet |
| |
|
| | StackOffset |
| | } |
| |
|
| | func (loc VarLoc) absent() bool { |
| | return loc.Registers == 0 && !loc.onStack() |
| | } |
| |
|
| | func (loc VarLoc) intersect(other VarLoc) VarLoc { |
| | if !loc.onStack() || !other.onStack() || loc.StackOffset != other.StackOffset { |
| | loc.StackOffset = 0 |
| | } |
| | loc.Registers &= other.Registers |
| | return loc |
| | } |
| |
|
| | var BlockStart = &Value{ |
| | ID: -10000, |
| | Op: OpInvalid, |
| | Aux: StringToAux("BlockStart"), |
| | } |
| |
|
| | var BlockEnd = &Value{ |
| | ID: -20000, |
| | Op: OpInvalid, |
| | Aux: StringToAux("BlockEnd"), |
| | } |
| |
|
| | var FuncEnd = &Value{ |
| | ID: -30000, |
| | Op: OpInvalid, |
| | Aux: StringToAux("FuncEnd"), |
| | } |
| |
|
| | |
| | type RegisterSet uint64 |
| |
|
| | |
| | |
| | |
| | func (s *debugState) logf(msg string, args ...any) { |
| | if s.f.PrintOrHtmlSSA { |
| | fmt.Printf(msg, args...) |
| | } |
| | } |
| |
|
| | type debugState struct { |
| | |
| | slots []LocalSlot |
| | vars []*ir.Name |
| | varSlots [][]SlotID |
| | lists [][]byte |
| |
|
| | |
| | slotVars []VarID |
| |
|
| | f *Func |
| | loggingLevel int |
| | convergeCount int |
| | registers []Register |
| | stackOffset func(LocalSlot) int32 |
| | ctxt *obj.Link |
| |
|
| | |
| | valueNames [][]SlotID |
| |
|
| | |
| | currentState stateAtPC |
| | changedVars *sparseSet |
| | changedSlots *sparseSet |
| |
|
| | |
| | pendingEntries []pendingEntry |
| |
|
| | varParts map[*ir.Name][]SlotID |
| | blockDebug []BlockDebug |
| | pendingSlotLocs []VarLoc |
| | } |
| |
|
| | func (state *debugState) initializeCache(f *Func, numVars, numSlots int) { |
| | |
| | if cap(state.blockDebug) < f.NumBlocks() { |
| | state.blockDebug = make([]BlockDebug, f.NumBlocks()) |
| | } else { |
| | clear(state.blockDebug[:f.NumBlocks()]) |
| | } |
| |
|
| | |
| | if cap(state.valueNames) < f.NumValues() { |
| | old := state.valueNames |
| | state.valueNames = make([][]SlotID, f.NumValues()) |
| | copy(state.valueNames, old) |
| | } |
| | vn := state.valueNames[:f.NumValues()] |
| | for i := range vn { |
| | vn[i] = vn[i][:0] |
| | } |
| |
|
| | |
| | if cap(state.currentState.slots) < numSlots { |
| | state.currentState.slots = make([]VarLoc, numSlots) |
| | } else { |
| | state.currentState.slots = state.currentState.slots[:numSlots] |
| | } |
| | if cap(state.currentState.registers) < len(state.registers) { |
| | state.currentState.registers = make([][]SlotID, len(state.registers)) |
| | } else { |
| | state.currentState.registers = state.currentState.registers[:len(state.registers)] |
| | } |
| |
|
| | |
| | state.changedVars = newSparseSet(numVars) |
| | state.changedSlots = newSparseSet(numSlots) |
| |
|
| | |
| | numPieces := 0 |
| | for i := range state.varSlots { |
| | numPieces += len(state.varSlots[i]) |
| | } |
| | if cap(state.pendingSlotLocs) < numPieces { |
| | state.pendingSlotLocs = make([]VarLoc, numPieces) |
| | } else { |
| | clear(state.pendingSlotLocs[:numPieces]) |
| | } |
| | if cap(state.pendingEntries) < numVars { |
| | state.pendingEntries = make([]pendingEntry, numVars) |
| | } |
| | pe := state.pendingEntries[:numVars] |
| | freePieceIdx := 0 |
| | for varID, slots := range state.varSlots { |
| | pe[varID] = pendingEntry{ |
| | pieces: state.pendingSlotLocs[freePieceIdx : freePieceIdx+len(slots)], |
| | } |
| | freePieceIdx += len(slots) |
| | } |
| | state.pendingEntries = pe |
| |
|
| | if cap(state.lists) < numVars { |
| | state.lists = make([][]byte, numVars) |
| | } else { |
| | state.lists = state.lists[:numVars] |
| | clear(state.lists) |
| | } |
| | } |
| |
|
| | func (state *debugState) allocBlock(b *Block) *BlockDebug { |
| | return &state.blockDebug[b.ID] |
| | } |
| |
|
| | func (s *debugState) blockEndStateString(b *BlockDebug) string { |
| | endState := stateAtPC{slots: make([]VarLoc, len(s.slots)), registers: make([][]SlotID, len(s.registers))} |
| | endState.reset(b.endState) |
| | return s.stateString(endState) |
| | } |
| |
|
| | func (s *debugState) stateString(state stateAtPC) string { |
| | var strs []string |
| | for slotID, loc := range state.slots { |
| | if !loc.absent() { |
| | strs = append(strs, fmt.Sprintf("\t%v = %v\n", s.slots[slotID], s.LocString(loc))) |
| | } |
| | } |
| |
|
| | strs = append(strs, "\n") |
| | for reg, slots := range state.registers { |
| | if len(slots) != 0 { |
| | var slotStrs []string |
| | for _, slot := range slots { |
| | slotStrs = append(slotStrs, s.slots[slot].String()) |
| | } |
| | strs = append(strs, fmt.Sprintf("\t%v = %v\n", &s.registers[reg], slotStrs)) |
| | } |
| | } |
| |
|
| | if len(strs) == 1 { |
| | return "(no vars)\n" |
| | } |
| | return strings.Join(strs, "") |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | type slotCanonicalizer struct { |
| | slmap map[slotKey]SlKeyIdx |
| | slkeys []LocalSlot |
| | } |
| |
|
| | func newSlotCanonicalizer() *slotCanonicalizer { |
| | return &slotCanonicalizer{ |
| | slmap: make(map[slotKey]SlKeyIdx), |
| | slkeys: []LocalSlot{LocalSlot{N: nil}}, |
| | } |
| | } |
| |
|
| | type SlKeyIdx uint32 |
| |
|
| | const noSlot = SlKeyIdx(0) |
| |
|
| | |
| | |
| | type slotKey struct { |
| | name *ir.Name |
| | offset int64 |
| | width int64 |
| | splitOf SlKeyIdx |
| | splitOffset int64 |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func (sc *slotCanonicalizer) lookup(ls LocalSlot) (SlKeyIdx, bool) { |
| | split := noSlot |
| | if ls.SplitOf != nil { |
| | split, _ = sc.lookup(*ls.SplitOf) |
| | } |
| | k := slotKey{ |
| | name: ls.N, offset: ls.Off, width: ls.Type.Size(), |
| | splitOf: split, splitOffset: ls.SplitOffset, |
| | } |
| | if idx, ok := sc.slmap[k]; ok { |
| | return idx, true |
| | } |
| | rv := SlKeyIdx(len(sc.slkeys)) |
| | sc.slkeys = append(sc.slkeys, ls) |
| | sc.slmap[k] = rv |
| | return rv, false |
| | } |
| |
|
| | func (sc *slotCanonicalizer) canonSlot(idx SlKeyIdx) LocalSlot { |
| | return sc.slkeys[idx] |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func PopulateABIInRegArgOps(f *Func) { |
| | pri := f.ABISelf.ABIAnalyzeFuncType(f.Type) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | sc := newSlotCanonicalizer() |
| | for _, sl := range f.Names { |
| | sc.lookup(*sl) |
| | } |
| |
|
| | |
| | addToNV := func(v *Value, sl LocalSlot) { |
| | values, ok := f.NamedValues[sl] |
| | if !ok { |
| | |
| | sla := f.localSlotAddr(sl) |
| | f.Names = append(f.Names, sla) |
| | } else { |
| | for _, ev := range values { |
| | if v == ev { |
| | return |
| | } |
| | } |
| | } |
| | values = append(values, v) |
| | f.NamedValues[sl] = values |
| | } |
| |
|
| | newValues := []*Value{} |
| |
|
| | abiRegIndexToRegister := func(reg abi.RegIndex) int8 { |
| | i := f.ABISelf.FloatIndexFor(reg) |
| | if i >= 0 { |
| | return f.Config.floatParamRegs[i] |
| | } else { |
| | return f.Config.intParamRegs[reg] |
| | } |
| | } |
| |
|
| | |
| | var pos src.XPos |
| | if len(f.Entry.Values) != 0 { |
| | pos = f.Entry.Values[0].Pos |
| | } |
| | synthesizeOpIntFloatArg := func(n *ir.Name, t *types.Type, reg abi.RegIndex, sl LocalSlot) *Value { |
| | aux := &AuxNameOffset{n, sl.Off} |
| | op, auxInt := ArgOpAndRegisterFor(reg, f.ABISelf) |
| | v := f.newValueNoBlock(op, t, pos) |
| | v.AuxInt = auxInt |
| | v.Aux = aux |
| | v.Args = nil |
| | v.Block = f.Entry |
| | newValues = append(newValues, v) |
| | addToNV(v, sl) |
| | f.setHome(v, &f.Config.registers[abiRegIndexToRegister(reg)]) |
| | return v |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | for _, v := range f.Entry.Values { |
| | if v.Op == OpArgIntReg || v.Op == OpArgFloatReg { |
| | aux := v.Aux.(*AuxNameOffset) |
| | sl := LocalSlot{N: aux.Name, Type: v.Type, Off: aux.Offset} |
| | |
| | idx, _ := sc.lookup(sl) |
| | |
| | addToNV(v, sc.canonSlot(idx)) |
| | } else if v.Op.IsCall() { |
| | |
| | break |
| | } |
| | } |
| |
|
| | |
| | |
| | for _, inp := range pri.InParams() { |
| | if !isNamedRegParam(inp) { |
| | continue |
| | } |
| | n := inp.Name |
| |
|
| | |
| | |
| | types, offsets := inp.RegisterTypesAndOffsets() |
| | for k, t := range types { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | pieceSlot := LocalSlot{N: n, Type: t, Off: offsets[k]} |
| |
|
| | |
| | |
| | _, found := sc.lookup(pieceSlot) |
| | if !found { |
| | |
| | |
| | |
| | |
| | synthesizeOpIntFloatArg(n, t, inp.Registers[k], |
| | pieceSlot) |
| | } |
| | } |
| | } |
| |
|
| | |
| | f.Entry.Values = append(newValues, f.Entry.Values...) |
| | } |
| |
|
| | |
| | |
| | |
| | func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingLevel int, stackOffset func(LocalSlot) int32, rval *FuncDebug) { |
| | if f.RegAlloc == nil { |
| | f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f) |
| | } |
| | state := &f.Cache.debugState |
| | state.loggingLevel = loggingLevel % 1000 |
| |
|
| | |
| | |
| | |
| | |
| | state.convergeCount = loggingLevel / 1000 |
| | state.f = f |
| | state.registers = f.Config.registers |
| | state.stackOffset = stackOffset |
| | state.ctxt = ctxt |
| |
|
| | if buildcfg.Experiment.RegabiArgs { |
| | PopulateABIInRegArgOps(f) |
| | } |
| |
|
| | if state.loggingLevel > 0 { |
| | state.logf("Generating location lists for function %q\n", f.Name) |
| | } |
| |
|
| | if state.varParts == nil { |
| | state.varParts = make(map[*ir.Name][]SlotID) |
| | } else { |
| | clear(state.varParts) |
| | } |
| |
|
| | |
| | |
| |
|
| | state.slots = state.slots[:0] |
| | state.vars = state.vars[:0] |
| | for i, slot := range f.Names { |
| | state.slots = append(state.slots, *slot) |
| | if ir.IsSynthetic(slot.N) || !IsVarWantedForDebug(slot.N) { |
| | continue |
| | } |
| |
|
| | topSlot := slot |
| | for topSlot.SplitOf != nil { |
| | topSlot = topSlot.SplitOf |
| | } |
| | if _, ok := state.varParts[topSlot.N]; !ok { |
| | state.vars = append(state.vars, topSlot.N) |
| | } |
| | state.varParts[topSlot.N] = append(state.varParts[topSlot.N], SlotID(i)) |
| | } |
| |
|
| | |
| | |
| | for _, b := range f.Blocks { |
| | for _, v := range b.Values { |
| | if v.Op == OpVarDef { |
| | n := v.Aux.(*ir.Name) |
| | if ir.IsSynthetic(n) || !IsVarWantedForDebug(n) { |
| | continue |
| | } |
| |
|
| | if _, ok := state.varParts[n]; !ok { |
| | slot := LocalSlot{N: n, Type: v.Type, Off: 0} |
| | state.slots = append(state.slots, slot) |
| | state.varParts[n] = []SlotID{SlotID(len(state.slots) - 1)} |
| | state.vars = append(state.vars, n) |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | if cap(state.varSlots) < len(state.vars) { |
| | state.varSlots = make([][]SlotID, len(state.vars)) |
| | } else { |
| | state.varSlots = state.varSlots[:len(state.vars)] |
| | for i := range state.varSlots { |
| | state.varSlots[i] = state.varSlots[i][:0] |
| | } |
| | } |
| | if cap(state.slotVars) < len(state.slots) { |
| | state.slotVars = make([]VarID, len(state.slots)) |
| | } else { |
| | state.slotVars = state.slotVars[:len(state.slots)] |
| | } |
| |
|
| | for varID, n := range state.vars { |
| | parts := state.varParts[n] |
| | slices.SortFunc(parts, func(a, b SlotID) int { |
| | return cmp.Compare(varOffset(state.slots[a]), varOffset(state.slots[b])) |
| | }) |
| |
|
| | state.varSlots[varID] = parts |
| | for _, slotID := range parts { |
| | state.slotVars[slotID] = VarID(varID) |
| | } |
| | } |
| |
|
| | state.initializeCache(f, len(state.varParts), len(state.slots)) |
| |
|
| | for i, slot := range f.Names { |
| | if ir.IsSynthetic(slot.N) || !IsVarWantedForDebug(slot.N) { |
| | continue |
| | } |
| | for _, value := range f.NamedValues[*slot] { |
| | state.valueNames[value.ID] = append(state.valueNames[value.ID], SlotID(i)) |
| | } |
| | } |
| |
|
| | blockLocs := state.liveness() |
| | state.buildLocationLists(blockLocs) |
| |
|
| | |
| | rval.Slots = state.slots |
| | rval.VarSlots = state.varSlots |
| | rval.Vars = state.vars |
| | rval.LocationLists = state.lists |
| | } |
| |
|
| | |
| | |
| | func (state *debugState) liveness() []*BlockDebug { |
| | blockLocs := make([]*BlockDebug, state.f.NumBlocks()) |
| | counterTime := int32(1) |
| |
|
| | |
| | |
| | po := state.f.Postorder() |
| | converged := false |
| |
|
| | |
| | |
| | |
| | |
| | |
| | keepGoing := func(k int) bool { |
| | if state.convergeCount == 0 { |
| | return !converged |
| | } |
| | return k < state.convergeCount |
| | } |
| | for k := 0; keepGoing(k); k++ { |
| | if state.loggingLevel > 0 { |
| | state.logf("Liveness pass %d\n", k) |
| | } |
| | converged = true |
| | for i := len(po) - 1; i >= 0; i-- { |
| | b := po[i] |
| | locs := blockLocs[b.ID] |
| | if locs == nil { |
| | locs = state.allocBlock(b) |
| | blockLocs[b.ID] = locs |
| | } |
| |
|
| | |
| | |
| | startState, blockChanged := state.mergePredecessors(b, blockLocs, nil, false) |
| | locs.lastCheckedTime = counterTime |
| | counterTime++ |
| | if state.loggingLevel > 1 { |
| | state.logf("Processing %v, block changed %v, initial state:\n%v", b, blockChanged, state.stateString(state.currentState)) |
| | } |
| |
|
| | if blockChanged { |
| | |
| | converged = false |
| | changed := false |
| | state.changedSlots.clear() |
| |
|
| | |
| | for _, v := range b.Values { |
| | slots := state.valueNames[v.ID] |
| |
|
| | |
| | var source *Value |
| | switch v.Op { |
| | case OpStoreReg: |
| | source = v.Args[0] |
| | case OpLoadReg: |
| | switch a := v.Args[0]; a.Op { |
| | case OpArg, OpPhi: |
| | source = a |
| | case OpStoreReg: |
| | source = a.Args[0] |
| | default: |
| | if state.loggingLevel > 1 { |
| | state.logf("at %v: load with unexpected source op: %v (%v)\n", v, a.Op, a) |
| | } |
| | } |
| | } |
| | |
| | |
| | if source != nil && k == 0 { |
| | |
| | slots = append(slots, state.valueNames[source.ID]...) |
| | state.valueNames[v.ID] = slots |
| | } |
| |
|
| | reg, _ := state.f.getHome(v.ID).(*Register) |
| | c := state.processValue(v, slots, reg) |
| | changed = changed || c |
| | } |
| |
|
| | if state.loggingLevel > 1 { |
| | state.logf("Block %v done, locs:\n%v", b, state.stateString(state.currentState)) |
| | } |
| |
|
| | locs.relevant = locs.relevant || changed |
| | if !changed { |
| | locs.endState = startState |
| | } else { |
| | for _, id := range state.changedSlots.contents() { |
| | slotID := SlotID(id) |
| | slotLoc := state.currentState.slots[slotID] |
| | if slotLoc.absent() { |
| | startState.Delete(int32(slotID)) |
| | continue |
| | } |
| | old := startState.Find(int32(slotID)) |
| | if oldLS, ok := old.(*liveSlot); !ok || oldLS.VarLoc != slotLoc { |
| | startState.Insert(int32(slotID), |
| | &liveSlot{VarLoc: slotLoc}) |
| | } |
| | } |
| | locs.endState = startState |
| | } |
| | locs.lastChangedTime = counterTime |
| | } |
| | counterTime++ |
| | } |
| | } |
| | return blockLocs |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug, previousBlock *Block, forLocationLists bool) (abt.T, bool) { |
| | |
| | var predsBuf [10]*Block |
| |
|
| | preds := predsBuf[:0] |
| | locs := blockLocs[b.ID] |
| |
|
| | blockChanged := !locs.everProcessed |
| | updating := locs.everProcessed |
| |
|
| | |
| | |
| | for _, pred := range b.Preds { |
| | if bl := blockLocs[pred.b.ID]; bl != nil && bl.everProcessed { |
| | |
| | preds = append(preds, pred.b) |
| | } |
| | } |
| |
|
| | locs.everProcessed = true |
| |
|
| | if state.loggingLevel > 1 { |
| | |
| | |
| | preds2 := make([]*Block, len(preds)) |
| | copy(preds2, preds) |
| | state.logf("Merging %v into %v (changed=%d, checked=%d)\n", preds2, b, locs.lastChangedTime, locs.lastCheckedTime) |
| | } |
| |
|
| | state.changedVars.clear() |
| |
|
| | markChangedVars := func(slots, merged abt.T) { |
| | if !forLocationLists { |
| | return |
| | } |
| | |
| | |
| | |
| | for it := slots.Iterator(); !it.Done(); { |
| | k, v := it.Next() |
| | m := merged.Find(k) |
| | if m == nil || v.(*liveSlot).VarLoc != m.(*liveSlot).VarLoc { |
| | state.changedVars.add(ID(state.slotVars[k])) |
| | } |
| | } |
| | } |
| |
|
| | reset := func(ourStartState abt.T) { |
| | if !(forLocationLists || blockChanged) { |
| | |
| | |
| | |
| | return |
| | } |
| | state.currentState.reset(ourStartState) |
| | } |
| |
|
| | |
| | if len(preds) == 0 { |
| | if previousBlock != nil { |
| | state.f.Fatalf("Function %v, block %s with no predecessors is not first block, has previous %s", state.f, b.String(), previousBlock.String()) |
| | } |
| | |
| | reset(abt.T{}) |
| | return abt.T{}, blockChanged |
| | } |
| |
|
| | |
| | l0 := blockLocs[preds[0].ID] |
| | p0 := l0.endState |
| | if len(preds) == 1 { |
| | if previousBlock != nil && preds[0].ID != previousBlock.ID { |
| | |
| | markChangedVars(blockLocs[previousBlock.ID].endState, p0) |
| | } |
| | locs.startState = p0 |
| | blockChanged = blockChanged || l0.lastChangedTime > locs.lastCheckedTime |
| | reset(p0) |
| | return p0, blockChanged |
| | } |
| |
|
| | |
| |
|
| | if updating { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | for i := len(preds) - 1; i >= 0; i-- { |
| | pred := preds[i] |
| | if blockLocs[pred.ID].lastChangedTime > locs.lastCheckedTime { |
| | continue |
| | } |
| | preds[i] = preds[len(preds)-1] |
| | preds = preds[:len(preds)-1] |
| | if state.loggingLevel > 2 { |
| | state.logf("Pruned b%d, lastChanged was %d but b%d lastChecked is %d\n", pred.ID, blockLocs[pred.ID].lastChangedTime, b.ID, locs.lastCheckedTime) |
| | } |
| | } |
| | |
| | |
| | if len(preds) == 0 { |
| | blockChanged = false |
| |
|
| | reset(locs.startState) |
| | if state.loggingLevel > 2 { |
| | state.logf("Early out, no predecessors changed since last check\n") |
| | } |
| | if previousBlock != nil { |
| | markChangedVars(blockLocs[previousBlock.ID].endState, locs.startState) |
| | } |
| | return locs.startState, blockChanged |
| | } |
| | } |
| |
|
| | baseID := preds[0].ID |
| | baseState := p0 |
| |
|
| | |
| | for _, pred := range preds[1:] { |
| | if blockLocs[pred.ID].endState.Size() < baseState.Size() { |
| | baseState = blockLocs[pred.ID].endState |
| | baseID = pred.ID |
| | } |
| | } |
| |
|
| | if state.loggingLevel > 2 { |
| | state.logf("Starting %v with state from b%v:\n%v", b, baseID, state.blockEndStateString(blockLocs[baseID])) |
| | for _, pred := range preds { |
| | if pred.ID == baseID { |
| | continue |
| | } |
| | state.logf("Merging in state from %v:\n%v", pred, state.blockEndStateString(blockLocs[pred.ID])) |
| | } |
| | } |
| |
|
| | state.currentState.reset(abt.T{}) |
| | |
| |
|
| | slotLocs := state.currentState.slots |
| |
|
| | |
| | |
| | |
| | |
| | newState := baseState |
| | if updating { |
| | newState = blockLocs[b.ID].startState |
| | } |
| |
|
| | for it := newState.Iterator(); !it.Done(); { |
| | k, d := it.Next() |
| | thisSlot := d.(*liveSlot) |
| | x := thisSlot.VarLoc |
| | x0 := x |
| |
|
| | |
| | for _, other := range preds { |
| | if !updating && other.ID == baseID { |
| | continue |
| | } |
| | otherSlot := blockLocs[other.ID].endState.Find(k) |
| | if otherSlot == nil { |
| | x = VarLoc{} |
| | break |
| | } |
| | y := otherSlot.(*liveSlot).VarLoc |
| | x = x.intersect(y) |
| | if x.absent() { |
| | x = VarLoc{} |
| | break |
| | } |
| | } |
| |
|
| | |
| | if x.absent() { |
| | if !x0.absent() { |
| | blockChanged = true |
| | newState.Delete(k) |
| | } |
| | slotLocs[k] = VarLoc{} |
| | continue |
| | } |
| | if x != x0 { |
| | blockChanged = true |
| | newState.Insert(k, &liveSlot{VarLoc: x}) |
| | } |
| |
|
| | slotLocs[k] = x |
| | mask := uint64(x.Registers) |
| | for { |
| | if mask == 0 { |
| | break |
| | } |
| | reg := uint8(bits.TrailingZeros64(mask)) |
| | mask &^= 1 << reg |
| | state.currentState.registers[reg] = append(state.currentState.registers[reg], SlotID(k)) |
| | } |
| | } |
| |
|
| | if previousBlock != nil { |
| | markChangedVars(blockLocs[previousBlock.ID].endState, newState) |
| | } |
| | locs.startState = newState |
| | return newState, blockChanged |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) bool { |
| | locs := state.currentState |
| | changed := false |
| | setSlot := func(slot SlotID, loc VarLoc) { |
| | changed = true |
| | state.changedVars.add(ID(state.slotVars[slot])) |
| | state.changedSlots.add(ID(slot)) |
| | state.currentState.slots[slot] = loc |
| | } |
| |
|
| | |
| | |
| | |
| | clobbers := uint64(opcodeTable[v.Op].reg.clobbers) |
| | for { |
| | if clobbers == 0 { |
| | break |
| | } |
| | reg := uint8(bits.TrailingZeros64(clobbers)) |
| | clobbers &^= 1 << reg |
| |
|
| | for _, slot := range locs.registers[reg] { |
| | if state.loggingLevel > 1 { |
| | state.logf("at %v: %v clobbered out of %v\n", v, state.slots[slot], &state.registers[reg]) |
| | } |
| |
|
| | last := locs.slots[slot] |
| | if last.absent() { |
| | state.f.Fatalf("at %v: slot %v in register %v with no location entry", v, state.slots[slot], &state.registers[reg]) |
| | continue |
| | } |
| | regs := last.Registers &^ (1 << reg) |
| | setSlot(slot, VarLoc{regs, last.StackOffset}) |
| | } |
| |
|
| | locs.registers[reg] = locs.registers[reg][:0] |
| | } |
| |
|
| | switch { |
| | case v.Op == OpVarDef: |
| | n := v.Aux.(*ir.Name) |
| | if ir.IsSynthetic(n) || !IsVarWantedForDebug(n) { |
| | break |
| | } |
| |
|
| | slotID := state.varParts[n][0] |
| | var stackOffset StackOffset |
| | if v.Op == OpVarDef { |
| | stackOffset = StackOffset(state.stackOffset(state.slots[slotID])<<1 | 1) |
| | } |
| | setSlot(slotID, VarLoc{0, stackOffset}) |
| | if state.loggingLevel > 1 { |
| | if v.Op == OpVarDef { |
| | state.logf("at %v: stack-only var %v now live\n", v, state.slots[slotID]) |
| | } else { |
| | state.logf("at %v: stack-only var %v now dead\n", v, state.slots[slotID]) |
| | } |
| | } |
| |
|
| | case v.Op == OpArg: |
| | home := state.f.getHome(v.ID).(LocalSlot) |
| | stackOffset := state.stackOffset(home)<<1 | 1 |
| | for _, slot := range vSlots { |
| | if state.loggingLevel > 1 { |
| | state.logf("at %v: arg %v now on stack in location %v\n", v, state.slots[slot], home) |
| | if last := locs.slots[slot]; !last.absent() { |
| | state.logf("at %v: unexpected arg op on already-live slot %v\n", v, state.slots[slot]) |
| | } |
| | } |
| |
|
| | setSlot(slot, VarLoc{0, StackOffset(stackOffset)}) |
| | } |
| |
|
| | case v.Op == OpStoreReg: |
| | home := state.f.getHome(v.ID).(LocalSlot) |
| | stackOffset := state.stackOffset(home)<<1 | 1 |
| | for _, slot := range vSlots { |
| | last := locs.slots[slot] |
| | if last.absent() { |
| | if state.loggingLevel > 1 { |
| | state.logf("at %v: unexpected spill of unnamed register %s\n", v, vReg) |
| | } |
| | break |
| | } |
| |
|
| | setSlot(slot, VarLoc{last.Registers, StackOffset(stackOffset)}) |
| | if state.loggingLevel > 1 { |
| | state.logf("at %v: %v spilled to stack location %v@%d\n", v, state.slots[slot], home, state.stackOffset(home)) |
| | } |
| | } |
| |
|
| | case vReg != nil: |
| | if state.loggingLevel > 1 { |
| | newSlots := make([]bool, len(state.slots)) |
| | for _, slot := range vSlots { |
| | newSlots[slot] = true |
| | } |
| |
|
| | for _, slot := range locs.registers[vReg.num] { |
| | if !newSlots[slot] { |
| | state.logf("at %v: overwrote %v in register %v\n", v, state.slots[slot], vReg) |
| | } |
| | } |
| | } |
| |
|
| | for _, slot := range locs.registers[vReg.num] { |
| | last := locs.slots[slot] |
| | setSlot(slot, VarLoc{last.Registers &^ (1 << uint8(vReg.num)), last.StackOffset}) |
| | } |
| | locs.registers[vReg.num] = locs.registers[vReg.num][:0] |
| | locs.registers[vReg.num] = append(locs.registers[vReg.num], vSlots...) |
| | for _, slot := range vSlots { |
| | if state.loggingLevel > 1 { |
| | state.logf("at %v: %v now in %s\n", v, state.slots[slot], vReg) |
| | } |
| |
|
| | last := locs.slots[slot] |
| | setSlot(slot, VarLoc{1<<uint8(vReg.num) | last.Registers, last.StackOffset}) |
| | } |
| | } |
| | return changed |
| | } |
| |
|
| | |
| | |
| | func varOffset(slot LocalSlot) int64 { |
| | offset := slot.Off |
| | s := &slot |
| | for ; s.SplitOf != nil; s = s.SplitOf { |
| | offset += s.SplitOffset |
| | } |
| | return offset |
| | } |
| |
|
| | |
| | |
| | type pendingEntry struct { |
| | present bool |
| | startBlock, startValue ID |
| | |
| | |
| | pieces []VarLoc |
| | } |
| |
|
| | func (e *pendingEntry) clear() { |
| | e.present = false |
| | e.startBlock = 0 |
| | e.startValue = 0 |
| | clear(e.pieces) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func canMerge(pending, new VarLoc) bool { |
| | if pending.absent() && new.absent() { |
| | return true |
| | } |
| | if pending.absent() || new.absent() { |
| | return false |
| | } |
| | |
| | |
| | if pending.onStack() && pending.StackOffset != new.StackOffset { |
| | |
| | |
| | return false |
| | } |
| | if pending.Registers&new.Registers != pending.Registers { |
| | |
| | return false |
| | } |
| | return true |
| | } |
| |
|
| | |
| | func firstReg(set RegisterSet) uint8 { |
| | if set == 0 { |
| | |
| | |
| | return 0 |
| | } |
| | return uint8(bits.TrailingZeros64(uint64(set))) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) { |
| | |
| | |
| |
|
| | var prevBlock *Block |
| | for _, b := range state.f.Blocks { |
| | state.mergePredecessors(b, blockLocs, prevBlock, true) |
| |
|
| | |
| | for _, varID := range state.changedVars.contents() { |
| | state.updateVar(VarID(varID), b, BlockStart) |
| | } |
| | state.changedVars.clear() |
| |
|
| | if !blockLocs[b.ID].relevant { |
| | continue |
| | } |
| |
|
| | mustBeFirst := func(v *Value) bool { |
| | return v.Op == OpPhi || v.Op.isLoweredGetClosurePtr() || |
| | v.Op == OpArgIntReg || v.Op == OpArgFloatReg |
| | } |
| |
|
| | blockPrologComplete := func(v *Value) bool { |
| | if b.ID != state.f.Entry.ID { |
| | return !opcodeTable[v.Op].zeroWidth |
| | } else { |
| | return v.Op == OpInitMem |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | for idx := 0; idx < len(b.Values); idx++ { |
| | v := b.Values[idx] |
| | if blockPrologComplete(v) { |
| | break |
| | } |
| | |
| | if !mustBeFirst(v) && v.Op != OpArg { |
| | continue |
| | } |
| | slots := state.valueNames[v.ID] |
| | reg, _ := state.f.getHome(v.ID).(*Register) |
| | changed := state.processValue(v, slots, reg) |
| | if changed { |
| | for _, varID := range state.changedVars.contents() { |
| | state.updateVar(VarID(varID), v.Block, BlockStart) |
| | } |
| | state.changedVars.clear() |
| | } |
| | } |
| |
|
| | |
| | |
| | zeroWidthPending := false |
| | prologComplete := false |
| | |
| | for _, v := range b.Values { |
| | if blockPrologComplete(v) { |
| | prologComplete = true |
| | } |
| | slots := state.valueNames[v.ID] |
| | reg, _ := state.f.getHome(v.ID).(*Register) |
| | changed := state.processValue(v, slots, reg) |
| |
|
| | if opcodeTable[v.Op].zeroWidth { |
| | if prologComplete && mustBeFirst(v) { |
| | panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func)) |
| | } |
| | if changed { |
| | if mustBeFirst(v) || v.Op == OpArg { |
| | |
| | continue |
| | } |
| | zeroWidthPending = true |
| | } |
| | continue |
| | } |
| | if !changed && !zeroWidthPending { |
| | continue |
| | } |
| |
|
| | |
| | zeroWidthPending = false |
| | for _, varID := range state.changedVars.contents() { |
| | state.updateVar(VarID(varID), v.Block, v) |
| | } |
| | state.changedVars.clear() |
| | } |
| | for _, varID := range state.changedVars.contents() { |
| | state.updateVar(VarID(varID), b, BlockEnd) |
| | } |
| |
|
| | prevBlock = b |
| | } |
| |
|
| | if state.loggingLevel > 0 { |
| | state.logf("location lists:\n") |
| | } |
| |
|
| | |
| | for varID := range state.lists { |
| | state.writePendingEntry(VarID(varID), -1, FuncEnd.ID) |
| | list := state.lists[varID] |
| | if state.loggingLevel > 0 { |
| | if len(list) == 0 { |
| | state.logf("\t%v : empty list\n", state.vars[varID]) |
| | } else { |
| | state.logf("\t%v : %q\n", state.vars[varID], hex.EncodeToString(state.lists[varID])) |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func (state *debugState) updateVar(varID VarID, b *Block, v *Value) { |
| | curLoc := state.currentState.slots |
| | |
| | empty := true |
| | for _, slotID := range state.varSlots[varID] { |
| | if !curLoc[slotID].absent() { |
| | empty = false |
| | break |
| | } |
| | } |
| | pending := &state.pendingEntries[varID] |
| | if empty { |
| | state.writePendingEntry(varID, b.ID, v.ID) |
| | pending.clear() |
| | return |
| | } |
| |
|
| | |
| | if pending.present { |
| | merge := true |
| | for i, slotID := range state.varSlots[varID] { |
| | if !canMerge(pending.pieces[i], curLoc[slotID]) { |
| | merge = false |
| | break |
| | } |
| | } |
| | if merge { |
| | return |
| | } |
| | } |
| |
|
| | state.writePendingEntry(varID, b.ID, v.ID) |
| | pending.present = true |
| | pending.startBlock = b.ID |
| | pending.startValue = v.ID |
| | for i, slot := range state.varSlots[varID] { |
| | pending.pieces[i] = curLoc[slot] |
| | } |
| | } |
| |
|
| | |
| | |
| | func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) { |
| | pending := state.pendingEntries[varID] |
| | if !pending.present { |
| | return |
| | } |
| |
|
| | |
| | |
| | start, startOK := encodeValue(state.ctxt, pending.startBlock, pending.startValue) |
| | end, endOK := encodeValue(state.ctxt, endBlock, endValue) |
| | if !startOK || !endOK { |
| | |
| | |
| | return |
| | } |
| | if start == end { |
| | if state.loggingLevel > 1 { |
| | |
| | |
| | state.logf("Skipping empty location list for %v in %s\n", state.vars[varID], state.f.Name) |
| | } |
| | return |
| | } |
| |
|
| | list := state.lists[varID] |
| | list = appendPtr(state.ctxt, list, start) |
| | list = appendPtr(state.ctxt, list, end) |
| | |
| | |
| | sizeIdx := len(list) |
| | list = list[:len(list)+2] |
| |
|
| | if state.loggingLevel > 1 { |
| | var partStrs []string |
| | for i, slot := range state.varSlots[varID] { |
| | partStrs = append(partStrs, fmt.Sprintf("%v@%v", state.slots[slot], state.LocString(pending.pieces[i]))) |
| | } |
| | state.logf("Add entry for %v: \tb%vv%v-b%vv%v = \t%v\n", state.vars[varID], pending.startBlock, pending.startValue, endBlock, endValue, strings.Join(partStrs, " ")) |
| | } |
| |
|
| | for i, slotID := range state.varSlots[varID] { |
| | loc := pending.pieces[i] |
| | slot := state.slots[slotID] |
| |
|
| | if !loc.absent() { |
| | if loc.onStack() { |
| | if loc.stackOffsetValue() == 0 { |
| | list = append(list, dwarf.DW_OP_call_frame_cfa) |
| | } else { |
| | list = append(list, dwarf.DW_OP_fbreg) |
| | list = dwarf.AppendSleb128(list, int64(loc.stackOffsetValue())) |
| | } |
| | } else { |
| | regnum := state.ctxt.Arch.DWARFRegisters[state.registers[firstReg(loc.Registers)].ObjNum()] |
| | if regnum < 32 { |
| | list = append(list, dwarf.DW_OP_reg0+byte(regnum)) |
| | } else { |
| | list = append(list, dwarf.DW_OP_regx) |
| | list = dwarf.AppendUleb128(list, uint64(regnum)) |
| | } |
| | } |
| | } |
| |
|
| | if len(state.varSlots[varID]) > 1 { |
| | list = append(list, dwarf.DW_OP_piece) |
| | list = dwarf.AppendUleb128(list, uint64(slot.Type.Size())) |
| | } |
| | } |
| | state.ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2)) |
| | state.lists[varID] = list |
| | } |
| |
|
| | |
| | |
| | func (debugInfo *FuncDebug) PutLocationList(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) { |
| | if buildcfg.Experiment.Dwarf5 { |
| | debugInfo.PutLocationListDwarf5(list, ctxt, listSym, startPC) |
| | } else { |
| | debugInfo.PutLocationListDwarf4(list, ctxt, listSym, startPC) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func (debugInfo *FuncDebug) PutLocationListDwarf5(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) { |
| | getPC := debugInfo.GetPC |
| |
|
| | |
| | listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_base_addressx) |
| | listSym.WriteDwTxtAddrx(ctxt, listSym.Size, startPC, ctxt.DwTextCount*2) |
| |
|
| | var stbuf, enbuf [10]byte |
| | stb, enb := stbuf[:], enbuf[:] |
| | |
| | for i := 0; i < len(list); { |
| | begin := getPC(decodeValue(ctxt, readPtr(ctxt, list[i:]))) |
| | end := getPC(decodeValue(ctxt, readPtr(ctxt, list[i+ctxt.Arch.PtrSize:]))) |
| |
|
| | |
| | |
| | listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_offset_pair) |
| | stb, enb = stb[:0], enb[:0] |
| | stb = dwarf.AppendUleb128(stb, uint64(begin)) |
| | enb = dwarf.AppendUleb128(enb, uint64(end)) |
| | listSym.WriteBytes(ctxt, listSym.Size, stb) |
| | listSym.WriteBytes(ctxt, listSym.Size, enb) |
| |
|
| | |
| | |
| | |
| | i += 2 * ctxt.Arch.PtrSize |
| | datalen := int(ctxt.Arch.ByteOrder.Uint16(list[i:])) |
| | i += 2 |
| | stb = stb[:0] |
| | stb = dwarf.AppendUleb128(stb, uint64(datalen)) |
| | listSym.WriteBytes(ctxt, listSym.Size, stb) |
| | listSym.WriteBytes(ctxt, listSym.Size, list[i:i+datalen]) |
| |
|
| | i += datalen |
| | } |
| |
|
| | |
| | listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_end_of_list) |
| | } |
| |
|
| | |
| | |
| | func (debugInfo *FuncDebug) PutLocationListDwarf4(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) { |
| | getPC := debugInfo.GetPC |
| |
|
| | if ctxt.UseBASEntries { |
| | listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, ^0) |
| | listSym.WriteAddr(ctxt, listSym.Size, ctxt.Arch.PtrSize, startPC, 0) |
| | } |
| |
|
| | |
| | for i := 0; i < len(list); { |
| | begin := getPC(decodeValue(ctxt, readPtr(ctxt, list[i:]))) |
| | end := getPC(decodeValue(ctxt, readPtr(ctxt, list[i+ctxt.Arch.PtrSize:]))) |
| |
|
| | |
| | |
| | |
| | |
| | if begin == 0 && end == 0 { |
| | end = 1 |
| | } |
| |
|
| | if ctxt.UseBASEntries { |
| | listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, begin) |
| | listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, end) |
| | } else { |
| | listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, begin) |
| | listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, end) |
| | } |
| |
|
| | i += 2 * ctxt.Arch.PtrSize |
| | datalen := 2 + int(ctxt.Arch.ByteOrder.Uint16(list[i:])) |
| | listSym.WriteBytes(ctxt, listSym.Size, list[i:i+datalen]) |
| | i += datalen |
| | } |
| |
|
| | |
| | |
| | listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0) |
| | listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | func encodeValue(ctxt *obj.Link, b, v ID) (uint64, bool) { |
| | if ctxt.Arch.PtrSize == 8 { |
| | result := uint64(b)<<32 | uint64(uint32(v)) |
| | |
| | return result, true |
| | } |
| | if ctxt.Arch.PtrSize != 4 { |
| | panic("unexpected pointer size") |
| | } |
| | if ID(int16(b)) != b || ID(int16(v)) != v { |
| | return 0, false |
| | } |
| | return uint64(b)<<16 | uint64(uint16(v)), true |
| | } |
| |
|
| | |
| | func decodeValue(ctxt *obj.Link, word uint64) (ID, ID) { |
| | if ctxt.Arch.PtrSize == 8 { |
| | b, v := ID(word>>32), ID(word) |
| | |
| | return b, v |
| | } |
| | if ctxt.Arch.PtrSize != 4 { |
| | panic("unexpected pointer size") |
| | } |
| | return ID(word >> 16), ID(int16(word)) |
| | } |
| |
|
| | |
| | func appendPtr(ctxt *obj.Link, buf []byte, word uint64) []byte { |
| | if cap(buf) < len(buf)+20 { |
| | b := make([]byte, len(buf), 20+cap(buf)*2) |
| | copy(b, buf) |
| | buf = b |
| | } |
| | writeAt := len(buf) |
| | buf = buf[0 : len(buf)+ctxt.Arch.PtrSize] |
| | writePtr(ctxt, buf[writeAt:], word) |
| | return buf |
| | } |
| |
|
| | |
| | func writePtr(ctxt *obj.Link, buf []byte, word uint64) { |
| | switch ctxt.Arch.PtrSize { |
| | case 4: |
| | ctxt.Arch.ByteOrder.PutUint32(buf, uint32(word)) |
| | case 8: |
| | ctxt.Arch.ByteOrder.PutUint64(buf, word) |
| | default: |
| | panic("unexpected pointer size") |
| | } |
| |
|
| | } |
| |
|
| | |
| | func readPtr(ctxt *obj.Link, buf []byte) uint64 { |
| | switch ctxt.Arch.PtrSize { |
| | case 4: |
| | return uint64(ctxt.Arch.ByteOrder.Uint32(buf)) |
| | case 8: |
| | return ctxt.Arch.ByteOrder.Uint64(buf) |
| | default: |
| | panic("unexpected pointer size") |
| | } |
| |
|
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func SetupLocList(ctxt *obj.Link, entryID ID, list []byte, st, en ID) ([]byte, int) { |
| | start, startOK := encodeValue(ctxt, entryID, st) |
| | end, endOK := encodeValue(ctxt, entryID, en) |
| | if !startOK || !endOK { |
| | |
| | |
| | |
| | return nil, 0 |
| | } |
| | list = appendPtr(ctxt, list, start) |
| | list = appendPtr(ctxt, list, end) |
| |
|
| | |
| | |
| | sizeIdx := len(list) |
| | list = list[:len(list)+2] |
| | return list, sizeIdx |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func locatePrologEnd(f *Func, needCloCtx bool) (ID, *Value) { |
| |
|
| | |
| | |
| | |
| | isRegMoveLike := func(v *Value) (bool, ID) { |
| | n, ok := v.Aux.(*ir.Name) |
| | var r ID |
| | if (!ok || n.Class != ir.PPARAM) && !needCloCtx { |
| | return false, r |
| | } |
| | regInputs, memInputs, spInputs := 0, 0, 0 |
| | for _, a := range v.Args { |
| | if a.Op == OpArgIntReg || a.Op == OpArgFloatReg || |
| | (needCloCtx && a.Op.isLoweredGetClosurePtr()) { |
| | regInputs++ |
| | r = a.ID |
| | } else if a.Type.IsMemory() { |
| | memInputs++ |
| | } else if a.Op == OpSP { |
| | spInputs++ |
| | } else { |
| | return false, r |
| | } |
| | } |
| | return v.Type.IsMemory() && memInputs == 1 && |
| | regInputs == 1 && spInputs == 1, r |
| | } |
| |
|
| | |
| | |
| | regArgs := make([]ID, 0, 32) |
| |
|
| | |
| | |
| | removeReg := func(r ID) bool { |
| | for i := 0; i < len(regArgs); i++ { |
| | if regArgs[i] == r { |
| | regArgs = slices.Delete(regArgs, i, i+1) |
| | return true |
| | } |
| | } |
| | return false |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | var cloRegStore *Value |
| | for k, v := range f.Entry.Values { |
| | if v.Op == OpArgIntReg || v.Op == OpArgFloatReg { |
| | regArgs = append(regArgs, v.ID) |
| | continue |
| | } |
| | if needCloCtx && v.Op.isLoweredGetClosurePtr() { |
| | regArgs = append(regArgs, v.ID) |
| | cloRegStore = v |
| | continue |
| | } |
| | if ok, r := isRegMoveLike(v); ok { |
| | if removed := removeReg(r); removed { |
| | if len(regArgs) == 0 { |
| | |
| | |
| | |
| | |
| | if k < len(f.Entry.Values)-1 { |
| | return f.Entry.Values[k+1].ID, cloRegStore |
| | } |
| | return BlockEnd.ID, cloRegStore |
| | } |
| | } |
| | } |
| | if v.Op.IsCall() { |
| | |
| | return v.ID, cloRegStore |
| | } |
| | } |
| | |
| | return ID(-1), cloRegStore |
| | } |
| |
|
| | |
| | |
| | |
| | func isNamedRegParam(p abi.ABIParamAssignment) bool { |
| | if p.Name == nil { |
| | return false |
| | } |
| | n := p.Name |
| | if n.Sym() == nil || n.Sym().IsBlank() { |
| | return false |
| | } |
| | if len(p.Registers) == 0 { |
| | return false |
| | } |
| | return true |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) { |
| | needCloCtx := f.CloSlot != nil |
| | pri := f.ABISelf.ABIAnalyzeFuncType(f.Type) |
| |
|
| | |
| | |
| | |
| | |
| | numRegParams := 0 |
| | for _, inp := range pri.InParams() { |
| | if isNamedRegParam(inp) { |
| | numRegParams++ |
| | } |
| | } |
| | if numRegParams == 0 && !needCloCtx { |
| | return |
| | } |
| |
|
| | state := debugState{f: f} |
| |
|
| | if loggingEnabled { |
| | state.logf("generating -N reg param loc lists for func %q\n", f.Name) |
| | } |
| |
|
| | |
| | |
| | var cloReg int16 |
| |
|
| | extraForCloCtx := 0 |
| | if needCloCtx { |
| | extraForCloCtx = 1 |
| | } |
| |
|
| | |
| | rval.LocationLists = make([][]byte, numRegParams+extraForCloCtx) |
| |
|
| | |
| | |
| | afterPrologVal, cloRegStore := locatePrologEnd(f, needCloCtx) |
| |
|
| | if needCloCtx { |
| | reg, _ := state.f.getHome(cloRegStore.ID).(*Register) |
| | cloReg = reg.ObjNum() |
| | if loggingEnabled { |
| | state.logf("needCloCtx is true for func %q, cloreg=%v\n", |
| | f.Name, reg) |
| | } |
| | } |
| |
|
| | addVarSlot := func(name *ir.Name, typ *types.Type) { |
| | sl := LocalSlot{N: name, Type: typ, Off: 0} |
| | rval.Vars = append(rval.Vars, name) |
| | rval.Slots = append(rval.Slots, sl) |
| | slid := len(rval.VarSlots) |
| | rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)}) |
| | } |
| |
|
| | |
| | |
| | |
| | params := []abi.ABIParamAssignment{} |
| | for _, inp := range pri.InParams() { |
| | if !isNamedRegParam(inp) { |
| | |
| | continue |
| | } |
| | if !IsVarWantedForDebug(inp.Name) { |
| | continue |
| | } |
| | addVarSlot(inp.Name, inp.Type) |
| | params = append(params, inp) |
| | } |
| | if needCloCtx { |
| | addVarSlot(f.CloSlot, f.CloSlot.Type()) |
| | cloAssign := abi.ABIParamAssignment{ |
| | Type: f.CloSlot.Type(), |
| | Name: f.CloSlot, |
| | Registers: []abi.RegIndex{0}, |
| | } |
| | params = append(params, cloAssign) |
| | } |
| |
|
| | |
| | pidx := 0 |
| | for _, inp := range params { |
| | if !isNamedRegParam(inp) { |
| | |
| | continue |
| | } |
| | if !IsVarWantedForDebug(inp.Name) { |
| | continue |
| | } |
| |
|
| | sl := rval.Slots[pidx] |
| | n := rval.Vars[pidx] |
| |
|
| | if afterPrologVal == ID(-1) { |
| | |
| | |
| | |
| | |
| | if loggingEnabled { |
| | state.logf("locatePrologEnd failed, skipping %v\n", n) |
| | } |
| | pidx++ |
| | continue |
| | } |
| |
|
| | |
| | |
| | |
| | list, sizeIdx := SetupLocList(ctxt, f.Entry.ID, rval.LocationLists[pidx], |
| | BlockStart.ID, afterPrologVal) |
| | if list == nil { |
| | pidx++ |
| | continue |
| | } |
| | if loggingEnabled { |
| | state.logf("param %v:\n [<entry>, %d]:\n", n, afterPrologVal) |
| | } |
| | rtypes, _ := inp.RegisterTypesAndOffsets() |
| | padding := make([]uint64, 0, 32) |
| | padding = inp.ComputePadding(padding) |
| | for k, r := range inp.Registers { |
| | var reg int16 |
| | if n == f.CloSlot { |
| | reg = cloReg |
| | } else { |
| | reg = ObjRegForAbiReg(r, f.Config) |
| | } |
| | dwreg := ctxt.Arch.DWARFRegisters[reg] |
| | if dwreg < 32 { |
| | list = append(list, dwarf.DW_OP_reg0+byte(dwreg)) |
| | } else { |
| | list = append(list, dwarf.DW_OP_regx) |
| | list = dwarf.AppendUleb128(list, uint64(dwreg)) |
| | } |
| | if loggingEnabled { |
| | state.logf(" piece %d -> dwreg %d", k, dwreg) |
| | } |
| | if len(inp.Registers) > 1 { |
| | list = append(list, dwarf.DW_OP_piece) |
| | ts := rtypes[k].Size() |
| | list = dwarf.AppendUleb128(list, uint64(ts)) |
| | if padding[k] > 0 { |
| | if loggingEnabled { |
| | state.logf(" [pad %d bytes]", padding[k]) |
| | } |
| | list = append(list, dwarf.DW_OP_piece) |
| | list = dwarf.AppendUleb128(list, padding[k]) |
| | } |
| | } |
| | if loggingEnabled { |
| | state.logf("\n") |
| | } |
| | } |
| | |
| | ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2)) |
| |
|
| | |
| | |
| | list, sizeIdx = SetupLocList(ctxt, f.Entry.ID, list, |
| | afterPrologVal, FuncEnd.ID) |
| | if list == nil { |
| | pidx++ |
| | continue |
| | } |
| | soff := stackOffset(sl) |
| | if soff == 0 { |
| | list = append(list, dwarf.DW_OP_call_frame_cfa) |
| | } else { |
| | list = append(list, dwarf.DW_OP_fbreg) |
| | list = dwarf.AppendSleb128(list, int64(soff)) |
| | } |
| | if loggingEnabled { |
| | state.logf(" [%d, <end>): stackOffset=%d\n", afterPrologVal, soff) |
| | } |
| |
|
| | |
| | ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2)) |
| |
|
| | rval.LocationLists[pidx] = list |
| | pidx++ |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func IsVarWantedForDebug(n ir.Node) bool { |
| | name := n.Sym().Name |
| | if len(name) > 0 && name[0] == '&' { |
| | name = name[1:] |
| | } |
| | if len(name) > 0 && name[0] == '#' { |
| | |
| | return strings.HasPrefix(name, "#yield") |
| | } |
| | return true |
| | } |
| |
|