cmd/compile: change Node.Nbody, Func.Inl from *NodeList to Nodes

Passes toolstash -cmp.

Casual timings show about a 3% improvement in compile times.

Update #14473.

Change-Id: I584add2e8f1a52486ba418b25ba6122b7347b643
Reviewed-on: https://go-review.googlesource.com/19989
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/cmd/compile/internal/gc/alg.go b/src/cmd/compile/internal/gc/alg.go
index c993737..a0ff489 100644
--- a/src/cmd/compile/internal/gc/alg.go
+++ b/src/cmd/compile/internal/gc/alg.go
@@ -232,9 +232,9 @@
 		na.Etype = 1 // no escape to heap
 		call.List = list(call.List, na)
 		call.List = list(call.List, nh)
-		n.Nbody = list(n.Nbody, Nod(OAS, nh, call))
+		n.Nbody.Append(Nod(OAS, nh, call))
 
-		fn.Nbody = list(fn.Nbody, n)
+		fn.Nbody.Append(n)
 
 	// Walk the struct using memhash for runs of AMEM
 	// and calling specific hash functions for the others.
@@ -262,7 +262,7 @@
 				call.List = list(call.List, na)
 				call.List = list(call.List, nh)
 				call.List = list(call.List, Nodintconst(size))
-				fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
+				fn.Nbody.Append(Nod(OAS, nh, call))
 			}
 
 			if t1 == nil {
@@ -285,7 +285,7 @@
 			na.Etype = 1 // no escape to heap
 			call.List = list(call.List, na)
 			call.List = list(call.List, nh)
-			fn.Nbody = list(fn.Nbody, Nod(OAS, nh, call))
+			fn.Nbody.Append(Nod(OAS, nh, call))
 
 			t1 = t1.Down
 		}
@@ -293,17 +293,17 @@
 
 	r := Nod(ORETURN, nil, nil)
 	r.List = list(r.List, nh)
-	fn.Nbody = list(fn.Nbody, r)
+	fn.Nbody.Append(r)
 
 	if Debug['r'] != 0 {
-		dumplist("genhash body", fn.Nbody)
+		dumpslice("genhash body", fn.Nbody.Slice())
 	}
 
 	funcbody(fn)
 	Curfn = fn
 	fn.Func.Dupok = true
 	typecheck(&fn, Etop)
-	typechecklist(fn.Nbody, Etop)
+	typecheckslice(fn.Nbody.Slice(), Etop)
 	Curfn = nil
 
 	// Disable safemode while compiling this code: the code we
@@ -429,14 +429,14 @@
 		nif.Left = Nod(ONE, nx, ny)
 		r := Nod(ORETURN, nil, nil)
 		r.List = list(r.List, Nodbool(false))
-		nif.Nbody = list(nif.Nbody, r)
-		nrange.Nbody = list(nrange.Nbody, nif)
-		fn.Nbody = list(fn.Nbody, nrange)
+		nif.Nbody.Append(r)
+		nrange.Nbody.Append(nif)
+		fn.Nbody.Append(nrange)
 
 		// return true
 		ret := Nod(ORETURN, nil, nil)
 		ret.List = list(ret.List, Nodbool(true))
-		fn.Nbody = list(fn.Nbody, ret)
+		fn.Nbody.Append(ret)
 
 	// Walk the struct using memequal for runs of AMEM
 	// and calling specific equality tests for the others.
@@ -500,18 +500,18 @@
 
 		ret := Nod(ORETURN, nil, nil)
 		ret.List = list(ret.List, and)
-		fn.Nbody = list(fn.Nbody, ret)
+		fn.Nbody.Append(ret)
 	}
 
 	if Debug['r'] != 0 {
-		dumplist("geneq body", fn.Nbody)
+		dumpslice("geneq body", fn.Nbody.Slice())
 	}
 
 	funcbody(fn)
 	Curfn = fn
 	fn.Func.Dupok = true
 	typecheck(&fn, Etop)
-	typechecklist(fn.Nbody, Etop)
+	typecheckslice(fn.Nbody.Slice(), Etop)
 	Curfn = nil
 
 	// Disable safemode while compiling this code: the code we
diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index dc55bb0..49793bf 100644
--- a/src/cmd/compile/internal/gc/bexport.go
+++ b/src/cmd/compile/internal/gc/bexport.go
@@ -748,7 +748,7 @@
 // is written out for exported functions with inlined function bodies.
 
 func (p *exporter) collectInlined(n *Node) int {
-	if n != nil && n.Func != nil && n.Func.Inl != nil {
+	if n != nil && n.Func != nil && len(n.Func.Inl.Slice()) != 0 {
 		// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
 		// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
 		if Debug['l'] < 2 {
@@ -762,13 +762,13 @@
 
 func (p *exporter) body(i int, f *Func) {
 	p.int(i)
-	p.block(f.Inl)
+	p.block(f.Inl.Slice())
 }
 
-func (p *exporter) block(list *NodeList) {
-	p.int(count(list))
-	for q := list; q != nil; q = q.Next {
-		p.stmt(q.N)
+func (p *exporter) block(list []*Node) {
+	p.int(len(list))
+	for _, n := range list {
+		p.stmt(n)
 	}
 }
 
diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go
index f330f1b..5c2ffa6 100644
--- a/src/cmd/compile/internal/gc/bimport.go
+++ b/src/cmd/compile/internal/gc/bimport.go
@@ -95,7 +95,7 @@
 		funchdr(n)
 
 		// go.y:hidden_import
-		n.Func.Inl = nil
+		n.Func.Inl.Set(nil)
 		funcbody(n)
 		importlist = append(importlist, n) // TODO(gri) do this only if body is inlineable?
 	}
@@ -253,7 +253,7 @@
 			n.Type.Nname = n
 
 			// go.y:hidden_import
-			n.Func.Inl = nil
+			n.Func.Inl.Set(nil)
 			funcbody(n)
 			importlist = append(importlist, n) // TODO(gri) do this only if body is inlineable?
 		}
diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go
index 9a7a5c0..d8ec059 100644
--- a/src/cmd/compile/internal/gc/closure.go
+++ b/src/cmd/compile/internal/gc/closure.go
@@ -59,7 +59,7 @@
 	}
 
 	func_ := Curfn
-	func_.Nbody = body
+	func_.Nbody.SetToNodeList(body)
 	func_.Func.Endlineno = lineno
 	funcbody(func_)
 
@@ -111,7 +111,7 @@
 		Curfn = func_
 		olddd := decldepth
 		decldepth = 1
-		typechecklist(func_.Nbody, Etop)
+		typecheckslice(func_.Nbody.Slice(), Etop)
 		decldepth = olddd
 		Curfn = oldfn
 	}
@@ -193,10 +193,10 @@
 	xfunc.Func.Endlineno = func_.Func.Endlineno
 	makefuncsym(xfunc.Func.Nname.Sym)
 
-	xfunc.Nbody = func_.Nbody
+	xfunc.Nbody.Set(func_.Nbody.Slice())
 	xfunc.Func.Dcl = append(func_.Func.Dcl, xfunc.Func.Dcl...)
 	func_.Func.Dcl = nil
-	if xfunc.Nbody == nil {
+	if len(xfunc.Nbody.Slice()) == 0 {
 		Fatalf("empty body - won't generate any code")
 	}
 	typecheck(&xfunc, Etop)
@@ -204,7 +204,7 @@
 	xfunc.Func.Closure = func_
 	func_.Func.Closure = xfunc
 
-	func_.Nbody = nil
+	func_.Nbody.Set(nil)
 	func_.List = nil
 	func_.Rlist = nil
 
@@ -589,30 +589,30 @@
 	ptr.Used = true
 	ptr.Name.Curfn = xfunc
 	xfunc.Func.Dcl = append(xfunc.Func.Dcl, ptr)
-	var body *NodeList
+	var body []*Node
 	if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) {
 		ptr.Name.Param.Ntype = typenod(rcvrtype)
-		body = list(body, Nod(OAS, ptr, cv))
+		body = append(body, Nod(OAS, ptr, cv))
 	} else {
 		ptr.Name.Param.Ntype = typenod(Ptrto(rcvrtype))
-		body = list(body, Nod(OAS, ptr, Nod(OADDR, cv, nil)))
+		body = append(body, Nod(OAS, ptr, Nod(OADDR, cv, nil)))
 	}
 
 	call := Nod(OCALL, Nod(OXDOT, ptr, meth), nil)
 	call.List = callargs
 	call.Isddd = ddd
 	if t0.Outtuple == 0 {
-		body = list(body, call)
+		body = append(body, call)
 	} else {
 		n := Nod(OAS2, nil, nil)
 		n.List = retargs
 		n.Rlist = list1(call)
-		body = list(body, n)
+		body = append(body, n)
 		n = Nod(ORETURN, nil, nil)
-		body = list(body, n)
+		body = append(body, n)
 	}
 
-	xfunc.Nbody = body
+	xfunc.Nbody.Set(body)
 
 	typecheck(&xfunc, Etop)
 	sym.Def = xfunc
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index 33c04c5..52ada12 100644
--- a/src/cmd/compile/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -1520,7 +1520,7 @@
 			for _, n := range list {
 				if n.Func.WBLineno == 0 {
 					c.curfn = n
-					c.visitcodelist(n.Nbody)
+					c.visitcodeslice(n.Nbody.Slice())
 				}
 			}
 			if c.stable {
@@ -1557,6 +1557,12 @@
 	}
 }
 
+func (c *nowritebarrierrecChecker) visitcodeslice(l []*Node) {
+	for _, n := range l {
+		c.visitcode(n)
+	}
+}
+
 func (c *nowritebarrierrecChecker) visitcode(n *Node) {
 	if n == nil {
 		return
@@ -1570,7 +1576,7 @@
 	c.visitcode(n.Left)
 	c.visitcode(n.Right)
 	c.visitcodelist(n.List)
-	c.visitcodelist(n.Nbody)
+	c.visitcodeslice(n.Nbody.Slice())
 	c.visitcodelist(n.Rlist)
 }
 
diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
index 5745994..e26cbb3 100644
--- a/src/cmd/compile/internal/gc/esc.go
+++ b/src/cmd/compile/internal/gc/esc.go
@@ -78,7 +78,7 @@
 	min := v.visitgen
 
 	v.stack = append(v.stack, n)
-	min = v.visitcodelist(n.Nbody, min)
+	min = v.visitcodeslice(n.Nbody.Slice(), min)
 	if (min == id || min == id+1) && n.Func.FCurfn == nil {
 		// This node is the root of a strongly connected component.
 
@@ -117,6 +117,13 @@
 	return min
 }
 
+func (v *bottomUpVisitor) visitcodeslice(l []*Node, min uint32) uint32 {
+	for _, n := range l {
+		min = v.visitcode(n, min)
+	}
+	return min
+}
+
 func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 {
 	if n == nil {
 		return min
@@ -126,7 +133,7 @@
 	min = v.visitcode(n.Left, min)
 	min = v.visitcode(n.Right, min)
 	min = v.visitcodelist(n.List, min)
-	min = v.visitcodelist(n.Nbody, min)
+	min = v.visitcodeslice(n.Nbody.Slice(), min)
 	min = v.visitcodelist(n.Rlist, min)
 
 	if n.Op == OCALLFUNC || n.Op == OCALLMETH {
@@ -491,7 +498,7 @@
 			if ln.Type != nil && !haspointers(ln.Type) {
 				break
 			}
-			if Curfn.Nbody == nil && !Curfn.Noescape {
+			if len(Curfn.Nbody.Slice()) == 0 && !Curfn.Noescape {
 				ln.Esc = EscHeap
 			} else {
 				ln.Esc = EscNone // prime for escflood later
@@ -509,8 +516,8 @@
 		}
 	}
 
-	escloopdepthlist(e, Curfn.Nbody)
-	esclist(e, Curfn.Nbody, Curfn)
+	escloopdepthslice(e, Curfn.Nbody.Slice())
+	escslice(e, Curfn.Nbody.Slice(), Curfn)
 	Curfn = savefn
 	e.loopdepth = saveld
 }
@@ -528,6 +535,12 @@
 	}
 }
 
+func escloopdepthslice(e *EscState, l []*Node) {
+	for _, n := range l {
+		escloopdepth(e, n)
+	}
+}
+
 func escloopdepth(e *EscState, n *Node) {
 	if n == nil {
 		return
@@ -562,7 +575,7 @@
 	escloopdepth(e, n.Left)
 	escloopdepth(e, n.Right)
 	escloopdepthlist(e, n.List)
-	escloopdepthlist(e, n.Nbody)
+	escloopdepthslice(e, n.Nbody.Slice())
 	escloopdepthlist(e, n.Rlist)
 }
 
@@ -572,6 +585,12 @@
 	}
 }
 
+func escslice(e *EscState, l []*Node, up *Node) {
+	for _, n := range l {
+		esc(e, n, up)
+	}
+}
+
 func esc(e *EscState, n *Node, up *Node) {
 	if n == nil {
 		return
@@ -622,7 +641,7 @@
 
 	esc(e, n.Left, n)
 	esc(e, n.Right, n)
-	esclist(e, n.Nbody, n)
+	escslice(e, n.Nbody.Slice(), n)
 	esclist(e, n.List, n)
 	esclist(e, n.Rlist, n)
 
@@ -1395,7 +1414,7 @@
 
 	nE := e.nodeEscState(n)
 	if fn != nil && fn.Op == ONAME && fn.Class == PFUNC &&
-		fn.Name.Defn != nil && fn.Name.Defn.Nbody != nil && fn.Name.Param.Ntype != nil && fn.Name.Defn.Esc < EscFuncTagged {
+		fn.Name.Defn != nil && len(fn.Name.Defn.Nbody.Slice()) != 0 && fn.Name.Param.Ntype != nil && fn.Name.Defn.Esc < EscFuncTagged {
 		if Debug['m'] > 2 {
 			fmt.Printf("%v::esccall:: %v in recursive group\n", Ctxt.Line(int(lineno)), Nconv(n, obj.FmtShort))
 		}
@@ -1833,7 +1852,7 @@
 
 	// External functions are assumed unsafe,
 	// unless //go:noescape is given before the declaration.
-	if func_.Nbody == nil {
+	if len(func_.Nbody.Slice()) == 0 {
 		if func_.Noescape {
 			for t := getinargx(func_.Type).Type; t != nil; t = t.Down {
 				if haspointers(t.Type) {
diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
index 1b61d7f..b36fe7b 100644
--- a/src/cmd/compile/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -112,6 +112,12 @@
 	}
 }
 
+func reexportdepslice(ll []*Node) {
+	for _, n := range ll {
+		reexportdep(n)
+	}
+}
+
 func reexportdep(n *Node) {
 	if n == nil {
 		return
@@ -217,7 +223,7 @@
 	reexportdeplist(n.List)
 	reexportdeplist(n.Rlist)
 	reexportdeplist(n.Ninit)
-	reexportdeplist(n.Nbody)
+	reexportdepslice(n.Nbody.Slice())
 }
 
 func dumpexportconst(s *Sym) {
@@ -249,7 +255,7 @@
 	dumpexporttype(t)
 
 	if t.Etype == TFUNC && n.Class == PFUNC {
-		if n.Func != nil && n.Func.Inl != nil {
+		if n.Func != nil && len(n.Func.Inl.Slice()) != 0 {
 			// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
 			// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
 			if Debug['l'] < 2 {
@@ -257,9 +263,9 @@
 			}
 
 			// NOTE: The space after %#S here is necessary for ld's export data parser.
-			exportf("\tfunc %v %v { %v }\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp), Hconv(n.Func.Inl, obj.FmtSharp|obj.FmtBody))
+			exportf("\tfunc %v %v { %v }\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp), Hconvslice(n.Func.Inl.Slice(), obj.FmtSharp|obj.FmtBody))
 
-			reexportdeplist(n.Func.Inl)
+			reexportdepslice(n.Func.Inl.Slice())
 		} else {
 			exportf("\tfunc %v %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp))
 		}
@@ -307,15 +313,15 @@
 		if f.Nointerface {
 			exportf("\t//go:nointerface\n")
 		}
-		if f.Type.Nname != nil && f.Type.Nname.Func.Inl != nil { // nname was set by caninl
+		if f.Type.Nname != nil && len(f.Type.Nname.Func.Inl.Slice()) != 0 { // nname was set by caninl
 
 			// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
 			// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
 			if Debug['l'] < 2 {
 				typecheckinl(f.Type.Nname)
 			}
-			exportf("\tfunc (%v) %v %v { %v }\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp), Hconv(f.Type.Nname.Func.Inl, obj.FmtSharp))
-			reexportdeplist(f.Type.Nname.Func.Inl)
+			exportf("\tfunc (%v) %v %v { %v }\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp), Hconvslice(f.Type.Nname.Func.Inl.Slice(), obj.FmtSharp))
+			reexportdepslice(f.Type.Nname.Func.Inl.Slice())
 		} else {
 			exportf("\tfunc (%v) %v %v\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp))
 		}
diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go
index 9327a13..39b74f6 100644
--- a/src/cmd/compile/internal/gc/fmt.go
+++ b/src/cmd/compile/internal/gc/fmt.go
@@ -1216,7 +1216,7 @@
 		if fmtmode == FErr {
 			return "func literal"
 		}
-		if n.Nbody != nil {
+		if len(n.Nbody.Slice()) != 0 {
 			return fmt.Sprintf("%v { %v }", n.Type, n.Nbody)
 		}
 		return fmt.Sprintf("%v { %v }", n.Type, n.Name.Param.Closure.Nbody)
@@ -1583,7 +1583,7 @@
 			fmt.Fprintf(&buf, "%v-rlist%v", Oconv(int(n.Op), 0), n.Rlist)
 		}
 
-		if n.Nbody != nil {
+		if len(n.Nbody.Slice()) != 0 {
 			indent(&buf)
 			fmt.Fprintf(&buf, "%v-body%v", Oconv(int(n.Op), 0), n.Nbody)
 		}
@@ -1699,6 +1699,10 @@
 	return Hconv(l, 0)
 }
 
+func (n Nodes) String() string {
+	return Hconvslice(n.Slice(), 0)
+}
+
 // Fmt '%H': NodeList.
 // Flags: all those of %N plus ',': separate with comma's instead of semicolons.
 func Hconv(l *NodeList, flag int) string {
diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go
index 4edef2b..2292a56 100644
--- a/src/cmd/compile/internal/gc/gen.go
+++ b/src/cmd/compile/internal/gc/gen.go
@@ -777,7 +777,7 @@
 		gen(n.Right)                     // contin:	incr
 		Patch(p1, Pc)                    // test:
 		Bgen(n.Left, false, -1, breakpc) //		if(!test) goto break
-		Genlist(n.Nbody)                 //		body
+		Genslice(n.Nbody.Slice())        //		body
 		gjmp(continpc)
 		Patch(breakpc, Pc) // done:
 		continpc = scontin
@@ -792,7 +792,7 @@
 		p2 := gjmp(nil)                         // p2:		goto else
 		Patch(p1, Pc)                           // test:
 		Bgen(n.Left, false, int(-n.Likely), p2) //		if(!test) goto p2
-		Genlist(n.Nbody)                        //		then
+		Genslice(n.Nbody.Slice())               //		then
 		p3 := gjmp(nil)                         //		goto done
 		Patch(p2, Pc)                           // else:
 		Genlist(n.Rlist)                        //		else
@@ -809,9 +809,9 @@
 			lab.Breakpc = breakpc
 		}
 
-		Patch(p1, Pc)      // test:
-		Genlist(n.Nbody)   //		switch(test) body
-		Patch(breakpc, Pc) // done:
+		Patch(p1, Pc)             // test:
+		Genslice(n.Nbody.Slice()) //		switch(test) body
+		Patch(breakpc, Pc)        // done:
 		breakpc = sbreak
 		if lab != nil {
 			lab.Breakpc = nil
@@ -828,9 +828,9 @@
 			lab.Breakpc = breakpc
 		}
 
-		Patch(p1, Pc)      // test:
-		Genlist(n.Nbody)   //		select() body
-		Patch(breakpc, Pc) // done:
+		Patch(p1, Pc)             // test:
+		Genslice(n.Nbody.Slice()) //		select() body
+		Patch(breakpc, Pc)        // done:
 		breakpc = sbreak
 		if lab != nil {
 			lab.Breakpc = nil
diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go
index 6071ab4..d7db786 100644
--- a/src/cmd/compile/internal/gc/init.go
+++ b/src/cmd/compile/internal/gc/init.go
@@ -46,15 +46,15 @@
 //		initdoneĀ· = 2;				(10)
 //		return					(11)
 //	}
-func anyinit(n *NodeList) bool {
+func anyinit(n []*Node) bool {
 	// are there any interesting init statements
-	for l := n; l != nil; l = l.Next {
-		switch l.N.Op {
+	for _, ln := range n {
+		switch ln.Op {
 		case ODCLFUNC, ODCLCONST, ODCLTYPE, OEMPTY:
 			break
 
 		case OAS, OASWB:
-			if isblank(l.N.Left) && candiscard(l.N.Right) {
+			if isblank(ln.Left) && candiscard(ln.Right) {
 				break
 			}
 			fallthrough
@@ -94,12 +94,12 @@
 		return
 	}
 
-	n = initfix(n)
-	if !anyinit(n) {
+	nf := initfix(n)
+	if !anyinit(nf) {
 		return
 	}
 
-	var r *NodeList
+	var r []*Node
 
 	// (1)
 	gatevar := newname(Lookup("initdoneĀ·"))
@@ -120,37 +120,37 @@
 	a := Nod(OIF, nil, nil)
 
 	a.Left = Nod(ONE, gatevar, Nodintconst(0))
-	r = list(r, a)
+	r = append(r, a)
 
 	// (4)
 	b := Nod(OIF, nil, nil)
 
 	b.Left = Nod(OEQ, gatevar, Nodintconst(2))
-	b.Nbody = list1(Nod(ORETURN, nil, nil))
-	a.Nbody = list1(b)
+	b.Nbody.Set([]*Node{Nod(ORETURN, nil, nil)})
+	a.Nbody.Set([]*Node{b})
 
 	// (5)
 	b = syslook("throwinit", 0)
 
 	b = Nod(OCALL, b, nil)
-	a.Nbody = list(a.Nbody, b)
+	a.Nbody.Append(b)
 
 	// (6)
 	a = Nod(OAS, gatevar, Nodintconst(1))
 
-	r = list(r, a)
+	r = append(r, a)
 
 	// (7)
 	for _, s := range initSyms {
 		if s.Def != nil && s != initsym {
 			// could check that it is fn of no args/returns
 			a = Nod(OCALL, s.Def, nil)
-			r = list(r, a)
+			r = append(r, a)
 		}
 	}
 
 	// (8)
-	r = concat(r, n)
+	r = append(r, nf...)
 
 	// (9)
 	// could check that it is fn of no args/returns
@@ -160,26 +160,26 @@
 			break
 		}
 		a = Nod(OCALL, s.Def, nil)
-		r = list(r, a)
+		r = append(r, a)
 	}
 
 	// (10)
 	a = Nod(OAS, gatevar, Nodintconst(2))
 
-	r = list(r, a)
+	r = append(r, a)
 
 	// (11)
 	a = Nod(ORETURN, nil, nil)
 
-	r = list(r, a)
+	r = append(r, a)
 	exportsym(fn.Func.Nname)
 
-	fn.Nbody = r
+	fn.Nbody.Set(r)
 	funcbody(fn)
 
 	Curfn = fn
 	typecheck(&fn, Etop)
-	typechecklist(r, Etop)
+	typecheckslice(r, Etop)
 	Curfn = nil
 	funccompile(fn)
 }
diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go
index f5c3265..8406565 100644
--- a/src/cmd/compile/internal/gc/inl.go
+++ b/src/cmd/compile/internal/gc/inl.go
@@ -79,7 +79,7 @@
 	}
 
 	if Debug['m'] > 2 {
-		fmt.Printf("typecheck import [%v] %v { %v }\n", fn.Sym, Nconv(fn, obj.FmtLong), Hconv(fn.Func.Inl, obj.FmtSharp))
+		fmt.Printf("typecheck import [%v] %v { %v }\n", fn.Sym, Nconv(fn, obj.FmtLong), Hconvslice(fn.Func.Inl.Slice(), obj.FmtSharp))
 	}
 
 	save_safemode := safemode
@@ -87,7 +87,7 @@
 
 	savefn := Curfn
 	Curfn = fn
-	typechecklist(fn.Func.Inl, Etop)
+	typecheckslice(fn.Func.Inl.Slice(), Etop)
 	Curfn = savefn
 
 	safemode = save_safemode
@@ -112,7 +112,7 @@
 	}
 
 	// If fn has no body (is defined outside of Go), cannot inline it.
-	if fn.Nbody == nil {
+	if len(fn.Nbody.Slice()) == 0 {
 		return
 	}
 
@@ -141,15 +141,15 @@
 
 	const maxBudget = 80
 	budget := maxBudget // allowed hairyness
-	if ishairylist(fn.Nbody, &budget) || budget < 0 {
+	if ishairyslice(fn.Nbody.Slice(), &budget) || budget < 0 {
 		return
 	}
 
 	savefn := Curfn
 	Curfn = fn
 
-	fn.Func.Nname.Func.Inl = fn.Nbody
-	fn.Nbody = inlcopylist(fn.Func.Nname.Func.Inl)
+	fn.Func.Nname.Func.Inl.Set(fn.Nbody.Slice())
+	fn.Nbody.Set(inlcopyslice(fn.Func.Nname.Func.Inl.Slice()))
 	inldcl := inlcopyslice(fn.Func.Nname.Name.Defn.Func.Dcl)
 	if len(inldcl) > 0 {
 		fn.Func.Nname.Func.Inldcl = &inldcl
@@ -161,7 +161,7 @@
 	fn.Type.Nname = fn.Func.Nname
 
 	if Debug['m'] > 1 {
-		fmt.Printf("%v: can inline %v as: %v { %v }\n", fn.Line(), Nconv(fn.Func.Nname, obj.FmtSharp), Tconv(fn.Type, obj.FmtSharp), Hconv(fn.Func.Nname.Func.Inl, obj.FmtSharp))
+		fmt.Printf("%v: can inline %v as: %v { %v }\n", fn.Line(), Nconv(fn.Func.Nname, obj.FmtSharp), Tconv(fn.Type, obj.FmtSharp), Hconvslice(fn.Func.Nname.Func.Inl.Slice(), obj.FmtSharp))
 	} else if Debug['m'] != 0 {
 		fmt.Printf("%v: can inline %v\n", fn.Line(), fn.Func.Nname)
 	}
@@ -179,6 +179,15 @@
 	return false
 }
 
+func ishairyslice(ll []*Node, budget *int) bool {
+	for _, n := range ll {
+		if ishairy(n, budget) {
+			return true
+		}
+	}
+	return false
+}
+
 func ishairy(n *Node, budget *int) bool {
 	if n == nil {
 		return false
@@ -187,12 +196,12 @@
 	switch n.Op {
 	// Call is okay if inlinable and we have the budget for the body.
 	case OCALLFUNC:
-		if n.Left.Func != nil && n.Left.Func.Inl != nil {
+		if n.Left.Func != nil && len(n.Left.Func.Inl.Slice()) != 0 {
 			*budget -= int(n.Left.Func.InlCost)
 			break
 		}
 		if n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME { // methods called as functions
-			if n.Left.Sym.Def != nil && n.Left.Sym.Def.Func.Inl != nil {
+			if n.Left.Sym.Def != nil && len(n.Left.Sym.Def.Func.Inl.Slice()) != 0 {
 				*budget -= int(n.Left.Sym.Def.Func.InlCost)
 				break
 			}
@@ -209,7 +218,7 @@
 		if n.Left.Type.Nname == nil {
 			Fatalf("no function definition for [%p] %v\n", n.Left.Type, Tconv(n.Left.Type, obj.FmtSign))
 		}
-		if n.Left.Type.Nname.Func.Inl != nil {
+		if len(n.Left.Type.Nname.Func.Inl.Slice()) != 0 {
 			*budget -= int(n.Left.Type.Nname.Func.InlCost)
 			break
 		}
@@ -239,7 +248,7 @@
 
 	(*budget)--
 
-	return *budget < 0 || ishairy(n.Left, budget) || ishairy(n.Right, budget) || ishairylist(n.List, budget) || ishairylist(n.Rlist, budget) || ishairylist(n.Ninit, budget) || ishairylist(n.Nbody, budget)
+	return *budget < 0 || ishairy(n.Left, budget) || ishairy(n.Right, budget) || ishairylist(n.List, budget) || ishairylist(n.Rlist, budget) || ishairylist(n.Ninit, budget) || ishairyslice(n.Nbody.Slice(), budget)
 }
 
 // Inlcopy and inlcopylist recursively copy the body of a function.
@@ -266,14 +275,14 @@
 	m := Nod(OXXX, nil, nil)
 	*m = *n
 	if m.Func != nil {
-		m.Func.Inl = nil
+		m.Func.Inl.Set(nil)
 	}
 	m.Left = inlcopy(n.Left)
 	m.Right = inlcopy(n.Right)
 	m.List = inlcopylist(n.List)
 	m.Rlist = inlcopylist(n.Rlist)
 	m.Ninit = inlcopylist(n.Ninit)
-	m.Nbody = inlcopylist(n.Nbody)
+	m.Nbody.Set(inlcopyslice(n.Nbody.Slice()))
 
 	return m
 }
@@ -307,9 +316,9 @@
 	n.Op = OBLOCK
 
 	// n->ninit stays
-	n.List = n.Nbody
+	n.List = n.Nbody.NodeList()
 
-	n.Nbody = nil
+	n.Nbody.Set(nil)
 	n.Rlist = nil
 }
 
@@ -317,7 +326,7 @@
 func inlconv2expr(np **Node) {
 	n := *np
 	r := n.Rlist.N
-	addinit(&r, concat(n.Ninit, n.Nbody))
+	addinit(&r, concat(n.Ninit, n.Nbody.NodeList()))
 	*np = r
 }
 
@@ -332,7 +341,7 @@
 	}
 
 	l := n.Rlist
-	addinit(&l.N, concat(n.Ninit, n.Nbody))
+	addinit(&l.N, concat(n.Ninit, n.Nbody.NodeList()))
 	return l
 }
 
@@ -342,6 +351,12 @@
 	}
 }
 
+func inlnodeslice(l []*Node) {
+	for i := range l {
+		inlnode(&l[i])
+	}
+}
+
 // inlnode recurses over the tree to find inlineable calls, which will
 // be turned into OINLCALLs by mkinlcall.  When the recursion comes
 // back up will examine left, right, list, rlist, ninit, ntest, nincr,
@@ -454,10 +469,10 @@
 		}
 	}
 
-	inlnodelist(n.Nbody)
-	for l := n.Nbody; l != nil; l = l.Next {
-		if l.N.Op == OINLCALL {
-			inlconv2stmt(l.N)
+	inlnodeslice(n.Nbody.Slice())
+	for _, n := range n.Nbody.Slice() {
+		if n.Op == OINLCALL {
+			inlconv2stmt(n)
 		}
 	}
 
@@ -477,7 +492,7 @@
 		if Debug['m'] > 3 {
 			fmt.Printf("%v:call to func %v\n", n.Line(), Nconv(n.Left, obj.FmtSign))
 		}
-		if n.Left.Func != nil && n.Left.Func.Inl != nil { // normal case
+		if n.Left.Func != nil && len(n.Left.Func.Inl.Slice()) != 0 { // normal case
 			mkinlcall(np, n.Left, n.Isddd)
 		} else if n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME { // methods called as functions
 			if n.Left.Sym.Def != nil {
@@ -539,7 +554,7 @@
 // parameters.
 func mkinlcall1(np **Node, fn *Node, isddd bool) {
 	// For variadic fn.
-	if fn.Func.Inl == nil {
+	if len(fn.Func.Inl.Slice()) == 0 {
 		return
 	}
 
@@ -555,7 +570,7 @@
 
 	// Bingo, we have a function node, and it has an inlineable body
 	if Debug['m'] > 1 {
-		fmt.Printf("%v: inlining call to %v %v { %v }\n", n.Line(), fn.Sym, Tconv(fn.Type, obj.FmtSharp), Hconv(fn.Func.Inl, obj.FmtSharp))
+		fmt.Printf("%v: inlining call to %v %v { %v }\n", n.Line(), fn.Sym, Tconv(fn.Type, obj.FmtSharp), Hconvslice(fn.Func.Inl.Slice(), obj.FmtSharp))
 	} else if Debug['m'] != 0 {
 		fmt.Printf("%v: inlining call to %v\n", n.Line(), fn)
 	}
@@ -796,19 +811,19 @@
 
 	inlretlabel = newlabel_inl()
 	inlgen++
-	body := inlsubstlist(fn.Func.Inl)
+	body := inlsubstslice(fn.Func.Inl.Slice())
 
-	body = list(body, Nod(OGOTO, inlretlabel, nil)) // avoid 'not used' when function doesn't have return
-	body = list(body, Nod(OLABEL, inlretlabel, nil))
+	body = append(body, Nod(OGOTO, inlretlabel, nil)) // avoid 'not used' when function doesn't have return
+	body = append(body, Nod(OLABEL, inlretlabel, nil))
 
-	typechecklist(body, Etop)
+	typecheckslice(body, Etop)
 
 	//dumplist("ninit post", ninit);
 
 	call := Nod(OINLCALL, nil, nil)
 
 	call.Ninit = ninit
-	call.Nbody = body
+	call.Nbody.Set(body)
 	call.Rlist = inlretvars
 	call.Type = n.Type
 	call.Typecheck = 1
@@ -834,15 +849,15 @@
 	// instead we emit the things that the body needs
 	// and each use must redo the inlining.
 	// luckily these are small.
-	body = fn.Func.Inl
-	fn.Func.Inl = nil // prevent infinite recursion (shouldn't happen anyway)
-	inlnodelist(call.Nbody)
-	for ll := call.Nbody; ll != nil; ll = ll.Next {
-		if ll.N.Op == OINLCALL {
-			inlconv2stmt(ll.N)
+	body = fn.Func.Inl.Slice()
+	fn.Func.Inl.Set(nil) // prevent infinite recursion (shouldn't happen anyway)
+	inlnodeslice(call.Nbody.Slice())
+	for _, n := range call.Nbody.Slice() {
+		if n.Op == OINLCALL {
+			inlconv2stmt(n)
 		}
 	}
-	fn.Func.Inl = body
+	fn.Func.Inl.Set(body)
 
 	if Debug['m'] > 2 {
 		fmt.Printf("%v: After inlining %v\n\n", n.Line(), Nconv(*np, obj.FmtSign))
@@ -907,8 +922,8 @@
 	return n
 }
 
-// inlsubst and inlsubstlist recursively copy the body of the saved
-// pristine ->inl body of the function while substituting references
+// inlsubst, inlsubstlist, and inlsubstslice recursively copy the body of the
+// saved pristine ->inl body of the function while substituting references
 // to input/output parameters with ones to the tmpnames, and
 // substituting returns with assignments to the output.
 func inlsubstlist(ll *NodeList) *NodeList {
@@ -919,6 +934,14 @@
 	return l
 }
 
+func inlsubstslice(ll []*Node) []*Node {
+	l := make([]*Node, 0, len(ll))
+	for _, n := range ll {
+		l = append(l, inlsubst(n))
+	}
+	return l
+}
+
 func inlsubst(n *Node) *Node {
 	if n == nil {
 		return nil
@@ -990,7 +1013,7 @@
 	m.List = inlsubstlist(n.List)
 	m.Rlist = inlsubstlist(n.Rlist)
 	m.Ninit = concat(m.Ninit, inlsubstlist(n.Ninit))
-	m.Nbody = inlsubstlist(n.Nbody)
+	m.Nbody.Set(inlsubstslice(n.Nbody.Slice()))
 
 	return m
 }
@@ -1002,6 +1025,12 @@
 	}
 }
 
+func setlnoslice(ll []*Node, lno int) {
+	for _, n := range ll {
+		setlno(n, lno)
+	}
+}
+
 func setlno(n *Node, lno int) {
 	if n == nil {
 		return
@@ -1017,5 +1046,5 @@
 	setlnolist(n.List, lno)
 	setlnolist(n.Rlist, lno)
 	setlnolist(n.Ninit, lno)
-	setlnolist(n.Nbody, lno)
+	setlnoslice(n.Nbody.Slice(), lno)
 }
diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
index 62cdded..995fd13 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/gc/lex.go
@@ -388,10 +388,10 @@
 			Curfn = l.N
 			decldepth = 1
 			saveerrors()
-			typechecklist(l.N.Nbody, Etop)
+			typecheckslice(l.N.Nbody.Slice(), Etop)
 			checkreturn(l.N)
 			if nerrors != 0 {
-				l.N.Nbody = nil // type errors; do not compile
+				l.N.Nbody.Set(nil) // type errors; do not compile
 			}
 		}
 	}
@@ -417,7 +417,7 @@
 		// Typecheck imported function bodies if debug['l'] > 1,
 		// otherwise lazily when used or re-exported.
 		for _, n := range importlist {
-			if n.Func.Inl != nil {
+			if len(n.Func.Inl.Slice()) != 0 {
 				saveerrors()
 				typecheckinl(n)
 			}
diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
index cc74ea5..e94ff21 100644
--- a/src/cmd/compile/internal/gc/order.go
+++ b/src/cmd/compile/internal/gc/order.go
@@ -41,8 +41,8 @@
 
 // Order holds state during the ordering process.
 type Order struct {
-	out  *NodeList // list of generated statements
-	temp []*Node   // stack of temporary variables
+	out  []*Node // list of generated statements
+	temp []*Node // stack of temporary variables
 }
 
 // Order rewrites fn->nbody to apply the ordering constraints
@@ -50,10 +50,10 @@
 func order(fn *Node) {
 	if Debug['W'] > 1 {
 		s := fmt.Sprintf("\nbefore order %v", fn.Func.Nname.Sym)
-		dumplist(s, fn.Nbody)
+		dumpslice(s, fn.Nbody.Slice())
 	}
 
-	orderblock(&fn.Nbody)
+	orderblockNodes(&fn.Nbody)
 }
 
 // Ordertemp allocates a new temporary with the given type,
@@ -64,7 +64,7 @@
 	if clear {
 		a := Nod(OAS, var_, nil)
 		typecheck(&a, Etop)
-		order.out = list(order.out, a)
+		order.out = append(order.out, a)
 	}
 
 	order.temp = append(order.temp, var_)
@@ -87,7 +87,7 @@
 	var_ := ordertemp(t, order, clear != 0)
 	a := Nod(OAS, var_, n)
 	typecheck(&a, Etop)
-	order.out = list(order.out, a)
+	order.out = append(order.out, a)
 	return var_
 }
 
@@ -223,7 +223,7 @@
 // Cleantempnopop emits to *out VARKILL instructions for each temporary
 // above the mark on the temporary stack, but it does not pop them
 // from the stack.
-func cleantempnopop(mark ordermarker, order *Order, out **NodeList) {
+func cleantempnopop(mark ordermarker, order *Order, out *[]*Node) {
 	var kill *Node
 
 	for i := len(order.temp) - 1; i >= int(mark); i-- {
@@ -232,11 +232,11 @@
 			n.Name.Keepalive = false
 			kill = Nod(OVARLIVE, n, nil)
 			typecheck(&kill, Etop)
-			*out = list(*out, kill)
+			*out = append(*out, kill)
 		}
 		kill = Nod(OVARKILL, n, nil)
 		typecheck(&kill, Etop)
-		*out = list(*out, kill)
+		*out = append(*out, kill)
 	}
 }
 
@@ -254,6 +254,13 @@
 	}
 }
 
+// Orderstmtslice orders each of the statements in the slice.
+func orderstmtslice(l []*Node, order *Order) {
+	for _, n := range l {
+		orderstmt(n, order)
+	}
+}
+
 // Orderblock orders the block of statements *l onto a new list,
 // and then replaces *l with that list.
 func orderblock(l **NodeList) {
@@ -261,7 +268,21 @@
 	mark := marktemp(&order)
 	orderstmtlist(*l, &order)
 	cleantemp(mark, &order)
-	*l = order.out
+	var ll *NodeList
+	for _, n := range order.out {
+		ll = list(ll, n)
+	}
+	*l = ll
+}
+
+// OrderblockNodes orders the block of statements in n into a new slice,
+// and then replaces the old slice in n with the new slice.
+func orderblockNodes(n *Nodes) {
+	var order Order
+	mark := marktemp(&order)
+	orderstmtslice(n.Slice(), &order)
+	cleantemp(mark, &order)
+	n.Set(order.out)
 }
 
 // Orderexprinplace orders the side effects in *np and
@@ -270,7 +291,7 @@
 	n := *np
 	var order Order
 	orderexpr(&n, &order, nil)
-	addinit(&n, order.out)
+	addinitslice(&n, order.out)
 
 	// insert new temporaries from order
 	// at head of outer list.
@@ -287,7 +308,7 @@
 	mark := marktemp(&order)
 	orderstmt(n, &order)
 	cleantemp(mark, &order)
-	*np = liststmt(order.out)
+	*np = liststmtslice(order.out)
 }
 
 // Orderinit moves n's init list to order->out.
@@ -413,7 +434,7 @@
 		Fatalf("ordermapassign %v", Oconv(int(n.Op), 0))
 
 	case OAS:
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 
 		// We call writebarrierfat only for values > 4 pointers long. See walk.go.
 		if (n.Left.Op == OINDEXMAP || (needwritebarrier(n.Left, n.Right) && n.Left.Type.Width > int64(4*Widthptr))) && !isaddrokay(n.Right) {
@@ -421,11 +442,11 @@
 			n.Left = ordertemp(m.Type, order, false)
 			a := Nod(OAS, m, n.Left)
 			typecheck(&a, Etop)
-			order.out = list(order.out, a)
+			order.out = append(order.out, a)
 		}
 
 	case OAS2, OAS2DOTTYPE, OAS2MAPR, OAS2FUNC:
-		var post *NodeList
+		var post []*Node
 		var m *Node
 		var a *Node
 		for l := n.List; l != nil; l = l.Next {
@@ -440,18 +461,18 @@
 				l.N = ordertemp(m.Type, order, false)
 				a = Nod(OAS, m, l.N)
 				typecheck(&a, Etop)
-				post = list(post, a)
+				post = append(post, a)
 			} else if instrumenting && n.Op == OAS2FUNC && !isblank(l.N) {
 				m = l.N
 				l.N = ordertemp(m.Type, order, false)
 				a = Nod(OAS, m, l.N)
 				typecheck(&a, Etop)
-				post = list(post, a)
+				post = append(post, a)
 			}
 		}
 
-		order.out = list(order.out, n)
-		order.out = concat(order.out, post)
+		order.out = append(order.out, n)
+		order.out = append(order.out, post...)
 	}
 }
 
@@ -472,7 +493,7 @@
 		Fatalf("orderstmt %v", Oconv(int(n.Op), 0))
 
 	case OVARKILL, OVARLIVE:
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 
 	case OAS:
 		t := marktemp(order)
@@ -497,7 +518,7 @@
 		case OAS2, OAS2DOTTYPE:
 			ordermapassign(n, order)
 		default:
-			order.out = list(order.out, n)
+			order.out = append(order.out, n)
 		}
 		cleantemp(t, order)
 
@@ -561,11 +582,11 @@
 		orderexprlist(n.List, order)
 		orderexpr(&n.Rlist.N.Left, order, nil) // i in i.(T)
 		if isblank(n.List.N) {
-			order.out = list(order.out, n)
+			order.out = append(order.out, n)
 		} else {
 			typ := n.Rlist.N.Type
 			tmp1 := ordertemp(typ, order, haspointers(typ))
-			order.out = list(order.out, n)
+			order.out = append(order.out, n)
 			r := Nod(OAS, n.List.N, tmp1)
 			typecheck(&r, Etop)
 			ordermapassign(r, order)
@@ -589,7 +610,7 @@
 		} else {
 			tmp2 = ordertemp(Types[TBOOL], order, false)
 		}
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 		r := Nod(OAS, n.List.N, tmp1)
 		typecheck(&r, Etop)
 		ordermapassign(r, order)
@@ -614,14 +635,14 @@
 		OGOTO,
 		OLABEL,
 		ORETJMP:
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 
 		// Special: handle call arguments.
 	case OCALLFUNC, OCALLINTER, OCALLMETH:
 		t := marktemp(order)
 
 		ordercall(n, order)
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 		cleantemp(t, order)
 
 		// Special: order arguments to inner call but not call itself.
@@ -644,7 +665,7 @@
 			ordercall(n.Left, order)
 		}
 
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 		cleantemp(t, order)
 
 	case ODELETE:
@@ -652,7 +673,7 @@
 		orderexpr(&n.List.N, order, nil)
 		orderexpr(&n.List.Next.N, order, nil)
 		orderaddrtemp(&n.List.Next.N, order) // map key
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 		cleantemp(t, order)
 
 		// Clean temporaries from condition evaluation at
@@ -661,12 +682,12 @@
 		t := marktemp(order)
 
 		orderexprinplace(&n.Left, order)
-		var l *NodeList
+		var l []*Node
 		cleantempnopop(t, order, &l)
-		n.Nbody = concat(l, n.Nbody)
-		orderblock(&n.Nbody)
+		n.Nbody.Set(append(l, n.Nbody.Slice()...))
+		orderblockNodes(&n.Nbody)
 		orderstmtinplace(&n.Right)
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 		cleantemp(t, order)
 
 		// Clean temporaries from condition at
@@ -675,16 +696,20 @@
 		t := marktemp(order)
 
 		orderexprinplace(&n.Left, order)
-		var l *NodeList
+		var l []*Node
 		cleantempnopop(t, order, &l)
-		n.Nbody = concat(l, n.Nbody)
+		n.Nbody.Set(append(l, n.Nbody.Slice()...))
 		l = nil
 		cleantempnopop(t, order, &l)
-		n.Rlist = concat(l, n.Rlist)
+		var ll *NodeList
+		for _, n := range l {
+			ll = list(ll, n)
+		}
+		n.Rlist = concat(ll, n.Rlist)
 		poptemp(t, order)
-		orderblock(&n.Nbody)
+		orderblockNodes(&n.Nbody)
 		orderblock(&n.Rlist)
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 
 		// Special: argument will be converted to interface using convT2E
 	// so make sure it is an addressable temporary.
@@ -695,7 +720,7 @@
 		if !Isinter(n.Left.Type) {
 			orderaddrtemp(&n.Left, order)
 		}
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 		cleantemp(t, order)
 
 		// n->right is the expression being ranged over.
@@ -756,13 +781,13 @@
 		for l := n.List; l != nil; l = l.Next {
 			orderexprinplace(&l.N, order)
 		}
-		orderblock(&n.Nbody)
-		order.out = list(order.out, n)
+		orderblockNodes(&n.Nbody)
+		order.out = append(order.out, n)
 		cleantemp(t, order)
 
 	case ORETURN:
 		ordercallargs(&n.List, order)
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 
 	// Special: clean case temporaries in each block entry.
 	// Select must enter one of its blocks, so there is no
@@ -897,19 +922,23 @@
 				}
 			}
 
-			orderblock(&l.N.Nbody)
+			orderblockNodes(&l.N.Nbody)
 		}
 
 		// Now that we have accumulated all the temporaries, clean them.
 		// Also insert any ninit queued during the previous loop.
 		// (The temporary cleaning must follow that ninit work.)
 		for l := n.List; l != nil; l = l.Next {
-			cleantempnopop(t, order, &l.N.Ninit)
-			l.N.Nbody = concat(l.N.Ninit, l.N.Nbody)
+			s := make([]*Node, 0, count(l.N.Ninit))
+			for ll := l.N.Ninit; ll != nil; ll = ll.Next {
+				s = append(s, ll.N)
+			}
+			cleantempnopop(t, order, &s)
+			l.N.Nbody.Set(append(s, l.N.Nbody.Slice()...))
 			l.N.Ninit = nil
 		}
 
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 		poptemp(t, order)
 
 		// Special: value being sent is passed as a pointer; make it addressable.
@@ -919,7 +948,7 @@
 		orderexpr(&n.Left, order, nil)
 		orderexpr(&n.Right, order, nil)
 		orderaddrtemp(&n.Right, order)
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 		cleantemp(t, order)
 
 		// TODO(rsc): Clean temporaries more aggressively.
@@ -938,10 +967,10 @@
 				Fatalf("order switch case %v", Oconv(int(l.N.Op), 0))
 			}
 			orderexprlistinplace(l.N.List, order)
-			orderblock(&l.N.Nbody)
+			orderblockNodes(&l.N.Nbody)
 		}
 
-		order.out = list(order.out, n)
+		order.out = append(order.out, n)
 		cleantemp(t, order)
 	}
 
@@ -1080,9 +1109,13 @@
 		// Clean temporaries from first branch at beginning of second.
 		// Leave them on the stack so that they can be killed in the outer
 		// context in case the short circuit is taken.
-		var l *NodeList
+		var s []*Node
 
-		cleantempnopop(mark, order, &l)
+		cleantempnopop(mark, order, &s)
+		var l *NodeList
+		for _, n := range s {
+			l = list(l, n)
+		}
 		n.Right.Ninit = concat(l, n.Right.Ninit)
 		orderexprinplace(&n.Right, order)
 
diff --git a/src/cmd/compile/internal/gc/parser.go b/src/cmd/compile/internal/gc/parser.go
index 621be57..c8dbcc5 100644
--- a/src/cmd/compile/internal/gc/parser.go
+++ b/src/cmd/compile/internal/gc/parser.go
@@ -858,7 +858,7 @@
 
 	stmt := p.case_(tswitch) // does markdcl
 	stmt.Xoffset = int64(block)
-	stmt.Nbody = p.stmt_list()
+	stmt.Nbody.SetToNodeList(p.stmt_list())
 
 	popdcl()
 
@@ -946,7 +946,7 @@
 	stmt := p.for_header()
 	body := p.loop_body("for clause")
 
-	stmt.Nbody = concat(stmt.Nbody, body)
+	stmt.Nbody.AppendNodeList(body)
 	return stmt
 }
 
@@ -1043,7 +1043,7 @@
 		Yyerror("missing condition in if statement")
 	}
 
-	stmt.Nbody = p.loop_body("if clause")
+	stmt.Nbody.SetToNodeList(p.loop_body("if clause"))
 
 	if p.got(LELSE) {
 		if p.tok == LIF {
@@ -1858,7 +1858,7 @@
 		return nil
 	}
 
-	f.Nbody = body
+	f.Nbody.SetToNodeList(body)
 	f.Noescape = p.pragma&Noescape != 0
 	if f.Noescape && body != nil {
 		Yyerror("can only use //go:noescape with external func implementations")
@@ -2079,7 +2079,7 @@
 			l = list(l, p.xfndcl())
 
 		default:
-			if p.tok == '{' && l != nil && l.End.N.Op == ODCLFUNC && l.End.N.Nbody == nil {
+			if p.tok == '{' && l != nil && l.End.N.Op == ODCLFUNC && len(l.End.N.Nbody.Slice()) == 0 {
 				// opening { of function declaration on next line
 				p.syntax_error("unexpected semicolon or newline before {")
 			} else {
@@ -2835,14 +2835,14 @@
 			return
 		}
 
-		s2.Func.Inl = s3
+		s2.Func.Inl.SetToNodeList(s3)
 
 		funcbody(s2)
 		importlist = append(importlist, s2)
 
 		if Debug['E'] > 0 {
 			fmt.Printf("import [%q] func %v \n", importpkg.Path, s2)
-			if Debug['m'] > 2 && s2.Func.Inl != nil {
+			if Debug['m'] > 2 && len(s2.Func.Inl.Slice()) != 0 {
 				fmt.Printf("inl body:%v\n", s2.Func.Inl)
 			}
 		}
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index 6c5fb2d..31cc3bc 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -358,7 +358,7 @@
 	var nam *Node
 	var gcargs *Sym
 	var gclocals *Sym
-	if fn.Nbody == nil {
+	if len(fn.Nbody.Slice()) == 0 {
 		if pure_go != 0 || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") {
 			Yyerror("missing function body for %q", fn.Func.Nname.Sym.Name)
 			goto ret
@@ -385,7 +385,7 @@
 			if t.Nname != nil {
 				n = Nod(OAS, t.Nname, nil)
 				typecheck(&n, Etop)
-				Curfn.Nbody = concat(list1(n), Curfn.Nbody)
+				Curfn.Nbody.Set(append([]*Node{n}, Curfn.Nbody.Slice()...))
 			}
 
 			t = structnext(&save)
@@ -472,7 +472,7 @@
 	}
 
 	Genslice(Curfn.Func.Enter.Slice())
-	Genlist(Curfn.Nbody)
+	Genslice(Curfn.Nbody.Slice())
 	gclean()
 	checklabels()
 	if nerrors != 0 {
diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go
index d1f6cef..352a399 100644
--- a/src/cmd/compile/internal/gc/racewalk.go
+++ b/src/cmd/compile/internal/gc/racewalk.go
@@ -55,7 +55,7 @@
 	}
 
 	if flag_race == 0 || !ispkgin(norace_inst_pkgs) {
-		instrumentlist(fn.Nbody, nil)
+		instrumentslice(fn.Nbody.Slice(), nil)
 
 		// nothing interesting for race detector in fn->enter
 		instrumentslice(fn.Func.Exit.Slice(), nil)
@@ -78,7 +78,7 @@
 
 	if Debug['W'] != 0 {
 		s := fmt.Sprintf("after instrument %v", fn.Func.Nname.Sym)
-		dumplist(s, fn.Nbody)
+		dumpslice(s, fn.Nbody.Slice())
 		s = fmt.Sprintf("enter %v", fn.Func.Nname.Sym)
 		dumpslice(s, fn.Func.Enter.Slice())
 		s = fmt.Sprintf("exit %v", fn.Func.Nname.Sym)
@@ -439,7 +439,7 @@
 	if n.Op != OBLOCK { // OBLOCK is handled above in a special way.
 		instrumentlist(n.List, init)
 	}
-	instrumentlist(n.Nbody, nil)
+	instrumentslice(n.Nbody.Slice(), nil)
 	instrumentlist(n.Rlist, nil)
 	*np = n
 }
@@ -612,12 +612,18 @@
 	}
 }
 
+func foreachslice(l []*Node, f func(*Node, interface{}), c interface{}) {
+	for _, n := range l {
+		foreachnode(n, f, c)
+	}
+}
+
 func foreach(n *Node, f func(*Node, interface{}), c interface{}) {
 	foreachlist(n.Ninit, f, c)
 	foreachnode(n.Left, f, c)
 	foreachnode(n.Right, f, c)
 	foreachlist(n.List, f, c)
-	foreachlist(n.Nbody, f, c)
+	foreachslice(n.Nbody.Slice(), f, c)
 	foreachlist(n.Rlist, f, c)
 }
 
diff --git a/src/cmd/compile/internal/gc/range.go b/src/cmd/compile/internal/gc/range.go
index 4386bcf..2270d71 100644
--- a/src/cmd/compile/internal/gc/range.go
+++ b/src/cmd/compile/internal/gc/range.go
@@ -128,7 +128,7 @@
 	}
 
 	decldepth++
-	typechecklist(n.Nbody, Etop)
+	typecheckslice(n.Nbody.Slice(), Etop)
 	decldepth--
 }
 
@@ -159,7 +159,7 @@
 	// to avoid erroneous processing by racewalk.
 	n.List = nil
 
-	var body *NodeList
+	var body []*Node
 	var init *NodeList
 	switch t.Etype {
 	default:
@@ -192,12 +192,12 @@
 		if v1 == nil {
 			body = nil
 		} else if v2 == nil {
-			body = list1(Nod(OAS, v1, hv1))
+			body = []*Node{Nod(OAS, v1, hv1)}
 		} else {
 			a := Nod(OAS2, nil, nil)
 			a.List = list(list1(v1), v2)
 			a.Rlist = list(list1(hv1), Nod(OIND, hp, nil))
-			body = list1(a)
+			body = []*Node{a}
 
 			// Advance pointer as part of increment.
 			// We used to advance the pointer before executing the loop body,
@@ -245,14 +245,14 @@
 		if v1 == nil {
 			body = nil
 		} else if v2 == nil {
-			body = list1(Nod(OAS, v1, key))
+			body = []*Node{Nod(OAS, v1, key)}
 		} else {
 			val := Nod(ODOT, hit, valname)
 			val = Nod(OIND, val, nil)
 			a := Nod(OAS2, nil, nil)
 			a.List = list(list1(v1), v2)
 			a.Rlist = list(list1(key), val)
-			body = list1(a)
+			body = []*Node{a}
 		}
 
 		// orderstmt arranged for a copy of the channel variable.
@@ -277,7 +277,7 @@
 		if v1 == nil {
 			body = nil
 		} else {
-			body = list1(Nod(OAS, v1, hv1))
+			body = []*Node{Nod(OAS, v1, hv1)}
 		}
 
 		// orderstmt arranged for a copy of the string variable.
@@ -306,10 +306,10 @@
 
 		body = nil
 		if v1 != nil {
-			body = list1(Nod(OAS, v1, ohv1))
+			body = []*Node{Nod(OAS, v1, ohv1)}
 		}
 		if v2 != nil {
-			body = list(body, Nod(OAS, v2, hv2))
+			body = append(body, Nod(OAS, v2, hv2))
 		}
 	}
 
@@ -319,8 +319,8 @@
 	typechecklist(n.Left.Ninit, Etop)
 	typecheck(&n.Left, Erv)
 	typecheck(&n.Right, Etop)
-	typechecklist(body, Etop)
-	n.Nbody = concat(body, n.Nbody)
+	typecheckslice(body, Etop)
+	n.Nbody.Set(append(body, n.Nbody.Slice()...))
 	walkstmt(&n)
 
 	lineno = int32(lno)
@@ -344,10 +344,10 @@
 	if v1 == nil || v2 != nil {
 		return false
 	}
-	if n.Nbody == nil || n.Nbody.N == nil || n.Nbody.Next != nil {
+	if len(n.Nbody.Slice()) == 0 || n.Nbody.Slice()[0] == nil || len(n.Nbody.Slice()) > 1 {
 		return false
 	}
-	stmt := n.Nbody.N // only stmt in body
+	stmt := n.Nbody.Slice()[0] // only stmt in body
 	if stmt.Op != OAS || stmt.Left.Op != OINDEX {
 		return false
 	}
@@ -368,7 +368,7 @@
 	// }
 	n.Op = OIF
 
-	n.Nbody = nil
+	n.Nbody.Set(nil)
 	n.Left = Nod(ONE, Nod(OLEN, a, nil), Nodintconst(0))
 
 	// hp = &a[0]
@@ -379,7 +379,7 @@
 	tmp = Nod(OADDR, tmp, nil)
 	tmp = Nod(OCONVNOP, tmp, nil)
 	tmp.Type = Ptrto(Types[TUINT8])
-	n.Nbody = list(n.Nbody, Nod(OAS, hp, tmp))
+	n.Nbody.Append(Nod(OAS, hp, tmp))
 
 	// hn = len(a) * sizeof(elem(a))
 	hn := temp(Types[TUINTPTR])
@@ -387,20 +387,20 @@
 	tmp = Nod(OLEN, a, nil)
 	tmp = Nod(OMUL, tmp, Nodintconst(elemsize))
 	tmp = conv(tmp, Types[TUINTPTR])
-	n.Nbody = list(n.Nbody, Nod(OAS, hn, tmp))
+	n.Nbody.Append(Nod(OAS, hn, tmp))
 
 	// memclr(hp, hn)
 	fn := mkcall("memclr", nil, nil, hp, hn)
 
-	n.Nbody = list(n.Nbody, fn)
+	n.Nbody.Append(fn)
 
 	// i = len(a) - 1
 	v1 = Nod(OAS, v1, Nod(OSUB, Nod(OLEN, a, nil), Nodintconst(1)))
 
-	n.Nbody = list(n.Nbody, v1)
+	n.Nbody.Append(v1)
 
 	typecheck(&n.Left, Erv)
-	typechecklist(n.Nbody, Etop)
+	typecheckslice(n.Nbody.Slice(), Etop)
 	walkstmt(&n)
 	return true
 }
diff --git a/src/cmd/compile/internal/gc/select.go b/src/cmd/compile/internal/gc/select.go
index e770c8f..0243947 100644
--- a/src/cmd/compile/internal/gc/select.go
+++ b/src/cmd/compile/internal/gc/select.go
@@ -79,7 +79,7 @@
 			}
 		}
 
-		typechecklist(ncase.Nbody, Etop)
+		typecheckslice(ncase.Nbody.Slice(), Etop)
 	}
 
 	sel.Xoffset = int64(count)
@@ -95,14 +95,14 @@
 	i := count(sel.List)
 
 	// optimization: zero-case select
-	var init *NodeList
+	var init []*Node
 	var r *Node
 	var n *Node
 	var var_ *Node
 	var selv *Node
 	var cas *Node
 	if i == 0 {
-		sel.Nbody = list1(mkcall("block", nil, nil))
+		sel.Nbody.Set([]*Node{mkcall("block", nil, nil)})
 		goto out
 	}
 
@@ -155,14 +155,18 @@
 			a := Nod(OIF, nil, nil)
 
 			a.Left = Nod(OEQ, ch, nodnil())
-			a.Nbody = list1(mkcall("block", nil, &l))
+			a.Nbody.Set([]*Node{mkcall("block", nil, &l)})
 			typecheck(&a, Etop)
 			l = list(l, a)
 			l = list(l, n)
 		}
 
-		l = concat(l, cas.Nbody)
-		sel.Nbody = l
+		s := make([]*Node, 0, count(l))
+		for ll := l; ll != nil; ll = ll.Next {
+			s = append(s, ll.N)
+		}
+		s = append(s, cas.Nbody.Slice()...)
+		sel.Nbody.Set(s)
 		goto out
 	}
 
@@ -242,13 +246,16 @@
 		}
 
 		typecheck(&r.Left, Erv)
-		r.Nbody = cas.Nbody
-		r.Rlist = concat(dflt.Ninit, dflt.Nbody)
-		sel.Nbody = list1(r)
+		r.Nbody.Set(cas.Nbody.Slice())
+		r.Rlist = concat(dflt.Ninit, dflt.Nbody.NodeList())
+		sel.Nbody.Set([]*Node{r})
 		goto out
 	}
 
-	init = sel.Ninit
+	init = make([]*Node, 0, count(sel.Ninit))
+	for ll := sel.Ninit; ll != nil; ll = ll.Next {
+		init = append(init, ll.N)
+	}
 	sel.Ninit = nil
 
 	// generate sel-struct
@@ -257,11 +264,11 @@
 	selv = temp(selecttype(int32(sel.Xoffset)))
 	r = Nod(OAS, selv, nil)
 	typecheck(&r, Etop)
-	init = list(init, r)
+	init = append(init, r)
 	var_ = conv(conv(Nod(OADDR, selv, nil), Types[TUNSAFEPTR]), Ptrto(Types[TUINT8]))
 	r = mkcall("newselect", nil, nil, var_, Nodintconst(selv.Type.Width), Nodintconst(sel.Xoffset))
 	typecheck(&r, Etop)
-	init = list(init, r)
+	init = append(init, r)
 
 	// register cases
 	for l := sel.List; l != nil; l = l.Next {
@@ -299,22 +306,22 @@
 		}
 
 		// selv is no longer alive after use.
-		r.Nbody = list(r.Nbody, Nod(OVARKILL, selv, nil))
+		r.Nbody.Append(Nod(OVARKILL, selv, nil))
 
-		r.Nbody = concat(r.Nbody, cas.Nbody)
-		r.Nbody = list(r.Nbody, Nod(OBREAK, nil, nil))
-		init = list(init, r)
+		r.Nbody.Append(cas.Nbody.Slice()...)
+		r.Nbody.Append(Nod(OBREAK, nil, nil))
+		init = append(init, r)
 	}
 
 	// run the select
 	setlineno(sel)
 
-	init = list(init, mkcall("selectgo", nil, nil, var_))
-	sel.Nbody = init
+	init = append(init, mkcall("selectgo", nil, nil, var_))
+	sel.Nbody.Set(init)
 
 out:
 	sel.List = nil
-	walkstmtlist(sel.Nbody)
+	walkstmtslice(sel.Nbody.Slice())
 	lineno = int32(lno)
 }
 
diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go
index f149c2c..c6647cc 100644
--- a/src/cmd/compile/internal/gc/sinit.go
+++ b/src/cmd/compile/internal/gc/sinit.go
@@ -24,7 +24,7 @@
 
 // init1 walks the AST starting at n, and accumulates in out
 // the list of definitions needing init code in dependency order.
-func init1(n *Node, out **NodeList) {
+func init1(n *Node, out *[]*Node) {
 	if n == nil {
 		return
 	}
@@ -98,7 +98,7 @@
 			Fatalf("init1: bad defn")
 
 		case ODCLFUNC:
-			init2list(defn.Nbody, out)
+			init2slice(defn.Nbody.Slice(), out)
 
 		case OAS:
 			if defn.Left != n {
@@ -120,7 +120,7 @@
 				if Debug['%'] != 0 {
 					Dump("nonstatic", defn)
 				}
-				*out = list(*out, defn)
+				*out = append(*out, defn)
 			}
 
 		case OAS2FUNC, OAS2MAPR, OAS2DOTTYPE, OAS2RECV:
@@ -134,7 +134,7 @@
 			if Debug['%'] != 0 {
 				Dump("nonstatic", defn)
 			}
-			*out = list(*out, defn)
+			*out = append(*out, defn)
 			defn.Initorder = InitDone
 		}
 	}
@@ -187,7 +187,7 @@
 }
 
 // recurse over n, doing init1 everywhere.
-func init2(n *Node, out **NodeList) {
+func init2(n *Node, out *[]*Node) {
 	if n == nil || n.Initorder == InitDone {
 		return
 	}
@@ -202,23 +202,29 @@
 	init2list(n.Ninit, out)
 	init2list(n.List, out)
 	init2list(n.Rlist, out)
-	init2list(n.Nbody, out)
+	init2slice(n.Nbody.Slice(), out)
 
 	if n.Op == OCLOSURE {
-		init2list(n.Func.Closure.Nbody, out)
+		init2slice(n.Func.Closure.Nbody.Slice(), out)
 	}
 	if n.Op == ODOTMETH || n.Op == OCALLPART {
 		init2(n.Type.Nname, out)
 	}
 }
 
-func init2list(l *NodeList, out **NodeList) {
+func init2list(l *NodeList, out *[]*Node) {
 	for ; l != nil; l = l.Next {
 		init2(l.N, out)
 	}
 }
 
-func initreorder(l *NodeList, out **NodeList) {
+func init2slice(l []*Node, out *[]*Node) {
+	for _, n := range l {
+		init2(n, out)
+	}
+}
+
+func initreorder(l *NodeList, out *[]*Node) {
 	var n *Node
 
 	for ; l != nil; l = l.Next {
@@ -237,8 +243,8 @@
 // initfix computes initialization order for a list l of top-level
 // declarations and outputs the corresponding list of statements
 // to include in the init() function body.
-func initfix(l *NodeList) *NodeList {
-	var lout *NodeList
+func initfix(l *NodeList) []*Node {
+	var lout []*Node
 	initplans = make(map[*Node]*InitPlan)
 	lno := int(lineno)
 	initreorder(l, &lout)
@@ -249,7 +255,7 @@
 
 // compilation of top-level (static) assignments
 // into DATA statements if at all possible.
-func staticinit(n *Node, out **NodeList) bool {
+func staticinit(n *Node, out *[]*Node) bool {
 	if n.Op != ONAME || n.Class != PEXTERN || n.Name.Defn == nil || n.Name.Defn.Op != OAS {
 		Fatalf("staticinit")
 	}
@@ -262,7 +268,7 @@
 
 // like staticassign but we are copying an already
 // initialized value r.
-func staticcopy(l *Node, r *Node, out **NodeList) bool {
+func staticcopy(l *Node, r *Node, out *[]*Node) bool {
 	if r.Op != ONAME {
 		return false
 	}
@@ -291,7 +297,7 @@
 		if staticcopy(l, r, out) {
 			return true
 		}
-		*out = list(*out, Nod(OAS, l, r))
+		*out = append(*out, Nod(OAS, l, r))
 		return true
 
 	case OLITERAL:
@@ -362,7 +368,7 @@
 					rr.Type = ll.Type
 					rr.Xoffset += e.Xoffset
 					setlineno(rr)
-					*out = list(*out, Nod(OAS, ll, rr))
+					*out = append(*out, Nod(OAS, ll, rr))
 				}
 			}
 		}
@@ -373,7 +379,7 @@
 	return false
 }
 
-func staticassign(l *Node, r *Node, out **NodeList) bool {
+func staticassign(l *Node, r *Node, out *[]*Node) bool {
 	for r.Op == OCONVNOP {
 		r = r.Left
 	}
@@ -410,7 +416,7 @@
 
 			// Init underlying literal.
 			if !staticassign(a, r.Left, out) {
-				*out = list(*out, Nod(OAS, a, r.Left))
+				*out = append(*out, Nod(OAS, a, r.Left))
 			}
 			return true
 		}
@@ -463,7 +469,7 @@
 				*a = n
 				a.Orig = a // completely separate copy
 				if !staticassign(a, e.Expr, out) {
-					*out = list(*out, Nod(OAS, a, e.Expr))
+					*out = append(*out, Nod(OAS, a, e.Expr))
 				}
 			}
 		}
@@ -967,7 +973,7 @@
 		r = Nod(OAS, r, a)
 
 		a = Nod(OFOR, nil, nil)
-		a.Nbody = list1(r)
+		a.Nbody.Set([]*Node{r})
 
 		a.Ninit = list1(Nod(OAS, index, Nodintconst(0)))
 		a.Left = Nod(OLT, index, Nodintconst(t.Bound))
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 6c55820..204962c 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -2170,8 +2170,8 @@
 		l = list(l, nodlit(v)) // method name
 		call := Nod(OCALL, syslook("panicwrap", 0), nil)
 		call.List = l
-		n.Nbody = list1(call)
-		fn.Nbody = list(fn.Nbody, n)
+		n.Nbody.Set([]*Node{call})
+		fn.Nbody.Append(n)
 	}
 
 	dot := adddot(Nod(OXDOT, this.Left, newname(method.Sym)))
@@ -2185,10 +2185,10 @@
 		}
 		as := Nod(OAS, this.Left, Nod(OCONVNOP, dot, nil))
 		as.Right.Type = rcvr
-		fn.Nbody = list(fn.Nbody, as)
+		fn.Nbody.Append(as)
 		n := Nod(ORETJMP, nil, nil)
 		n.Left = newname(methodsym(method.Sym, methodrcvr, 0))
-		fn.Nbody = list(fn.Nbody, n)
+		fn.Nbody.Append(n)
 	} else {
 		fn.Func.Wrapper = true // ignore frame for panic+recover matching
 		call := Nod(OCALL, dot, nil)
@@ -2200,11 +2200,11 @@
 			call = n
 		}
 
-		fn.Nbody = list(fn.Nbody, call)
+		fn.Nbody.Append(call)
 	}
 
 	if false && Debug['r'] != 0 {
-		dumplist("genwrapper body", fn.Nbody)
+		dumpslice("genwrapper body", fn.Nbody.Slice())
 	}
 
 	funcbody(fn)
@@ -2215,7 +2215,7 @@
 		fn.Func.Dupok = true
 	}
 	typecheck(&fn, Etop)
-	typechecklist(fn.Nbody, Etop)
+	typecheckslice(fn.Nbody.Slice(), Etop)
 
 	inlcalls(fn)
 	escAnalyze([]*Node{fn}, false)
@@ -2394,6 +2394,14 @@
 	return n
 }
 
+func liststmtslice(l []*Node) *Node {
+	var ll *NodeList
+	for _, n := range l {
+		ll = list(ll, n)
+	}
+	return liststmt(ll)
+}
+
 // return nelem of list
 func structcount(t *Type) int {
 	var s Iter
@@ -2740,6 +2748,14 @@
 	n.Ullman = UINF
 }
 
+func addinitslice(np **Node, init []*Node) {
+	var l *NodeList
+	for _, n := range init {
+		l = list(l, n)
+	}
+	addinit(np, l)
+}
+
 var reservedimports = []string{
 	"go",
 	"type",
diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
index 661b3ee..6e74349 100644
--- a/src/cmd/compile/internal/gc/swt.go
+++ b/src/cmd/compile/internal/gc/swt.go
@@ -181,7 +181,7 @@
 			}
 		}
 
-		typechecklist(ncase.Nbody, Etop)
+		typecheckslice(ncase.Nbody.Slice(), Etop)
 	}
 
 	lineno = int32(lno)
@@ -230,7 +230,7 @@
 	}
 
 	// convert the switch into OIF statements
-	var cas *NodeList
+	var cas []*Node
 	if s.kind == switchKindTrue || s.kind == switchKindFalse {
 		s.exprname = Nodbool(s.kind == switchKindTrue)
 	} else if consttype(cond) >= 0 {
@@ -238,8 +238,8 @@
 		s.exprname = cond
 	} else {
 		s.exprname = temp(cond.Type)
-		cas = list1(Nod(OAS, s.exprname, cond))
-		typechecklist(cas, Etop)
+		cas = []*Node{Nod(OAS, s.exprname, cond)}
+		typecheckslice(cas, Etop)
 	}
 
 	// enumerate the cases, and lop off the default case
@@ -258,7 +258,7 @@
 		// deal with expressions one at a time
 		if !okforcmp[t.Etype] || cc[0].typ != caseKindExprConst {
 			a := s.walkCases(cc[:1])
-			cas = list(cas, a)
+			cas = append(cas, a)
 			cc = cc[1:]
 			continue
 		}
@@ -271,15 +271,15 @@
 		// sort and compile constants
 		sort.Sort(caseClauseByExpr(cc[:run]))
 		a := s.walkCases(cc[:run])
-		cas = list(cas, a)
+		cas = append(cas, a)
 		cc = cc[run:]
 	}
 
 	// handle default case
 	if nerrors == 0 {
-		cas = list(cas, def)
-		sw.Nbody = concat(cas, sw.Nbody)
-		walkstmtlist(sw.Nbody)
+		cas = append(cas, def)
+		sw.Nbody.Set(append(cas, sw.Nbody.Slice()...))
+		walkstmtslice(sw.Nbody.Slice())
 	}
 }
 
@@ -303,7 +303,7 @@
 				a.Left = Nod(ONOT, n.Left, nil) // if !val
 				typecheck(&a.Left, Erv)
 			}
-			a.Nbody = list1(n.Right) // goto l
+			a.Nbody.Set([]*Node{n.Right}) // goto l
 
 			cas = list(cas, a)
 			lineno = int32(lno)
@@ -325,7 +325,7 @@
 		a.Left = le
 	}
 	typecheck(&a.Left, Erv)
