diff --git a/src/cmd/compile/fmt_test.go b/src/cmd/compile/fmt_test.go
index 59de326a91947bd0a3dd8156542cb8de5cae2554..2052a4200ef5e96e7c1db7acc523a5663e36bd93 100644
--- a/src/cmd/compile/fmt_test.go
+++ b/src/cmd/compile/fmt_test.go
@@ -571,9 +571,16 @@ var knownFormats = map[string]string{
 	"*cmd/compile/internal/ssa.Block %s":              "",
 	"*cmd/compile/internal/ssa.Block %v":              "",
 	"*cmd/compile/internal/ssa.Func %s":               "",
+	"*cmd/compile/internal/ssa.Func %v":               "",
+	"*cmd/compile/internal/ssa.FuncDebug %v":          "",
+	"*cmd/compile/internal/ssa.LocalSlot %+v":         "",
+	"*cmd/compile/internal/ssa.LocalSlot %v":          "",
+	"*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.VarLoc %v":             "",
 	"*cmd/compile/internal/ssa.sparseTreeMapEntry %v": "",
 	"*cmd/compile/internal/types.Field %p":            "",
 	"*cmd/compile/internal/types.Field %v":            "",
@@ -592,6 +599,7 @@ 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":                       "",
 	"*cmd/internal/obj.Prog %s":                       "",
@@ -600,17 +608,21 @@ 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":        "",
 	"[]string %v":                                     "",
 	"bool %v":                                         "",
 	"byte %08b":                                       "",
 	"byte %c":                                         "",
 	"cmd/compile/internal/arm.shift %d":               "",
 	"cmd/compile/internal/gc.Class %d":                "",
+	"cmd/compile/internal/gc.Class %v":                "",
 	"cmd/compile/internal/gc.Ctype %d":                "",
 	"cmd/compile/internal/gc.Ctype %v":                "",
 	"cmd/compile/internal/gc.Level %d":                "",
@@ -630,11 +642,13 @@ var knownFormats = map[string]string{
 	"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 %v":           "",
 	"cmd/compile/internal/ssa.Location %v":            "",
 	"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":            "",
@@ -648,6 +662,7 @@ var knownFormats = map[string]string{
 	"cmd/compile/internal/types.EType %d":             "",
 	"cmd/compile/internal/types.EType %s":             "",
 	"cmd/compile/internal/types.EType %v":             "",
+	"cmd/internal/dwarf.Location %#v":                 "",
 	"cmd/internal/src.Pos %s":                         "",
 	"cmd/internal/src.Pos %v":                         "",
 	"error %v":                                        "",
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 2b61564ad8d8cb3819c85f8e371bcd3963a07a64..a1f4767c8f0b9df96f6d47784f58b51a012dc0b5 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -44,6 +44,7 @@ var (
 	Debug_vlog         bool
 	Debug_wb           int
 	Debug_pctab        string
+	Debug_locationlist int
 )
 
 // Debug arguments.
@@ -69,6 +70,7 @@ var debugtab = []struct {
 	{"wb", "print information about write barriers", &Debug_wb},
 	{"export", "print export data", &Debug_export},
 	{"pctab", "print named pc-value table", &Debug_pctab},
+	{"locationlists", "print information about DWARF location list creation", &Debug_locationlist},
 }
 
 const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
@@ -192,6 +194,7 @@ func Main(archInit func(*Arch)) {
 	flag.BoolVar(&pure_go, "complete", false, "compiling complete package (no C or assembly)")
 	flag.StringVar(&debugstr, "d", "", "print debug information about items in `list`; try -d help")
 	flag.BoolVar(&flagDWARF, "dwarf", true, "generate DWARF symbols")
+	flag.BoolVar(&Ctxt.Flag_locationlists, "dwarflocationlists", false, "add location lists to DWARF in optimized mode")
 	objabi.Flagcount("e", "no limit on number of errors reported", &Debug['e'])
 	objabi.Flagcount("f", "debug stack frames", &Debug['f'])
 	objabi.Flagcount("h", "halt on error", &Debug['h'])
@@ -298,6 +301,9 @@ func Main(archInit func(*Arch)) {
 	if nBackendWorkers > 1 && !concurrentBackendAllowed() {
 		log.Fatalf("cannot use concurrent backend compilation with provided flags; invoked as %v", os.Args)
 	}
+	if Ctxt.Flag_locationlists && len(Ctxt.Arch.DWARFRegisters) == 0 {
+		log.Fatalf("location lists requested but register mapping not available on %v", Ctxt.Arch.Name)
+	}
 
 	// parse -d argument
 	if debugstr != "" {
@@ -383,7 +389,7 @@ func Main(archInit func(*Arch)) {
 		Debug['l'] = 1 - Debug['l']
 	}
 
-	trackScopes = flagDWARF && Debug['l'] == 0 && Debug['N'] != 0
+	trackScopes = flagDWARF && ((Debug['l'] == 0 && Debug['N'] != 0) || Ctxt.Flag_locationlists)
 
 	Widthptr = thearch.LinkArch.PtrSize
 	Widthreg = thearch.LinkArch.RegSize
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index d301ae19c8dd7e7426e36b733578f232615f03a7..542fd43b63117ccf0868bfe7b6fdbbeb57ba394f 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -13,6 +13,7 @@ import (
 	"cmd/internal/src"
 	"cmd/internal/sys"
 	"fmt"
+	"math"
 	"math/rand"
 	"sort"
 	"sync"
@@ -303,47 +304,31 @@ func compileFunctions() {
 
 func debuginfo(fnsym *obj.LSym, curfn interface{}) []dwarf.Scope {
 	fn := curfn.(*Node)
+	debugInfo := fn.Func.DebugInfo
+	fn.Func.DebugInfo = nil
 	if expect := fn.Func.Nname.Sym.Linksym(); fnsym != expect {
 		Fatalf("unexpected fnsym: %v != %v", fnsym, expect)
 	}
 
-	var dwarfVars []*dwarf.Var
-	var varScopes []ScopeID
-
+	var automDecls []*Node
+	// Populate Automs for fn.
 	for _, n := range fn.Func.Dcl {
 		if n.Op != ONAME { // might be OTYPE or OLITERAL
 			continue
 		}
-
 		var name obj.AddrName
-		var abbrev int
-		offs := n.Xoffset
-
 		switch n.Class() {
 		case PAUTO:
 			if !n.Name.Used() {
 				Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)")
 			}
 			name = obj.NAME_AUTO
-
-			abbrev = dwarf.DW_ABRV_AUTO
-			if Ctxt.FixedFrameSize() == 0 {
-				offs -= int64(Widthptr)
-			}
-			if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
-				offs -= int64(Widthptr)
-			}
-
 		case PPARAM, PPARAMOUT:
 			name = obj.NAME_PARAM
-
-			abbrev = dwarf.DW_ABRV_PARAM
-			offs += Ctxt.FixedFrameSize()
-
 		default:
 			continue
 		}
-
+		automDecls = append(automDecls, n)
 		gotype := ngotype(n).Linksym()
 		fnsym.Func.Autom = append(fnsym.Func.Autom, &obj.Auto{
 			Asym:    Ctxt.Lookup(n.Sym.Name),
@@ -351,32 +336,336 @@ func debuginfo(fnsym *obj.LSym, curfn interface{}) []dwarf.Scope {
 			Name:    name,
 			Gotype:  gotype,
 		})
+	}
+
+	var dwarfVars []*dwarf.Var
+	var decls []*Node
+	if Ctxt.Flag_locationlists && Ctxt.Flag_optimize {
+		decls, dwarfVars = createComplexVars(fn, debugInfo)
+	} else {
+		decls, dwarfVars = createSimpleVars(automDecls)
+	}
+
+	var varScopes []ScopeID
+	for _, decl := range decls {
+		var scope ScopeID
+		if !decl.Name.Captured() && !decl.Name.Byval() {
+			// n.Pos of captured variables is their first
+			// use in the closure but they should always
+			// be assigned to scope 0 instead.
+			// TODO(mdempsky): Verify this.
+			scope = findScope(fn.Func.Marks, decl.Pos)
+		}
+		varScopes = append(varScopes, scope)
+	}
+	return assembleScopes(fnsym, fn, dwarfVars, varScopes)
+}
 
+// createSimpleVars creates a DWARF entry for every variable declared in the
+// function, claiming that they are permanently on the stack.
+func createSimpleVars(automDecls []*Node) ([]*Node, []*dwarf.Var) {
+	var vars []*dwarf.Var
+	var decls []*Node
+	for _, n := range automDecls {
 		if n.IsAutoTmp() {
 			continue
 		}
+		var abbrev int
+		offs := n.Xoffset
+
+		switch n.Class() {
+		case PAUTO:
+			abbrev = dwarf.DW_ABRV_AUTO
+			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
+			offs += Ctxt.FixedFrameSize()
+		default:
+			Fatalf("createSimpleVars unexpected type %v for node %v", n.Class(), n)
+		}
 
-		typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
-		dwarfVars = append(dwarfVars, &dwarf.Var{
+		typename := dwarf.InfoPrefix + typesymname(n.Type)
+		decls = append(decls, n)
+		vars = append(vars, &dwarf.Var{
 			Name:        n.Sym.Name,
 			Abbrev:      abbrev,
 			StackOffset: int32(offs),
 			Type:        Ctxt.Lookup(typename),
 		})
+	}
+	return decls, vars
+}
 
-		var scope ScopeID
-		if !n.Name.Captured() && !n.Name.Byval() {
-			// n.Pos of captured variables is their first
-			// use in the closure but they should always
-			// be assigned to scope 0 instead.
-			// TODO(mdempsky): Verify this.
-			scope = findScope(fn.Func.Marks, n.Pos)
+type varPart struct {
+	varOffset int64
+	slot      ssa.SlotID
+	locs      ssa.VarLocList
+}
+
+func createComplexVars(fn *Node, debugInfo *ssa.FuncDebug) ([]*Node, []*dwarf.Var) {
+	for _, locList := range debugInfo.Variables {
+		for _, loc := range locList.Locations {
+			if loc.StartProg != nil {
+				loc.StartPC = loc.StartProg.Pc
+			}
+			if loc.EndProg != nil {
+				loc.EndPC = loc.EndProg.Pc
+			}
+			if Debug_locationlist == 0 {
+				loc.EndProg = nil
+				loc.StartProg = nil
+			}
 		}
+	}
 
-		varScopes = append(varScopes, scope)
+	// Group SSA variables by the user variable they were decomposed from.
+	varParts := map[*Node][]varPart{}
+	for slotID, locList := range debugInfo.Variables {
+		if len(locList.Locations) == 0 {
+			continue
+		}
+		slot := debugInfo.Slots[slotID]
+		for slot.SplitOf != nil {
+			slot = slot.SplitOf
+		}
+		n := slot.N.(*Node)
+		varParts[n] = append(varParts[n], varPart{varOffset(slot), ssa.SlotID(slotID), locList})
 	}
 
-	return assembleScopes(fnsym, fn, dwarfVars, varScopes)
+	// 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.Slots {
+		for slot.SplitOf != nil {
+			slot = slot.SplitOf
+		}
+		n := slot.N.(*Node)
+		parts := varParts[n]
+		if parts == nil {
+			continue
+		}
+
+		// Get the order the parts need to be in to represent the memory
+		// of the decomposed user variable.
+		sort.Sort(partsByVarOffset(parts))
+
+		if dvar := createComplexVar(debugInfo, n, parts); dvar != nil {
+			decls = append(decls, n)
+			vars = append(vars, dvar)
+		}
+	}
+	return decls, vars
+}
+
+// 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] }
+
+// 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
+	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
+	}
+
+	gotype := ngotype(n).Linksym()
+	typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
+	// The stack offset is used as a sorting key, so for decomposed
+	// variables just give it the lowest one. It's not used otherwise.
+	stackOffset := debugInfo.Slots[parts[0].slot].N.(*Node).Xoffset + offs
+	dvar := &dwarf.Var{
+		Name:        n.Sym.Name,
+		Abbrev:      abbrev,
+		Type:        Ctxt.Lookup(typename),
+		StackOffset: int32(stackOffset),
+	}
+
+	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], part.locs)
+		}
+	}
+
+	// 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
+
+	curLoc := make([]int, len(slots))
+
+	// findBoundaryAfter finds the next beginning or end of a piece after currentPC.
+	findBoundaryAfter := func(currentPC int64) int64 {
+		min := int64(math.MaxInt64)
+		for slot, 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 := curLoc[slot]; i < len(part.locs.Locations); i++ {
+				for _, pc := range [2]int64{part.locs.Locations[i].StartPC, part.locs.Locations[i].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 := -1
+			for i := curLoc[part.slot]; i < len(part.locs.Locations); i++ {
+				if part.locs.Locations[i].StartPC > start {
+					break //overshot
+				}
+				choice = i // best yet
+			}
+			if choice != -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(),
+			}
+			locIdx := curLoc[part.slot]
+			if locIdx >= len(part.locs.Locations) ||
+				start >= part.locs.Locations[locIdx].EndPC ||
+				end <= part.locs.Locations[locIdx].StartPC {
+				if Debug_locationlist != 0 {
+					Ctxt.Logf("\t%v: missing", slots[part.slot])
+				}
+				dpiece.Missing = true
+				dloc.Pieces = append(dloc.Pieces, dpiece)
+				continue
+			}
+			present++
+			loc := part.locs.Locations[locIdx]
+			if Debug_locationlist != 0 {
+				Ctxt.Logf("\t%v: %v", slots[part.slot], loc)
+			}
+			if loc.OnStack {
+				dpiece.OnStack = true
+				dpiece.StackOffset = int32(offs + slots[part.slot].Off + slots[part.slot].N.(*Node).Xoffset)
+			} 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)
+		}
+	}
+	return dvar
 }
 
 // fieldtrack adds R_USEFIELD relocations to fnsym to record any
diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go
index 1ca0a615353afe1f3962bc7a573591f0806143e9..bd4453fa842120040f390bd5e6b99aaba5f61169 100644
--- a/src/cmd/compile/internal/gc/sizeof_test.go
+++ b/src/cmd/compile/internal/gc/sizeof_test.go
@@ -22,7 +22,7 @@ func TestSizeof(t *testing.T) {
 		_32bit uintptr     // size on 32bit platforms
 		_64bit uintptr     // size on 64bit platforms
 	}{
-		{Func{}, 124, 216},
+		{Func{}, 128, 224},
 		{Name{}, 36, 56},
 		{Param{}, 28, 56},
 		{Node{}, 76, 128},
diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go
index f8aefaae5e76020744bd8375443c4632d868365e..c769efe8cd3144f589d340b1a82cbad79e4c18b7 100644
--- a/src/cmd/compile/internal/gc/ssa.go
+++ b/src/cmd/compile/internal/gc/ssa.go
@@ -4384,6 +4384,7 @@ func genssa(f *ssa.Func, pp *Progs) {
 	s.pp = pp
 	var progToValue map[*obj.Prog]*ssa.Value
 	var progToBlock map[*obj.Prog]*ssa.Block
+	var valueToProg []*obj.Prog
 	var logProgs = e.log
 	if logProgs {
 		progToValue = make(map[*obj.Prog]*ssa.Value, f.NumValues())
@@ -4398,6 +4399,11 @@ 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)
+		valueToProg = make([]*obj.Prog, f.NumValues())
+	}
 	// Emit basic blocks
 	for i, b := range f.Blocks {
 		s.bstart[b.ID] = s.pp.next
@@ -4438,12 +4444,16 @@ 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)
 			}
 
+			if Ctxt.Flag_locationlists {
+				valueToProg[v.ID] = x
+			}
 			if logProgs {
 				for ; x != s.pp.next; x = x.Link {
 					progToValue[x] = v
@@ -4469,6 +4479,22 @@ func genssa(f *ssa.Func, pp *Progs) {
 		}
 	}
 
+	if Ctxt.Flag_locationlists {
+		for _, locList := range e.curfn.Func.DebugInfo.Variables {
+			for _, loc := range locList.Locations {
+				loc.StartProg = valueToProg[loc.Start.ID]
+				if loc.End == nil {
+					Fatalf("empty loc %v compiling %v", loc, f.Name)
+				}
+				loc.EndProg = valueToProg[loc.End.ID]
+				if !logLocationLists {
+					loc.Start = nil
+					loc.End = nil
+				}
+			}
+		}
+	}
+
 	// Resolve branches
 	for _, br := range s.Branches {
 		br.P.To.Val = s.bstart[br.B.ID]
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index 0fd146bca26deb57258173aa37e0f9d9c15785ce..32ae6f2f28ee4f96b7b922c5427c1f7ec3d62eeb 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -7,6 +7,7 @@
 package gc
 
 import (
+	"cmd/compile/internal/ssa"
 	"cmd/compile/internal/syntax"
 	"cmd/compile/internal/types"
 	"cmd/internal/obj"
@@ -369,6 +370,7 @@ type Func struct {
 	Closgen    int
 	Outerfunc  *Node // outer function (for closure)
 	FieldTrack map[*types.Sym]struct{}
+	DebugInfo  *ssa.FuncDebug
 	Ntype      *Node // signature
 	Top        int   // top context (Ecall, Eproc, etc)
 	Closure    *Node // OCLOSURE <-> ODCLFUNC
diff --git a/src/cmd/compile/internal/ssa/cache.go b/src/cmd/compile/internal/ssa/cache.go
index f1018da497fc63a5e806f67f36d1ac7d887d7999..8434084bde28e4a2fa680c6137f2ba4e70d388fb 100644
--- a/src/cmd/compile/internal/ssa/cache.go
+++ b/src/cmd/compile/internal/ssa/cache.go
@@ -14,6 +14,11 @@ 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
@@ -38,4 +43,21 @@ func (c *Cache) Reset() {
 	for i := range xl {
 		xl[i] = nil
 	}
+	xvl := c.varLocs[:c.curVarLoc]
+	for i := range xvl {
+		xvl[i] = VarLoc{}
+	}
+	c.curVarLoc = 0
+}
+
+func (c *Cache) NewVarLoc() *VarLoc {
+	if c.varLocs == nil {
+		c.varLocs = make([]VarLoc, 4000)
+	}
+	if c.curVarLoc == len(c.varLocs) {
+		return &VarLoc{}
+	}
+	vl := &c.varLocs[c.curVarLoc]
+	c.curVarLoc++
+	return vl
 }
diff --git a/src/cmd/compile/internal/ssa/debug.go b/src/cmd/compile/internal/ssa/debug.go
new file mode 100644
index 0000000000000000000000000000000000000000..55db45b642acdd2a0568f3eb5b94d413de291f7f
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/debug.go
@@ -0,0 +1,559 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package ssa
+
+import (
+	"cmd/internal/obj"
+	"fmt"
+	"strings"
+)
+
+type SlotID 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     []*LocalSlot
+	Variables []VarLocList
+	Registers []Register
+}
+
+// append adds a location to the location list for slot.
+func (f *FuncDebug) append(slot SlotID, loc *VarLoc) {
+	f.Variables[slot].append(loc)
+}
+
+// lastLoc returns the last VarLoc for slot, or nil if it has none.
+func (f *FuncDebug) lastLoc(slot SlotID) *VarLoc {
+	return f.Variables[slot].last()
+}
+
+func (f *FuncDebug) String() string {
+	var vars []string
+	for slot, list := range f.Variables {
+		if len(list.Locations) == 0 {
+			continue
+		}
+		vars = append(vars, fmt.Sprintf("%v = %v", f.Slots[slot], list))
+	}
+	return fmt.Sprintf("{%v}", strings.Join(vars, ", "))
+}
+
+// A VarLocList contains the locations for a variable, in program text order.
+// It will often have gaps.
+type VarLocList struct {
+	Locations []*VarLoc
+}
+
+func (l *VarLocList) append(loc *VarLoc) {
+	l.Locations = append(l.Locations, loc)
+}
+
+// last returns the last location in the list.
+func (l *VarLocList) last() *VarLoc {
+	if l == nil || len(l.Locations) == 0 {
+		return nil
+	}
+	return l.Locations[len(l.Locations)-1]
+}
+
+// A VarLoc describes a variable's location in a single contiguous range
+// of program text. It is generated from the SSA representation, but it
+// refers to the generated machine code, so the Values referenced are better
+// understood as PCs than actual Values, and the ranges can cross blocks.
+// The range is defined first by Values, which are then mapped to Progs
+// during genssa and finally to function PCs after assembly.
+// A variable can be on the stack and in any number of registers.
+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.
+	Start *Value
+	// Exclusive -- the first SSA value after start that the range doesn't
+	// cover. A location with start == end is empty.
+	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.
+	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
+	// Indicates whether the variable is on the stack. The stack position is
+	// stored in the associated gc.Node.
+	OnStack bool
+
+	// Used only during generation. Indicates whether this location lasts
+	// past the block's end. Without this, there would be no way to distinguish
+	// between a range that ended on the last Value of a block and one that
+	// didn't end at all.
+	survivedBlock bool
+}
+
+// RegisterSet is a bitmap of registers, indexed by Register.num.
+type RegisterSet uint64
+
+func (v *VarLoc) String() string {
+	var registers []Register
+	if v.Start != nil {
+		registers = v.Start.Block.Func.Config.registers
+	}
+	loc := ""
+	if !v.OnStack && v.Registers == 0 {
+		loc = "!!!no location!!!"
+	}
+	if v.OnStack {
+		loc += "stack,"
+	}
+	var regnames []string
+	for reg := 0; reg < 64; reg++ {
+		if v.Registers&(1<<uint8(reg)) == 0 {
+			continue
+		}
+		if registers != nil {
+			regnames = append(regnames, registers[reg].Name())
+		} else {
+			regnames = append(regnames, fmt.Sprintf("reg%v", reg))
+		}
+	}
+	loc += strings.Join(regnames, ",")
+	pos := func(v *Value, p *obj.Prog, pc int64) string {
+		if v == nil {
+			return "?"
+		}
+		if p == nil {
+			return fmt.Sprintf("v%v", v.ID)
+		}
+		return fmt.Sprintf("v%v/%x", v.ID, pc)
+	}
+	surv := ""
+	if v.survivedBlock {
+		surv = "+"
+	}
+	return fmt.Sprintf("%v-%v%s@%s", pos(v.Start, v.StartProg, v.StartPC), pos(v.End, v.EndProg, v.EndPC), surv, loc)
+}
+
+// unexpected is used to indicate an inconsistency or bug in the debug info
+// generation process. These are not fixable by users. At time of writing,
+// changing this to a Fprintf(os.Stderr) and running make.bash generates
+// thousands of warnings.
+func (s *debugState) unexpected(v *Value, msg string, args ...interface{}) {
+	s.f.Logf("unexpected at "+fmt.Sprint(v.ID)+":"+msg, args...)
+}
+
+func (s *debugState) logf(msg string, args ...interface{}) {
+	s.f.Logf(msg, args...)
+}
+
+type debugState struct {
+	loggingEnabled bool
+	slots          []*LocalSlot
+	f              *Func
+	cache          *Cache
+	numRegisters   int
+
+	// working storage for BuildFuncDebug, reused between blocks.
+	registerContents [][]SlotID
+}
+
+// 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 {
+	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)),
+	}
+	// TODO: consider storing this in Cache and reusing across functions.
+	valueNames := make([][]SlotID, f.NumValues())
+
+	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))
+		}
+	}
+
+	if state.loggingEnabled {
+		var names []string
+		for i, name := range f.Names {
+			names = append(names, fmt.Sprintf("%v = %v", i, name))
+		}
+		state.logf("Name table: %v\n", strings.Join(names, ", "))
+	}
+
+	// Build up block states, starting with the first block, then
+	// processing blocks once their predecessors have been processed.
+
+	// TODO: use a reverse post-order traversal instead of the work queue.
+
+	// Location list entries for each block.
+	blockLocs := make([]*FuncDebug, f.NumBlocks())
+
+	// Work queue of blocks to visit. Some of them may already be processed.
+	work := []*Block{f.Entry}
+
+	for len(work) > 0 {
+		b := work[0]
+		work = work[1:]
+		if blockLocs[b.ID] != nil {
+			continue // already processed
+		}
+		if !state.predecessorsDone(b, blockLocs) {
+			continue // not ready yet
+		}
+
+		for _, edge := range b.Succs {
+			if blockLocs[edge.Block().ID] != nil {
+				continue
+			}
+			work = append(work, edge.Block())
+		}
+
+		// 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, locs, state.registerContents)
+		}
+		// Update locs/registers with the effects of each Value.
+		for _, v := range b.Values {
+			slots := valueNames[v.ID]
+
+			// Loads and stores inherit the names of their sources.
+			var source *Value
+			switch v.Op {
+			case OpStoreReg:
+				source = v.Args[0]
+			case OpLoadReg:
+				switch a := v.Args[0]; a.Op {
+				case OpArg:
+					source = a
+				case OpStoreReg:
+					source = a.Args[0]
+				default:
+					state.unexpected(v, "load with unexpected source op %v", a)
+				}
+			}
+			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
+			}
+
+			if len(slots) == 0 {
+				continue
+			}
+
+			reg, _ := f.getHome(v.ID).(*Register)
+			state.processValue(locs, v, slots, reg)
+
+		}
+
+		// The block is done; end the locations for all its slots.
+		for _, locList := range locs.Variables {
+			last := locList.last()
+			if last == nil || last.End != nil {
+				continue
+			}
+			if len(b.Values) != 0 {
+				last.End = b.Values[len(b.Values)-1]
+			} else {
+				// This happens when a value survives into an empty block from its predecessor.
+				// Just carry it forward for liveness's sake.
+				last.End = last.Start
+			}
+			last.survivedBlock = true
+		}
+		if state.loggingEnabled {
+			f.Logf("Block done: locs %v, regs %v. work = %+v\n", locs, state.registerContents, work)
+		}
+		blockLocs[b.ID] = locs
+	}
+
+	// Build the complete debug info by concatenating each of the blocks'
+	// locations together.
+	info := &FuncDebug{
+		Variables: make([]VarLocList, len(state.slots)),
+		Slots:     state.slots,
+		Registers: f.Config.registers,
+	}
+	for _, b := range f.Blocks {
+		// Ignore empty blocks; there will be some records for liveness
+		// but they're all useless.
+		if len(b.Values) == 0 {
+			continue
+		}
+		if blockLocs[b.ID] == nil {
+			state.unexpected(b.Values[0], "Never processed block %v\n", b)
+			continue
+		}
+		for slot, blockLocList := range blockLocs[b.ID].Variables {
+			for _, loc := range blockLocList.Locations {
+				if !loc.OnStack && loc.Registers == 0 {
+					state.unexpected(loc.Start, "Location for %v with no storage: %+v\n", state.slots[slot], loc)
+					continue // don't confuse downstream with our bugs
+				}
+				if loc.Start == nil || loc.End == nil {
+					state.unexpected(b.Values[0], "Location for %v missing start or end: %v\n", state.slots[slot], loc)
+					continue
+				}
+				info.append(SlotID(slot), loc)
+			}
+		}
+	}
+	if state.loggingEnabled {
+		f.Logf("Final result:\n")
+		for slot, locList := range info.Variables {
+			f.Logf("\t%v => %v\n", state.slots[slot], locList)
+		}
+	}
+	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.Name()[0]
+	return c == '.' || c == '~'
+}
+
+// predecessorsDone reports whether block is ready to be processed.
+func (state *debugState) predecessorsDone(b *Block, blockLocs []*FuncDebug) bool {
+	f := b.Func
+	for _, edge := range b.Preds {
+		// Ignore back branches, e.g. the continuation of a for loop.
+		// This may not work for functions with mutual gotos, which are not
+		// reducible, in which case debug information will be missing for any
+		// code after that point in the control flow.
+		if f.sdom().isAncestorEq(b, edge.b) {
+			if state.loggingEnabled {
+				f.Logf("ignoring back branch from %v to %v\n", edge.b, b)
+			}
+			continue // back branch
+		}
+		if blockLocs[edge.b.ID] == nil {
+			if state.loggingEnabled {
+				f.Logf("%v is not ready because %v isn't done\n", b, edge.b)
+			}
+			return false
+		}
+	}
+	return true
+}
+
+// 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.
+func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *FuncDebug {
+	live := make([]VarLocList, len(state.slots))
+
+	// Filter out back branches.
+	var preds []*Block
+	for _, pred := range b.Preds {
+		if blockLocs[pred.b.ID] != nil {
+			preds = append(preds, pred.b)
+		}
+	}
+
+	if len(preds) > 0 {
+		p := preds[0]
+		for slot, locList := range blockLocs[p.ID].Variables {
+			last := locList.last()
+			if last == nil || !last.survivedBlock {
+				continue
+			}
+			// If this block is empty, carry forward the end value for liveness.
+			// It'll be ignored later.
+			start := last.End
+			if len(b.Values) != 0 {
+				start = b.Values[0]
+			}
+			loc := state.cache.NewVarLoc()
+			loc.Start = start
+			loc.OnStack = last.OnStack
+			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, blockLocs[b.Preds[0].b.ID])
+	}
+	for i := 1; i < len(preds); i++ {
+		p := preds[i]
+		if state.loggingEnabled {
+			state.logf("Merging in state from %v: %v &= %v\n", p, live, blockLocs[p.ID])
+		}
+
+		for slot, liveVar := range live {
+			liveLoc := liveVar.last()
+			if liveLoc == nil {
+				continue
+			}
+
+			predLoc := blockLocs[p.ID].lastLoc(SlotID(slot))
+			// Clear out slots missing/dead in p.
+			if predLoc == nil || !predLoc.survivedBlock {
+				live[slot].Locations = nil
+				continue
+			}
+
+			// Unify storage locations.
+			liveLoc.OnStack = liveLoc.OnStack && predLoc.OnStack
+			liveLoc.Registers &= predLoc.Registers
+		}
+	}
+
+	// Create final result.
+	locs := &FuncDebug{Variables: live, Slots: state.slots}
+	for reg := range state.registerContents {
+		state.registerContents[reg] = state.registerContents[reg][:0]
+	}
+	for slot, locList := range live {
+		loc := locList.last()
+		if loc == nil {
+			continue
+		}
+		for reg := 0; reg < state.numRegisters; reg++ {
+			if loc.Registers&(1<<uint8(reg)) != 0 {
+				state.registerContents[reg] = append(state.registerContents[reg], SlotID(slot))
+			}
+		}
+	}
+	return locs
+}
+
+// processValue updates locs and state.registerContents to reflect v, a value with
+// the names in vSlots and homed in vReg.
+func (state *debugState) processValue(locs *FuncDebug, 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
+
+		for _, slot := range vSlots {
+			last := locs.lastLoc(slot)
+			if last == nil {
+				state.unexpected(v, "regkill of already dead %v, %+v\n", vReg, state.slots[slot])
+				continue
+			}
+			if state.loggingEnabled {
+				state.logf("at %v: %v regkilled out of %v\n", v.ID, state.slots[slot], vReg.Name())
+			}
+			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
+			}
+			loc := state.cache.NewVarLoc()
+			loc.Start = v
+			loc.OnStack = last.OnStack
+			loc.Registers = regs
+			locs.append(slot, loc)
+		}
+	case v.Op == OpArg:
+		for _, slot := range vSlots {
+			if state.loggingEnabled {
+				state.logf("at %v: %v now on stack from arg\n", v.ID, state.slots[slot])
+			}
+			loc := state.cache.NewVarLoc()
+			loc.Start = v
+			loc.OnStack = true
+			locs.append(slot, loc)
+		}
+
+	case v.Op == OpStoreReg:
+		for _, slot := range vSlots {
+			if state.loggingEnabled {
+				state.logf("at %v: %v spilled to stack\n", v.ID, state.slots[slot])
+			}
+			last := locs.lastLoc(slot)
+			if last == nil {
+				state.unexpected(v, "spill of unnamed register %v\n", vReg)
+				break
+			}
+			last.End = v
+			loc := state.cache.NewVarLoc()
+			loc.Start = v
+			loc.OnStack = true
+			loc.Registers = last.Registers
+			locs.append(slot, loc)
+		}
+
+	case vReg != nil:
+		if state.loggingEnabled {
+			newSlots := make([]bool, len(state.slots))
+			for _, slot := range vSlots {
+				newSlots[slot] = true
+			}
+
+			for _, slot := range state.registerContents[vReg.num] {
+				if !newSlots[slot] {
+					state.unexpected(v, "%v clobbered\n", state.slots[slot])
+				}
+			}
+		}
+
+		for _, slot := range vSlots {
+			if state.loggingEnabled {
+				state.logf("at %v: %v now in %v\n", v.ID, state.slots[slot], vReg.Name())
+			}
+			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 {
+				loc.OnStack = last.OnStack
+				loc.Registers = last.Registers
+			}
+			loc.Registers |= 1 << uint8(vReg.num)
+			locs.append(slot, loc)
+		}
+	default:
+		state.unexpected(v, "named value with no reg\n")
+	}
+
+}
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index d962e4a193bf8d9663078d3b5843d4df77c32174..63bb9a8b61d1a2d096a74610630cd4929c4bf782 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -417,6 +417,7 @@ 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: "None"},            // aux is a *gc.Node of a variable that must be kept live.  arg0=mem, returns mem
 	{name: "KeepAlive", argLength: 2, typ: "Mem"},                             // arg[0] is a value that must be kept alive until this mark.  arg[1]=mem, returns mem
