func fixlargeoffset(n *gc.Node) { if n == nil { return } if n.Op != gc.OINDREG { return } if -4096 <= n.Xoffset && n.Xoffset < 4096 { return } a := gc.Node(*n) a.Op = gc.OREGISTER a.Type = gc.Types[gc.Tptr] a.Xoffset = 0 gc.Cgen_checknil(&a) ginscon(optoas(gc.OADD, gc.Types[gc.Tptr]), n.Xoffset, &a) n.Xoffset = 0 }
func fixlargeoffset(n *gc.Node) { if n == nil { return } if n.Op != gc.OINDREG { return } if n.Val.U.Reg == ppc64.REGSP { // stack offset cannot be large return } if n.Xoffset != int64(int32(n.Xoffset)) { // TODO(minux): offset too large, move into R31 and add to R31 instead. // this is used only in test/fixedbugs/issue6036.go. gc.Fatal("offset too large: %v", gc.Nconv(n, 0)) a := gc.Node(*n) a.Op = gc.OREGISTER a.Type = gc.Types[gc.Tptr] a.Xoffset = 0 gc.Cgen_checknil(&a) ginscon(optoas(gc.OADD, gc.Types[gc.Tptr]), n.Xoffset, &a) n.Xoffset = 0 } }
/* * allocate a register (reusing res if possible) and generate * a = &n * The caller must call regfree(a). * The generated code checks that the result is not nil. */ func agenr(n *gc.Node, a *gc.Node, res *gc.Node) { if gc.Debug['g'] != 0 { gc.Dump("agenr-n", n) } nl := n.Left nr := n.Right switch n.Op { case gc.ODOT, gc.ODOTPTR, gc.OCALLFUNC, gc.OCALLMETH, gc.OCALLINTER: var n1 gc.Node igen(n, &n1, res) regalloc(a, gc.Types[gc.Tptr], &n1) agen(&n1, a) regfree(&n1) case gc.OIND: cgenr(n.Left, a, res) gc.Cgen_checknil(a) case gc.OINDEX: var p2 *obj.Prog // to be patched to panicindex. w := uint32(n.Type.Width) //bounded = debug['B'] || n->bounded; var n3 gc.Node var n1 gc.Node if nr.Addable != 0 { var tmp gc.Node if !gc.Isconst(nr, gc.CTINT) { gc.Tempname(&tmp, gc.Types[gc.TINT64]) } if !gc.Isconst(nl, gc.CTSTR) { agenr(nl, &n3, res) } if !gc.Isconst(nr, gc.CTINT) { cgen(nr, &tmp) regalloc(&n1, tmp.Type, nil) gmove(&tmp, &n1) } } else if nl.Addable != 0 { if !gc.Isconst(nr, gc.CTINT) { var tmp gc.Node gc.Tempname(&tmp, gc.Types[gc.TINT64]) cgen(nr, &tmp) regalloc(&n1, tmp.Type, nil) gmove(&tmp, &n1) } if !gc.Isconst(nl, gc.CTSTR) { agenr(nl, &n3, res) } } else { var tmp gc.Node gc.Tempname(&tmp, gc.Types[gc.TINT64]) cgen(nr, &tmp) nr = &tmp if !gc.Isconst(nl, gc.CTSTR) { agenr(nl, &n3, res) } regalloc(&n1, tmp.Type, nil) gins(optoas(gc.OAS, tmp.Type), &tmp, &n1) } // &a is in &n3 (allocated in res) // i is in &n1 (if not constant) // w is width // constant index if gc.Isconst(nr, gc.CTINT) { if gc.Isconst(nl, gc.CTSTR) { gc.Fatal("constant string constant index") } v := uint64(gc.Mpgetfix(nr.Val.U.Xval)) if gc.Isslice(nl.Type) || nl.Type.Etype == gc.TSTRING { if gc.Debug['B'] == 0 && !n.Bounded { n1 = n3 n1.Op = gc.OINDREG n1.Type = gc.Types[gc.Tptr] n1.Xoffset = int64(gc.Array_nel) var n4 gc.Node regalloc(&n4, n1.Type, nil) gmove(&n1, &n4) ginscon2(optoas(gc.OCMP, gc.Types[gc.TUINT64]), &n4, int64(v)) regfree(&n4) p1 := gc.Gbranch(optoas(gc.OGT, gc.Types[gc.TUINT64]), nil, +1) ginscall(gc.Panicindex, 0) gc.Patch(p1, gc.Pc) } n1 = n3 n1.Op = gc.OINDREG n1.Type = gc.Types[gc.Tptr] n1.Xoffset = int64(gc.Array_array) gmove(&n1, &n3) } if v*uint64(w) != 0 { ginscon(optoas(gc.OADD, gc.Types[gc.Tptr]), int64(v*uint64(w)), &n3) } *a = n3 break } var n2 gc.Node regalloc(&n2, gc.Types[gc.TINT64], &n1) // i gmove(&n1, &n2) regfree(&n1) var n4 gc.Node if gc.Debug['B'] == 0 && !n.Bounded { // check bounds if gc.Isconst(nl, gc.CTSTR) { gc.Nodconst(&n4, gc.Types[gc.TUINT64], int64(len(nl.Val.U.Sval))) } else if gc.Isslice(nl.Type) || nl.Type.Etype == gc.TSTRING { n1 = n3 n1.Op = gc.OINDREG n1.Type = gc.Types[gc.Tptr] n1.Xoffset = int64(gc.Array_nel) regalloc(&n4, gc.Types[gc.TUINT64], nil) gmove(&n1, &n4) } else { if nl.Type.Bound < (1<<15)-1 { gc.Nodconst(&n4, gc.Types[gc.TUINT64], nl.Type.Bound) } else { regalloc(&n4, gc.Types[gc.TUINT64], nil) p1 := gins(ppc64.AMOVD, nil, &n4) p1.From.Type = obj.TYPE_CONST p1.From.Offset = nl.Type.Bound } } gins(optoas(gc.OCMP, gc.Types[gc.TUINT64]), &n2, &n4) if n4.Op == gc.OREGISTER { regfree(&n4) } p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1) if p2 != nil { gc.Patch(p2, gc.Pc) } ginscall(gc.Panicindex, 0) gc.Patch(p1, gc.Pc) } if gc.Isconst(nl, gc.CTSTR) { regalloc(&n3, gc.Types[gc.Tptr], res) p1 := gins(ppc64.AMOVD, nil, &n3) gc.Datastring(nl.Val.U.Sval, &p1.From) p1.From.Type = obj.TYPE_ADDR } else if gc.Isslice(nl.Type) || nl.Type.Etype == gc.TSTRING { n1 = n3 n1.Op = gc.OINDREG n1.Type = gc.Types[gc.Tptr] n1.Xoffset = int64(gc.Array_array) gmove(&n1, &n3) } if w == 0 { } else // nothing to do if w == 1 { /* w already scaled */ gins(optoas(gc.OADD, gc.Types[gc.Tptr]), &n2, &n3) /* else if(w == 2 || w == 4 || w == 8) { // TODO(minux): scale using shift } */ } else { regalloc(&n4, gc.Types[gc.TUINT64], nil) gc.Nodconst(&n1, gc.Types[gc.TUINT64], int64(w)) gmove(&n1, &n4) gins(optoas(gc.OMUL, gc.Types[gc.TUINT64]), &n4, &n2) gins(optoas(gc.OADD, gc.Types[gc.Tptr]), &n2, &n3) regfree(&n4) } *a = n3 regfree(&n2) default: regalloc(a, gc.Types[gc.Tptr], res) agen(n, a) } }
/* * generate: * newreg = &n; * res = newreg * * on exit, a has been changed to be *newreg. * caller must regfree(a). * The generated code checks that the result is not *nil. */ func igen(n *gc.Node, a *gc.Node, res *gc.Node) { if gc.Debug['g'] != 0 { gc.Dump("\nigen-n", n) } switch n.Op { case gc.ONAME: if (n.Class&gc.PHEAP != 0) || n.Class == gc.PPARAMREF { break } *a = *n return // Increase the refcount of the register so that igen's caller // has to call regfree. case gc.OINDREG: if n.Val.U.Reg != ppc64.REGSP { reg[n.Val.U.Reg]++ } *a = *n return case gc.ODOT: igen(n.Left, a, res) a.Xoffset += n.Xoffset a.Type = n.Type fixlargeoffset(a) return case gc.ODOTPTR: cgenr(n.Left, a, res) gc.Cgen_checknil(a) a.Op = gc.OINDREG a.Xoffset += n.Xoffset a.Type = n.Type fixlargeoffset(a) return case gc.OCALLFUNC, gc.OCALLMETH, gc.OCALLINTER: switch n.Op { case gc.OCALLFUNC: cgen_call(n, 0) case gc.OCALLMETH: gc.Cgen_callmeth(n, 0) case gc.OCALLINTER: cgen_callinter(n, nil, 0) } var flist gc.Iter fp := gc.Structfirst(&flist, gc.Getoutarg(n.Left.Type)) *a = gc.Node{} a.Op = gc.OINDREG a.Val.U.Reg = ppc64.REGSP a.Addable = 1 a.Xoffset = fp.Width + int64(gc.Widthptr) // +widthptr: saved lr at 0(SP) a.Type = n.Type return // Index of fixed-size array by constant can // put the offset in the addressing. // Could do the same for slice except that we need // to use the real index for the bounds checking. case gc.OINDEX: if gc.Isfixedarray(n.Left.Type) || (gc.Isptr[n.Left.Type.Etype] && gc.Isfixedarray(n.Left.Left.Type)) { if gc.Isconst(n.Right, gc.CTINT) { // Compute &a. if !gc.Isptr[n.Left.Type.Etype] { igen(n.Left, a, res) } else { var n1 gc.Node igen(n.Left, &n1, res) gc.Cgen_checknil(&n1) regalloc(a, gc.Types[gc.Tptr], res) gmove(&n1, a) regfree(&n1) a.Op = gc.OINDREG } // Compute &a[i] as &a + i*width. a.Type = n.Type a.Xoffset += gc.Mpgetfix(n.Right.Val.U.Xval) * n.Type.Width fixlargeoffset(a) return } } } agenr(n, a, res) a.Op = gc.OINDREG a.Type = n.Type }
/* * generate code to compute address of n, * a reference to a (perhaps nested) field inside * an array or struct. * return 0 on failure, 1 on success. * on success, leaves usable address in a. * * caller is responsible for calling sudoclean * after successful sudoaddable, * to release the register used for a. */ func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { if n.Type == nil { return false } *a = obj.Addr{} switch n.Op { case gc.OLITERAL: if !gc.Isconst(n, gc.CTINT) { break } v := gc.Mpgetfix(n.Val.U.Xval) if v >= 32000 || v <= -32000 { break } switch as { default: return false case arm.AADD, arm.ASUB, arm.AAND, arm.AORR, arm.AEOR, arm.AMOVB, arm.AMOVBS, arm.AMOVBU, arm.AMOVH, arm.AMOVHS, arm.AMOVHU, arm.AMOVW: break } cleani += 2 reg := &clean[cleani-1] reg1 := &clean[cleani-2] reg.Op = gc.OEMPTY reg1.Op = gc.OEMPTY gc.Naddr(a, n) return true case gc.ODOT, gc.ODOTPTR: cleani += 2 reg := &clean[cleani-1] reg1 := &clean[cleani-2] reg.Op = gc.OEMPTY reg1.Op = gc.OEMPTY var nn *gc.Node var oary [10]int64 o := gc.Dotoffset(n, oary[:], &nn) if nn == nil { sudoclean() return false } if nn.Addable && o == 1 && oary[0] >= 0 { // directly addressable set of DOTs n1 := *nn n1.Type = n.Type n1.Xoffset += oary[0] gc.Naddr(a, &n1) return true } gc.Regalloc(reg, gc.Types[gc.Tptr], nil) n1 := *reg n1.Op = gc.OINDREG if oary[0] >= 0 { gc.Agen(nn, reg) n1.Xoffset = oary[0] } else { gc.Cgen(nn, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[0] + 1) } for i := 1; i < o; i++ { if oary[i] >= 0 { gc.Fatal("can't happen") } gins(arm.AMOVW, &n1, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[i] + 1) } a.Type = obj.TYPE_NONE a.Name = obj.NAME_NONE n1.Type = n.Type gc.Naddr(a, &n1) return true case gc.OINDEX: return false } return false }
func anyregalloc() bool { var j int for i := int(0); i < len(reg); i++ { if reg[i] == 0 { goto ok } for j = 0; j < len(resvd); j++ { if resvd[j] == i { goto ok } } return true ok: } return false } /* * allocate register of type t, leave in n. * if o != N, o is desired fixed register. * caller must regfree(n). */ func regalloc(n *gc.Node, t *gc.Type, o *gc.Node) { if t == nil { gc.Fatal("regalloc: t nil") } et := int(int(gc.Simtype[t.Etype])) if gc.Debug['r'] != 0 { fixfree := int(0) fltfree := int(0) for i := int(arm64.REG_R0); i < arm64.REG_F31; i++ { if reg[i-arm64.REG_R0] == 0 { if i < arm64.REG_F0 { fixfree++ } else { fltfree++ } } } fmt.Printf("regalloc fix %d flt %d free\n", fixfree, fltfree) } var i int switch et { case gc.TINT8, gc.TUINT8, gc.TINT16, gc.TUINT16, gc.TINT32, gc.TUINT32, gc.TINT64, gc.TUINT64, gc.TPTR32, gc.TPTR64, gc.TBOOL: if o != nil && o.Op == gc.OREGISTER { i = int(o.Val.U.Reg) if i >= arm64.REGMIN && i <= arm64.REGMAX { goto out } } for i = arm64.REGMIN; i <= arm64.REGMAX; i++ { if reg[i-arm64.REG_R0] == 0 { regpc[i-arm64.REG_R0] = uint32(obj.Getcallerpc(&n)) goto out } } gc.Flusherrors() for i := int(arm64.REG_R0); i < arm64.REG_R0+arm64.NREG; i++ { fmt.Printf("R%d %p\n", i, regpc[i-arm64.REG_R0]) } gc.Fatal("out of fixed registers") case gc.TFLOAT32, gc.TFLOAT64: if o != nil && o.Op == gc.OREGISTER { i = int(o.Val.U.Reg) if i >= arm64.FREGMIN && i <= arm64.FREGMAX { goto out } } for i = arm64.FREGMIN; i <= arm64.FREGMAX; i++ { if reg[i-arm64.REG_R0] == 0 { regpc[i-arm64.REG_R0] = uint32(obj.Getcallerpc(&n)) goto out } } gc.Flusherrors() for i := int(arm64.REG_F0); i < arm64.REG_F0+arm64.NREG; i++ { fmt.Printf("F%d %p\n", i, regpc[i-arm64.REG_R0]) } gc.Fatal("out of floating registers") case gc.TCOMPLEX64, gc.TCOMPLEX128: gc.Tempname(n, t) return } gc.Fatal("regalloc: unknown type %v", gc.Tconv(t, 0)) return out: reg[i-arm64.REG_R0]++ gc.Nodreg(n, t, i) } func regfree(n *gc.Node) { if n.Op == gc.ONAME { return } if n.Op != gc.OREGISTER && n.Op != gc.OINDREG { gc.Fatal("regfree: not a register") } i := int(int(n.Val.U.Reg) - arm64.REG_R0) if i == arm64.REGSP-arm64.REG_R0 { return } if i < 0 || i >= len(reg) { gc.Fatal("regfree: reg out of range") } if reg[i] <= 0 { gc.Fatal("regfree: reg not allocated") } reg[i]-- if reg[i] == 0 { regpc[i] = 0 } } /* * generate * as $c, n */ func ginscon(as int, c int64, n2 *gc.Node) { var n1 gc.Node gc.Nodconst(&n1, gc.Types[gc.TINT64], c) if as != arm64.AMOVD && (c < -arm64.BIG || c > arm64.BIG) { // cannot have more than 16-bit of immediate in ADD, etc. // instead, MOV into register first. var ntmp gc.Node regalloc(&ntmp, gc.Types[gc.TINT64], nil) gins(arm64.AMOVD, &n1, &ntmp) gins(as, &ntmp, n2) regfree(&ntmp) return } gins(as, &n1, n2) } /* * generate * as n, $c (CMP) */ func ginscon2(as int, n2 *gc.Node, c int64) { var n1 gc.Node gc.Nodconst(&n1, gc.Types[gc.TINT64], c) switch as { default: gc.Fatal("ginscon2") case arm64.ACMP: if -arm64.BIG <= c && c <= arm64.BIG { gcmp(as, n2, &n1) return } } // MOV n1 into register first var ntmp gc.Node regalloc(&ntmp, gc.Types[gc.TINT64], nil) gins(arm64.AMOVD, &n1, &ntmp) gcmp(as, n2, &ntmp) regfree(&ntmp) } /* * generate move: * t = f * hard part is conversions. */ func gmove(f *gc.Node, t *gc.Node) { if gc.Debug['M'] != 0 { fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, obj.FmtLong), gc.Nconv(t, obj.FmtLong)) } ft := int(gc.Simsimtype(f.Type)) tt := int(gc.Simsimtype(t.Type)) cvt := (*gc.Type)(t.Type) if gc.Iscomplex[ft] || gc.Iscomplex[tt] { gc.Complexmove(f, t) return } // cannot have two memory operands var r1 gc.Node var a int if gc.Ismem(f) && gc.Ismem(t) { goto hard } // convert constant to desired type if f.Op == gc.OLITERAL { var con gc.Node switch tt { default: gc.Convconst(&con, t.Type, &f.Val) case gc.TINT32, gc.TINT16, gc.TINT8: var con gc.Node gc.Convconst(&con, gc.Types[gc.TINT64], &f.Val) var r1 gc.Node regalloc(&r1, con.Type, t) gins(arm64.AMOVD, &con, &r1) gmove(&r1, t) regfree(&r1) return case gc.TUINT32, gc.TUINT16, gc.TUINT8: var con gc.Node gc.Convconst(&con, gc.Types[gc.TUINT64], &f.Val) var r1 gc.Node regalloc(&r1, con.Type, t) gins(arm64.AMOVD, &con, &r1) gmove(&r1, t) regfree(&r1) return } f = &con ft = tt // so big switch will choose a simple mov // constants can't move directly to memory. if gc.Ismem(t) { goto hard } } // value -> value copy, first operand in memory. // any floating point operand requires register // src, so goto hard to copy to register first. if gc.Ismem(f) && ft != tt && (gc.Isfloat[ft] || gc.Isfloat[tt]) { cvt = gc.Types[ft] goto hard } // value -> value copy, only one memory operand. // figure out the instruction to use. // break out of switch for one-instruction gins. // goto rdst for "destination must be register". // goto hard for "convert to cvt type first". // otherwise handle and return. switch uint32(ft)<<16 | uint32(tt) { default: gc.Fatal("gmove %v -> %v", gc.Tconv(f.Type, obj.FmtLong), gc.Tconv(t.Type, obj.FmtLong)) /* * integer copy and truncate */ case gc.TINT8<<16 | gc.TINT8, // same size gc.TUINT8<<16 | gc.TINT8, gc.TINT16<<16 | gc.TINT8, // truncate gc.TUINT16<<16 | gc.TINT8, gc.TINT32<<16 | gc.TINT8, gc.TUINT32<<16 | gc.TINT8, gc.TINT64<<16 | gc.TINT8, gc.TUINT64<<16 | gc.TINT8: a = arm64.AMOVB case gc.TINT8<<16 | gc.TUINT8, // same size gc.TUINT8<<16 | gc.TUINT8, gc.TINT16<<16 | gc.TUINT8, // truncate gc.TUINT16<<16 | gc.TUINT8, gc.TINT32<<16 | gc.TUINT8, gc.TUINT32<<16 | gc.TUINT8, gc.TINT64<<16 | gc.TUINT8, gc.TUINT64<<16 | gc.TUINT8: a = arm64.AMOVBU case gc.TINT16<<16 | gc.TINT16, // same size gc.TUINT16<<16 | gc.TINT16, gc.TINT32<<16 | gc.TINT16, // truncate gc.TUINT32<<16 | gc.TINT16, gc.TINT64<<16 | gc.TINT16, gc.TUINT64<<16 | gc.TINT16: a = arm64.AMOVH case gc.TINT16<<16 | gc.TUINT16, // same size gc.TUINT16<<16 | gc.TUINT16, gc.TINT32<<16 | gc.TUINT16, // truncate gc.TUINT32<<16 | gc.TUINT16, gc.TINT64<<16 | gc.TUINT16, gc.TUINT64<<16 | gc.TUINT16: a = arm64.AMOVHU case gc.TINT32<<16 | gc.TINT32, // same size gc.TUINT32<<16 | gc.TINT32, gc.TINT64<<16 | gc.TINT32, // truncate gc.TUINT64<<16 | gc.TINT32: a = arm64.AMOVW case gc.TINT32<<16 | gc.TUINT32, // same size gc.TUINT32<<16 | gc.TUINT32, gc.TINT64<<16 | gc.TUINT32, gc.TUINT64<<16 | gc.TUINT32: a = arm64.AMOVWU case gc.TINT64<<16 | gc.TINT64, // same size gc.TINT64<<16 | gc.TUINT64, gc.TUINT64<<16 | gc.TINT64, gc.TUINT64<<16 | gc.TUINT64: a = arm64.AMOVD /* * integer up-conversions */ case gc.TINT8<<16 | gc.TINT16, // sign extend int8 gc.TINT8<<16 | gc.TUINT16, gc.TINT8<<16 | gc.TINT32, gc.TINT8<<16 | gc.TUINT32, gc.TINT8<<16 | gc.TINT64, gc.TINT8<<16 | gc.TUINT64: a = arm64.AMOVB goto rdst case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 gc.TUINT8<<16 | gc.TUINT16, gc.TUINT8<<16 | gc.TINT32, gc.TUINT8<<16 | gc.TUINT32, gc.TUINT8<<16 | gc.TINT64, gc.TUINT8<<16 | gc.TUINT64: a = arm64.AMOVBU goto rdst case gc.TINT16<<16 | gc.TINT32, // sign extend int16 gc.TINT16<<16 | gc.TUINT32, gc.TINT16<<16 | gc.TINT64, gc.TINT16<<16 | gc.TUINT64: a = arm64.AMOVH goto rdst case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 gc.TUINT16<<16 | gc.TUINT32, gc.TUINT16<<16 | gc.TINT64, gc.TUINT16<<16 | gc.TUINT64: a = arm64.AMOVHU goto rdst case gc.TINT32<<16 | gc.TINT64, // sign extend int32 gc.TINT32<<16 | gc.TUINT64: a = arm64.AMOVW goto rdst case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 gc.TUINT32<<16 | gc.TUINT64: a = arm64.AMOVWU goto rdst /* * float to integer */ case gc.TFLOAT32<<16 | gc.TINT32: a = arm64.AFCVTZSSW goto rdst case gc.TFLOAT64<<16 | gc.TINT32: a = arm64.AFCVTZSDW goto rdst case gc.TFLOAT32<<16 | gc.TINT64: a = arm64.AFCVTZSS goto rdst case gc.TFLOAT64<<16 | gc.TINT64: a = arm64.AFCVTZSD goto rdst case gc.TFLOAT32<<16 | gc.TUINT32: a = arm64.AFCVTZUSW goto rdst case gc.TFLOAT64<<16 | gc.TUINT32: a = arm64.AFCVTZUDW goto rdst case gc.TFLOAT32<<16 | gc.TUINT64: a = arm64.AFCVTZUS goto rdst case gc.TFLOAT64<<16 | gc.TUINT64: a = arm64.AFCVTZUD goto rdst case gc.TFLOAT32<<16 | gc.TINT16, gc.TFLOAT32<<16 | gc.TINT8, gc.TFLOAT64<<16 | gc.TINT16, gc.TFLOAT64<<16 | gc.TINT8: cvt = gc.Types[gc.TINT32] goto hard case gc.TFLOAT32<<16 | gc.TUINT16, gc.TFLOAT32<<16 | gc.TUINT8, gc.TFLOAT64<<16 | gc.TUINT16, gc.TFLOAT64<<16 | gc.TUINT8: cvt = gc.Types[gc.TUINT32] goto hard /* * integer to float */ case gc.TINT8<<16 | gc.TFLOAT32, gc.TINT16<<16 | gc.TFLOAT32, gc.TINT32<<16 | gc.TFLOAT32: a = arm64.ASCVTFWS goto rdst case gc.TINT8<<16 | gc.TFLOAT64, gc.TINT16<<16 | gc.TFLOAT64, gc.TINT32<<16 | gc.TFLOAT64: a = arm64.ASCVTFWD goto rdst case gc.TINT64<<16 | gc.TFLOAT32: a = arm64.ASCVTFS goto rdst case gc.TINT64<<16 | gc.TFLOAT64: a = arm64.ASCVTFD goto rdst case gc.TUINT8<<16 | gc.TFLOAT32, gc.TUINT16<<16 | gc.TFLOAT32, gc.TUINT32<<16 | gc.TFLOAT32: a = arm64.AUCVTFWS goto rdst case gc.TUINT8<<16 | gc.TFLOAT64, gc.TUINT16<<16 | gc.TFLOAT64, gc.TUINT32<<16 | gc.TFLOAT64: a = arm64.AUCVTFWD goto rdst case gc.TUINT64<<16 | gc.TFLOAT32: a = arm64.AUCVTFS goto rdst case gc.TUINT64<<16 | gc.TFLOAT64: a = arm64.AUCVTFD goto rdst /* * float to float */ case gc.TFLOAT32<<16 | gc.TFLOAT32: a = arm64.AFMOVS case gc.TFLOAT64<<16 | gc.TFLOAT64: a = arm64.AFMOVD case gc.TFLOAT32<<16 | gc.TFLOAT64: a = arm64.AFCVTSD goto rdst case gc.TFLOAT64<<16 | gc.TFLOAT32: a = arm64.AFCVTDS goto rdst } gins(a, f, t) return // requires register destination rdst: regalloc(&r1, t.Type, t) gins(a, f, &r1) gmove(&r1, t) regfree(&r1) return // requires register intermediate hard: regalloc(&r1, cvt, t) gmove(f, &r1) gmove(&r1, t) regfree(&r1) return } /* * generate one instruction: * as f, t */ func gins(as int, f *gc.Node, t *gc.Node) *obj.Prog { // TODO(austin): Add self-move test like in 6g (but be careful // of truncation moves) af := obj.Addr(obj.Addr{}) at := obj.Addr(obj.Addr{}) if f != nil { af = gc.Naddr(f) } if t != nil { at = gc.Naddr(t) } p := (*obj.Prog)(gc.Prog(as)) if f != nil { p.From = af } if t != nil { p.To = at } if gc.Debug['g'] != 0 { fmt.Printf("%v\n", p) } w := int32(0) switch as { case arm64.AMOVB, arm64.AMOVBU: w = 1 case arm64.AMOVH, arm64.AMOVHU: w = 2 case arm64.AMOVW, arm64.AMOVWU: w = 4 case arm64.AMOVD: if af.Type == obj.TYPE_CONST || af.Type == obj.TYPE_ADDR { break } w = 8 } if w != 0 && ((f != nil && af.Width < int64(w)) || (t != nil && at.Type != obj.TYPE_REG && at.Width > int64(w))) { gc.Dump("f", f) gc.Dump("t", t) gc.Fatal("bad width: %v (%d, %d)\n", p, af.Width, at.Width) } return p } func fixlargeoffset(n *gc.Node) { if n == nil { return } if n.Op != gc.OINDREG { return } if -4096 <= n.Xoffset && n.Xoffset < 4096 { return } a := gc.Node(*n) a.Op = gc.OREGISTER a.Type = gc.Types[gc.Tptr] a.Xoffset = 0 gc.Cgen_checknil(&a) ginscon(optoas(gc.OADD, gc.Types[gc.Tptr]), n.Xoffset, &a) n.Xoffset = 0 } /* * insert n into reg slot of p */ func raddr(n *gc.Node, p *obj.Prog) { var a obj.Addr a = gc.Naddr(n) if a.Type != obj.TYPE_REG { if n != nil { gc.Fatal("bad in raddr: %v", gc.Oconv(int(n.Op), 0)) } else { gc.Fatal("bad in raddr: <null>") } p.Reg = 0 } else { p.Reg = a.Reg } } func gcmp(as int, lhs *gc.Node, rhs *gc.Node) *obj.Prog { if lhs.Op != gc.OREGISTER { gc.Fatal("bad operands to gcmp: %v %v", gc.Oconv(int(lhs.Op), 0), gc.Oconv(int(rhs.Op), 0)) } p := gins(as, rhs, nil) raddr(lhs, p) return p } /* * return Axxx for Oxxx on type t. */ func optoas(op int, t *gc.Type) int { if t == nil { gc.Fatal("optoas: t is nil") } a := int(obj.AXXX) switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { default: gc.Fatal("optoas: no entry for op=%v type=%v", gc.Oconv(int(op), 0), gc.Tconv(t, 0)) case gc.OEQ<<16 | gc.TBOOL, gc.OEQ<<16 | gc.TINT8, gc.OEQ<<16 | gc.TUINT8, gc.OEQ<<16 | gc.TINT16, gc.OEQ<<16 | gc.TUINT16, gc.OEQ<<16 | gc.TINT32, gc.OEQ<<16 | gc.TUINT32, gc.OEQ<<16 | gc.TINT64, gc.OEQ<<16 | gc.TUINT64, gc.OEQ<<16 | gc.TPTR32, gc.OEQ<<16 | gc.TPTR64, gc.OEQ<<16 | gc.TFLOAT32, gc.OEQ<<16 | gc.TFLOAT64: a = arm64.ABEQ case gc.ONE<<16 | gc.TBOOL, gc.ONE<<16 | gc.TINT8, gc.ONE<<16 | gc.TUINT8, gc.ONE<<16 | gc.TINT16, gc.ONE<<16 | gc.TUINT16, gc.ONE<<16 | gc.TINT32, gc.ONE<<16 | gc.TUINT32, gc.ONE<<16 | gc.TINT64, gc.ONE<<16 | gc.TUINT64, gc.ONE<<16 | gc.TPTR32, gc.ONE<<16 | gc.TPTR64, gc.ONE<<16 | gc.TFLOAT32, gc.ONE<<16 | gc.TFLOAT64: a = arm64.ABNE case gc.OLT<<16 | gc.TINT8, gc.OLT<<16 | gc.TINT16, gc.OLT<<16 | gc.TINT32, gc.OLT<<16 | gc.TINT64: a = arm64.ABLT case gc.OLT<<16 | gc.TUINT8, gc.OLT<<16 | gc.TUINT16, gc.OLT<<16 | gc.TUINT32, gc.OLT<<16 | gc.TUINT64, gc.OLT<<16 | gc.TFLOAT32, gc.OLT<<16 | gc.TFLOAT64: a = arm64.ABLO case gc.OLE<<16 | gc.TINT8, gc.OLE<<16 | gc.TINT16, gc.OLE<<16 | gc.TINT32, gc.OLE<<16 | gc.TINT64: a = arm64.ABLE case gc.OLE<<16 | gc.TUINT8, gc.OLE<<16 | gc.TUINT16, gc.OLE<<16 | gc.TUINT32, gc.OLE<<16 | gc.TUINT64, gc.OLE<<16 | gc.TFLOAT32, gc.OLE<<16 | gc.TFLOAT64: a = arm64.ABLS case gc.OGT<<16 | gc.TINT8, gc.OGT<<16 | gc.TINT16, gc.OGT<<16 | gc.TINT32, gc.OGT<<16 | gc.TINT64, gc.OGT<<16 | gc.TFLOAT32, gc.OGT<<16 | gc.TFLOAT64: a = arm64.ABGT case gc.OGT<<16 | gc.TUINT8, gc.OGT<<16 | gc.TUINT16, gc.OGT<<16 | gc.TUINT32, gc.OGT<<16 | gc.TUINT64: a = arm64.ABHI case gc.OGE<<16 | gc.TINT8, gc.OGE<<16 | gc.TINT16, gc.OGE<<16 | gc.TINT32, gc.OGE<<16 | gc.TINT64, gc.OGE<<16 | gc.TFLOAT32, gc.OGE<<16 | gc.TFLOAT64: a = arm64.ABGE case gc.OGE<<16 | gc.TUINT8, gc.OGE<<16 | gc.TUINT16, gc.OGE<<16 | gc.TUINT32, gc.OGE<<16 | gc.TUINT64: a = arm64.ABHS case gc.OCMP<<16 | gc.TBOOL, gc.OCMP<<16 | gc.TINT8, gc.OCMP<<16 | gc.TINT16, gc.OCMP<<16 | gc.TINT32, gc.OCMP<<16 | gc.TPTR32, gc.OCMP<<16 | gc.TINT64, gc.OCMP<<16 | gc.TUINT8, gc.OCMP<<16 | gc.TUINT16, gc.OCMP<<16 | gc.TUINT32, gc.OCMP<<16 | gc.TUINT64, gc.OCMP<<16 | gc.TPTR64: a = arm64.ACMP case gc.OCMP<<16 | gc.TFLOAT32: a = arm64.AFCMPS case gc.OCMP<<16 | gc.TFLOAT64: a = arm64.AFCMPD case gc.OAS<<16 | gc.TBOOL, gc.OAS<<16 | gc.TINT8: a = arm64.AMOVB case gc.OAS<<16 | gc.TUINT8: a = arm64.AMOVBU case gc.OAS<<16 | gc.TINT16: a = arm64.AMOVH case gc.OAS<<16 | gc.TUINT16: a = arm64.AMOVHU case gc.OAS<<16 | gc.TINT32: a = arm64.AMOVW case gc.OAS<<16 | gc.TUINT32, gc.OAS<<16 | gc.TPTR32: a = arm64.AMOVWU case gc.OAS<<16 | gc.TINT64, gc.OAS<<16 | gc.TUINT64, gc.OAS<<16 | gc.TPTR64: a = arm64.AMOVD case gc.OAS<<16 | gc.TFLOAT32: a = arm64.AFMOVS case gc.OAS<<16 | gc.TFLOAT64: a = arm64.AFMOVD case gc.OADD<<16 | gc.TINT8, gc.OADD<<16 | gc.TUINT8, gc.OADD<<16 | gc.TINT16, gc.OADD<<16 | gc.TUINT16, gc.OADD<<16 | gc.TINT32, gc.OADD<<16 | gc.TUINT32, gc.OADD<<16 | gc.TPTR32, gc.OADD<<16 | gc.TINT64, gc.OADD<<16 | gc.TUINT64, gc.OADD<<16 | gc.TPTR64: a = arm64.AADD case gc.OADD<<16 | gc.TFLOAT32: a = arm64.AFADDS case gc.OADD<<16 | gc.TFLOAT64: a = arm64.AFADDD case gc.OSUB<<16 | gc.TINT8, gc.OSUB<<16 | gc.TUINT8, gc.OSUB<<16 | gc.TINT16, gc.OSUB<<16 | gc.TUINT16, gc.OSUB<<16 | gc.TINT32, gc.OSUB<<16 | gc.TUINT32, gc.OSUB<<16 | gc.TPTR32, gc.OSUB<<16 | gc.TINT64, gc.OSUB<<16 | gc.TUINT64, gc.OSUB<<16 | gc.TPTR64: a = arm64.ASUB case gc.OSUB<<16 | gc.TFLOAT32: a = arm64.AFSUBS case gc.OSUB<<16 | gc.TFLOAT64: a = arm64.AFSUBD case gc.OMINUS<<16 | gc.TINT8, gc.OMINUS<<16 | gc.TUINT8, gc.OMINUS<<16 | gc.TINT16, gc.OMINUS<<16 | gc.TUINT16, gc.OMINUS<<16 | gc.TINT32, gc.OMINUS<<16 | gc.TUINT32, gc.OMINUS<<16 | gc.TPTR32, gc.OMINUS<<16 | gc.TINT64, gc.OMINUS<<16 | gc.TUINT64, gc.OMINUS<<16 | gc.TPTR64: a = arm64.ANEG case gc.OMINUS<<16 | gc.TFLOAT32: a = arm64.AFNEGS case gc.OMINUS<<16 | gc.TFLOAT64: a = arm64.AFNEGD case gc.OAND<<16 | gc.TINT8, gc.OAND<<16 | gc.TUINT8, gc.OAND<<16 | gc.TINT16, gc.OAND<<16 | gc.TUINT16, gc.OAND<<16 | gc.TINT32, gc.OAND<<16 | gc.TUINT32, gc.OAND<<16 | gc.TPTR32, gc.OAND<<16 | gc.TINT64, gc.OAND<<16 | gc.TUINT64, gc.OAND<<16 | gc.TPTR64: a = arm64.AAND case gc.OOR<<16 | gc.TINT8, gc.OOR<<16 | gc.TUINT8, gc.OOR<<16 | gc.TINT16, gc.OOR<<16 | gc.TUINT16, gc.OOR<<16 | gc.TINT32, gc.OOR<<16 | gc.TUINT32, gc.OOR<<16 | gc.TPTR32, gc.OOR<<16 | gc.TINT64, gc.OOR<<16 | gc.TUINT64, gc.OOR<<16 | gc.TPTR64: a = arm64.AORR case gc.OXOR<<16 | gc.TINT8, gc.OXOR<<16 | gc.TUINT8, gc.OXOR<<16 | gc.TINT16, gc.OXOR<<16 | gc.TUINT16, gc.OXOR<<16 | gc.TINT32, gc.OXOR<<16 | gc.TUINT32, gc.OXOR<<16 | gc.TPTR32, gc.OXOR<<16 | gc.TINT64, gc.OXOR<<16 | gc.TUINT64, gc.OXOR<<16 | gc.TPTR64: a = arm64.AEOR // TODO(minux): handle rotates //case CASE(OLROT, TINT8): //case CASE(OLROT, TUINT8): //case CASE(OLROT, TINT16): //case CASE(OLROT, TUINT16): //case CASE(OLROT, TINT32): //case CASE(OLROT, TUINT32): //case CASE(OLROT, TPTR32): //case CASE(OLROT, TINT64): //case CASE(OLROT, TUINT64): //case CASE(OLROT, TPTR64): // a = 0//???; RLDC? // break; case gc.OLSH<<16 | gc.TINT8, gc.OLSH<<16 | gc.TUINT8, gc.OLSH<<16 | gc.TINT16, gc.OLSH<<16 | gc.TUINT16, gc.OLSH<<16 | gc.TINT32, gc.OLSH<<16 | gc.TUINT32, gc.OLSH<<16 | gc.TPTR32, gc.OLSH<<16 | gc.TINT64, gc.OLSH<<16 | gc.TUINT64, gc.OLSH<<16 | gc.TPTR64: a = arm64.ALSL case gc.ORSH<<16 | gc.TUINT8, gc.ORSH<<16 | gc.TUINT16, gc.ORSH<<16 | gc.TUINT32, gc.ORSH<<16 | gc.TPTR32, gc.ORSH<<16 | gc.TUINT64, gc.ORSH<<16 | gc.TPTR64: a = arm64.ALSR case gc.ORSH<<16 | gc.TINT8, gc.ORSH<<16 | gc.TINT16, gc.ORSH<<16 | gc.TINT32, gc.ORSH<<16 | gc.TINT64: a = arm64.AASR // TODO(minux): handle rotates //case CASE(ORROTC, TINT8): //case CASE(ORROTC, TUINT8): //case CASE(ORROTC, TINT16): //case CASE(ORROTC, TUINT16): //case CASE(ORROTC, TINT32): //case CASE(ORROTC, TUINT32): //case CASE(ORROTC, TINT64): //case CASE(ORROTC, TUINT64): // a = 0//??? RLDC?? // break; case gc.OHMUL<<16 | gc.TINT64: a = arm64.ASMULH case gc.OHMUL<<16 | gc.TUINT64, gc.OHMUL<<16 | gc.TPTR64: a = arm64.AUMULH case gc.OMUL<<16 | gc.TINT8, gc.OMUL<<16 | gc.TINT16, gc.OMUL<<16 | gc.TINT32: a = arm64.ASMULL case gc.OMUL<<16 | gc.TINT64: a = arm64.AMUL case gc.OMUL<<16 | gc.TUINT8, gc.OMUL<<16 | gc.TUINT16, gc.OMUL<<16 | gc.TUINT32, gc.OMUL<<16 | gc.TPTR32: // don't use word multiply, the high 32-bit are undefined. a = arm64.AUMULL case gc.OMUL<<16 | gc.TUINT64, gc.OMUL<<16 | gc.TPTR64: a = arm64.AMUL // for 64-bit multiplies, signedness doesn't matter. case gc.OMUL<<16 | gc.TFLOAT32: a = arm64.AFMULS case gc.OMUL<<16 | gc.TFLOAT64: a = arm64.AFMULD case gc.ODIV<<16 | gc.TINT8, gc.ODIV<<16 | gc.TINT16, gc.ODIV<<16 | gc.TINT32, gc.ODIV<<16 | gc.TINT64: a = arm64.ASDIV case gc.ODIV<<16 | gc.TUINT8, gc.ODIV<<16 | gc.TUINT16, gc.ODIV<<16 | gc.TUINT32, gc.ODIV<<16 | gc.TPTR32, gc.ODIV<<16 | gc.TUINT64, gc.ODIV<<16 | gc.TPTR64: a = arm64.AUDIV case gc.ODIV<<16 | gc.TFLOAT32: a = arm64.AFDIVS case gc.ODIV<<16 | gc.TFLOAT64: a = arm64.AFDIVD } return a } const ( ODynam = 1 << 0 OAddable = 1 << 1 ) func xgen(n *gc.Node, a *gc.Node, o int) bool { // TODO(minux) return -1 != 0 /*TypeKind(100016)*/ } func sudoclean() { return } /* * generate code to compute address of n, * a reference to a (perhaps nested) field inside * an array or struct. * return 0 on failure, 1 on success. * on success, leaves usable address in a. * * caller is responsible for calling sudoclean * after successful sudoaddable, * to release the register used for a. */ func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { // TODO(minux) *a = obj.Addr{} return false }
/* * n is call to interface method. * generate res = n. */ func cgen_callinter(n *gc.Node, res *gc.Node, proc int) { 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 { var tmpi gc.Node 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. var nodi gc.Node igen(i, &nodi, res) // REG = &inter var nodsp gc.Node gc.Nodindreg(&nodsp, gc.Types[gc.Tptr], x86.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 var nodo gc.Node 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) var nodr gc.Node 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(x86.ALEAL, &nodo, &nodr) // REG = &(20+offset(REG)) -- i.tab->fun[f] } nodr.Type = n.Left.Type ginscall(&nodr, proc) regfree(&nodr) regfree(&nodo) }
/* * address gen * res = &n; * The generated code checks that the result is not nil. */ func agen(n *gc.Node, res *gc.Node) { if gc.Debug['g'] != 0 { gc.Dump("\nagen-res", res) gc.Dump("agen-r", n) } if n == nil || n.Type == nil || res == nil || res.Type == nil { gc.Fatal("agen") } for n.Op == gc.OCONVNOP { n = n.Left } if gc.Isconst(n, gc.CTNIL) && n.Type.Width > int64(gc.Widthptr) { // Use of a nil interface or nil slice. // Create a temporary we can take the address of and read. // The generated code is just going to panic, so it need not // be terribly efficient. See issue 3670. var n1 gc.Node gc.Tempname(&n1, n.Type) gc.Gvardef(&n1) clearfat(&n1) var n2 gc.Node regalloc(&n2, gc.Types[gc.Tptr], res) gins(x86.ALEAL, &n1, &n2) gmove(&n2, res) regfree(&n2) return } // addressable var is easy if n.Addable != 0 { if n.Op == gc.OREGISTER { gc.Fatal("agen OREGISTER") } var n1 gc.Node regalloc(&n1, gc.Types[gc.Tptr], res) gins(x86.ALEAL, n, &n1) gmove(&n1, res) regfree(&n1) return } // let's compute nl := n.Left nr := n.Right switch n.Op { default: gc.Fatal("agen %v", gc.Oconv(int(n.Op), 0)) case gc.OCALLMETH: gc.Cgen_callmeth(n, 0) cgen_aret(n, res) case gc.OCALLINTER: cgen_callinter(n, res, 0) cgen_aret(n, res) case gc.OCALLFUNC: cgen_call(n, 0) cgen_aret(n, res) case gc.OSLICE, gc.OSLICEARR, gc.OSLICESTR, gc.OSLICE3, gc.OSLICE3ARR: var n1 gc.Node gc.Tempname(&n1, n.Type) gc.Cgen_slice(n, &n1) agen(&n1, res) case gc.OEFACE: var n1 gc.Node gc.Tempname(&n1, n.Type) gc.Cgen_eface(n, &n1) agen(&n1, res) case gc.OINDEX: var p2 *obj.Prog // to be patched to panicindex. w := uint32(n.Type.Width) bounded := gc.Debug['B'] != 0 || n.Bounded var n3 gc.Node var tmp gc.Node var n1 gc.Node if nr.Addable != 0 { // Generate &nl first, and move nr into register. if !gc.Isconst(nl, gc.CTSTR) { igen(nl, &n3, res) } if !gc.Isconst(nr, gc.CTINT) { p2 = igenindex(nr, &tmp, bool2int(bounded)) regalloc(&n1, tmp.Type, nil) gmove(&tmp, &n1) } } else if nl.Addable != 0 { // Generate nr first, and move &nl into register. if !gc.Isconst(nr, gc.CTINT) { p2 = igenindex(nr, &tmp, bool2int(bounded)) regalloc(&n1, tmp.Type, nil) gmove(&tmp, &n1) } if !gc.Isconst(nl, gc.CTSTR) { igen(nl, &n3, res) } } else { p2 = igenindex(nr, &tmp, bool2int(bounded)) nr = &tmp if !gc.Isconst(nl, gc.CTSTR) { igen(nl, &n3, res) } regalloc(&n1, tmp.Type, nil) gins(optoas(gc.OAS, tmp.Type), &tmp, &n1) } // For fixed array we really want the pointer in n3. var n2 gc.Node if gc.Isfixedarray(nl.Type) { regalloc(&n2, gc.Types[gc.Tptr], &n3) agen(&n3, &n2) regfree(&n3) n3 = n2 } // &a[0] is in n3 (allocated in res) // i is in n1 (if not constant) // len(a) is in nlen (if needed) // w is width // constant index if gc.Isconst(nr, gc.CTINT) { if gc.Isconst(nl, gc.CTSTR) { gc.Fatal("constant string constant index") // front end should handle } v := uint64(gc.Mpgetfix(nr.Val.U.Xval)) if gc.Isslice(nl.Type) || nl.Type.Etype == gc.TSTRING { if gc.Debug['B'] == 0 && !n.Bounded { nlen := n3 nlen.Type = gc.Types[gc.TUINT32] nlen.Xoffset += int64(gc.Array_nel) gc.Nodconst(&n2, gc.Types[gc.TUINT32], int64(v)) gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &nlen, &n2) p1 := gc.Gbranch(optoas(gc.OGT, gc.Types[gc.TUINT32]), nil, +1) ginscall(gc.Panicindex, -1) gc.Patch(p1, gc.Pc) } } // Load base pointer in n2 = n3. regalloc(&n2, gc.Types[gc.Tptr], &n3) n3.Type = gc.Types[gc.Tptr] n3.Xoffset += int64(gc.Array_array) gmove(&n3, &n2) regfree(&n3) if v*uint64(w) != 0 { gc.Nodconst(&n1, gc.Types[gc.Tptr], int64(v*uint64(w))) gins(optoas(gc.OADD, gc.Types[gc.Tptr]), &n1, &n2) } gmove(&n2, res) regfree(&n2) break } // i is in register n1, extend to 32 bits. t := gc.Types[gc.TUINT32] if gc.Issigned[n1.Type.Etype] { t = gc.Types[gc.TINT32] } regalloc(&n2, t, &n1) // i gmove(&n1, &n2) regfree(&n1) if gc.Debug['B'] == 0 && !n.Bounded { // check bounds t := gc.Types[gc.TUINT32] var nlen gc.Node if gc.Isconst(nl, gc.CTSTR) { gc.Nodconst(&nlen, t, int64(len(nl.Val.U.Sval))) } else if gc.Isslice(nl.Type) || nl.Type.Etype == gc.TSTRING { nlen = n3 nlen.Type = t nlen.Xoffset += int64(gc.Array_nel) } else { gc.Nodconst(&nlen, t, nl.Type.Bound) } gins(optoas(gc.OCMP, t), &n2, &nlen) p1 := gc.Gbranch(optoas(gc.OLT, t), nil, +1) if p2 != nil { gc.Patch(p2, gc.Pc) } ginscall(gc.Panicindex, -1) gc.Patch(p1, gc.Pc) } if gc.Isconst(nl, gc.CTSTR) { regalloc(&n3, gc.Types[gc.Tptr], res) p1 := gins(x86.ALEAL, nil, &n3) gc.Datastring(nl.Val.U.Sval, &p1.From) p1.From.Scale = 1 p1.From.Index = n2.Val.U.Reg goto indexdone } // Load base pointer in n3. regalloc(&tmp, gc.Types[gc.Tptr], &n3) if gc.Isslice(nl.Type) || nl.Type.Etype == gc.TSTRING { n3.Type = gc.Types[gc.Tptr] n3.Xoffset += int64(gc.Array_array) gmove(&n3, &tmp) } regfree(&n3) n3 = tmp if w == 0 { } else // nothing to do if w == 1 || w == 2 || w == 4 || w == 8 { // LEAL (n3)(n2*w), n3 p1 := gins(x86.ALEAL, &n2, &n3) p1.From.Scale = int16(w) p1.From.Type = obj.TYPE_MEM p1.From.Index = p1.From.Reg p1.From.Reg = p1.To.Reg } else { gc.Nodconst(&tmp, gc.Types[gc.TUINT32], int64(w)) gins(optoas(gc.OMUL, gc.Types[gc.TUINT32]), &tmp, &n2) gins(optoas(gc.OADD, gc.Types[gc.Tptr]), &n2, &n3) } indexdone: gmove(&n3, res) regfree(&n2) regfree(&n3) // should only get here with names in this func. case gc.ONAME: if n.Funcdepth > 0 && n.Funcdepth != gc.Funcdepth { gc.Dump("bad agen", n) gc.Fatal("agen: bad ONAME funcdepth %d != %d", n.Funcdepth, gc.Funcdepth) } // should only get here for heap vars or paramref if n.Class&gc.PHEAP == 0 && n.Class != gc.PPARAMREF { gc.Dump("bad agen", n) gc.Fatal("agen: bad ONAME class %#x", n.Class) } cgen(n.Heapaddr, res) if n.Xoffset != 0 { var n1 gc.Node gc.Nodconst(&n1, gc.Types[gc.Tptr], n.Xoffset) gins(optoas(gc.OADD, gc.Types[gc.Tptr]), &n1, res) } case gc.OIND: cgen(nl, res) gc.Cgen_checknil(res) case gc.ODOT: agen(nl, res) if n.Xoffset != 0 { var n1 gc.Node gc.Nodconst(&n1, gc.Types[gc.Tptr], n.Xoffset) gins(optoas(gc.OADD, gc.Types[gc.Tptr]), &n1, res) } case gc.ODOTPTR: t := nl.Type if !gc.Isptr[t.Etype] { gc.Fatal("agen: not ptr %v", gc.Nconv(n, 0)) } cgen(nl, res) gc.Cgen_checknil(res) if n.Xoffset != 0 { var n1 gc.Node gc.Nodconst(&n1, gc.Types[gc.Tptr], n.Xoffset) gins(optoas(gc.OADD, gc.Types[gc.Tptr]), &n1, res) } } }
/* * n is call to interface method. * generate res = n. */ func cgen_callinter(n *gc.Node, res *gc.Node, proc int) { 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 // Release res register during genlist and cgen, // which might have their own function calls. r := -1 if res != nil && (res.Op == gc.OREGISTER || res.Op == gc.OINDREG) { r = int(res.Val.U.Reg) reg[r]-- } if i.Addable == 0 { var tmpi gc.Node gc.Tempname(&tmpi, i.Type) cgen(i, &tmpi) i = &tmpi } gc.Genlist(n.List) // args if r >= 0 { reg[r]++ } var nodr gc.Node regalloc(&nodr, gc.Types[gc.Tptr], res) var nodo gc.Node regalloc(&nodo, gc.Types[gc.Tptr], &nodr) nodo.Op = gc.OINDREG agen(i, &nodr) // REG = &inter var nodsp gc.Node gc.Nodindreg(&nodsp, gc.Types[gc.Tptr], arm.REGSP) nodsp.Xoffset = int64(gc.Widthptr) if proc != 0 { nodsp.Xoffset += 2 * int64(gc.Widthptr) // leave room for size & fn } nodo.Xoffset += int64(gc.Widthptr) cgen(&nodo, &nodsp) // {4 or 12}(SP) = 4(REG) -- i.data nodo.Xoffset -= int64(gc.Widthptr) cgen(&nodo, &nodr) // REG = 0(REG) -- i.tab gc.Cgen_checknil(&nodr) // in case offset is huge 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] nodr.Op = gc.OINDREG proc = 3 } else { // go/defer. generate go func value. p := gins(arm.AMOVW, &nodo, &nodr) p.From.Type = obj.TYPE_ADDR // REG = &(20+offset(REG)) -- i.tab->fun[f] } nodr.Type = n.Left.Type ginscall(&nodr, proc) regfree(&nodr) regfree(&nodo) }
func anyregalloc() bool { var j int for i := 0; i < len(reg); i++ { if reg[i] == 0 { goto ok } for j = 0; j < len(resvd); j++ { if resvd[j] == i { goto ok } } return true ok: } return false } var regpc [REGALLOC_FMAX + 1]uint32 /* * allocate register of type t, leave in n. * if o != N, o is desired fixed register. * caller must regfree(n). */ func regalloc(n *gc.Node, t *gc.Type, o *gc.Node) { if false && gc.Debug['r'] != 0 { fixfree := 0 for i := REGALLOC_R0; i <= REGALLOC_RMAX; i++ { if reg[i] == 0 { fixfree++ } } floatfree := 0 for i := REGALLOC_F0; i <= REGALLOC_FMAX; i++ { if reg[i] == 0 { floatfree++ } } fmt.Printf("regalloc fix %d float %d\n", fixfree, floatfree) } if t == nil { gc.Fatal("regalloc: t nil") } et := int(gc.Simtype[t.Etype]) if gc.Is64(t) { gc.Fatal("regalloc: 64 bit type %v") } var i int switch et { case gc.TINT8, gc.TUINT8, gc.TINT16, gc.TUINT16, gc.TINT32, gc.TUINT32, gc.TPTR32, gc.TBOOL: if o != nil && o.Op == gc.OREGISTER { i = int(o.Val.U.Reg) if i >= REGALLOC_R0 && i <= REGALLOC_RMAX { goto out } } for i = REGALLOC_R0; i <= REGALLOC_RMAX; i++ { if reg[i] == 0 { regpc[i] = uint32(obj.Getcallerpc(&n)) goto out } } fmt.Printf("registers allocated at\n") for i := REGALLOC_R0; i <= REGALLOC_RMAX; i++ { fmt.Printf("%d %p\n", i, regpc[i]) } gc.Fatal("out of fixed registers") goto err case gc.TFLOAT32, gc.TFLOAT64: if o != nil && o.Op == gc.OREGISTER { i = int(o.Val.U.Reg) if i >= REGALLOC_F0 && i <= REGALLOC_FMAX { goto out } } for i = REGALLOC_F0; i <= REGALLOC_FMAX; i++ { if reg[i] == 0 { goto out } } gc.Fatal("out of floating point registers") goto err case gc.TCOMPLEX64, gc.TCOMPLEX128: gc.Tempname(n, t) return } gc.Yyerror("regalloc: unknown type %v", gc.Tconv(t, 0)) err: gc.Nodreg(n, t, arm.REG_R0) return out: reg[i]++ gc.Nodreg(n, t, i) } func regfree(n *gc.Node) { if false && gc.Debug['r'] != 0 { fixfree := 0 for i := REGALLOC_R0; i <= REGALLOC_RMAX; i++ { if reg[i] == 0 { fixfree++ } } floatfree := 0 for i := REGALLOC_F0; i <= REGALLOC_FMAX; i++ { if reg[i] == 0 { floatfree++ } } fmt.Printf("regalloc fix %d float %d\n", fixfree, floatfree) } if n.Op == gc.ONAME { return } if n.Op != gc.OREGISTER && n.Op != gc.OINDREG { gc.Fatal("regfree: not a register") } i := int(n.Val.U.Reg) if i == arm.REGSP { return } if i < 0 || i >= len(reg) || i >= len(regpc) { gc.Fatal("regfree: reg out of range") } if reg[i] <= 0 { gc.Fatal("regfree: reg %v not allocated", obj.Rconv(i)) } reg[i]-- if reg[i] == 0 { regpc[i] = 0 } } /* * return constant i node. * overwritten by next call, but useful in calls to gins. */ var ncon_n gc.Node func ncon(i uint32) *gc.Node { if ncon_n.Type == nil { gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0) } gc.Mpmovecfix(ncon_n.Val.U.Xval, int64(i)) return &ncon_n } var sclean [10]gc.Node var nsclean int /* * n is a 64-bit value. fill in lo and hi to refer to its 32-bit halves. */ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) { if !gc.Is64(n.Type) { gc.Fatal("split64 %v", gc.Tconv(n.Type, 0)) } if nsclean >= len(sclean) { gc.Fatal("split64 clean") } sclean[nsclean].Op = gc.OEMPTY nsclean++ switch n.Op { default: switch n.Op { default: var n1 gc.Node if !dotaddable(n, &n1) { igen(n, &n1, nil) sclean[nsclean-1] = n1 } n = &n1 case gc.ONAME: if n.Class == gc.PPARAMREF { var n1 gc.Node cgen(n.Heapaddr, &n1) sclean[nsclean-1] = n1 n = &n1 } // nothing case gc.OINDREG: break } *lo = *n *hi = *n lo.Type = gc.Types[gc.TUINT32] if n.Type.Etype == gc.TINT64 { hi.Type = gc.Types[gc.TINT32] } else { hi.Type = gc.Types[gc.TUINT32] } hi.Xoffset += 4 case gc.OLITERAL: var n1 gc.Node gc.Convconst(&n1, n.Type, &n.Val) i := gc.Mpgetfix(n1.Val.U.Xval) gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i))) i >>= 32 if n.Type.Etype == gc.TINT64 { gc.Nodconst(hi, gc.Types[gc.TINT32], int64(int32(i))) } else { gc.Nodconst(hi, gc.Types[gc.TUINT32], int64(uint32(i))) } } } func splitclean() { if nsclean <= 0 { gc.Fatal("splitclean") } nsclean-- if sclean[nsclean].Op != gc.OEMPTY { regfree(&sclean[nsclean]) } } func gmove(f *gc.Node, t *gc.Node) { if gc.Debug['M'] != 0 { fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, 0), gc.Nconv(t, 0)) } ft := gc.Simsimtype(f.Type) tt := gc.Simsimtype(t.Type) cvt := t.Type if gc.Iscomplex[ft] || gc.Iscomplex[tt] { gc.Complexmove(f, t) return } // cannot have two memory operands; // except 64-bit, which always copies via registers anyway. var a int var r1 gc.Node if !gc.Is64(f.Type) && !gc.Is64(t.Type) && gc.Ismem(f) && gc.Ismem(t) { goto hard } // convert constant to desired type if f.Op == gc.OLITERAL { var con gc.Node switch tt { default: gc.Convconst(&con, t.Type, &f.Val) case gc.TINT16, gc.TINT8: var con gc.Node gc.Convconst(&con, gc.Types[gc.TINT32], &f.Val) var r1 gc.Node regalloc(&r1, con.Type, t) gins(arm.AMOVW, &con, &r1) gmove(&r1, t) regfree(&r1) return case gc.TUINT16, gc.TUINT8: var con gc.Node gc.Convconst(&con, gc.Types[gc.TUINT32], &f.Val) var r1 gc.Node regalloc(&r1, con.Type, t) gins(arm.AMOVW, &con, &r1) gmove(&r1, t) regfree(&r1) return } f = &con ft = gc.Simsimtype(con.Type) // constants can't move directly to memory if gc.Ismem(t) && !gc.Is64(t.Type) { goto hard } } // value -> value copy, only one memory operand. // figure out the instruction to use. // break out of switch for one-instruction gins. // goto rdst for "destination must be register". // goto hard for "convert to cvt type first". // otherwise handle and return. switch uint32(ft)<<16 | uint32(tt) { default: // should not happen gc.Fatal("gmove %v -> %v", gc.Nconv(f, 0), gc.Nconv(t, 0)) return /* * integer copy and truncate */ case gc.TINT8<<16 | gc.TINT8: // same size if !gc.Ismem(f) { a = arm.AMOVB break } fallthrough case gc.TUINT8<<16 | gc.TINT8, gc.TINT16<<16 | gc.TINT8, // truncate gc.TUINT16<<16 | gc.TINT8, gc.TINT32<<16 | gc.TINT8, gc.TUINT32<<16 | gc.TINT8: a = arm.AMOVBS case gc.TUINT8<<16 | gc.TUINT8: if !gc.Ismem(f) { a = arm.AMOVB break } fallthrough case gc.TINT8<<16 | gc.TUINT8, gc.TINT16<<16 | gc.TUINT8, gc.TUINT16<<16 | gc.TUINT8, gc.TINT32<<16 | gc.TUINT8, gc.TUINT32<<16 | gc.TUINT8: a = arm.AMOVBU case gc.TINT64<<16 | gc.TINT8, // truncate low word gc.TUINT64<<16 | gc.TINT8: a = arm.AMOVBS goto trunc64 case gc.TINT64<<16 | gc.TUINT8, gc.TUINT64<<16 | gc.TUINT8: a = arm.AMOVBU goto trunc64 case gc.TINT16<<16 | gc.TINT16: // same size if !gc.Ismem(f) { a = arm.AMOVH break } fallthrough case gc.TUINT16<<16 | gc.TINT16, gc.TINT32<<16 | gc.TINT16, // truncate gc.TUINT32<<16 | gc.TINT16: a = arm.AMOVHS case gc.TUINT16<<16 | gc.TUINT16: if !gc.Ismem(f) { a = arm.AMOVH break } fallthrough case gc.TINT16<<16 | gc.TUINT16, gc.TINT32<<16 | gc.TUINT16, gc.TUINT32<<16 | gc.TUINT16: a = arm.AMOVHU case gc.TINT64<<16 | gc.TINT16, // truncate low word gc.TUINT64<<16 | gc.TINT16: a = arm.AMOVHS goto trunc64 case gc.TINT64<<16 | gc.TUINT16, gc.TUINT64<<16 | gc.TUINT16: a = arm.AMOVHU goto trunc64 case gc.TINT32<<16 | gc.TINT32, // same size gc.TINT32<<16 | gc.TUINT32, gc.TUINT32<<16 | gc.TINT32, gc.TUINT32<<16 | gc.TUINT32: a = arm.AMOVW case gc.TINT64<<16 | gc.TINT32, // truncate gc.TUINT64<<16 | gc.TINT32, gc.TINT64<<16 | gc.TUINT32, gc.TUINT64<<16 | gc.TUINT32: var flo gc.Node var fhi gc.Node split64(f, &flo, &fhi) var r1 gc.Node regalloc(&r1, t.Type, nil) gins(arm.AMOVW, &flo, &r1) gins(arm.AMOVW, &r1, t) regfree(&r1) splitclean() return case gc.TINT64<<16 | gc.TINT64, // same size gc.TINT64<<16 | gc.TUINT64, gc.TUINT64<<16 | gc.TINT64, gc.TUINT64<<16 | gc.TUINT64: var fhi gc.Node var flo gc.Node split64(f, &flo, &fhi) var tlo gc.Node var thi gc.Node split64(t, &tlo, &thi) var r1 gc.Node regalloc(&r1, flo.Type, nil) var r2 gc.Node regalloc(&r2, fhi.Type, nil) gins(arm.AMOVW, &flo, &r1) gins(arm.AMOVW, &fhi, &r2) gins(arm.AMOVW, &r1, &tlo) gins(arm.AMOVW, &r2, &thi) regfree(&r1) regfree(&r2) splitclean() splitclean() return /* * integer up-conversions */ case gc.TINT8<<16 | gc.TINT16, // sign extend int8 gc.TINT8<<16 | gc.TUINT16, gc.TINT8<<16 | gc.TINT32, gc.TINT8<<16 | gc.TUINT32: a = arm.AMOVBS goto rdst case gc.TINT8<<16 | gc.TINT64, // convert via int32 gc.TINT8<<16 | gc.TUINT64: cvt = gc.Types[gc.TINT32] goto hard case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 gc.TUINT8<<16 | gc.TUINT16, gc.TUINT8<<16 | gc.TINT32, gc.TUINT8<<16 | gc.TUINT32: a = arm.AMOVBU goto rdst case gc.TUINT8<<16 | gc.TINT64, // convert via uint32 gc.TUINT8<<16 | gc.TUINT64: cvt = gc.Types[gc.TUINT32] goto hard case gc.TINT16<<16 | gc.TINT32, // sign extend int16 gc.TINT16<<16 | gc.TUINT32: a = arm.AMOVHS goto rdst case gc.TINT16<<16 | gc.TINT64, // convert via int32 gc.TINT16<<16 | gc.TUINT64: cvt = gc.Types[gc.TINT32] goto hard case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 gc.TUINT16<<16 | gc.TUINT32: a = arm.AMOVHU goto rdst case gc.TUINT16<<16 | gc.TINT64, // convert via uint32 gc.TUINT16<<16 | gc.TUINT64: cvt = gc.Types[gc.TUINT32] goto hard case gc.TINT32<<16 | gc.TINT64, // sign extend int32 gc.TINT32<<16 | gc.TUINT64: var tlo gc.Node var thi gc.Node split64(t, &tlo, &thi) var r1 gc.Node regalloc(&r1, tlo.Type, nil) var r2 gc.Node regalloc(&r2, thi.Type, nil) gmove(f, &r1) p1 := gins(arm.AMOVW, &r1, &r2) p1.From.Type = obj.TYPE_SHIFT p1.From.Offset = 2<<5 | 31<<7 | int64(r1.Val.U.Reg)&15 // r1->31 p1.From.Reg = 0 //print("gmove: %P\n", p1); gins(arm.AMOVW, &r1, &tlo) gins(arm.AMOVW, &r2, &thi) regfree(&r1) regfree(&r2) splitclean() return case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 gc.TUINT32<<16 | gc.TUINT64: var thi gc.Node var tlo gc.Node split64(t, &tlo, &thi) gmove(f, &tlo) var r1 gc.Node regalloc(&r1, thi.Type, nil) gins(arm.AMOVW, ncon(0), &r1) gins(arm.AMOVW, &r1, &thi) regfree(&r1) splitclean() return // case CASE(TFLOAT64, TUINT64): /* * float to integer */ case gc.TFLOAT32<<16 | gc.TINT8, gc.TFLOAT32<<16 | gc.TUINT8, gc.TFLOAT32<<16 | gc.TINT16, gc.TFLOAT32<<16 | gc.TUINT16, gc.TFLOAT32<<16 | gc.TINT32, gc.TFLOAT32<<16 | gc.TUINT32, // case CASE(TFLOAT32, TUINT64): gc.TFLOAT64<<16 | gc.TINT8, gc.TFLOAT64<<16 | gc.TUINT8, gc.TFLOAT64<<16 | gc.TINT16, gc.TFLOAT64<<16 | gc.TUINT16, gc.TFLOAT64<<16 | gc.TINT32, gc.TFLOAT64<<16 | gc.TUINT32: fa := arm.AMOVF a := arm.AMOVFW if ft == gc.TFLOAT64 { fa = arm.AMOVD a = arm.AMOVDW } ta := arm.AMOVW switch tt { case gc.TINT8: ta = arm.AMOVBS case gc.TUINT8: ta = arm.AMOVBU case gc.TINT16: ta = arm.AMOVHS case gc.TUINT16: ta = arm.AMOVHU } var r1 gc.Node regalloc(&r1, gc.Types[ft], f) var r2 gc.Node regalloc(&r2, gc.Types[tt], t) gins(fa, f, &r1) // load to fpu p1 := gins(a, &r1, &r1) // convert to w switch tt { case gc.TUINT8, gc.TUINT16, gc.TUINT32: p1.Scond |= arm.C_UBIT } gins(arm.AMOVW, &r1, &r2) // copy to cpu gins(ta, &r2, t) // store regfree(&r1) regfree(&r2) return /* * integer to float */ case gc.TINT8<<16 | gc.TFLOAT32, gc.TUINT8<<16 | gc.TFLOAT32, gc.TINT16<<16 | gc.TFLOAT32, gc.TUINT16<<16 | gc.TFLOAT32, gc.TINT32<<16 | gc.TFLOAT32, gc.TUINT32<<16 | gc.TFLOAT32, gc.TINT8<<16 | gc.TFLOAT64, gc.TUINT8<<16 | gc.TFLOAT64, gc.TINT16<<16 | gc.TFLOAT64, gc.TUINT16<<16 | gc.TFLOAT64, gc.TINT32<<16 | gc.TFLOAT64, gc.TUINT32<<16 | gc.TFLOAT64: fa := arm.AMOVW switch ft { case gc.TINT8: fa = arm.AMOVBS case gc.TUINT8: fa = arm.AMOVBU case gc.TINT16: fa = arm.AMOVHS case gc.TUINT16: fa = arm.AMOVHU } a := arm.AMOVWF ta := arm.AMOVF if tt == gc.TFLOAT64 { a = arm.AMOVWD ta = arm.AMOVD } var r1 gc.Node regalloc(&r1, gc.Types[ft], f) var r2 gc.Node regalloc(&r2, gc.Types[tt], t) gins(fa, f, &r1) // load to cpu gins(arm.AMOVW, &r1, &r2) // copy to fpu p1 := gins(a, &r2, &r2) // convert switch ft { case gc.TUINT8, gc.TUINT16, gc.TUINT32: p1.Scond |= arm.C_UBIT } gins(ta, &r2, t) // store regfree(&r1) regfree(&r2) return case gc.TUINT64<<16 | gc.TFLOAT32, gc.TUINT64<<16 | gc.TFLOAT64: gc.Fatal("gmove UINT64, TFLOAT not implemented") return /* * float to float */ case gc.TFLOAT32<<16 | gc.TFLOAT32: a = arm.AMOVF case gc.TFLOAT64<<16 | gc.TFLOAT64: a = arm.AMOVD case gc.TFLOAT32<<16 | gc.TFLOAT64: var r1 gc.Node regalloc(&r1, gc.Types[gc.TFLOAT64], t) gins(arm.AMOVF, f, &r1) gins(arm.AMOVFD, &r1, &r1) gins(arm.AMOVD, &r1, t) regfree(&r1) return case gc.TFLOAT64<<16 | gc.TFLOAT32: var r1 gc.Node regalloc(&r1, gc.Types[gc.TFLOAT64], t) gins(arm.AMOVD, f, &r1) gins(arm.AMOVDF, &r1, &r1) gins(arm.AMOVF, &r1, t) regfree(&r1) return } gins(a, f, t) return // TODO(kaib): we almost always require a register dest anyway, this can probably be // removed. // requires register destination rdst: { regalloc(&r1, t.Type, t) gins(a, f, &r1) gmove(&r1, t) regfree(&r1) return } // requires register intermediate hard: regalloc(&r1, cvt, t) gmove(f, &r1) gmove(&r1, t) regfree(&r1) return // truncate 64 bit integer trunc64: var fhi gc.Node var flo gc.Node split64(f, &flo, &fhi) regalloc(&r1, t.Type, nil) gins(a, &flo, &r1) gins(a, &r1, t) regfree(&r1) splitclean() return } func samaddr(f *gc.Node, t *gc.Node) bool { if f.Op != t.Op { return false } switch f.Op { case gc.OREGISTER: if f.Val.U.Reg != t.Val.U.Reg { break } return true } return false } /* * generate one instruction: * as f, t */ func gins(as int, f *gc.Node, t *gc.Node) *obj.Prog { // Node nod; // int32 v; if f != nil && f.Op == gc.OINDEX { gc.Fatal("gins OINDEX not implemented") } // regalloc(&nod, ®node, Z); // v = constnode.vconst; // cgen(f->right, &nod); // constnode.vconst = v; // idx.reg = nod.reg; // regfree(&nod); if t != nil && t.Op == gc.OINDEX { gc.Fatal("gins OINDEX not implemented") } // regalloc(&nod, ®node, Z); // v = constnode.vconst; // cgen(t->right, &nod); // constnode.vconst = v; // idx.reg = nod.reg; // regfree(&nod); var af obj.Addr var at obj.Addr if f != nil { af = gc.Naddr(f) } if t != nil { at = gc.Naddr(t) } p := gc.Prog(as) if f != nil { p.From = af } if t != nil { p.To = at } if gc.Debug['g'] != 0 { fmt.Printf("%v\n", p) } return p } /* * insert n into reg slot of p */ func raddr(n *gc.Node, p *obj.Prog) { var a obj.Addr a = gc.Naddr(n) if a.Type != obj.TYPE_REG { if n != nil { gc.Fatal("bad in raddr: %v", gc.Oconv(int(n.Op), 0)) } else { gc.Fatal("bad in raddr: <null>") } p.Reg = 0 } else { p.Reg = a.Reg } } /* generate a comparison TODO(kaib): one of the args can actually be a small constant. relax the constraint and fix call sites. */ func gcmp(as int, lhs *gc.Node, rhs *gc.Node) *obj.Prog { if lhs.Op != gc.OREGISTER { gc.Fatal("bad operands to gcmp: %v %v", gc.Oconv(int(lhs.Op), 0), gc.Oconv(int(rhs.Op), 0)) } p := gins(as, rhs, nil) raddr(lhs, p) return p } /* generate a constant shift * arm encodes a shift by 32 as 0, thus asking for 0 shift is illegal. */ func gshift(as int, lhs *gc.Node, stype int32, sval int32, rhs *gc.Node) *obj.Prog { if sval <= 0 || sval > 32 { gc.Fatal("bad shift value: %d", sval) } sval = sval & 0x1f p := gins(as, nil, rhs) p.From.Type = obj.TYPE_SHIFT p.From.Offset = int64(stype) | int64(sval)<<7 | int64(lhs.Val.U.Reg)&15 return p } /* generate a register shift */ func gregshift(as int, lhs *gc.Node, stype int32, reg *gc.Node, rhs *gc.Node) *obj.Prog { p := gins(as, nil, rhs) p.From.Type = obj.TYPE_SHIFT p.From.Offset = int64(stype) | (int64(reg.Val.U.Reg)&15)<<8 | 1<<4 | int64(lhs.Val.U.Reg)&15 return p } /* * return Axxx for Oxxx on type t. */ func optoas(op int, t *gc.Type) int { if t == nil { gc.Fatal("optoas: t is nil") } a := obj.AXXX switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { default: gc.Fatal("optoas: no entry %v-%v etype %v simtype %v", gc.Oconv(int(op), 0), gc.Tconv(t, 0), gc.Tconv(gc.Types[t.Etype], 0), gc.Tconv(gc.Types[gc.Simtype[t.Etype]], 0)) /* case CASE(OADDR, TPTR32): a = ALEAL; break; case CASE(OADDR, TPTR64): a = ALEAQ; break; */ // TODO(kaib): make sure the conditional branches work on all edge cases case gc.OEQ<<16 | gc.TBOOL, gc.OEQ<<16 | gc.TINT8, gc.OEQ<<16 | gc.TUINT8, gc.OEQ<<16 | gc.TINT16, gc.OEQ<<16 | gc.TUINT16, gc.OEQ<<16 | gc.TINT32, gc.OEQ<<16 | gc.TUINT32, gc.OEQ<<16 | gc.TINT64, gc.OEQ<<16 | gc.TUINT64, gc.OEQ<<16 | gc.TPTR32, gc.OEQ<<16 | gc.TPTR64, gc.OEQ<<16 | gc.TFLOAT32, gc.OEQ<<16 | gc.TFLOAT64: a = arm.ABEQ case gc.ONE<<16 | gc.TBOOL, gc.ONE<<16 | gc.TINT8, gc.ONE<<16 | gc.TUINT8, gc.ONE<<16 | gc.TINT16, gc.ONE<<16 | gc.TUINT16, gc.ONE<<16 | gc.TINT32, gc.ONE<<16 | gc.TUINT32, gc.ONE<<16 | gc.TINT64, gc.ONE<<16 | gc.TUINT64, gc.ONE<<16 | gc.TPTR32, gc.ONE<<16 | gc.TPTR64, gc.ONE<<16 | gc.TFLOAT32, gc.ONE<<16 | gc.TFLOAT64: a = arm.ABNE case gc.OLT<<16 | gc.TINT8, gc.OLT<<16 | gc.TINT16, gc.OLT<<16 | gc.TINT32, gc.OLT<<16 | gc.TINT64, gc.OLT<<16 | gc.TFLOAT32, gc.OLT<<16 | gc.TFLOAT64: a = arm.ABLT case gc.OLT<<16 | gc.TUINT8, gc.OLT<<16 | gc.TUINT16, gc.OLT<<16 | gc.TUINT32, gc.OLT<<16 | gc.TUINT64: a = arm.ABLO case gc.OLE<<16 | gc.TINT8, gc.OLE<<16 | gc.TINT16, gc.OLE<<16 | gc.TINT32, gc.OLE<<16 | gc.TINT64, gc.OLE<<16 | gc.TFLOAT32, gc.OLE<<16 | gc.TFLOAT64: a = arm.ABLE case gc.OLE<<16 | gc.TUINT8, gc.OLE<<16 | gc.TUINT16, gc.OLE<<16 | gc.TUINT32, gc.OLE<<16 | gc.TUINT64: a = arm.ABLS case gc.OGT<<16 | gc.TINT8, gc.OGT<<16 | gc.TINT16, gc.OGT<<16 | gc.TINT32, gc.OGT<<16 | gc.TINT64, gc.OGT<<16 | gc.TFLOAT32, gc.OGT<<16 | gc.TFLOAT64: a = arm.ABGT case gc.OGT<<16 | gc.TUINT8, gc.OGT<<16 | gc.TUINT16, gc.OGT<<16 | gc.TUINT32, gc.OGT<<16 | gc.TUINT64: a = arm.ABHI case gc.OGE<<16 | gc.TINT8, gc.OGE<<16 | gc.TINT16, gc.OGE<<16 | gc.TINT32, gc.OGE<<16 | gc.TINT64, gc.OGE<<16 | gc.TFLOAT32, gc.OGE<<16 | gc.TFLOAT64: a = arm.ABGE case gc.OGE<<16 | gc.TUINT8, gc.OGE<<16 | gc.TUINT16, gc.OGE<<16 | gc.TUINT32, gc.OGE<<16 | gc.TUINT64: a = arm.ABHS case gc.OCMP<<16 | gc.TBOOL, gc.OCMP<<16 | gc.TINT8, gc.OCMP<<16 | gc.TUINT8, gc.OCMP<<16 | gc.TINT16, gc.OCMP<<16 | gc.TUINT16, gc.OCMP<<16 | gc.TINT32, gc.OCMP<<16 | gc.TUINT32, gc.OCMP<<16 | gc.TPTR32: a = arm.ACMP case gc.OCMP<<16 | gc.TFLOAT32: a = arm.ACMPF case gc.OCMP<<16 | gc.TFLOAT64: a = arm.ACMPD case gc.OAS<<16 | gc.TBOOL: a = arm.AMOVB case gc.OAS<<16 | gc.TINT8: a = arm.AMOVBS case gc.OAS<<16 | gc.TUINT8: a = arm.AMOVBU case gc.OAS<<16 | gc.TINT16: a = arm.AMOVHS case gc.OAS<<16 | gc.TUINT16: a = arm.AMOVHU case gc.OAS<<16 | gc.TINT32, gc.OAS<<16 | gc.TUINT32, gc.OAS<<16 | gc.TPTR32: a = arm.AMOVW case gc.OAS<<16 | gc.TFLOAT32: a = arm.AMOVF case gc.OAS<<16 | gc.TFLOAT64: a = arm.AMOVD case gc.OADD<<16 | gc.TINT8, gc.OADD<<16 | gc.TUINT8, gc.OADD<<16 | gc.TINT16, gc.OADD<<16 | gc.TUINT16, gc.OADD<<16 | gc.TINT32, gc.OADD<<16 | gc.TUINT32, gc.OADD<<16 | gc.TPTR32: a = arm.AADD case gc.OADD<<16 | gc.TFLOAT32: a = arm.AADDF case gc.OADD<<16 | gc.TFLOAT64: a = arm.AADDD case gc.OSUB<<16 | gc.TINT8, gc.OSUB<<16 | gc.TUINT8, gc.OSUB<<16 | gc.TINT16, gc.OSUB<<16 | gc.TUINT16, gc.OSUB<<16 | gc.TINT32, gc.OSUB<<16 | gc.TUINT32, gc.OSUB<<16 | gc.TPTR32: a = arm.ASUB case gc.OSUB<<16 | gc.TFLOAT32: a = arm.ASUBF case gc.OSUB<<16 | gc.TFLOAT64: a = arm.ASUBD case gc.OMINUS<<16 | gc.TINT8, gc.OMINUS<<16 | gc.TUINT8, gc.OMINUS<<16 | gc.TINT16, gc.OMINUS<<16 | gc.TUINT16, gc.OMINUS<<16 | gc.TINT32, gc.OMINUS<<16 | gc.TUINT32, gc.OMINUS<<16 | gc.TPTR32: a = arm.ARSB case gc.OAND<<16 | gc.TINT8, gc.OAND<<16 | gc.TUINT8, gc.OAND<<16 | gc.TINT16, gc.OAND<<16 | gc.TUINT16, gc.OAND<<16 | gc.TINT32, gc.OAND<<16 | gc.TUINT32, gc.OAND<<16 | gc.TPTR32: a = arm.AAND case gc.OOR<<16 | gc.TINT8, gc.OOR<<16 | gc.TUINT8, gc.OOR<<16 | gc.TINT16, gc.OOR<<16 | gc.TUINT16, gc.OOR<<16 | gc.TINT32, gc.OOR<<16 | gc.TUINT32, gc.OOR<<16 | gc.TPTR32: a = arm.AORR case gc.OXOR<<16 | gc.TINT8, gc.OXOR<<16 | gc.TUINT8, gc.OXOR<<16 | gc.TINT16, gc.OXOR<<16 | gc.TUINT16, gc.OXOR<<16 | gc.TINT32, gc.OXOR<<16 | gc.TUINT32, gc.OXOR<<16 | gc.TPTR32: a = arm.AEOR case gc.OLSH<<16 | gc.TINT8, gc.OLSH<<16 | gc.TUINT8, gc.OLSH<<16 | gc.TINT16, gc.OLSH<<16 | gc.TUINT16, gc.OLSH<<16 | gc.TINT32, gc.OLSH<<16 | gc.TUINT32, gc.OLSH<<16 | gc.TPTR32: a = arm.ASLL case gc.ORSH<<16 | gc.TUINT8, gc.ORSH<<16 | gc.TUINT16, gc.ORSH<<16 | gc.TUINT32, gc.ORSH<<16 | gc.TPTR32: a = arm.ASRL case gc.ORSH<<16 | gc.TINT8, gc.ORSH<<16 | gc.TINT16, gc.ORSH<<16 | gc.TINT32: a = arm.ASRA case gc.OMUL<<16 | gc.TUINT8, gc.OMUL<<16 | gc.TUINT16, gc.OMUL<<16 | gc.TUINT32, gc.OMUL<<16 | gc.TPTR32: a = arm.AMULU case gc.OMUL<<16 | gc.TINT8, gc.OMUL<<16 | gc.TINT16, gc.OMUL<<16 | gc.TINT32: a = arm.AMUL case gc.OMUL<<16 | gc.TFLOAT32: a = arm.AMULF case gc.OMUL<<16 | gc.TFLOAT64: a = arm.AMULD case gc.ODIV<<16 | gc.TUINT8, gc.ODIV<<16 | gc.TUINT16, gc.ODIV<<16 | gc.TUINT32, gc.ODIV<<16 | gc.TPTR32: a = arm.ADIVU case gc.ODIV<<16 | gc.TINT8, gc.ODIV<<16 | gc.TINT16, gc.ODIV<<16 | gc.TINT32: a = arm.ADIV case gc.OMOD<<16 | gc.TUINT8, gc.OMOD<<16 | gc.TUINT16, gc.OMOD<<16 | gc.TUINT32, gc.OMOD<<16 | gc.TPTR32: a = arm.AMODU case gc.OMOD<<16 | gc.TINT8, gc.OMOD<<16 | gc.TINT16, gc.OMOD<<16 | gc.TINT32: a = arm.AMOD // case CASE(OEXTEND, TINT16): // a = ACWD; // break; // case CASE(OEXTEND, TINT32): // a = ACDQ; // break; // case CASE(OEXTEND, TINT64): // a = ACQO; // break; case gc.ODIV<<16 | gc.TFLOAT32: a = arm.ADIVF case gc.ODIV<<16 | gc.TFLOAT64: a = arm.ADIVD } return a } const ( ODynam = 1 << 0 OPtrto = 1 << 1 ) var clean [20]gc.Node var cleani int = 0 func sudoclean() { if clean[cleani-1].Op != gc.OEMPTY { regfree(&clean[cleani-1]) } if clean[cleani-2].Op != gc.OEMPTY { regfree(&clean[cleani-2]) } cleani -= 2 } func dotaddable(n *gc.Node, n1 *gc.Node) bool { if n.Op != gc.ODOT { return false } var oary [10]int64 var nn *gc.Node o := gc.Dotoffset(n, oary[:], &nn) if nn != nil && nn.Addable != 0 && o == 1 && oary[0] >= 0 { *n1 = *nn n1.Type = n.Type n1.Xoffset += oary[0] return true } return false } /* * generate code to compute address of n, * a reference to a (perhaps nested) field inside * an array or struct. * return 0 on failure, 1 on success. * on success, leaves usable address in a. * * caller is responsible for calling sudoclean * after successful sudoaddable, * to release the register used for a. */ func sudoaddable(as int, n *gc.Node, a *obj.Addr, w *int) bool { if n.Type == nil { return false } *a = obj.Addr{} switch n.Op { case gc.OLITERAL: if !gc.Isconst(n, gc.CTINT) { break } v := gc.Mpgetfix(n.Val.U.Xval) if v >= 32000 || v <= -32000 { break } switch as { default: return false case arm.AADD, arm.ASUB, arm.AAND, arm.AORR, arm.AEOR, arm.AMOVB, arm.AMOVBS, arm.AMOVBU, arm.AMOVH, arm.AMOVHS, arm.AMOVHU, arm.AMOVW: break } cleani += 2 reg := &clean[cleani-1] reg1 := &clean[cleani-2] reg.Op = gc.OEMPTY reg1.Op = gc.OEMPTY *a = gc.Naddr(n) return true case gc.ODOT, gc.ODOTPTR: cleani += 2 reg := &clean[cleani-1] reg1 := &clean[cleani-2] reg.Op = gc.OEMPTY reg1.Op = gc.OEMPTY var nn *gc.Node var oary [10]int64 o := gc.Dotoffset(n, oary[:], &nn) if nn == nil { sudoclean() return false } if nn.Addable != 0 && o == 1 && oary[0] >= 0 { // directly addressable set of DOTs n1 := *nn n1.Type = n.Type n1.Xoffset += oary[0] *a = gc.Naddr(&n1) return true } regalloc(reg, gc.Types[gc.Tptr], nil) n1 := *reg n1.Op = gc.OINDREG if oary[0] >= 0 { agen(nn, reg) n1.Xoffset = oary[0] } else { cgen(nn, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[0] + 1) } for i := 1; i < o; i++ { if oary[i] >= 0 { gc.Fatal("can't happen") } gins(arm.AMOVW, &n1, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[i] + 1) } a.Type = obj.TYPE_NONE a.Name = obj.NAME_NONE n1.Type = n.Type *a = gc.Naddr(&n1) return true case gc.OINDEX: return false } return false }
/* * allocate a register (reusing res if possible) and generate * a = &n * The caller must call regfree(a). * The generated code checks that the result is not nil. */ func agenr(n *gc.Node, a *gc.Node, res *gc.Node) { if gc.Debug['g'] != 0 { gc.Dump("\nagenr-n", n) } nl := n.Left nr := n.Right switch n.Op { case gc.ODOT, gc.ODOTPTR, gc.OCALLFUNC, gc.OCALLMETH, gc.OCALLINTER: var n1 gc.Node igen(n, &n1, res) regalloc(a, gc.Types[gc.Tptr], &n1) agen(&n1, a) regfree(&n1) case gc.OIND: cgenr(n.Left, a, res) gc.Cgen_checknil(a) case gc.OINDEX: freelen := 0 w := uint64(n.Type.Width) // Generate the non-addressable child first. var n3 gc.Node var nlen gc.Node var tmp gc.Node var n1 gc.Node if nr.Addable != 0 { goto irad } if nl.Addable != 0 { cgenr(nr, &n1, nil) if !gc.Isconst(nl, gc.CTSTR) { if gc.Isfixedarray(nl.Type) { agenr(nl, &n3, res) } else { igen(nl, &nlen, res) freelen = 1 nlen.Type = gc.Types[gc.Tptr] nlen.Xoffset += int64(gc.Array_array) regalloc(&n3, gc.Types[gc.Tptr], res) gmove(&nlen, &n3) nlen.Type = gc.Types[gc.Simtype[gc.TUINT]] nlen.Xoffset += int64(gc.Array_nel) - int64(gc.Array_array) } } goto index } gc.Tempname(&tmp, nr.Type) cgen(nr, &tmp) nr = &tmp irad: if !gc.Isconst(nl, gc.CTSTR) { if gc.Isfixedarray(nl.Type) { agenr(nl, &n3, res) } else { if nl.Addable == 0 { // igen will need an addressable node. var tmp2 gc.Node gc.Tempname(&tmp2, nl.Type) cgen(nl, &tmp2) nl = &tmp2 } igen(nl, &nlen, res) freelen = 1 nlen.Type = gc.Types[gc.Tptr] nlen.Xoffset += int64(gc.Array_array) regalloc(&n3, gc.Types[gc.Tptr], res) gmove(&nlen, &n3) nlen.Type = gc.Types[gc.Simtype[gc.TUINT]] nlen.Xoffset += int64(gc.Array_nel) - int64(gc.Array_array) } } if !gc.Isconst(nr, gc.CTINT) { cgenr(nr, &n1, nil) } goto index // &a is in &n3 (allocated in res) // i is in &n1 (if not constant) // len(a) is in nlen (if needed) // w is width // constant index index: if gc.Isconst(nr, gc.CTINT) { if gc.Isconst(nl, gc.CTSTR) { gc.Fatal("constant string constant index") // front end should handle } v := uint64(gc.Mpgetfix(nr.Val.U.Xval)) if gc.Isslice(nl.Type) || nl.Type.Etype == gc.TSTRING { if gc.Debug['B'] == 0 && !n.Bounded { var n2 gc.Node gc.Nodconst(&n2, gc.Types[gc.Simtype[gc.TUINT]], int64(v)) if gc.Smallintconst(nr) { gins(optoas(gc.OCMP, gc.Types[gc.Simtype[gc.TUINT]]), &nlen, &n2) } else { regalloc(&tmp, gc.Types[gc.Simtype[gc.TUINT]], nil) gmove(&n2, &tmp) gins(optoas(gc.OCMP, gc.Types[gc.Simtype[gc.TUINT]]), &nlen, &tmp) regfree(&tmp) } p1 := gc.Gbranch(optoas(gc.OGT, gc.Types[gc.Simtype[gc.TUINT]]), nil, +1) ginscall(gc.Panicindex, -1) gc.Patch(p1, gc.Pc) } regfree(&nlen) } if v*w != 0 { ginscon(optoas(gc.OADD, gc.Types[gc.Tptr]), int64(v*w), &n3) } *a = n3 break } // type of the index t := gc.Types[gc.TUINT64] if gc.Issigned[n1.Type.Etype] { t = gc.Types[gc.TINT64] } var n2 gc.Node regalloc(&n2, t, &n1) // i gmove(&n1, &n2) regfree(&n1) if gc.Debug['B'] == 0 && !n.Bounded { // check bounds t = gc.Types[gc.Simtype[gc.TUINT]] if gc.Is64(nr.Type) { t = gc.Types[gc.TUINT64] } if gc.Isconst(nl, gc.CTSTR) { gc.Nodconst(&nlen, t, int64(len(nl.Val.U.Sval))) } else if gc.Isslice(nl.Type) || nl.Type.Etype == gc.TSTRING { if gc.Is64(nr.Type) { var n5 gc.Node regalloc(&n5, t, nil) gmove(&nlen, &n5) regfree(&nlen) nlen = n5 } } else { gc.Nodconst(&nlen, t, nl.Type.Bound) if !gc.Smallintconst(&nlen) { var n5 gc.Node regalloc(&n5, t, nil) gmove(&nlen, &n5) nlen = n5 freelen = 1 } } gins(optoas(gc.OCMP, t), &n2, &nlen) p1 := gc.Gbranch(optoas(gc.OLT, t), nil, +1) ginscall(gc.Panicindex, -1) gc.Patch(p1, gc.Pc) } if gc.Isconst(nl, gc.CTSTR) { regalloc(&n3, gc.Types[gc.Tptr], res) p1 := gins(x86.ALEAQ, nil, &n3) gc.Datastring(nl.Val.U.Sval, &p1.From) gins(x86.AADDQ, &n2, &n3) goto indexdone } if w == 0 { } else // nothing to do if w == 1 || w == 2 || w == 4 || w == 8 { p1 := gins(x86.ALEAQ, &n2, &n3) p1.From.Type = obj.TYPE_MEM p1.From.Scale = int16(w) p1.From.Index = p1.From.Reg p1.From.Reg = p1.To.Reg } else { ginscon(optoas(gc.OMUL, t), int64(w), &n2) gins(optoas(gc.OADD, gc.Types[gc.Tptr]), &n2, &n3) } indexdone: *a = n3 regfree(&n2) if freelen != 0 { regfree(&nlen) } default: regalloc(a, gc.Types[gc.Tptr], res) agen(n, a) } }
func anyregalloc() bool { var j int for i := int(0); i < len(reg); i++ { if reg[i] == 0 { goto ok } for j = 0; j < len(resvd); j++ { if resvd[j] == i { goto ok } } return true ok: } return false } /* * allocate register of type t, leave in n. * if o != N, o is desired fixed register. * caller must regfree(n). */ func regalloc(n *gc.Node, t *gc.Type, o *gc.Node) { if t == nil { gc.Fatal("regalloc: t nil") } et := int(int(gc.Simtype[t.Etype])) if gc.Debug['r'] != 0 { fixfree := int(0) fltfree := int(0) for i := int(ppc64.REG_R0); i < ppc64.REG_F31; i++ { if reg[i-ppc64.REG_R0] == 0 { if i < ppc64.REG_F0 { fixfree++ } else { fltfree++ } } } fmt.Printf("regalloc fix %d flt %d free\n", fixfree, fltfree) } var i int switch et { case gc.TINT8, gc.TUINT8, gc.TINT16, gc.TUINT16, gc.TINT32, gc.TUINT32, gc.TINT64, gc.TUINT64, gc.TPTR32, gc.TPTR64, gc.TBOOL: if o != nil && o.Op == gc.OREGISTER { i = int(o.Val.U.Reg) if i >= ppc64.REGMIN && i <= ppc64.REGMAX { goto out } } for i = ppc64.REGMIN; i <= ppc64.REGMAX; i++ { if reg[i-ppc64.REG_R0] == 0 { regpc[i-ppc64.REG_R0] = uint32(obj.Getcallerpc(&n)) goto out } } gc.Flusherrors() for i := int(ppc64.REG_R0); i < ppc64.REG_R0+ppc64.NREG; i++ { fmt.Printf("R%d %p\n", i, regpc[i-ppc64.REG_R0]) } gc.Fatal("out of fixed registers") case gc.TFLOAT32, gc.TFLOAT64: if o != nil && o.Op == gc.OREGISTER { i = int(o.Val.U.Reg) if i >= ppc64.FREGMIN && i <= ppc64.FREGMAX { goto out } } for i = ppc64.FREGMIN; i <= ppc64.FREGMAX; i++ { if reg[i-ppc64.REG_R0] == 0 { regpc[i-ppc64.REG_R0] = uint32(obj.Getcallerpc(&n)) goto out } } gc.Flusherrors() for i := int(ppc64.REG_F0); i < ppc64.REG_F0+ppc64.NREG; i++ { fmt.Printf("F%d %p\n", i, regpc[i-ppc64.REG_R0]) } gc.Fatal("out of floating registers") case gc.TCOMPLEX64, gc.TCOMPLEX128: gc.Tempname(n, t) return } gc.Fatal("regalloc: unknown type %v", gc.Tconv(t, 0)) return out: reg[i-ppc64.REG_R0]++ gc.Nodreg(n, t, i) } func regfree(n *gc.Node) { if n.Op == gc.ONAME { return } if n.Op != gc.OREGISTER && n.Op != gc.OINDREG { gc.Fatal("regfree: not a register") } i := int(int(n.Val.U.Reg) - ppc64.REG_R0) if i == ppc64.REGSP-ppc64.REG_R0 { return } if i < 0 || i >= len(reg) { gc.Fatal("regfree: reg out of range") } if reg[i] <= 0 { gc.Fatal("regfree: reg not allocated") } reg[i]-- if reg[i] == 0 { regpc[i] = 0 } } /* * generate * as $c, n */ func ginscon(as int, c int64, n2 *gc.Node) { var n1 gc.Node gc.Nodconst(&n1, gc.Types[gc.TINT64], c) if as != ppc64.AMOVD && (c < -ppc64.BIG || c > ppc64.BIG) { // cannot have more than 16-bit of immediate in ADD, etc. // instead, MOV into register first. var ntmp gc.Node regalloc(&ntmp, gc.Types[gc.TINT64], nil) gins(ppc64.AMOVD, &n1, &ntmp) gins(as, &ntmp, n2) regfree(&ntmp) return } gins(as, &n1, n2) } /* * generate * as n, $c (CMP/CMPU) */ func ginscon2(as int, n2 *gc.Node, c int64) { var n1 gc.Node gc.Nodconst(&n1, gc.Types[gc.TINT64], c) switch as { default: gc.Fatal("ginscon2") case ppc64.ACMP: if -ppc64.BIG <= c && c <= ppc64.BIG { gins(as, n2, &n1) return } case ppc64.ACMPU: if 0 <= c && c <= 2*ppc64.BIG { gins(as, n2, &n1) return } } // MOV n1 into register first var ntmp gc.Node regalloc(&ntmp, gc.Types[gc.TINT64], nil) gins(ppc64.AMOVD, &n1, &ntmp) gins(as, n2, &ntmp) regfree(&ntmp) } /* * set up nodes representing 2^63 */ var bigi gc.Node var bigf gc.Node var bignodes_did int func bignodes() { if bignodes_did != 0 { return } bignodes_did = 1 gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 1) gc.Mpshiftfix(bigi.Val.U.Xval, 63) bigf = bigi bigf.Type = gc.Types[gc.TFLOAT64] bigf.Val.Ctype = gc.CTFLT bigf.Val.U.Fval = new(gc.Mpflt) gc.Mpmovefixflt(bigf.Val.U.Fval, bigi.Val.U.Xval) } /* * generate move: * t = f * hard part is conversions. */ func gmove(f *gc.Node, t *gc.Node) { if gc.Debug['M'] != 0 { fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, obj.FmtLong), gc.Nconv(t, obj.FmtLong)) } ft := int(gc.Simsimtype(f.Type)) tt := int(gc.Simsimtype(t.Type)) cvt := (*gc.Type)(t.Type) if gc.Iscomplex[ft] || gc.Iscomplex[tt] { gc.Complexmove(f, t) return } // cannot have two memory operands var r2 gc.Node var r1 gc.Node var a int if gc.Ismem(f) && gc.Ismem(t) { goto hard } // convert constant to desired type if f.Op == gc.OLITERAL { var con gc.Node switch tt { default: gc.Convconst(&con, t.Type, &f.Val) case gc.TINT32, gc.TINT16, gc.TINT8: var con gc.Node gc.Convconst(&con, gc.Types[gc.TINT64], &f.Val) var r1 gc.Node regalloc(&r1, con.Type, t) gins(ppc64.AMOVD, &con, &r1) gmove(&r1, t) regfree(&r1) return case gc.TUINT32, gc.TUINT16, gc.TUINT8: var con gc.Node gc.Convconst(&con, gc.Types[gc.TUINT64], &f.Val) var r1 gc.Node regalloc(&r1, con.Type, t) gins(ppc64.AMOVD, &con, &r1) gmove(&r1, t) regfree(&r1) return } f = &con ft = tt // so big switch will choose a simple mov // constants can't move directly to memory. if gc.Ismem(t) { goto hard } } // float constants come from memory. //if(isfloat[tt]) // goto hard; // 64-bit immediates are also from memory. //if(isint[tt]) // goto hard; //// 64-bit immediates are really 32-bit sign-extended //// unless moving into a register. //if(isint[tt]) { // if(mpcmpfixfix(con.val.u.xval, minintval[TINT32]) < 0) // goto hard; // if(mpcmpfixfix(con.val.u.xval, maxintval[TINT32]) > 0) // goto hard; //} // value -> value copy, only one memory operand. // figure out the instruction to use. // break out of switch for one-instruction gins. // goto rdst for "destination must be register". // goto hard for "convert to cvt type first". // otherwise handle and return. switch uint32(ft)<<16 | uint32(tt) { default: gc.Fatal("gmove %v -> %v", gc.Tconv(f.Type, obj.FmtLong), gc.Tconv(t.Type, obj.FmtLong)) /* * integer copy and truncate */ case gc.TINT8<<16 | gc.TINT8, // same size gc.TUINT8<<16 | gc.TINT8, gc.TINT16<<16 | gc.TINT8, // truncate gc.TUINT16<<16 | gc.TINT8, gc.TINT32<<16 | gc.TINT8, gc.TUINT32<<16 | gc.TINT8, gc.TINT64<<16 | gc.TINT8, gc.TUINT64<<16 | gc.TINT8: a = ppc64.AMOVB case gc.TINT8<<16 | gc.TUINT8, // same size gc.TUINT8<<16 | gc.TUINT8, gc.TINT16<<16 | gc.TUINT8, // truncate gc.TUINT16<<16 | gc.TUINT8, gc.TINT32<<16 | gc.TUINT8, gc.TUINT32<<16 | gc.TUINT8, gc.TINT64<<16 | gc.TUINT8, gc.TUINT64<<16 | gc.TUINT8: a = ppc64.AMOVBZ case gc.TINT16<<16 | gc.TINT16, // same size gc.TUINT16<<16 | gc.TINT16, gc.TINT32<<16 | gc.TINT16, // truncate gc.TUINT32<<16 | gc.TINT16, gc.TINT64<<16 | gc.TINT16, gc.TUINT64<<16 | gc.TINT16: a = ppc64.AMOVH case gc.TINT16<<16 | gc.TUINT16, // same size gc.TUINT16<<16 | gc.TUINT16, gc.TINT32<<16 | gc.TUINT16, // truncate gc.TUINT32<<16 | gc.TUINT16, gc.TINT64<<16 | gc.TUINT16, gc.TUINT64<<16 | gc.TUINT16: a = ppc64.AMOVHZ case gc.TINT32<<16 | gc.TINT32, // same size gc.TUINT32<<16 | gc.TINT32, gc.TINT64<<16 | gc.TINT32, // truncate gc.TUINT64<<16 | gc.TINT32: a = ppc64.AMOVW case gc.TINT32<<16 | gc.TUINT32, // same size gc.TUINT32<<16 | gc.TUINT32, gc.TINT64<<16 | gc.TUINT32, gc.TUINT64<<16 | gc.TUINT32: a = ppc64.AMOVWZ case gc.TINT64<<16 | gc.TINT64, // same size gc.TINT64<<16 | gc.TUINT64, gc.TUINT64<<16 | gc.TINT64, gc.TUINT64<<16 | gc.TUINT64: a = ppc64.AMOVD /* * integer up-conversions */ case gc.TINT8<<16 | gc.TINT16, // sign extend int8 gc.TINT8<<16 | gc.TUINT16, gc.TINT8<<16 | gc.TINT32, gc.TINT8<<16 | gc.TUINT32, gc.TINT8<<16 | gc.TINT64, gc.TINT8<<16 | gc.TUINT64: a = ppc64.AMOVB goto rdst case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 gc.TUINT8<<16 | gc.TUINT16, gc.TUINT8<<16 | gc.TINT32, gc.TUINT8<<16 | gc.TUINT32, gc.TUINT8<<16 | gc.TINT64, gc.TUINT8<<16 | gc.TUINT64: a = ppc64.AMOVBZ goto rdst case gc.TINT16<<16 | gc.TINT32, // sign extend int16 gc.TINT16<<16 | gc.TUINT32, gc.TINT16<<16 | gc.TINT64, gc.TINT16<<16 | gc.TUINT64: a = ppc64.AMOVH goto rdst case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 gc.TUINT16<<16 | gc.TUINT32, gc.TUINT16<<16 | gc.TINT64, gc.TUINT16<<16 | gc.TUINT64: a = ppc64.AMOVHZ goto rdst case gc.TINT32<<16 | gc.TINT64, // sign extend int32 gc.TINT32<<16 | gc.TUINT64: a = ppc64.AMOVW goto rdst case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 gc.TUINT32<<16 | gc.TUINT64: a = ppc64.AMOVWZ goto rdst //warn("gmove: convert float to int not implemented: %N -> %N\n", f, t); //return; // algorithm is: // if small enough, use native float64 -> int64 conversion. // otherwise, subtract 2^63, convert, and add it back. /* * float to integer */ case gc.TFLOAT32<<16 | gc.TINT32, gc.TFLOAT64<<16 | gc.TINT32, gc.TFLOAT32<<16 | gc.TINT64, gc.TFLOAT64<<16 | gc.TINT64, gc.TFLOAT32<<16 | gc.TINT16, gc.TFLOAT32<<16 | gc.TINT8, gc.TFLOAT32<<16 | gc.TUINT16, gc.TFLOAT32<<16 | gc.TUINT8, gc.TFLOAT64<<16 | gc.TINT16, gc.TFLOAT64<<16 | gc.TINT8, gc.TFLOAT64<<16 | gc.TUINT16, gc.TFLOAT64<<16 | gc.TUINT8, gc.TFLOAT32<<16 | gc.TUINT32, gc.TFLOAT64<<16 | gc.TUINT32, gc.TFLOAT32<<16 | gc.TUINT64, gc.TFLOAT64<<16 | gc.TUINT64: bignodes() var r1 gc.Node regalloc(&r1, gc.Types[ft], f) gmove(f, &r1) if tt == gc.TUINT64 { regalloc(&r2, gc.Types[gc.TFLOAT64], nil) gmove(&bigf, &r2) gins(ppc64.AFCMPU, &r1, &r2) p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TFLOAT64]), nil, +1)) gins(ppc64.AFSUB, &r2, &r1) gc.Patch(p1, gc.Pc) regfree(&r2) } regalloc(&r2, gc.Types[gc.TFLOAT64], nil) var r3 gc.Node regalloc(&r3, gc.Types[gc.TINT64], t) gins(ppc64.AFCTIDZ, &r1, &r2) p1 := (*obj.Prog)(gins(ppc64.AFMOVD, &r2, nil)) p1.To.Type = obj.TYPE_MEM p1.To.Reg = ppc64.REGSP p1.To.Offset = -8 p1 = gins(ppc64.AMOVD, nil, &r3) p1.From.Type = obj.TYPE_MEM p1.From.Reg = ppc64.REGSP p1.From.Offset = -8 regfree(&r2) regfree(&r1) if tt == gc.TUINT64 { p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TFLOAT64]), nil, +1)) // use CR0 here again gc.Nodreg(&r1, gc.Types[gc.TINT64], ppc64.REGTMP) gins(ppc64.AMOVD, &bigi, &r1) gins(ppc64.AADD, &r1, &r3) gc.Patch(p1, gc.Pc) } gmove(&r3, t) regfree(&r3) return //warn("gmove: convert int to float not implemented: %N -> %N\n", f, t); //return; // algorithm is: // if small enough, use native int64 -> uint64 conversion. // otherwise, halve (rounding to odd?), convert, and double. /* * integer to float */ case gc.TINT32<<16 | gc.TFLOAT32, gc.TINT32<<16 | gc.TFLOAT64, gc.TINT64<<16 | gc.TFLOAT32, gc.TINT64<<16 | gc.TFLOAT64, gc.TINT16<<16 | gc.TFLOAT32, gc.TINT16<<16 | gc.TFLOAT64, gc.TINT8<<16 | gc.TFLOAT32, gc.TINT8<<16 | gc.TFLOAT64, gc.TUINT16<<16 | gc.TFLOAT32, gc.TUINT16<<16 | gc.TFLOAT64, gc.TUINT8<<16 | gc.TFLOAT32, gc.TUINT8<<16 | gc.TFLOAT64, gc.TUINT32<<16 | gc.TFLOAT32, gc.TUINT32<<16 | gc.TFLOAT64, gc.TUINT64<<16 | gc.TFLOAT32, gc.TUINT64<<16 | gc.TFLOAT64: bignodes() var r1 gc.Node regalloc(&r1, gc.Types[gc.TINT64], nil) gmove(f, &r1) if ft == gc.TUINT64 { gc.Nodreg(&r2, gc.Types[gc.TUINT64], ppc64.REGTMP) gmove(&bigi, &r2) gins(ppc64.ACMPU, &r1, &r2) p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1)) p2 := (*obj.Prog)(gins(ppc64.ASRD, nil, &r1)) p2.From.Type = obj.TYPE_CONST p2.From.Offset = 1 gc.Patch(p1, gc.Pc) } regalloc(&r2, gc.Types[gc.TFLOAT64], t) p1 := (*obj.Prog)(gins(ppc64.AMOVD, &r1, nil)) p1.To.Type = obj.TYPE_MEM p1.To.Reg = ppc64.REGSP p1.To.Offset = -8 p1 = gins(ppc64.AFMOVD, nil, &r2) p1.From.Type = obj.TYPE_MEM p1.From.Reg = ppc64.REGSP p1.From.Offset = -8 gins(ppc64.AFCFID, &r2, &r2) regfree(&r1) if ft == gc.TUINT64 { p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1)) // use CR0 here again gc.Nodreg(&r1, gc.Types[gc.TFLOAT64], ppc64.FREGTWO) gins(ppc64.AFMUL, &r1, &r2) gc.Patch(p1, gc.Pc) } gmove(&r2, t) regfree(&r2) return /* * float to float */ case gc.TFLOAT32<<16 | gc.TFLOAT32: a = ppc64.AFMOVS case gc.TFLOAT64<<16 | gc.TFLOAT64: a = ppc64.AFMOVD case gc.TFLOAT32<<16 | gc.TFLOAT64: a = ppc64.AFMOVS goto rdst case gc.TFLOAT64<<16 | gc.TFLOAT32: a = ppc64.AFRSP goto rdst } gins(a, f, t) return // requires register destination rdst: { regalloc(&r1, t.Type, t) gins(a, f, &r1) gmove(&r1, t) regfree(&r1) return } // requires register intermediate hard: regalloc(&r1, cvt, t) gmove(f, &r1) gmove(&r1, t) regfree(&r1) return } /* * generate one instruction: * as f, t */ func gins(as int, f *gc.Node, t *gc.Node) *obj.Prog { // TODO(austin): Add self-move test like in 6g (but be careful // of truncation moves) af := obj.Addr(obj.Addr{}) at := obj.Addr(obj.Addr{}) if f != nil { af = gc.Naddr(f) } if t != nil { at = gc.Naddr(t) } p := (*obj.Prog)(gc.Prog(as)) if f != nil { p.From = af } if t != nil { p.To = at } if gc.Debug['g'] != 0 { fmt.Printf("%v\n", p) } w := int32(0) switch as { case ppc64.AMOVB, ppc64.AMOVBU, ppc64.AMOVBZ, ppc64.AMOVBZU: w = 1 case ppc64.AMOVH, ppc64.AMOVHU, ppc64.AMOVHZ, ppc64.AMOVHZU: w = 2 case ppc64.AMOVW, ppc64.AMOVWU, ppc64.AMOVWZ, ppc64.AMOVWZU: w = 4 case ppc64.AMOVD, ppc64.AMOVDU: if af.Type == obj.TYPE_CONST || af.Type == obj.TYPE_ADDR { break } w = 8 } if w != 0 && ((f != nil && af.Width < int64(w)) || (t != nil && at.Type != obj.TYPE_REG && at.Width > int64(w))) { gc.Dump("f", f) gc.Dump("t", t) gc.Fatal("bad width: %v (%d, %d)\n", p, af.Width, at.Width) } return p } func fixlargeoffset(n *gc.Node) { if n == nil { return } if n.Op != gc.OINDREG { return } if n.Val.U.Reg == ppc64.REGSP { // stack offset cannot be large return } if n.Xoffset != int64(int32(n.Xoffset)) { // TODO(minux): offset too large, move into R31 and add to R31 instead. // this is used only in test/fixedbugs/issue6036.go. gc.Fatal("offset too large: %v", gc.Nconv(n, 0)) a := gc.Node(*n) a.Op = gc.OREGISTER a.Type = gc.Types[gc.Tptr] a.Xoffset = 0 gc.Cgen_checknil(&a) ginscon(optoas(gc.OADD, gc.Types[gc.Tptr]), n.Xoffset, &a) n.Xoffset = 0 } } /* * return Axxx for Oxxx on type t. */ func optoas(op int, t *gc.Type) int { if t == nil { gc.Fatal("optoas: t is nil") } a := int(obj.AXXX) switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { default: gc.Fatal("optoas: no entry for op=%v type=%v", gc.Oconv(int(op), 0), gc.Tconv(t, 0)) case gc.OEQ<<16 | gc.TBOOL, gc.OEQ<<16 | gc.TINT8, gc.OEQ<<16 | gc.TUINT8, gc.OEQ<<16 | gc.TINT16, gc.OEQ<<16 | gc.TUINT16, gc.OEQ<<16 | gc.TINT32, gc.OEQ<<16 | gc.TUINT32, gc.OEQ<<16 | gc.TINT64, gc.OEQ<<16 | gc.TUINT64, gc.OEQ<<16 | gc.TPTR32, gc.OEQ<<16 | gc.TPTR64, gc.OEQ<<16 | gc.TFLOAT32, gc.OEQ<<16 | gc.TFLOAT64: a = ppc64.ABEQ case gc.ONE<<16 | gc.TBOOL, gc.ONE<<16 | gc.TINT8, gc.ONE<<16 | gc.TUINT8, gc.ONE<<16 | gc.TINT16, gc.ONE<<16 | gc.TUINT16, gc.ONE<<16 | gc.TINT32, gc.ONE<<16 | gc.TUINT32, gc.ONE<<16 | gc.TINT64, gc.ONE<<16 | gc.TUINT64, gc.ONE<<16 | gc.TPTR32, gc.ONE<<16 | gc.TPTR64, gc.ONE<<16 | gc.TFLOAT32, gc.ONE<<16 | gc.TFLOAT64: a = ppc64.ABNE case gc.OLT<<16 | gc.TINT8, // ACMP gc.OLT<<16 | gc.TINT16, gc.OLT<<16 | gc.TINT32, gc.OLT<<16 | gc.TINT64, gc.OLT<<16 | gc.TUINT8, // ACMPU gc.OLT<<16 | gc.TUINT16, gc.OLT<<16 | gc.TUINT32, gc.OLT<<16 | gc.TUINT64, gc.OLT<<16 | gc.TFLOAT32, // AFCMPU gc.OLT<<16 | gc.TFLOAT64: a = ppc64.ABLT case gc.OLE<<16 | gc.TINT8, // ACMP gc.OLE<<16 | gc.TINT16, gc.OLE<<16 | gc.TINT32, gc.OLE<<16 | gc.TINT64, gc.OLE<<16 | gc.TUINT8, // ACMPU gc.OLE<<16 | gc.TUINT16, gc.OLE<<16 | gc.TUINT32, gc.OLE<<16 | gc.TUINT64, gc.OLE<<16 | gc.TFLOAT32, // AFCMPU gc.OLE<<16 | gc.TFLOAT64: a = ppc64.ABLE case gc.OGT<<16 | gc.TINT8, gc.OGT<<16 | gc.TINT16, gc.OGT<<16 | gc.TINT32, gc.OGT<<16 | gc.TINT64, gc.OGT<<16 | gc.TUINT8, gc.OGT<<16 | gc.TUINT16, gc.OGT<<16 | gc.TUINT32, gc.OGT<<16 | gc.TUINT64, gc.OGT<<16 | gc.TFLOAT32, gc.OGT<<16 | gc.TFLOAT64: a = ppc64.ABGT case gc.OGE<<16 | gc.TINT8, gc.OGE<<16 | gc.TINT16, gc.OGE<<16 | gc.TINT32, gc.OGE<<16 | gc.TINT64, gc.OGE<<16 | gc.TUINT8, gc.OGE<<16 | gc.TUINT16, gc.OGE<<16 | gc.TUINT32, gc.OGE<<16 | gc.TUINT64, gc.OGE<<16 | gc.TFLOAT32, gc.OGE<<16 | gc.TFLOAT64: a = ppc64.ABGE case gc.OCMP<<16 | gc.TBOOL, gc.OCMP<<16 | gc.TINT8, gc.OCMP<<16 | gc.TINT16, gc.OCMP<<16 | gc.TINT32, gc.OCMP<<16 | gc.TPTR32, gc.OCMP<<16 | gc.TINT64: a = ppc64.ACMP case gc.OCMP<<16 | gc.TUINT8, gc.OCMP<<16 | gc.TUINT16, gc.OCMP<<16 | gc.TUINT32, gc.OCMP<<16 | gc.TUINT64, gc.OCMP<<16 | gc.TPTR64: a = ppc64.ACMPU case gc.OCMP<<16 | gc.TFLOAT32, gc.OCMP<<16 | gc.TFLOAT64: a = ppc64.AFCMPU case gc.OAS<<16 | gc.TBOOL, gc.OAS<<16 | gc.TINT8: a = ppc64.AMOVB case gc.OAS<<16 | gc.TUINT8: a = ppc64.AMOVBZ case gc.OAS<<16 | gc.TINT16: a = ppc64.AMOVH case gc.OAS<<16 | gc.TUINT16: a = ppc64.AMOVHZ case gc.OAS<<16 | gc.TINT32: a = ppc64.AMOVW case gc.OAS<<16 | gc.TUINT32, gc.OAS<<16 | gc.TPTR32: a = ppc64.AMOVWZ case gc.OAS<<16 | gc.TINT64, gc.OAS<<16 | gc.TUINT64, gc.OAS<<16 | gc.TPTR64: a = ppc64.AMOVD case gc.OAS<<16 | gc.TFLOAT32: a = ppc64.AFMOVS case gc.OAS<<16 | gc.TFLOAT64: a = ppc64.AFMOVD case gc.OADD<<16 | gc.TINT8, gc.OADD<<16 | gc.TUINT8, gc.OADD<<16 | gc.TINT16, gc.OADD<<16 | gc.TUINT16, gc.OADD<<16 | gc.TINT32, gc.OADD<<16 | gc.TUINT32, gc.OADD<<16 | gc.TPTR32, gc.OADD<<16 | gc.TINT64, gc.OADD<<16 | gc.TUINT64, gc.OADD<<16 | gc.TPTR64: a = ppc64.AADD case gc.OADD<<16 | gc.TFLOAT32: a = ppc64.AFADDS case gc.OADD<<16 | gc.TFLOAT64: a = ppc64.AFADD case gc.OSUB<<16 | gc.TINT8, gc.OSUB<<16 | gc.TUINT8, gc.OSUB<<16 | gc.TINT16, gc.OSUB<<16 | gc.TUINT16, gc.OSUB<<16 | gc.TINT32, gc.OSUB<<16 | gc.TUINT32, gc.OSUB<<16 | gc.TPTR32, gc.OSUB<<16 | gc.TINT64, gc.OSUB<<16 | gc.TUINT64, gc.OSUB<<16 | gc.TPTR64: a = ppc64.ASUB case gc.OSUB<<16 | gc.TFLOAT32: a = ppc64.AFSUBS case gc.OSUB<<16 | gc.TFLOAT64: a = ppc64.AFSUB case gc.OMINUS<<16 | gc.TINT8, gc.OMINUS<<16 | gc.TUINT8, gc.OMINUS<<16 | gc.TINT16, gc.OMINUS<<16 | gc.TUINT16, gc.OMINUS<<16 | gc.TINT32, gc.OMINUS<<16 | gc.TUINT32, gc.OMINUS<<16 | gc.TPTR32, gc.OMINUS<<16 | gc.TINT64, gc.OMINUS<<16 | gc.TUINT64, gc.OMINUS<<16 | gc.TPTR64: a = ppc64.ANEG case gc.OAND<<16 | gc.TINT8, gc.OAND<<16 | gc.TUINT8, gc.OAND<<16 | gc.TINT16, gc.OAND<<16 | gc.TUINT16, gc.OAND<<16 | gc.TINT32, gc.OAND<<16 | gc.TUINT32, gc.OAND<<16 | gc.TPTR32, gc.OAND<<16 | gc.TINT64, gc.OAND<<16 | gc.TUINT64, gc.OAND<<16 | gc.TPTR64: a = ppc64.AAND case gc.OOR<<16 | gc.TINT8, gc.OOR<<16 | gc.TUINT8, gc.OOR<<16 | gc.TINT16, gc.OOR<<16 | gc.TUINT16, gc.OOR<<16 | gc.TINT32, gc.OOR<<16 | gc.TUINT32, gc.OOR<<16 | gc.TPTR32, gc.OOR<<16 | gc.TINT64, gc.OOR<<16 | gc.TUINT64, gc.OOR<<16 | gc.TPTR64: a = ppc64.AOR case gc.OXOR<<16 | gc.TINT8, gc.OXOR<<16 | gc.TUINT8, gc.OXOR<<16 | gc.TINT16, gc.OXOR<<16 | gc.TUINT16, gc.OXOR<<16 | gc.TINT32, gc.OXOR<<16 | gc.TUINT32, gc.OXOR<<16 | gc.TPTR32, gc.OXOR<<16 | gc.TINT64, gc.OXOR<<16 | gc.TUINT64, gc.OXOR<<16 | gc.TPTR64: a = ppc64.AXOR // TODO(minux): handle rotates //case CASE(OLROT, TINT8): //case CASE(OLROT, TUINT8): //case CASE(OLROT, TINT16): //case CASE(OLROT, TUINT16): //case CASE(OLROT, TINT32): //case CASE(OLROT, TUINT32): //case CASE(OLROT, TPTR32): //case CASE(OLROT, TINT64): //case CASE(OLROT, TUINT64): //case CASE(OLROT, TPTR64): // a = 0//???; RLDC? // break; case gc.OLSH<<16 | gc.TINT8, gc.OLSH<<16 | gc.TUINT8, gc.OLSH<<16 | gc.TINT16, gc.OLSH<<16 | gc.TUINT16, gc.OLSH<<16 | gc.TINT32, gc.OLSH<<16 | gc.TUINT32, gc.OLSH<<16 | gc.TPTR32, gc.OLSH<<16 | gc.TINT64, gc.OLSH<<16 | gc.TUINT64, gc.OLSH<<16 | gc.TPTR64: a = ppc64.ASLD case gc.ORSH<<16 | gc.TUINT8, gc.ORSH<<16 | gc.TUINT16, gc.ORSH<<16 | gc.TUINT32, gc.ORSH<<16 | gc.TPTR32, gc.ORSH<<16 | gc.TUINT64, gc.ORSH<<16 | gc.TPTR64: a = ppc64.ASRD case gc.ORSH<<16 | gc.TINT8, gc.ORSH<<16 | gc.TINT16, gc.ORSH<<16 | gc.TINT32, gc.ORSH<<16 | gc.TINT64: a = ppc64.ASRAD // TODO(minux): handle rotates //case CASE(ORROTC, TINT8): //case CASE(ORROTC, TUINT8): //case CASE(ORROTC, TINT16): //case CASE(ORROTC, TUINT16): //case CASE(ORROTC, TINT32): //case CASE(ORROTC, TUINT32): //case CASE(ORROTC, TINT64): //case CASE(ORROTC, TUINT64): // a = 0//??? RLDC?? // break; case gc.OHMUL<<16 | gc.TINT64: a = ppc64.AMULHD case gc.OHMUL<<16 | gc.TUINT64, gc.OHMUL<<16 | gc.TPTR64: a = ppc64.AMULHDU case gc.OMUL<<16 | gc.TINT8, gc.OMUL<<16 | gc.TINT16, gc.OMUL<<16 | gc.TINT32, gc.OMUL<<16 | gc.TINT64: a = ppc64.AMULLD case gc.OMUL<<16 | gc.TUINT8, gc.OMUL<<16 | gc.TUINT16, gc.OMUL<<16 | gc.TUINT32, gc.OMUL<<16 | gc.TPTR32, // don't use word multiply, the high 32-bit are undefined. // fallthrough gc.OMUL<<16 | gc.TUINT64, gc.OMUL<<16 | gc.TPTR64: a = ppc64.AMULLD // for 64-bit multiplies, signedness doesn't matter. case gc.OMUL<<16 | gc.TFLOAT32: a = ppc64.AFMULS case gc.OMUL<<16 | gc.TFLOAT64: a = ppc64.AFMUL case gc.ODIV<<16 | gc.TINT8, gc.ODIV<<16 | gc.TINT16, gc.ODIV<<16 | gc.TINT32, gc.ODIV<<16 | gc.TINT64: a = ppc64.ADIVD case gc.ODIV<<16 | gc.TUINT8, gc.ODIV<<16 | gc.TUINT16, gc.ODIV<<16 | gc.TUINT32, gc.ODIV<<16 | gc.TPTR32, gc.ODIV<<16 | gc.TUINT64, gc.ODIV<<16 | gc.TPTR64: a = ppc64.ADIVDU case gc.ODIV<<16 | gc.TFLOAT32: a = ppc64.AFDIVS case gc.ODIV<<16 | gc.TFLOAT64: a = ppc64.AFDIV } return a } const ( ODynam = 1 << 0 OAddable = 1 << 1 ) func xgen(n *gc.Node, a *gc.Node, o int) bool { // TODO(minux) return -1 != 0 /*TypeKind(100016)*/ } func sudoclean() { return } /* * generate code to compute address of n, * a reference to a (perhaps nested) field inside * an array or struct. * return 0 on failure, 1 on success. * on success, leaves usable address in a. * * caller is responsible for calling sudoclean * after successful sudoaddable, * to release the register used for a. */ func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { // TODO(minux) *a = obj.Addr{} return false }
/* * generate: * newreg = &n; * * caller must regfree(a). * The generated code checks that the result is not nil. */ func agenr(n *gc.Node, a *gc.Node, res *gc.Node) { if gc.Debug['g'] != 0 { gc.Dump("agenr-n", n) } nl := n.Left nr := n.Right switch n.Op { case gc.ODOT, gc.ODOTPTR, gc.OCALLFUNC, gc.OCALLMETH, gc.OCALLINTER: var n1 gc.Node igen(n, &n1, res) regalloc(a, gc.Types[gc.Tptr], &n1) agen(&n1, a) regfree(&n1) case gc.OIND: cgenr(n.Left, a, res) gc.Cgen_checknil(a) case gc.OINDEX: var p2 *obj.Prog // to be patched to panicindex. w := uint32(n.Type.Width) bounded := gc.Debug['B'] != 0 || n.Bounded var n1 gc.Node var n3 gc.Node if nr.Addable != 0 { var tmp gc.Node if !gc.Isconst(nr, gc.CTINT) { gc.Tempname(&tmp, gc.Types[gc.TINT32]) } if !gc.Isconst(nl, gc.CTSTR) { agenr(nl, &n3, res) } if !gc.Isconst(nr, gc.CTINT) { p2 = cgenindex(nr, &tmp, bounded) regalloc(&n1, tmp.Type, nil) gmove(&tmp, &n1) } } else if nl.Addable != 0 { if !gc.Isconst(nr, gc.CTINT) { var tmp gc.Node gc.Tempname(&tmp, gc.Types[gc.TINT32]) p2 = cgenindex(nr, &tmp, bounded) regalloc(&n1, tmp.Type, nil) gmove(&tmp, &n1) } if !gc.Isconst(nl, gc.CTSTR) { agenr(nl, &n3, res) } } else { var tmp gc.Node gc.Tempname(&tmp, gc.Types[gc.TINT32]) p2 = cgenindex(nr, &tmp, bounded) nr = &tmp if !gc.Isconst(nl, gc.CTSTR) { agenr(nl, &n3, res) } regalloc(&n1, tmp.Type, nil) gins(optoas(gc.OAS, tmp.Type), &tmp, &n1) } // &a is in &n3 (allocated in res) // i is in &n1 (if not constant) // w is width // constant index if gc.Isconst(nr, gc.CTINT) { if gc.Isconst(nl, gc.CTSTR) { gc.Fatal("constant string constant index") } v := uint64(gc.Mpgetfix(nr.Val.U.Xval)) var n2 gc.Node if gc.Isslice(nl.Type) || nl.Type.Etype == gc.TSTRING { if gc.Debug['B'] == 0 && !n.Bounded { n1 = n3 n1.Op = gc.OINDREG n1.Type = gc.Types[gc.Tptr] n1.Xoffset = int64(gc.Array_nel) var n4 gc.Node regalloc(&n4, n1.Type, nil) gmove(&n1, &n4) gc.Nodconst(&n2, gc.Types[gc.TUINT32], int64(v)) gcmp(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &n4, &n2) regfree(&n4) p1 := gc.Gbranch(optoas(gc.OGT, gc.Types[gc.TUINT32]), nil, +1) ginscall(gc.Panicindex, 0) gc.Patch(p1, gc.Pc) } n1 = n3 n1.Op = gc.OINDREG n1.Type = gc.Types[gc.Tptr] n1.Xoffset = int64(gc.Array_array) gmove(&n1, &n3) } gc.Nodconst(&n2, gc.Types[gc.Tptr], int64(v*uint64(w))) gins(optoas(gc.OADD, gc.Types[gc.Tptr]), &n2, &n3) *a = n3 break } var n2 gc.Node regalloc(&n2, gc.Types[gc.TINT32], &n1) // i gmove(&n1, &n2) regfree(&n1) var n4 gc.Node if gc.Debug['B'] == 0 && !n.Bounded { // check bounds if gc.Isconst(nl, gc.CTSTR) { gc.Nodconst(&n4, gc.Types[gc.TUINT32], int64(len(nl.Val.U.Sval))) } else if gc.Isslice(nl.Type) || nl.Type.Etype == gc.TSTRING { n1 = n3 n1.Op = gc.OINDREG n1.Type = gc.Types[gc.Tptr] n1.Xoffset = int64(gc.Array_nel) regalloc(&n4, gc.Types[gc.TUINT32], nil) gmove(&n1, &n4) } else { gc.Nodconst(&n4, gc.Types[gc.TUINT32], nl.Type.Bound) } gcmp(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &n2, &n4) if n4.Op == gc.OREGISTER { regfree(&n4) } p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) if p2 != nil { gc.Patch(p2, gc.Pc) } ginscall(gc.Panicindex, 0) gc.Patch(p1, gc.Pc) } if gc.Isconst(nl, gc.CTSTR) { regalloc(&n3, gc.Types[gc.Tptr], res) p1 := gins(arm.AMOVW, nil, &n3) gc.Datastring(nl.Val.U.Sval, &p1.From) p1.From.Type = obj.TYPE_ADDR } else if gc.Isslice(nl.Type) || nl.Type.Etype == gc.TSTRING { n1 = n3 n1.Op = gc.OINDREG n1.Type = gc.Types[gc.Tptr] n1.Xoffset = int64(gc.Array_array) gmove(&n1, &n3) } if w == 0 { } else // nothing to do if w == 1 || w == 2 || w == 4 || w == 8 { n4 = gc.Node{} n4.Op = gc.OADDR n4.Left = &n2 cgen(&n4, &n3) if w == 1 { gins(arm.AADD, &n2, &n3) } else if w == 2 { gshift(arm.AADD, &n2, arm.SHIFT_LL, 1, &n3) } else if w == 4 { gshift(arm.AADD, &n2, arm.SHIFT_LL, 2, &n3) } else if w == 8 { gshift(arm.AADD, &n2, arm.SHIFT_LL, 3, &n3) } } else { regalloc(&n4, gc.Types[gc.TUINT32], nil) gc.Nodconst(&n1, gc.Types[gc.TUINT32], int64(w)) gmove(&n1, &n4) gins(optoas(gc.OMUL, gc.Types[gc.TUINT32]), &n4, &n2) gins(optoas(gc.OADD, gc.Types[gc.Tptr]), &n2, &n3) regfree(&n4) } *a = n3 regfree(&n2) default: regalloc(a, gc.Types[gc.Tptr], res) agen(n, a) } }
/* * generate: * newreg = &n; * res = newreg * * on exit, a has been changed to be *newreg. * caller must regfree(a). * The generated code checks that the result is not *nil. */ func igen(n *gc.Node, a *gc.Node, res *gc.Node) { if gc.Debug['g'] != 0 { gc.Dump("\nigen-n", n) } switch n.Op { case gc.ONAME: if (n.Class&gc.PHEAP != 0) || n.Class == gc.PPARAMREF { break } *a = *n return // Increase the refcount of the register so that igen's caller // has to call regfree. case gc.OINDREG: if n.Val.U.Reg != arm.REGSP { reg[n.Val.U.Reg]++ } *a = *n return case gc.ODOT: igen(n.Left, a, res) a.Xoffset += n.Xoffset a.Type = n.Type return case gc.ODOTPTR: if n.Left.Addable != 0 || n.Left.Op == gc.OCALLFUNC || n.Left.Op == gc.OCALLMETH || n.Left.Op == gc.OCALLINTER { // igen-able nodes. var n1 gc.Node igen(n.Left, &n1, res) regalloc(a, gc.Types[gc.Tptr], &n1) gmove(&n1, a) regfree(&n1) } else { regalloc(a, gc.Types[gc.Tptr], res) cgen(n.Left, a) } gc.Cgen_checknil(a) a.Op = gc.OINDREG a.Xoffset = n.Xoffset a.Type = n.Type return // Release res so that it is available for cgen_call. // Pick it up again after the call. case gc.OCALLMETH, gc.OCALLFUNC, gc.OCALLINTER: r := -1 if n.Ullman >= gc.UINF { if res != nil && (res.Op == gc.OREGISTER || res.Op == gc.OINDREG) { r = int(res.Val.U.Reg) reg[r]-- } } switch n.Op { case gc.OCALLMETH: gc.Cgen_callmeth(n, 0) case gc.OCALLFUNC: cgen_call(n, 0) case gc.OCALLINTER: cgen_callinter(n, nil, 0) } if r >= 0 { reg[r]++ } regalloc(a, gc.Types[gc.Tptr], res) cgen_aret(n, a) a.Op = gc.OINDREG a.Type = n.Type return } agenr(n, a, res) a.Op = gc.OINDREG a.Type = n.Type }
/* * generate: * newreg = &n; * res = newreg * * on exit, a has been changed to be *newreg. * caller must regfree(a). * The generated code checks that the result is not *nil. */ func igen(n *gc.Node, a *gc.Node, res *gc.Node) { if gc.Debug['g'] != 0 { gc.Dump("\nigen-n", n) } switch n.Op { case gc.ONAME: if (n.Class&gc.PHEAP != 0) || n.Class == gc.PPARAMREF { break } *a = *n return // Increase the refcount of the register so that igen's caller // has to call regfree. case gc.OINDREG: if n.Val.U.Reg != x86.REG_SP { reg[n.Val.U.Reg]++ } *a = *n return case gc.ODOT: igen(n.Left, a, res) a.Xoffset += n.Xoffset a.Type = n.Type return case gc.ODOTPTR: switch n.Left.Op { // igen-able nodes. case gc.ODOT, gc.ODOTPTR, gc.OCALLFUNC, gc.OCALLMETH, gc.OCALLINTER: var n1 gc.Node igen(n.Left, &n1, res) regalloc(a, gc.Types[gc.Tptr], &n1) gmove(&n1, a) regfree(&n1) default: regalloc(a, gc.Types[gc.Tptr], res) cgen(n.Left, a) } gc.Cgen_checknil(a) a.Op = gc.OINDREG a.Xoffset += n.Xoffset a.Type = n.Type return case gc.OCALLFUNC, gc.OCALLMETH, gc.OCALLINTER: switch n.Op { case gc.OCALLFUNC: cgen_call(n, 0) case gc.OCALLMETH: gc.Cgen_callmeth(n, 0) case gc.OCALLINTER: cgen_callinter(n, nil, 0) } var flist gc.Iter fp := gc.Structfirst(&flist, gc.Getoutarg(n.Left.Type)) *a = gc.Node{} a.Op = gc.OINDREG a.Val.U.Reg = x86.REG_SP a.Addable = 1 a.Xoffset = fp.Width a.Type = n.Type return // Index of fixed-size array by constant can // put the offset in the addressing. // Could do the same for slice except that we need // to use the real index for the bounds checking. case gc.OINDEX: if gc.Isfixedarray(n.Left.Type) || (gc.Isptr[n.Left.Type.Etype] && gc.Isfixedarray(n.Left.Left.Type)) { if gc.Isconst(n.Right, gc.CTINT) { // Compute &a. if !gc.Isptr[n.Left.Type.Etype] { igen(n.Left, a, res) } else { var n1 gc.Node igen(n.Left, &n1, res) gc.Cgen_checknil(&n1) regalloc(a, gc.Types[gc.Tptr], res) gmove(&n1, a) regfree(&n1) a.Op = gc.OINDREG } // Compute &a[i] as &a + i*width. a.Type = n.Type a.Xoffset += gc.Mpgetfix(n.Right.Val.U.Xval) * n.Type.Width return } } } // release register for now, to avoid // confusing tempname. if res != nil && res.Op == gc.OREGISTER { reg[res.Val.U.Reg]-- } var n1 gc.Node gc.Tempname(&n1, gc.Types[gc.Tptr]) agen(n, &n1) if res != nil && res.Op == gc.OREGISTER { reg[res.Val.U.Reg]++ } regalloc(a, gc.Types[gc.Tptr], res) gmove(&n1, a) a.Op = gc.OINDREG a.Type = n.Type }
/* * generate: * res = &n; * The generated code checks that the result is not nil. */ func agen(n *gc.Node, res *gc.Node) { if gc.Debug['g'] != 0 { gc.Dump("\nagen-res", res) gc.Dump("agen-r", n) } if n == nil || n.Type == nil { return } for n.Op == gc.OCONVNOP { n = n.Left } if gc.Isconst(n, gc.CTNIL) && n.Type.Width > int64(gc.Widthptr) { // Use of a nil interface or nil slice. // Create a temporary we can take the address of and read. // The generated code is just going to panic, so it need not // be terribly efficient. See issue 3670. var n1 gc.Node gc.Tempname(&n1, n.Type) gc.Gvardef(&n1) clearfat(&n1) var n2 gc.Node regalloc(&n2, gc.Types[gc.Tptr], res) var n3 gc.Node n3.Op = gc.OADDR n3.Left = &n1 gins(ppc64.AMOVD, &n3, &n2) gmove(&n2, res) regfree(&n2) return } if n.Addable != 0 { var n1 gc.Node n1.Op = gc.OADDR n1.Left = n var n2 gc.Node regalloc(&n2, gc.Types[gc.Tptr], res) gins(ppc64.AMOVD, &n1, &n2) gmove(&n2, res) regfree(&n2) return } nl := n.Left switch n.Op { default: gc.Fatal("agen: unknown op %v", gc.Nconv(n, obj.FmtShort|obj.FmtSign)) // TODO(minux): 5g has this: Release res so that it is available for cgen_call. // Pick it up again after the call for OCALLMETH and OCALLFUNC. case gc.OCALLMETH: gc.Cgen_callmeth(n, 0) cgen_aret(n, res) case gc.OCALLINTER: cgen_callinter(n, res, 0) cgen_aret(n, res) case gc.OCALLFUNC: cgen_call(n, 0) cgen_aret(n, res) case gc.OSLICE, gc.OSLICEARR, gc.OSLICESTR, gc.OSLICE3, gc.OSLICE3ARR: var n1 gc.Node gc.Tempname(&n1, n.Type) gc.Cgen_slice(n, &n1) agen(&n1, res) case gc.OEFACE: var n1 gc.Node gc.Tempname(&n1, n.Type) gc.Cgen_eface(n, &n1) agen(&n1, res) case gc.OINDEX: var n1 gc.Node agenr(n, &n1, res) gmove(&n1, res) regfree(&n1) // should only get here with names in this func. case gc.ONAME: if n.Funcdepth > 0 && n.Funcdepth != gc.Funcdepth { gc.Dump("bad agen", n) gc.Fatal("agen: bad ONAME funcdepth %d != %d", n.Funcdepth, gc.Funcdepth) } // should only get here for heap vars or paramref if n.Class&gc.PHEAP == 0 && n.Class != gc.PPARAMREF { gc.Dump("bad agen", n) gc.Fatal("agen: bad ONAME class %#x", n.Class) } cgen(n.Heapaddr, res) if n.Xoffset != 0 { ginsadd(optoas(gc.OADD, gc.Types[gc.Tptr]), n.Xoffset, res) } case gc.OIND: cgen(nl, res) gc.Cgen_checknil(res) case gc.ODOT: agen(nl, res) if n.Xoffset != 0 { ginsadd(optoas(gc.OADD, gc.Types[gc.Tptr]), n.Xoffset, res) } case gc.ODOTPTR: cgen(nl, res) gc.Cgen_checknil(res) if n.Xoffset != 0 { ginsadd(optoas(gc.OADD, gc.Types[gc.Tptr]), n.Xoffset, res) } } }
/* * generate code to compute address of n, * a reference to a (perhaps nested) field inside * an array or struct. * return 0 on failure, 1 on success. * on success, leaves usable address in a. * * caller is responsible for calling sudoclean * after successful sudoaddable, * to release the register used for a. */ func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { if n.Type == nil { return false } *a = obj.Addr{} switch n.Op { case gc.OLITERAL: if !gc.Isconst(n, gc.CTINT) { break } v := n.Int() if v >= 32000 || v <= -32000 { break } switch as { default: return false case x86.AADDB, x86.AADDW, x86.AADDL, x86.AADDQ, x86.ASUBB, x86.ASUBW, x86.ASUBL, x86.ASUBQ, x86.AANDB, x86.AANDW, x86.AANDL, x86.AANDQ, x86.AORB, x86.AORW, x86.AORL, x86.AORQ, x86.AXORB, x86.AXORW, x86.AXORL, x86.AXORQ, x86.AINCB, x86.AINCW, x86.AINCL, x86.AINCQ, x86.ADECB, x86.ADECW, x86.ADECL, x86.ADECQ, x86.AMOVB, x86.AMOVW, x86.AMOVL, x86.AMOVQ: break } cleani += 2 reg := &clean[cleani-1] reg1 := &clean[cleani-2] reg.Op = gc.OEMPTY reg1.Op = gc.OEMPTY gc.Naddr(a, n) return true case gc.ODOT, gc.ODOTPTR: cleani += 2 reg := &clean[cleani-1] reg1 := &clean[cleani-2] reg.Op = gc.OEMPTY reg1.Op = gc.OEMPTY var nn *gc.Node var oary [10]int64 o := gc.Dotoffset(n, oary[:], &nn) if nn == nil { sudoclean() return false } if nn.Addable && o == 1 && oary[0] >= 0 { // directly addressable set of DOTs n1 := *nn n1.Type = n.Type n1.Xoffset += oary[0] gc.Naddr(a, &n1) return true } gc.Regalloc(reg, gc.Types[gc.Tptr], nil) n1 := *reg n1.Op = gc.OINDREG if oary[0] >= 0 { gc.Agen(nn, reg) n1.Xoffset = oary[0] } else { gc.Cgen(nn, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[0] + 1) } for i := 1; i < o; i++ { if oary[i] >= 0 { gc.Fatal("can't happen") } gins(movptr, &n1, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[i] + 1) } a.Type = obj.TYPE_NONE a.Index = obj.TYPE_NONE gc.Fixlargeoffset(&n1) gc.Naddr(a, &n1) return true case gc.OINDEX: return false } return false }
func anyregalloc() bool { var j int for i := x86.REG_AX; i <= x86.REG_R15; i++ { if reg[i] == 0 { goto ok } for j = 0; j < len(resvd); j++ { if resvd[j] == i { goto ok } } return true ok: } return false } var regpc [x86.REG_R15 + 1 - x86.REG_AX]uint32 /* * allocate register of type t, leave in n. * if o != N, o is desired fixed register. * caller must regfree(n). */ func regalloc(n *gc.Node, t *gc.Type, o *gc.Node) { if t == nil { gc.Fatal("regalloc: t nil") } et := int(gc.Simtype[t.Etype]) var i int switch et { case gc.TINT8, gc.TUINT8, gc.TINT16, gc.TUINT16, gc.TINT32, gc.TUINT32, gc.TINT64, gc.TUINT64, gc.TPTR32, gc.TPTR64, gc.TBOOL: if o != nil && o.Op == gc.OREGISTER { i = int(o.Val.U.Reg) if i >= x86.REG_AX && i <= x86.REG_R15 { goto out } } for i = x86.REG_AX; i <= x86.REG_R15; i++ { if reg[i] == 0 { regpc[i-x86.REG_AX] = uint32(obj.Getcallerpc(&n)) goto out } } gc.Flusherrors() for i := 0; i+x86.REG_AX <= x86.REG_R15; i++ { fmt.Printf("%d %p\n", i, regpc[i]) } gc.Fatal("out of fixed registers") case gc.TFLOAT32, gc.TFLOAT64: if o != nil && o.Op == gc.OREGISTER { i = int(o.Val.U.Reg) if i >= x86.REG_X0 && i <= x86.REG_X15 { goto out } } for i = x86.REG_X0; i <= x86.REG_X15; i++ { if reg[i] == 0 { goto out } } gc.Fatal("out of floating registers") case gc.TCOMPLEX64, gc.TCOMPLEX128: gc.Tempname(n, t) return } gc.Fatal("regalloc: unknown type %v", gc.Tconv(t, 0)) return out: reg[i]++ gc.Nodreg(n, t, i) } func regfree(n *gc.Node) { if n.Op == gc.ONAME { return } if n.Op != gc.OREGISTER && n.Op != gc.OINDREG { gc.Fatal("regfree: not a register") } i := int(n.Val.U.Reg) if i == x86.REG_SP { return } if i < 0 || i >= len(reg) { gc.Fatal("regfree: reg out of range") } if reg[i] <= 0 { gc.Fatal("regfree: reg not allocated") } reg[i]-- if reg[i] == 0 && x86.REG_AX <= i && i <= x86.REG_R15 { regpc[i-x86.REG_AX] = 0 } } /* * generate * as $c, reg */ func gconreg(as int, c int64, reg int) { var nr gc.Node switch as { case x86.AADDL, x86.AMOVL, x86.ALEAL: gc.Nodreg(&nr, gc.Types[gc.TINT32], reg) default: gc.Nodreg(&nr, gc.Types[gc.TINT64], reg) } ginscon(as, c, &nr) } /* * generate * as $c, n */ func ginscon(as int, c int64, n2 *gc.Node) { var n1 gc.Node switch as { case x86.AADDL, x86.AMOVL, x86.ALEAL: gc.Nodconst(&n1, gc.Types[gc.TINT32], c) default: gc.Nodconst(&n1, gc.Types[gc.TINT64], c) } if as != x86.AMOVQ && (c < -(1<<31) || c >= 1<<31) { // cannot have 64-bit immediate in ADD, etc. // instead, MOV into register first. var ntmp gc.Node regalloc(&ntmp, gc.Types[gc.TINT64], nil) gins(x86.AMOVQ, &n1, &ntmp) gins(as, &ntmp, n2) regfree(&ntmp) return } gins(as, &n1, n2) } /* * set up nodes representing 2^63 */ var bigi gc.Node var bigf gc.Node var bignodes_did int func bignodes() { if bignodes_did != 0 { return } bignodes_did = 1 gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 1) gc.Mpshiftfix(bigi.Val.U.Xval, 63) bigf = bigi bigf.Type = gc.Types[gc.TFLOAT64] bigf.Val.Ctype = gc.CTFLT bigf.Val.U.Fval = new(gc.Mpflt) gc.Mpmovefixflt(bigf.Val.U.Fval, bigi.Val.U.Xval) } /* * generate move: * t = f * hard part is conversions. */ func gmove(f *gc.Node, t *gc.Node) { if gc.Debug['M'] != 0 { fmt.Printf("gmove %v -> %v\n", gc.Nconv(f, obj.FmtLong), gc.Nconv(t, obj.FmtLong)) } ft := gc.Simsimtype(f.Type) tt := gc.Simsimtype(t.Type) cvt := t.Type if gc.Iscomplex[ft] || gc.Iscomplex[tt] { gc.Complexmove(f, t) return } // cannot have two memory operands var a int if gc.Ismem(f) && gc.Ismem(t) { goto hard } // convert constant to desired type if f.Op == gc.OLITERAL { var con gc.Node gc.Convconst(&con, t.Type, &f.Val) f = &con ft = tt // so big switch will choose a simple mov // some constants can't move directly to memory. if gc.Ismem(t) { // float constants come from memory. if gc.Isfloat[tt] { goto hard } // 64-bit immediates are really 32-bit sign-extended // unless moving into a register. if gc.Isint[tt] { if gc.Mpcmpfixfix(con.Val.U.Xval, gc.Minintval[gc.TINT32]) < 0 { goto hard } if gc.Mpcmpfixfix(con.Val.U.Xval, gc.Maxintval[gc.TINT32]) > 0 { goto hard } } } } // value -> value copy, only one memory operand. // figure out the instruction to use. // break out of switch for one-instruction gins. // goto rdst for "destination must be register". // goto hard for "convert to cvt type first". // otherwise handle and return. switch uint32(ft)<<16 | uint32(tt) { default: gc.Fatal("gmove %v -> %v", gc.Tconv(f.Type, obj.FmtLong), gc.Tconv(t.Type, obj.FmtLong)) /* * integer copy and truncate */ case gc.TINT8<<16 | gc.TINT8, // same size gc.TINT8<<16 | gc.TUINT8, gc.TUINT8<<16 | gc.TINT8, gc.TUINT8<<16 | gc.TUINT8, gc.TINT16<<16 | gc.TINT8, // truncate gc.TUINT16<<16 | gc.TINT8, gc.TINT32<<16 | gc.TINT8, gc.TUINT32<<16 | gc.TINT8, gc.TINT64<<16 | gc.TINT8, gc.TUINT64<<16 | gc.TINT8, gc.TINT16<<16 | gc.TUINT8, gc.TUINT16<<16 | gc.TUINT8, gc.TINT32<<16 | gc.TUINT8, gc.TUINT32<<16 | gc.TUINT8, gc.TINT64<<16 | gc.TUINT8, gc.TUINT64<<16 | gc.TUINT8: a = x86.AMOVB case gc.TINT16<<16 | gc.TINT16, // same size gc.TINT16<<16 | gc.TUINT16, gc.TUINT16<<16 | gc.TINT16, gc.TUINT16<<16 | gc.TUINT16, gc.TINT32<<16 | gc.TINT16, // truncate gc.TUINT32<<16 | gc.TINT16, gc.TINT64<<16 | gc.TINT16, gc.TUINT64<<16 | gc.TINT16, gc.TINT32<<16 | gc.TUINT16, gc.TUINT32<<16 | gc.TUINT16, gc.TINT64<<16 | gc.TUINT16, gc.TUINT64<<16 | gc.TUINT16: a = x86.AMOVW case gc.TINT32<<16 | gc.TINT32, // same size gc.TINT32<<16 | gc.TUINT32, gc.TUINT32<<16 | gc.TINT32, gc.TUINT32<<16 | gc.TUINT32: a = x86.AMOVL case gc.TINT64<<16 | gc.TINT32, // truncate gc.TUINT64<<16 | gc.TINT32, gc.TINT64<<16 | gc.TUINT32, gc.TUINT64<<16 | gc.TUINT32: a = x86.AMOVQL case gc.TINT64<<16 | gc.TINT64, // same size gc.TINT64<<16 | gc.TUINT64, gc.TUINT64<<16 | gc.TINT64, gc.TUINT64<<16 | gc.TUINT64: a = x86.AMOVQ /* * integer up-conversions */ case gc.TINT8<<16 | gc.TINT16, // sign extend int8 gc.TINT8<<16 | gc.TUINT16: a = x86.AMOVBWSX goto rdst case gc.TINT8<<16 | gc.TINT32, gc.TINT8<<16 | gc.TUINT32: a = x86.AMOVBLSX goto rdst case gc.TINT8<<16 | gc.TINT64, gc.TINT8<<16 | gc.TUINT64: a = x86.AMOVBQSX goto rdst case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 gc.TUINT8<<16 | gc.TUINT16: a = x86.AMOVBWZX goto rdst case gc.TUINT8<<16 | gc.TINT32, gc.TUINT8<<16 | gc.TUINT32: a = x86.AMOVBLZX goto rdst case gc.TUINT8<<16 | gc.TINT64, gc.TUINT8<<16 | gc.TUINT64: a = x86.AMOVBQZX goto rdst case gc.TINT16<<16 | gc.TINT32, // sign extend int16 gc.TINT16<<16 | gc.TUINT32: a = x86.AMOVWLSX goto rdst case gc.TINT16<<16 | gc.TINT64, gc.TINT16<<16 | gc.TUINT64: a = x86.AMOVWQSX goto rdst case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 gc.TUINT16<<16 | gc.TUINT32: a = x86.AMOVWLZX goto rdst case gc.TUINT16<<16 | gc.TINT64, gc.TUINT16<<16 | gc.TUINT64: a = x86.AMOVWQZX goto rdst case gc.TINT32<<16 | gc.TINT64, // sign extend int32 gc.TINT32<<16 | gc.TUINT64: a = x86.AMOVLQSX goto rdst // AMOVL into a register zeros the top of the register, // so this is not always necessary, but if we rely on AMOVL // the optimizer is almost certain to screw with us. case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 gc.TUINT32<<16 | gc.TUINT64: a = x86.AMOVLQZX goto rdst /* * float to integer */ case gc.TFLOAT32<<16 | gc.TINT32: a = x86.ACVTTSS2SL goto rdst case gc.TFLOAT64<<16 | gc.TINT32: a = x86.ACVTTSD2SL goto rdst case gc.TFLOAT32<<16 | gc.TINT64: a = x86.ACVTTSS2SQ goto rdst case gc.TFLOAT64<<16 | gc.TINT64: a = x86.ACVTTSD2SQ goto rdst // convert via int32. case gc.TFLOAT32<<16 | gc.TINT16, gc.TFLOAT32<<16 | gc.TINT8, gc.TFLOAT32<<16 | gc.TUINT16, gc.TFLOAT32<<16 | gc.TUINT8, gc.TFLOAT64<<16 | gc.TINT16, gc.TFLOAT64<<16 | gc.TINT8, gc.TFLOAT64<<16 | gc.TUINT16, gc.TFLOAT64<<16 | gc.TUINT8: cvt = gc.Types[gc.TINT32] goto hard // convert via int64. case gc.TFLOAT32<<16 | gc.TUINT32, gc.TFLOAT64<<16 | gc.TUINT32: cvt = gc.Types[gc.TINT64] goto hard // algorithm is: // if small enough, use native float64 -> int64 conversion. // otherwise, subtract 2^63, convert, and add it back. case gc.TFLOAT32<<16 | gc.TUINT64, gc.TFLOAT64<<16 | gc.TUINT64: a := x86.ACVTTSS2SQ if ft == gc.TFLOAT64 { a = x86.ACVTTSD2SQ } bignodes() var r1 gc.Node regalloc(&r1, gc.Types[ft], nil) var r2 gc.Node regalloc(&r2, gc.Types[tt], t) var r3 gc.Node regalloc(&r3, gc.Types[ft], nil) var r4 gc.Node regalloc(&r4, gc.Types[tt], nil) gins(optoas(gc.OAS, f.Type), f, &r1) gins(optoas(gc.OCMP, f.Type), &bigf, &r1) p1 := gc.Gbranch(optoas(gc.OLE, f.Type), nil, +1) gins(a, &r1, &r2) p2 := gc.Gbranch(obj.AJMP, nil, 0) gc.Patch(p1, gc.Pc) gins(optoas(gc.OAS, f.Type), &bigf, &r3) gins(optoas(gc.OSUB, f.Type), &r3, &r1) gins(a, &r1, &r2) gins(x86.AMOVQ, &bigi, &r4) gins(x86.AXORQ, &r4, &r2) gc.Patch(p2, gc.Pc) gmove(&r2, t) regfree(&r4) regfree(&r3) regfree(&r2) regfree(&r1) return /* * integer to float */ case gc.TINT32<<16 | gc.TFLOAT32: a = x86.ACVTSL2SS goto rdst case gc.TINT32<<16 | gc.TFLOAT64: a = x86.ACVTSL2SD goto rdst case gc.TINT64<<16 | gc.TFLOAT32: a = x86.ACVTSQ2SS goto rdst case gc.TINT64<<16 | gc.TFLOAT64: a = x86.ACVTSQ2SD goto rdst // convert via int32 case gc.TINT16<<16 | gc.TFLOAT32, gc.TINT16<<16 | gc.TFLOAT64, gc.TINT8<<16 | gc.TFLOAT32, gc.TINT8<<16 | gc.TFLOAT64, gc.TUINT16<<16 | gc.TFLOAT32, gc.TUINT16<<16 | gc.TFLOAT64, gc.TUINT8<<16 | gc.TFLOAT32, gc.TUINT8<<16 | gc.TFLOAT64: cvt = gc.Types[gc.TINT32] goto hard // convert via int64. case gc.TUINT32<<16 | gc.TFLOAT32, gc.TUINT32<<16 | gc.TFLOAT64: cvt = gc.Types[gc.TINT64] goto hard // algorithm is: // if small enough, use native int64 -> uint64 conversion. // otherwise, halve (rounding to odd?), convert, and double. case gc.TUINT64<<16 | gc.TFLOAT32, gc.TUINT64<<16 | gc.TFLOAT64: a := x86.ACVTSQ2SS if tt == gc.TFLOAT64 { a = x86.ACVTSQ2SD } var zero gc.Node gc.Nodconst(&zero, gc.Types[gc.TUINT64], 0) var one gc.Node gc.Nodconst(&one, gc.Types[gc.TUINT64], 1) var r1 gc.Node regalloc(&r1, f.Type, f) var r2 gc.Node regalloc(&r2, t.Type, t) var r3 gc.Node regalloc(&r3, f.Type, nil) var r4 gc.Node regalloc(&r4, f.Type, nil) gmove(f, &r1) gins(x86.ACMPQ, &r1, &zero) p1 := gc.Gbranch(x86.AJLT, nil, +1) gins(a, &r1, &r2) p2 := gc.Gbranch(obj.AJMP, nil, 0) gc.Patch(p1, gc.Pc) gmove(&r1, &r3) gins(x86.ASHRQ, &one, &r3) gmove(&r1, &r4) gins(x86.AANDL, &one, &r4) gins(x86.AORQ, &r4, &r3) gins(a, &r3, &r2) gins(optoas(gc.OADD, t.Type), &r2, &r2) gc.Patch(p2, gc.Pc) gmove(&r2, t) regfree(&r4) regfree(&r3) regfree(&r2) regfree(&r1) return /* * float to float */ case gc.TFLOAT32<<16 | gc.TFLOAT32: a = x86.AMOVSS case gc.TFLOAT64<<16 | gc.TFLOAT64: a = x86.AMOVSD case gc.TFLOAT32<<16 | gc.TFLOAT64: a = x86.ACVTSS2SD goto rdst case gc.TFLOAT64<<16 | gc.TFLOAT32: a = x86.ACVTSD2SS goto rdst } gins(a, f, t) return // requires register destination rdst: { var r1 gc.Node regalloc(&r1, t.Type, t) gins(a, f, &r1) gmove(&r1, t) regfree(&r1) return } // requires register intermediate hard: var r1 gc.Node regalloc(&r1, cvt, t) gmove(f, &r1) gmove(&r1, t) regfree(&r1) return } func samaddr(f *gc.Node, t *gc.Node) bool { if f.Op != t.Op { return false } switch f.Op { case gc.OREGISTER: if f.Val.U.Reg != t.Val.U.Reg { break } return true } return false } /* * generate one instruction: * as f, t */ func gins(as int, f *gc.Node, t *gc.Node) *obj.Prog { // Node nod; // if(f != N && f->op == OINDEX) { // regalloc(&nod, ®node, Z); // v = constnode.vconst; // cgen(f->right, &nod); // constnode.vconst = v; // idx.reg = nod.reg; // regfree(&nod); // } // if(t != N && t->op == OINDEX) { // regalloc(&nod, ®node, Z); // v = constnode.vconst; // cgen(t->right, &nod); // constnode.vconst = v; // idx.reg = nod.reg; // regfree(&nod); // } switch as { case x86.AMOVB, x86.AMOVW, x86.AMOVL, x86.AMOVQ, x86.AMOVSS, x86.AMOVSD: if f != nil && t != nil && samaddr(f, t) { return nil } case x86.ALEAQ: if f != nil && gc.Isconst(f, gc.CTNIL) { gc.Fatal("gins LEAQ nil %v", gc.Tconv(f.Type, 0)) } } var af obj.Addr if f != nil { af = gc.Naddr(f) } var at obj.Addr if t != nil { at = gc.Naddr(t) } p := gc.Prog(as) if f != nil { p.From = af } if t != nil { p.To = at } if gc.Debug['g'] != 0 { fmt.Printf("%v\n", p) } w := int32(0) switch as { case x86.AMOVB: w = 1 case x86.AMOVW: w = 2 case x86.AMOVL: w = 4 case x86.AMOVQ: w = 8 } if w != 0 && ((f != nil && af.Width < int64(w)) || (t != nil && at.Width > int64(w))) { gc.Dump("f", f) gc.Dump("t", t) gc.Fatal("bad width: %v (%d, %d)\n", p, af.Width, at.Width) } if p.To.Type == obj.TYPE_ADDR && w > 0 { gc.Fatal("bad use of addr: %v", p) } return p } func fixlargeoffset(n *gc.Node) { if n == nil { return } if n.Op != gc.OINDREG { return } if n.Val.U.Reg == x86.REG_SP { // stack offset cannot be large return } if n.Xoffset != int64(int32(n.Xoffset)) { // offset too large, add to register instead. a := *n a.Op = gc.OREGISTER a.Type = gc.Types[gc.Tptr] a.Xoffset = 0 gc.Cgen_checknil(&a) ginscon(optoas(gc.OADD, gc.Types[gc.Tptr]), n.Xoffset, &a) n.Xoffset = 0 } } /* * return Axxx for Oxxx on type t. */ func optoas(op int, t *gc.Type) int { if t == nil { gc.Fatal("optoas: t is nil") } a := obj.AXXX switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { default: gc.Fatal("optoas: no entry %v-%v", gc.Oconv(int(op), 0), gc.Tconv(t, 0)) case gc.OADDR<<16 | gc.TPTR32: a = x86.ALEAL case gc.OADDR<<16 | gc.TPTR64: a = x86.ALEAQ case gc.OEQ<<16 | gc.TBOOL, gc.OEQ<<16 | gc.TINT8, gc.OEQ<<16 | gc.TUINT8, gc.OEQ<<16 | gc.TINT16, gc.OEQ<<16 | gc.TUINT16, gc.OEQ<<16 | gc.TINT32, gc.OEQ<<16 | gc.TUINT32, gc.OEQ<<16 | gc.TINT64, gc.OEQ<<16 | gc.TUINT64, gc.OEQ<<16 | gc.TPTR32, gc.OEQ<<16 | gc.TPTR64, gc.OEQ<<16 | gc.TFLOAT32, gc.OEQ<<16 | gc.TFLOAT64: a = x86.AJEQ case gc.ONE<<16 | gc.TBOOL, gc.ONE<<16 | gc.TINT8, gc.ONE<<16 | gc.TUINT8, gc.ONE<<16 | gc.TINT16, gc.ONE<<16 | gc.TUINT16, gc.ONE<<16 | gc.TINT32, gc.ONE<<16 | gc.TUINT32, gc.ONE<<16 | gc.TINT64, gc.ONE<<16 | gc.TUINT64, gc.ONE<<16 | gc.TPTR32, gc.ONE<<16 | gc.TPTR64, gc.ONE<<16 | gc.TFLOAT32, gc.ONE<<16 | gc.TFLOAT64: a = x86.AJNE case gc.OLT<<16 | gc.TINT8, gc.OLT<<16 | gc.TINT16, gc.OLT<<16 | gc.TINT32, gc.OLT<<16 | gc.TINT64: a = x86.AJLT case gc.OLT<<16 | gc.TUINT8, gc.OLT<<16 | gc.TUINT16, gc.OLT<<16 | gc.TUINT32, gc.OLT<<16 | gc.TUINT64: a = x86.AJCS case gc.OLE<<16 | gc.TINT8, gc.OLE<<16 | gc.TINT16, gc.OLE<<16 | gc.TINT32, gc.OLE<<16 | gc.TINT64: a = x86.AJLE case gc.OLE<<16 | gc.TUINT8, gc.OLE<<16 | gc.TUINT16, gc.OLE<<16 | gc.TUINT32, gc.OLE<<16 | gc.TUINT64: a = x86.AJLS case gc.OGT<<16 | gc.TINT8, gc.OGT<<16 | gc.TINT16, gc.OGT<<16 | gc.TINT32, gc.OGT<<16 | gc.TINT64: a = x86.AJGT case gc.OGT<<16 | gc.TUINT8, gc.OGT<<16 | gc.TUINT16, gc.OGT<<16 | gc.TUINT32, gc.OGT<<16 | gc.TUINT64, gc.OLT<<16 | gc.TFLOAT32, gc.OLT<<16 | gc.TFLOAT64: a = x86.AJHI case gc.OGE<<16 | gc.TINT8, gc.OGE<<16 | gc.TINT16, gc.OGE<<16 | gc.TINT32, gc.OGE<<16 | gc.TINT64: a = x86.AJGE case gc.OGE<<16 | gc.TUINT8, gc.OGE<<16 | gc.TUINT16, gc.OGE<<16 | gc.TUINT32, gc.OGE<<16 | gc.TUINT64, gc.OLE<<16 | gc.TFLOAT32, gc.OLE<<16 | gc.TFLOAT64: a = x86.AJCC case gc.OCMP<<16 | gc.TBOOL, gc.OCMP<<16 | gc.TINT8, gc.OCMP<<16 | gc.TUINT8: a = x86.ACMPB case gc.OCMP<<16 | gc.TINT16, gc.OCMP<<16 | gc.TUINT16: a = x86.ACMPW case gc.OCMP<<16 | gc.TINT32, gc.OCMP<<16 | gc.TUINT32, gc.OCMP<<16 | gc.TPTR32: a = x86.ACMPL case gc.OCMP<<16 | gc.TINT64, gc.OCMP<<16 | gc.TUINT64, gc.OCMP<<16 | gc.TPTR64: a = x86.ACMPQ case gc.OCMP<<16 | gc.TFLOAT32: a = x86.AUCOMISS case gc.OCMP<<16 | gc.TFLOAT64: a = x86.AUCOMISD case gc.OAS<<16 | gc.TBOOL, gc.OAS<<16 | gc.TINT8, gc.OAS<<16 | gc.TUINT8: a = x86.AMOVB case gc.OAS<<16 | gc.TINT16, gc.OAS<<16 | gc.TUINT16: a = x86.AMOVW case gc.OAS<<16 | gc.TINT32, gc.OAS<<16 | gc.TUINT32, gc.OAS<<16 | gc.TPTR32: a = x86.AMOVL case gc.OAS<<16 | gc.TINT64, gc.OAS<<16 | gc.TUINT64, gc.OAS<<16 | gc.TPTR64: a = x86.AMOVQ case gc.OAS<<16 | gc.TFLOAT32: a = x86.AMOVSS case gc.OAS<<16 | gc.TFLOAT64: a = x86.AMOVSD case gc.OADD<<16 | gc.TINT8, gc.OADD<<16 | gc.TUINT8: a = x86.AADDB case gc.OADD<<16 | gc.TINT16, gc.OADD<<16 | gc.TUINT16: a = x86.AADDW case gc.OADD<<16 | gc.TINT32, gc.OADD<<16 | gc.TUINT32, gc.OADD<<16 | gc.TPTR32: a = x86.AADDL case gc.OADD<<16 | gc.TINT64, gc.OADD<<16 | gc.TUINT64, gc.OADD<<16 | gc.TPTR64: a = x86.AADDQ case gc.OADD<<16 | gc.TFLOAT32: a = x86.AADDSS case gc.OADD<<16 | gc.TFLOAT64: a = x86.AADDSD case gc.OSUB<<16 | gc.TINT8, gc.OSUB<<16 | gc.TUINT8: a = x86.ASUBB case gc.OSUB<<16 | gc.TINT16, gc.OSUB<<16 | gc.TUINT16: a = x86.ASUBW case gc.OSUB<<16 | gc.TINT32, gc.OSUB<<16 | gc.TUINT32, gc.OSUB<<16 | gc.TPTR32: a = x86.ASUBL case gc.OSUB<<16 | gc.TINT64, gc.OSUB<<16 | gc.TUINT64, gc.OSUB<<16 | gc.TPTR64: a = x86.ASUBQ case gc.OSUB<<16 | gc.TFLOAT32: a = x86.ASUBSS case gc.OSUB<<16 | gc.TFLOAT64: a = x86.ASUBSD case gc.OINC<<16 | gc.TINT8, gc.OINC<<16 | gc.TUINT8: a = x86.AINCB case gc.OINC<<16 | gc.TINT16, gc.OINC<<16 | gc.TUINT16: a = x86.AINCW case gc.OINC<<16 | gc.TINT32, gc.OINC<<16 | gc.TUINT32, gc.OINC<<16 | gc.TPTR32: a = x86.AINCL case gc.OINC<<16 | gc.TINT64, gc.OINC<<16 | gc.TUINT64, gc.OINC<<16 | gc.TPTR64: a = x86.AINCQ case gc.ODEC<<16 | gc.TINT8, gc.ODEC<<16 | gc.TUINT8: a = x86.ADECB case gc.ODEC<<16 | gc.TINT16, gc.ODEC<<16 | gc.TUINT16: a = x86.ADECW case gc.ODEC<<16 | gc.TINT32, gc.ODEC<<16 | gc.TUINT32, gc.ODEC<<16 | gc.TPTR32: a = x86.ADECL case gc.ODEC<<16 | gc.TINT64, gc.ODEC<<16 | gc.TUINT64, gc.ODEC<<16 | gc.TPTR64: a = x86.ADECQ case gc.OMINUS<<16 | gc.TINT8, gc.OMINUS<<16 | gc.TUINT8: a = x86.ANEGB case gc.OMINUS<<16 | gc.TINT16, gc.OMINUS<<16 | gc.TUINT16: a = x86.ANEGW case gc.OMINUS<<16 | gc.TINT32, gc.OMINUS<<16 | gc.TUINT32, gc.OMINUS<<16 | gc.TPTR32: a = x86.ANEGL case gc.OMINUS<<16 | gc.TINT64, gc.OMINUS<<16 | gc.TUINT64, gc.OMINUS<<16 | gc.TPTR64: a = x86.ANEGQ case gc.OAND<<16 | gc.TINT8, gc.OAND<<16 | gc.TUINT8: a = x86.AANDB case gc.OAND<<16 | gc.TINT16, gc.OAND<<16 | gc.TUINT16: a = x86.AANDW case gc.OAND<<16 | gc.TINT32, gc.OAND<<16 | gc.TUINT32, gc.OAND<<16 | gc.TPTR32: a = x86.AANDL case gc.OAND<<16 | gc.TINT64, gc.OAND<<16 | gc.TUINT64, gc.OAND<<16 | gc.TPTR64: a = x86.AANDQ case gc.OOR<<16 | gc.TINT8, gc.OOR<<16 | gc.TUINT8: a = x86.AORB case gc.OOR<<16 | gc.TINT16, gc.OOR<<16 | gc.TUINT16: a = x86.AORW case gc.OOR<<16 | gc.TINT32, gc.OOR<<16 | gc.TUINT32, gc.OOR<<16 | gc.TPTR32: a = x86.AORL case gc.OOR<<16 | gc.TINT64, gc.OOR<<16 | gc.TUINT64, gc.OOR<<16 | gc.TPTR64: a = x86.AORQ case gc.OXOR<<16 | gc.TINT8, gc.OXOR<<16 | gc.TUINT8: a = x86.AXORB case gc.OXOR<<16 | gc.TINT16, gc.OXOR<<16 | gc.TUINT16: a = x86.AXORW case gc.OXOR<<16 | gc.TINT32, gc.OXOR<<16 | gc.TUINT32, gc.OXOR<<16 | gc.TPTR32: a = x86.AXORL case gc.OXOR<<16 | gc.TINT64, gc.OXOR<<16 | gc.TUINT64, gc.OXOR<<16 | gc.TPTR64: a = x86.AXORQ case gc.OLROT<<16 | gc.TINT8, gc.OLROT<<16 | gc.TUINT8: a = x86.AROLB case gc.OLROT<<16 | gc.TINT16, gc.OLROT<<16 | gc.TUINT16: a = x86.AROLW case gc.OLROT<<16 | gc.TINT32, gc.OLROT<<16 | gc.TUINT32, gc.OLROT<<16 | gc.TPTR32: a = x86.AROLL case gc.OLROT<<16 | gc.TINT64, gc.OLROT<<16 | gc.TUINT64, gc.OLROT<<16 | gc.TPTR64: a = x86.AROLQ case gc.OLSH<<16 | gc.TINT8, gc.OLSH<<16 | gc.TUINT8: a = x86.ASHLB case gc.OLSH<<16 | gc.TINT16, gc.OLSH<<16 | gc.TUINT16: a = x86.ASHLW case gc.OLSH<<16 | gc.TINT32, gc.OLSH<<16 | gc.TUINT32, gc.OLSH<<16 | gc.TPTR32: a = x86.ASHLL case gc.OLSH<<16 | gc.TINT64, gc.OLSH<<16 | gc.TUINT64, gc.OLSH<<16 | gc.TPTR64: a = x86.ASHLQ case gc.ORSH<<16 | gc.TUINT8: a = x86.ASHRB case gc.ORSH<<16 | gc.TUINT16: a = x86.ASHRW case gc.ORSH<<16 | gc.TUINT32, gc.ORSH<<16 | gc.TPTR32: a = x86.ASHRL case gc.ORSH<<16 | gc.TUINT64, gc.ORSH<<16 | gc.TPTR64: a = x86.ASHRQ case gc.ORSH<<16 | gc.TINT8: a = x86.ASARB case gc.ORSH<<16 | gc.TINT16: a = x86.ASARW case gc.ORSH<<16 | gc.TINT32: a = x86.ASARL case gc.ORSH<<16 | gc.TINT64: a = x86.ASARQ case gc.ORROTC<<16 | gc.TINT8, gc.ORROTC<<16 | gc.TUINT8: a = x86.ARCRB case gc.ORROTC<<16 | gc.TINT16, gc.ORROTC<<16 | gc.TUINT16: a = x86.ARCRW case gc.ORROTC<<16 | gc.TINT32, gc.ORROTC<<16 | gc.TUINT32: a = x86.ARCRL case gc.ORROTC<<16 | gc.TINT64, gc.ORROTC<<16 | gc.TUINT64: a = x86.ARCRQ case gc.OHMUL<<16 | gc.TINT8, gc.OMUL<<16 | gc.TINT8, gc.OMUL<<16 | gc.TUINT8: a = x86.AIMULB case gc.OHMUL<<16 | gc.TINT16, gc.OMUL<<16 | gc.TINT16, gc.OMUL<<16 | gc.TUINT16: a = x86.AIMULW case gc.OHMUL<<16 | gc.TINT32, gc.OMUL<<16 | gc.TINT32, gc.OMUL<<16 | gc.TUINT32, gc.OMUL<<16 | gc.TPTR32: a = x86.AIMULL case gc.OHMUL<<16 | gc.TINT64, gc.OMUL<<16 | gc.TINT64, gc.OMUL<<16 | gc.TUINT64, gc.OMUL<<16 | gc.TPTR64: a = x86.AIMULQ case gc.OHMUL<<16 | gc.TUINT8: a = x86.AMULB case gc.OHMUL<<16 | gc.TUINT16: a = x86.AMULW case gc.OHMUL<<16 | gc.TUINT32, gc.OHMUL<<16 | gc.TPTR32: a = x86.AMULL case gc.OHMUL<<16 | gc.TUINT64, gc.OHMUL<<16 | gc.TPTR64: a = x86.AMULQ case gc.OMUL<<16 | gc.TFLOAT32: a = x86.AMULSS case gc.OMUL<<16 | gc.TFLOAT64: a = x86.AMULSD case gc.ODIV<<16 | gc.TINT8, gc.OMOD<<16 | gc.TINT8: a = x86.AIDIVB case gc.ODIV<<16 | gc.TUINT8, gc.OMOD<<16 | gc.TUINT8: a = x86.ADIVB case gc.ODIV<<16 | gc.TINT16, gc.OMOD<<16 | gc.TINT16: a = x86.AIDIVW case gc.ODIV<<16 | gc.TUINT16, gc.OMOD<<16 | gc.TUINT16: a = x86.ADIVW case gc.ODIV<<16 | gc.TINT32, gc.OMOD<<16 | gc.TINT32: a = x86.AIDIVL case gc.ODIV<<16 | gc.TUINT32, gc.ODIV<<16 | gc.TPTR32, gc.OMOD<<16 | gc.TUINT32, gc.OMOD<<16 | gc.TPTR32: a = x86.ADIVL case gc.ODIV<<16 | gc.TINT64, gc.OMOD<<16 | gc.TINT64: a = x86.AIDIVQ case gc.ODIV<<16 | gc.TUINT64, gc.ODIV<<16 | gc.TPTR64, gc.OMOD<<16 | gc.TUINT64, gc.OMOD<<16 | gc.TPTR64: a = x86.ADIVQ case gc.OEXTEND<<16 | gc.TINT16: a = x86.ACWD case gc.OEXTEND<<16 | gc.TINT32: a = x86.ACDQ case gc.OEXTEND<<16 | gc.TINT64: a = x86.ACQO case gc.ODIV<<16 | gc.TFLOAT32: a = x86.ADIVSS case gc.ODIV<<16 | gc.TFLOAT64: a = x86.ADIVSD } return a } const ( ODynam = 1 << 0 OAddable = 1 << 1 ) var clean [20]gc.Node var cleani int = 0 func xgen(n *gc.Node, a *gc.Node, o int) bool { regalloc(a, gc.Types[gc.Tptr], nil) if o&ODynam != 0 { if n.Addable != 0 { if n.Op != gc.OINDREG { if n.Op != gc.OREGISTER { return true } } } } agen(n, a) return false } func sudoclean() { if clean[cleani-1].Op != gc.OEMPTY { regfree(&clean[cleani-1]) } if clean[cleani-2].Op != gc.OEMPTY { regfree(&clean[cleani-2]) } cleani -= 2 } /* * generate code to compute address of n, * a reference to a (perhaps nested) field inside * an array or struct. * return 0 on failure, 1 on success. * on success, leaves usable address in a. * * caller is responsible for calling sudoclean * after successful sudoaddable, * to release the register used for a. */ func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool { if n.Type == nil { return false } *a = obj.Addr{} switch n.Op { case gc.OLITERAL: if !gc.Isconst(n, gc.CTINT) { break } v := gc.Mpgetfix(n.Val.U.Xval) if v >= 32000 || v <= -32000 { break } switch as { default: return false case x86.AADDB, x86.AADDW, x86.AADDL, x86.AADDQ, x86.ASUBB, x86.ASUBW, x86.ASUBL, x86.ASUBQ, x86.AANDB, x86.AANDW, x86.AANDL, x86.AANDQ, x86.AORB, x86.AORW, x86.AORL, x86.AORQ, x86.AXORB, x86.AXORW, x86.AXORL, x86.AXORQ, x86.AINCB, x86.AINCW, x86.AINCL, x86.AINCQ, x86.ADECB, x86.ADECW, x86.ADECL, x86.ADECQ, x86.AMOVB, x86.AMOVW, x86.AMOVL, x86.AMOVQ: break } cleani += 2 reg := &clean[cleani-1] reg1 := &clean[cleani-2] reg.Op = gc.OEMPTY reg1.Op = gc.OEMPTY *a = gc.Naddr(n) return true case gc.ODOT, gc.ODOTPTR: cleani += 2 reg := &clean[cleani-1] reg1 := &clean[cleani-2] reg.Op = gc.OEMPTY reg1.Op = gc.OEMPTY var nn *gc.Node var oary [10]int64 o := gc.Dotoffset(n, oary[:], &nn) if nn == nil { sudoclean() return false } if nn.Addable != 0 && o == 1 && oary[0] >= 0 { // directly addressable set of DOTs n1 := *nn n1.Type = n.Type n1.Xoffset += oary[0] *a = gc.Naddr(&n1) return true } regalloc(reg, gc.Types[gc.Tptr], nil) n1 := *reg n1.Op = gc.OINDREG if oary[0] >= 0 { agen(nn, reg) n1.Xoffset = oary[0] } else { cgen(nn, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[0] + 1) } for i := 1; i < o; i++ { if oary[i] >= 0 { gc.Fatal("can't happen") } gins(movptr, &n1, reg) gc.Cgen_checknil(reg) n1.Xoffset = -(oary[i] + 1) } a.Type = obj.TYPE_NONE a.Index = obj.TYPE_NONE fixlargeoffset(&n1) *a = gc.Naddr(&n1) return true case gc.OINDEX: return false } return false }