-	a.Nbody = list1(s.walkCases(cc[:half]))
+	a.Nbody.Set([]*Node{s.walkCases(cc[:half])})
 	a.Rlist = list1(s.walkCases(cc[half:]))
 	return a
 }
@@ -340,9 +340,9 @@
 
 	lno := setlineno(sw)
 
-	var cas *NodeList  // cases
-	var stat *NodeList // statements
-	var def *Node      // defaults
+	var cas *NodeList // cases
+	var stat []*Node  // statements
+	var def *Node     // defaults
 	br := Nod(OBREAK, nil, nil)
 
 	for l := sw.List; l != nil; l = l.Next {
@@ -377,17 +377,19 @@
 			}
 		}
 
-		stat = list(stat, Nod(OLABEL, jmp.Left, nil))
+		stat = append(stat, Nod(OLABEL, jmp.Left, nil))
 		if typeswvar != nil && needvar && n.Rlist != nil {
-			l := list1(Nod(ODCL, n.Rlist.N, nil))
-			l = list(l, Nod(OAS, n.Rlist.N, typeswvar))
-			typechecklist(l, Etop)
-			stat = concat(stat, l)
+			l := []*Node{
+				Nod(ODCL, n.Rlist.N, nil),
+				Nod(OAS, n.Rlist.N, typeswvar),
+			}
+			typecheckslice(l, Etop)
+			stat = append(stat, l...)
 		}
