]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: reimplement location list generation
authorHeschi Kreinick <heschi@google.com>
Thu, 26 Oct 2017 19:40:17 +0000 (15:40 -0400)
committerHeschi Kreinick <heschi@google.com>
Wed, 14 Feb 2018 18:29:19 +0000 (18:29 +0000)
Completely redesign and reimplement location list generation to be more
efficient, and hopefully not too hard to understand.

RegKills are gone. Instead of using the regalloc's liveness
calculations, redo them using the Ops' clobber information. Besides
saving a lot of Values, this avoids adding RegKills to blocks that would
be empty otherwise, which was messing up optimizations. This does mean
that it's much harder to tell whether the generation process is buggy
(there's nothing to cross-check it with), and there may be disagreements
with GC liveness. But the performance gain is significant, and it's nice
not to be messing with earlier compiler phases.

The intermediate representations are gone. Instead of producing
ssa.BlockDebugs, then dwarf.LocationLists, and then finally real
location lists, go directly from the SSA to a (mostly) real location
list. Because the SSA analysis happens before assembly, it stores
encoded block/value IDs where PCs would normally go. It would be easier
to do the SSA analysis after assembly, but I didn't want to retain the
SSA just for that.

Generation proceeds in two phases: first, it traverses the function in
CFG order, storing the state of the block at the beginning and end. End
states are used to produce the start states of the successor blocks. In
the second phase, it traverses in program text order and produces the
location lists. The processing in the second phase is redundant, but
much cheaper than storing the intermediate representation. It might be
possible to combine the two phases somewhat to take advantage of cases
where the CFG matches the block layout, but I haven't tried.

Location lists are finalized by adding a base address selection entry,
translating each encoded block/value ID to a real PC, and adding the
terminating zero entry. This probably won't work on OSX, where dsymutil
will choke on the base address selection. I tried emitting CU-relative
relocations for each address, and it was *very* bad for performance --
it uses more memory storing all the relocations than it does for the
actual location list bytes. I think I'm going to end up synthesizing the
relocations in the linker only on OSX, but TBD.

TestNexting needs updating: with more optimizations working, the
debugger doesn't stop on the continue (line 88) any more, and the test's
duplicate suppression kicks in. Also, dx and dy live a little longer
now, but they have the correct values.

Change-Id: Ie772dfe23a4e389ca573624fac4d05401ae32307
Reviewed-on: https://go-review.googlesource.com/89356
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
src/cmd/compile/fmt_test.go
src/cmd/compile/internal/gc/pgen.go
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/ssa/cache.go
src/cmd/compile/internal/ssa/check.go
src/cmd/compile/internal/ssa/debug.go
src/cmd/compile/internal/ssa/gen/genericOps.go
src/cmd/compile/internal/ssa/opGen.go
src/cmd/compile/internal/ssa/regalloc.go
src/cmd/compile/internal/ssa/testdata/hist.gdb-opt.nexts
src/cmd/internal/dwarf/dwarf.go

index ca6e6d0b691168916c765e286c8c7f87b9ec41d9..bfc72109852edf62ed6f44d64133af87b84867a8 100644 (file)
@@ -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":                                        "",
index 6fa301659ddbb7a9bb3674e1f438b27dabcbad98..80b771a831b53c8a5ed07737f79759dea6ce85ff 100644 (file)
@@ -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<<uint8(reg)) != 0 {
-                                               dpiece.RegNum = Ctxt.Arch.DWARFRegisters[debugInfo.Registers[reg].ObjNum()]
-                                       }
-                               }
-                       }
-                       dloc.Pieces = append(dloc.Pieces, dpiece)
-               }
-               if present == 0 {
-                       if Debug_locationlist != 0 {
-                               Ctxt.Logf(" -> 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
index 5ec01b6a61f802481ad9d32f93b22aee66377102..b512b10e0128d36f3718905a5fee1b242d95e466 100644 (file)
@@ -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)
                        }
                }
        }
index 8434084bde28e4a2fa680c6137f2ba4e70d388fb..f1018da497fc63a5e806f67f36d1ac7d887d7999 100644 (file)
@@ -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
 }
index 1c2fcd7948b691629f6a7505b0fbf7be63049274..de77aff480310fce892f20fe2f1473135155850a 100644 (file)
@@ -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
                                }
