diff --git a/src/cmd/compile/fmt_test.go b/src/cmd/compile/fmt_test.go index ca6e6d0b69..bfc7210985 100644 --- a/src/cmd/compile/fmt_test.go +++ b/src/cmd/compile/fmt_test.go @@ -572,13 +572,12 @@ var knownFormats = map[string]string{ "*cmd/compile/internal/ssa.Block %v": "", "*cmd/compile/internal/ssa.Func %s": "", "*cmd/compile/internal/ssa.Func %v": "", - "*cmd/compile/internal/ssa.LocalSlot %+v": "", "*cmd/compile/internal/ssa.LocalSlot %v": "", "*cmd/compile/internal/ssa.Register %s": "", + "*cmd/compile/internal/ssa.Register %v": "", "*cmd/compile/internal/ssa.SparseTreeNode %v": "", "*cmd/compile/internal/ssa.Value %s": "", "*cmd/compile/internal/ssa.Value %v": "", - "*cmd/compile/internal/ssa.VarLoc %v": "", "*cmd/compile/internal/ssa.sparseTreeMapEntry %v": "", "*cmd/compile/internal/types.Field %p": "", "*cmd/compile/internal/types.Field %v": "", @@ -597,7 +596,6 @@ var knownFormats = map[string]string{ "*cmd/compile/internal/types.Type %p": "", "*cmd/compile/internal/types.Type %s": "", "*cmd/compile/internal/types.Type %v": "", - "*cmd/internal/dwarf.Location %#v": "", "*cmd/internal/obj.Addr %v": "", "*cmd/internal/obj.LSym %v": "", "*math/big.Int %#x": "", @@ -605,13 +603,12 @@ var knownFormats = map[string]string{ "[16]byte %x": "", "[]*cmd/compile/internal/gc.Node %v": "", "[]*cmd/compile/internal/gc.Sig %#v": "", + "[]*cmd/compile/internal/ssa.Block %v": "", "[]*cmd/compile/internal/ssa.Value %v": "", - "[][]cmd/compile/internal/ssa.SlotID %v": "", "[]byte %s": "", "[]byte %x": "", "[]cmd/compile/internal/ssa.Edge %v": "", "[]cmd/compile/internal/ssa.ID %v": "", - "[]cmd/compile/internal/ssa.VarLocList %v": "", "[]cmd/compile/internal/syntax.token %s": "", "[]string %v": "", "bool %v": "", @@ -637,18 +634,17 @@ var knownFormats = map[string]string{ "cmd/compile/internal/gc.Val %v": "", "cmd/compile/internal/gc.fmtMode %d": "", "cmd/compile/internal/gc.initKind %d": "", - "cmd/compile/internal/gc.locID %v": "", "cmd/compile/internal/ssa.BranchPrediction %d": "", "cmd/compile/internal/ssa.Edge %v": "", "cmd/compile/internal/ssa.GCNode %v": "", "cmd/compile/internal/ssa.ID %d": "", "cmd/compile/internal/ssa.ID %v": "", "cmd/compile/internal/ssa.LocalSlot %s": "", + "cmd/compile/internal/ssa.LocalSlot %v": "", "cmd/compile/internal/ssa.Location %s": "", "cmd/compile/internal/ssa.Op %s": "", "cmd/compile/internal/ssa.Op %v": "", "cmd/compile/internal/ssa.ValAndOff %s": "", - "cmd/compile/internal/ssa.VarLocList %v": "", "cmd/compile/internal/ssa.rbrank %d": "", "cmd/compile/internal/ssa.regMask %d": "", "cmd/compile/internal/ssa.register %d": "", @@ -663,7 +659,6 @@ var knownFormats = map[string]string{ "cmd/compile/internal/types.EType %d": "", "cmd/compile/internal/types.EType %s": "", "cmd/compile/internal/types.EType %v": "", - "cmd/internal/dwarf.Location %#v": "", "cmd/internal/src.Pos %s": "", "cmd/internal/src.Pos %v": "", "error %v": "", diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 6fa301659d..80b771a831 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -13,7 +13,6 @@ import ( "cmd/internal/src" "cmd/internal/sys" "fmt" - "math" "math/rand" "sort" "strings" @@ -304,8 +303,6 @@ func compileFunctions() { func debuginfo(fnsym *obj.LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) { fn := curfn.(*Node) - debugInfo := fn.Func.DebugInfo - fn.Func.DebugInfo = nil if fn.Func.Nname != nil { if expect := fn.Func.Nname.Sym.Linksym(); fnsym != expect { Fatalf("unexpected fnsym: %v != %v", fnsym, expect) @@ -344,7 +341,7 @@ func debuginfo(fnsym *obj.LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCall }) } - decls, dwarfVars := createDwarfVars(fnsym, debugInfo, automDecls) + decls, dwarfVars := createDwarfVars(fnsym, fn.Func, automDecls) var varScopes []ScopeID for _, decl := range decls { @@ -437,65 +434,24 @@ func createSimpleVars(automDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool return decls, vars, selected } -type varPart struct { - varOffset int64 - slot ssa.SlotID -} - -func createComplexVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug, automDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool) { - for _, blockDebug := range debugInfo.Blocks { - for _, locList := range blockDebug.Variables { - for _, loc := range locList.Locations { - if loc.StartProg != nil { - loc.StartPC = loc.StartProg.Pc - } - if loc.EndProg != nil { - loc.EndPC = loc.EndProg.Pc - } else { - loc.EndPC = fnsym.Size - } - if Debug_locationlist == 0 { - loc.EndProg = nil - loc.StartProg = nil - } - } - } - } - - // Group SSA variables by the user variable they were decomposed from. - varParts := map[*Node][]varPart{} - ssaVars := make(map[*Node]bool) - for slotID, slot := range debugInfo.VarSlots { - for slot.SplitOf != nil { - slot = slot.SplitOf - } - n := slot.N.(*Node) - ssaVars[n] = true - varParts[n] = append(varParts[n], varPart{varOffset(slot), ssa.SlotID(slotID)}) - } +// createComplexVars creates recomposed DWARF vars with location lists, +// suitable for describing optimized code. +func createComplexVars(fnsym *obj.LSym, fn *Func, automDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool) { + debugInfo := fn.DebugInfo // Produce a DWARF variable entry for each user variable. - // Don't iterate over the map -- that's nondeterministic, and - // createComplexVar has side effects. Instead, go by slot. var decls []*Node var vars []*dwarf.Var - for _, slot := range debugInfo.VarSlots { - for slot.SplitOf != nil { - slot = slot.SplitOf - } - n := slot.N.(*Node) - parts := varParts[n] - if parts == nil { - continue - } - // Don't work on this variable again, no matter how many slots it has. - delete(varParts, n) + ssaVars := make(map[*Node]bool) - // Get the order the parts need to be in to represent the memory - // of the decomposed user variable. - sort.Sort(partsByVarOffset(parts)) + for varID := range debugInfo.Vars { + n := debugInfo.Vars[varID].(*Node) + ssaVars[n] = true + for _, slot := range debugInfo.VarSlots[varID] { + ssaVars[debugInfo.Slots[slot].N.(*Node)] = true + } - if dvar := createComplexVar(debugInfo, n, parts); dvar != nil { + if dvar := createComplexVar(fn, ssa.VarID(varID)); dvar != nil { decls = append(decls, n) vars = append(vars, dvar) } @@ -504,13 +460,15 @@ func createComplexVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug, automDecls []* return decls, vars, ssaVars } -func createDwarfVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug, automDecls []*Node) ([]*Node, []*dwarf.Var) { +// createDwarfVars process fn, returning a list of DWARF variables and the +// Nodes they represent. +func createDwarfVars(fnsym *obj.LSym, fn *Func, automDecls []*Node) ([]*Node, []*dwarf.Var) { // Collect a raw list of DWARF vars. var vars []*dwarf.Var var decls []*Node var selected map[*Node]bool - if Ctxt.Flag_locationlists && Ctxt.Flag_optimize && debugInfo != nil { - decls, vars, selected = createComplexVars(fnsym, debugInfo, automDecls) + if Ctxt.Flag_locationlists && Ctxt.Flag_optimize && fn.DebugInfo != nil { + decls, vars, selected = createComplexVars(fnsym, fn, automDecls) } else { decls, vars, selected = createSimpleVars(automDecls) } @@ -635,22 +593,6 @@ func (s byNodeName) Len() int { return len(s) } func (s byNodeName) Less(i, j int) bool { return cmpNodeName(s[i], s[j]) } func (s byNodeName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -// varOffset returns the offset of slot within the user variable it was -// decomposed from. This has nothing to do with its stack offset. -func varOffset(slot *ssa.LocalSlot) int64 { - offset := slot.Off - for ; slot.SplitOf != nil; slot = slot.SplitOf { - offset += slot.SplitOffset - } - return offset -} - -type partsByVarOffset []varPart - -func (a partsByVarOffset) Len() int { return len(a) } -func (a partsByVarOffset) Less(i, j int) bool { return a[i].varOffset < a[j].varOffset } -func (a partsByVarOffset) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - // stackOffset returns the stack location of a LocalSlot relative to the // stack pointer, suitable for use in a DWARF location entry. This has nothing // to do with its offset in the user variable. @@ -671,24 +613,17 @@ func stackOffset(slot *ssa.LocalSlot) int32 { return int32(base + n.Xoffset + slot.Off) } -// createComplexVar builds a DWARF variable entry and location list representing n. -func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf.Var { - slots := debugInfo.Slots - var offs int64 // base stack offset for this kind of variable +// createComplexVar builds a single DWARF variable entry and location list. +func createComplexVar(fn *Func, varID ssa.VarID) *dwarf.Var { + debug := fn.DebugInfo + n := debug.Vars[varID].(*Node) + var abbrev int switch n.Class() { case PAUTO: abbrev = dwarf.DW_ABRV_AUTO_LOCLIST - if Ctxt.FixedFrameSize() == 0 { - offs -= int64(Widthptr) - } - if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { - offs -= int64(Widthptr) - } - case PPARAM, PPARAMOUT: abbrev = dwarf.DW_ABRV_PARAM_LOCLIST - offs += Ctxt.FixedFrameSize() default: return nil } @@ -712,196 +647,20 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf Abbrev: abbrev, Type: Ctxt.Lookup(typename), // The stack offset is used as a sorting key, so for decomposed - // variables just give it the lowest one. It's not used otherwise. + // variables just give it the first one. It's not used otherwise. // This won't work well if the first slot hasn't been assigned a stack // location, but it's not obvious how to do better. - StackOffset: int32(stackOffset(slots[parts[0].slot])), + StackOffset: stackOffset(debug.Slots[debug.VarSlots[varID][0]]), DeclFile: declpos.Base().SymFilename(), DeclLine: declpos.Line(), DeclCol: declpos.Col(), InlIndex: int32(inlIndex), ChildIndex: -1, } - - if Debug_locationlist != 0 { - Ctxt.Logf("Building location list for %+v. Parts:\n", n) - for _, part := range parts { - Ctxt.Logf("\t%v => %v\n", debugInfo.Slots[part.slot], debugInfo.SlotLocsString(part.slot)) - } - } - - // Given a variable that's been decomposed into multiple parts, - // its location list may need a new entry after the beginning or - // end of every location entry for each of its parts. For example: - // - // [variable] [pc range] - // string.ptr |----|-----| |----| - // string.len |------------| |--| - // ... needs a location list like: - // string |----|-----|-| |--|-| - // - // Note that location entries may or may not line up with each other, - // and some of the result will only have one or the other part. - // - // To build the resulting list: - // - keep a "current" pointer for each part - // - find the next transition point - // - advance the current pointer for each part up to that transition point - // - build the piece for the range between that transition point and the next - // - repeat - - type locID struct { - block int - loc int - } - findLoc := func(part varPart, id locID) *ssa.VarLoc { - if id.block >= len(debugInfo.Blocks) { - return nil - } - return debugInfo.Blocks[id.block].Variables[part.slot].Locations[id.loc] - } - nextLoc := func(part varPart, id locID) (locID, *ssa.VarLoc) { - // Check if there's another loc in this block - id.loc++ - if b := debugInfo.Blocks[id.block]; b != nil && id.loc < len(b.Variables[part.slot].Locations) { - return id, findLoc(part, id) - } - // Find the next block that has a loc for this part. - id.loc = 0 - id.block++ - for ; id.block < len(debugInfo.Blocks); id.block++ { - if b := debugInfo.Blocks[id.block]; b != nil && len(b.Variables[part.slot].Locations) != 0 { - return id, findLoc(part, id) - } - } - return id, nil - } - curLoc := make([]locID, len(slots)) - // Position each pointer at the first entry for its slot. - for _, part := range parts { - if b := debugInfo.Blocks[0]; b != nil && len(b.Variables[part.slot].Locations) != 0 { - // Block 0 has an entry; no need to advance. - continue - } - curLoc[part.slot], _ = nextLoc(part, curLoc[part.slot]) - } - - // findBoundaryAfter finds the next beginning or end of a piece after currentPC. - findBoundaryAfter := func(currentPC int64) int64 { - min := int64(math.MaxInt64) - for _, part := range parts { - // For each part, find the first PC greater than current. Doesn't - // matter if it's a start or an end, since we're looking for any boundary. - // If it's the new winner, save it. - onePart: - for i, loc := curLoc[part.slot], findLoc(part, curLoc[part.slot]); loc != nil; i, loc = nextLoc(part, i) { - for _, pc := range [2]int64{loc.StartPC, loc.EndPC} { - if pc > currentPC { - if pc < min { - min = pc - } - break onePart - } - } - } - } - return min - } - var start int64 - end := findBoundaryAfter(0) - for { - // Advance to the next chunk. - start = end - end = findBoundaryAfter(start) - if end == math.MaxInt64 { - break - } - - dloc := dwarf.Location{StartPC: start, EndPC: end} - if Debug_locationlist != 0 { - Ctxt.Logf("Processing range %x -> %x\n", start, end) - } - - // Advance curLoc to the last location that starts before/at start. - // After this loop, if there's a location that covers [start, end), it will be current. - // Otherwise the current piece will be too early. - for _, part := range parts { - choice := locID{-1, -1} - for i, loc := curLoc[part.slot], findLoc(part, curLoc[part.slot]); loc != nil; i, loc = nextLoc(part, i) { - if loc.StartPC > start { - break //overshot - } - choice = i // best yet - } - if choice.block != -1 { - curLoc[part.slot] = choice - } - if Debug_locationlist != 0 { - Ctxt.Logf("\t %v => %v", slots[part.slot], curLoc[part.slot]) - } - } - if Debug_locationlist != 0 { - Ctxt.Logf("\n") - } - // Assemble the location list entry for this chunk. - present := 0 - for _, part := range parts { - dpiece := dwarf.Piece{ - Length: slots[part.slot].Type.Size(), - } - loc := findLoc(part, curLoc[part.slot]) - if loc == nil || start >= loc.EndPC || end <= loc.StartPC { - if Debug_locationlist != 0 { - Ctxt.Logf("\t%v: missing", slots[part.slot]) - } - dpiece.Missing = true - dloc.Pieces = append(dloc.Pieces, dpiece) - continue - } - present++ - if Debug_locationlist != 0 { - Ctxt.Logf("\t%v: %v", slots[part.slot], debugInfo.Blocks[curLoc[part.slot].block].LocString(loc)) - } - if loc.OnStack { - dpiece.OnStack = true - dpiece.StackOffset = stackOffset(slots[loc.StackLocation]) - } else { - for reg := 0; reg < len(debugInfo.Registers); reg++ { - if loc.Registers&(1< totally missing\n") - } - continue - } - // Extend the previous entry if possible. - if len(dvar.LocationList) > 0 { - prev := &dvar.LocationList[len(dvar.LocationList)-1] - if prev.EndPC == dloc.StartPC && len(prev.Pieces) == len(dloc.Pieces) { - equal := true - for i := range prev.Pieces { - if prev.Pieces[i] != dloc.Pieces[i] { - equal = false - } - } - if equal { - prev.EndPC = end - if Debug_locationlist != 0 { - Ctxt.Logf("-> merged with previous, now %#v\n", prev) - } - continue - } - } - } - dvar.LocationList = append(dvar.LocationList, dloc) - if Debug_locationlist != 0 { - Ctxt.Logf("-> added: %#v\n", dloc) + list := debug.LocationLists[varID] + if len(list) != 0 { + dvar.PutLocationList = func(listSym, startPC dwarf.Sym) { + debug.PutLocationList(list, Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym)) } } return dvar diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 5ec01b6a61..b512b10e01 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4652,15 +4652,14 @@ func genssa(f *ssa.Func, pp *Progs) { s.ScratchFpMem = e.scratchFpMem - logLocationLists := Debug_locationlist != 0 if Ctxt.Flag_locationlists { - e.curfn.Func.DebugInfo = ssa.BuildFuncDebug(f, logLocationLists) valueToProgAfter = make([]*obj.Prog, f.NumValues()) } // Emit basic blocks for i, b := range f.Blocks { s.bstart[b.ID] = s.pp.next + // Emit values in block thearch.SSAMarkMoves(&s, b) for _, v := range b.Values { @@ -4698,8 +4697,6 @@ func genssa(f *ssa.Func, pp *Progs) { } case ssa.OpPhi: CheckLoweredPhi(v) - case ssa.OpRegKill: - // nothing to do default: // let the backend handle it thearch.SSAGenValue(&s, v) @@ -4708,12 +4705,14 @@ func genssa(f *ssa.Func, pp *Progs) { if Ctxt.Flag_locationlists { valueToProgAfter[v.ID] = s.pp.next } + if logProgs { for ; x != s.pp.next; x = x.Link { progToValue[x] = v } } } + // Emit control flow instructions for block var next *ssa.Block if i < len(f.Blocks)-1 && Debug['N'] == 0 { @@ -4734,41 +4733,19 @@ func genssa(f *ssa.Func, pp *Progs) { } if Ctxt.Flag_locationlists { - for i := range f.Blocks { - blockDebug := e.curfn.Func.DebugInfo.Blocks[i] - for _, locList := range blockDebug.Variables { - for _, loc := range locList.Locations { - if loc.Start == ssa.BlockStart { - loc.StartProg = s.bstart[f.Blocks[i].ID] - } else { - loc.StartProg = valueToProgAfter[loc.Start.ID] - } - if loc.End == nil { - Fatalf("empty loc %v compiling %v", loc, f.Name) - } - - if loc.End == ssa.BlockEnd { - // If this variable was live at the end of the block, it should be - // live over the control flow instructions. Extend it up to the - // beginning of the next block. - // If this is the last block, then there's no Prog to use for it, and - // EndProg is unset. - if i < len(f.Blocks)-1 { - loc.EndProg = s.bstart[f.Blocks[i+1].ID] - } - } else { - // Advance the "end" forward by one; the end-of-range doesn't take effect - // until the instruction actually executes. - loc.EndProg = valueToProgAfter[loc.End.ID].Link - if loc.EndProg == nil { - Fatalf("nil loc.EndProg compiling %v, loc=%v", f.Name, loc) - } - } - if !logLocationLists { - loc.Start = nil - loc.End = nil - } - } + e.curfn.Func.DebugInfo = ssa.BuildFuncDebug(Ctxt, f, Debug_locationlist > 1, stackOffset) + bstart := s.bstart + // Note that at this moment, Prog.Pc is a sequence number; it's + // not a real PC until after assembly, so this mapping has to + // be done later. + e.curfn.Func.DebugInfo.GetPC = func(b, v ssa.ID) int64 { + switch v { + case ssa.BlockStart.ID: + return int64(bstart[b].Pc) + case ssa.BlockEnd.ID: + return int64(e.curfn.Func.lsym.Size) + default: + return int64(valueToProgAfter[v].Pc) } } } diff --git a/src/cmd/compile/internal/ssa/cache.go b/src/cmd/compile/internal/ssa/cache.go index 8434084bde..f1018da497 100644 --- a/src/cmd/compile/internal/ssa/cache.go +++ b/src/cmd/compile/internal/ssa/cache.go @@ -14,11 +14,6 @@ type Cache struct { blocks [200]Block locs [2000]Location - // Storage for DWARF variable locations. Lazily allocated - // since location lists are off by default. - varLocs []VarLoc - curVarLoc int - // Reusable stackAllocState. // See stackalloc.go's {new,put}StackAllocState. stackAllocState *stackAllocState @@ -43,21 +38,4 @@ func (c *Cache) Reset() { for i := range xl { xl[i] = nil } - xvl := c.varLocs[:c.curVarLoc] - for i := range xvl { - xvl[i] = VarLoc{} - } - c.curVarLoc = 0 -} - -func (c *Cache) NewVarLoc() *VarLoc { - if c.varLocs == nil { - c.varLocs = make([]VarLoc, 4000) - } - if c.curVarLoc == len(c.varLocs) { - return &VarLoc{} - } - vl := &c.varLocs[c.curVarLoc] - c.curVarLoc++ - return vl } diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go index 1c2fcd7948..de77aff480 100644 --- a/src/cmd/compile/internal/ssa/check.go +++ b/src/cmd/compile/internal/ssa/check.go @@ -465,10 +465,6 @@ func memCheck(f *Func) { if seenNonPhi { f.Fatalf("phi after non-phi @ %s: %s", b, v) } - case OpRegKill: - if f.RegAlloc == nil { - f.Fatalf("RegKill seen before register allocation @ %s: %s", b, v) - } default: seenNonPhi = true } diff --git a/src/cmd/compile/internal/ssa/debug.go b/src/cmd/compile/internal/ssa/debug.go index dcef9f2447..8ec146287c 100644 --- a/src/cmd/compile/internal/ssa/debug.go +++ b/src/cmd/compile/internal/ssa/debug.go @@ -4,59 +4,80 @@ package ssa import ( + "cmd/internal/dwarf" "cmd/internal/obj" + "encoding/hex" "fmt" + "sort" "strings" ) type SlotID int32 +type VarID int32 // A FuncDebug contains all the debug information for the variables in a // function. Variables are identified by their LocalSlot, which may be the // result of decomposing a larger variable. type FuncDebug struct { // Slots is all the slots used in the debug info, indexed by their SlotID. - // Use this when getting a LocalSlot from a SlotID. Slots []*LocalSlot - // VarSlots is the slots that represent part of user variables. - // Use this when iterating over all the slots to generate debug information. - VarSlots []*LocalSlot - // The blocks in the function, in program text order. - Blocks []*BlockDebug - // The registers of the current architecture, indexed by Register.num. - Registers []Register -} + // The user variables, indexed by VarID. + Vars []GCNode + // The slots that make up each variable, indexed by VarID. + VarSlots [][]SlotID + // The location list data, indexed by VarID. Must be processed by PutLocationList. + LocationLists [][]byte -func (f *FuncDebug) BlockString(b *BlockDebug) string { - var vars []string - - for slot := range f.VarSlots { - if len(b.Variables[slot].Locations) == 0 { - continue - } - vars = append(vars, fmt.Sprintf("%v = %v", f.Slots[slot], b.Variables[slot])) - } - return fmt.Sprintf("{%v}", strings.Join(vars, ", ")) -} - -func (f *FuncDebug) SlotLocsString(id SlotID) string { - var locs []string - for _, block := range f.Blocks { - for _, loc := range block.Variables[id].Locations { - locs = append(locs, block.LocString(loc)) - } - } - return strings.Join(locs, " ") + // Filled in by the user. Translates Block and Value ID to PC. + GetPC func(ID, ID) int64 } type BlockDebug struct { // The SSA block that this tracks. For debug logging only. Block *Block - // The variables in this block, indexed by their SlotID. - Variables []VarLocList + // State at entry to the block. Both this and endState are immutable + // once initialized. + startState []liveSlot + // State at the end of the block if it's fully processed. + endState []liveSlot } -func (b *BlockDebug) LocString(loc *VarLoc) string { +// A liveSlot is a slot that's live in loc at entry/exit of a block. +type liveSlot struct { + slot SlotID + loc VarLoc +} + +// stateAtPC is the current state of all variables at some point. +type stateAtPC struct { + // The location of each known slot, indexed by SlotID. + slots []VarLoc + // The slots present in each register, indexed by register number. + registers [][]SlotID +} + +// reset fills state with the live variables from live. +func (state *stateAtPC) reset(live []liveSlot) { + for i := range state.slots { + state.slots[i] = VarLoc{} + } + for i := range state.registers { + state.registers[i] = state.registers[i][:0] + } + for _, live := range live { + state.slots[live.slot] = live.loc + for reg, regMask := 0, 1; reg < len(state.registers); reg, regMask = reg+1, regMask<<1 { + if live.loc.Registers&RegisterSet(regMask) != 0 { + state.registers[reg] = append(state.registers[reg], SlotID(live.slot)) + } + } + } +} + +func (b *BlockDebug) LocString(loc VarLoc) string { + if loc.absent() { + return "" + } registers := b.Block.Func.Config.registers var storage []string @@ -77,95 +98,21 @@ func (b *BlockDebug) LocString(loc *VarLoc) string { if len(storage) == 0 { storage = append(storage, "!!!no storage!!!") } - pos := func(v *Value, p *obj.Prog, pc int64) string { - if v == nil { - return "?" - } - vStr := fmt.Sprintf("v%d", v.ID) - if v == BlockStart { - vStr = fmt.Sprintf("b%dStart", b.Block.ID) - } - if v == BlockEnd { - vStr = fmt.Sprintf("b%dEnd", b.Block.ID) - } - if p == nil { - return vStr - } - return fmt.Sprintf("%s/%x", vStr, pc) - } - start := pos(loc.Start, loc.StartProg, loc.StartPC) - end := pos(loc.End, loc.EndProg, loc.EndPC) - return fmt.Sprintf("%v-%v@%s", start, end, strings.Join(storage, ",")) - + return strings.Join(storage, ",") } -// append adds a location to the location list for slot. -func (b *BlockDebug) append(slot SlotID, loc *VarLoc) { - b.Variables[slot].append(loc) -} - -// lastLoc returns the last VarLoc for slot, or nil if it has none. -func (b *BlockDebug) lastLoc(slot SlotID) *VarLoc { - return b.Variables[slot].last() -} - -// A VarLocList contains the locations for a variable, in program text order. -// It will often have gaps. -type VarLocList struct { - Locations []*VarLoc -} - -func (l *VarLocList) append(loc *VarLoc) { - l.Locations = append(l.Locations, loc) -} - -// last returns the last location in the list. -func (l *VarLocList) last() *VarLoc { - if l == nil || len(l.Locations) == 0 { - return nil - } - return l.Locations[len(l.Locations)-1] -} - -// A VarLoc describes a variable's location in a single contiguous range -// of program text. It is generated from the SSA representation, but it -// refers to the generated machine code, so the Values referenced are better -// understood as PCs than actual Values, and the ranges can cross blocks. -// The range is defined first by Values, which are then mapped to Progs -// during genssa and finally to function PCs after assembly. -// A variable can be on the stack and in any number of registers. +// A VarLoc describes the storage for part of a user variable. type VarLoc struct { - // Inclusive -- the first SSA value that the range covers. The value - // doesn't necessarily have anything to do with the variable; it just - // identifies a point in the program text. - // The special sentinel value BlockStart indicates that the range begins - // at the beginning of the containing block, even if the block doesn't - // actually have a Value to use to indicate that. - Start *Value - // Exclusive -- the first SSA value after start that the range doesn't - // cover. A location with start == end is empty. - // The special sentinel value BlockEnd indicates that the variable survives - // to the end of the of the containing block, after all its Values and any - // control flow instructions added later. - End *Value - - // The prog/PCs corresponding to Start and End above. These are for the - // convenience of later passes, since code generation isn't done when - // BuildFuncDebug runs. - // Control flow instructions don't correspond to a Value, so EndProg - // may point to a Prog in the next block if SurvivedBlock is true. For - // the last block, where there's no later Prog, it will be nil to indicate - // the end of the function. - StartProg, EndProg *obj.Prog - StartPC, EndPC int64 - // The registers this variable is available in. There can be more than // one in various situations, e.g. it's being moved between registers. Registers RegisterSet - // OnStack indicates that the variable is on the stack in the LocalSlot - // identified by StackLocation. - OnStack bool - StackLocation SlotID + // OnStack indicates that the variable is on the stack at StackOffset. + OnStack bool + StackOffset int32 +} + +func (loc *VarLoc) absent() bool { + return loc.Registers == 0 && !loc.OnStack } var BlockStart = &Value{ @@ -196,104 +143,153 @@ func (s *debugState) logf(msg string, args ...interface{}) { } type debugState struct { - loggingEnabled bool - slots []*LocalSlot - varSlots []*LocalSlot - f *Func - cache *Cache - numRegisters int + // See FuncDebug. + slots []*LocalSlot + vars []GCNode + varSlots [][]SlotID - // working storage for BuildFuncDebug, reused between blocks. - registerContents [][]SlotID + // The user variable that each slot rolls up to, indexed by SlotID. + slotVars []VarID + + f *Func + loggingEnabled bool + cache *Cache + registers []Register + stackOffset func(*LocalSlot) int32 + + // The names (slots) associated with each value, indexed by Value ID. + valueNames [][]SlotID + + // The current state of whatever analysis is running. + currentState stateAtPC + changedVars []bool } -// getHomeSlot returns the SlotID of the home slot for v, adding to s.slots -// if necessary. -func (s *debugState) getHomeSlot(v *Value) SlotID { - home := s.f.getHome(v.ID).(LocalSlot) - for id, slot := range s.slots { - if *slot == home { - return SlotID(id) +func (s *debugState) blockEndStateString(b *BlockDebug) string { + endState := stateAtPC{slots: make([]VarLoc, len(s.slots)), registers: make([][]SlotID, len(s.slots))} + endState.reset(b.endState) + return s.stateString(b, endState) +} + +func (s *debugState) stateString(b *BlockDebug, 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], b.LocString(loc))) } } - // This slot wasn't in the NamedValue table so it needs to be added. - s.slots = append(s.slots, &home) - return SlotID(len(s.slots) - 1) -} -func (s *debugState) BlockString(b *BlockDebug) string { - f := &FuncDebug{ - Slots: s.slots, - VarSlots: s.varSlots, - Registers: s.f.Config.registers, + 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)) + } } - return f.BlockString(b) + + if len(strs) == 1 { + return "(no vars)\n" + } + return strings.Join(strs, "") } // BuildFuncDebug returns debug information for f. // f must be fully processed, so that each Value is where it will be when // machine code is emitted. -func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug { +func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(*LocalSlot) int32) *FuncDebug { if f.RegAlloc == nil { f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f) } state := &debugState{ - loggingEnabled: loggingEnabled, - slots: make([]*LocalSlot, len(f.Names)), - cache: f.Cache, - f: f, - numRegisters: len(f.Config.registers), - registerContents: make([][]SlotID, len(f.Config.registers)), + loggingEnabled: loggingEnabled, + slots: make([]*LocalSlot, len(f.Names)), + + f: f, + cache: f.Cache, + registers: f.Config.registers, + stackOffset: stackOffset, + currentState: stateAtPC{make([]VarLoc, len(f.Names)), make([][]SlotID, len(f.Config.registers))}, } // TODO: consider storing this in Cache and reusing across functions. - valueNames := make([][]SlotID, f.NumValues()) + state.valueNames = make([][]SlotID, f.NumValues()) + // Recompose any decomposed variables, and record the names associated with each value. + varParts := map[GCNode][]SlotID{} for i, slot := range f.Names { slot := slot state.slots[i] = &slot - if isSynthetic(&slot) { continue } for _, value := range f.NamedValues[slot] { - valueNames[value.ID] = append(valueNames[value.ID], SlotID(i)) + state.valueNames[value.ID] = append(state.valueNames[value.ID], SlotID(i)) } - } - // state.varSlots is never changed, and state.slots is only appended to, - // so aliasing is safe. - state.varSlots = state.slots - if state.loggingEnabled { - var names []string - for i, name := range f.Names { - names = append(names, fmt.Sprintf("%d = %s", i, name)) + topSlot := &slot + for topSlot.SplitOf != nil { + topSlot = topSlot.SplitOf } - state.logf("Name table: %v\n", strings.Join(names, ", ")) + if _, ok := varParts[topSlot.N]; !ok { + state.vars = append(state.vars, topSlot.N) + } + varParts[topSlot.N] = append(varParts[topSlot.N], SlotID(i)) } - // Build up block states, starting with the first block, then - // processing blocks once their predecessors have been processed. + // Fill in the var<->slot mappings. + state.varSlots = make([][]SlotID, len(state.vars)) + state.slotVars = make([]VarID, len(state.slots)) + for varID, n := range state.vars { + parts := varParts[n] + state.varSlots[varID] = parts + for _, slotID := range parts { + state.slotVars[slotID] = VarID(varID) + } + } + state.changedVars = make([]bool, len(state.vars)) - // Location list entries for each block. - blockLocs := make([]*BlockDebug, f.NumBlocks()) + blockLocs := state.liveness() + lists := state.buildLocationLists(ctxt, stackOffset, blockLocs) + + return &FuncDebug{ + Slots: state.slots, + VarSlots: state.varSlots, + Vars: state.vars, + LocationLists: lists, + } +} + +// isSynthetic reports whether if slot represents a compiler-inserted variable, +// e.g. an autotmp or an anonymous return value that needed a stack slot. +func isSynthetic(slot *LocalSlot) bool { + c := slot.N.String()[0] + return c == '.' || c == '~' +} + +// liveness walks the function in control flow order, calculating the start +// and end state of each block. +func (state *debugState) liveness() []*BlockDebug { + blockLocs := make([]*BlockDebug, state.f.NumBlocks()) // Reverse postorder: visit a block after as many as possible of its // predecessors have been visited. - po := f.Postorder() + po := state.f.Postorder() for i := len(po) - 1; i >= 0; i-- { b := po[i] // Build the starting state for the block from the final // state of its predecessors. locs := state.mergePredecessors(b, blockLocs) + if state.loggingEnabled { - state.logf("Processing %v, initial locs %v, regs %v\n", b, state.BlockString(locs), state.registerContents) + state.logf("Processing %v, initial state:\n%v", b, state.stateString(locs, state.currentState)) } + // Update locs/registers with the effects of each Value. - // The location list generated here needs to be slightly adjusted for use by gdb. - // These adjustments are applied in genssa. for _, v := range b.Values { - slots := valueNames[v.ID] + slots := state.valueNames[v.ID] // Loads and stores inherit the names of their sources. var source *Value @@ -310,68 +306,41 @@ func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug { state.unexpected(v, "load with unexpected source op %v", a) } } + // Update valueNames with the source so that later steps + // don't need special handling. if source != nil { - slots = append(slots, valueNames[source.ID]...) - // As of writing, the compiler never uses a load/store as a - // source of another load/store, so there's no reason this should - // ever be consulted. Update just in case, and so that when - // valueNames is cached, we can reuse the memory. - valueNames[v.ID] = slots + slots = append(slots, state.valueNames[source.ID]...) + state.valueNames[v.ID] = slots } - if len(slots) == 0 { - continue - } - - reg, _ := f.getHome(v.ID).(*Register) - state.processValue(locs, v, slots, reg) + reg, _ := state.f.getHome(v.ID).(*Register) + state.processValue(v, slots, reg) } - // The block is done; mark any live locations as ending with the block. - for _, locList := range locs.Variables { - last := locList.last() - if last == nil || last.End != nil { - continue - } - last.End = BlockEnd - } if state.loggingEnabled { - f.Logf("Block done: locs %v, regs %v\n", state.BlockString(locs), state.registerContents) + state.f.Logf("Block %v done, locs:\n%v", b, state.stateString(locs, state.currentState)) } + + for slotID, slotLoc := range state.currentState.slots { + if slotLoc.absent() { + continue + } + locs.endState = append(locs.endState, liveSlot{SlotID(slotID), slotLoc}) + } + blockLocs[b.ID] = locs } - - info := &FuncDebug{ - Slots: state.slots, - VarSlots: state.varSlots, - Registers: f.Config.registers, - } - // Consumers want the information in textual order, not by block ID. - for _, b := range f.Blocks { - info.Blocks = append(info.Blocks, blockLocs[b.ID]) - } - - if state.loggingEnabled { - f.Logf("Final result:\n") - for slot := range info.VarSlots { - f.Logf("\t%v => %v\n", info.Slots[slot], info.SlotLocsString(SlotID(slot))) - } - } - return info -} - -// isSynthetic reports whether if slot represents a compiler-inserted variable, -// e.g. an autotmp or an anonymous return value that needed a stack slot. -func isSynthetic(slot *LocalSlot) bool { - c := slot.String()[0] - return c == '.' || c == '~' + return blockLocs } // mergePredecessors takes the end state of each of b's predecessors and -// intersects them to form the starting state for b. -// The registers slice (the second return value) will be reused for each call to mergePredecessors. +// intersects them to form the starting state for b. It returns that state in +// the BlockDebug, and fills state.currentState with it. func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug) *BlockDebug { - live := make([]VarLocList, len(state.slots)) + result := &BlockDebug{} + if state.loggingEnabled { + result.Block = b + } // Filter out back branches. var preds []*Block @@ -381,157 +350,146 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug) *B } } - if len(preds) > 0 { - p := preds[0] - for slot, locList := range blockLocs[p.ID].Variables { - last := locList.last() - if last == nil || last.End != BlockEnd { - continue - } - loc := state.cache.NewVarLoc() - loc.Start = BlockStart - loc.OnStack = last.OnStack - loc.StackLocation = last.StackLocation - loc.Registers = last.Registers - live[slot].append(loc) - } + if state.loggingEnabled { + state.logf("Merging %v into %v\n", preds, b) } - if state.loggingEnabled && len(b.Preds) > 1 { - state.logf("Starting merge with state from %v: %v\n", b.Preds[0].b, state.BlockString(blockLocs[b.Preds[0].b.ID])) + + if len(preds) == 0 { + if state.loggingEnabled { + } + state.currentState.reset(nil) + return result + } + + if len(preds) == 1 { + p := blockLocs[preds[0].ID] + result.startState = p.endState + state.currentState.reset(p.endState) + return result + } + + if state.loggingEnabled { + state.logf("Starting %v with state from %v:\n%v", b, preds[0], state.blockEndStateString(blockLocs[preds[0].ID])) + } + + count := make([]int, len(state.slots)) + slotLocs := state.currentState.slots + for _, predSlot := range blockLocs[preds[0].ID].endState { + slotLocs[predSlot.slot] = predSlot.loc + count[predSlot.slot] = 1 } for i := 1; i < len(preds); i++ { - p := preds[i] if state.loggingEnabled { - state.logf("Merging in state from %v: %v &= %v\n", p, live, state.BlockString(blockLocs[p.ID])) + state.logf("Merging in state from %v:\n%v", preds[i], state.blockEndStateString(blockLocs[preds[i].ID])) } - - for slot, liveVar := range live { - liveLoc := liveVar.last() - if liveLoc == nil { - continue - } - - predLoc := blockLocs[p.ID].Variables[SlotID(slot)].last() - // Clear out slots missing/dead in p. - if predLoc == nil || predLoc.End != BlockEnd { - live[slot].Locations = nil - continue - } - - // Unify storage locations. - if !liveLoc.OnStack || !predLoc.OnStack || liveLoc.StackLocation != predLoc.StackLocation { + for _, predSlot := range blockLocs[preds[i].ID].endState { + count[predSlot.slot]++ + liveLoc := slotLocs[predSlot.slot] + if !liveLoc.OnStack || !predSlot.loc.OnStack || liveLoc.StackOffset != predSlot.loc.StackOffset { liveLoc.OnStack = false - liveLoc.StackLocation = 0 + liveLoc.StackOffset = 0 } - liveLoc.Registers &= predLoc.Registers + liveLoc.Registers &= predSlot.loc.Registers + slotLocs[predSlot.slot] = liveLoc } } - // Create final result. - locs := &BlockDebug{Variables: live} - if state.loggingEnabled { - locs.Block = b + for reg := range state.currentState.registers { + state.currentState.registers[reg] = state.currentState.registers[reg][:0] } - for reg := range state.registerContents { - state.registerContents[reg] = state.registerContents[reg][:0] - } - for slot, locList := range live { - loc := locList.last() - if loc == nil { + + // A slot is live if it was seen in all predecessors, and they all had + // some storage in common. + for slotID, slotLoc := range slotLocs { + // Not seen in any predecessor. + if slotLoc.absent() { continue } - for reg := 0; reg < state.numRegisters; reg++ { - if loc.Registers&(1<65K values, + // they get incomplete debug info on 32-bit platforms. + return + } + list = appendPtr(Ctxt, list, start) + list = appendPtr(Ctxt, list, end) + // Where to write the length of the location description once + // we know how big it is. + sizeIdx := len(list) + list = list[:len(list)+2] + + if state.loggingEnabled { + var partStrs []string + for _, part := range varParts[varID] { + partStrs = append(partStrs, fmt.Sprintf("%v@%v", state.slots[part.slot], blockLocs[endBlock].LocString(pending.pieces[part.slot]))) + } + 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 _, part := range varParts[varID] { + loc := pending.pieces[part.slot] + slot := state.slots[part.slot] + + if !loc.absent() { + if loc.OnStack { + if loc.StackOffset == 0 { + list = append(list, dwarf.DW_OP_call_frame_cfa) + } else { + list = append(list, dwarf.DW_OP_fbreg) + list = dwarf.AppendSleb128(list, int64(loc.StackOffset)) + } + } else { + regnum := 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(varParts[varID]) > 1 { + list = append(list, dwarf.DW_OP_piece) + list = dwarf.AppendUleb128(list, uint64(slot.Type.Size())) + } + } + Ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2)) + lists[varID] = list + } + + // updateVar updates the pending location list entry for varID to + // reflect the new locations in curLoc, caused by v. + updateVar := func(varID VarID, v *Value, curLoc []VarLoc) { + // Assemble the location list entry with whatever's live. + empty := true + for _, part := range varParts[varID] { + if !curLoc[part.slot].absent() { + empty = false + break + } + } + pending := &pendingEntries[varID] + if empty { + writePendingEntry(varID, v.Block.ID, v.ID) + pending.clear() + return + } + + // Extend the previous entry if possible. + if pending.present { + merge := true + for _, part := range varParts[varID] { + if !canMerge(pending.pieces[part.slot], curLoc[part.slot]) { + merge = false + break + } + } + if merge { + return + } + } + + writePendingEntry(varID, v.Block.ID, v.ID) + pending.present = true + pending.startBlock = v.Block.ID + pending.startValue = v.ID + copy(pending.pieces, curLoc) + return + + } + + // Run through the function in program text order, building up location + // lists as we go. The heavy lifting has mostly already been done. + for _, b := range state.f.Blocks { + state.currentState.reset(blockLocs[b.ID].startState) + + for _, v := range b.Values { + slots := state.valueNames[v.ID] + reg, _ := state.f.getHome(v.ID).(*Register) + state.processValue(v, slots, reg) + + if v.Op == OpPhi { + continue + } + + for varID := range state.changedVars { + if !state.changedVars[varID] { + continue + } + state.changedVars[varID] = false + updateVar(VarID(varID), v, state.currentState.slots) + } + } + + } + + if state.loggingEnabled { + state.logf("location lists:\n") + } + + // Flush any leftover entries live at the end of the last block. + for varID := range lists { + writePendingEntry(VarID(varID), state.f.Blocks[len(state.f.Blocks)-1].ID, BlockEnd.ID) + list := lists[varID] + if len(list) == 0 { + continue + } + + if state.loggingEnabled { + state.logf("\t%v : %q\n", state.vars[varID], hex.EncodeToString(lists[varID])) + } + } + return lists +} + +// PutLocationList adds list (a location list in its intermediate representation) to listSym. +func (debugInfo *FuncDebug) PutLocationList(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) { + getPC := debugInfo.GetPC + // Re-read list, translating its address from block/value ID to PC. + for i := 0; i < len(list); { + translate := func() { + bv := readPtr(ctxt, list[i:]) + pc := getPC(decodeValue(ctxt, bv)) + writePtr(ctxt, list[i:], uint64(pc)) + i += ctxt.Arch.PtrSize + } + translate() + translate() + i += 2 + int(ctxt.Arch.ByteOrder.Uint16(list[i:])) + } + + // Base address entry. + listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, ^0) + listSym.WriteAddr(ctxt, listSym.Size, ctxt.Arch.PtrSize, startPC, 0) + // Location list contents, now with real PCs. + listSym.WriteBytes(ctxt, listSym.Size, list) + // End entry. + listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0) + listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0) +} + +// Pack a value and block ID into an address-sized uint, returning ~0 if they +// don't fit. +func encodeValue(ctxt *obj.Link, b, v ID) (uint64, bool) { + if ctxt.Arch.PtrSize == 8 { + result := uint64(b)<<32 | uint64(uint32(v)) + //ctxt.Logf("b %#x (%d) v %#x (%d) -> %#x\n", b, b, v, v, result) + 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 +} + +// Unpack a value and block ID encoded by encodeValue. +func decodeValue(ctxt *obj.Link, word uint64) (ID, ID) { + if ctxt.Arch.PtrSize == 8 { + b, v := ID(word>>32), ID(word) + //ctxt.Logf("%#x -> b %#x (%d) v %#x (%d)\n", word, b, b, v, v) + return b, v + } + if ctxt.Arch.PtrSize != 4 { + panic("unexpected pointer size") + } + return ID(word >> 16), ID(word) +} + +// Append a pointer-sized uint to buf. +func appendPtr(ctxt *obj.Link, buf []byte, word uint64) []byte { + if cap(buf) < len(buf)+100 { + b := make([]byte, len(buf), 100+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 +} + +// Write a pointer-sized uint to the beginning of 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") + } + +} + +// Read a pointer-sized uint from the beginning of buf. +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") + } + +} diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go index 5ce11c7e87..e970b8519b 100644 --- a/src/cmd/compile/internal/ssa/gen/genericOps.go +++ b/src/cmd/compile/internal/ssa/gen/genericOps.go @@ -450,7 +450,6 @@ var genericOps = []opData{ {name: "VarKill", argLength: 1, aux: "Sym", symEffect: "None"}, // aux is a *gc.Node of a variable that is known to be dead. arg0=mem, returns mem {name: "VarLive", argLength: 1, aux: "Sym", symEffect: "Read"}, // aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem {name: "KeepAlive", argLength: 2, typ: "Mem"}, // arg[0] is a value that must be kept alive until this mark. arg[1]=mem, returns mem - {name: "RegKill"}, // regalloc has determined that the value in this register is dead // Ops for breaking 64-bit operations on 32-bit architectures {name: "Int64Make", argLength: 2, typ: "UInt64"}, // arg0=hi, arg1=lo diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index 0c2b8f61c6..43343d7d5c 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -2017,7 +2017,6 @@ const ( OpVarKill OpVarLive OpKeepAlive - OpRegKill OpInt64Make OpInt64Hi OpInt64Lo @@ -24081,11 +24080,6 @@ var opcodeTable = [...]opInfo{ argLen: 2, generic: true, }, - { - name: "RegKill", - argLen: 0, - generic: true, - }, { name: "Int64Make", argLen: 2, diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index bc0a972da4..aafa36d64f 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -242,9 +242,6 @@ type regAllocState struct { // current state of each (preregalloc) Value values []valState - // names associated with each Value - valueNames [][]LocalSlot - // ID of SP, SB values sp, sb ID @@ -303,13 +300,6 @@ type startReg struct { // freeReg frees up register r. Any current user of r is kicked out. func (s *regAllocState) freeReg(r register) { - s.freeOrResetReg(r, false) -} - -// freeOrResetReg frees up register r. Any current user of r is kicked out. -// resetting indicates that the operation is only for bookkeeping, -// e.g. when clearing out state upon entry to a new block. -func (s *regAllocState) freeOrResetReg(r register, resetting bool) { v := s.regs[r].v if v == nil { s.f.Fatalf("tried to free an already free register %d\n", r) @@ -319,16 +309,6 @@ func (s *regAllocState) freeOrResetReg(r register, resetting bool) { if s.f.pass.debug > regDebug { fmt.Printf("freeReg %s (dump %s/%s)\n", &s.registers[r], v, s.regs[r].c) } - if !resetting && s.f.Config.ctxt.Flag_locationlists && len(s.valueNames[v.ID]) != 0 { - kill := s.curBlock.NewValue0(src.NoXPos, OpRegKill, types.TypeVoid) - for int(kill.ID) >= len(s.orig) { - s.orig = append(s.orig, nil) - } - for _, name := range s.valueNames[v.ID] { - s.f.NamedValues[name] = append(s.f.NamedValues[name], kill) - } - s.f.setHome(kill, &s.registers[r]) - } s.regs[r] = regState{} s.values[v.ID].regs &^= regMask(1) << r s.used &^= regMask(1) << r @@ -613,17 +593,6 @@ func (s *regAllocState) init(f *Func) { s.values = make([]valState, f.NumValues()) s.orig = make([]*Value, f.NumValues()) s.copies = make(map[*Value]bool) - if s.f.Config.ctxt.Flag_locationlists { - s.valueNames = make([][]LocalSlot, f.NumValues()) - for slot, values := range f.NamedValues { - if isSynthetic(&slot) { - continue - } - for _, value := range values { - s.valueNames[value.ID] = append(s.valueNames[value.ID], slot) - } - } - } for _, b := range f.Blocks { for _, v := range b.Values { if !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() && !v.Type.IsTuple() { @@ -717,9 +686,7 @@ func (s *regAllocState) liveAfterCurrentInstruction(v *Value) bool { // Sets the state of the registers to that encoded in regs. func (s *regAllocState) setState(regs []endReg) { - for s.used != 0 { - s.freeOrResetReg(pickReg(s.used), true) - } + s.freeRegs(s.used) for _, x := range regs { s.assignReg(x.r, x.v, x.c) } @@ -1035,7 +1002,7 @@ func (s *regAllocState) regalloc(f *Func) { pidx := e.i for _, v := range succ.Values { if v.Op != OpPhi { - continue + break } if !s.values[v.ID].needReg { continue @@ -1598,9 +1565,6 @@ func (s *regAllocState) placeSpills() { for _, b := range f.Blocks { var m regMask for _, v := range b.Values { - if v.Op == OpRegKill { - continue - } if v.Op != OpPhi { break } @@ -1711,7 +1675,7 @@ func (s *regAllocState) placeSpills() { for _, b := range f.Blocks { nphi := 0 for _, v := range b.Values { - if v.Op != OpRegKill && v.Op != OpPhi { + if v.Op != OpPhi { break } nphi++ @@ -1832,9 +1796,6 @@ func (e *edgeState) setup(idx int, srcReg []endReg, dstReg []startReg, stacklive } // Phis need their args to end up in a specific location. for _, v := range e.b.Values { - if v.Op == OpRegKill { - continue - } if v.Op != OpPhi { break } @@ -2094,16 +2055,6 @@ func (e *edgeState) erase(loc Location) { fmt.Printf("v%d no longer available in %s:%s\n", vid, loc, c) } a[i], a = a[len(a)-1], a[:len(a)-1] - if e.s.f.Config.ctxt.Flag_locationlists { - if _, isReg := loc.(*Register); isReg && int(c.ID) < len(e.s.valueNames) && len(e.s.valueNames[c.ID]) != 0 { - kill := e.p.NewValue0(src.NoXPos, OpRegKill, types.TypeVoid) - e.s.f.setHome(kill, loc) - for _, name := range e.s.valueNames[c.ID] { - e.s.f.NamedValues[name] = append(e.s.f.NamedValues[name], kill) - } - } - } - break } } diff --git a/src/cmd/compile/internal/ssa/testdata/hist.gdb-opt.nexts b/src/cmd/compile/internal/ssa/testdata/hist.gdb-opt.nexts index 9ca178492a..d943eb1d58 100644 --- a/src/cmd/compile/internal/ssa/testdata/hist.gdb-opt.nexts +++ b/src/cmd/compile/internal/ssa/testdata/hist.gdb-opt.nexts @@ -14,7 +14,7 @@ dy = dx = 2 dy = 2 63: hist := make([]int, 7) //gdb-opt=(dx/O,dy/O) // TODO sink is missing if this code is in 'test' instead of 'main' -dx = +dx = 2 dy = 64: var reader io.Reader = strings.NewReader(cannedInput) //gdb-dbg=(hist/A) // TODO cannedInput/A is missing if this code is in 'test' instead of 'main' 65: if len(os.Args) > 1 { @@ -116,11 +116,6 @@ scanner = (struct bufio.Scanner *) a = 0 n = 0 t = 0 -88: continue -87: if a == 0 { //gdb-opt=(a,n,t) -a = 3 -n = 0 -t = 0 92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 91: n += a 92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) @@ -147,11 +142,6 @@ t = 3 a = 0 n = 6 t = 9 -88: continue -87: if a == 0 { //gdb-opt=(a,n,t) -a = 2 -n = 6 -t = 9 92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) 91: n += a 92: fmt.Fprintf(os.Stderr, "%d\t%d\t%d\t%d\t%d\n", i, a, n, i*a, t) //gdb-dbg=(n,i,t) @@ -178,5 +168,4 @@ t = 17 a = 0 n = 9 t = 22 -88: continue 98: } diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go index 0d6b1971a6..478a75c4a7 100644 --- a/src/cmd/internal/dwarf/dwarf.go +++ b/src/cmd/internal/dwarf/dwarf.go @@ -44,23 +44,6 @@ type Sym interface { Len() int64 } -// A Location represents a variable's location at a particular PC range. -// It becomes a location list entry in the DWARF. -type Location struct { - StartPC, EndPC int64 - Pieces []Piece -} - -// A Piece represents the location of a particular part of a variable. -// It becomes part of a location list entry (a DW_OP_piece) in the DWARF. -type Piece struct { - Length int64 - StackOffset int32 - RegNum int16 - Missing bool - OnStack bool // if true, RegNum is unset. -} - // A Var represents a local variable or a function parameter. type Var struct { Name string @@ -68,15 +51,17 @@ type Var struct { IsReturnValue bool IsInlFormal bool StackOffset int32 - LocationList []Location - Scope int32 - Type Sym - DeclFile string - DeclLine uint - DeclCol uint - InlIndex int32 // subtract 1 to form real index into InlTree - ChildIndex int32 // child DIE index in abstract function - IsInAbstract bool // variable exists in abstract function + // This package can't use the ssa package, so it can't mention ssa.FuncDebug, + // so indirect through a closure. + PutLocationList func(listSym, startPC Sym) + Scope int32 + Type Sym + DeclFile string + DeclLine uint + DeclCol uint + InlIndex int32 // subtract 1 to form real index into InlTree + ChildIndex int32 // child DIE index in abstract function + IsInAbstract bool // variable exists in abstract function } // A Scope represents a lexical scope. All variables declared within a @@ -1360,10 +1345,10 @@ func determineVarAbbrev(v *Var, fnabbrev int) (int, bool, bool) { // convert to an inline abbreviation and emit an empty location. missing := false switch { - case abbrev == DW_ABRV_AUTO_LOCLIST && len(v.LocationList) == 0: + case abbrev == DW_ABRV_AUTO_LOCLIST && v.PutLocationList == nil: missing = true abbrev = DW_ABRV_AUTO - case abbrev == DW_ABRV_PARAM_LOCLIST && len(v.LocationList) == 0: + case abbrev == DW_ABRV_PARAM_LOCLIST && v.PutLocationList == nil: missing = true abbrev = DW_ABRV_PARAM } @@ -1470,7 +1455,7 @@ func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int, if abbrevUsesLoclist(abbrev) { putattr(ctxt, s.Info, abbrev, DW_FORM_sec_offset, DW_CLS_PTR, int64(s.Loc.Len()), s.Loc) - addLocList(ctxt, s.Loc, s.StartPC, v, encbuf) + v.PutLocationList(s.Loc, s.StartPC) } else { loc := encbuf[:0] switch { @@ -1488,45 +1473,6 @@ func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int, // Var has no children => no terminator } -func addLocList(ctxt Context, listSym, startPC Sym, v *Var, encbuf []byte) { - // Base address entry: max ptr followed by the base address. - ctxt.AddInt(listSym, ctxt.PtrSize(), ^0) - ctxt.AddAddress(listSym, startPC, 0) - for _, entry := range v.LocationList { - ctxt.AddInt(listSym, ctxt.PtrSize(), entry.StartPC) - ctxt.AddInt(listSym, ctxt.PtrSize(), entry.EndPC) - locBuf := encbuf[:0] - for _, piece := range entry.Pieces { - if !piece.Missing { - if piece.OnStack { - if piece.StackOffset == 0 { - locBuf = append(locBuf, DW_OP_call_frame_cfa) - } else { - locBuf = append(locBuf, DW_OP_fbreg) - locBuf = AppendSleb128(locBuf, int64(piece.StackOffset)) - } - } else { - if piece.RegNum < 32 { - locBuf = append(locBuf, DW_OP_reg0+byte(piece.RegNum)) - } else { - locBuf = append(locBuf, DW_OP_regx) - locBuf = AppendUleb128(locBuf, uint64(piece.RegNum)) - } - } - } - if len(entry.Pieces) > 1 { - locBuf = append(locBuf, DW_OP_piece) - locBuf = AppendUleb128(locBuf, uint64(piece.Length)) - } - } - ctxt.AddInt(listSym, 2, int64(len(locBuf))) - ctxt.AddBytes(listSym, locBuf) - } - // End list - ctxt.AddInt(listSym, ctxt.PtrSize(), 0) - ctxt.AddInt(listSym, ctxt.PtrSize(), 0) -} - // VarsByOffset attaches the methods of sort.Interface to []*Var, // sorting in increasing StackOffset. type VarsByOffset []*Var