-		stat = concat(stat, n.Nbody)
+		stat = append(stat, n.Nbody.Slice()...)
 
 		// botch - shouldn't fall thru declaration
-		last := stat.End.N
+		last := stat[len(stat)-1]
 		if last.Xoffset == n.Xoffset && last.Op == OXFALL {
 			if typeswvar != nil {
 				setlineno(last)
@@ -401,17 +403,17 @@
 
 			last.Op = OFALL
 		} else {
-			stat = list(stat, br)
+			stat = append(stat, br)
 		}
 	}
 
-	stat = list(stat, br)
+	stat = append(stat, br)
 	if def != nil {
 		cas = list(cas, def)
 	}
 
 	sw.List = cas
-	sw.Nbody = stat
+	sw.Nbody.Set(stat)
 	lineno = lno
 }
 
@@ -531,14 +533,14 @@
 		return
 	}
 
-	var cas *NodeList
+	var cas []*Node
 
 	// predeclare temporary variables and the boolean var
 	s.facename = temp(cond.Right.Type)
 
 	a := Nod(OAS, s.facename, cond.Right)
 	typecheck(&a, Etop)
-	cas = list(cas, a)
+	cas = append(cas, a)
 
 	s.okname = temp(Types[TBOOL])
 	typecheck(&s.okname, Erv)
