Exemplo n.º 1
0
/*
 * allocate a register (reusing res if possible) and generate
 *  a = n
 * The caller must call regfree(a).
 */
func cgenr(n *gc.Node, a *gc.Node, res *gc.Node) {
	if gc.Debug['g'] != 0 {
		gc.Dump("cgenr-n", n)
	}

	if gc.Isfat(n.Type) {
		gc.Fatal("cgenr on fat node")
	}

	if n.Addable != 0 {
		regalloc(a, n.Type, res)
		gmove(n, a)
		return
	}

	switch n.Op {
	case gc.ONAME,
		gc.ODOT,
		gc.ODOTPTR,
		gc.OINDEX,
		gc.OCALLFUNC,
		gc.OCALLMETH,
		gc.OCALLINTER:
		var n1 gc.Node
		igen(n, &n1, res)
		regalloc(a, gc.Types[gc.Tptr], &n1)
		gmove(&n1, a)
		regfree(&n1)

	default:
		regalloc(a, n.Type, res)
		cgen(n, a)
	}
}
Exemplo n.º 2
0
/*
 * generate:
 *	res = n;
 * simplifies and calls gmove.
 */
func cgen(n *gc.Node, res *gc.Node) {
	//print("cgen %N(%d) -> %N(%d)\n", n, n->addable, res, res->addable);
	if gc.Debug['g'] != 0 {
		gc.Dump("\ncgen-n", n)
		gc.Dump("cgen-res", res)
	}

	if n == nil || n.Type == nil {
		return
	}

	if res == nil || res.Type == nil {
		gc.Fatal("cgen: res nil")
	}

	for n.Op == gc.OCONVNOP {
		n = n.Left
	}

	switch n.Op {
	case gc.OSLICE,
		gc.OSLICEARR,
		gc.OSLICESTR,
		gc.OSLICE3,
		gc.OSLICE3ARR:
		if res.Op != gc.ONAME || res.Addable == 0 {
			var n1 gc.Node
			gc.Tempname(&n1, n.Type)
			gc.Cgen_slice(n, &n1)
			cgen(&n1, res)
		} else {
			gc.Cgen_slice(n, res)
		}
		return

	case gc.OEFACE:
		if res.Op != gc.ONAME || res.Addable == 0 {
			var n1 gc.Node
			gc.Tempname(&n1, n.Type)
			gc.Cgen_eface(n, &n1)
			cgen(&n1, res)
		} else {
			gc.Cgen_eface(n, res)
		}
		return
	}

	if n.Ullman >= gc.UINF {
		if n.Op == gc.OINDREG {
			gc.Fatal("cgen: this is going to misscompile")
		}
		if res.Ullman >= gc.UINF {
			var n1 gc.Node
			gc.Tempname(&n1, n.Type)
			cgen(n, &n1)
			cgen(&n1, res)
			return
		}
	}

	if gc.Isfat(n.Type) {
		if n.Type.Width < 0 {
			gc.Fatal("forgot to compute width for %v", gc.Tconv(n.Type, 0))
		}
		sgen(n, res, n.Type.Width)
		return
	}

	if res.Addable == 0 {
		if n.Ullman > res.Ullman {
			var n1 gc.Node
			regalloc(&n1, n.Type, res)
			cgen(n, &n1)
			if n1.Ullman > res.Ullman {
				gc.Dump("n1", &n1)
				gc.Dump("res", res)
				gc.Fatal("loop in cgen")
			}

			cgen(&n1, res)
			regfree(&n1)
			return
		}

		var f int
		if res.Ullman >= gc.UINF {
			goto gen
		}

		if gc.Complexop(n, res) {
			gc.Complexgen(n, res)
			return
		}

		f = 1 // gen thru register
		switch n.Op {
		case gc.OLITERAL:
			if gc.Smallintconst(n) {
				f = 0
			}

		case gc.OREGISTER:
			f = 0
		}

		if !gc.Iscomplex[n.Type.Etype] {
			a := optoas(gc.OAS, res.Type)
			var addr obj.Addr
			if sudoaddable(a, res, &addr) {
				var p1 *obj.Prog
				if f != 0 {
					var n2 gc.Node
					regalloc(&n2, res.Type, nil)
					cgen(n, &n2)
					p1 = gins(a, &n2, nil)
					regfree(&n2)
				} else {
					p1 = gins(a, n, nil)
				}
				p1.To = addr
				if gc.Debug['g'] != 0 {
					fmt.Printf("%v [ignore previous line]\n", p1)
				}
				sudoclean()
				return
			}
		}

	gen:
		var n1 gc.Node
		igen(res, &n1, nil)
		cgen(n, &n1)
		regfree(&n1)
		return
	}

	// update addressability for string, slice
	// can't do in walk because n->left->addable
	// changes if n->left is an escaping local variable.
	switch n.Op {
	case gc.OSPTR,
		gc.OLEN:
		if gc.Isslice(n.Left.Type) || gc.Istype(n.Left.Type, gc.TSTRING) {
			n.Addable = n.Left.Addable
		}

	case gc.OCAP:
		if gc.Isslice(n.Left.Type) {
			n.Addable = n.Left.Addable
		}

	case gc.OITAB:
		n.Addable = n.Left.Addable
	}

	if gc.Complexop(n, res) {
		gc.Complexgen(n, res)
		return
	}

	// if both are addressable, move
	if n.Addable != 0 {
		if n.Op == gc.OREGISTER || res.Op == gc.OREGISTER {
			gmove(n, res)
		} else {
			var n1 gc.Node
			regalloc(&n1, n.Type, nil)
			gmove(n, &n1)
			cgen(&n1, res)
			regfree(&n1)
		}

		return
	}

	nl := n.Left
	nr := n.Right

	if nl != nil && nl.Ullman >= gc.UINF {
		if nr != nil && nr.Ullman >= gc.UINF {
			var n1 gc.Node
			gc.Tempname(&n1, nl.Type)
			cgen(nl, &n1)
			n2 := *n
			n2.Left = &n1
			cgen(&n2, res)
			return
		}
	}

	if !gc.Iscomplex[n.Type.Etype] {
		a := optoas(gc.OAS, n.Type)
		var addr obj.Addr
		if sudoaddable(a, n, &addr) {
			if res.Op == gc.OREGISTER {
				p1 := gins(a, nil, res)
				p1.From = addr
			} else {
				var n2 gc.Node
				regalloc(&n2, n.Type, nil)
				p1 := gins(a, nil, &n2)
				p1.From = addr
				gins(a, &n2, res)
				regfree(&n2)
			}

			sudoclean()
			return
		}
	}

	// TODO(minux): we shouldn't reverse FP comparisons, but then we need to synthesize
	// OGE, OLE, and ONE ourselves.
	// if(nl != N && isfloat[n->type->etype] && isfloat[nl->type->etype]) goto flt;

	var a int
	switch n.Op {
	default:
		gc.Dump("cgen", n)
		gc.Fatal("cgen: unknown op %v", gc.Nconv(n, obj.FmtShort|obj.FmtSign))

		// these call bgen to get a bool value
	case gc.OOROR,
		gc.OANDAND,
		gc.OEQ,
		gc.ONE,
		gc.OLT,
		gc.OLE,
		gc.OGE,
		gc.OGT,
		gc.ONOT:
		p1 := gc.Gbranch(ppc64.ABR, nil, 0)

		p2 := gc.Pc
		gmove(gc.Nodbool(true), res)
		p3 := gc.Gbranch(ppc64.ABR, nil, 0)
		gc.Patch(p1, gc.Pc)
		bgen(n, true, 0, p2)
		gmove(gc.Nodbool(false), res)
		gc.Patch(p3, gc.Pc)
		return

	case gc.OPLUS:
		cgen(nl, res)
		return

		// unary
	case gc.OCOM:
		a := optoas(gc.OXOR, nl.Type)

		var n1 gc.Node
		regalloc(&n1, nl.Type, nil)
		cgen(nl, &n1)
		var n2 gc.Node
		gc.Nodconst(&n2, nl.Type, -1)
		gins(a, &n2, &n1)
		gmove(&n1, res)
		regfree(&n1)
		return

	case gc.OMINUS:
		if gc.Isfloat[nl.Type.Etype] {
			nr = gc.Nodintconst(-1)
			gc.Convlit(&nr, n.Type)
			a = optoas(gc.OMUL, nl.Type)
			goto sbop
		}

		a := optoas(int(n.Op), nl.Type)
		// unary
		var n1 gc.Node
		regalloc(&n1, nl.Type, res)

		cgen(nl, &n1)
		gins(a, nil, &n1)
		gmove(&n1, res)
		regfree(&n1)
		return

		// symmetric binary
	case gc.OAND,
		gc.OOR,
		gc.OXOR,
		gc.OADD,
		gc.OMUL:
		a = optoas(int(n.Op), nl.Type)

		goto sbop

		// asymmetric binary
	case gc.OSUB:
		a = optoas(int(n.Op), nl.Type)

		goto abop

	case gc.OHMUL:
		cgen_hmul(nl, nr, res)

	case gc.OCONV:
		if n.Type.Width > nl.Type.Width {
			// If loading from memory, do conversion during load,
			// so as to avoid use of 8-bit register in, say, int(*byteptr).
			switch nl.Op {
			case gc.ODOT,
				gc.ODOTPTR,
				gc.OINDEX,
				gc.OIND,
				gc.ONAME:
				var n1 gc.Node
				igen(nl, &n1, res)
				var n2 gc.Node
				regalloc(&n2, n.Type, res)
				gmove(&n1, &n2)
				gmove(&n2, res)
				regfree(&n2)
				regfree(&n1)
				return
			}
		}

		var n1 gc.Node
		regalloc(&n1, nl.Type, res)
		var n2 gc.Node
		regalloc(&n2, n.Type, &n1)
		cgen(nl, &n1)

		// if we do the conversion n1 -> n2 here
		// reusing the register, then gmove won't
		// have to allocate its own register.
		gmove(&n1, &n2)

		gmove(&n2, res)
		regfree(&n2)
		regfree(&n1)

	case gc.ODOT,
		gc.ODOTPTR,
		gc.OINDEX,
		gc.OIND,
		gc.ONAME: // PHEAP or PPARAMREF var
		var n1 gc.Node
		igen(n, &n1, res)

		gmove(&n1, res)
		regfree(&n1)

		// interface table is first word of interface value
	case gc.OITAB:
		var n1 gc.Node
		igen(nl, &n1, res)

		n1.Type = n.Type
		gmove(&n1, res)
		regfree(&n1)

		// pointer is the first word of string or slice.
	case gc.OSPTR:
		if gc.Isconst(nl, gc.CTSTR) {
			var n1 gc.Node
			regalloc(&n1, gc.Types[gc.Tptr], res)
			p1 := gins(ppc64.AMOVD, nil, &n1)
			gc.Datastring(nl.Val.U.Sval, &p1.From)
			gmove(&n1, res)
			regfree(&n1)
			break
		}

		var n1 gc.Node
		igen(nl, &n1, res)
		n1.Type = n.Type
		gmove(&n1, res)
		regfree(&n1)

	case gc.OLEN:
		if gc.Istype(nl.Type, gc.TMAP) || gc.Istype(nl.Type, gc.TCHAN) {
			// map and chan have len in the first int-sized word.
			// a zero pointer means zero length
			var n1 gc.Node
			regalloc(&n1, gc.Types[gc.Tptr], res)

			cgen(nl, &n1)

			var n2 gc.Node
			gc.Nodconst(&n2, gc.Types[gc.Tptr], 0)
			gins(optoas(gc.OCMP, gc.Types[gc.Tptr]), &n1, &n2)
			p1 := gc.Gbranch(optoas(gc.OEQ, gc.Types[gc.Tptr]), nil, 0)

			n2 = n1
			n2.Op = gc.OINDREG
			n2.Type = gc.Types[gc.Simtype[gc.TINT]]
			gmove(&n2, &n1)

			gc.Patch(p1, gc.Pc)

			gmove(&n1, res)
			regfree(&n1)
			break
		}

		if gc.Istype(nl.Type, gc.TSTRING) || gc.Isslice(nl.Type) {
			// both slice and string have len one pointer into the struct.
			// a zero pointer means zero length
			var n1 gc.Node
			igen(nl, &n1, res)

			n1.Type = gc.Types[gc.Simtype[gc.TUINT]]
			n1.Xoffset += int64(gc.Array_nel)
			gmove(&n1, res)
			regfree(&n1)
			break
		}

		gc.Fatal("cgen: OLEN: unknown type %v", gc.Tconv(nl.Type, obj.FmtLong))

	case gc.OCAP:
		if gc.Istype(nl.Type, gc.TCHAN) {
			// chan has cap in the second int-sized word.
			// a zero pointer means zero length
			var n1 gc.Node
			regalloc(&n1, gc.Types[gc.Tptr], res)

			cgen(nl, &n1)

			var n2 gc.Node
			gc.Nodconst(&n2, gc.Types[gc.Tptr], 0)
			gins(optoas(gc.OCMP, gc.Types[gc.Tptr]), &n1, &n2)
			p1 := gc.Gbranch(optoas(gc.OEQ, gc.Types[gc.Tptr]), nil, 0)

			n2 = n1
			n2.Op = gc.OINDREG
			n2.Xoffset = int64(gc.Widthint)
			n2.Type = gc.Types[gc.Simtype[gc.TINT]]
			gmove(&n2, &n1)

			gc.Patch(p1, gc.Pc)

			gmove(&n1, res)
			regfree(&n1)
			break
		}

		if gc.Isslice(nl.Type) {
			var n1 gc.Node
			igen(nl, &n1, res)
			n1.Type = gc.Types[gc.Simtype[gc.TUINT]]
			n1.Xoffset += int64(gc.Array_cap)
			gmove(&n1, res)
			regfree(&n1)
			break
		}

		gc.Fatal("cgen: OCAP: unknown type %v", gc.Tconv(nl.Type, obj.FmtLong))

	case gc.OADDR:
		if n.Bounded { // let race detector avoid nil checks
			gc.Disable_checknil++
		}
		agen(nl, res)
		if n.Bounded {
			gc.Disable_checknil--
		}

	case gc.OCALLMETH:
		gc.Cgen_callmeth(n, 0)
		cgen_callret(n, res)

	case gc.OCALLINTER:
		cgen_callinter(n, res, 0)
		cgen_callret(n, res)

	case gc.OCALLFUNC:
		cgen_call(n, 0)
		cgen_callret(n, res)

	case gc.OMOD,
		gc.ODIV:
		if gc.Isfloat[n.Type.Etype] {
			a = optoas(int(n.Op), nl.Type)
			goto abop
		}

		if nl.Ullman >= nr.Ullman {
			var n1 gc.Node
			regalloc(&n1, nl.Type, res)
			cgen(nl, &n1)
			cgen_div(int(n.Op), &n1, nr, res)
			regfree(&n1)
		} else {
			var n2 gc.Node
			if !gc.Smallintconst(nr) {
				regalloc(&n2, nr.Type, res)
				cgen(nr, &n2)
			} else {
				n2 = *nr
			}

			cgen_div(int(n.Op), nl, &n2, res)
			if n2.Op != gc.OLITERAL {
				regfree(&n2)
			}
		}

	case gc.OLSH,
		gc.ORSH,
		gc.OLROT:
		cgen_shift(int(n.Op), n.Bounded, nl, nr, res)
	}

	return

	/*
	 * put simplest on right - we'll generate into left
	 * and then adjust it using the computation of right.
	 * constants and variables have the same ullman
	 * count, so look for constants specially.
	 *
	 * an integer constant we can use as an immediate
	 * is simpler than a variable - we can use the immediate
	 * in the adjustment instruction directly - so it goes
	 * on the right.
	 *
	 * other constants, like big integers or floating point
	 * constants, require a mov into a register, so those
	 * might as well go on the left, so we can reuse that
	 * register for the computation.
	 */
sbop: // symmetric binary
	if nl.Ullman < nr.Ullman || (nl.Ullman == nr.Ullman && (gc.Smallintconst(nl) || (nr.Op == gc.OLITERAL && !gc.Smallintconst(nr)))) {
		r := nl
		nl = nr
		nr = r
	}

abop: // asymmetric binary
	var n1 gc.Node
	var n2 gc.Node
	if nl.Ullman >= nr.Ullman {
		regalloc(&n1, nl.Type, res)
		cgen(nl, &n1)

		/*
			 * This generates smaller code - it avoids a MOV - but it's
			 * easily 10% slower due to not being able to
			 * optimize/manipulate the move.
			 * To see, run: go test -bench . crypto/md5
			 * with and without.
			 *
				if(sudoaddable(a, nr, &addr)) {
					p1 = gins(a, N, &n1);
					p1->from = addr;
					gmove(&n1, res);
					sudoclean();
					regfree(&n1);
					goto ret;
				}
			 *
		*/
		// TODO(minux): enable using constants directly in certain instructions.
		//if(smallintconst(nr))
		//	n2 = *nr;
		//else {
		regalloc(&n2, nr.Type, nil)

		cgen(nr, &n2)
	} else //}
	{
		//if(smallintconst(nr))
		//	n2 = *nr;
		//else {
		regalloc(&n2, nr.Type, res)

		cgen(nr, &n2)

		//}
		regalloc(&n1, nl.Type, nil)

		cgen(nl, &n1)
	}

	gins(a, &n2, &n1)

	// Normalize result for types smaller than word.
	if n.Type.Width < int64(gc.Widthreg) {
		switch n.Op {
		case gc.OADD,
			gc.OSUB,
			gc.OMUL,
			gc.OLSH:
			gins(optoas(gc.OAS, n.Type), &n1, &n1)
		}
	}

	gmove(&n1, res)
	regfree(&n1)
	if n2.Op != gc.OLITERAL {
		regfree(&n2)
	}
	return
}
Exemplo n.º 3
0
/*
 * generate:
 *	res = n;
 * simplifies and calls gmove.
 */
