[dev.cc] cmd/internal/gc, cmd/new6g etc: convert from cmd/gc, cmd/6g etc

First draft of converted Go compiler, using rsc.io/c2go rev 83d795a.

Change-Id: I29f4c7010de07d2ff1947bbca9865879d83c32c3
Reviewed-on: https://go-review.googlesource.com/4851
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/src/cmd/new8g/ggen.go b/src/cmd/new8g/ggen.go
new file mode 100644
index 0000000..8dd469c
--- /dev/null
+++ b/src/cmd/new8g/ggen.go
@@ -0,0 +1,1297 @@
+// Copyright 2009 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 main
+
+import (
+	"cmd/internal/obj"
+	"cmd/internal/obj/i386"
+)
+import "cmd/internal/gc"
+
+func defframe(ptxt *obj.Prog) {
+	var frame uint32
+	var ax uint32
+	var p *obj.Prog
+	var lo int64
+	var hi int64
+	var l *gc.NodeList
+	var n *gc.Node
+
+	// fill in argument size, stack size
+	ptxt.To.Type = obj.TYPE_TEXTSIZE
+
+	ptxt.To.U.Argsize = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr)))
+	frame = uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg)))
+	ptxt.To.Offset = int64(frame)
+
+	// insert code to zero ambiguously live variables
+	// so that the garbage collector only sees initialized values
+	// when it looks for pointers.
+	p = ptxt
+
+	hi = 0
+	lo = hi
+	ax = 0
+	for l = gc.Curfn.Dcl; l != nil; l = l.Next {
+		n = l.N
+		if !(n.Needzero != 0) {
+			continue
+		}
+		if n.Class != gc.PAUTO {
+			gc.Fatal("needzero class %d", n.Class)
+		}
+		if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 {
+			gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset))
+		}
+		if lo != hi && n.Xoffset+n.Type.Width == lo-int64(2*gc.Widthptr) {
+			// merge with range we already have
+			lo = n.Xoffset
+
+			continue
+		}
+
+		// zero old range
+		p = zerorange(p, int64(frame), lo, hi, &ax)
+
+		// set new range
+		hi = n.Xoffset + n.Type.Width
+
+		lo = n.Xoffset
+	}
+
+	// zero final range
+	zerorange(p, int64(frame), lo, hi, &ax)
+}
+
+func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32) *obj.Prog {
+	var cnt int64
+	var i int64
+
+	cnt = hi - lo
+	if cnt == 0 {
+		return p
+	}
+	if *ax == 0 {
+		p = appendpp(p, i386.AMOVL, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, i386.REG_AX, 0)
+		*ax = 1
+	}
+
+	if cnt <= int64(4*gc.Widthreg) {
+		for i = 0; i < cnt; i += int64(gc.Widthreg) {
+			p = appendpp(p, i386.AMOVL, obj.TYPE_REG, i386.REG_AX, 0, obj.TYPE_MEM, i386.REG_SP, frame+lo+i)
+		}
+	} else if !gc.Nacl && cnt <= int64(128*gc.Widthreg) {
+		p = appendpp(p, i386.ALEAL, obj.TYPE_MEM, i386.REG_SP, frame+lo, obj.TYPE_REG, i386.REG_DI, 0)
+		p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, 1*(128-cnt/int64(gc.Widthreg)))
+		p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
+	} else {
+		p = appendpp(p, i386.AMOVL, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, i386.REG_CX, 0)
+		p = appendpp(p, i386.ALEAL, obj.TYPE_MEM, i386.REG_SP, frame+lo, obj.TYPE_REG, i386.REG_DI, 0)
+		p = appendpp(p, i386.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
+		p = appendpp(p, i386.ASTOSL, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0)
+	}
+
+	return p
+}
+
+func appendpp(p *obj.Prog, as int, ftype int, freg int, foffset int64, ttype int, treg int, toffset int64) *obj.Prog {
+	var q *obj.Prog
+	q = gc.Ctxt.NewProg()
+	gc.Clearp(q)
+	q.As = int16(as)
+	q.Lineno = p.Lineno
+	q.From.Type = int16(ftype)
+	q.From.Reg = int16(freg)
+	q.From.Offset = foffset
+	q.To.Type = int16(ttype)
+	q.To.Reg = int16(treg)
+	q.To.Offset = toffset
+	q.Link = p.Link
+	p.Link = q
+	return q
+}
+
+func clearfat(nl *gc.Node) {
+	var w uint32
+	var c uint32
+	var q uint32
+	var n1 gc.Node
+	var z gc.Node
+	var p *obj.Prog
+
+	/* clear a fat object */
+	if gc.Debug['g'] != 0 {
+		gc.Dump("\nclearfat", nl)
+	}
+
+	w = uint32(nl.Type.Width)
+
+	// Avoid taking the address for simple enough types.
+	if componentgen(nil, nl) != 0 {
+		return
+	}
+
+	c = w % 4 // bytes
+	q = w / 4 // quads
+
+	if q < 4 {
+		// Write sequence of MOV 0, off(base) instead of using STOSL.
+		// The hope is that although the code will be slightly longer,
+		// the MOVs will have no dependencies and pipeline better
+		// than the unrolled STOSL loop.
+		// NOTE: Must use agen, not igen, so that optimizer sees address
+		// being taken. We are not writing on field boundaries.
+		regalloc(&n1, gc.Types[gc.Tptr], nil)
+
+		agen(nl, &n1)
+		n1.Op = gc.OINDREG
+		gc.Nodconst(&z, gc.Types[gc.TUINT64], 0)
+		for {
+			tmp14 := q
+			q--
+			if !(tmp14 > 0) {
+				break
+			}
+			n1.Type = z.Type
+			gins(i386.AMOVL, &z, &n1)
+			n1.Xoffset += 4
+		}
+
+		gc.Nodconst(&z, gc.Types[gc.TUINT8], 0)
+		for {
+			tmp15 := c
+			c--
+			if !(tmp15 > 0) {
+				break
+			}
+			n1.Type = z.Type
+			gins(i386.AMOVB, &z, &n1)
+			n1.Xoffset++
+		}
+
+		regfree(&n1)
+		return
+	}
+
+	gc.Nodreg(&n1, gc.Types[gc.Tptr], i386.REG_DI)
+	agen(nl, &n1)
+	gconreg(i386.AMOVL, 0, i386.REG_AX)
+
+	if q > 128 || (q >= 4 && gc.Nacl) {
+		gconreg(i386.AMOVL, int64(q), i386.REG_CX)
+		gins(i386.AREP, nil, nil)   // repeat
+		gins(i386.ASTOSL, nil, nil) // STOL AL,*(DI)+
+	} else if q >= 4 {
+		p = gins(obj.ADUFFZERO, nil, nil)
+		p.To.Type = obj.TYPE_ADDR
+		p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg))
+
+		// 1 and 128 = magic constants: see ../../runtime/asm_386.s
+		p.To.Offset = 1 * (128 - int64(q))
+	} else {
+		for q > 0 {
+			gins(i386.ASTOSL, nil, nil) // STOL AL,*(DI)+
+			q--
+		}
+	}
+
+	for c > 0 {
+		gins(i386.ASTOSB, nil, nil) // STOB AL,*(DI)+
+		c--
+	}
+}
+
+/*
+ * generate:
+ *	call f
+ *	proc=-1	normal call but no return
+ *	proc=0	normal call
+ *	proc=1	goroutine run in new proc
+ *	proc=2	defer call save away stack
+  *	proc=3	normal call to C pointer (not Go func value)
+*/
+func ginscall(f *gc.Node, proc int) {
+	var p *obj.Prog
+	var reg gc.Node
+	var r1 gc.Node
+	var con gc.Node
+	var stk gc.Node
+	var extra int32
+
+	if f.Type != nil {
+		extra = 0
+		if proc == 1 || proc == 2 {
+			extra = 2 * int32(gc.Widthptr)
+		}
+		gc.Setmaxarg(f.Type, extra)
+	}
+
+	switch proc {
+	default:
+		gc.Fatal("ginscall: bad proc %d", proc)
+
+	case 0, // normal call
+		-1: // normal call but no return
+		if f.Op == gc.ONAME && f.Class == gc.PFUNC {
+			if f == gc.Deferreturn {
+				// Deferred calls will appear to be returning to
+				// the CALL deferreturn(SB) that we are about to emit.
+				// However, the stack trace code will show the line
+				// of the instruction byte before the return PC.
+				// To avoid that being an unrelated instruction,
+				// insert an x86 NOP that we will have the right line number.
+				// x86 NOP 0x90 is really XCHG AX, AX; use that description
+				// because the NOP pseudo-instruction will be removed by
+				// the linker.
+				gc.Nodreg(&reg, gc.Types[gc.TINT], i386.REG_AX)
+
+				gins(i386.AXCHGL, &reg, &reg)
+			}
+
+			p = gins(obj.ACALL, nil, f)
+			gc.Afunclit(&p.To, f)
+			if proc == -1 || gc.Noreturn(p) != 0 {
+				gins(obj.AUNDEF, nil, nil)
+			}
+			break
+		}
+
+		gc.Nodreg(&reg, gc.Types[gc.Tptr], i386.REG_DX)
+		gc.Nodreg(&r1, gc.Types[gc.Tptr], i386.REG_BX)
+		gmove(f, &reg)
+		reg.Op = gc.OINDREG
+		gmove(&reg, &r1)
+		reg.Op = gc.OREGISTER
+		gins(obj.ACALL, &reg, &r1)
+
+	case 3: // normal call of c function pointer
+		gins(obj.ACALL, nil, f)
+
+	case 1, // call in new proc (go)
+		2: // deferred call (defer)
+		stk = gc.Node{}
+
+		stk.Op = gc.OINDREG
+		stk.Val.U.Reg = i386.REG_SP
+		stk.Xoffset = 0
+
+		// size of arguments at 0(SP)
+		gc.Nodconst(&con, gc.Types[gc.TINT32], int64(gc.Argsize(f.Type)))
+
+		gins(i386.AMOVL, &con, &stk)
+
+		// FuncVal* at 4(SP)
+		stk.Xoffset = int64(gc.Widthptr)
+
+		gins(i386.AMOVL, f, &stk)
+
+		if proc == 1 {
+			ginscall(gc.Newproc, 0)
+		} else {
+			ginscall(gc.Deferproc, 0)
+		}
+		if proc == 2 {
+			gc.Nodreg(&reg, gc.Types[gc.TINT32], i386.REG_AX)
+			gins(i386.ATESTL, &reg, &reg)
+			p = gc.Gbranch(i386.AJEQ, nil, +1)
+			cgen_ret(nil)
+			gc.Patch(p, gc.Pc)
+		}
+	}
+}
+
+/*
+ * n is call to interface method.
+ * generate res = n.
+ */
+func cgen_callinter(n *gc.Node, res *gc.Node, proc int) {
+	var i *gc.Node
+	var f *gc.Node
+	var tmpi gc.Node
+	var nodi gc.Node
+	var nodo gc.Node
+	var nodr gc.Node
+	var nodsp gc.Node
+
+	i = n.Left
+	if i.Op != gc.ODOTINTER {
+		gc.Fatal("cgen_callinter: not ODOTINTER %v", gc.Oconv(int(i.Op), 0))
+	}
+
+	f = i.Right // field
+	if f.Op != gc.ONAME {
+		gc.Fatal("cgen_callinter: not ONAME %v", gc.Oconv(int(f.Op), 0))
+	}
+
+	i = i.Left // interface
+
+	if !(i.Addable != 0) {
+		gc.Tempname(&tmpi, i.Type)
+		cgen(i, &tmpi)
+		i = &tmpi
+	}
+
+	gc.Genlist(n.List) // assign the args
+
+	// i is now addable, prepare an indirected
+	// register to hold its address.
+	igen(i, &nodi, res) // REG = &inter
+
+	gc.Nodindreg(&nodsp, gc.Types[gc.Tptr], i386.REG_SP)
+
+	nodsp.Xoffset = 0
+	if proc != 0 {
+		nodsp.Xoffset += 2 * int64(gc.Widthptr) // leave room for size & fn
+	}
+	nodi.Type = gc.Types[gc.Tptr]
+	nodi.Xoffset += int64(gc.Widthptr)
+	cgen(&nodi, &nodsp) // {0 or 8}(SP) = 4(REG) -- i.data
+
+	regalloc(&nodo, gc.Types[gc.Tptr], res)
+
+	nodi.Type = gc.Types[gc.Tptr]
+	nodi.Xoffset -= int64(gc.Widthptr)
+	cgen(&nodi, &nodo) // REG = 0(REG) -- i.tab
+	regfree(&nodi)
+
+	regalloc(&nodr, gc.Types[gc.Tptr], &nodo)
+	if n.Left.Xoffset == gc.BADWIDTH {
+		gc.Fatal("cgen_callinter: badwidth")
+	}
+	gc.Cgen_checknil(&nodo)
+	nodo.Op = gc.OINDREG
+	nodo.Xoffset = n.Left.Xoffset + 3*int64(gc.Widthptr) + 8
+
+	if proc == 0 {
+		// plain call: use direct c function pointer - more efficient
+		cgen(&nodo, &nodr) // REG = 20+offset(REG) -- i.tab->fun[f]
+		proc = 3
+	} else {
+		// go/defer. generate go func value.
+		gins(i386.ALEAL, &nodo, &nodr) // REG = &(20+offset(REG)) -- i.tab->fun[f]
+	}
+
+	nodr.Type = n.Left.Type
+	ginscall(&nodr, proc)
+
+	regfree(&nodr)
+	regfree(&nodo)
+}
+
+/*
+ * generate function call;
+ *	proc=0	normal call
+ *	proc=1	goroutine run in new proc
+ *	proc=2	defer call save away stack
+ */
+func cgen_call(n *gc.Node, proc int) {
+	var t *gc.Type
+	var nod gc.Node
+	var afun gc.Node
+
+	if n == nil {
+		return
+	}
+
+	if n.Left.Ullman >= gc.UINF {
+		// if name involves a fn call
+		// precompute the address of the fn
+		gc.Tempname(&afun, gc.Types[gc.Tptr])
+
+		cgen(n.Left, &afun)
+	}
+
+	gc.Genlist(n.List) // assign the args
+	t = n.Left.Type
+
+	// call tempname pointer
+	if n.Left.Ullman >= gc.UINF {
+		regalloc(&nod, gc.Types[gc.Tptr], nil)
+		gc.Cgen_as(&nod, &afun)
+		nod.Type = t
+		ginscall(&nod, proc)
+		regfree(&nod)
+		return
+	}
+
+	// call pointer
+	if n.Left.Op != gc.ONAME || n.Left.Class != gc.PFUNC {
+		regalloc(&nod, gc.Types[gc.Tptr], nil)
+		gc.Cgen_as(&nod, n.Left)
+		nod.Type = t
+		ginscall(&nod, proc)
+		regfree(&nod)
+		return
+	}
+
+	// call direct
+	n.Left.Method = 1
+
+	ginscall(n.Left, proc)
+}
+
+/*
+ * call to n has already been generated.
+ * generate:
+ *	res = return value from call.
+ */
+func cgen_callret(n *gc.Node, res *gc.Node) {
+	var nod gc.Node
+	var fp *gc.Type
+	var t *gc.Type
+	var flist gc.Iter
+
+	t = n.Left.Type
+	if t.Etype == gc.TPTR32 || t.Etype == gc.TPTR64 {
+		t = t.Type
+	}
+
+	fp = gc.Structfirst(&flist, gc.Getoutarg(t))
+	if fp == nil {
+		gc.Fatal("cgen_callret: nil")
+	}
+
+	nod = gc.Node{}
+	nod.Op = gc.OINDREG
+	nod.Val.U.Reg = i386.REG_SP
+	nod.Addable = 1
+
+	nod.Xoffset = fp.Width
+	nod.Type = fp.Type
+	gc.Cgen_as(res, &nod)
+}
+
+/*
+ * call to n has already been generated.
+ * generate:
+ *	res = &return value from call.
+ */
+func cgen_aret(n *gc.Node, res *gc.Node) {
+	var nod1 gc.Node
+	var nod2 gc.Node
+	var fp *gc.Type
+	var t *gc.Type
+	var flist gc.Iter
+
+	t = n.Left.Type
+	if gc.Isptr[t.Etype] != 0 {
+		t = t.Type
+	}
+
+	fp = gc.Structfirst(&flist, gc.Getoutarg(t))
+	if fp == nil {
+		gc.Fatal("cgen_aret: nil")
+	}
+
+	nod1 = gc.Node{}
+	nod1.Op = gc.OINDREG
+	nod1.Val.U.Reg = i386.REG_SP
+	nod1.Addable = 1
+
+	nod1.Xoffset = fp.Width
+	nod1.Type = fp.Type
+
+	if res.Op != gc.OREGISTER {
+		regalloc(&nod2, gc.Types[gc.Tptr], res)
+		gins(i386.ALEAL, &nod1, &nod2)
+		gins(i386.AMOVL, &nod2, res)
+		regfree(&nod2)
+	} else {
+		gins(i386.ALEAL, &nod1, res)
+	}
+}
+
+/*
+ * generate return.
+ * n->left is assignments to return values.
+ */
+func cgen_ret(n *gc.Node) {
+	var p *obj.Prog
+
+	if n != nil {
+		gc.Genlist(n.List) // copy out args
+	}
+	if gc.Hasdefer != 0 {
+		ginscall(gc.Deferreturn, 0)
+	}
+	gc.Genlist(gc.Curfn.Exit)
+	p = gins(obj.ARET, nil, nil)
+	if n != nil && n.Op == gc.ORETJMP {
+		p.To.Type = obj.TYPE_MEM
+		p.To.Name = obj.NAME_EXTERN
+		p.To.Sym = gc.Linksym(n.Left.Sym)
+	}
+}
+
+/*
+ * generate division.
+ * caller must set:
+ *	ax = allocated AX register
+ *	dx = allocated DX register
+ * generates one of:
+ *	res = nl / nr
+ *	res = nl % nr
+ * according to op.
+ */
+func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node, ax *gc.Node, dx *gc.Node) {
+	var check int
+	var n1 gc.Node
+	var t1 gc.Node
+	var t2 gc.Node
+	var t3 gc.Node
+	var t4 gc.Node
+	var n4 gc.Node
+	var nz gc.Node
+	var t *gc.Type
+	var t0 *gc.Type
+	var p1 *obj.Prog
+	var p2 *obj.Prog
+
+	// Have to be careful about handling
+	// most negative int divided by -1 correctly.
+	// The hardware will trap.
+	// Also the byte divide instruction needs AH,
+	// which we otherwise don't have to deal with.
+	// Easiest way to avoid for int8, int16: use int32.
+	// For int32 and int64, use explicit test.
+	// Could use int64 hw for int32.
+	t = nl.Type
+
+	t0 = t
+	check = 0
+	if gc.Issigned[t.Etype] != 0 {
+		check = 1
+		if gc.Isconst(nl, gc.CTINT) != 0 && gc.Mpgetfix(nl.Val.U.Xval) != -1<<uint64(t.Width*8-1) {
+			check = 0
+		} else if gc.Isconst(nr, gc.CTINT) != 0 && gc.Mpgetfix(nr.Val.U.Xval) != -1 {
+			check = 0
+		}
+	}
+
+	if t.Width < 4 {
+		if gc.Issigned[t.Etype] != 0 {
+			t = gc.Types[gc.TINT32]
+		} else {
+			t = gc.Types[gc.TUINT32]
+		}
+		check = 0
+	}
+
+	gc.Tempname(&t1, t)
+	gc.Tempname(&t2, t)
+	if t0 != t {
+		gc.Tempname(&t3, t0)
+		gc.Tempname(&t4, t0)
+		cgen(nl, &t3)
+		cgen(nr, &t4)
+
+		// Convert.
+		gmove(&t3, &t1)
+
+		gmove(&t4, &t2)
+	} else {
+		cgen(nl, &t1)
+		cgen(nr, &t2)
+	}
+
+	if !(gc.Samereg(ax, res) != 0) && !(gc.Samereg(dx, res) != 0) {
+		regalloc(&n1, t, res)
+	} else {
+		regalloc(&n1, t, nil)
+	}
+	gmove(&t2, &n1)
+	gmove(&t1, ax)
+	p2 = nil
+	if gc.Nacl {
+		// Native Client does not relay the divide-by-zero trap
+		// to the executing program, so we must insert a check
+		// for ourselves.
+		gc.Nodconst(&n4, t, 0)
+
+		gins(optoas(gc.OCMP, t), &n1, &n4)
+		p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1)
+		if panicdiv == nil {
+			panicdiv = gc.Sysfunc("panicdivide")
+		}
+		ginscall(panicdiv, -1)
+		gc.Patch(p1, gc.Pc)
+	}
+
+	if check != 0 {
+		gc.Nodconst(&n4, t, -1)
+		gins(optoas(gc.OCMP, t), &n1, &n4)
+		p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1)
+		if op == gc.ODIV {
+			// a / (-1) is -a.
+			gins(optoas(gc.OMINUS, t), nil, ax)
+
+			gmove(ax, res)
+		} else {
+			// a % (-1) is 0.
+			gc.Nodconst(&n4, t, 0)
+
+			gmove(&n4, res)
+		}
+
+		p2 = gc.Gbranch(obj.AJMP, nil, 0)
+		gc.Patch(p1, gc.Pc)
+	}
+
+	if !(gc.Issigned[t.Etype] != 0) {
+		gc.Nodconst(&nz, t, 0)
+		gmove(&nz, dx)
+	} else {
+		gins(optoas(gc.OEXTEND, t), nil, nil)
+	}
+	gins(optoas(op, t), &n1, nil)
+	regfree(&n1)
+
+	if op == gc.ODIV {
+		gmove(ax, res)
+	} else {
+		gmove(dx, res)
+	}
+	if check != 0 {
+		gc.Patch(p2, gc.Pc)
+	}
+}
+
+func savex(dr int, x *gc.Node, oldx *gc.Node, res *gc.Node, t *gc.Type) {
+	var r int
+
+	r = int(reg[dr])
+	gc.Nodreg(x, gc.Types[gc.TINT32], dr)
+
+	// save current ax and dx if they are live
+	// and not the destination
+	*oldx = gc.Node{}
+
+	if r > 0 && !(gc.Samereg(x, res) != 0) {
+		gc.Tempname(oldx, gc.Types[gc.TINT32])
+		gmove(x, oldx)
+	}
+
+	regalloc(x, t, x)
+}
+
+func restx(x *gc.Node, oldx *gc.Node) {
+	regfree(x)
+
+	if oldx.Op != 0 {
+		x.Type = gc.Types[gc.TINT32]
+		gmove(oldx, x)
+	}
+}
+
+/*
+ * generate division according to op, one of:
+ *	res = nl / nr
+ *	res = nl % nr
+ */
+func cgen_div(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) {
+	var ax gc.Node
+	var dx gc.Node
+	var oldax gc.Node
+	var olddx gc.Node
+	var t *gc.Type
+
+	if gc.Is64(nl.Type) != 0 {
+		gc.Fatal("cgen_div %v", gc.Tconv(nl.Type, 0))
+	}
+
+	if gc.Issigned[nl.Type.Etype] != 0 {
+		t = gc.Types[gc.TINT32]
+	} else {
+		t = gc.Types[gc.TUINT32]
+	}
+	savex(i386.REG_AX, &ax, &oldax, res, t)
+	savex(i386.REG_DX, &dx, &olddx, res, t)
+	dodiv(op, nl, nr, res, &ax, &dx)
+	restx(&dx, &olddx)
+	restx(&ax, &oldax)
+}
+
+/*
+ * generate shift according to op, one of:
+ *	res = nl << nr
+ *	res = nl >> nr
+ */
+func cgen_shift(op int, bounded int, nl *gc.Node, nr *gc.Node, res *gc.Node) {
+	var n1 gc.Node
+	var n2 gc.Node
+	var nt gc.Node
+	var cx gc.Node
+	var oldcx gc.Node
+	var hi gc.Node
+	var lo gc.Node
+	var a int
+	var w int
+	var p1 *obj.Prog
+	var p2 *obj.Prog
+	var sc uint64
+
+	if nl.Type.Width > 4 {
+		gc.Fatal("cgen_shift %v", gc.Tconv(nl.Type, 0))
+	}
+
+	w = int(nl.Type.Width * 8)
+
+	a = optoas(op, nl.Type)
+
+	if nr.Op == gc.OLITERAL {
+		gc.Tempname(&n2, nl.Type)
+		cgen(nl, &n2)
+		regalloc(&n1, nl.Type, res)
+		gmove(&n2, &n1)
+		sc = uint64(gc.Mpgetfix(nr.Val.U.Xval))
+		if sc >= uint64(nl.Type.Width*8) {
+			// large shift gets 2 shifts by width-1
+			gins(a, ncon(uint32(w)-1), &n1)
+
+			gins(a, ncon(uint32(w)-1), &n1)
+		} else {
+			gins(a, nr, &n1)
+		}
+		gmove(&n1, res)
+		regfree(&n1)
+		return
+	}
+
+	oldcx = gc.Node{}
+	gc.Nodreg(&cx, gc.Types[gc.TUINT32], i386.REG_CX)
+	if reg[i386.REG_CX] > 1 && !(gc.Samereg(&cx, res) != 0) {
+		gc.Tempname(&oldcx, gc.Types[gc.TUINT32])
+		gmove(&cx, &oldcx)
+	}
+
+	if nr.Type.Width > 4 {
+		gc.Tempname(&nt, nr.Type)
+		n1 = nt
+	} else {
+		gc.Nodreg(&n1, gc.Types[gc.TUINT32], i386.REG_CX)
+		regalloc(&n1, nr.Type, &n1) // to hold the shift type in CX
+	}
+
+	if gc.Samereg(&cx, res) != 0 {
+		regalloc(&n2, nl.Type, nil)
+	} else {
+		regalloc(&n2, nl.Type, res)
+	}
+	if nl.Ullman >= nr.Ullman {
+		cgen(nl, &n2)
+		cgen(nr, &n1)
+	} else {
+		cgen(nr, &n1)
+		cgen(nl, &n2)
+	}
+
+	// test and fix up large shifts
+	if bounded != 0 {
+		if nr.Type.Width > 4 {
+			// delayed reg alloc
+			gc.Nodreg(&n1, gc.Types[gc.TUINT32], i386.REG_CX)
+
+			regalloc(&n1, gc.Types[gc.TUINT32], &n1) // to hold the shift type in CX
+			split64(&nt, &lo, &hi)
+			gmove(&lo, &n1)
+			splitclean()
+		}
+	} else {
+		if nr.Type.Width > 4 {
+			// delayed reg alloc
+			gc.Nodreg(&n1, gc.Types[gc.TUINT32], i386.REG_CX)
+
+			regalloc(&n1, gc.Types[gc.TUINT32], &n1) // to hold the shift type in CX
+			split64(&nt, &lo, &hi)
+			gmove(&lo, &n1)
+			gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &hi, ncon(0))
+			p2 = gc.Gbranch(optoas(gc.ONE, gc.Types[gc.TUINT32]), nil, +1)
+			gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &n1, ncon(uint32(w)))
+			p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1)
+			splitclean()
+			gc.Patch(p2, gc.Pc)
+		} else {
+			gins(optoas(gc.OCMP, nr.Type), &n1, ncon(uint32(w)))
+			p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1)
+		}
+
+		if op == gc.ORSH && gc.Issigned[nl.Type.Etype] != 0 {
+			gins(a, ncon(uint32(w)-1), &n2)
+		} else {
+			gmove(ncon(0), &n2)
+		}
+
+		gc.Patch(p1, gc.Pc)
+	}
+
+	gins(a, &n1, &n2)
+
+	if oldcx.Op != 0 {
+		gmove(&oldcx, &cx)
+	}
+
+	gmove(&n2, res)
+
+	regfree(&n1)
+	regfree(&n2)
+}
+
+/*
+ * generate byte multiply:
+ *	res = nl * nr
+ * there is no 2-operand byte multiply instruction so
+ * we do a full-width multiplication and truncate afterwards.
+ */
+func cgen_bmul(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) {
+	var n1 gc.Node
+	var n2 gc.Node
+	var nt gc.Node
+	var tmp *gc.Node
+	var t *gc.Type
+	var a int
+
+	// copy from byte to full registers
+	t = gc.Types[gc.TUINT32]
+
+	if gc.Issigned[nl.Type.Etype] != 0 {
+		t = gc.Types[gc.TINT32]
+	}
+
+	// largest ullman on left.
+	if nl.Ullman < nr.Ullman {
+		tmp = nl
+		nl = nr
+		nr = tmp
+	}
+
+	gc.Tempname(&nt, nl.Type)
+	cgen(nl, &nt)
+	regalloc(&n1, t, res)
+	cgen(nr, &n1)
+	regalloc(&n2, t, nil)
+	gmove(&nt, &n2)
+	a = optoas(op, t)
+	gins(a, &n2, &n1)
+	regfree(&n2)
+	gmove(&n1, res)
+	regfree(&n1)
+}
+
+/*
+ * generate high multiply:
+ *   res = (nl*nr) >> width
+ */
+func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) {
+	var t *gc.Type
+	var a int
+	var n1 gc.Node
+	var n2 gc.Node
+	var ax gc.Node
+	var dx gc.Node
+
+	t = nl.Type
+	a = optoas(gc.OHMUL, t)
+
+	// gen nl in n1.
+	gc.Tempname(&n1, t)
+
+	cgen(nl, &n1)
+
+	// gen nr in n2.
+	regalloc(&n2, t, res)
+
+	cgen(nr, &n2)
+
+	// multiply.
+	gc.Nodreg(&ax, t, i386.REG_AX)
+
+	gmove(&n2, &ax)
+	gins(a, &n1, nil)
+	regfree(&n2)
+
+	if t.Width == 1 {
+		// byte multiply behaves differently.
+		gc.Nodreg(&ax, t, i386.REG_AH)
+
+		gc.Nodreg(&dx, t, i386.REG_DX)
+		gmove(&ax, &dx)
+	}
+
+	gc.Nodreg(&dx, t, i386.REG_DX)
+	gmove(&dx, res)
+}
+
+/*
+ * generate floating-point operation.
+ */
+func cgen_float(n *gc.Node, res *gc.Node) {
+	var nl *gc.Node
+	var n1 gc.Node
+	var n2 gc.Node
+	var p1 *obj.Prog
+	var p2 *obj.Prog
+	var p3 *obj.Prog
+
+	nl = n.Left
+	switch n.Op {
+	case gc.OEQ,
+		gc.ONE,
+		gc.OLT,
+		gc.OLE,
+		gc.OGE:
+		p1 = gc.Gbranch(obj.AJMP, nil, 0)
+		p2 = gc.Pc
+		gmove(gc.Nodbool(1), res)
+		p3 = gc.Gbranch(obj.AJMP, nil, 0)
+		gc.Patch(p1, gc.Pc)
+		bgen(n, true, 0, p2)
+		gmove(gc.Nodbool(0), res)
+		gc.Patch(p3, gc.Pc)
+		return
+
+	case gc.OPLUS:
+		cgen(nl, res)
+		return
+
+	case gc.OCONV:
+		if gc.Eqtype(n.Type, nl.Type) || gc.Noconv(n.Type, nl.Type) != 0 {
+			cgen(nl, res)
+			return
+		}
+
+		gc.Tempname(&n2, n.Type)
+		mgen(nl, &n1, res)
+		gmove(&n1, &n2)
+		gmove(&n2, res)
+		mfree(&n1)
+		return
+	}
+
+	if gc.Use_sse != 0 {
+		cgen_floatsse(n, res)
+	} else {
+		cgen_float387(n, res)
+	}
+}
+
+// floating-point.  387 (not SSE2)
+func cgen_float387(n *gc.Node, res *gc.Node) {
+	var f0 gc.Node
+	var f1 gc.Node
+	var nl *gc.Node
+	var nr *gc.Node
+
+	nl = n.Left
+	nr = n.Right
+	gc.Nodreg(&f0, nl.Type, i386.REG_F0)
+	gc.Nodreg(&f1, n.Type, i386.REG_F0+1)
+	if nr != nil {
+		goto flt2
+	}
+
+	// unary
+	cgen(nl, &f0)
+
+	if n.Op != gc.OCONV && n.Op != gc.OPLUS {
+		gins(foptoas(int(n.Op), n.Type, 0), nil, nil)
+	}
+	gmove(&f0, res)
+	return
+
+flt2: // binary
+	if nl.Ullman >= nr.Ullman {
+		cgen(nl, &f0)
+		if nr.Addable != 0 {
+			gins(foptoas(int(n.Op), n.Type, 0), nr, &f0)
+		} else {
+			cgen(nr, &f0)
+			gins(foptoas(int(n.Op), n.Type, Fpop), &f0, &f1)
+		}
+	} else {
+		cgen(nr, &f0)
+		if nl.Addable != 0 {
+			gins(foptoas(int(n.Op), n.Type, Frev), nl, &f0)
+		} else {
+			cgen(nl, &f0)
+			gins(foptoas(int(n.Op), n.Type, Frev|Fpop), &f0, &f1)
+		}
+	}
+
+	gmove(&f0, res)
+	return
+}
+
+func cgen_floatsse(n *gc.Node, res *gc.Node) {
+	var nl *gc.Node
+	var nr *gc.Node
+	var r *gc.Node
+	var n1 gc.Node
+	var n2 gc.Node
+	var nt gc.Node
+	var a int
+
+	nl = n.Left
+	nr = n.Right
+	switch n.Op {
+	default:
+		gc.Dump("cgen_floatsse", n)
+		gc.Fatal("cgen_floatsse %v", gc.Oconv(int(n.Op), 0))
+		return
+
+	case gc.OMINUS,
+		gc.OCOM:
+		nr = gc.Nodintconst(-1)
+		gc.Convlit(&nr, n.Type)
+		a = foptoas(gc.OMUL, nl.Type, 0)
+		goto sbop
+
+		// symmetric binary
+	case gc.OADD,
+		gc.OMUL:
+		a = foptoas(int(n.Op), nl.Type, 0)
+
+		goto sbop
+
+		// asymmetric binary
+	case gc.OSUB,
+		gc.OMOD,
+		gc.ODIV:
+		a = foptoas(int(n.Op), nl.Type, 0)
+
+		goto abop
+	}
+
+sbop: // symmetric binary
+	if nl.Ullman < nr.Ullman || nl.Op == gc.OLITERAL {
+		r = nl
+		nl = nr
+		nr = r
+	}
+
+abop: // asymmetric binary
+	if nl.Ullman >= nr.Ullman {
+		gc.Tempname(&nt, nl.Type)
+		cgen(nl, &nt)
+		mgen(nr, &n2, nil)
+		regalloc(&n1, nl.Type, res)
+		gmove(&nt, &n1)
+		gins(a, &n2, &n1)
+		gmove(&n1, res)
+		regfree(&n1)
+		mfree(&n2)
+	} else {
+		regalloc(&n2, nr.Type, res)
+		cgen(nr, &n2)
+		regalloc(&n1, nl.Type, nil)
+		cgen(nl, &n1)
+		gins(a, &n2, &n1)
+		regfree(&n2)
+		gmove(&n1, res)
+		regfree(&n1)
+	}
+
+	return
+}
+
+func bgen_float(n *gc.Node, true_ int, likely int, to *obj.Prog) {
+	var et int
+	var a int
+	var nl *gc.Node
+	var nr *gc.Node
+	var r *gc.Node
+	var n1 gc.Node
+	var n2 gc.Node
+	var n3 gc.Node
+	var tmp gc.Node
+	var t1 gc.Node
+	var t2 gc.Node
+	var ax gc.Node
+	var p1 *obj.Prog
+	var p2 *obj.Prog
+
+	nl = n.Left
+	nr = n.Right
+	a = int(n.Op)
+	if !(true_ != 0) {
+		// brcom is not valid on floats when NaN is involved.
+		p1 = gc.Gbranch(obj.AJMP, nil, 0)
+
+		p2 = gc.Gbranch(obj.AJMP, nil, 0)
+		gc.Patch(p1, gc.Pc)
+
+		// No need to avoid re-genning ninit.
+		bgen_float(n, 1, -likely, p2)
+
+		gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to)
+		gc.Patch(p2, gc.Pc)
+		return
+	}
+
+	if gc.Use_sse != 0 {
+		goto sse
+	} else {
+		goto x87
+	}
+
+x87:
+	a = gc.Brrev(a) // because the args are stacked
+	if a == gc.OGE || a == gc.OGT {
+		// only < and <= work right with NaN; reverse if needed
+		r = nr
+
+		nr = nl
+		nl = r
+		a = gc.Brrev(a)
+	}
+
+	gc.Nodreg(&tmp, nr.Type, i386.REG_F0)
+	gc.Nodreg(&n2, nr.Type, i386.REG_F0+1)
+	gc.Nodreg(&ax, gc.Types[gc.TUINT16], i386.REG_AX)
+	et = gc.Simsimtype(nr.Type)
+	if et == gc.TFLOAT64 {
+		if nl.Ullman > nr.Ullman {
+			cgen(nl, &tmp)
+			cgen(nr, &tmp)
+			gins(i386.AFXCHD, &tmp, &n2)
+		} else {
+			cgen(nr, &tmp)
+			cgen(nl, &tmp)
+		}
+
+		gins(i386.AFUCOMIP, &tmp, &n2)
+		gins(i386.AFMOVDP, &tmp, &tmp) // annoying pop but still better than STSW+SAHF
+	} else {
+		// TODO(rsc): The moves back and forth to memory
+		// here are for truncating the value to 32 bits.
+		// This handles 32-bit comparison but presumably
+		// all the other ops have the same problem.
+		// We need to figure out what the right general
+		// solution is, besides telling people to use float64.
+		gc.Tempname(&t1, gc.Types[gc.TFLOAT32])
+
+		gc.Tempname(&t2, gc.Types[gc.TFLOAT32])
+		cgen(nr, &t1)
+		cgen(nl, &t2)
+		gmove(&t2, &tmp)
+		gins(i386.AFCOMFP, &t1, &tmp)
+		gins(i386.AFSTSW, nil, &ax)
+		gins(i386.ASAHF, nil, nil)
+	}
+
+	goto ret
+
+sse:
+	if !(nl.Addable != 0) {
+		gc.Tempname(&n1, nl.Type)
+		cgen(nl, &n1)
+		nl = &n1
+	}
+
+	if !(nr.Addable != 0) {
+		gc.Tempname(&tmp, nr.Type)
+		cgen(nr, &tmp)
+		nr = &tmp
+	}
+
+	regalloc(&n2, nr.Type, nil)
+	gmove(nr, &n2)
+	nr = &n2
+
+	if nl.Op != gc.OREGISTER {
+		regalloc(&n3, nl.Type, nil)
+		gmove(nl, &n3)
+		nl = &n3
+	}
+
+	if a == gc.OGE || a == gc.OGT {
+		// only < and <= work right with NaN; reverse if needed
+		r = nr
+
+		nr = nl
+		nl = r
+		a = gc.Brrev(a)
+	}
+
+	gins(foptoas(gc.OCMP, nr.Type, 0), nl, nr)
+	if nl.Op == gc.OREGISTER {
+		regfree(nl)
+	}
+	regfree(nr)
+
+ret:
+	if a == gc.OEQ {
+		// neither NE nor P
+		p1 = gc.Gbranch(i386.AJNE, nil, -likely)
+
+		p2 = gc.Gbranch(i386.AJPS, nil, -likely)
+		gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to)
+		gc.Patch(p1, gc.Pc)
+		gc.Patch(p2, gc.Pc)
+	} else if a == gc.ONE {
+		// either NE or P
+		gc.Patch(gc.Gbranch(i386.AJNE, nil, likely), to)
+
+		gc.Patch(gc.Gbranch(i386.AJPS, nil, likely), to)
+	} else {
+		gc.Patch(gc.Gbranch(optoas(a, nr.Type), nil, likely), to)
+	}
+}
+
+// Called after regopt and peep have run.
+// Expand CHECKNIL pseudo-op into actual nil pointer check.
+func expandchecks(firstp *obj.Prog) {
+	var p *obj.Prog
+	var p1 *obj.Prog
+	var p2 *obj.Prog
+
+	for p = firstp; p != nil; p = p.Link {
+		if p.As != obj.ACHECKNIL {
+			continue
+		}
+		if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers
+			gc.Warnl(int(p.Lineno), "generated nil check")
+		}
+
+		// check is
+		//	CMP arg, $0
+		//	JNE 2(PC) (likely)
+		//	MOV AX, 0
+		p1 = gc.Ctxt.NewProg()
+
+		p2 = gc.Ctxt.NewProg()
+		gc.Clearp(p1)
+		gc.Clearp(p2)
+		p1.Link = p2
+		p2.Link = p.Link
+		p.Link = p1
+		p1.Lineno = p.Lineno
+		p2.Lineno = p.Lineno
+		p1.Pc = 9999
+		p2.Pc = 9999
+		p.As = i386.ACMPL
+		p.To.Type = obj.TYPE_CONST
+		p.To.Offset = 0
+		p1.As = i386.AJNE
+		p1.From.Type = obj.TYPE_CONST
+		p1.From.Offset = 1 // likely
+		p1.To.Type = obj.TYPE_BRANCH
+		p1.To.U.Branch = p2.Link
+
+		// crash by write to memory address 0.
+		// if possible, since we know arg is 0, use 0(arg),
+		// which will be shorter to encode than plain 0.
+		p2.As = i386.AMOVL
+
+		p2.From.Type = obj.TYPE_REG
+		p2.From.Reg = i386.REG_AX
+		if regtyp(&p.From) != 0 {
+			p2.To.Type = obj.TYPE_MEM
+			p2.To.Reg = p.From.Reg
+		} else {
+			p2.To.Type = obj.TYPE_MEM
+		}
+		p2.To.Offset = 0
+	}
+}