@@ -579,18 +581,18 @@
 	i.Left = Nod(OEQ, typ, nodnil())
 	if typenil != nil {
 		// Do explicit nil case right here.
-		i.Nbody = list1(typenil)
+		i.Nbody.Set([]*Node{typenil})
 	} else {
 		// Jump to default case.
 		lbl := newCaseLabel()
-		i.Nbody = list1(Nod(OGOTO, lbl, nil))
+		i.Nbody.Set([]*Node{Nod(OGOTO, lbl, nil)})
 		// Wrap default case with label.
 		blk := Nod(OBLOCK, nil, nil)
 		blk.List = list(list1(Nod(OLABEL, lbl, nil)), def)
 		def = blk
 	}
 	typecheck(&i.Left, Erv)
-	cas = list(cas, i)
+	cas = append(cas, i)
 
 	if !isnilinter(cond.Right.Type) {
 		// Load type from itab.
@@ -608,7 +610,7 @@
 	h.Bounded = true                // guaranteed not to fault
 	a = Nod(OAS, s.hashname, h)
 	typecheck(&a, Etop)
-	cas = list(cas, a)
+	cas = append(cas, a)
 
 	// insert type equality check into each case block
 	for _, c := range cc {
@@ -625,7 +627,7 @@
 	for len(cc) > 0 {
 		if cc[0].typ != caseKindTypeConst {
 			n := cc[0].node
-			cas = list(cas, n.Right)
+			cas = append(cas, n.Right)
 			cc = cc[1:]
 			continue
 		}
@@ -642,7 +644,7 @@
 		if false {
 			for i := 0; i < run; i++ {
 				n := cc[i].node
-				cas = list(cas, n.Right)
+				cas = append(cas, n.Right)
 			}
 			continue
 		}
@@ -659,16 +661,16 @@
 		}
 
 		// binary search among cases to narrow by hash
-		cas = list(cas, s.walkCases(cc[:ncase]))
+		cas = append(cas, s.walkCases(cc[:ncase]))
 		cc = cc[ncase:]
 	}
 
 	// handle default case
 	if nerrors == 0 {
-		cas = list(cas, def)
-		sw.Nbody = concat(cas, sw.Nbody)
+		cas = append(cas, def)
+		sw.Nbody.Set(append(cas, sw.Nbody.Slice()...))
 		sw.List = nil
-		walkstmtlist(sw.Nbody)
+		walkstmtslice(sw.Nbody.Slice())
 	}
 }
 