func cgen(n *gc.Node, res *gc.Node) {
	if gc.Debug['g'] != 0 {
		gc.Dump("\ncgen-n", n)
		gc.Dump("cgen-res", res)
	}

	if n == nil || n.Type == nil {
		return
	}

	if res == nil || res.Type == nil {
		gc.Fatal("cgen: res nil")
	}

	switch n.Op {
	case gc.OSLICE,
		gc.OSLICEARR,
		gc.OSLICESTR,
		gc.OSLICE3,
		gc.OSLICE3ARR:
		if res.Op != gc.ONAME || res.Addable == 0 {
			var n1 gc.Node
			gc.Tempname(&n1, n.Type)
			gc.Cgen_slice(n, &n1)
			cgen(&n1, res)
		} else {
			gc.Cgen_slice(n, res)
		}
		return

	case gc.OEFACE:
		if res.Op != gc.ONAME || res.Addable == 0 {
			var n1 gc.Node
			gc.Tempname(&n1, n.Type)
			gc.Cgen_eface(n, &n1)
			cgen(&n1, res)
		} else {
			gc.Cgen_eface(n, res)
		}
		return
	}

	for n.Op == gc.OCONVNOP {
		n = n.Left
	}

	if n.Ullman >= gc.UINF {
		if n.Op == gc.OINDREG {
			gc.Fatal("cgen: this is going to misscompile")
		}
		if res.Ullman >= gc.UINF {
			var n1 gc.Node
			gc.Tempname(&n1, n.Type)
			cgen(n, &n1)
			cgen(&n1, res)
			return
		}
	}

	if gc.Isfat(n.Type) {
		if n.Type.Width < 0 {
			gc.Fatal("forgot to compute width for %v", gc.Tconv(n.Type, 0))
		}
		sgen(n, res, n.Type.Width)
		return
	}

	// update addressability for string, slice
	// can't do in walk because n->left->addable
	// changes if n->left is an escaping local variable.
	switch n.Op {
	case gc.OSPTR,
		gc.OLEN:
		if gc.Isslice(n.Left.Type) || gc.Istype(n.Left.Type, gc.TSTRING) {
			n.Addable = n.Left.Addable
		}

	case gc.OCAP:
		if gc.Isslice(n.Left.Type) {
			n.Addable = n.Left.Addable
		}

	case gc.OITAB:
		n.Addable = n.Left.Addable
	}

	// if both are addressable, move
	if n.Addable != 0 && res.Addable != 0 {
		if gc.Is64(n.Type) || gc.Is64(res.Type) || n.Op == gc.OREGISTER || res.Op == gc.OREGISTER || gc.Iscomplex[n.Type.Etype] || gc.Iscomplex[res.Type.Etype] {
			gmove(n, res)
		} else {
			var n1 gc.Node
			regalloc(&n1, n.Type, nil)
			gmove(n, &n1)
			cgen(&n1, res)
			regfree(&n1)
		}

		return
	}

	// if both are not addressable, use a temporary.
	if n.Addable == 0 && res.Addable == 0 {
		// could use regalloc here sometimes,
		// but have to check for ullman >= UINF.
		var n1 gc.Node
		gc.Tempname(&n1, n.Type)

		cgen(n, &n1)
		cgen(&n1, res)
		return
	}

	// if result is not addressable directly but n is,
	// compute its address and then store via the address.
	if res.Addable == 0 {
		var n1 gc.Node
		igen(res, &n1, nil)
		cgen(n, &n1)
		regfree(&n1)
		return
	}

	if gc.Complexop(n, res) {
		gc.Complexgen(n, res)
		return
	}

	// if n is sudoaddable generate addr and move
	if !gc.Is64(n.Type) && !gc.Is64(res.Type) && !gc.Iscomplex[n.Type.Etype] && !gc.Iscomplex[res.Type.Etype] {
		a := optoas(gc.OAS, n.Type)
		var w int
		var addr obj.Addr
		if sudoaddable(a, n, &addr, &w) {
			if res.Op != gc.OREGISTER {
				var n2 gc.Node
				regalloc(&n2, res.Type, nil)
				p1 := gins(a, nil, &n2)
				p1.From = addr
				if gc.Debug['g'] != 0 {
					fmt.Printf("%v [ignore previous line]\n", p1)
				}
				gmove(&n2, res)
				regfree(&n2)
			} else {
				p1 := gins(a, nil, res)
				p1.From = addr
				if gc.Debug['g'] != 0 {
					fmt.Printf("%v [ignore previous line]\n", p1)
				}
			}

			sudoclean()
			return
		}
	}

	// otherwise, the result is addressable but n is not.
	// let's do some computation.

	nl := n.Left

	nr := n.Right

	if nl != nil && nl.Ullman >= gc.UINF {
		if nr != nil && nr.Ullman >= gc.UINF {
			var n1 gc.Node
			gc.Tempname(&n1, nl.Type)
			cgen(nl, &n1)
			n2 := *n
			n2.Left = &n1
			cgen(&n2, res)
			return
		}
	}

	// 64-bit ops are hard on 32-bit machine.
	if gc.Is64(n.Type) || gc.Is64(res.Type) || n.Left != nil && gc.Is64(n.Left.Type) {
		switch n.Op {
		// math goes to cgen64.
		case gc.OMINUS,
			gc.OCOM,
			gc.OADD,
			gc.OSUB,
			gc.OMUL,
			gc.OLROT,
			gc.OLSH,
			gc.ORSH,
			gc.OAND,
			gc.OOR,
			gc.OXOR:
			cgen64(n, res)

			return
		}
	}

	var a int
	var f0 gc.Node
	var n1 gc.Node
	var n2 gc.Node
	if nl != nil && gc.Isfloat[n.Type.Etype] && gc.Isfloat[nl.Type.Etype] {
		// floating-point.
		regalloc(&f0, nl.Type, res)

		if nr != nil {
			goto flt2
		}

		if n.Op == gc.OMINUS {
			nr = gc.Nodintconst(-1)
			gc.Convlit(&nr, n.Type)
			n.Op = gc.OMUL
			goto flt2
		}

		// unary
		cgen(nl, &f0)

		if n.Op != gc.OCONV && n.Op != gc.OPLUS {
			gins(optoas(int(n.Op), n.Type), &f0, &f0)
		}
		gmove(&f0, res)
		regfree(&f0)
		return
	}
	switch n.Op {
	default:
		gc.Dump("cgen", n)
		gc.Fatal("cgen: unknown op %v", gc.Nconv(n, obj.FmtShort|obj.FmtSign))

	case gc.OREAL,
		gc.OIMAG,
		gc.OCOMPLEX:
		gc.Fatal("unexpected complex")

		// these call bgen to get a bool value
	case gc.OOROR,
		gc.OANDAND,
		gc.OEQ,
		gc.ONE,
		gc.OLT,
		gc.OLE,
		gc.OGE,
		gc.OGT,
		gc.ONOT:
		p1 := gc.Gbranch(arm.AB, nil, 0)

		p2 := gc.Pc
		gmove(gc.Nodbool(true), res)
		p3 := gc.Gbranch(arm.AB, nil, 0)
		gc.Patch(p1, gc.Pc)
		bgen(n, true, 0, p2)
		gmove(gc.Nodbool(false), res)
		gc.Patch(p3, gc.Pc)
		return

	case gc.OPLUS:
		cgen(nl, res)
		return

		// unary
	case gc.OCOM:
		a := optoas(gc.OXOR, nl.Type)

		regalloc(&n1, nl.Type, nil)
		cgen(nl, &n1)
		gc.Nodconst(&n2, nl.Type, -1)
		gins(a, &n2, &n1)
		goto norm

	case gc.OMINUS:
		regalloc(&n1, nl.Type, nil)
		cgen(nl, &n1)
		gc.Nodconst(&n2, nl.Type, 0)
		gins(optoas(gc.OMINUS, nl.Type), &n2, &n1)
		goto norm

		// symmetric binary
	case gc.OAND,
		gc.OOR,
		gc.OXOR,
		gc.OADD,
		gc.OMUL:
		a = optoas(int(n.Op), nl.Type)

		// symmetric binary
		if nl.Ullman < nr.Ullman {
			r := nl
			nl = nr
			nr = r
		}
		goto abop

		// asymmetric binary
	case gc.OSUB:
		a = optoas(int(n.Op), nl.Type)

		goto abop

	case gc.OHMUL:
		cgen_hmul(nl, nr, res)

	case gc.OLROT,
		gc.OLSH,
		gc.ORSH:
		cgen_shift(int(n.Op), n.Bounded, nl, nr, res)

	case gc.OCONV:
		if gc.Eqtype(n.Type, nl.Type) || gc.Noconv(n.Type, nl.Type) {
			cgen(nl, res)
			break
		}

		var n1 gc.Node
		if nl.Addable != 0 && !gc.Is64(nl.Type) {
			regalloc(&n1, nl.Type, res)
			gmove(nl, &n1)
		} else {
			if n.Type.Width > int64(gc.Widthptr) || gc.Is64(nl.Type) || gc.Isfloat[nl.Type.Etype] {
				gc.Tempname(&n1, nl.Type)
			} else {
				regalloc(&n1, nl.Type, res)
			}
			cgen(nl, &n1)
		}

		var n2 gc.Node
		if n.Type.Width > int64(gc.Widthptr) || gc.Is64(n.Type) || gc.Isfloat[n.Type.Etype] {
			gc.Tempname(&n2, n.Type)
		} else {
			regalloc(&n2, n.Type, nil)
		}
		gmove(&n1, &n2)
		gmove(&n2, res)
		if n1.Op == gc.OREGISTER {
			regfree(&n1)
		}
		if n2.Op == gc.OREGISTER {
			regfree(&n2)
		}

	case gc.ODOT,
		gc.ODOTPTR,
		gc.OINDEX,
		gc.OIND,
		gc.ONAME: // PHEAP or PPARAMREF var
		var n1 gc.Node
		igen(n, &n1, res)

		gmove(&n1, res)
		regfree(&n1)

		// interface table is first word of interface value
	case gc.OITAB:
		var n1 gc.Node
		igen(nl, &n1, res)

		n1.Type = n.Type
		gmove(&n1, res)
		regfree(&n1)

		// pointer is the first word of string or slice.
	case gc.OSPTR:
		if gc.Isconst(nl, gc.CTSTR) {
			var n1 gc.Node
			regalloc(&n1, gc.Types[gc.Tptr], res)
			p1 := gins(arm.AMOVW, nil, &n1)
			gc.Datastring(nl.Val.U.Sval, &p1.From)
			gmove(&n1, res)
			regfree(&n1)
			break
		}

		var n1 gc.Node
		igen(nl, &n1, res)
		n1.Type = n.Type
		gmove(&n1, res)
		regfree(&n1)

	case gc.OLEN:
		if gc.Istype(nl.Type, gc.TMAP) || gc.Istype(nl.Type, gc.TCHAN) {
			// map has len in the first 32-bit word.
			// a zero pointer means zero length
			var n1 gc.Node
			regalloc(&n1, gc.Types[gc.Tptr], res)

			cgen(nl, &n1)

			var n2 gc.Node
			gc.Nodconst(&n2, gc.Types[gc.Tptr], 0)
			gcmp(optoas(gc.OCMP, gc.Types[gc.Tptr]), &n1, &n2)
			p1 := gc.Gbranch(optoas(gc.OEQ, gc.Types[gc.Tptr]), nil, -1)

			n2 = n1
			n2.Op = gc.OINDREG
			n2.Type = gc.Types[gc.TINT32]
			gmove(&n2, &n1)

			gc.Patch(p1, gc.Pc)

			gmove(&n1, res)
			regfree(&n1)
			break
		}

		if gc.Istype(nl.Type, gc.TSTRING) || gc.Isslice(nl.Type) {
			// both slice and string have len one pointer into the struct.
			var n1 gc.Node
			igen(nl, &n1, res)

			n1.Type = gc.Types[gc.TUINT32]
			n1.Xoffset += int64(gc.Array_nel)
			gmove(&n1, res)
			regfree(&n1)
			break
		}

		gc.Fatal("cgen: OLEN: unknown type %v", gc.Tconv(nl.Type, obj.FmtLong))

	case gc.OCAP:
		if gc.Istype(nl.Type, gc.TCHAN) {
			// chan has cap in the second 32-bit word.
			// a zero pointer means zero length
			var n1 gc.Node
			regalloc(&n1, gc.Types[gc.Tptr], res)

			cgen(nl, &n1)

			var n2 gc.Node
			gc.Nodconst(&n2, gc.Types[gc.Tptr], 0)
			gcmp(optoas(gc.OCMP, gc.Types[gc.Tptr]), &n1, &n2)
			p1 := gc.Gbranch(optoas(gc.OEQ, gc.Types[gc.Tptr]), nil, -1)

			n2 = n1
			n2.Op = gc.OINDREG
			n2.Xoffset = 4
			n2.Type = gc.Types[gc.TINT32]
			gmove(&n2, &n1)

			gc.Patch(p1, gc.Pc)

			gmove(&n1, res)
			regfree(&n1)
			break
		}

		if gc.Isslice(nl.Type) {
			var n1 gc.Node
			igen(nl, &n1, res)
			n1.Type = gc.Types[gc.TUINT32]
			n1.Xoffset += int64(gc.Array_cap)
			gmove(&n1, res)
			regfree(&n1)
			break
		}

		gc.Fatal("cgen: OCAP: unknown type %v", gc.Tconv(nl.Type, obj.FmtLong))

	case gc.OADDR:
		agen(nl, res)

		// Release res so that it is available for cgen_call.
	// Pick it up again after the call.
	case gc.OCALLMETH,
		gc.OCALLFUNC:
		rg := -1

		if n.Ullman >= gc.UINF {
			if res != nil && (res.Op == gc.OREGISTER || res.Op == gc.OINDREG) {
				rg = int(res.Val.U.Reg)
				reg[rg]--
			}
		}

		if n.Op == gc.OCALLMETH {
			gc.Cgen_callmeth(n, 0)
		} else {
			cgen_call(n, 0)
		}
		if rg >= 0 {
			reg[rg]++
		}
		cgen_callret(n, res)

	case gc.OCALLINTER:
		cgen_callinter(n, res, 0)
		cgen_callret(n, res)

	case gc.OMOD,
		gc.ODIV:
		a = optoas(int(n.Op), nl.Type)
		goto abop
	}

	return

	// TODO(kaib): use fewer registers here.
abop: // asymmetric binary
	if nl.Ullman >= nr.Ullman {
		regalloc(&n1, nl.Type, res)
		cgen(nl, &n1)
		switch n.Op {
		case gc.OADD,
			gc.OSUB,
			gc.OAND,
			gc.OOR,
			gc.OXOR:
			if gc.Smallintconst(nr) {
				n2 = *nr
				break
			}
			fallthrough

		default:
			regalloc(&n2, nr.Type, nil)
			cgen(nr, &n2)
		}
	} else {
		switch n.Op {
		case gc.OADD,
			gc.OSUB,
			gc.OAND,
			gc.OOR,
			gc.OXOR:
			if gc.Smallintconst(nr) {
				n2 = *nr
				break
			}
			fallthrough

		default:
			regalloc(&n2, nr.Type, res)
			cgen(nr, &n2)
		}

		regalloc(&n1, nl.Type, nil)
		cgen(nl, &n1)
	}

	gins(a, &n2, &n1)

	// Normalize result for types smaller than word.
norm:
	if n.Type.Width < int64(gc.Widthptr) {
		switch n.Op {
		case gc.OADD,
			gc.OSUB,
			gc.OMUL,
			gc.OCOM,
			gc.OMINUS:
			gins(optoas(gc.OAS, n.Type), &n1, &n1)
		}
	}

	gmove(&n1, res)
	regfree(&n1)
	if n2.Op != gc.OLITERAL {
		regfree(&n2)
	}
	return

flt2: // binary
	var f1 gc.Node
	if nl.Ullman >= nr.Ullman {
		cgen(nl, &f0)
		regalloc(&f1, n.Type, nil)
		gmove(&f0, &f1)
		cgen(nr, &f0)
		gins(optoas(int(n.Op), n.Type), &f0, &f1)
	} else {
		cgen(nr, &f0)
		regalloc(&f1, n.Type, nil)
		cgen(nl, &f1)
		gins(optoas(int(n.Op), n.Type), &f0, &f1)
	}

	gmove(&f1, res)
	regfree(&f0)
	regfree(&f1)
	return
}
Exemplo n.º 4
0
/*
 * copy a composite value by moving its individual components.
 * Slices, strings and interfaces are supported.
 * Small structs or arrays with elements of basic type are
 * also supported.
 * nr is N when assigning a zero value.
 * return 1 if can do, 0 if can't.
 */
