/* * generate floating-point operation. */ func cgen_float(n *gc.Node, res *gc.Node) { 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(true), res) p3 := gc.Gbranch(obj.AJMP, nil, 0) gc.Patch(p1, gc.Pc) gc.Bgen(n, true, 0, p2) gmove(gc.Nodbool(false), res) gc.Patch(p3, gc.Pc) return case gc.OPLUS: gc.Cgen(nl, res) return case gc.OCONV: if gc.Eqtype(n.Type, nl.Type) || gc.Noconv(n.Type, nl.Type) { gc.Cgen(nl, res) return } var n2 gc.Node gc.Tempname(&n2, n.Type) var n1 gc.Node gc.Mgen(nl, &n1, res) gmove(&n1, &n2) gmove(&n2, res) gc.Mfree(&n1) return } if gc.Thearch.Use387 { cgen_float387(n, res) } else { cgen_floatsse(n, res) } }
/* * generate: * if(n == true) goto to; */ func bgen(n *gc.Node, true_ bool, likely int, to *obj.Prog) { if gc.Debug['g'] != 0 { gc.Dump("\nbgen", n) } if n == nil { n = gc.Nodbool(true) } if n.Ninit != nil { gc.Genlist(n.Ninit) } if n.Type == nil { gc.Convlit(&n, gc.Types[gc.TBOOL]) if n.Type == nil { return } } et := int(n.Type.Etype) if et != gc.TBOOL { gc.Yyerror("cgen: bad type %v for %v", gc.Tconv(n.Type, 0), gc.Oconv(int(n.Op), 0)) gc.Patch(gins(obj.AEND, nil, nil), to) return } var nr *gc.Node for n.Op == gc.OCONVNOP { n = n.Left if n.Ninit != nil { gc.Genlist(n.Ninit) } } var nl *gc.Node switch n.Op { default: var n1 gc.Node regalloc(&n1, n.Type, nil) cgen(n, &n1) var n2 gc.Node gc.Nodconst(&n2, n.Type, 0) gins(optoas(gc.OCMP, n.Type), &n1, &n2) a := ppc64.ABNE if !true_ { a = ppc64.ABEQ } gc.Patch(gc.Gbranch(a, n.Type, likely), to) regfree(&n1) return // need to ask if it is bool? case gc.OLITERAL: if !true_ == (n.Val.U.Bval == 0) { gc.Patch(gc.Gbranch(ppc64.ABR, nil, likely), to) } return case gc.OANDAND, gc.OOROR: if (n.Op == gc.OANDAND) == true_ { p1 := gc.Gbranch(obj.AJMP, nil, 0) p2 := gc.Gbranch(obj.AJMP, nil, 0) gc.Patch(p1, gc.Pc) bgen(n.Left, !true_, -likely, p2) bgen(n.Right, !true_, -likely, p2) p1 = gc.Gbranch(obj.AJMP, nil, 0) gc.Patch(p1, to) gc.Patch(p2, gc.Pc) } else { bgen(n.Left, true_, likely, to) bgen(n.Right, true_, likely, to) } return case gc.OEQ, gc.ONE, gc.OLT, gc.OGT, gc.OLE, gc.OGE: nr = n.Right if nr == nil || nr.Type == nil { return } fallthrough case gc.ONOT: // unary nl = n.Left if nl == nil || nl.Type == nil { return } } switch n.Op { case gc.ONOT: bgen(nl, !true_, likely, to) return case gc.OEQ, gc.ONE, gc.OLT, gc.OGT, gc.OLE, gc.OGE: a := int(n.Op) if !true_ { if gc.Isfloat[nr.Type.Etype] { // brcom is not valid on floats when NaN is involved. p1 := gc.Gbranch(ppc64.ABR, nil, 0) p2 := gc.Gbranch(ppc64.ABR, nil, 0) gc.Patch(p1, gc.Pc) ll := n.Ninit // avoid re-genning ninit n.Ninit = nil bgen(n, true, -likely, p2) n.Ninit = ll gc.Patch(gc.Gbranch(ppc64.ABR, nil, 0), to) gc.Patch(p2, gc.Pc) return } a = gc.Brcom(a) true_ = !true_ } // make simplest on right if nl.Op == gc.OLITERAL || (nl.Ullman < nr.Ullman && nl.Ullman < gc.UINF) { a = gc.Brrev(a) r := nl nl = nr nr = r } if gc.Isslice(nl.Type) { // front end should only leave cmp to literal nil if (a != gc.OEQ && a != gc.ONE) || nr.Op != gc.OLITERAL { gc.Yyerror("illegal slice comparison") break } a = optoas(a, gc.Types[gc.Tptr]) var n1 gc.Node igen(nl, &n1, nil) n1.Xoffset += int64(gc.Array_array) n1.Type = gc.Types[gc.Tptr] var tmp gc.Node gc.Nodconst(&tmp, gc.Types[gc.Tptr], 0) var n2 gc.Node regalloc(&n2, gc.Types[gc.Tptr], &n1) gmove(&n1, &n2) gins(optoas(gc.OCMP, gc.Types[gc.Tptr]), &n2, &tmp) regfree(&n2) gc.Patch(gc.Gbranch(a, gc.Types[gc.Tptr], likely), to) regfree(&n1) break } if gc.Isinter(nl.Type) { // front end should only leave cmp to literal nil if (a != gc.OEQ && a != gc.ONE) || nr.Op != gc.OLITERAL { gc.Yyerror("illegal interface comparison") break } a = optoas(a, gc.Types[gc.Tptr]) var n1 gc.Node igen(nl, &n1, nil) n1.Type = gc.Types[gc.Tptr] var tmp gc.Node gc.Nodconst(&tmp, gc.Types[gc.Tptr], 0) var n2 gc.Node regalloc(&n2, gc.Types[gc.Tptr], &n1) gmove(&n1, &n2) gins(optoas(gc.OCMP, gc.Types[gc.Tptr]), &n2, &tmp) regfree(&n2) gc.Patch(gc.Gbranch(a, gc.Types[gc.Tptr], likely), to) regfree(&n1) break } if gc.Iscomplex[nl.Type.Etype] { gc.Complexbool(a, nl, nr, true_, likely, to) break } var n1 gc.Node var n2 gc.Node if nr.Ullman >= gc.UINF { regalloc(&n1, nl.Type, nil) cgen(nl, &n1) var tmp gc.Node gc.Tempname(&tmp, nl.Type) gmove(&n1, &tmp) regfree(&n1) regalloc(&n2, nr.Type, nil) cgen(nr, &n2) regalloc(&n1, nl.Type, nil) cgen(&tmp, &n1) goto cmp } regalloc(&n1, nl.Type, nil) cgen(nl, &n1) // TODO(minux): cmpi does accept 16-bit signed immediate as p->to. // and cmpli accepts 16-bit unsigned immediate. //if(smallintconst(nr)) { // gins(optoas(OCMP, nr->type), &n1, nr); // patch(gbranch(optoas(a, nr->type), nr->type, likely), to); // regfree(&n1); // break; //} regalloc(&n2, nr.Type, nil) cgen(nr, &n2) cmp: l := &n1 r := &n2 gins(optoas(gc.OCMP, nr.Type), l, r) if gc.Isfloat[nr.Type.Etype] && (a == gc.OLE || a == gc.OGE) { // To get NaN right, must rewrite x <= y into separate x < y or x = y. switch a { case gc.OLE: a = gc.OLT case gc.OGE: a = gc.OGT } gc.Patch(gc.Gbranch(optoas(a, nr.Type), nr.Type, likely), to) gc.Patch(gc.Gbranch(optoas(gc.OEQ, nr.Type), nr.Type, likely), to) } else { gc.Patch(gc.Gbranch(optoas(a, nr.Type), nr.Type, likely), to) } regfree(&n1) regfree(&n2) } return }
/* * 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 }
/* * generate: * if(n == true) goto to; */ func bgen(n *gc.Node, true_ bool, likely int, to *obj.Prog) { if gc.Debug['g'] != 0 { gc.Dump("\nbgen", n) } if n == nil { n = gc.Nodbool(true) } if n.Ninit != nil { gc.Genlist(n.Ninit) } if n.Type == nil { gc.Convlit(&n, gc.Types[gc.TBOOL]) if n.Type == nil { return } } et := int(n.Type.Etype) if et != gc.TBOOL { gc.Yyerror("cgen: bad type %v for %v", gc.Tconv(n.Type, 0), gc.Oconv(int(n.Op), 0)) gc.Patch(gins(obj.AEND, nil, nil), to) return } for n.Op == gc.OCONVNOP { n = n.Left if n.Ninit != nil { gc.Genlist(n.Ninit) } } var nl *gc.Node var nr *gc.Node switch n.Op { default: goto def // need to ask if it is bool? case gc.OLITERAL: if !true_ == (n.Val.U.Bval == 0) { gc.Patch(gc.Gbranch(obj.AJMP, nil, likely), to) } return case gc.ONAME: if n.Addable == 0 { goto def } var n1 gc.Node gc.Nodconst(&n1, n.Type, 0) gins(optoas(gc.OCMP, n.Type), n, &n1) a := x86.AJNE if !true_ { a = x86.AJEQ } gc.Patch(gc.Gbranch(a, n.Type, likely), to) return case gc.OANDAND, gc.OOROR: if (n.Op == gc.OANDAND) == true_ { p1 := gc.Gbranch(obj.AJMP, nil, 0) p2 := gc.Gbranch(obj.AJMP, nil, 0) gc.Patch(p1, gc.Pc) bgen(n.Left, !true_, -likely, p2) bgen(n.Right, !true_, -likely, p2) p1 = gc.Gbranch(obj.AJMP, nil, 0) gc.Patch(p1, to) gc.Patch(p2, gc.Pc) } else { bgen(n.Left, true_, likely, to) bgen(n.Right, true_, likely, to) } return case gc.OEQ, gc.ONE, gc.OLT, gc.OGT, gc.OLE, gc.OGE: nr = n.Right if nr == nil || nr.Type == nil { return } fallthrough case gc.ONOT: // unary nl = n.Left if nl == nil || nl.Type == nil { return } } switch n.Op { case gc.ONOT: bgen(nl, !true_, likely, to) return case gc.OEQ, gc.ONE, gc.OLT, gc.OGT, gc.OLE, gc.OGE: a := int(n.Op) if !true_ { if gc.Isfloat[nr.Type.Etype] { // 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) ll := n.Ninit // avoid re-genning ninit n.Ninit = nil bgen(n, true, -likely, p2) n.Ninit = ll gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to) gc.Patch(p2, gc.Pc) return } a = gc.Brcom(a) true_ = !true_ } // make simplest on right if nl.Op == gc.OLITERAL || (nl.Ullman < nr.Ullman && nl.Ullman < gc.UINF) { a = gc.Brrev(a) r := nl nl = nr nr = r } if gc.Isslice(nl.Type) { // front end should only leave cmp to literal nil if (a != gc.OEQ && a != gc.ONE) || nr.Op != gc.OLITERAL { gc.Yyerror("illegal slice comparison") break } a = optoas(a, gc.Types[gc.Tptr]) var n1 gc.Node igen(nl, &n1, nil) n1.Xoffset += int64(gc.Array_array) n1.Type = gc.Types[gc.Tptr] var tmp gc.Node gc.Nodconst(&tmp, gc.Types[gc.Tptr], 0) gins(optoas(gc.OCMP, gc.Types[gc.Tptr]), &n1, &tmp) gc.Patch(gc.Gbranch(a, gc.Types[gc.Tptr], likely), to) regfree(&n1) break } if gc.Isinter(nl.Type) { // front end should only leave cmp to literal nil if (a != gc.OEQ && a != gc.ONE) || nr.Op != gc.OLITERAL { gc.Yyerror("illegal interface comparison") break } a = optoas(a, gc.Types[gc.Tptr]) var n1 gc.Node igen(nl, &n1, nil) n1.Type = gc.Types[gc.Tptr] var tmp gc.Node gc.Nodconst(&tmp, gc.Types[gc.Tptr], 0) gins(optoas(gc.OCMP, gc.Types[gc.Tptr]), &n1, &tmp) gc.Patch(gc.Gbranch(a, gc.Types[gc.Tptr], likely), to) regfree(&n1) break } if gc.Iscomplex[nl.Type.Etype] { gc.Complexbool(a, nl, nr, true_, likely, to) break } var n2 gc.Node var n1 gc.Node if nr.Ullman >= gc.UINF { regalloc(&n1, nl.Type, nil) cgen(nl, &n1) var tmp gc.Node gc.Tempname(&tmp, nl.Type) gmove(&n1, &tmp) regfree(&n1) regalloc(&n2, nr.Type, nil) cgen(nr, &n2) regalloc(&n1, nl.Type, nil) cgen(&tmp, &n1) goto cmp } regalloc(&n1, nl.Type, nil) cgen(nl, &n1) if gc.Smallintconst(nr) { gins(optoas(gc.OCMP, nr.Type), &n1, nr) gc.Patch(gc.Gbranch(optoas(a, nr.Type), nr.Type, likely), to) regfree(&n1) break } regalloc(&n2, nr.Type, nil) cgen(nr, &n2) // only < and <= work right with NaN; reverse if needed cmp: l := &n1 r := &n2 if gc.Isfloat[nl.Type.Etype] && (a == gc.OGT || a == gc.OGE) { l = &n2 r = &n1 a = gc.Brrev(a) } gins(optoas(gc.OCMP, nr.Type), l, r) if gc.Isfloat[nr.Type.Etype] && (n.Op == gc.OEQ || n.Op == gc.ONE) { if n.Op == gc.OEQ { // neither NE nor P p1 := gc.Gbranch(x86.AJNE, nil, -likely) p2 := gc.Gbranch(x86.AJPS, nil, -likely) gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to) gc.Patch(p1, gc.Pc) gc.Patch(p2, gc.Pc) } else { // either NE or P gc.Patch(gc.Gbranch(x86.AJNE, nil, likely), to) gc.Patch(gc.Gbranch(x86.AJPS, nil, likely), to) } } else { gc.Patch(gc.Gbranch(optoas(a, nr.Type), nr.Type, likely), to) } regfree(&n1) regfree(&n2) } return def: var n1 gc.Node regalloc(&n1, n.Type, nil) cgen(n, &n1) var n2 gc.Node gc.Nodconst(&n2, n.Type, 0) gins(optoas(gc.OCMP, n.Type), &n1, &n2) a := x86.AJNE if !true_ { a = x86.AJEQ } gc.Patch(gc.Gbranch(a, n.Type, likely), to) regfree(&n1) return }
/* * 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 }
/* * generate: * if(n == true) goto to; */ func bgen(n *gc.Node, true_ bool, likely int, to *obj.Prog) { if gc.Debug['g'] != 0 { gc.Dump("\nbgen", n) } if n == nil { n = gc.Nodbool(true) } if n.Ninit != nil { gc.Genlist(n.Ninit) } if n.Type == nil { gc.Convlit(&n, gc.Types[gc.TBOOL]) if n.Type == nil { return } } et := int(n.Type.Etype) if et != gc.TBOOL { gc.Yyerror("cgen: bad type %v for %v", gc.Tconv(n.Type, 0), gc.Oconv(int(n.Op), 0)) gc.Patch(gins(obj.AEND, nil, nil), to) return } var nr *gc.Node var nl *gc.Node switch n.Op { default: a := gc.ONE if !true_ { a = gc.OEQ } gencmp0(n, n.Type, a, likely, to) return // need to ask if it is bool? case gc.OLITERAL: if !true_ == (n.Val.U.Bval == 0) { gc.Patch(gc.Gbranch(arm.AB, nil, 0), to) } return case gc.OANDAND, gc.OOROR: if (n.Op == gc.OANDAND) == true_ { p1 := gc.Gbranch(obj.AJMP, nil, 0) p2 := gc.Gbranch(obj.AJMP, nil, 0) gc.Patch(p1, gc.Pc) bgen(n.Left, !true_, -likely, p2) bgen(n.Right, !true_, -likely, p2) p1 = gc.Gbranch(obj.AJMP, nil, 0) gc.Patch(p1, to) gc.Patch(p2, gc.Pc) } else { bgen(n.Left, true_, likely, to) bgen(n.Right, true_, likely, to) } return case gc.OEQ, gc.ONE, gc.OLT, gc.OGT, gc.OLE, gc.OGE: nr = n.Right if nr == nil || nr.Type == nil { return } fallthrough case gc.ONOT: // unary nl = n.Left if nl == nil || nl.Type == nil { return } } switch n.Op { case gc.ONOT: bgen(nl, !true_, likely, to) return case gc.OEQ, gc.ONE, gc.OLT, gc.OGT, gc.OLE, gc.OGE: a := int(n.Op) if !true_ { if gc.Isfloat[nl.Type.Etype] { // brcom is not valid on floats when NaN is involved. p1 := gc.Gbranch(arm.AB, nil, 0) p2 := gc.Gbranch(arm.AB, nil, 0) gc.Patch(p1, gc.Pc) ll := n.Ninit n.Ninit = nil bgen(n, true, -likely, p2) n.Ninit = ll gc.Patch(gc.Gbranch(arm.AB, nil, 0), to) gc.Patch(p2, gc.Pc) return } a = gc.Brcom(a) true_ = !true_ } // make simplest on right if nl.Op == gc.OLITERAL || (nl.Ullman < gc.UINF && nl.Ullman < nr.Ullman) { a = gc.Brrev(a) r := nl nl = nr nr = r } if gc.Isslice(nl.Type) { // only valid to cmp darray to literal nil if (a != gc.OEQ && a != gc.ONE) || nr.Op != gc.OLITERAL { gc.Yyerror("illegal array comparison") break } var n1 gc.Node igen(nl, &n1, nil) n1.Xoffset += int64(gc.Array_array) n1.Type = gc.Types[gc.Tptr] gencmp0(&n1, gc.Types[gc.Tptr], a, likely, to) regfree(&n1) break } if gc.Isinter(nl.Type) { // front end shold only leave cmp to literal nil if (a != gc.OEQ && a != gc.ONE) || nr.Op != gc.OLITERAL { gc.Yyerror("illegal interface comparison") break } var n1 gc.Node igen(nl, &n1, nil) n1.Type = gc.Types[gc.Tptr] n1.Xoffset += 0 gencmp0(&n1, gc.Types[gc.Tptr], a, likely, to) regfree(&n1) break } if gc.Iscomplex[nl.Type.Etype] { gc.Complexbool(a, nl, nr, true_, likely, to) break } if gc.Is64(nr.Type) { if nl.Addable == 0 { var n1 gc.Node gc.Tempname(&n1, nl.Type) cgen(nl, &n1) nl = &n1 } if nr.Addable == 0 { var n2 gc.Node gc.Tempname(&n2, nr.Type) cgen(nr, &n2) nr = &n2 } cmp64(nl, nr, a, likely, to) break } if nr.Op == gc.OLITERAL { if gc.Isconst(nr, gc.CTINT) && gc.Mpgetfix(nr.Val.U.Xval) == 0 { gencmp0(nl, nl.Type, a, likely, to) break } if nr.Val.Ctype == gc.CTNIL { gencmp0(nl, nl.Type, a, likely, to) break } } a = optoas(a, nr.Type) if nr.Ullman >= gc.UINF { var n1 gc.Node regalloc(&n1, nl.Type, nil) cgen(nl, &n1) var tmp gc.Node gc.Tempname(&tmp, nl.Type) gmove(&n1, &tmp) regfree(&n1) var n2 gc.Node regalloc(&n2, nr.Type, nil) cgen(nr, &n2) regalloc(&n1, nl.Type, nil) cgen(&tmp, &n1) gcmp(optoas(gc.OCMP, nr.Type), &n1, &n2) gc.Patch(gc.Gbranch(a, nr.Type, likely), to) regfree(&n1) regfree(&n2) break } var n3 gc.Node gc.Tempname(&n3, nl.Type) cgen(nl, &n3) var tmp gc.Node gc.Tempname(&tmp, nr.Type) cgen(nr, &tmp) var n1 gc.Node regalloc(&n1, nl.Type, nil) gmove(&n3, &n1) var n2 gc.Node regalloc(&n2, nr.Type, nil) gmove(&tmp, &n2) gcmp(optoas(gc.OCMP, nr.Type), &n1, &n2) if gc.Isfloat[nl.Type.Etype] { if n.Op == gc.ONE { p1 := gc.Gbranch(arm.ABVS, nr.Type, likely) gc.Patch(gc.Gbranch(a, nr.Type, likely), to) gc.Patch(p1, to) } else { p1 := gc.Gbranch(arm.ABVS, nr.Type, -likely) gc.Patch(gc.Gbranch(a, nr.Type, likely), to) gc.Patch(p1, gc.Pc) } } else { gc.Patch(gc.Gbranch(a, nr.Type, likely), to) } regfree(&n1) regfree(&n2) } return }
/* * branch gen * if(n == true) goto to; */ func bgen(n *gc.Node, true_ bool, likely int, to *obj.Prog) { if gc.Debug['g'] != 0 { gc.Dump("\nbgen", n) } if n == nil { n = gc.Nodbool(true) } if n.Ninit != nil { gc.Genlist(n.Ninit) } if n.Type == nil { gc.Convlit(&n, gc.Types[gc.TBOOL]) if n.Type == nil { return } } et := int(n.Type.Etype) if et != gc.TBOOL { gc.Yyerror("cgen: bad type %v for %v", gc.Tconv(n.Type, 0), gc.Oconv(int(n.Op), 0)) gc.Patch(gins(obj.AEND, nil, nil), to) return } for n.Op == gc.OCONVNOP { n = n.Left if n.Ninit != nil { gc.Genlist(n.Ninit) } } nl := n.Left var nr *gc.Node if nl != nil && gc.Isfloat[nl.Type.Etype] { bgen_float(n, bool2int(true_), likely, to) return } switch n.Op { default: goto def // need to ask if it is bool? case gc.OLITERAL: if !true_ == (n.Val.U.Bval == 0) { gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to) } return case gc.ONAME: if n.Addable == 0 { goto def } var n1 gc.Node gc.Nodconst(&n1, n.Type, 0) gins(optoas(gc.OCMP, n.Type), n, &n1) a := x86.AJNE if !true_ { a = x86.AJEQ } gc.Patch(gc.Gbranch(a, n.Type, likely), to) return case gc.OANDAND, gc.OOROR: if (n.Op == gc.OANDAND) == true_ { p1 := gc.Gbranch(obj.AJMP, nil, 0) p2 := gc.Gbranch(obj.AJMP, nil, 0) gc.Patch(p1, gc.Pc) bgen(n.Left, !true_, -likely, p2) bgen(n.Right, !true_, -likely, p2) p1 = gc.Gbranch(obj.AJMP, nil, 0) gc.Patch(p1, to) gc.Patch(p2, gc.Pc) } else { bgen(n.Left, true_, likely, to) bgen(n.Right, true_, likely, to) } return case gc.OEQ, gc.ONE, gc.OLT, gc.OGT, gc.OLE, gc.OGE: nr = n.Right if nr == nil || nr.Type == nil { return } fallthrough case gc.ONOT: // unary nl = n.Left if nl == nil || nl.Type == nil { return } } switch n.Op { case gc.ONOT: bgen(nl, !true_, likely, to) case gc.OEQ, gc.ONE, gc.OLT, gc.OGT, gc.OLE, gc.OGE: a := int(n.Op) if !true_ { a = gc.Brcom(a) true_ = !true_ } // make simplest on right if nl.Op == gc.OLITERAL || (nl.Ullman < nr.Ullman && nl.Ullman < gc.UINF) { a = gc.Brrev(a) r := nl nl = nr nr = r } if gc.Isslice(nl.Type) { // front end should only leave cmp to literal nil if (a != gc.OEQ && a != gc.ONE) || nr.Op != gc.OLITERAL { gc.Yyerror("illegal slice comparison") break } a = optoas(a, gc.Types[gc.Tptr]) var n1 gc.Node igen(nl, &n1, nil) n1.Xoffset += int64(gc.Array_array) n1.Type = gc.Types[gc.Tptr] var tmp gc.Node gc.Nodconst(&tmp, gc.Types[gc.Tptr], 0) gins(optoas(gc.OCMP, gc.Types[gc.Tptr]), &n1, &tmp) gc.Patch(gc.Gbranch(a, gc.Types[gc.Tptr], likely), to) regfree(&n1) break } if gc.Isinter(nl.Type) { // front end should only leave cmp to literal nil if (a != gc.OEQ && a != gc.ONE) || nr.Op != gc.OLITERAL { gc.Yyerror("illegal interface comparison") break } a = optoas(a, gc.Types[gc.Tptr]) var n1 gc.Node igen(nl, &n1, nil) n1.Type = gc.Types[gc.Tptr] var tmp gc.Node gc.Nodconst(&tmp, gc.Types[gc.Tptr], 0) gins(optoas(gc.OCMP, gc.Types[gc.Tptr]), &n1, &tmp) gc.Patch(gc.Gbranch(a, gc.Types[gc.Tptr], likely), to) regfree(&n1) break } if gc.Iscomplex[nl.Type.Etype] { gc.Complexbool(a, nl, nr, true_, likely, to) break } if gc.Is64(nr.Type) { if nl.Addable == 0 || gc.Isconst(nl, gc.CTINT) { var n1 gc.Node gc.Tempname(&n1, nl.Type) cgen(nl, &n1) nl = &n1 } if nr.Addable == 0 { var n2 gc.Node gc.Tempname(&n2, nr.Type) cgen(nr, &n2) nr = &n2 } cmp64(nl, nr, a, likely, to) break } var n2 gc.Node if nr.Ullman >= gc.UINF { if nl.Addable == 0 { var n1 gc.Node gc.Tempname(&n1, nl.Type) cgen(nl, &n1) nl = &n1 } if nr.Addable == 0 { var tmp gc.Node gc.Tempname(&tmp, nr.Type) cgen(nr, &tmp) nr = &tmp } var n2 gc.Node regalloc(&n2, nr.Type, nil) cgen(nr, &n2) nr = &n2 goto cmp } if nl.Addable == 0 { var n1 gc.Node gc.Tempname(&n1, nl.Type) cgen(nl, &n1) nl = &n1 } if gc.Smallintconst(nr) { gins(optoas(gc.OCMP, nr.Type), nl, nr) gc.Patch(gc.Gbranch(optoas(a, nr.Type), nr.Type, likely), to) break } if nr.Addable == 0 { var tmp gc.Node gc.Tempname(&tmp, nr.Type) cgen(nr, &tmp) nr = &tmp } regalloc(&n2, nr.Type, nil) gmove(nr, &n2) nr = &n2 cmp: gins(optoas(gc.OCMP, nr.Type), nl, nr) gc.Patch(gc.Gbranch(optoas(a, nr.Type), nr.Type, likely), to) if nl.Op == gc.OREGISTER { regfree(nl) } regfree(nr) } return def: var n1 gc.Node regalloc(&n1, n.Type, nil) cgen(n, &n1) var n2 gc.Node gc.Nodconst(&n2, n.Type, 0) gins(optoas(gc.OCMP, n.Type), &n1, &n2) a := x86.AJNE if !true_ { a = x86.AJEQ } gc.Patch(gc.Gbranch(a, n.Type, likely), to) regfree(&n1) return }
/* * 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 }