@@ -698,7 +700,7 @@
 
 	c := Nod(OIF, nil, nil)
 	c.Left = s.okname
-	c.Nbody = list1(t.Right) // if ok { goto l }
+	c.Nbody.Set([]*Node{t.Right}) // if ok { goto l }
 
 	return liststmt(list(init, c))
 }
@@ -715,7 +717,7 @@
 			a := Nod(OIF, nil, nil)
 			a.Left = Nod(OEQ, s.hashname, Nodintconst(int64(c.hash)))
 			typecheck(&a.Left, Erv)
-			a.Nbody = list1(n.Right)
+			a.Nbody.Set([]*Node{n.Right})
 			cas = list(cas, a)
 		}
 		return liststmt(cas)
@@ -726,7 +728,7 @@
 	a := Nod(OIF, nil, nil)
 	a.Left = Nod(OLE, s.hashname, Nodintconst(int64(cc[half-1].hash)))
 	typecheck(&a.Left, Erv)
-	a.Nbody = list1(s.walkCases(cc[:half]))
+	a.Nbody.Set([]*Node{s.walkCases(cc[:half])})
 	a.Rlist = list1(s.walkCases(cc[half:]))
 	return a
 }
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index 7c34862..72944a7 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -16,7 +16,7 @@
 	Left  *Node
 	Right *Node
 	Ninit *NodeList
