diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go
index 04f3e669e27bee824aa68dff3ec30f285e7b31c5..143e1969c7d796ae79949b7dd78d40394d1df854 100644
--- a/src/cmd/compile/internal/gc/closure.go
+++ b/src/cmd/compile/internal/gc/closure.go
@@ -5,19 +5,21 @@
 package gc
 
 import (
+	"cmd/compile/internal/syntax"
 	"cmd/compile/internal/types"
 	"fmt"
 )
 
-// function literals aka closures
-func closurehdr(ntype *Node) {
-	n := nod(OCLOSURE, nil, nil)
+func (p *noder) funcLit(expr *syntax.FuncLit) *Node {
+	ntype := p.typeExpr(expr.Type)
+
+	n := p.nod(expr, OCLOSURE, nil, nil)
 	n.Func.SetIsHiddenClosure(Curfn != nil)
 	n.Func.Ntype = ntype
 	n.Func.Depth = funcdepth
 	n.Func.Outerfunc = Curfn
 
-	funchdr(n)
+	old := p.funchdr(n, expr.Pos())
 
 	// steal ntype's argument names and
 	// leave a fresh copy in their place.
@@ -25,8 +27,8 @@ func closurehdr(ntype *Node) {
 	// refer to the variables in the external
 	// function declared below; see walkclosure.
 	n.List.Set(ntype.List.Slice())
-
 	n.Rlist.Set(ntype.Rlist.Slice())
+
 	ntype.List.Set(nil)
 	ntype.Rlist.Set(nil)
 	for _, n1 := range n.List.Slice() {
@@ -48,23 +50,23 @@ func closurehdr(ntype *Node) {
 		}
 		ntype.Rlist.Append(nod(ODCLFIELD, name, n2.Right))
 	}
-}
 
-func closurebody(body []*Node) *Node {
+	body := p.stmts(expr.Body.List)
+
+	lineno = Ctxt.PosTable.XPos(expr.Body.Rbrace)
 	if len(body) == 0 {
 		body = []*Node{nod(OEMPTY, nil, nil)}
 	}
 
-	func_ := Curfn
-	func_.Nbody.Set(body)
-	func_.Func.Endlineno = lineno
-	funcbody(func_)
+	n.Nbody.Set(body)
+	n.Func.Endlineno = lineno
+	p.funcbody(n, expr.Body.Rbrace, old)
 
 	// closure-specific variables are hanging off the
 	// ordinary ones in the symbol table; see oldname.
 	// unhook them.
 	// make the list of pointers for the closure call.
-	for _, v := range func_.Func.Cvars.Slice() {
+	for _, v := range n.Func.Cvars.Slice() {
 		// Unlink from v1; see comment in syntax.go type Param for these fields.
 		v1 := v.Name.Defn
 		v1.Name.Param.Innermost = v.Name.Param.Outer
@@ -100,7 +102,7 @@ func closurebody(body []*Node) *Node {
 		v.Name.Param.Outer = oldname(v.Sym)
 	}
 
-	return func_
+	return n
 }
 
 func typecheckclosure(func_ *Node, top int) {
@@ -227,7 +229,11 @@ func makeclosure(func_ *Node) *Node {
 
 	xfunc.Nbody.Set(func_.Nbody.Slice())
 	xfunc.Func.Dcl = append(func_.Func.Dcl, xfunc.Func.Dcl...)
+	xfunc.Func.Parents = func_.Func.Parents
+	xfunc.Func.Marks = func_.Func.Marks
 	func_.Func.Dcl = nil
+	func_.Func.Parents = nil
+	func_.Func.Marks = nil
 	if xfunc.Nbody.Len() == 0 {
 		Fatalf("empty body - won't generate any code")
 	}
diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
index 0bf349b33af825c7d07c8b9b85072e9e27361ffd..44dd3058308012ac390aa9d673d161d692f9062e 100644
--- a/src/cmd/compile/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -216,6 +216,9 @@ var flagDWARF bool
 // when the race detector is enabled.
 var instrumenting bool
 
+// Whether we are tracking lexical scopes for DWARF.
+var trackScopes bool
+
 var debuglive int
 
 var Ctxt *obj.Link
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index a2472fa08bb35cf3db8da1ab76472e97d7586681..d8d061136ed8fa2bee2b76e7b42dab8cd52c761f 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -380,6 +380,8 @@ func Main(archInit func(*Arch)) {
 		Debug['l'] = 1 - Debug['l']
 	}
 
+	trackScopes = flagDWARF && Debug['l'] == 0 && Debug['N'] != 0
+
 	Widthptr = thearch.LinkArch.PtrSize
 	Widthreg = thearch.LinkArch.RegSize
 
diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go
index 77a7a8a436ed991ff0b8bda4f2de3791d2aaddeb..f00095c866ac6c4d7a7ddb11b9d1b352c25b2f25 100644
--- a/src/cmd/compile/internal/gc/noder.go
+++ b/src/cmd/compile/internal/gc/noder.go
@@ -75,6 +75,62 @@ type noder struct {
 	linknames  []linkname
 	pragcgobuf string
 	err        chan syntax.Error
+	scope      ScopeID
+}
+
+func (p *noder) funchdr(n *Node, pos src.Pos) ScopeID {
+	old := p.scope
+	p.scope = 0
+	funchdr(n)
+	return old
+}
+
+func (p *noder) funcbody(n *Node, pos src.Pos, old ScopeID) {
+	funcbody(n)
+	p.scope = old
+}
+
+func (p *noder) openScope(pos src.Pos) {
+	types.Markdcl()
+
+	if trackScopes {
+		Curfn.Func.Parents = append(Curfn.Func.Parents, p.scope)
+		p.scope = ScopeID(len(Curfn.Func.Parents))
+
+		p.markScope(pos)
+	}
+}
+
+func (p *noder) closeScope(pos src.Pos) {
+	types.Popdcl()
+
+	if trackScopes {
+		p.scope = Curfn.Func.Parents[p.scope-1]
+
+		p.markScope(pos)
+	}
+}
+
+func (p *noder) markScope(pos src.Pos) {
+	xpos := Ctxt.PosTable.XPos(pos)
+	if i := len(Curfn.Func.Marks); i > 0 && Curfn.Func.Marks[i-1].Pos == xpos {
+		Curfn.Func.Marks[i-1].Scope = p.scope
+	} else {
+		Curfn.Func.Marks = append(Curfn.Func.Marks, Mark{xpos, p.scope})
+	}
+}
+
+// closeAnotherScope is like closeScope, but it reuses the same mark
+// position as the last closeScope call. This is useful for "for" and
+// "if" statements, as their implicit blocks always end at the same
+// position as an explicit block.
+func (p *noder) closeAnotherScope() {
+	types.Popdcl()
+
+	if trackScopes {
+		p.scope = Curfn.Func.Parents[p.scope-1]
+		Curfn.Func.Marks[len(Curfn.Func.Marks)-1].Scope = p.scope
+	}
 }
 
 // linkname records a //go:linkname directive.
@@ -326,8 +382,9 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
 		declare(f.Func.Nname, PFUNC)
 	}
 
-	funchdr(f)
+	oldScope := p.funchdr(f, fun.Pos())
 
+	endPos := fun.Pos()
 	if fun.Body != nil {
 		if f.Noescape() {
 			yyerrorl(f.Pos, "can only use //go:noescape with external func implementations")
@@ -339,6 +396,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
 		}
 		f.Nbody.Set(body)
 
+		endPos = fun.Body.Rbrace
 		lineno = Ctxt.PosTable.XPos(fun.Body.Rbrace)
 		f.Func.Endlineno = lineno
 	} else {
@@ -347,8 +405,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
 		}
 	}
 
-	funcbody(f)
-
+	p.funcbody(f, endPos, oldScope)
 	return f
 }
 
@@ -439,10 +496,7 @@ func (p *noder) expr(expr syntax.Expr) *Node {
 	case *syntax.KeyValueExpr:
 		return p.nod(expr, OKEY, p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value)))
 	case *syntax.FuncLit:
-		closurehdr(p.typeExpr(expr.Type))
-		body := p.stmts(expr.Body.List)
-		lineno = Ctxt.PosTable.XPos(expr.Body.Rbrace)
-		return p.setlineno(expr, closurebody(body))
+		return p.funcLit(expr)
 	case *syntax.ParenExpr:
 		return p.nod(expr, OPAREN, p.expr(expr.X), nil)
 	case *syntax.SelectorExpr:
@@ -774,14 +828,14 @@ func (p *noder) stmt(stmt syntax.Stmt) *Node {
 }
 
 func (p *noder) blockStmt(stmt *syntax.BlockStmt) []*Node {
-	types.Markdcl()
+	p.openScope(stmt.Pos())
 	nodes := p.stmts(stmt.List)
-	types.Popdcl()
+	p.closeScope(stmt.Rbrace)
 	return nodes
 }
 
 func (p *noder) ifStmt(stmt *syntax.IfStmt) *Node {
-	types.Markdcl()
+	p.openScope(stmt.Pos())
 	n := p.nod(stmt, OIF, nil, nil)
 	if stmt.Init != nil {
 		n.Ninit.Set1(p.stmt(stmt.Init))
@@ -798,12 +852,12 @@ func (p *noder) ifStmt(stmt *syntax.IfStmt) *Node {
 			n.Rlist.Set1(e)
 		}
 	}
-	types.Popdcl()
+	p.closeAnotherScope()
 	return n
 }
 
 func (p *noder) forStmt(stmt *syntax.ForStmt) *Node {
-	types.Markdcl()
+	p.openScope(stmt.Pos())
 	var n *Node
 	if r, ok := stmt.Init.(*syntax.RangeClause); ok {
 		if stmt.Cond != nil || stmt.Post != nil {
@@ -832,12 +886,12 @@ func (p *noder) forStmt(stmt *syntax.ForStmt) *Node {
 		}
 	}
 	n.Nbody.Set(p.blockStmt(stmt.Body))
-	types.Popdcl()
+	p.closeAnotherScope()
 	return n
 }
 
 func (p *noder) switchStmt(stmt *syntax.SwitchStmt) *Node {
-	types.Markdcl()
+	p.openScope(stmt.Pos())
 	n := p.nod(stmt, OSWITCH, nil, nil)
 	if stmt.Init != nil {
 		n.Ninit.Set1(p.stmt(stmt.Init))
@@ -850,18 +904,21 @@ func (p *noder) switchStmt(stmt *syntax.SwitchStmt) *Node {
 	if tswitch != nil && (tswitch.Op != OTYPESW || tswitch.Left == nil) {
 		tswitch = nil
 	}
+	n.List.Set(p.caseClauses(stmt.Body, tswitch, stmt.Rbrace))
 
-	n.List.Set(p.caseClauses(stmt.Body, tswitch))
-
-	types.Popdcl()
+	p.closeScope(stmt.Rbrace)
 	return n
 }
 
-func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node) []*Node {
+func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node, rbrace src.Pos) []*Node {
 	var nodes []*Node
-	for _, clause := range clauses {
+	for i, clause := range clauses {
 		p.lineno(clause)
-		types.Markdcl()
+		if i > 0 {
+			p.closeScope(clause.Pos())
+		}
+		p.openScope(clause.Pos())
+
 		n := p.nod(clause, OXCASE, nil, nil)
 		if clause.Cases != nil {
 			n.List.Set(p.exprList(clause.Cases))
@@ -875,32 +932,40 @@ func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node) []*Node
 		}
 		n.Xoffset = int64(types.Block)
 		n.Nbody.Set(p.stmts(clause.Body))
-		types.Popdcl()
 		nodes = append(nodes, n)
 	}
+	if len(clauses) > 0 {
+		p.closeScope(rbrace)
+	}
 	return nodes
 }
 
 func (p *noder) selectStmt(stmt *syntax.SelectStmt) *Node {
 	n := p.nod(stmt, OSELECT, nil, nil)
-	n.List.Set(p.commClauses(stmt.Body))
+	n.List.Set(p.commClauses(stmt.Body, stmt.Rbrace))
 	return n
 }
 
-func (p *noder) commClauses(clauses []*syntax.CommClause) []*Node {
+func (p *noder) commClauses(clauses []*syntax.CommClause, rbrace src.Pos) []*Node {
 	var nodes []*Node
-	for _, clause := range clauses {
+	for i, clause := range clauses {
 		p.lineno(clause)
-		types.Markdcl()
+		if i > 0 {
+			p.closeScope(clause.Pos())
+		}
+		p.openScope(clause.Pos())
+
 		n := p.nod(clause, OXCASE, nil, nil)
 		if clause.Comm != nil {
 			n.List.Set1(p.stmt(clause.Comm))
 		}
 		n.Xoffset = int64(types.Block)
 		n.Nbody.Set(p.stmts(clause.Body))
-		types.Popdcl()
 		nodes = append(nodes, n)
 	}
+	if len(clauses) > 0 {
+		p.closeScope(rbrace)
+	}
 	return nodes
 }
 
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index 355df9d32660bf5db943cc581dc785879672ef1d..427146ffb0bc60be534cd360076a07828e304980 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -299,13 +299,15 @@ func compileFunctions() {
 	}
 }
 
-func debuginfo(fnsym *obj.LSym, curfn interface{}) []*dwarf.Var {
+func debuginfo(fnsym *obj.LSym, curfn interface{}) []dwarf.Scope {
 	fn := curfn.(*Node)
 	if expect := fn.Func.Nname.Sym.Linksym(); fnsym != expect {
 		Fatalf("unexpected fnsym: %v != %v", fnsym, expect)
 	}
 
-	var vars []*dwarf.Var
+	var dwarfVars []*dwarf.Var
+	var varScopes []ScopeID
+
 	for _, n := range fn.Func.Dcl {
 		if n.Op != ONAME { // might be OTYPE or OLITERAL
 			continue
@@ -353,18 +355,26 @@ func debuginfo(fnsym *obj.LSym, curfn interface{}) []*dwarf.Var {
 		}
 
 		typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
-		vars = append(vars, &dwarf.Var{
+		dwarfVars = append(dwarfVars, &dwarf.Var{
 			Name:   n.Sym.Name,
 			Abbrev: abbrev,
 			Offset: int32(offs),
 			Type:   Ctxt.Lookup(typename),
 		})
-	}
 
-	// Stable sort so that ties are broken with declaration order.
-	sort.Stable(dwarf.VarsByOffset(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)
+		}
+
+		varScopes = append(varScopes, scope)
+	}
 
-	return vars
+	return assembleScopes(fnsym, fn, dwarfVars, varScopes)
 }
 
 // fieldtrack adds R_USEFIELD relocations to fnsym to record any
diff --git a/src/cmd/compile/internal/gc/scope.go b/src/cmd/compile/internal/gc/scope.go
new file mode 100644
index 0000000000000000000000000000000000000000..b0bc7f6908632cad2115da0bb6d184d5903dfb12
--- /dev/null
+++ b/src/cmd/compile/internal/gc/scope.go
@@ -0,0 +1,177 @@
+// 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 gc
+
+import (
+	"cmd/internal/dwarf"
+	"cmd/internal/obj"
+	"cmd/internal/src"
+	"sort"
+)
+
+// See golang.org/issue/20390.
+func xposBefore(p, q src.XPos) bool {
+	return Ctxt.PosTable.Pos(p).Before(Ctxt.PosTable.Pos(q))
+}
+
+func findScope(marks []Mark, pos src.XPos) ScopeID {
+	i := sort.Search(len(marks), func(i int) bool {
+		return xposBefore(pos, marks[i].Pos)
+	})
+	if i == 0 {
+		return 0
+	}
+	return marks[i-1].Scope
+}
+
+func assembleScopes(fnsym *obj.LSym, fn *Node, dwarfVars []*dwarf.Var, varScopes []ScopeID) []dwarf.Scope {
+	// Initialize the DWARF scope tree based on lexical scopes.
+	dwarfScopes := make([]dwarf.Scope, 1+len(fn.Func.Parents))
+	for i, parent := range fn.Func.Parents {
+		dwarfScopes[i+1].Parent = int32(parent)
+	}
+
+	scopeVariables(dwarfVars, varScopes, dwarfScopes)
+	scopePCs(fnsym, fn.Func.Marks, dwarfScopes)
+	return compactScopes(dwarfScopes)
+}
+
+// scopeVariables assigns DWARF variable records to their scopes.
+func scopeVariables(dwarfVars []*dwarf.Var, varScopes []ScopeID, dwarfScopes []dwarf.Scope) {
+	sort.Stable(varsByScopeAndOffset{dwarfVars, varScopes})
+
+	i0 := 0
+	for i := range dwarfVars {
+		if varScopes[i] == varScopes[i0] {
+			continue
+		}
+		dwarfScopes[varScopes[i0]].Vars = dwarfVars[i0:i]
+		i0 = i
+	}
+	if i0 < len(dwarfVars) {
+		dwarfScopes[varScopes[i0]].Vars = dwarfVars[i0:]
+	}
+}
+
+// A scopedPCs represents a non-empty half-open interval of PCs that
+// share a common source position.
+type scopedPCs struct {
+	start, end int64
+	pos        src.XPos
+	scope      ScopeID
+}
+
+// scopePCs assigns PC ranges to their scopes.
+func scopePCs(fnsym *obj.LSym, marks []Mark, dwarfScopes []dwarf.Scope) {
+	// If there aren't any child scopes (in particular, when scope
+	// tracking is disabled), we can skip a whole lot of work.
+	if len(marks) == 0 {
+		return
+	}
+
+	// Break function text into scopedPCs.
+	var pcs []scopedPCs
+	p0 := fnsym.Func.Text
+	for p := fnsym.Func.Text; p != nil; p = p.Link {
+		if p.Pos == p0.Pos {
+			continue
+		}
+		if p0.Pc < p.Pc {
+			pcs = append(pcs, scopedPCs{start: p0.Pc, end: p.Pc, pos: p0.Pos})
+		}
+		p0 = p
+	}
+	if p0.Pc < fnsym.Size {
+		pcs = append(pcs, scopedPCs{start: p0.Pc, end: fnsym.Size, pos: p0.Pos})
+	}
+
+	// Sort PCs by source position, and walk in parallel with
+	// scope marks to assign a lexical scope to each PC interval.
+	sort.Sort(pcsByPos(pcs))
+	var marki int
+	var scope ScopeID
+	for i := range pcs {
+		for marki < len(marks) && !xposBefore(pcs[i].pos, marks[marki].Pos) {
+			scope = marks[marki].Scope
+			marki++
+		}
+		pcs[i].scope = scope
+	}
+
+	// Re-sort to create sorted PC ranges for each DWARF scope.
+	sort.Sort(pcsByPC(pcs))
+	for _, pc := range pcs {
+		r := &dwarfScopes[pc.scope].Ranges
+		if i := len(*r); i > 0 && (*r)[i-1].End == pc.start {
+			(*r)[i-1].End = pc.end
+		} else {
+			*r = append(*r, dwarf.Range{Start: pc.start, End: pc.end})
+		}
+	}
+}
+
+func compactScopes(dwarfScopes []dwarf.Scope) []dwarf.Scope {
+	// Forward pass to collapse empty scopes into parents.
+	remap := make([]int32, len(dwarfScopes))
+	j := int32(1)
+	for i := 1; i < len(dwarfScopes); i++ {
+		s := &dwarfScopes[i]
+		s.Parent = remap[s.Parent]
+		if len(s.Vars) == 0 {
+			dwarfScopes[s.Parent].UnifyRanges(s)
+			remap[i] = s.Parent
+			continue
+		}
+		remap[i] = j
+		dwarfScopes[j] = *s
+		j++
+	}
+	dwarfScopes = dwarfScopes[:j]
+
+	// Reverse pass to propagate PC ranges to parent scopes.
+	for i := len(dwarfScopes) - 1; i > 0; i-- {
+		s := &dwarfScopes[i]
+		dwarfScopes[s.Parent].UnifyRanges(s)
+	}
+
+	return dwarfScopes
+}
+
+type pcsByPC []scopedPCs
+
+func (s pcsByPC) Len() int      { return len(s) }
+func (s pcsByPC) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s pcsByPC) Less(i, j int) bool {
+	return s[i].start < s[j].start
+}
+
+type pcsByPos []scopedPCs
+
+func (s pcsByPos) Len() int      { return len(s) }
+func (s pcsByPos) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s pcsByPos) Less(i, j int) bool {
+	return xposBefore(s[i].pos, s[j].pos)
+}
+
+type varsByScopeAndOffset struct {
+	vars   []*dwarf.Var
+	scopes []ScopeID
+}
+
+func (v varsByScopeAndOffset) Len() int {
+	return len(v.vars)
+}
+
+func (v varsByScopeAndOffset) Less(i, j int) bool {
+	if v.scopes[i] != v.scopes[j] {
+		return v.scopes[i] < v.scopes[j]
+	}
+	return v.vars[i].Offset < v.vars[j].Offset
+}
+
+func (v varsByScopeAndOffset) Swap(i, j int) {
+	v.vars[i], v.vars[j] = v.vars[j], v.vars[i]
+	v.scopes[i], v.scopes[j] = v.scopes[j], v.scopes[i]
+}
diff --git a/src/cmd/compile/internal/gc/scope_test.go b/src/cmd/compile/internal/gc/scope_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5833b1e64990ea712361404ebfd0b98002f9a730
--- /dev/null
+++ b/src/cmd/compile/internal/gc/scope_test.go
@@ -0,0 +1,407 @@
+// Copyright 2016 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 gc_test
+
+import (
+	"cmd/internal/objfile"
+	"debug/dwarf"
+	"internal/testenv"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"sort"
+	"strconv"
+	"strings"
+	"testing"
+)
+
+type testline struct {
+	// line is one line of go source
+	line string
+
+	// scopes is a list of scope IDs of all the lexical scopes that this line
+	// of code belongs to.
+	// Scope IDs are assigned by traversing the tree of lexical blocks of a
+	// function in pre-order
+	// Scope IDs are function specific, i.e. scope 0 is always the root scope
+	// of the function that this line belongs to. Empty scopes are not assigned
+	// an ID (because they are not saved in debug_info).
+	// Scope 0 is always omitted from this list since all lines always belong
+	// to it.
+	scopes []int
+
+	// vars is the list of variables that belong in scopes[len(scopes)-1].
+	// Local variables are prefixed with "var ", formal parameters with "arg ".
+	// Must be ordered alphabetically.
+	// Set to nil to skip the check.
+	vars []string
+}
+
+var testfile = []testline{
+	{line: "package main"},
+	{line: "func f1(x int) { }"},
+	{line: "func f2(x int) { }"},
+	{line: "func f3(x int) { }"},
+	{line: "func f4(x int) { }"},
+	{line: "func f5(x int) { }"},
+	{line: "func f6(x int) { }"},
+	{line: "func gret1() int { return 2 }"},
+	{line: "func gretbool() bool { return true }"},
+	{line: "func gret3() (int, int, int) { return 0, 1, 2 }"},
+	{line: "var v = []int{ 0, 1, 2 }"},
+	{line: "var ch = make(chan int)"},
+	{line: "var floatch = make(chan float64)"},
+	{line: "var iface interface{}"},
+	{line: "func TestNestedFor() {", vars: []string{"var a int"}},
+	{line: "	a := 0"},
+	{line: "	f1(a)"},
+	{line: "	for i := 0; i < 5; i++ {", scopes: []int{1}, vars: []string{"var i int"}},
+	{line: "		f2(i)", scopes: []int{1}},
+	{line: "		for i := 0; i < 5; i++ {", scopes: []int{1, 2}, vars: []string{"var i int"}},
+	{line: "			f3(i)", scopes: []int{1, 2}},
+	{line: "		}"},
+	{line: "		f4(i)", scopes: []int{1}},
+	{line: "	}"},
+	{line: "	f5(a)"},
+	{line: "}"},
+	{line: "func TestOas2() {", vars: []string{}},
+	{line: "	if a, b, c := gret3(); a != 1 {", scopes: []int{1}, vars: []string{"var a int", "var b int", "var c int"}},
+	{line: "		f1(a)", scopes: []int{1}},
+	{line: "		f1(b)", scopes: []int{1}},
+	{line: "		f1(c)", scopes: []int{1}},
+	{line: "	}"},
+	{line: "	for i, x := range v {", scopes: []int{2}, vars: []string{"var i int", "var x int"}},
+	{line: "		f1(i)", scopes: []int{2}},
+	{line: "		f1(x)", scopes: []int{2}},
+	{line: "	}"},
+	{line: "	if a, ok := <- ch; ok {", scopes: []int{3}, vars: []string{"var a int", "var ok bool"}},
+	{line: "		f1(a)", scopes: []int{3}},
+	{line: "	}"},
+	{line: "	if a, ok := iface.(int); ok {", scopes: []int{4}, vars: []string{"var a int", "var ok bool"}},
+	{line: "		f1(a)", scopes: []int{4}},
+	{line: "	}"},
+	{line: "}"},
+	{line: "func TestIfElse() {"},
+	{line: "	if x := gret1(); x != 0 {", scopes: []int{1}, vars: []string{"var x int"}},
+	{line: "		a := 0", scopes: []int{1, 2}, vars: []string{"var a int"}},
+	{line: "		f1(a); f1(x)", scopes: []int{1, 2}},
+	{line: "	} else {"},
+	{line: "		b := 1", scopes: []int{1, 3}, vars: []string{"var b int"}},
+	{line: "		f1(b); f1(x+1)", scopes: []int{1, 3}},
+	{line: "	}"},
+	{line: "}"},
+	{line: "func TestSwitch() {", vars: []string{}},
+	{line: "	switch x := gret1(); x {", scopes: []int{1}, vars: []string{"var x int"}},
+	{line: "	case 0:", scopes: []int{1, 2}},
+	{line: "		i := x + 5", scopes: []int{1, 2}, vars: []string{"var i int"}},
+	{line: "		f1(x); f1(i)", scopes: []int{1, 2}},
+	{line: "	case 1:", scopes: []int{1, 3}},
+	{line: "		j := x + 10", scopes: []int{1, 3}, vars: []string{"var j int"}},
+	{line: "		f1(x); f1(j)", scopes: []int{1, 3}},
+	{line: "	case 2:", scopes: []int{1, 4}},
+	{line: "		k := x + 2", scopes: []int{1, 4}, vars: []string{"var k int"}},
+	{line: "		f1(x); f1(k)", scopes: []int{1, 4}},
+	{line: "	}"},
+	{line: "}"},
+	{line: "func TestTypeSwitch() {", vars: []string{}},
+	{line: "	switch x := iface.(type) {"},
+	{line: "	case int:", scopes: []int{1}},
+	{line: "		f1(x)", scopes: []int{1}, vars: []string{"var x int"}},
+	{line: "	case uint8:", scopes: []int{2}},
+	{line: "		f1(int(x))", scopes: []int{2}, vars: []string{"var x uint8"}},
+	{line: "	case float64:", scopes: []int{3}},
+	{line: "		f1(int(x)+1)", scopes: []int{3}, vars: []string{"var x float64"}},
+	{line: "	}"},
+	{line: "}"},
+	{line: "func TestSelectScope() {"},
+	{line: "	select {"},
+	{line: "	case i := <- ch:", scopes: []int{1}},
+	{line: "		f1(i)", scopes: []int{1}, vars: []string{"var i int"}},
+	{line: "	case f := <- floatch:", scopes: []int{2}},
+	{line: "		f1(int(f))", scopes: []int{2}, vars: []string{"var f float64"}},
+	{line: "	}"},
+	{line: "}"},
+	{line: "func TestBlock() {", vars: []string{"var a int"}},
+	{line: "	a := 1"},
+	{line: "	{"},
+	{line: "		b := 2", scopes: []int{1}, vars: []string{"var b int"}},
+	{line: "		f1(b)", scopes: []int{1}},
+	{line: "		f1(a)", scopes: []int{1}},
+	{line: "	}"},
+	{line: "}"},
+	{line: "func TestDiscontiguousRanges() {", vars: []string{"var a int"}},
+	{line: "	a := 0"},
+	{line: "	f1(a)"},
+	{line: "	{"},
+	{line: "		b := 0", scopes: []int{1}, vars: []string{"var b int"}},
+	{line: "		f2(b)", scopes: []int{1}},
+	{line: "		if gretbool() {", scopes: []int{1}},
+	{line: "			c := 0", scopes: []int{1, 2}, vars: []string{"var c int"}},
+	{line: "			f3(c)", scopes: []int{1, 2}},
+	{line: "		} else {"},
+	{line: "			c := 1.1", scopes: []int{1, 3}, vars: []string{"var c float64"}},
+	{line: "			f4(int(c))", scopes: []int{1, 3}},
+	{line: "		}"},
+	{line: "		f5(b)", scopes: []int{1}},
+	{line: "	}"},
+	{line: "	f6(a)"},
+	{line: "}"},
+	{line: "func TestClosureScope() {", vars: []string{"var a int", "var b int", "var f func(int)"}},
+	{line: "	a := 1; b := 1"},
+	{line: "	f := func(c int) {", scopes: []int{0}, vars: []string{"arg c int", "var &b *int", "var a int", "var d int"}},
+	{line: "		d := 3"},
+	{line: "		f1(c); f1(d)"},
+	{line: "		if e := 3; e != 0 {", scopes: []int{1}, vars: []string{"var e int"}},
+	{line: "			f1(e)", scopes: []int{1}},
+	{line: "			f1(a)", scopes: []int{1}},
+	{line: "			b = 2", scopes: []int{1}},
+	{line: "		}"},
+	{line: "	}"},
+	{line: "	f(3); f1(b)"},
+	{line: "}"},
+	{line: "func main() {"},
+	{line: "	TestNestedFor()"},
+	{line: "	TestOas2()"},
+	{line: "	TestIfElse()"},
+	{line: "	TestSwitch()"},
+	{line: "	TestTypeSwitch()"},
+	{line: "	TestSelectScope()"},
+	{line: "	TestBlock()"},
+	{line: "	TestDiscontiguousRanges()"},
+	{line: "	TestClosureScope()"},
+	{line: "}"},
+}
+
+const detailOutput = false
+
+// Compiles testfile checks that the description of lexical blocks emitted
+// by the linker in debug_info, for each function in the main package,
+// corresponds to what we expect it to be.
+func TestScopeRanges(t *testing.T) {
+	testenv.MustHaveGoBuild(t)
+	dir, err := ioutil.TempDir("", "TestScopeRanges")
+	if err != nil {
+		t.Fatalf("could not create directory: %v", err)
+	}
+	defer os.RemoveAll(dir)
+
+	src, f := gobuild(t, dir, testfile)
+	defer f.Close()
+
+	// the compiler uses forward slashes for paths even on windows
+	src = strings.Replace(src, "\\", "/", -1)
+
+	pcln, err := f.PCLineTable()
+	if err != nil {
+		t.Fatal(err)
+	}
+	dwarfData, err := f.DWARF()
+	if err != nil {
+		t.Fatal(err)
+	}
+	dwarfReader := dwarfData.Reader()
+
+	lines := make(map[line][]*lexblock)
+
+	for {
+		entry, err := dwarfReader.Next()
+		if err != nil {
+			t.Fatal(err)
+		}
+		if entry == nil {
+			break
+		}
+
+		if entry.Tag != dwarf.TagSubprogram {
+			continue
+		}
+
+		name, ok := entry.Val(dwarf.AttrName).(string)
+		if !ok || !strings.HasPrefix(name, "main.Test") {
+			continue
+		}
+
+		var scope lexblock
+		ctxt := scopexplainContext{
+			dwarfData:   dwarfData,
+			dwarfReader: dwarfReader,
+			scopegen:    1,
+		}
+
+		readScope(&ctxt, &scope, entry)
+
+		scope.markLines(pcln, lines)
+	}
+
+	anyerror := false
+	for i := range testfile {
+		tgt := testfile[i].scopes
+		out := lines[line{src, i + 1}]
+
+		if detailOutput {
+			t.Logf("%s // %v", testfile[i].line, out)
+		}
+
+		scopesok := checkScopes(tgt, out)
+		if !scopesok {
+			t.Logf("mismatch at line %d %q: expected: %v got: %v\n", i, testfile[i].line, tgt, scopesToString(out))
+		}
+
+		varsok := true
+		if testfile[i].vars != nil {
+			if len(out) > 0 {
+				varsok = checkVars(testfile[i].vars, out[len(out)-1].vars)
+				if !varsok {
+					t.Logf("variable mismatch at line %d %q for scope %d: expected: %v got: %v\n", i, testfile[i].line, out[len(out)-1].id, testfile[i].vars, out[len(out)-1].vars)
+				}
+			}
+		}
+
+		anyerror = anyerror || !scopesok || !varsok
+	}
+
+	if anyerror {
+		t.Fatalf("mismatched output")
+	}
+}
+
+func scopesToString(v []*lexblock) string {
+	r := make([]string, len(v))
+	for i, s := range v {
+		r[i] = strconv.Itoa(s.id)
+	}
+	return "[ " + strings.Join(r, ", ") + " ]"
+}
+
+func checkScopes(tgt []int, out []*lexblock) bool {
+	if len(out) > 0 {
+		// omit scope 0
+		out = out[1:]
+	}
+	if len(tgt) != len(out) {
+		return false
+	}
+	for i := range tgt {
+		if tgt[i] != out[i].id {
+			return false
+		}
+	}
+	return true
+}
+
+func checkVars(tgt, out []string) bool {
+	if len(tgt) != len(out) {
+		return false
+	}
+	for i := range tgt {
+		if tgt[i] != out[i] {
+			return false
+		}
+	}
+	return true
+}
+
+type lexblock struct {
+	id     int
+	ranges [][2]uint64
+	vars   []string
+	scopes []lexblock
+}
+
+type line struct {
+	file   string
+	lineno int
+}
+
+type scopexplainContext struct {
+	dwarfData   *dwarf.Data
+	dwarfReader *dwarf.Reader
+	scopegen    int
+	lines       map[line][]int
+}
+
+// readScope reads the DW_TAG_lexical_block or the DW_TAG_subprogram in
+// entry and writes a description in scope.
+// Nested DW_TAG_lexical_block entries are read recursively.
+func readScope(ctxt *scopexplainContext, scope *lexblock, entry *dwarf.Entry) {
+	var err error
+	scope.ranges, err = ctxt.dwarfData.Ranges(entry)
+	if err != nil {
+		panic(err)
+	}
+	for {
+		e, err := ctxt.dwarfReader.Next()
+		if err != nil {
+			panic(err)
+		}
+		switch e.Tag {
+		case 0:
+			sort.Strings(scope.vars)
+			return
+		case dwarf.TagFormalParameter:
+			typ, err := ctxt.dwarfData.Type(e.Val(dwarf.AttrType).(dwarf.Offset))
+			if err != nil {
+				panic(err)
+			}
+			scope.vars = append(scope.vars, "arg "+e.Val(dwarf.AttrName).(string)+" "+typ.String())
+		case dwarf.TagVariable:
+			typ, err := ctxt.dwarfData.Type(e.Val(dwarf.AttrType).(dwarf.Offset))
+			if err != nil {
+				panic(err)
+			}
+			scope.vars = append(scope.vars, "var "+e.Val(dwarf.AttrName).(string)+" "+typ.String())
+		case dwarf.TagLexDwarfBlock:
+			scope.scopes = append(scope.scopes, lexblock{id: ctxt.scopegen})
+			ctxt.scopegen++
+			readScope(ctxt, &scope.scopes[len(scope.scopes)-1], e)
+		}
+	}
+}
+
+// markLines marks all lines that belong to this scope with this scope
+// Recursively calls markLines for all children scopes.
+func (scope *lexblock) markLines(pcln objfile.Liner, lines map[line][]*lexblock) {
+	for _, r := range scope.ranges {
+		for pc := r[0]; pc < r[1]; pc++ {
+			file, lineno, _ := pcln.PCToLine(pc)
+			l := line{file, lineno}
+			if len(lines[l]) == 0 || lines[l][len(lines[l])-1] != scope {
+				lines[l] = append(lines[l], scope)
+			}
+		}
+	}
+
+	for i := range scope.scopes {
+		scope.scopes[i].markLines(pcln, lines)
+	}
+}
+
+func gobuild(t *testing.T, dir string, testfile []testline) (string, *objfile.File) {
+	src := filepath.Join(dir, "test.go")
+	dst := filepath.Join(dir, "out.o")
+
+	f, err := os.Create(src)
+	if err != nil {
+		t.Fatal(err)
+	}
+	for i := range testfile {
+		f.Write([]byte(testfile[i].line))
+		f.Write([]byte{'\n'})
+	}
+	f.Close()
+
+	cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", dst, src)
+	if b, err := cmd.CombinedOutput(); err != nil {
+		t.Logf("build: %s\n", string(b))
+		t.Fatal(err)
+	}
+
+	pkg, err := objfile.Open(dst)
+	if err != nil {
+		t.Fatal(err)
+	}
+	return src, pkg
+}
diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go
index e25b117338135be2511ca2f86f1866173eea384d..1ca0a615353afe1f3962bc7a573591f0806143e9 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{}, 100, 168},
+		{Func{}, 124, 216},
 		{Name{}, 36, 56},
 		{Param{}, 28, 56},
 		{Node{}, 76, 128},
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index 9b1eeaf43e83c7bd0d1a237304e506219c585af8..0fd146bca26deb57258173aa37e0f9d9c15785ce 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -351,12 +351,21 @@ type Param struct {
 
 // Func holds Node fields used only with function-like nodes.
 type Func struct {
-	Shortname  *types.Sym
-	Enter      Nodes // for example, allocate and initialize memory for escaping parameters
-	Exit       Nodes
-	Cvars      Nodes   // closure params
-	Dcl        []*Node // autodcl for this func/closure
-	Inldcl     Nodes   // copy of dcl for use in inlining
+	Shortname *types.Sym
+	Enter     Nodes // for example, allocate and initialize memory for escaping parameters
+	Exit      Nodes
+	Cvars     Nodes   // closure params
+	Dcl       []*Node // autodcl for this func/closure
+	Inldcl    Nodes   // copy of dcl for use in inlining
+
+	// Parents records the parent scope of each scope within a
+	// function. The root scope (0) has no parent, so the i'th
+	// scope's parent is stored at Parents[i-1].
+	Parents []ScopeID
+
+	// Marks records scope boundary changes.
+	Marks []Mark
+
 	Closgen    int
 	Outerfunc  *Node // outer function (for closure)
 	FieldTrack map[*types.Sym]struct{}
@@ -380,6 +389,19 @@ type Func struct {
 	flags bitset8
 }
 
+// A Mark represents a scope boundary.
+type Mark struct {
+	// Pos is the position of the token that marks the scope
+	// change.
+	Pos src.XPos
+
+	// Scope identifies the innermost scope to the right of Pos.
+	Scope ScopeID
+}
+
+// A ScopeID represents a lexical scope within a function.
+type ScopeID int32
+
 const (
 	funcDupok         = 1 << iota // duplicate definitions ok
 	funcWrapper                   // is method wrapper
diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go
index 042a79b5a6824d102668a5fbf6ffb934b7baf05d..2974bf50921d4818d982348f13b2dac977c5a86c 100644
--- a/src/cmd/internal/dwarf/dwarf.go
+++ b/src/cmd/internal/dwarf/dwarf.go
@@ -8,14 +8,19 @@
 package dwarf
 
 import (
+	"errors"
 	"fmt"
 )
 
 // 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 range lists.
+const RangePrefix = "go.range."
+
 // Sym represents a symbol.
 type Sym interface {
+	Len() int64
 }
 
 // A Var represents a local variable or a function parameter.
@@ -23,9 +28,62 @@ type Var struct {
 	Name   string
 	Abbrev int // Either DW_ABRV_AUTO or DW_ABRV_PARAM
 	Offset int32
+	Scope  int32
 	Type   Sym
 }
 
+// A Scope represents a lexical scope. All variables declared within a
+// scope will only be visible to instructions covered by the scope.
+// Lexical scopes are contiguous in source files but can end up being
+// compiled to discontiguous blocks of instructions in the executable.
+// The Ranges field lists all the blocks of instructions that belong
+// in this scope.
+type Scope struct {
+	Parent int32
+	Ranges []Range
+	Vars   []*Var
+}
+
+// A Range represents a half-open interval [Start, End).
+type Range struct {
+	Start, End int64
+}
+
+// UnifyRanges merges the list of ranges of c into the list of ranges of s
+func (s *Scope) UnifyRanges(c *Scope) {
+	out := make([]Range, 0, len(s.Ranges)+len(c.Ranges))
+
+	i, j := 0, 0
+	for {
+		var cur Range
+		if i < len(s.Ranges) && j < len(c.Ranges) {
+			if s.Ranges[i].Start < c.Ranges[j].Start {
+				cur = s.Ranges[i]
+				i++
+			} else {
+				cur = c.Ranges[j]
+				j++
+			}
+		} else if i < len(s.Ranges) {
+			cur = s.Ranges[i]
+			i++
+		} else if j < len(c.Ranges) {
+			cur = c.Ranges[j]
+			j++
+		} else {
+			break
+		}
+
+		if n := len(out); n > 0 && cur.Start <= out[n-1].End {
+			out[n-1].End = cur.End
+		} else {
+			out = append(out, cur)
+		}
+	}
+
+	s.Ranges = out
+}
+
 // A Context specifies how to add data to a Sym.
 type Context interface {
 	PtrSize() int
@@ -156,6 +214,8 @@ const (
 	DW_ABRV_VARIABLE
 	DW_ABRV_AUTO
 	DW_ABRV_PARAM
+	DW_ABRV_LEXICAL_BLOCK_RANGES
+	DW_ABRV_LEXICAL_BLOCK_SIMPLE
 	DW_ABRV_STRUCTFIELD
 	DW_ABRV_FUNCTYPEPARAM
 	DW_ABRV_DOTDOTDOT
@@ -247,6 +307,25 @@ var abbrevs = [DW_NABRV]dwAbbrev{
 		},
 	},
 
+	/* LEXICAL_BLOCK_RANGES */
+	{
+		DW_TAG_lexical_block,
+		DW_CHILDREN_yes,
+		[]dwAttrForm{
+			{DW_AT_ranges, DW_FORM_data4}, // replace with DW_FORM_sec_offset in DWARFv4.
+		},
+	},
+
+	/* LEXICAL_BLOCK_SIMPLE */
+	{
+		DW_TAG_lexical_block,
+		DW_CHILDREN_yes,
+		[]dwAttrForm{
+			{DW_AT_low_pc, DW_FORM_addr},
+			{DW_AT_high_pc, DW_FORM_addr},
+		},
+	},
+
 	/* STRUCTFIELD */
 	{
 		DW_TAG_member,
@@ -525,8 +604,8 @@ func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, da
 		ctxt.AddInt(s, 2, value)
 
 	case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr
-		if cls == DW_CLS_PTR { // DW_AT_stmt_list
-			ctxt.AddSectionOffset(s, 4, data, 0)
+		if cls == DW_CLS_PTR { // DW_AT_stmt_list and DW_AT_ranges
+			ctxt.AddSectionOffset(s, 4, data, value)
 			break
 		}
 		ctxt.AddInt(s, 4, value)
@@ -555,15 +634,13 @@ func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, da
 			ctxt.AddInt(s, 1, 0)
 		}
 
-	// In DWARF 2 (which is what we claim to generate),
-	// the ref_addr is the same size as a normal address.
-	// In DWARF 3 it is always 32 bits, unless emitting a large
+	// In DWARF 3 the ref_addr is always 32 bits, unless emitting a large
 	// (> 4 GB of debug info aka "64-bit") unit, which we don't implement.
 	case DW_FORM_ref_addr: // reference to a DIE in the .info section
 		if data == nil {
 			return fmt.Errorf("dwarf: null reference in %d", abbrev)
 		} else {
-			ctxt.AddSectionOffset(s, ctxt.PtrSize(), data, 0)
+			ctxt.AddSectionOffset(s, 4, data, 0)
 		}
 
 	case DW_FORM_ref1, // reference within the compilation unit
@@ -606,7 +683,7 @@ 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 Sym, name string, external bool, startPC Sym, size int64, vars []*Var) {
+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)
@@ -616,29 +693,67 @@ func PutFunc(ctxt Context, s Sym, name string, external bool, startPC Sym, size
 		ev = 1
 	}
 	putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
-	names := make(map[string]bool)
-	var encbuf [20]byte
-	for _, v := range vars {
-		var n string
-		if names[v.Name] {
-			n = fmt.Sprintf("%s#%d", v.Name, len(names))
-		} else {
-			n = v.Name
-		}
-		names[n] = true
-
-		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.Offset != 0 {
-			loc = append(loc, DW_OP_consts)
-			loc = AppendSleb128(loc, int64(v.Offset))
-			loc = append(loc, DW_OP_plus)
+	if len(scopes) > 0 {
+		var encbuf [20]byte
+		if putscope(ctxt, s, ranges, startPC, 0, scopes, encbuf[:0]) < int32(len(scopes)) {
+			return errors.New("multiple toplevel scopes")
 		}
-		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)
 	}
+
 	Uleb128put(ctxt, s, 0)
+	return nil
+}
+
+func putscope(ctxt Context, s, ranges Sym, startPC Sym, curscope int32, scopes []Scope, encbuf []byte) int32 {
+	for _, v := range scopes[curscope].Vars {
+		putvar(ctxt, s, v, encbuf)
+	}
+	this := curscope
+	curscope++
+	for curscope < int32(len(scopes)) {
+		scope := scopes[curscope]
+		if scope.Parent != this {
+			return curscope
+		}
+
+		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)
+		} else {
+			Uleb128put(ctxt, s, DW_ABRV_LEXICAL_BLOCK_RANGES)
+			putattr(ctxt, s, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_data4, DW_CLS_PTR, ranges.Len(), ranges)
+
+			ctxt.AddAddress(ranges, nil, -1)
+			ctxt.AddAddress(ranges, startPC, 0)
+			for _, r := range scope.Ranges {
+				ctxt.AddAddress(ranges, nil, r.Start)
+				ctxt.AddAddress(ranges, nil, r.End)
+			}
+			ctxt.AddAddress(ranges, nil, 0)
+			ctxt.AddAddress(ranges, nil, 0)
+		}
+
+		curscope = putscope(ctxt, s, ranges, startPC, curscope, scopes, encbuf)
+
+		Uleb128put(ctxt, s, 0)
+	}
+	return curscope
+}
+
+func putvar(ctxt Context, s Sym, v *Var, 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.Offset != 0 {
+		loc = append(loc, DW_OP_consts)
+		loc = AppendSleb128(loc, int64(v.Offset))
+		loc = append(loc, DW_OP_plus)
+	}
+	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)
 }
 
 // VarsByOffset attaches the methods of sort.Interface to []*Var,
diff --git a/src/cmd/internal/obj/data.go b/src/cmd/internal/obj/data.go
index 8b1bdb1056fc17dcf572557857d5cbec96a3baf6..23d1809e0c146386a44341a7d0a2d9d0dd0a1831 100644
--- a/src/cmd/internal/obj/data.go
+++ b/src/cmd/internal/obj/data.go
@@ -120,7 +120,8 @@ func (s *LSym) WriteInt(ctxt *Link, off int64, siz int, i int64) {
 // WriteAddr writes an address of size siz into s at offset off.
 // rsym and roff specify the relocation for the address.
 func (s *LSym) WriteAddr(ctxt *Link, off int64, siz int, rsym *LSym, roff int64) {
-	if siz != ctxt.Arch.PtrSize {
+	// Allow 4-byte addresses for DWARF.
+	if siz != ctxt.Arch.PtrSize && siz != 4 {
 		ctxt.Diag("WriteAddr: bad address size %d in %s", siz, s.Name)
 	}
 	s.prepwrite(ctxt, off, siz)
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 8bdc3f55e99e4b4a6783dca4831cd582c56448e4..d49bc8c5644c78ad75c96e2bbd130effd73e81cd 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -324,12 +324,15 @@ type LSym struct {
 
 // A FuncInfo contains extra fields for STEXT symbols.
 type FuncInfo struct {
-	Args     int32
-	Locals   int32
-	Text     *Prog
-	Autom    []*Auto
-	Pcln     Pcln
-	dwarfSym *LSym
+	Args   int32
+	Locals int32
+	Text   *Prog
+	Autom  []*Auto
+	Pcln   Pcln
+
+	dwarfSym       *LSym
+	dwarfRangesSym *LSym
+
 	GCArgs   LSym
 	GCLocals LSym
 }
@@ -490,7 +493,7 @@ type Link struct {
 	InlTree       InlTree // global inlining tree used by gc/inl.go
 	Imports       []string
 	DiagFunc      func(string, ...interface{})
-	DebugInfo     func(fn *LSym, curfn interface{}) []*dwarf.Var // if non-nil, curfn is a *gc.Node
+	DebugInfo     func(fn *LSym, curfn interface{}) []dwarf.Scope // if non-nil, curfn is a *gc.Node
 	Errors        int
 
 	Framepointer_enabled bool
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index dc22eacdf48bfe75f94bfcb2546b3c8a7fe67313..e309c5f7e785122a37edc74fad443ddc83872725 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -447,10 +447,14 @@ func (c dwCtxt) SymValue(s dwarf.Sym) int64 {
 	return 0
 }
 func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
-	rsym := data.(*LSym)
 	ls := s.(*LSym)
 	size := c.PtrSize()
-	ls.WriteAddr(c.Link, ls.Size, size, rsym, value)
+	if data != nil {
+		rsym := data.(*LSym)
+		ls.WriteAddr(c.Link, ls.Size, size, rsym, value)
+	} else {
+		ls.WriteInt(c.Link, ls.Size, size, value)
+	}
 }
 func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
 	ls := s.(*LSym)
@@ -460,27 +464,35 @@ func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64
 	r.Type = objabi.R_DWARFREF
 }
 
-// dwarfSym returns the DWARF symbol for TEXT symbol.
-func (ctxt *Link) dwarfSym(s *LSym) *LSym {
+// dwarfSym returns the DWARF symbols for TEXT symbol.
+func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, 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)
+		s.Func.dwarfRangesSym = ctxt.LookupDerived(s, dwarf.RangePrefix+s.Name)
 	}
-	return s.Func.dwarfSym
+	return s.Func.dwarfSym, s.Func.dwarfRangesSym
 }
 
-// populateDWARF fills in the DWARF Debugging Information Entry for TEXT symbol s.
-// The DWARF symbol must already have been initialized in InitTextSym.
+func (s *LSym) Len() int64 {
+	return s.Size
+}
+
+// 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 := ctxt.dwarfSym(s)
+	dsym, drsym := ctxt.dwarfSym(s)
 	if dsym.Size != 0 {
 		ctxt.Diag("makeFuncDebugEntry double process %v", s)
 	}
-	var vars []*dwarf.Var
+	var scopes []dwarf.Scope
 	if ctxt.DebugInfo != nil {
-		vars = ctxt.DebugInfo(s, curfn)
+		scopes = ctxt.DebugInfo(s, curfn)
+	}
+	err := dwarf.PutFunc(dwCtxt{ctxt}, dsym, drsym, s.Name, !s.Static(), s, s.Size, scopes)
+	if err != nil {
+		ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
 	}
-	dwarf.PutFunc(dwCtxt{ctxt}, dsym, s.Name, !s.Static(), s, s.Size, vars)
 }
diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go
index 5c86c20e73db5c75c6453d28a5078e552d3cc0b0..861da88703c859f6e27d166776d7820b320e4456 100644
--- a/src/cmd/internal/obj/plist.go
+++ b/src/cmd/internal/obj/plist.go
@@ -135,11 +135,14 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
 	s.Type = objabi.STEXT
 	ctxt.Text = append(ctxt.Text, s)
 
-	// Set up DWARF entry for s.
-	dsym := ctxt.dwarfSym(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)
 
 	// Set up the function's gcargs and gclocals.
 	// They will be filled in later if needed.
diff --git a/src/cmd/internal/objabi/symkind.go b/src/cmd/internal/objabi/symkind.go
index 62a7efd964084f3f355b5dfa2df9db4e395e0007..b037e9e4ed17c6ecfef1d136d3b226f484ff2618 100644
--- a/src/cmd/internal/objabi/symkind.go
+++ b/src/cmd/internal/objabi/symkind.go
@@ -56,4 +56,5 @@ const (
 	STLSBSS
 	// Debugging data
 	SDWARFINFO
+	SDWARFRANGE
 )
diff --git a/src/cmd/internal/objabi/symkind_string.go b/src/cmd/internal/objabi/symkind_string.go
index aabcfd2d54c1aee8caa84c133310402edc8f52cf..5123dc7097f49ad8aa917425af8f8d7f0d207ae0 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 = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFINFO"
+const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFINFOSDWARFRANGE"
 
-var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 61}
+var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 61, 72}
 
 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 d724c596c138758a53838de6249d43e6f2c0c74b..def7055707abc9b720869ae8f38eade09fc99361 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -574,9 +574,18 @@ func relocsym(ctxt *Link, s *Symbol) {
 			}
 
 		case objabi.R_DWARFREF:
-			if r.Sym.Sect == nil {
+			var sectName string
+			var vaddr int64
+			switch {
+			case r.Sym.Sect != nil:
+				sectName = r.Sym.Sect.Name
+				vaddr = int64(r.Sym.Sect.Vaddr)
+			case r.Sym.Type == SDWARFRANGE:
+				sectName = ".debug_ranges"
+			default:
 				Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name)
 			}
+
 			if Linkmode == LinkExternal {
 				r.Done = 0
 				// PE code emits IMAGE_REL_I386_SECREL and IMAGE_REL_AMD64_SECREL
@@ -588,8 +597,9 @@ func relocsym(ctxt *Link, s *Symbol) {
 					r.Type = objabi.R_ADDR
 				}
 
-				r.Xsym = ctxt.Syms.ROLookup(r.Sym.Sect.Name, 0)
-				r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr)
+				r.Xsym = ctxt.Syms.ROLookup(sectName, 0)
+				r.Xadd = r.Add + Symaddr(r.Sym) - vaddr
+
 				o = r.Xadd
 				rs = r.Xsym
 				if Iself && SysArch.Family == sys.AMD64 {
@@ -597,7 +607,7 @@ func relocsym(ctxt *Link, s *Symbol) {
 				}
 				break
 			}
-			o = Symaddr(r.Sym) + r.Add - int64(r.Sym.Sect.Vaddr)
+			o = Symaddr(r.Sym) + r.Add - vaddr
 
 		case objabi.R_WEAKADDROFF:
 			if !r.Sym.Attr.Reachable() {
@@ -1821,6 +1831,7 @@ func (ctxt *Link) dodata() {
 		if s.Type != SDWARFSECT {
 			break
 		}
+
 		sect = addsection(&Segdwarf, s.Name, 04)
 		sect.Align = 1
 		datsize = Rnd(datsize, int64(sect.Align))
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 785fe374d210c6e9acff91b98e69a9c28127a508..c705bf3ba55401f84046bb026341c118e282d4a8 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -8,7 +8,6 @@
 //   - assign global variables and types to their packages
 //   - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg
 //     ptype struct '[]uint8' and qualifiers need to be quoted away
-//   - lexical scoping is lost, so gdb gets confused as to which 'main.i' you mean.
 //   - file:line info for variables
 //   - make strings a typedef so prettyprinters can see the underlying string type
 
@@ -76,6 +75,7 @@ var arangessec *Symbol
 var framesec *Symbol
 var infosec *Symbol
 var linesec *Symbol
+var rangesec *Symbol
 
 var gdbscript string
 
@@ -1291,6 +1291,33 @@ func writeframes(ctxt *Link, syms []*Symbol) []*Symbol {
 	return syms
 }
 
+func writeranges(ctxt *Link, syms []*Symbol) []*Symbol {
+	if rangesec == nil {
+		rangesec = ctxt.Syms.Lookup(".debug_ranges", 0)
+	}
+	rangesec.Type = SDWARFSECT
+	rangesec.Attr |= AttrReachable
+	rangesec.R = rangesec.R[:0]
+
+	for _, s := range ctxt.Textp {
+		rangeSym := ctxt.Syms.Lookup(dwarf.RangePrefix+s.Name, int(s.Version))
+		rangeSym.Attr |= AttrReachable
+		rangeSym.Type = SDWARFRANGE
+		rangeSym.Value = rangesec.Size
+		rangesec.P = append(rangesec.P, rangeSym.P...)
+		for _, r := range rangeSym.R {
+			r.Off += int32(rangesec.Size)
+			rangesec.R = append(rangesec.R, r)
+		}
+		rangesec.Size += rangeSym.Size
+	}
+	if rangesec.Size > 0 {
+		// PE does not like empty sections
+		syms = append(syms, rangesec)
+	}
+	return syms
+}
+
 /*
  *  Walk DWarfDebugInfoEntries, and emit .debug_info
  */
@@ -1321,7 +1348,7 @@ func writeinfo(ctxt *Link, syms []*Symbol, funcs []*Symbol) []*Symbol {
 		// Fields marked with (*) must be changed for 64-bit dwarf
 		// This must match COMPUNITHEADERSIZE above.
 		Adduint32(ctxt, s, 0) // unit_length (*), will be filled in later.
-		Adduint16(ctxt, s, 2) // dwarf version (appendix F)
+		Adduint16(ctxt, s, 3) // dwarf version (appendix F)
 
 		// debug_abbrev_offset (*)
 		adddwarfref(ctxt, s, abbrevsym, 4)
@@ -1553,6 +1580,7 @@ func dwarfgeneratedebugsyms(ctxt *Link) {
 	syms := writeabbrev(ctxt, nil)
 	syms, funcs := writelines(ctxt, syms)
 	syms = writeframes(ctxt, syms)
+	syms = writeranges(ctxt, syms)
 
 	synthesizestringtypes(ctxt, dwtypes.Child)
 	synthesizeslicetypes(ctxt, dwtypes.Child)
@@ -1594,6 +1622,7 @@ func dwarfaddshstrings(ctxt *Link, shstrtab *Symbol) {
 	Addstring(shstrtab, ".debug_pubnames")
 	Addstring(shstrtab, ".debug_pubtypes")
 	Addstring(shstrtab, ".debug_gdb_scripts")
+	Addstring(shstrtab, ".debug_ranges")
 	if Linkmode == LinkExternal {
 		Addstring(shstrtab, elfRelType+".debug_info")
 		Addstring(shstrtab, elfRelType+".debug_aranges")
@@ -1601,6 +1630,7 @@ func dwarfaddshstrings(ctxt *Link, shstrtab *Symbol) {
 		Addstring(shstrtab, elfRelType+".debug_frame")
 		Addstring(shstrtab, elfRelType+".debug_pubnames")
 		Addstring(shstrtab, elfRelType+".debug_pubtypes")
+		Addstring(shstrtab, elfRelType+".debug_ranges")
 	}
 }
 
@@ -1621,6 +1651,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_ranges", 0)
+	if sym.Sect != nil {
+		putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
+	}
 }
 
 /*
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index 6a1a47e213a740e29f3f7ad815d929c923538646..8d9e4a7cf3a019d1db0d3f3a9cf95e7bf9cef68e 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -89,6 +89,10 @@ func (s *Symbol) ElfsymForReloc() int32 {
 	}
 }
 
+func (s *Symbol) Len() int64 {
+	return s.Size
+}
+
 // Attribute is a set of common symbol attributes.
 type Attribute int16
 
diff --git a/src/cmd/link/internal/ld/nooptcgolink_test.go b/src/cmd/link/internal/ld/nooptcgolink_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..1df29652b2016b81c4097cb32ac2e86332040035
--- /dev/null
+++ b/src/cmd/link/internal/ld/nooptcgolink_test.go
@@ -0,0 +1,32 @@
+// 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 ld
+
+import (
+	"internal/testenv"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"testing"
+)
+
+func TestNooptCgoBuild(t *testing.T) {
+	testenv.MustHaveGoBuild(t)
+	testenv.MustHaveCGO(t)
+	dir, err := ioutil.TempDir("", "go-build")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+	cmd := exec.Command("go", "build", "-gcflags=-N -l", "-o", filepath.Join(dir, "a.out"))
+	cmd.Dir = filepath.Join(runtime.GOROOT(), "src", "runtime", "testdata", "testprogcgo")
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		t.Logf("go build output: %s", out)
+		t.Fatal(err)
+	}
+}
diff --git a/src/cmd/link/internal/ld/symkind.go b/src/cmd/link/internal/ld/symkind.go
index 1f95f8afa8b1bca9b15a027e88c9d826233f1962..c057f6cd0c937a70c061d68ca757d4bd742acca1 100644
--- a/src/cmd/link/internal/ld/symkind.go
+++ b/src/cmd/link/internal/ld/symkind.go
@@ -104,6 +104,7 @@ const (
 	SHOSTOBJ
 	SDWARFSECT
 	SDWARFINFO
+	SDWARFRANGE
 	SSUB       = SymKind(1 << 8)
 	SMASK      = SymKind(SSUB - 1)
 	SHIDDEN    = SymKind(1 << 9)
@@ -122,6 +123,7 @@ var abiSymKindToSymKind = [...]SymKind{
 	SNOPTRBSS,
 	STLSBSS,
 	SDWARFINFO,
+	SDWARFRANGE,
 }
 
 // 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 b9aaa72f3486d68db14cee81d78ebe306557f42d..2178b50c36654f3bc82b3f6db4c39bd4cf28a3dc 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 = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILESFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFO"
+const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILESFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFOSDWARFRANGE"
 
-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}
+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}
 
 func (i SymKind) String() string {
 	if i < 0 || i >= SymKind(len(_SymKind_index)-1) {