index dcef9f2447ee557acff93a4c86a978e438ae5fa2..8ec146287c2707c4cda62cf5de7b398afeb666cc 100644 (file)
@@ -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
+
+       // Filled in by the user. Translates Block and Value ID to PC.
+       GetPC func(ID, ID) int64
 }
 
-func (f *FuncDebug) BlockString(b *BlockDebug) string {
-       var vars []string
+type BlockDebug struct {
+       // The SSA block that this tracks. For debug logging only.
+       Block *Block
+       // 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
+}
 
-       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, ", "))
+// A liveSlot is a slot that's live in loc at entry/exit of a block.
+type liveSlot struct {
+       slot SlotID
+       loc  VarLoc
 }
 
-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, " ")
+// 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
 }
 
-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
+// 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 {
+func (b *BlockDebug) LocString(loc VarLoc) string {
+       if loc.absent() {
+               return "<nil>"
+       }
        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, ","))
-
-}
-
-// 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]
+       return strings.Join(storage, ",")
 }
 
-// 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
+       // See FuncDebug.
+       slots    []*LocalSlot
+       vars     []GCNode
+       varSlots [][]SlotID
+
+       // The user variable that each slot rolls up to, indexed by SlotID.
+       slotVars []VarID
+
        f              *Func
+       loggingEnabled bool
        cache          *Cache
-       numRegisters   int
+       registers      []Register
+       stackOffset    func(*LocalSlot) int32
+
+       // The names (slots) associated with each value, indexed by Value ID.
+       valueNames [][]SlotID
 
-       // working storage for BuildFuncDebug, reused between blocks.
-       registerContents [][]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))
+               }
+       }
+
+       if len(strs) == 1 {
+               return "(no vars)\n"
        }
-       return f.BlockString(b)
+       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))
                }
+
+               topSlot := &slot
+               for topSlot.SplitOf != nil {
+                       topSlot = topSlot.SplitOf
+               }
+               if _, ok := varParts[topSlot.N]; !ok {
+                       state.vars = append(state.vars, topSlot.N)
+               }
+               varParts[topSlot.N] = append(varParts[topSlot.N], 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))
+       // 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.logf("Name table: %v\n", strings.Join(names, ", "))
        }
+       state.changedVars = make([]bool, len(state.vars))
 
-       // Build up block states, starting with the first block, then
-       // processing blocks once their predecessors have been processed.
+       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 == '~'
+}
 
-       // Location list entries for each block.
-       blockLocs := make([]*BlockDebug, f.NumBlocks())
+// 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, _ := state.f.getHome(v.ID).(*Register)
+                       state.processValue(v, slots, reg)
+               }
 
-                       reg, _ := f.getHome(v.ID).(*Register)
-                       state.processValue(locs, v, slots, reg)
+               if state.loggingEnabled {
+                       state.f.Logf("Block %v done, locs:\n%v", b, state.stateString(locs, state.currentState))
                }
 
-               // 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 {
+               for slotID, slotLoc := range state.currentState.slots {
+                       if slotLoc.absent() {
                                continue
                        }
-                       last.End = BlockEnd
+                       locs.endState = append(locs.endState, liveSlot{SlotID(slotID), slotLoc})
                }
-               if state.loggingEnabled {
-                       f.Logf("Block done: locs %v, regs %v\n", state.BlockString(locs), state.registerContents)
-               }
-               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)))
-               }
+               blockLocs[b.ID] = locs
        }
-       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 && 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 state.loggingEnabled {
+               state.logf("Merging %v into %v\n", preds, b)
        }