-	Nbody *NodeList
+	Nbody Nodes
 	List  *NodeList
 	Rlist *NodeList
 
@@ -164,7 +164,7 @@
 	FCurfn     *Node
 	Nname      *Node
 
-	Inl     *NodeList // copy of the body for use in inlining
+	Inl     Nodes // copy of the body for use in inlining
 	InlCost int32
 	Depth   int32
 
@@ -503,7 +503,7 @@
 
 // NodeList returns the entries in Nodes as a NodeList.
 // Changes to the NodeList entries (as in l.N = n) will *not* be
-// reflect in the Nodes.
+// reflected in the Nodes.
 // This wastes memory and should be used as little as possible.
 func (n *Nodes) NodeList() *NodeList {
 	if n.slice == nil {
@@ -537,3 +537,23 @@
 		*n.slice = append(*n.slice, a...)
 	}
 }
+
+// SetToNodeList sets Nodes to the contents of a NodeList.
+func (n *Nodes) SetToNodeList(l *NodeList) {
+	s := make([]*Node, 0, count(l))
+	for ; l != nil; l = l.Next {
+		s = append(s, l.N)
+	}
+	n.Set(s)
+}
+
+// AppendNodeList appends the contents of a NodeList.
+func (n *Nodes) AppendNodeList(l *NodeList) {
+	if n.slice == nil {
+		n.SetToNodeList(l)
+	} else {
+		for ; l != nil; l = l.Next {
+			*n.slice = append(*n.slice, l.N)
+		}
+	}
+}
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index 0445551..3288599 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -217,7 +217,7 @@
 		return true
 	}
 