func componentgen(nr *gc.Node, nl *gc.Node) bool {
	var nodl gc.Node
	var nodr gc.Node

	freel := 0
	freer := 0

	switch nl.Type.Etype {
	default:
		goto no

	case gc.TARRAY:
		t := nl.Type

		// Slices are ok.
		if gc.Isslice(t) {
			break
		}

		// Small arrays are ok.
		if t.Bound > 0 && t.Bound <= 3 && !gc.Isfat(t.Type) {
			break
		}

		goto no

		// Small structs with non-fat types are ok.
	// Zero-sized structs are treated separately elsewhere.
	case gc.TSTRUCT:
		fldcount := int64(0)

		for t := nl.Type.Type; t != nil; t = t.Down {
			if gc.Isfat(t.Type) {
				goto no
			}
			if t.Etype != gc.TFIELD {
				gc.Fatal("componentgen: not a TFIELD: %v", gc.Tconv(t, obj.FmtLong))
			}
			fldcount++
		}

		if fldcount == 0 || fldcount > 4 {
			goto no
		}

	case gc.TSTRING,
		gc.TINTER:
		break
	}

	nodl = *nl
	if !cadable(nl) {
		if nr != nil && !cadable(nr) {
			goto no
		}
		igen(nl, &nodl, nil)
		freel = 1
	}

	if nr != nil {
		nodr = *nr
		if !cadable(nr) {
			igen(nr, &nodr, nil)
			freer = 1
		}
	} else {
		// When zeroing, prepare a register containing zero.
		var tmp gc.Node
		gc.Nodconst(&tmp, nl.Type, 0)

		regalloc(&nodr, gc.Types[gc.TUINT], nil)
		gmove(&tmp, &nodr)
		freer = 1
	}

	// nl and nr are 'cadable' which basically means they are names (variables) now.
	// If they are the same variable, don't generate any code, because the
	// VARDEF we generate will mark the old value as dead incorrectly.
	// (And also the assignments are useless.)
	if nr != nil && nl.Op == gc.ONAME && nr.Op == gc.ONAME && nl == nr {
		goto yes
	}

	switch nl.Type.Etype {
	// componentgen for arrays.
	case gc.TARRAY:
		if nl.Op == gc.ONAME {
			gc.Gvardef(nl)
		}
		t := nl.Type
		if !gc.Isslice(t) {
			nodl.Type = t.Type
			nodr.Type = nodl.Type
			for fldcount := int64(0); fldcount < t.Bound; fldcount++ {
				if nr == nil {
					gc.Clearslim(&nodl)
				} else {
					gmove(&nodr, &nodl)
				}
				nodl.Xoffset += t.Type.Width
				nodr.Xoffset += t.Type.Width
			}

			goto yes
		}

		// componentgen for slices.
		nodl.Xoffset += int64(gc.Array_array)

		nodl.Type = gc.Ptrto(nl.Type.Type)

		if nr != nil {
			nodr.Xoffset += int64(gc.Array_array)
			nodr.Type = nodl.Type
		}

		gmove(&nodr, &nodl)

		nodl.Xoffset += int64(gc.Array_nel) - int64(gc.Array_array)
		nodl.Type = gc.Types[gc.Simtype[gc.TUINT]]

		if nr != nil {
			nodr.Xoffset += int64(gc.Array_nel) - int64(gc.Array_array)
			nodr.Type = nodl.Type
		}

		gmove(&nodr, &nodl)

		nodl.Xoffset += int64(gc.Array_cap) - int64(gc.Array_nel)
		nodl.Type = gc.Types[gc.Simtype[gc.TUINT]]

		if nr != nil {
			nodr.Xoffset += int64(gc.Array_cap) - int64(gc.Array_nel)
			nodr.Type = nodl.Type
		}

		gmove(&nodr, &nodl)

		goto yes

	case gc.TSTRING:
		if nl.Op == gc.ONAME {
			gc.Gvardef(nl)
		}
		nodl.Xoffset += int64(gc.Array_array)
		nodl.Type = gc.Ptrto(gc.Types[gc.TUINT8])

		if nr != nil {
			nodr.Xoffset += int64(gc.Array_array)
			nodr.Type = nodl.Type
		}

		gmove(&nodr, &nodl)

		nodl.Xoffset += int64(gc.Array_nel) - int64(gc.Array_array)
		nodl.Type = gc.Types[gc.Simtype[gc.TUINT]]

		if nr != nil {
			nodr.Xoffset += int64(gc.Array_nel) - int64(gc.Array_array)
			nodr.Type = nodl.Type
		}

		gmove(&nodr, &nodl)

		goto yes

	case gc.TINTER:
		if nl.Op == gc.ONAME {
			gc.Gvardef(nl)
		}
		nodl.Xoffset += int64(gc.Array_array)
		nodl.Type = gc.Ptrto(gc.Types[gc.TUINT8])

		if nr != nil {
			nodr.Xoffset += int64(gc.Array_array)
			nodr.Type = nodl.Type
		}

		gmove(&nodr, &nodl)

		nodl.Xoffset += int64(gc.Array_nel) - int64(gc.Array_array)
		nodl.Type = gc.Ptrto(gc.Types[gc.TUINT8])

		if nr != nil {
			nodr.Xoffset += int64(gc.Array_nel) - int64(gc.Array_array)
			nodr.Type = nodl.Type
		}

		gmove(&nodr, &nodl)

		goto yes

	case gc.TSTRUCT:
		if nl.Op == gc.ONAME {
			gc.Gvardef(nl)
		}
		loffset := nodl.Xoffset
		roffset := nodr.Xoffset

		// funarg structs may not begin at offset zero.
		if nl.Type.Etype == gc.TSTRUCT && nl.Type.Funarg != 0 && nl.Type.Type != nil {
			loffset -= nl.Type.Type.Width
		}
		if nr != nil && nr.Type.Etype == gc.TSTRUCT && nr.Type.Funarg != 0 && nr.Type.Type != nil {
			roffset -= nr.Type.Type.Width
		}

		for t := nl.Type.Type; t != nil; t = t.Down {
			nodl.Xoffset = loffset + t.Width
			nodl.Type = t.Type

			if nr == nil {
				gc.Clearslim(&nodl)
			} else {
				nodr.Xoffset = roffset + t.Width
				nodr.Type = nodl.Type
				gmove(&nodr, &nodl)
			}
		}

		goto yes
	}

no:
	if freer != 0 {
		regfree(&nodr)
	}
	if freel != 0 {
		regfree(&nodl)
	}
	return false

yes:
	if freer != 0 {
		regfree(&nodr)
	}
	if freel != 0 {
		regfree(&nodl)
	}
	return true
}
Exemplo n.º 5
0
/*
 * generate:
 *	res = n;
 * simplifies and calls gmove.
 *
 * TODO:
 *	sudoaddable
 */