-       for i := 1; i < len(preds); i++ {
-               p := preds[i]
+
+       if len(preds) == 0 {
                if state.loggingEnabled {
-                       state.logf("Merging in state from %v: %v &= %v\n", p, live, state.BlockString(blockLocs[p.ID]))
                }
+               state.currentState.reset(nil)
+               return result
+       }
 
-               for slot, liveVar := range live {
-                       liveLoc := liveVar.last()
-                       if liveLoc == nil {
-                               continue
-                       }
+       if len(preds) == 1 {
+               p := blockLocs[preds[0].ID]
+               result.startState = p.endState
+               state.currentState.reset(p.endState)
+               return result
+       }
 
-                       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
-                       }
+       if state.loggingEnabled {
+               state.logf("Starting %v with state from %v:\n%v", b, preds[0], state.blockEndStateString(blockLocs[preds[0].ID]))
+       }
 
-                       // Unify storage locations.
-                       if !liveLoc.OnStack || !predLoc.OnStack || liveLoc.StackLocation != predLoc.StackLocation {
+       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++ {
+               if state.loggingEnabled {
+                       state.logf("Merging in state from %v:\n%v", preds[i], state.blockEndStateString(blockLocs[preds[i].ID]))
+               }
+               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.registerContents {
-               state.registerContents[reg] = state.registerContents[reg][:0]
+       for reg := range state.currentState.registers {
+               state.currentState.registers[reg] = state.currentState.registers[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
+               }
+               // Seen in only some predecessors. Clear it out.
+               if count[slotID] != len(preds) {
+                       slotLocs[slotID] = VarLoc{}
+                       continue
+               }
+               // Present in all predecessors.
+               result.startState = append(result.startState, liveSlot{SlotID(slotID), slotLoc})
+               if slotLoc.Registers == 0 {
                        continue
                }
-               for reg := 0; reg < state.numRegisters; reg++ {
-                       if loc.Registers&(1<<uint8(reg)) != 0 {
-                               state.registerContents[reg] = append(state.registerContents[reg], SlotID(slot))
+               for reg, regMask := 0, 1; reg < len(state.registers); reg, regMask = reg+1, regMask<<1 {
+                       if slotLoc.Registers&RegisterSet(regMask) != 0 {
+                               state.currentState.registers[reg] = append(state.currentState.registers[reg], SlotID(slotID))
                        }
                }
        }
-       return locs
+       return result
 }
 
 // processValue updates locs and state.registerContents to reflect v, a value with
 // the names in vSlots and homed in vReg.  "v" becomes visible after execution of
-// the instructions evaluating it.
-func (state *debugState) processValue(locs *BlockDebug, v *Value, vSlots []SlotID, vReg *Register) {
-       switch {
-       case v.Op == OpRegKill:
-               if state.loggingEnabled {
-                       existingSlots := make([]bool, len(state.slots))
-                       for _, slot := range state.registerContents[vReg.num] {
-                               existingSlots[slot] = true
-                       }
-                       for _, slot := range vSlots {
-                               if existingSlots[slot] {
-                                       existingSlots[slot] = false
-                               } else {
-                                       state.unexpected(v, "regkill of unassociated name %v\n", state.slots[slot])
-                               }
-                       }
-                       for slot, live := range existingSlots {
-                               if live {
-                                       state.unexpected(v, "leftover register name: %v\n", state.slots[slot])
-                               }
-                       }
-               }
-               state.registerContents[vReg.num] = nil
+// the instructions evaluating it. It returns which VarIDs were modified by the
+// Value's execution.
+func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) {
+       locs := state.currentState
+       setSlot := func(slot SlotID, loc VarLoc) {
+               state.changedVars[state.slotVars[slot]] = true
+               state.currentState.slots[slot] = loc
+       }
 
-               for _, slot := range vSlots {
-                       last := locs.lastLoc(slot)
-                       if last == nil {
-                               state.unexpected(v, "regkill of already dead %s, %+v\n", vReg, state.slots[slot])
+       // Handle any register clobbering. Call operations, for example,
+       // clobber all registers even though they don't explicitly write to
+       // them.
+       if clobbers := opcodeTable[v.Op].reg.clobbers; clobbers != 0 {
+               for reg := 0; reg < len(state.registers); reg++ {
+                       if clobbers&(1<<uint8(reg)) == 0 {
                                continue
                        }
-                       if state.loggingEnabled {
-                               state.logf("at %v: %v regkilled out of %s\n", v.ID, state.slots[slot], vReg)
-                       }
-                       if last.End != nil {
-                               state.unexpected(v, "regkill of dead slot, died at %v\n", last.End)
-                       }
-                       last.End = v
 
-                       regs := last.Registers &^ (1 << uint8(vReg.num))
-                       if !last.OnStack && regs == 0 {
-                               continue
+                       for _, slot := range locs.registers[reg] {
+                               if state.loggingEnabled {
+                                       state.logf("at %v: %v clobbered out of %v\n", v.ID, 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 << uint8(reg))
+                               setSlot(slot, VarLoc{regs, last.OnStack, last.StackOffset})
                        }
-                       loc := state.cache.NewVarLoc()
-                       loc.Start = v
-                       loc.OnStack = last.OnStack
-                       loc.StackLocation = last.StackLocation
-                       loc.Registers = regs
-                       locs.append(slot, loc)
+
+                       locs.registers[reg] = locs.registers[reg][:0]
                }
+       }
+
+       switch {
        case v.Op == OpArg:
+               home := state.f.getHome(v.ID).(LocalSlot)
+               stackOffset := state.stackOffset(&home)
                for _, slot := range vSlots {
-                       if last := locs.lastLoc(slot); last != nil {
-                               state.unexpected(v, "Arg op on already-live slot %v", state.slots[slot])
-                               last.End = v
-                       }
-                       loc := state.cache.NewVarLoc()
-                       loc.Start = v
-                       loc.OnStack = true
-                       loc.StackLocation = state.getHomeSlot(v)
-                       locs.append(slot, loc)
                        if state.loggingEnabled {
-                               state.logf("at %v: arg %v now on stack in location %v\n", v.ID, state.slots[slot], state.slots[loc.StackLocation])
+                               state.logf("at %v: arg %v now on stack in location %v\n", v.ID, state.slots[slot], home)
+                               if last := locs.slots[slot]; !last.absent() {
+                                       state.unexpected(v, "Arg op on already-live slot %v", state.slots[slot])
+                               }
                        }
+
+                       setSlot(slot, VarLoc{0, true, stackOffset})
                }
 
        case v.Op == OpStoreReg:
+               home := state.f.getHome(v.ID).(LocalSlot)
+               stackOffset := state.stackOffset(&home)
                for _, slot := range vSlots {
-                       last := locs.lastLoc(slot)
-                       if last == nil {
+                       last := locs.slots[slot]
+                       if last.absent() {
                                state.unexpected(v, "spill of unnamed register %s\n", vReg)
                                break
                        }
-                       last.End = v
-                       loc := state.cache.NewVarLoc()
-                       loc.Start = v
-                       loc.OnStack = true
-                       loc.StackLocation = state.getHomeSlot(v)
-                       loc.Registers = last.Registers
-                       locs.append(slot, loc)
+
+                       setSlot(slot, VarLoc{last.Registers, true, stackOffset})
                        if state.loggingEnabled {
-                               state.logf("at %v: %v spilled to stack location %v\n", v.ID, state.slots[slot], state.slots[loc.StackLocation])
+                               state.logf("at %v: %v spilled to stack location %v\n", v.ID, state.slots[slot], home)
                        }
                }
 
@@ -542,33 +500,369 @@ func (state *debugState) processValue(locs *BlockDebug, v *Value, vSlots []SlotI
                                newSlots[slot] = true
                        }
 
-                       for _, slot := range state.registerContents[vReg.num] {
+                       for _, slot := range locs.registers[vReg.num] {
                                if !newSlots[slot] {
-                                       state.unexpected(v, "%v clobbered\n", state.slots[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.OnStack, 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.loggingEnabled {
                                state.logf("at %v: %v now in %s\n", v.ID, state.slots[slot], vReg)
                        }
-                       last := locs.lastLoc(slot)
-                       if last != nil && last.End == nil {
-                               last.End = v
-                       }
-                       state.registerContents[vReg.num] = append(state.registerContents[vReg.num], slot)
-                       loc := state.cache.NewVarLoc()
-                       loc.Start = v
-                       if last != nil {
+                       var loc VarLoc
+                       loc.Registers |= 1 << uint8(vReg.num)
+                       if last := locs.slots[slot]; !last.absent() {
                                loc.OnStack = last.OnStack
-                               loc.StackLocation = last.StackLocation
-                               loc.Registers = last.Registers
+                               loc.StackOffset = last.StackOffset
+                               loc.Registers |= last.Registers
                        }
-                       loc.Registers |= 1 << uint8(vReg.num)
-                       locs.append(slot, loc)
+                       setSlot(slot, loc)
+               }
+       }
+}
+
+// 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 *LocalSlot) int64 {
+       offset := slot.Off
+       for ; slot.SplitOf != nil; slot = slot.SplitOf {
+               offset += slot.SplitOffset
+       }
+       return offset
+}
+
+// This type is deleted in a subsequent CL.
+type varPart struct {
+       varOffset int64
+       slot      SlotID
+}
+
+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] }
+
+// A pendingEntry represents the beginning of a location list entry, missing
+// only its end coordinate.
+type pendingEntry struct {
+       present                bool
+       startBlock, startValue ID
+       // The location of each piece of the variable, indexed by *SlotID*,
+       // even though only a few slots are used in each entry. This could be
+       // improved by only storing the relevant slots.
+       pieces []VarLoc
+}
+
+func (e *pendingEntry) clear() {
+       e.present = false
+       e.startBlock = 0
+       e.startValue = 0
+       for i := range e.pieces {
+               e.pieces[i] = VarLoc{}
+       }
+}
+
+// canMerge returns true if the location description for new is the same as
+// pending.
+func canMerge(pending, new VarLoc) bool {
+       if pending.absent() && new.absent() {
+               return true
+       }
+       if pending.absent() || new.absent() {
+               return false
+       }
+       if pending.OnStack {
+               return new.OnStack && pending.StackOffset == new.StackOffset
+       }
+       if pending.Registers != 0 && new.Registers != 0 {
+               return firstReg(pending.Registers) == firstReg(new.Registers)
+       }
+       return false
+}
+
+// firstReg returns the first register in set that is present.
+func firstReg(set RegisterSet) uint8 {
+       for reg := 0; reg < 64; reg++ {
+               if set&(1<<uint8(reg)) != 0 {
+                       return uint8(reg)
+               }
+       }
+       // This is wrong, but there seem to be some situations where we
+       // produce locations with no storage.
+       return 0
+}
+
+// buildLocationLists builds location lists for all the user variables in
+// state.f, using the information about block state in blockLocs.
+// The returned location lists are not fully complete. They are in terms of
+// SSA values rather than PCs, and have no base address/end entries. They will
+// be finished by PutLocationList.
+func (state *debugState) buildLocationLists(Ctxt *obj.Link, stackOffset func(*LocalSlot) int32, blockLocs []*BlockDebug) [][]byte {
+       lists := make([][]byte, len(state.vars))
+       varParts := make([][]varPart, len(lists))
+       pendingEntries := make([]pendingEntry, len(lists))
+
+       for varID, parts := range state.varSlots {
+               for _, slotID := range parts {
+                       varParts[varID] = append(varParts[varID], varPart{varOffset(state.slots[slotID]), slotID})
+               }
+               // Get the order the parts need to be in to represent the memory
+               // of the decomposed user variable.
+               sort.Sort(partsByVarOffset(varParts[varID]))
+
+               pendingEntries[varID].pieces = make([]VarLoc, len(state.slots))
+       }
+
+       // writePendingEntry writes out the pending entry for varID, if any,
+       // terminated at endBlock/Value.
+       writePendingEntry := func(varID VarID, endBlock, endValue ID) {
+               list := lists[varID]
+               pending := pendingEntries[varID]
+               if !pending.present {
+                       return
+               }
+
+               // Pack the start/end coordinates into the start/end addresses
+               // of the entry, for decoding by PutLocationList.
+               start, startOK := encodeValue(Ctxt, pending.startBlock, pending.startValue)
+               end, endOK := encodeValue(Ctxt, endBlock, endValue)
+               if !startOK || !endOK {
+                       // If someone writes a function that uses >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:
-               state.unexpected(v, "named value with no reg\n")
+               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")
+       }
+
 }
index 5ce11c7e8796e50397cdd4312ec3d68f7610f2a8..e970b8519b3d4815c848f2a96768a97f03a4f6c1 100644 (file)
@@ -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
index 0c2b8f61c6dc9612ea348eeeeab56943ec1e74bc..43343d7d5c0ec3e7b5d49f69b271e06d08312ec8 100644 (file)
@@ -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,
index bc0a972da46a542e59d2bcb6f9d0c6e6a170fbaf..aafa36d64f4cb1f0292a7ab2b2101aa82fd51077 100644 (file)
@@ -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
                }
        }
index 9ca178492a4583af4c446bc6d77830fc965b6c76..d943eb1d58f52aa3d794e4bd991b92689e82676a 100644 (file)
@@ -14,7 +14,7 @@ dy = <Optimized out, as expected>
 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 = <Optimized out, as expected>
+dx = 2
 dy = <Optimized out, as expected>
 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>
 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:    }
index 0d6b1971a6a43d2544f8e156113a7fd8f64b4a43..478a75c4a7d7f33f01cd77df44358a83c2d3e9f8 100644 (file)
@@ -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