-	return callrecv(n.Left) || callrecv(n.Right) || callrecvlist(n.Ninit) || callrecvlist(n.Nbody) || callrecvlist(n.List) || callrecvlist(n.Rlist)
+	return callrecv(n.Left) || callrecv(n.Right) || callrecvlist(n.Ninit) || callrecvslice(n.Nbody.Slice()) || callrecvlist(n.List) || callrecvlist(n.Rlist)
 }
 
 func callrecvlist(l *NodeList) bool {
@@ -229,6 +229,15 @@
 	return false
 }
 
+func callrecvslice(l []*Node) bool {
+	for _, n := range l {
+		if callrecv(n) {
+			return true
+		}
+	}
+	return false
+}
+
 // indexlit implements typechecking of untyped values as
 // array/slice indexes. It is equivalent to defaultlit
 // except for constants of numerical kind, which are acceptable
@@ -2064,7 +2073,7 @@
 			}
 		}
 		typecheck(&n.Right, Etop)
-		typechecklist(n.Nbody, Etop)
+		typecheckslice(n.Nbody.Slice(), Etop)
 		decldepth--
 		break OpSwitch
 
@@ -2078,7 +2087,7 @@
 				Yyerror("non-bool %v used as if condition", Nconv(n.Left, obj.FmtLong))
 			}
 		}
-		typechecklist(n.Nbody, Etop)
+		typecheckslice(n.Nbody.Slice(), Etop)
 		typechecklist(n.Rlist, Etop)
 		break OpSwitch
 
@@ -2128,7 +2137,7 @@
 	case OXCASE:
 		ok |= Etop
 		typechecklist(n.List, Erv)
-		typechecklist(n.Nbody, Etop)
+		typecheckslice(n.Nbody.Slice(), Etop)
 		break OpSwitch
 
 	case ODCLFUNC:
@@ -3871,7 +3880,7 @@
 
 		markbreak(n.Right, implicit)
 		markbreaklist(n.Ninit, implicit)
-		markbreaklist(n.Nbody, implicit)
+		markbreakslice(n.Nbody.Slice(), implicit)
 		markbreaklist(n.List, implicit)
 		markbreaklist(n.Rlist, implicit)
 	}
@@ -3904,26 +3913,51 @@
 	}
 }
 
-func isterminating(l *NodeList, top int) bool {
+func markbreakslice(l []*Node, implicit *Node) {
+	for i := 0; i < len(l); i++ {
+		n := l[i]
+		if n.Op == OLABEL && i+1 < len(l) && n.Name.Defn == l[i+1] {
+			switch n.Name.Defn.Op {
+			case OFOR, OSWITCH, OTYPESW, OSELECT, ORANGE:
+				lab := new(Label)
+				lab.Def = n.Name.Defn
+				n.Left.Sym.Label = lab
+				markbreak(n.Name.Defn, n.Name.Defn)
+				n.Left.Sym.Label = nil
+				i++
+				continue
+			}
+		}
+
+		markbreak(n, implicit)
+	}
+}
+
+// Isterminating returns whether the NodeList l ends with a
+// terminating statement.
+func (l *NodeList) isterminating() bool {
 	if l == nil {
 		return false
 	}
-	if top != 0 {
-		for l.Next != nil && l.N.Op != OLABEL {
-			l = l.Next
-		}
-		markbreaklist(l, nil)
-	}
-
 	for l.Next != nil {
 		l = l.Next
 	}
-	n := l.N
+	return l.N.isterminating()
+}
 
-	if n == nil {
+// Isterminating whether the Nodes list ends with a terminating
+// statement.
+func (l Nodes) isterminating() bool {
+	c := len(l.Slice())
+	if c == 0 {
 		return false
 	}
+	return l.Slice()[c-1].isterminating()
+}
 
+// Isterminating returns whether the node n, the last one in a
+// statement list, is a terminating statement.
+func (n *Node) isterminating() bool {
 	switch n.Op {
 	// NOTE: OLABEL is treated as a separate statement,
 	// not a separate prefix, so skipping to the last statement
@@ -3931,7 +3965,7 @@
 	// skipping over the label. No case OLABEL here.
 
 	case OBLOCK:
-		return isterminating(n.List, 0)
+		return n.List.isterminating()
 
 	case OGOTO,
 		ORETURN,
@@ -3950,15 +3984,15 @@
 		return true
 
 	case OIF:
-		return isterminating(n.Nbody, 0) && isterminating(n.Rlist, 0)
+		return n.Nbody.isterminating() && n.Rlist.isterminating()
 
 	case OSWITCH, OTYPESW, OSELECT:
 		if n.Hasbreak {
 			return false
 		}
 		def := 0
-		for l = n.List; l != nil; l = l.Next {
-			if !isterminating(l.N.Nbody, 0) {
+		for l := n.List; l != nil; l = l.Next {
+			if !l.N.Nbody.isterminating() {
 				return false
 			}
 			if l.N.List == nil { // default
@@ -3976,8 +4010,9 @@
 }
 
 func checkreturn(fn *Node) {
-	if fn.Type.Outtuple != 0 && fn.Nbody != nil {
-		if !isterminating(fn.Nbody, 1) {
+	if fn.Type.Outtuple != 0 && len(fn.Nbody.Slice()) != 0 {
+		markbreakslice(fn.Nbody.Slice(), nil)
+		if !fn.Nbody.isterminating() {
 			yyerrorl(int(fn.Func.Endlineno), "missing return at end of function")
 		}
 	}
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 5e1db64..d0f942d 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -22,7 +22,7 @@
 
 	if Debug['W'] != 0 {
 		s := fmt.Sprintf("\nbefore %v", Curfn.Func.Nname.Sym)
-		dumplist(s, Curfn.Nbody)
+		dumpslice(s, Curfn.Nbody.Slice())
 	}
 
 	lno := int(lineno)
@@ -64,10 +64,10 @@
 	if nerrors != 0 {
 		return
 	}
-	walkstmtlist(Curfn.Nbody)
+	walkstmtslice(Curfn.Nbody.Slice())
 	if Debug['W'] != 0 {
 		s := fmt.Sprintf("after walk %v", Curfn.Func.Nname.Sym)
-		dumplist(s, Curfn.Nbody)
+		dumpslice(s, Curfn.Nbody.Slice())
 	}
 
 	heapmoves()
@@ -264,11 +264,11 @@
 		}
 
 		walkstmt(&n.Right)
-		walkstmtlist(n.Nbody)
+		walkstmtslice(n.Nbody.Slice())
 
 	case OIF:
 		walkexpr(&n.Left, &n.Ninit)
-		walkstmtlist(n.Nbody)
+		walkstmtslice(n.Nbody.Slice())
 		walkstmtlist(n.Rlist)
 
 	case OPROC:
@@ -1009,7 +1009,7 @@
 
 				n2 := Nod(OIF, nil, nil)
 				n2.Left = Nod(OEQ, l, nodnil())
-				n2.Nbody = list1(Nod(OAS, l, n1))
+				n2.Nbody.Set([]*Node{Nod(OAS, l, n1)})
 				n2.Likely = -1
 				typecheck(&n2, Etop)
 				*init = list(*init, n2)
@@ -2814,7 +2814,7 @@
 	substArgTypes(fn, s.Type.Type, s.Type.Type)
 
 	// s = growslice_n(T, s, n)
-	nif.Nbody = list1(Nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(s.Type), s, nt)))
+	nif.Nbody.Set([]*Node{Nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(s.Type), s, nt))})
 
 	l = list(l, nif)
 
@@ -2944,7 +2944,7 @@
 	fn := syslook("growslice", 1) //   growslice(<type>, old []T, mincap int) (ret []T)
 	substArgTypes(fn, ns.Type.Type, ns.Type.Type)
 
-	nx.Nbody = list1(Nod(OAS, ns, mkcall1(fn, ns.Type, &nx.Ninit, typename(ns.Type), ns, Nod(OADD, Nod(OLEN, ns, nil), na))))
+	nx.Nbody.Set([]*Node{Nod(OAS, ns, mkcall1(fn, ns.Type, &nx.Ninit, typename(ns.Type), ns, Nod(OADD, Nod(OLEN, ns, nil), na)))})
 
 	l = list(l, nx)
 
@@ -3018,7 +3018,7 @@
 	nif := Nod(OIF, nil, nil)
 
 	nif.Left = Nod(OGT, nlen, Nod(OLEN, nr, nil))
-	nif.Nbody = list(nif.Nbody, Nod(OAS, nlen, Nod(OLEN, nr, nil)))
+	nif.Nbody.Append(Nod(OAS, nlen, Nod(OLEN, nr, nil)))
 	l = list(l, nif)
 
 	// Call memmove.
@@ -3804,6 +3804,15 @@
 	return true
 }
 
+func candiscardslice(l []*Node) bool {
+	for _, n := range l {
+		if !candiscard(n) {
+			return false
+		}
+	}
+	return true
+}
+
 func candiscard(n *Node) bool {
 	if n == nil {
 		return true
@@ -3890,7 +3899,7 @@
 		return false
 	}
 
-	if !candiscard(n.Left) || !candiscard(n.Right) || !candiscardlist(n.Ninit) || !candiscardlist(n.Nbody) || !candiscardlist(n.List) || !candiscardlist(n.Rlist) {
+	if !candiscard(n.Left) || !candiscard(n.Right) || !candiscardlist(n.Ninit) || !candiscardslice(n.Nbody.Slice()) || !candiscardlist(n.List) || !candiscardlist(n.Rlist) {
 		return false
 	}
 
@@ -3946,12 +3955,12 @@
 	typecheck(&a, Etop)
 	walkstmt(&a)
 
-	fn.Nbody = list1(a)
+	fn.Nbody.Set([]*Node{a})
 
 	funcbody(fn)
 
 	typecheck(&fn, Etop)
-	typechecklist(fn.Nbody, Etop)
+	typecheckslice(fn.Nbody.Slice(), Etop)
 	xtop = list(xtop, fn)
 	Curfn = oldfn