+	{name: "RegKill"},                                                         // regalloc has determined that the value in this register is dead
 
 	// Ops for breaking 64-bit operations on 32-bit architectures
 	{name: "Int64Make", argLength: 2, typ: "UInt64"}, // arg0=hi, arg1=lo
diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go
index d554907bebe541b9a6dddbdc82ebea92869150a9..6efe93e74fd54a0f71e02858bf807f4401db6a43 100644
--- a/src/cmd/compile/internal/ssa/html.go
+++ b/src/cmd/compile/internal/ssa/html.go
@@ -11,6 +11,7 @@ import (
 	"html"
 	"io"
 	"os"
+	"strings"
 )
 
 type HTMLWriter struct {
@@ -362,6 +363,18 @@ func (v *Value) LongHTML() string {
 	if int(v.ID) < len(r) && r[v.ID] != nil {
 		s += " : " + html.EscapeString(r[v.ID].Name())
 	}
+	var names []string
+	for name, values := range v.Block.Func.NamedValues {
+		for _, value := range values {
+			if value == v {
+				names = append(names, name.Name())
+				break // drop duplicates.
+			}
+		}
+	}
+	if len(names) != 0 {
+		s += " (" + strings.Join(names, ", ") + ")"
+	}
 	s += "</span>"
 	return s
 }
diff --git a/src/cmd/compile/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go
index 70afa47e9d53aa97e4dbfcce0019b88b93bf37a8..dc01bd4235341f91734e6035b636efcfe5bdae00 100644
--- a/src/cmd/compile/internal/ssa/location.go
+++ b/src/cmd/compile/internal/ssa/location.go
@@ -26,6 +26,12 @@ func (r *Register) Name() string {
 	return r.name
 }
 
+// ObjNum returns the register number from cmd/internal/obj/$ARCH that
+// corresponds to this register.
+func (r *Register) ObjNum() int16 {
+	return r.objNum
+}
+
 // A LocalSlot is a location in the stack frame, which identifies and stores
 // part or all of a PPARAM, PPARAMOUT, or PAUTO ONAME node.
 // It can represent a whole variable, part of a larger stack slot, or part of a
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index ae2dd5f5500c867731e05d65b61d19f044087956..87a2ea0656a15da6e748b20d34cc6cd5ccdfb4b6 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -1897,6 +1897,7 @@ const (
 	OpVarKill
 	OpVarLive
 	OpKeepAlive
+	OpRegKill
 	OpInt64Make
 	OpInt64Hi
 	OpInt64Lo
@@ -22497,6 +22498,11 @@ var opcodeTable = [...]opInfo{
 		argLen:  2,
 		generic: true,
 	},
+	{
+		name:    "RegKill",
+		argLen:  0,
+		generic: true,
+	},
 	{
 		name:    "Int64Make",
 		argLen:  2,
diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go
index e297e6bce75719652e59d6dbaade74bf744ce46a..0abaeaeeb556b6e2a6cf3918e85d775cb09c654d 100644
--- a/src/cmd/compile/internal/ssa/regalloc.go
+++ b/src/cmd/compile/internal/ssa/regalloc.go
@@ -242,6 +242,9 @@ 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
 
@@ -300,6 +303,13 @@ 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)
@@ -309,6 +319,16 @@ func (s *regAllocState) freeReg(r register) {
 	if s.f.pass.debug > regDebug {
 		fmt.Printf("freeReg %s (dump %s/%s)\n", s.registers[r].Name(), 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
@@ -599,6 +619,17 @@ 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() {
@@ -692,7 +723,9 @@ func (s *regAllocState) liveAfterCurrentInstruction(v *Value) bool {
 
 // Sets the state of the registers to that encoded in regs.
 func (s *regAllocState) setState(regs []endReg) {
-	s.freeRegs(s.used)
+	for s.used != 0 {
+		s.freeOrResetReg(pickReg(s.used), true)
+	}
 	for _, x := range regs {
 		s.assignReg(x.r, x.v, x.c)
 	}
@@ -735,6 +768,9 @@ func (s *regAllocState) regalloc(f *Func) {
 	}
 
 	for _, b := range f.Blocks {
+		if s.f.pass.debug > regDebug {
+			fmt.Printf("Begin processing block %v\n", b)
+		}
 		s.curBlock = b
 
 		// Initialize regValLiveSet and uses fields for this block.
@@ -830,9 +866,6 @@ func (s *regAllocState) regalloc(f *Func) {
 			// This is the complicated case. We have more than one predecessor,
 			// which means we may have Phi ops.
 
-			// Copy phi ops into new schedule.
-			b.Values = append(b.Values, phis...)
-
 			// Start with the final register state of the primary predecessor
 			idx := s.primary[b.ID]
 			if idx < 0 {
@@ -910,6 +943,9 @@ func (s *regAllocState) regalloc(f *Func) {
 				}
 			}
 
+			// Copy phi ops into new schedule.
+			b.Values = append(b.Values, phis...)
+
 			// Third pass - pick registers for phis whose inputs
 			// were not in a register.
 			for i, v := range phis {
@@ -1005,7 +1041,7 @@ func (s *regAllocState) regalloc(f *Func) {
 			pidx := e.i
 			for _, v := range succ.Values {
 				if v.Op != OpPhi {
-					break
+					continue
 				}
 				if !s.values[v.ID].needReg {
 					continue
@@ -1565,6 +1601,9 @@ 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
 			}
@@ -1675,7 +1714,7 @@ func (s *regAllocState) placeSpills() {
 	for _, b := range f.Blocks {
 		nphi := 0
 		for _, v := range b.Values {
-			if v.Op != OpPhi {
+			if v.Op != OpRegKill && v.Op != OpPhi {
 				break
 			}
 			nphi++
@@ -1800,6 +1839,9 @@ 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
 		}
@@ -1878,6 +1920,7 @@ func (e *edgeState) process() {
 		if e.s.f.pass.debug > regDebug {
 			fmt.Printf("breaking cycle with v%d in %s:%s\n", vid, loc.Name(), c)
 		}
+		e.erase(r)
 		if _, isReg := loc.(*Register); isReg {
 			c = e.p.NewValue1(d.pos, OpCopy, c.Type, c)
 		} else {
@@ -1943,6 +1986,18 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
 		}
 	}
 	_, dstReg := loc.(*Register)
+
+	// Pre-clobber destination. This avoids the
+	// following situation:
+	//   - v is currently held in R0 and stacktmp0.
+	//   - We want to copy stacktmp1 to stacktmp0.
+	//   - We choose R0 as the temporary register.
+	// During the copy, both R0 and stacktmp0 are
+	// clobbered, losing both copies of v. Oops!
+	// Erasing the destination early means R0 will not
+	// be chosen as the temp register, as it will then
+	// be the last copy of v.
+	e.erase(loc)
 	var x *Value
 	if c == nil {
 		if !e.s.values[vid].rematerializeable {
@@ -1953,8 +2008,8 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
 		} else {
 			// Rematerialize into stack slot. Need a free
 			// register to accomplish this.
-			e.erase(loc) // see pre-clobber comment below
 			r := e.findRegFor(v.Type)
+			e.erase(r)
 			x = v.copyIntoNoXPos(e.p)
 			e.set(r, vid, x, false, pos)
 			// Make sure we spill with the size of the slot, not the
@@ -1976,20 +2031,8 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
 				x = e.p.NewValue1(pos, OpLoadReg, c.Type, c)
 			} else {
 				// mem->mem. Use temp register.
-
-				// Pre-clobber destination. This avoids the
-				// following situation:
-				//   - v is currently held in R0 and stacktmp0.
-				//   - We want to copy stacktmp1 to stacktmp0.
-				//   - We choose R0 as the temporary register.
-				// During the copy, both R0 and stacktmp0 are
-				// clobbered, losing both copies of v. Oops!
-				// Erasing the destination early means R0 will not
-				// be chosen as the temp register, as it will then
-				// be the last copy of v.
-				e.erase(loc)
-
 				r := e.findRegFor(c.Type)
+				e.erase(r)
 				t := e.p.NewValue1(pos, OpLoadReg, c.Type, c)
 				e.set(r, vid, t, false, pos)
 				x = e.p.NewValue1(pos, OpStoreReg, loc.(LocalSlot).Type, t)
@@ -2008,7 +2051,6 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
 // set changes the contents of location loc to hold the given value and its cached representative.
 func (e *edgeState) set(loc Location, vid ID, c *Value, final bool, pos src.XPos) {
 	e.s.f.setHome(c, loc)
-	e.erase(loc)
 	e.contents[loc] = contentRecord{vid, c, final, pos}
 	a := e.cache[vid]
 	if len(a) == 0 {
@@ -2059,6 +2101,16 @@ func (e *edgeState) erase(loc Location) {
 				fmt.Printf("v%d no longer available in %s:%s\n", vid, loc.Name(), c)
 			}
 			a[i], a = a[len(a)-1], a[:len(a)-1]
+			if e.s.f.Config.ctxt.Flag_locationlists {
+				if _, isReg := loc.(*Register); isReg && int(c.ID) < len(e.s.valueNames) && len(e.s.valueNames[c.ID]) != 0 {
+					kill := e.p.NewValue0(src.NoXPos, OpRegKill, types.TypeVoid)
+					e.s.f.setHome(kill, loc)
+					for _, name := range e.s.valueNames[c.ID] {
+						e.s.f.NamedValues[name] = append(e.s.f.NamedValues[name], kill)
+					}
+				}
+			}
+
 			break
 		}
 	}
diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go
index 7edc71be52a7d9d9293a6c26ce82cae8658e35e0..6df535153a7d573c9f486ba1bce1d65f7cc3f105 100644
--- a/src/cmd/compile/internal/ssa/value.go
+++ b/src/cmd/compile/internal/ssa/value.go
@@ -10,6 +10,7 @@ import (
 	"cmd/internal/src"
 	"fmt"
 	"math"
+	"strings"
 )
 
 // A Value represents a value in the SSA representation of the program.
@@ -98,7 +99,7 @@ func (v *Value) AuxValAndOff() ValAndOff {
 	return ValAndOff(v.AuxInt)
 }
 
-// long form print.  v# = opcode <type> [aux] args [: reg]
+// long form print.  v# = opcode <type> [aux] args [: reg] (names)
 func (v *Value) LongString() string {
 	s := fmt.Sprintf("v%d = %s", v.ID, v.Op)
 	s += " <" + v.Type.String() + ">"
@@ -110,6 +111,18 @@ func (v *Value) LongString() string {
 	if int(v.ID) < len(r) && r[v.ID] != nil {
 		s += " : " + r[v.ID].Name()
 	}
+	var names []string
+	for name, values := range v.Block.Func.NamedValues {
+		for _, value := range values {
+			if value == v {
+				names = append(names, name.Name())
+				break // drop duplicates.
+			}
+		}
+	}
+	if len(names) != 0 {
+		s += " (" + strings.Join(names, ", ") + ")"
+	}
 	return s
 }
 
diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go
index b58052beb3d90a6cf191296db76d9441ab8716d9..2b034257a6afc5b92c2e2fb1bed7ca119e16f42e 100644
--- a/src/cmd/internal/dwarf/dwarf.go
+++ b/src/cmd/internal/dwarf/dwarf.go
@@ -15,6 +15,9 @@ import (
 // InfoPrefix is the prefix for all the symbols containing DWARF info entries.
 const InfoPrefix = "go.info."
 
+// RangePrefix is the prefix for all the symbols containing DWARF location lists.
+const LocPrefix = "go.loc."
+
 // RangePrefix is the prefix for all the symbols containing DWARF range lists.
 const RangePrefix = "go.range."
 
@@ -23,13 +26,31 @@ 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
-	Abbrev      int // Either DW_ABRV_AUTO or DW_ABRV_PARAM
-	StackOffset int32
-	Scope       int32
-	Type        Sym
+	Name         string
+	Abbrev       int // Either DW_ABRV_AUTO or DW_ABRV_PARAM
+	StackOffset  int32
+	LocationList []Location
+	Scope        int32
+	Type         Sym
 }
 
 // A Scope represents a lexical scope. All variables declared within a
@@ -205,7 +226,7 @@ const (
 )
 
 // Index into the abbrevs table below.
-// Keep in sync with ispubname() and ispubtype() below.
+// Keep in sync with ispubname() and ispubtype() in ld/dwarf.go.
 // ispubtype considers >= NULLTYPE public
 const (
 	DW_ABRV_NULL = iota
@@ -709,31 +730,30 @@ func HasChildren(die *DWDie) bool {
 
 // PutFunc writes a DIE for a function to s.
 // It also writes child DIEs for each variable in vars.
-func PutFunc(ctxt Context, s, ranges Sym, name string, external bool, startPC Sym, size int64, scopes []Scope) error {
-	Uleb128put(ctxt, s, DW_ABRV_FUNCTION)
-	putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
-	putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, 0, startPC)
-	putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, size, startPC)
-	putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
+func PutFunc(ctxt Context, info, loc, ranges Sym, name string, external bool, startPC Sym, size int64, scopes []Scope) error {
+	Uleb128put(ctxt, info, DW_ABRV_FUNCTION)
+	putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
+	putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, 0, startPC)
+	putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, size, startPC)
+	putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
 	var ev int64
 	if external {
 		ev = 1
 	}
-	putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
+	putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
 	if len(scopes) > 0 {
 		var encbuf [20]byte
-		if putscope(ctxt, s, ranges, startPC, 0, scopes, encbuf[:0]) < int32(len(scopes)) {
+		if putscope(ctxt, info, loc, ranges, startPC, 0, scopes, encbuf[:0]) < int32(len(scopes)) {
 			return errors.New("multiple toplevel scopes")
 		}
 	}
-
-	Uleb128put(ctxt, s, 0)
+	Uleb128put(ctxt, info, 0)
 	return nil
 }
 
-func putscope(ctxt Context, s, ranges Sym, startPC Sym, curscope int32, scopes []Scope, encbuf []byte) int32 {
+func putscope(ctxt Context, info, loc, ranges, startPC Sym, curscope int32, scopes []Scope, encbuf []byte) int32 {
 	for _, v := range scopes[curscope].Vars {
-		putvar(ctxt, s, v, encbuf)
+		putvar(ctxt, info, loc, v, startPC, encbuf)
 	}
 	this := curscope
 	curscope++
@@ -744,12 +764,12 @@ func putscope(ctxt Context, s, ranges Sym, startPC Sym, curscope int32, scopes [
 		}
 
 		if len(scope.Ranges) == 1 {
-			Uleb128put(ctxt, s, DW_ABRV_LEXICAL_BLOCK_SIMPLE)
-			putattr(ctxt, s, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].Start, startPC)
-			putattr(ctxt, s, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].End, startPC)
+			Uleb128put(ctxt, info, DW_ABRV_LEXICAL_BLOCK_SIMPLE)
+			putattr(ctxt, info, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].Start, startPC)
+			putattr(ctxt, info, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].End, startPC)
 		} else {
-			Uleb128put(ctxt, s, DW_ABRV_LEXICAL_BLOCK_RANGES)
-			putattr(ctxt, s, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_sec_offset, DW_CLS_PTR, ranges.Len(), ranges)
+			Uleb128put(ctxt, info, DW_ABRV_LEXICAL_BLOCK_RANGES)
+			putattr(ctxt, info, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_sec_offset, DW_CLS_PTR, ranges.Len(), ranges)
 
 			ctxt.AddAddress(ranges, nil, -1)
 			ctxt.AddAddress(ranges, startPC, 0)
@@ -761,26 +781,66 @@ func putscope(ctxt Context, s, ranges Sym, startPC Sym, curscope int32, scopes [
 			ctxt.AddAddress(ranges, nil, 0)
 		}
 
-		curscope = putscope(ctxt, s, ranges, startPC, curscope, scopes, encbuf)
+		curscope = putscope(ctxt, info, loc, ranges, startPC, curscope, scopes, encbuf)
 
-		Uleb128put(ctxt, s, 0)
+		Uleb128put(ctxt, info, 0)
 	}
 	return curscope
 }
 
-func putvar(ctxt Context, s Sym, v *Var, encbuf []byte) {
+func putvar(ctxt Context, info, loc Sym, v *Var, startPC Sym, encbuf []byte) {
 	n := v.Name
 
-	Uleb128put(ctxt, s, int64(v.Abbrev))
-	putattr(ctxt, s, v.Abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
-	loc := append(encbuf[:0], DW_OP_call_frame_cfa)
-	if v.StackOffset != 0 {
-		loc = append(loc, DW_OP_consts)
-		loc = AppendSleb128(loc, int64(v.StackOffset))
-		loc = append(loc, DW_OP_plus)
+	Uleb128put(ctxt, info, int64(v.Abbrev))
+	putattr(ctxt, info, v.Abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
+	if v.Abbrev == DW_ABRV_AUTO_LOCLIST || v.Abbrev == DW_ABRV_PARAM_LOCLIST {
+		putattr(ctxt, info, v.Abbrev, DW_FORM_sec_offset, DW_CLS_PTR, int64(loc.Len()), loc)
+		addLocList(ctxt, loc, startPC, v, encbuf)
+	} else {
+		loc := append(encbuf[:0], DW_OP_call_frame_cfa)
+		if v.StackOffset != 0 {
+			loc = append(loc, DW_OP_consts)
+			loc = AppendSleb128(loc, int64(v.StackOffset))
+			loc = append(loc, DW_OP_plus)
+		}
+		putattr(ctxt, info, v.Abbrev, DW_FORM_block1, DW_CLS_BLOCK, int64(len(loc)), loc)
+	}
+	putattr(ctxt, info, v.Abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
+}
+
+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 {
+					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)
 	}
-	putattr(ctxt, s, v.Abbrev, DW_FORM_block1, DW_CLS_BLOCK, int64(len(loc)), loc)
-	putattr(ctxt, s, v.Abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
+	// End list
+	ctxt.AddInt(listSym, ctxt.PtrSize(), 0)
+	ctxt.AddInt(listSym, ctxt.PtrSize(), 0)
 }
 
 // VarsByOffset attaches the methods of sort.Interface to []*Var,
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index d49bc8c5644c78ad75c96e2bbd130effd73e81cd..68e1b70ac0dee9fd064cb1ad3dd6d003e3de16dd 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -330,7 +330,8 @@ type FuncInfo struct {
 	Autom  []*Auto
 	Pcln   Pcln
 
-	dwarfSym       *LSym
+	dwarfInfoSym   *LSym
+	dwarfLocSym    *LSym
 	dwarfRangesSym *LSym
 
 	GCArgs   LSym
@@ -476,25 +477,26 @@ type Pcdata struct {
 // Link holds the context for writing object code from a compiler
 // to be linker input or for reading that input into the linker.
 type Link struct {
-	Headtype      objabi.HeadType
-	Arch          *LinkArch
-	Debugasm      bool
-	Debugvlog     bool
-	Debugpcln     string
-	Flag_shared   bool
-	Flag_dynlink  bool
-	Flag_optimize bool
-	Bso           *bufio.Writer
-	Pathname      string
-	hashmu        sync.Mutex       // protects hash
-	hash          map[string]*LSym // name -> sym mapping
-	statichash    map[string]*LSym // name -> sym mapping for static syms
-	PosTable      src.PosTable
-	InlTree       InlTree // global inlining tree used by gc/inl.go
-	Imports       []string
-	DiagFunc      func(string, ...interface{})
-	DebugInfo     func(fn *LSym, curfn interface{}) []dwarf.Scope // if non-nil, curfn is a *gc.Node
-	Errors        int
+	Headtype           objabi.HeadType
+	Arch               *LinkArch
+	Debugasm           bool
+	Debugvlog          bool
+	Debugpcln          string
+	Flag_shared        bool
+	Flag_dynlink       bool
+	Flag_optimize      bool
+	Flag_locationlists bool
+	Bso                *bufio.Writer
+	Pathname           string
+	hashmu             sync.Mutex       // protects hash
+	hash               map[string]*LSym // name -> sym mapping
+	statichash         map[string]*LSym // name -> sym mapping for static syms
+	PosTable           src.PosTable
+	InlTree            InlTree // global inlining tree used by gc/inl.go
+	Imports            []string
+	DiagFunc           func(string, ...interface{})
+	DebugInfo          func(fn *LSym, curfn interface{}) []dwarf.Scope // if non-nil, curfn is a *gc.Node
+	Errors             int
 
 	Framepointer_enabled bool
 
@@ -533,9 +535,10 @@ func (ctxt *Link) FixedFrameSize() int64 {
 // LinkArch is the definition of a single architecture.
 type LinkArch struct {
 	*sys.Arch
-	Init       func(*Link)
-	Preprocess func(*Link, *LSym, ProgAlloc)
-	Assemble   func(*Link, *LSym, ProgAlloc)
-	Progedit   func(*Link, *Prog, ProgAlloc)
-	UnaryDst   map[As]bool // Instruction takes one operand, a destination.
+	Init           func(*Link)
+	Preprocess     func(*Link, *LSym, ProgAlloc)
+	Assemble       func(*Link, *LSym, ProgAlloc)
+	Progedit       func(*Link, *Prog, ProgAlloc)
+	UnaryDst       map[As]bool // Instruction takes one operand, a destination.
+	DWARFRegisters map[int16]int16
 }
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index e309c5f7e785122a37edc74fad443ddc83872725..539d01303764a976fb22aa74b96127db9034eeb2 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -465,15 +465,18 @@ func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64
 }
 
 // dwarfSym returns the DWARF symbols for TEXT symbol.
-func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfRangesSym *LSym) {
+func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym *LSym) {
 	if s.Type != objabi.STEXT {
 		ctxt.Diag("dwarfSym of non-TEXT %v", s)
 	}
-	if s.Func.dwarfSym == nil {
-		s.Func.dwarfSym = ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name)
+	if s.Func.dwarfInfoSym == nil {
+		s.Func.dwarfInfoSym = ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name)
+		if ctxt.Flag_locationlists {
+			s.Func.dwarfLocSym = ctxt.LookupDerived(s, dwarf.LocPrefix+s.Name)
+		}
 		s.Func.dwarfRangesSym = ctxt.LookupDerived(s, dwarf.RangePrefix+s.Name)
 	}
-	return s.Func.dwarfSym, s.Func.dwarfRangesSym
+	return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym
 }
 
 func (s *LSym) Len() int64 {
@@ -483,15 +486,15 @@ func (s *LSym) Len() int64 {
 // populateDWARF fills in the DWARF Debugging Information Entries for TEXT symbol s.
 // The DWARFs symbol must already have been initialized in InitTextSym.
 func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym) {
-	dsym, drsym := ctxt.dwarfSym(s)
-	if dsym.Size != 0 {
+	info, loc, ranges := ctxt.dwarfSym(s)
+	if info.Size != 0 {
 		ctxt.Diag("makeFuncDebugEntry double process %v", s)
 	}
 	var scopes []dwarf.Scope
 	if ctxt.DebugInfo != nil {
 		scopes = ctxt.DebugInfo(s, curfn)
 	}
-	err := dwarf.PutFunc(dwCtxt{ctxt}, dsym, drsym, s.Name, !s.Static(), s, s.Size, scopes)
+	err := dwarf.PutFunc(dwCtxt{ctxt}, info, loc, ranges, s.Name, !s.Static(), s, s.Size, scopes)
 	if err != nil {
 		ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
 	}
diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go
index 861da88703c859f6e27d166776d7820b320e4456..1bb05aedfa95a0d862da897a5abbc52ed4862f12 100644
--- a/src/cmd/internal/obj/plist.go
+++ b/src/cmd/internal/obj/plist.go
@@ -136,13 +136,17 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
 	ctxt.Text = append(ctxt.Text, s)
 
 	// Set up DWARF entries for s.
-	dsym, drsym := ctxt.dwarfSym(s)
-	dsym.Type = objabi.SDWARFINFO
-	dsym.Set(AttrDuplicateOK, s.DuplicateOK())
-	drsym.Type = objabi.SDWARFRANGE
-	drsym.Set(AttrDuplicateOK, s.DuplicateOK())
-	ctxt.Data = append(ctxt.Data, dsym)
-	ctxt.Data = append(ctxt.Data, drsym)
+	info, loc, ranges := ctxt.dwarfSym(s)
+	info.Type = objabi.SDWARFINFO
+	info.Set(AttrDuplicateOK, s.DuplicateOK())
+	if loc != nil {
+		loc.Type = objabi.SDWARFLOC
+		loc.Set(AttrDuplicateOK, s.DuplicateOK())
+		ctxt.Data = append(ctxt.Data, loc)
+	}
+	ranges.Type = objabi.SDWARFRANGE
+	ranges.Set(AttrDuplicateOK, s.DuplicateOK())
+	ctxt.Data = append(ctxt.Data, info, ranges)
 
 	// Set up the function's gcargs and gclocals.
 	// They will be filled in later if needed.
diff --git a/src/cmd/internal/obj/x86/a.out.go b/src/cmd/internal/obj/x86/a.out.go
index 04f9ef68a4e6f805bcc4c8a6bc46f7810508ea92..92d358ba4e7682f1377040c023b5a163b7d50a22 100644
--- a/src/cmd/internal/obj/x86/a.out.go
+++ b/src/cmd/internal/obj/x86/a.out.go
@@ -1006,3 +1006,120 @@ const (
 	T_64     = 1 << 6
 	T_GOTYPE = 1 << 7
 )
+
+// https://www.uclibc.org/docs/psABI-x86_64.pdf, figure 3.36
+var AMD64DWARFRegisters = map[int16]int16{
+	REG_AX:  0,
+	REG_DX:  1,
+	REG_CX:  2,
+	REG_BX:  3,
+	REG_SI:  4,
+	REG_DI:  5,
+	REG_BP:  6,
+	REG_SP:  7,
+	REG_R8:  8,
+	REG_R9:  9,
+	REG_R10: 10,
+	REG_R11: 11,
+	REG_R12: 12,
+	REG_R13: 13,
+	REG_R14: 14,
+	REG_R15: 15,
+	// 16 is "Return Address RA", whatever that is.
+	// XMM registers. %xmmN => XN.
+	REG_X0:  17,
+	REG_X1:  18,
+	REG_X2:  19,
+	REG_X3:  20,
+	REG_X4:  21,
+	REG_X5:  22,
+	REG_X6:  23,
+	REG_X7:  24,
+	REG_X8:  25,
+	REG_X9:  26,
+	REG_X10: 27,
+	REG_X11: 28,
+	REG_X12: 29,
+	REG_X13: 30,
+	REG_X14: 31,
+	REG_X15: 32,
+	// ST registers. %stN => FN.
+	REG_F0: 33,
+	REG_F1: 34,
+	REG_F2: 35,
+	REG_F3: 36,
+	REG_F4: 37,
+	REG_F5: 38,
+	REG_F6: 39,
+	REG_F7: 40,
+	// MMX registers. %mmN => MN.
+	REG_M0: 41,
+	REG_M1: 42,
+	REG_M2: 43,
+	REG_M3: 44,
+	REG_M4: 45,
+	REG_M5: 46,
+	REG_M6: 47,
+	REG_M7: 48,
+	// 48 is flags, which doesn't have a name.
+	REG_ES: 50,
+	REG_CS: 51,
+	REG_SS: 52,
+	REG_DS: 53,
+	REG_FS: 54,
+	REG_GS: 55,
+	// 58 and 59 are {fs,gs}base, which don't have names.
+	REG_TR:   62,
+	REG_LDTR: 63,
+	// 64-66 are mxcsr, fcw, fsw, which don't have names.
+}
+
+// https://www.uclibc.org/docs/psABI-i386.pdf, table 2.14
+var X86DWARFRegisters = map[int16]int16{
+	REG_AX: 0,
+	REG_CX: 1,
+	REG_DX: 2,
+	REG_BX: 3,
+	REG_SP: 4,
+	REG_BP: 5,
+	REG_SI: 6,
+	REG_DI: 7,
+	// 8 is "Return Address RA", whatever that is.
+	// 9 is flags, which doesn't have a name.
+	// ST registers. %stN => FN.
+	REG_F0: 11,
+	REG_F1: 12,
+	REG_F2: 13,
+	REG_F3: 14,
+	REG_F4: 15,
+	REG_F5: 16,
+	REG_F6: 17,
+	REG_F7: 18,
+	// XMM registers. %xmmN => XN.
+	REG_X0: 21,
+	REG_X1: 22,
+	REG_X2: 23,
+	REG_X3: 24,
+	REG_X4: 25,
+	REG_X5: 26,
+	REG_X6: 27,
+	REG_X7: 28,
+	// MMX registers. %mmN => MN.
+	REG_M0: 29,
+	REG_M1: 30,
+	REG_M2: 31,
+	REG_M3: 32,
+	REG_M4: 33,
+	REG_M5: 34,
+	REG_M6: 35,
+	REG_M7: 36,
+	// 39 is mxcsr, which doesn't have a name.
+	REG_ES:   40,
+	REG_CS:   41,
+	REG_SS:   42,
+	REG_DS:   43,
+	REG_FS:   44,
+	REG_GS:   45,
+	REG_TR:   48,
+	REG_LDTR: 49,
+}
diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go
index d34f0aeaa632af200a8717d1f13a7a344ce9ad33..27873e0824b6f636608f4d396bebebba87543d13 100644
--- a/src/cmd/internal/obj/x86/obj6.go
+++ b/src/cmd/internal/obj/x86/obj6.go
@@ -1231,28 +1231,31 @@ var unaryDst = map[obj.As]bool{
 }
 
 var Linkamd64 = obj.LinkArch{
-	Arch:       sys.ArchAMD64,
-	Init:       instinit,
-	Preprocess: preprocess,
-	Assemble:   span6,
-	Progedit:   progedit,
-	UnaryDst:   unaryDst,
+	Arch:           sys.ArchAMD64,
+	Init:           instinit,
+	Preprocess:     preprocess,
+	Assemble:       span6,
+	Progedit:       progedit,
+	UnaryDst:       unaryDst,
+	DWARFRegisters: AMD64DWARFRegisters,
 }
 
 var Linkamd64p32 = obj.LinkArch{
-	Arch:       sys.ArchAMD64P32,
-	Init:       instinit,
-	Preprocess: preprocess,
-	Assemble:   span6,
-	Progedit:   progedit,
-	UnaryDst:   unaryDst,
+	Arch:           sys.ArchAMD64P32,
+	Init:           instinit,
+	Preprocess:     preprocess,
+	Assemble:       span6,
+	Progedit:       progedit,
+	UnaryDst:       unaryDst,
+	DWARFRegisters: AMD64DWARFRegisters,
 }
 
 var Link386 = obj.LinkArch{
-	Arch:       sys.Arch386,
-	Init:       instinit,
-	Preprocess: preprocess,
-	Assemble:   span6,
-	Progedit:   progedit,
-	UnaryDst:   unaryDst,
+	Arch:           sys.Arch386,
+	Init:           instinit,
+	Preprocess:     preprocess,
+	Assemble:       span6,
+	Progedit:       progedit,
+	UnaryDst:       unaryDst,
+	DWARFRegisters: AMD64DWARFRegisters,
 }
diff --git a/src/cmd/internal/objabi/symkind.go b/src/cmd/internal/objabi/symkind.go
index b037e9e4ed17c6ecfef1d136d3b226f484ff2618..ac91824d1726b79c7f8f5542c6472e272536de2a 100644
--- a/src/cmd/internal/objabi/symkind.go
+++ b/src/cmd/internal/objabi/symkind.go
@@ -57,4 +57,5 @@ const (
 	// Debugging data
 	SDWARFINFO
 	SDWARFRANGE
+	SDWARFLOC
 )
diff --git a/src/cmd/internal/objabi/symkind_string.go b/src/cmd/internal/objabi/symkind_string.go
index 5123dc7097f49ad8aa917425af8f8d7f0d207ae0..3064c8ee0517fd584433caf4a18a96894d6d3ed3 100644
--- a/src/cmd/internal/objabi/symkind_string.go
+++ b/src/cmd/internal/objabi/symkind_string.go
@@ -4,9 +4,9 @@ package objabi
 
 import "fmt"
 
-const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFINFOSDWARFRANGE"
+const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFINFOSDWARFRANGESDWARFLOC"
 
-var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 61, 72}
+var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 61, 72, 81}
 
 func (i SymKind) String() string {
 	if i >= SymKind(len(_SymKind_index)-1) {
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index bf219f7b62863a90c30b55afe3cbd59610e03785..1d053d23b79b3e5edcbdc381ced192f0f1f07470 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -1862,6 +1862,8 @@ func (ctxt *Link) dodata() {
 			sect = addsection(&Segdwarf, ".debug_info", 04)
 		case SDWARFRANGE:
 			sect = addsection(&Segdwarf, ".debug_ranges", 04)
+		case SDWARFLOC:
+			sect = addsection(&Segdwarf, ".debug_loc", 04)
 		default:
 			Errorf(dwarfp[i], "unknown DWARF section %v", curType)
 		}
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 9b11fdcff604e2ec018d9e75156f561e911b0ca2..b6fb1bb5c18cb88351eb79ea0b308978afe9e557 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -1579,10 +1579,35 @@ func dwarfgeneratedebugsyms(ctxt *Link) {
 	syms = writearanges(ctxt, syms)
 	syms = writegdbscript(ctxt, syms)
 	syms = append(syms, infosyms...)
+	syms = collectlocs(ctxt, syms, funcs)
 	syms = writeranges(ctxt, syms)
 	dwarfp = syms
 }
 
+func collectlocs(ctxt *Link, syms []*Symbol, funcs []*Symbol) []*Symbol {
+	empty := true
+	for _, fn := range funcs {
+		for _, reloc := range fn.R {
+			if reloc.Type == objabi.R_DWARFREF && strings.HasPrefix(reloc.Sym.Name, dwarf.LocPrefix) {
+				reloc.Sym.Attr |= AttrReachable | AttrNotInSymbolTable
+				syms = append(syms, reloc.Sym)
+				empty = false
+				// One location list entry per function, but many relocations to it. Don't duplicate.
+				break
+			}
+		}
+	}
+	// Don't emit .debug_loc if it's empty -- it makes the ARM linker mad.
+	if !empty {
+		locsym := ctxt.Syms.Lookup(".debug_loc", 0)
+		locsym.R = locsym.R[:0]
+		locsym.Type = SDWARFLOC
+		locsym.Attr |= AttrReachable
+		syms = append(syms, locsym)
+	}
+	return syms
+}
+
 /*
  *  Elf.
  */
@@ -1595,6 +1620,7 @@ func dwarfaddshstrings(ctxt *Link, shstrtab *Symbol) {
 	Addstring(shstrtab, ".debug_aranges")
 	Addstring(shstrtab, ".debug_frame")
 	Addstring(shstrtab, ".debug_info")
+	Addstring(shstrtab, ".debug_loc")
 	Addstring(shstrtab, ".debug_line")
 	Addstring(shstrtab, ".debug_pubnames")
 	Addstring(shstrtab, ".debug_pubtypes")
@@ -1602,6 +1628,7 @@ func dwarfaddshstrings(ctxt *Link, shstrtab *Symbol) {
 	Addstring(shstrtab, ".debug_ranges")
 	if Linkmode == LinkExternal {
 		Addstring(shstrtab, elfRelType+".debug_info")
+		Addstring(shstrtab, elfRelType+".debug_loc")
 		Addstring(shstrtab, elfRelType+".debug_aranges")
 		Addstring(shstrtab, elfRelType+".debug_line")
 		Addstring(shstrtab, elfRelType+".debug_frame")
@@ -1628,6 +1655,10 @@ func dwarfaddelfsectionsyms(ctxt *Link) {
 	putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
 	sym = ctxt.Syms.Lookup(".debug_frame", 0)
 	putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
+	sym = ctxt.Syms.Lookup(".debug_loc", 0)
+	if sym.Sect != nil {
+		putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
+	}
 	sym = ctxt.Syms.Lookup(".debug_ranges", 0)
 	if sym.Sect != nil {
 		putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
index 0fc947fec29eea724f5996ceed79a074f7b6af14..78f8d6e70ea8fd28867a2d1c9174ad4355d454be 100644
--- a/src/cmd/link/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -1808,7 +1808,7 @@ func elfrelocsect(ctxt *Link, sect *Section, syms []*Symbol) {
 				continue
 			}
 			if r.Xsym == nil {
-				Errorf(sym, "missing xsym in relocation")
+				Errorf(sym, "missing xsym in relocation %#v %#v", r.Sym.Name, sym)
 				continue
 			}
 			if r.Xsym.ElfsymForReloc() == 0 {
@@ -2596,12 +2596,9 @@ elfobj:
 			elfshreloc(sect)
 		}
 		for _, s := range dwarfp {
-			if len(s.R) > 0 || s.Type == SDWARFINFO {
+			if len(s.R) > 0 || s.Type == SDWARFINFO || s.Type == SDWARFLOC {
 				elfshreloc(s.Sect)
 			}
-			if s.Type == SDWARFINFO {
-				break
-			}
 		}
 		// add a .note.GNU-stack section to mark the stack as non-executable
 		sh := elfshname(".note.GNU-stack")
diff --git a/src/cmd/link/internal/ld/symkind.go b/src/cmd/link/internal/ld/symkind.go
index c057f6cd0c937a70c061d68ca757d4bd742acca1..5ac04cf45acad480d1af460340581716912ad932 100644
--- a/src/cmd/link/internal/ld/symkind.go
+++ b/src/cmd/link/internal/ld/symkind.go
@@ -105,6 +105,7 @@ const (
 	SDWARFSECT
 	SDWARFINFO
 	SDWARFRANGE
+	SDWARFLOC
 	SSUB       = SymKind(1 << 8)
 	SMASK      = SymKind(SSUB - 1)
 	SHIDDEN    = SymKind(1 << 9)
@@ -124,6 +125,7 @@ var abiSymKindToSymKind = [...]SymKind{
 	STLSBSS,
 	SDWARFINFO,
 	SDWARFRANGE,
+	SDWARFLOC,
 }
 
 // readOnly are the symbol kinds that form read-only sections. In some
diff --git a/src/cmd/link/internal/ld/symkind_string.go b/src/cmd/link/internal/ld/symkind_string.go
index 2178b50c36654f3bc82b3f6db4c39bd4cf28a3dc..87da3c40bb5af05e8d35267f240e6f194e94c176 100644
--- a/src/cmd/link/internal/ld/symkind_string.go
+++ b/src/cmd/link/internal/ld/symkind_string.go
@@ -4,9 +4,9 @@ package ld
 
 import "fmt"
 
-const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILESFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFOSDWARFRANGE"
+const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILESFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFOSDWARFRANGESDWARFLOC"
 
-var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 214, 220, 229, 237, 244, 254, 262, 267, 271, 280, 287, 292, 304, 316, 333, 350, 355, 364, 370, 380, 388, 398, 408, 419}
+var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 214, 220, 229, 237, 244, 254, 262, 267, 271, 280, 287, 292, 304, 316, 333, 350, 355, 364, 370, 380, 388, 398, 408, 419, 428}
 
 func (i SymKind) String() string {
 	if i < 0 || i >= SymKind(len(_SymKind_index)-1) {