func cgen(n *gc.Node, res *gc.Node) {
	if gc.Debug['g'] != 0 {
		gc.Dump("\ncgen-n", n)
		gc.Dump("cgen-res", res)
	}

	if n == nil || n.Type == nil {
		gc.Fatal("cgen: n nil")
	}
	if res == nil || res.Type == nil {
		gc.Fatal("cgen: res nil")
	}

	switch n.Op {
	case gc.OSLICE,
		gc.OSLICEARR,
		gc.OSLICESTR,
		gc.OSLICE3,
		gc.OSLICE3ARR:
		if res.Op != gc.ONAME || res.Addable == 0 {
			var n1 gc.Node
			gc.Tempname(&n1, n.Type)
			gc.Cgen_slice(n, &n1)
			cgen(&n1, res)
		} else {
			gc.Cgen_slice(n, res)
		}
		return

	case gc.OEFACE:
		if res.Op != gc.ONAME || res.Addable == 0 {
			var n1 gc.Node
			gc.Tempname(&n1, n.Type)
			gc.Cgen_eface(n, &n1)
			cgen(&n1, res)
		} else {
			gc.Cgen_eface(n, res)
		}
		return
	}

	for n.Op == gc.OCONVNOP {
		n = n.Left
	}

	// function calls on both sides?  introduce temporary
	if n.Ullman >= gc.UINF && res.Ullman >= gc.UINF {
		var n1 gc.Node
		gc.Tempname(&n1, n.Type)
		cgen(n, &n1)
		cgen(&n1, res)
		return
	}

	// structs etc get handled specially
	if gc.Isfat(n.Type) {
		if n.Type.Width < 0 {
			gc.Fatal("forgot to compute width for %v", gc.Tconv(n.Type, 0))
		}
		sgen(n, res, n.Type.Width)
		return
	}

	// update addressability for string, slice
	// can't do in walk because n->left->addable
	// changes if n->left is an escaping local variable.
	switch n.Op {
	case gc.OSPTR,
		gc.OLEN:
		if gc.Isslice(n.Left.Type) || gc.Istype(n.Left.Type, gc.TSTRING) {
			n.Addable = n.Left.Addable
		}

	case gc.OCAP:
		if gc.Isslice(n.Left.Type) {
			n.Addable = n.Left.Addable
		}

	case gc.OITAB:
		n.Addable = n.Left.Addable
	}

	// if both are addressable, move
	if n.Addable != 0 && res.Addable != 0 {
		gmove(n, res)
		return
	}

	// if both are not addressable, use a temporary.
	if n.Addable == 0 && res.Addable == 0 {
		// could use regalloc here sometimes,
		// but have to check for ullman >= UINF.
		var n1 gc.Node
		gc.Tempname(&n1, n.Type)

		cgen(n, &n1)
		cgen(&n1, res)
		return
	}

	// if result is not addressable directly but n is,
	// compute its address and then store via the address.
	if res.Addable == 0 {
		var n1 gc.Node
		igen(res, &n1, nil)
		cgen(n, &n1)
		regfree(&n1)
		return
	}

	// complex types
	if gc.Complexop(n, res) {
		gc.Complexgen(n, res)
		return
	}

	// otherwise, the result is addressable but n is not.
	// let's do some computation.

	// use ullman to pick operand to eval first.
	nl := n.Left

	nr := n.Right
	if nl != nil && nl.Ullman >= gc.UINF {
		if nr != nil && nr.Ullman >= gc.UINF {
			// both are hard
			var n1 gc.Node
			gc.Tempname(&n1, nl.Type)

			cgen(nl, &n1)
			n2 := *n
			n2.Left = &n1
			cgen(&n2, res)
			return
		}
	}

	// 64-bit ops are hard on 32-bit machine.
	if gc.Is64(n.Type) || gc.Is64(res.Type) || n.Left != nil && gc.Is64(n.Left.Type) {
		switch n.Op {
		// math goes to cgen64.
		case gc.OMINUS,
			gc.OCOM,
			gc.OADD,
			gc.OSUB,
			gc.OMUL,
			gc.OLROT,
			gc.OLSH,
			gc.ORSH,
			gc.OAND,
			gc.OOR,
			gc.OXOR:
			cgen64(n, res)

			return
		}
	}

	if nl != nil && gc.Isfloat[n.Type.Etype] && gc.Isfloat[nl.Type.Etype] {
		cgen_float(n, res)
		return
	}

	var a int
	switch n.Op {
	default:
		gc.Dump("cgen", n)
		gc.Fatal("cgen %v", gc.Oconv(int(n.Op), 0))

	case gc.OREAL,
		gc.OIMAG,
		gc.OCOMPLEX:
		gc.Fatal("unexpected complex")
		return

		// these call bgen to get a bool value
	case gc.OOROR,
		gc.OANDAND,
		gc.OEQ,
		gc.ONE,
		gc.OLT,
		gc.OLE,
		gc.OGE,
		gc.OGT,
		gc.ONOT:
		p1 := gc.Gbranch(obj.AJMP, nil, 0)

		p2 := gc.Pc
		gmove(gc.Nodbool(true), res)
		p3 := gc.Gbranch(obj.AJMP, nil, 0)
		gc.Patch(p1, gc.Pc)
		bgen(n, true, 0, p2)
		gmove(gc.Nodbool(false), res)
		gc.Patch(p3, gc.Pc)
		return

	case gc.OPLUS:
		cgen(nl, res)
		return

	case gc.OMINUS,
		gc.OCOM:
		a := optoas(int(n.Op), nl.Type)
		// unary
		var n1 gc.Node
		gc.Tempname(&n1, nl.Type)

		cgen(nl, &n1)
		gins(a, nil, &n1)
		gmove(&n1, res)
		return

		// symmetric binary
	case gc.OAND,
		gc.OOR,
		gc.OXOR,
		gc.OADD,
		gc.OMUL:
		a = optoas(int(n.Op), nl.Type)

		if a == x86.AIMULB {
			cgen_bmul(int(n.Op), nl, nr, res)
			break
		}

		// symmetric binary
		if nl.Ullman < nr.Ullman || nl.Op == gc.OLITERAL {
			r := nl
			nl = nr
			nr = r
		}
		goto abop

		// asymmetric binary
	case gc.OSUB:
		a = optoas(int(n.Op), nl.Type)

		goto abop

	case gc.OHMUL:
		cgen_hmul(nl, nr, res)

	case gc.OCONV:
		if gc.Eqtype(n.Type, nl.Type) || gc.Noconv(n.Type, nl.Type) {
			cgen(nl, res)
			break
		}

		var n2 gc.Node
		gc.Tempname(&n2, n.Type)
		var n1 gc.Node
		mgen(nl, &n1, res)
		gmove(&n1, &n2)
		gmove(&n2, res)
		mfree(&n1)

	case gc.ODOT,
		gc.ODOTPTR,
		gc.OINDEX,
		gc.OIND,
		gc.ONAME: // PHEAP or PPARAMREF var
		var n1 gc.Node
		igen(n, &n1, res)

		gmove(&n1, res)
		regfree(&n1)

	case gc.OITAB:
		var n1 gc.Node
		igen(nl, &n1, res)
		n1.Type = gc.Ptrto(gc.Types[gc.TUINTPTR])
		gmove(&n1, res)
		regfree(&n1)

		// pointer is the first word of string or slice.
	case gc.OSPTR:
		if gc.Isconst(nl, gc.CTSTR) {
			var n1 gc.Node
			regalloc(&n1, gc.Types[gc.Tptr], res)
			p1 := gins(x86.ALEAL, nil, &n1)
			gc.Datastring(nl.Val.U.Sval, &p1.From)
			gmove(&n1, res)
			regfree(&n1)
			break
		}

		var n1 gc.Node
		igen(nl, &n1, res)
		n1.Type = n.Type
		gmove(&n1, res)
		regfree(&n1)

	case gc.OLEN:
		if gc.Istype(nl.Type, gc.TMAP) || gc.Istype(nl.Type, gc.TCHAN) {
			// map has len in the first 32-bit word.
			// a zero pointer means zero length
			var n1 gc.Node
			gc.Tempname(&n1, gc.Types[gc.Tptr])

			cgen(nl, &n1)
			var n2 gc.Node
			regalloc(&n2, gc.Types[gc.Tptr], nil)
			gmove(&n1, &n2)
			n1 = n2

			gc.Nodconst(&n2, gc.Types[gc.Tptr], 0)
			gins(optoas(gc.OCMP, gc.Types[gc.Tptr]), &n1, &n2)
			p1 := gc.Gbranch(optoas(gc.OEQ, gc.Types[gc.Tptr]), nil, -1)

			n2 = n1
			n2.Op = gc.OINDREG
			n2.Type = gc.Types[gc.TINT32]
			gmove(&n2, &n1)

			gc.Patch(p1, gc.Pc)

			gmove(&n1, res)
			regfree(&n1)
			break
		}

		if gc.Istype(nl.Type, gc.TSTRING) || gc.Isslice(nl.Type) {
			// both slice and string have len one pointer into the struct.
			var n1 gc.Node
			igen(nl, &n1, res)

			n1.Type = gc.Types[gc.TUINT32]
			n1.Xoffset += int64(gc.Array_nel)
			gmove(&n1, res)
			regfree(&n1)
			break
		}

		gc.Fatal("cgen: OLEN: unknown type %v", gc.Tconv(nl.Type, obj.FmtLong))

	case gc.OCAP:
		if gc.Istype(nl.Type, gc.TCHAN) {
			// chan has cap in the second 32-bit word.
			// a zero pointer means zero length
			var n1 gc.Node
			gc.Tempname(&n1, gc.Types[gc.Tptr])

			cgen(nl, &n1)
			var n2 gc.Node
			regalloc(&n2, gc.Types[gc.Tptr], nil)
			gmove(&n1, &n2)
			n1 = n2

			gc.Nodconst(&n2, gc.Types[gc.Tptr], 0)
			gins(optoas(gc.OCMP, gc.Types[gc.Tptr]), &n1, &n2)
			p1 := gc.Gbranch(optoas(gc.OEQ, gc.Types[gc.Tptr]), nil, -1)

			n2 = n1
			n2.Op = gc.OINDREG
			n2.Xoffset = 4
			n2.Type = gc.Types[gc.TINT32]
			gmove(&n2, &n1)

			gc.Patch(p1, gc.Pc)

			gmove(&n1, res)
			regfree(&n1)
			break
		}

		if gc.Isslice(nl.Type) {
			var n1 gc.Node
			igen(nl, &n1, res)
			n1.Type = gc.Types[gc.TUINT32]
			n1.Xoffset += int64(gc.Array_cap)
			gmove(&n1, res)
			regfree(&n1)
			break
		}

		gc.Fatal("cgen: OCAP: unknown type %v", gc.Tconv(nl.Type, obj.FmtLong))

	case gc.OADDR:
		agen(nl, res)

	case gc.OCALLMETH:
		gc.Cgen_callmeth(n, 0)
		cgen_callret(n, res)

	case gc.OCALLINTER:
		cgen_callinter(n, res, 0)
		cgen_callret(n, res)

	case gc.OCALLFUNC:
		cgen_call(n, 0)
		cgen_callret(n, res)

	case gc.OMOD,
		gc.ODIV:
		cgen_div(int(n.Op), nl, nr, res)

	case gc.OLSH,
		gc.ORSH,
		gc.OLROT:
		cgen_shift(int(n.Op), n.Bounded, nl, nr, res)
	}

	return

abop: // asymmetric binary
	if gc.Smallintconst(nr) {
		var n1 gc.Node
		mgen(nl, &n1, res)
		var n2 gc.Node
		regalloc(&n2, nl.Type, &n1)
		gmove(&n1, &n2)
		gins(a, nr, &n2)
		gmove(&n2, res)
		regfree(&n2)
		mfree(&n1)
	} else if nl.Ullman >= nr.Ullman {
		var nt gc.Node
		gc.Tempname(&nt, nl.Type)
		cgen(nl, &nt)
		var n2 gc.Node
		mgen(nr, &n2, nil)
		var n1 gc.Node
		regalloc(&n1, nl.Type, res)
		gmove(&nt, &n1)
		gins(a, &n2, &n1)
		gmove(&n1, res)
		regfree(&n1)
		mfree(&n2)
	} else {
		var n2 gc.Node
		regalloc(&n2, nr.Type, res)
		cgen(nr, &n2)
		var n1 gc.Node
		regalloc(&n1, nl.Type, nil)
		cgen(nl, &n1)
		gins(a, &n2, &n1)
		regfree(&n2)
		gmove(&n1, res)
		regfree(&n1)
	}

	return
}