/* * 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) { gc.Igen(n, &n1, nil) sclean[nsclean-1] = n1 } n = &n1 case gc.ONAME: if n.Class == gc.PPARAMREF { var n1 gc.Node gc.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 defframe(ptxt *obj.Prog) { var n *gc.Node // fill in argument size, stack size ptxt.To.Type = obj.TYPE_TEXTSIZE ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr))) frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg))) ptxt.To.Offset = int64(frame) // insert code to zero ambiguously live variables // so that the garbage collector only sees initialized values // when it looks for pointers. p := ptxt hi := int64(0) lo := hi ax := uint32(0) // iterate through declarations - they are sorted in decreasing xoffset order. for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next { n = l.N if !n.Needzero { continue } if n.Class != gc.PAUTO { gc.Fatal("needzero class %d", n.Class) } if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 { gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset)) } if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) { // merge with range we already have lo = n.Xoffset continue } // zero old range p = zerorange(p, int64(frame), lo, hi, &ax) // set new range hi = n.Xoffset + n.Type.Width lo = n.Xoffset } // zero final range zerorange(p, int64(frame), lo, hi, &ax) }
/* * insert n into reg slot of p */ func raddr(n *gc.Node, p *obj.Prog) { var a obj.Addr gc.Naddr(&a, 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 proginfo(p *obj.Prog) { info := &p.Info *info = progtable[p.As] if info.Flags == 0 { gc.Fatal("unknown instruction %v", p) } if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) { info.Flags &^= gc.LeftRead info.Flags |= gc.LeftAddr } if (info.Flags&gc.RegRead != 0) && p.Reg == 0 { info.Flags &^= gc.RegRead info.Flags |= gc.CanRegRead | gc.RightRead } if (p.Scond&arm.C_SCOND != arm.C_SCOND_NONE) && (info.Flags&gc.RightWrite != 0) { info.Flags |= gc.RightRead } switch p.As { case arm.ADIV, arm.ADIVU, arm.AMOD, arm.AMODU: info.Regset |= RtoB(arm.REG_R12) } }
func proginfo(p *obj.Prog) { info := &p.Info *info = progtable[p.As] if info.Flags == 0 { gc.Fatal("unknown instruction %v", p) } if (info.Flags&gc.ShiftCX != 0) && p.From.Type != obj.TYPE_CONST { info.Reguse |= CX } if info.Flags&gc.ImulAXDX != 0 { if p.To.Type == obj.TYPE_NONE { info.Reguse |= AX info.Regset |= AX | DX } else { info.Flags |= RightRdwr } } // Addressing makes some registers used. if p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_NONE { info.Regindex |= RtoB(int(p.From.Reg)) } if p.From.Index != x86.REG_NONE { info.Regindex |= RtoB(int(p.From.Index)) } if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE { info.Regindex |= RtoB(int(p.To.Reg)) } if p.To.Index != x86.REG_NONE { info.Regindex |= RtoB(int(p.To.Index)) } }
/* * call to n has already been generated. * generate: * res = &return value from call. */ func cgen_aret(n *gc.Node, res *gc.Node) { t := n.Left.Type if gc.Isptr[t.Etype] { t = t.Type } var flist gc.Iter fp := gc.Structfirst(&flist, gc.Getoutarg(t)) if fp == nil { gc.Fatal("cgen_aret: nil") } var nod1 gc.Node nod1.Op = gc.OINDREG nod1.Val.U.Reg = x86.REG_SP nod1.Addable = 1 nod1.Xoffset = fp.Width nod1.Type = fp.Type if res.Op != gc.OREGISTER { var nod2 gc.Node regalloc(&nod2, gc.Types[gc.Tptr], res) gins(x86.ALEAL, &nod1, &nod2) gins(x86.AMOVL, &nod2, res) regfree(&nod2) } else { gins(x86.ALEAL, &nod1, res) } }
/* * allocate a register (reusing res if possible) and generate * a = n * The caller must call regfree(a). */ func cgenr(n *gc.Node, a *gc.Node, res *gc.Node) { if gc.Debug['g'] != 0 { gc.Dump("cgenr-n", n) } if gc.Isfat(n.Type) { gc.Fatal("cgenr on fat node") } if n.Addable != 0 { regalloc(a, n.Type, res) gmove(n, a) return } switch n.Op { case gc.ONAME, gc.ODOT, gc.ODOTPTR, gc.OINDEX, gc.OCALLFUNC, gc.OCALLMETH, gc.OCALLINTER: var n1 gc.Node igen(n, &n1, res) regalloc(a, gc.Types[gc.Tptr], &n1) gmove(&n1, a) regfree(&n1) default: regalloc(a, n.Type, res) cgen(n, a) } }
/* * call to n has already been generated. * generate: * res = &return value from call. */ func cgen_aret(n *gc.Node, res *gc.Node) { t := n.Left.Type if gc.Isptr[t.Etype] { t = t.Type } var flist gc.Iter fp := gc.Structfirst(&flist, gc.Getoutarg(t)) if fp == nil { gc.Fatal("cgen_aret: nil") } var nod1 gc.Node nod1.Op = gc.OINDREG nod1.Val.U.Reg = ppc64.REGSP nod1.Addable = 1 nod1.Xoffset = fp.Width + int64(gc.Widthptr) // +widthptr: saved lr at 0(SP) nod1.Type = fp.Type if res.Op != gc.OREGISTER { var nod2 gc.Node regalloc(&nod2, gc.Types[gc.Tptr], res) agen(&nod1, &nod2) gins(ppc64.AMOVD, &nod2, res) regfree(&nod2) } else { agen(&nod1, res) } }
/* * 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 { rawgins(as, n2, &n1) return } case ppc64.ACMPU: if 0 <= c && c <= 2*ppc64.BIG { rawgins(as, n2, &n1) return } } // MOV n1 into register first var ntmp gc.Node gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) rawgins(ppc64.AMOVD, &n1, &ntmp) rawgins(as, n2, &ntmp) gc.Regfree(&ntmp) }
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 := rawgins(as, rhs, nil) raddr(lhs, p) return p }
func splitclean() { if nsclean <= 0 { gc.Fatal("splitclean") } nsclean-- if sclean[nsclean].Op != gc.OEMPTY { gc.Regfree(&sclean[nsclean]) } }
// as2variant returns the variant (V_*) flags of instruction as. func as2variant(as int) int { initvariants() for i := int(0); i < len(varianttable[as]); i++ { if varianttable[as][i] == as { return i } } gc.Fatal("as2variant: instruction %v is not a variant of itself", obj.Aconv(as)) return 0 }
/* * direct reference, * could be set/use depending on * semantics */ func copyas(a *obj.Addr, v *obj.Addr) bool { if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_BL { gc.Fatal("use of byte register") } if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_BL { gc.Fatal("use of byte register") } if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { return false } if regtyp(v) { return true } if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { if v.Offset == a.Offset { return true } } return false }
/* 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.Reg)&15 return p }
// Called after regopt and peep have run. // Expand CHECKNIL pseudo-op into actual nil pointer check. func expandchecks(firstp *obj.Prog) { var p1 *obj.Prog var p2 *obj.Prog for p := (*obj.Prog)(firstp); p != nil; p = p.Link { if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 { fmt.Printf("expandchecks: %v\n", p) } if p.As != obj.ACHECKNIL { continue } if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers gc.Warnl(int(p.Lineno), "generated nil check") } if p.From.Type != obj.TYPE_REG { gc.Fatal("invalid nil check %v\n", p) } // check is // CMP arg, ZR // BNE 2(PC) [likely] // MOVD ZR, 0(arg) p1 = gc.Ctxt.NewProg() p2 = gc.Ctxt.NewProg() gc.Clearp(p1) gc.Clearp(p2) p1.Link = p2 p2.Link = p.Link p.Link = p1 p1.Lineno = p.Lineno p2.Lineno = p.Lineno p1.Pc = 9999 p2.Pc = 9999 p.As = arm64.ACMP p.Reg = arm64.REGZERO p1.As = arm64.ABNE //p1->from.type = TYPE_CONST; //p1->from.offset = 1; // likely p1.To.Type = obj.TYPE_BRANCH p1.To.Val = p2.Link // crash by write to memory address 0. p2.As = arm64.AMOVD p2.From.Type = obj.TYPE_REG p2.From.Reg = arm64.REGZERO p2.To.Type = obj.TYPE_MEM p2.To.Reg = p.From.Reg p2.To.Offset = 0 } }
/* * generate high multiply * res = (nl * nr) >> wordsize */ func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { if nl.Ullman < nr.Ullman { tmp := nl nl = nr nr = tmp } t := nl.Type w := int(t.Width * 8) var n1 gc.Node gc.Regalloc(&n1, t, res) gc.Cgen(nl, &n1) var n2 gc.Node gc.Regalloc(&n2, t, nil) gc.Cgen(nr, &n2) switch gc.Simtype[t.Etype] { case gc.TINT8, gc.TINT16: gins(optoas(gc.OMUL, t), &n2, &n1) gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(w), &n1) case gc.TUINT8, gc.TUINT16: gins(optoas(gc.OMUL, t), &n2, &n1) gshift(arm.AMOVW, &n1, arm.SHIFT_LR, int32(w), &n1) // perform a long multiplication. case gc.TINT32, gc.TUINT32: var p *obj.Prog if gc.Issigned[t.Etype] { p = gins(arm.AMULL, &n2, nil) } else { p = gins(arm.AMULLU, &n2, nil) } // n2 * n1 -> (n1 n2) p.Reg = n1.Reg p.To.Type = obj.TYPE_REGREG p.To.Reg = n1.Reg p.To.Offset = int64(n2.Reg) default: gc.Fatal("cgen_hmul %v", t) } gc.Cgen(&n1, res) gc.Regfree(&n1) gc.Regfree(&n2) }
func proginfo(p *obj.Prog) (info gc.ProgInfo) { initproginfo() info = progtable[p.As] if info.Flags == 0 { info = progtable[ppc64.AADD] gc.Fatal("proginfo: unknown instruction %v", p) } if (info.Flags&gc.RegRead != 0) && p.Reg == 0 { info.Flags &^= gc.RegRead info.Flags |= gc.RightRead /*CanRegRead |*/ } if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR) && p.From.Reg != 0 { info.Regindex |= RtoB(int(p.From.Reg)) if info.Flags&gc.PostInc != 0 { info.Regset |= RtoB(int(p.From.Reg)) } } if (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) && p.To.Reg != 0 { info.Regindex |= RtoB(int(p.To.Reg)) if info.Flags&gc.PostInc != 0 { info.Regset |= RtoB(int(p.To.Reg)) } } if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) { info.Flags &^= gc.LeftRead info.Flags |= gc.LeftAddr } if p.As == obj.ADUFFZERO { info.Reguse |= 1<<0 | RtoB(ppc64.REG_R3) info.Regset |= RtoB(ppc64.REG_R3) } if p.As == obj.ADUFFCOPY { // TODO(austin) Revisit when duffcopy is implemented info.Reguse |= RtoB(ppc64.REG_R3) | RtoB(ppc64.REG_R4) | RtoB(ppc64.REG_R5) info.Regset |= RtoB(ppc64.REG_R3) | RtoB(ppc64.REG_R4) } return }
/* * generate high multiply: * res = (nl*nr) >> width */ func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { // largest ullman on left. if nl.Ullman < nr.Ullman { tmp := (*gc.Node)(nl) nl = nr nr = tmp } t := (*gc.Type)(nl.Type) w := int(int(t.Width * 8)) var n1 gc.Node gc.Cgenr(nl, &n1, res) var n2 gc.Node gc.Cgenr(nr, &n2, nil) switch gc.Simtype[t.Etype] { case gc.TINT8, gc.TINT16, gc.TINT32: gins(optoas(gc.OMUL, t), &n2, &n1) p := (*obj.Prog)(gins(ppc64.ASRAD, nil, &n1)) p.From.Type = obj.TYPE_CONST p.From.Offset = int64(w) case gc.TUINT8, gc.TUINT16, gc.TUINT32: gins(optoas(gc.OMUL, t), &n2, &n1) p := (*obj.Prog)(gins(ppc64.ASRD, nil, &n1)) p.From.Type = obj.TYPE_CONST p.From.Offset = int64(w) case gc.TINT64, gc.TUINT64: if gc.Issigned[t.Etype] { gins(ppc64.AMULHD, &n2, &n1) } else { gins(ppc64.AMULHDU, &n2, &n1) } default: gc.Fatal("cgen_hmul %v", gc.Tconv(t, 0)) } gc.Cgen(&n1, res) gc.Regfree(&n1) gc.Regfree(&n2) }
func proginfo(p *obj.Prog) { info := &p.Info *info = progtable[p.As] if info.Flags == 0 { gc.Fatal("unknown instruction %v", p) } if (info.Flags&gc.ShiftCX != 0) && p.From.Type != obj.TYPE_CONST { info.Reguse |= CX } if info.Flags&gc.ImulAXDX != 0 { if p.To.Type == obj.TYPE_NONE { info.Reguse |= AX info.Regset |= AX | DX } else { info.Flags |= RightRdwr } } // Addressing makes some registers used. if p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_NONE { info.Regindex |= RtoB(int(p.From.Reg)) } if p.From.Index != x86.REG_NONE { info.Regindex |= RtoB(int(p.From.Index)) } if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE { info.Regindex |= RtoB(int(p.To.Reg)) } if p.To.Index != x86.REG_NONE { info.Regindex |= RtoB(int(p.To.Index)) } if gc.Ctxt.Flag_dynlink { // When -dynlink is passed, many operations on external names (and // also calling duffzero/duffcopy) use R15 as a scratch register. if p.As == x86.ALEAQ || info.Flags == gc.Pseudo || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { return } if p.As == obj.ADUFFZERO || p.As == obj.ADUFFCOPY || (p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local) || (p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local) { info.Reguse |= R15 info.Regset |= R15 return } } }
func proginfo(p *obj.Prog) { info := &p.Info *info = progtable[p.As] if info.Flags == 0 { gc.Fatal("proginfo: unknown instruction %v", p) } if (info.Flags&gc.RegRead != 0) && p.Reg == 0 { info.Flags &^= gc.RegRead info.Flags |= gc.RightRead /*CanRegRead |*/ } if (p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR) && p.From.Reg != 0 { info.Regindex |= RtoB(int(p.From.Reg)) if p.Scond != 0 { info.Regset |= RtoB(int(p.From.Reg)) } } if (p.To.Type == obj.TYPE_MEM || p.To.Type == obj.TYPE_ADDR) && p.To.Reg != 0 { info.Regindex |= RtoB(int(p.To.Reg)) if p.Scond != 0 { info.Regset |= RtoB(int(p.To.Reg)) } } if p.From.Type == obj.TYPE_ADDR && p.From.Sym != nil && (info.Flags&gc.LeftRead != 0) { info.Flags &^= gc.LeftRead info.Flags |= gc.LeftAddr } if p.As == obj.ADUFFZERO { info.Reguse |= RtoB(arm64.REGRT1) info.Regset |= RtoB(arm64.REGRT1) } if p.As == obj.ADUFFCOPY { // TODO(austin) Revisit when duffcopy is implemented info.Reguse |= RtoB(arm64.REGRT1) | RtoB(arm64.REGRT2) | RtoB(arm64.REG_R5) info.Regset |= RtoB(arm64.REGRT1) | RtoB(arm64.REGRT2) } }
/* * generate division according to op, one of: * res = nl / nr * res = nl % nr */ func cgen_div(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) { if gc.Is64(nl.Type) { gc.Fatal("cgen_div %v", gc.Tconv(nl.Type, 0)) } var t *gc.Type if gc.Issigned[nl.Type.Etype] { t = gc.Types[gc.TINT32] } else { t = gc.Types[gc.TUINT32] } var ax gc.Node var oldax gc.Node savex(x86.REG_AX, &ax, &oldax, res, t) var olddx gc.Node var dx gc.Node savex(x86.REG_DX, &dx, &olddx, res, t) dodiv(op, nl, nr, res, &ax, &dx) restx(&dx, &olddx) restx(&ax, &oldax) }
/* * call to n has already been generated. * generate: * res = return value from call. */ func cgen_callret(n *gc.Node, res *gc.Node) { t := n.Left.Type if t.Etype == gc.TPTR32 || t.Etype == gc.TPTR64 { t = t.Type } var flist gc.Iter fp := gc.Structfirst(&flist, gc.Getoutarg(t)) if fp == nil { gc.Fatal("cgen_callret: nil") } var nod gc.Node nod.Op = gc.OINDREG nod.Val.U.Reg = x86.REG_SP nod.Addable = 1 nod.Xoffset = fp.Width nod.Type = fp.Type gc.Cgen_as(res, &nod) }
// Called after regopt and peep have run. // Expand CHECKNIL pseudo-op into actual nil pointer check. func expandchecks(firstp *obj.Prog) { var p1 *obj.Prog for p := (*obj.Prog)(firstp); p != nil; p = p.Link { if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 { fmt.Printf("expandchecks: %v\n", p) } if p.As != obj.ACHECKNIL { continue } if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers gc.Warnl(int(p.Lineno), "generated nil check") } if p.From.Type != obj.TYPE_REG { gc.Fatal("invalid nil check %v\n", p) } // check is // CBNZ arg, 2(PC) // MOVD ZR, 0(arg) p1 = gc.Ctxt.NewProg() gc.Clearp(p1) p1.Link = p.Link p.Link = p1 p1.Lineno = p.Lineno p1.Pc = 9999 p.As = arm64.ACBNZ p.To.Type = obj.TYPE_BRANCH p.To.Val = p1.Link // crash by write to memory address 0. p1.As = arm64.AMOVD p1.From.Type = obj.TYPE_REG p1.From.Reg = arm64.REGZERO p1.To.Type = obj.TYPE_MEM p1.To.Reg = p.From.Reg p1.To.Offset = 0 } }
// Called after regopt and peep have run. // Expand CHECKNIL pseudo-op into actual nil pointer check. func expandchecks(firstp *obj.Prog) { var reg int var p1 *obj.Prog for p := firstp; p != nil; p = p.Link { if p.As != obj.ACHECKNIL { continue } if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers gc.Warnl(int(p.Lineno), "generated nil check") } if p.From.Type != obj.TYPE_REG { gc.Fatal("invalid nil check %v", p) } reg = int(p.From.Reg) // check is // CMP arg, $0 // MOV.EQ arg, 0(arg) p1 = gc.Ctxt.NewProg() gc.Clearp(p1) p1.Link = p.Link p.Link = p1 p1.Lineno = p.Lineno p1.Pc = 9999 p1.As = arm.AMOVW p1.From.Type = obj.TYPE_REG p1.From.Reg = int16(reg) p1.To.Type = obj.TYPE_MEM p1.To.Reg = int16(reg) p1.To.Offset = 0 p1.Scond = arm.C_SCOND_EQ p.As = arm.ACMP p.From.Type = obj.TYPE_CONST p.From.Reg = 0 p.From.Offset = 0 p.Reg = int16(reg) } }
func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32) *obj.Prog { cnt := hi - lo if cnt == 0 { return p } if *ax == 0 { p = appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, 0, obj.TYPE_REG, x86.REG_AX, 0) *ax = 1 } if cnt%int64(gc.Widthreg) != 0 { // should only happen with nacl if cnt%int64(gc.Widthptr) != 0 { gc.Fatal("zerorange count not a multiple of widthptr %d", cnt) } p = appendpp(p, x86.AMOVL, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo) lo += int64(gc.Widthptr) cnt -= int64(gc.Widthptr) } if cnt <= int64(4*gc.Widthreg) { for i := int64(0); i < cnt; i += int64(gc.Widthreg) { p = appendpp(p, x86.AMOVQ, obj.TYPE_REG, x86.REG_AX, 0, obj.TYPE_MEM, x86.REG_SP, frame+lo+i) } } else if !gc.Nacl && (cnt <= int64(128*gc.Widthreg)) { q := cnt / int64(gc.Widthreg) p = appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo+dzDI(q), obj.TYPE_REG, x86.REG_DI, 0) p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_ADDR, 0, dzOff(q)) p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) } else { p = appendpp(p, x86.AMOVQ, obj.TYPE_CONST, 0, cnt/int64(gc.Widthreg), obj.TYPE_REG, x86.REG_CX, 0) p = appendpp(p, leaptr, obj.TYPE_MEM, x86.REG_SP, frame+lo, obj.TYPE_REG, x86.REG_DI, 0) p = appendpp(p, x86.AREP, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0) p = appendpp(p, x86.ASTOSQ, obj.TYPE_NONE, 0, 0, obj.TYPE_NONE, 0, 0) } return p }
// jmptoset returns ASETxx for AJxx. func jmptoset(jmp int) int { switch jmp { case x86.AJEQ: return x86.ASETEQ case x86.AJNE: return x86.ASETNE case x86.AJLT: return x86.ASETLT case x86.AJCS: return x86.ASETCS case x86.AJLE: return x86.ASETLE case x86.AJLS: return x86.ASETLS case x86.AJGT: return x86.ASETGT case x86.AJHI: return x86.ASETHI case x86.AJGE: return x86.ASETGE case x86.AJCC: return x86.ASETCC case x86.AJMI: return x86.ASETMI case x86.AJOC: return x86.ASETOC case x86.AJOS: return x86.ASETOS case x86.AJPC: return x86.ASETPC case x86.AJPL: return x86.ASETPL case x86.AJPS: return x86.ASETPS } gc.Fatal("jmptoset: no entry for %v", gc.Oconv(jmp, 0)) panic("unreachable") }
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 } }
func cgen_floatsse(n *gc.Node, res *gc.Node) { var a int nl := n.Left nr := n.Right switch n.Op { default: gc.Dump("cgen_floatsse", n) gc.Fatal("cgen_floatsse %v", gc.Oconv(int(n.Op), 0)) return case gc.OMINUS, gc.OCOM: nr = gc.Nodintconst(-1) gc.Convlit(&nr, n.Type) a = foptoas(gc.OMUL, nl.Type, 0) goto sbop // symmetric binary case gc.OADD, gc.OMUL: a = foptoas(int(n.Op), nl.Type, 0) goto sbop // asymmetric binary case gc.OSUB, gc.OMOD, gc.ODIV: a = foptoas(int(n.Op), nl.Type, 0) goto abop } sbop: // symmetric binary if nl.Ullman < nr.Ullman || nl.Op == gc.OLITERAL { r := nl nl = nr nr = r } abop: // asymmetric binary if nl.Ullman >= nr.Ullman { var nt gc.Node gc.Tempname(&nt, nl.Type) gc.Cgen(nl, &nt) var n2 gc.Node gc.Mgen(nr, &n2, nil) var n1 gc.Node gc.Regalloc(&n1, nl.Type, res) gmove(&nt, &n1) gins(a, &n2, &n1) gmove(&n1, res) gc.Regfree(&n1) gc.Mfree(&n2) } else { var n2 gc.Node gc.Regalloc(&n2, nr.Type, res) gc.Cgen(nr, &n2) var n1 gc.Node gc.Regalloc(&n1, nl.Type, nil) gc.Cgen(nl, &n1) gins(a, &n2, &n1) gc.Regfree(&n2) gmove(&n1, res) gc.Regfree(&n1) } return }
/* * generate shift according to op, one of: * res = nl << nr * res = nl >> nr */ func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { if nl.Type.Width > 4 { gc.Fatal("cgen_shift %v", gc.Tconv(nl.Type, 0)) } w := int(nl.Type.Width * 8) a := optoas(op, nl.Type) if nr.Op == gc.OLITERAL { var n2 gc.Node gc.Tempname(&n2, nl.Type) gc.Cgen(nl, &n2) var n1 gc.Node gc.Regalloc(&n1, nl.Type, res) gmove(&n2, &n1) sc := uint64(gc.Mpgetfix(nr.Val.U.Xval)) if sc >= uint64(nl.Type.Width*8) { // large shift gets 2 shifts by width-1 gins(a, ncon(uint32(w)-1), &n1) gins(a, ncon(uint32(w)-1), &n1) } else { gins(a, nr, &n1) } gmove(&n1, res) gc.Regfree(&n1) return } var oldcx gc.Node var cx gc.Node gc.Nodreg(&cx, gc.Types[gc.TUINT32], x86.REG_CX) if reg[x86.REG_CX] > 1 && !gc.Samereg(&cx, res) { gc.Tempname(&oldcx, gc.Types[gc.TUINT32]) gmove(&cx, &oldcx) } var n1 gc.Node var nt gc.Node if nr.Type.Width > 4 { gc.Tempname(&nt, nr.Type) n1 = nt } else { gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) gc.Regalloc(&n1, nr.Type, &n1) // to hold the shift type in CX } var n2 gc.Node if gc.Samereg(&cx, res) { gc.Regalloc(&n2, nl.Type, nil) } else { gc.Regalloc(&n2, nl.Type, res) } if nl.Ullman >= nr.Ullman { gc.Cgen(nl, &n2) gc.Cgen(nr, &n1) } else { gc.Cgen(nr, &n1) gc.Cgen(nl, &n2) } // test and fix up large shifts if bounded { if nr.Type.Width > 4 { // delayed reg alloc gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) gc.Regalloc(&n1, gc.Types[gc.TUINT32], &n1) // to hold the shift type in CX var lo gc.Node var hi gc.Node split64(&nt, &lo, &hi) gmove(&lo, &n1) splitclean() } } else { var p1 *obj.Prog if nr.Type.Width > 4 { // delayed reg alloc gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) gc.Regalloc(&n1, gc.Types[gc.TUINT32], &n1) // to hold the shift type in CX var lo gc.Node var hi gc.Node split64(&nt, &lo, &hi) gmove(&lo, &n1) gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &hi, ncon(0)) p2 := gc.Gbranch(optoas(gc.ONE, gc.Types[gc.TUINT32]), nil, +1) gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &n1, ncon(uint32(w))) p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) splitclean() gc.Patch(p2, gc.Pc) } else { gins(optoas(gc.OCMP, nr.Type), &n1, ncon(uint32(w))) p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) } if op == gc.ORSH && gc.Issigned[nl.Type.Etype] { gins(a, ncon(uint32(w)-1), &n2) } else { gmove(ncon(0), &n2) } gc.Patch(p1, gc.Pc) } gins(a, &n1, &n2) if oldcx.Op != 0 { gmove(&oldcx, &cx) } gmove(&n2, res) gc.Regfree(&n1) gc.Regfree(&n2) }
/* * 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: // No OLE for floats, because it mishandles NaN. // Front end must reverse comparison or use OLT and OEQ together. 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: // No OGE for floats, because it mishandles NaN. // Front end must reverse comparison or use OLT and OEQ together. 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 }