func outgcode(a int, g1 *obj.Addr, reg int, g2, g3 *obj.Addr) { var p *obj.Prog var pl *obj.Plist if asm.Pass == 1 { goto out } p = asm.Ctxt.NewProg() p.As = int16(a) p.Lineno = stmtline if nosched != 0 { p.Mark |= ppc64.NOSCHED } p.From = *g1 p.Reg = int16(reg) p.From3 = *g2 p.To = *g3 p.Pc = int64(asm.PC) if lastpc == nil { pl = obj.Linknewplist(asm.Ctxt) pl.Firstpc = p } else { lastpc.Link = p } lastpc = p out: if a != obj.AGLOBL && a != obj.ADATA { asm.PC++ } }
func outcode(a int, g2 *Addr2) { var p *obj.Prog var pl *obj.Plist if asm.Pass == 1 { goto out } p = new(obj.Prog) *p = obj.Prog{} p.Ctxt = asm.Ctxt p.As = int16(a) p.Lineno = stmtline p.From = g2.from if g2.from3.Type != 0 { p.From3 = new(obj.Addr) *p.From3 = g2.from3 } p.To = g2.to p.Pc = int64(asm.PC) if lastpc == nil { pl = obj.Linknewplist(asm.Ctxt) pl.Firstpc = p } else { lastpc.Link = p } lastpc = p out: if a != obj.AGLOBL && a != obj.ADATA { asm.PC++ } }
func compile(fn *Node) { if Newproc == nil { Newproc = Sysfunc("newproc") Deferproc = Sysfunc("deferproc") Deferreturn = Sysfunc("deferreturn") Panicindex = Sysfunc("panicindex") panicslice = Sysfunc("panicslice") throwreturn = Sysfunc("throwreturn") } lno := setlineno(fn) Curfn = fn dowidth(Curfn.Type) var oldstksize int64 var nod1 Node var ptxt *obj.Prog var pl *obj.Plist var p *obj.Prog var n *Node var nam *Node var gcargs *Sym var gclocals *Sym if fn.Nbody == nil { if pure_go != 0 || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") { Yyerror("missing function body for %q", fn.Func.Nname.Sym.Name) goto ret } if Debug['A'] != 0 { goto ret } emitptrargsmap() goto ret } saveerrors() // set up domain for labels clearlabels() if Curfn.Type.Outnamed { // add clearing of the output parameters var save Iter t := Structfirst(&save, Getoutarg(Curfn.Type)) for t != nil { if t.Nname != nil { n = Nod(OAS, t.Nname, nil) typecheck(&n, Etop) Curfn.Nbody = concat(list1(n), Curfn.Nbody) } t = structnext(&save) } } order(Curfn) if nerrors != 0 { goto ret } hasdefer = false walk(Curfn) if nerrors != 0 { goto ret } if instrumenting { instrument(Curfn) } if nerrors != 0 { goto ret } continpc = nil breakpc = nil pl = newplist() pl.Name = Linksym(Curfn.Func.Nname.Sym) setlineno(Curfn) Nodconst(&nod1, Types[TINT32], 0) nam = Curfn.Func.Nname if isblank(nam) { nam = nil } ptxt = Thearch.Gins(obj.ATEXT, nam, &nod1) Afunclit(&ptxt.From, Curfn.Func.Nname) ptxt.From3 = new(obj.Addr) if fn.Func.Dupok { ptxt.From3.Offset |= obj.DUPOK } if fn.Func.Wrapper { ptxt.From3.Offset |= obj.WRAPPER } if fn.Func.Needctxt { ptxt.From3.Offset |= obj.NEEDCTXT } if fn.Func.Nosplit { ptxt.From3.Offset |= obj.NOSPLIT } if fn.Func.Systemstack { ptxt.From.Sym.Cfunc = 1 } // Clumsy but important. // See test/recover.go for test cases and src/reflect/value.go // for the actual functions being considered. if myimportpath != "" && myimportpath == "reflect" { if Curfn.Func.Nname.Sym.Name == "callReflect" || Curfn.Func.Nname.Sym.Name == "callMethod" { ptxt.From3.Offset |= obj.WRAPPER } } ginit() gcargs = makefuncdatasym("gcargs·%d", obj.FUNCDATA_ArgsPointerMaps) gclocals = makefuncdatasym("gclocals·%d", obj.FUNCDATA_LocalsPointerMaps) for _, t := range Curfn.Func.Fieldtrack { gtrack(tracksym(t)) } for l := fn.Func.Dcl; l != nil; l = l.Next { n = l.N if n.Op != ONAME { // might be OTYPE or OLITERAL continue } switch n.Class { case PAUTO, PPARAM, PPARAMOUT: Nodconst(&nod1, Types[TUINTPTR], l.N.Type.Width) p = Thearch.Gins(obj.ATYPE, l.N, &nod1) p.From.Gotype = Linksym(ngotype(l.N)) } } Genlist(Curfn.Func.Enter) Genlist(Curfn.Nbody) gclean() checklabels() if nerrors != 0 { goto ret } if Curfn.Func.Endlineno != 0 { lineno = Curfn.Func.Endlineno } if Curfn.Type.Outtuple != 0 { Ginscall(throwreturn, 0) } ginit() // TODO: Determine when the final cgen_ret can be omitted. Perhaps always? cgen_ret(nil) if hasdefer { // deferreturn pretends to have one uintptr argument. // Reserve space for it so stack scanner is happy. if Maxarg < int64(Widthptr) { Maxarg = int64(Widthptr) } } gclean() if nerrors != 0 { goto ret } Pc.As = obj.ARET // overwrite AEND Pc.Lineno = lineno fixjmp(ptxt) if Debug['N'] == 0 || Debug['R'] != 0 || Debug['P'] != 0 { regopt(ptxt) nilopt(ptxt) } Thearch.Expandchecks(ptxt) oldstksize = Stksize allocauto(ptxt) if false { fmt.Printf("allocauto: %d to %d\n", oldstksize, int64(Stksize)) } setlineno(Curfn) if int64(Stksize)+Maxarg > 1<<31 { Yyerror("stack frame too large (>2GB)") goto ret } // Emit garbage collection symbols. liveness(Curfn, ptxt, gcargs, gclocals) gcsymdup(gcargs) gcsymdup(gclocals) Thearch.Defframe(ptxt) if Debug['f'] != 0 { frame(0) } // Remove leftover instrumentation from the instruction stream. removevardef(ptxt) ret: lineno = lno }
// zerorange clears the stack in the given range. func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog { cnt := hi - lo if cnt == 0 { return p } // Adjust the frame to account for LR. frame += gc.Ctxt.FixedFrameSize() offset := frame + lo reg := int16(s390x.REGSP) // If the offset cannot fit in a 12-bit unsigned displacement then we // need to create a copy of the stack pointer that we can adjust. // We also need to do this if we are going to loop. if offset < 0 || offset > 4096-clearLoopCutoff || cnt > clearLoopCutoff { p = appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, offset, obj.TYPE_REG, s390x.REGRT1, 0) p.Reg = int16(s390x.REGSP) reg = s390x.REGRT1 offset = 0 } // Generate a loop of large clears. if cnt > clearLoopCutoff { n := cnt - (cnt % 256) end := int16(s390x.REGRT2) p = appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, offset+n, obj.TYPE_REG, end, 0) p.Reg = reg p = appendpp(p, s390x.AXC, obj.TYPE_MEM, reg, offset, obj.TYPE_MEM, reg, offset) p.From3 = new(obj.Addr) p.From3.Type = obj.TYPE_CONST p.From3.Offset = 256 pl := p p = appendpp(p, s390x.AADD, obj.TYPE_CONST, 0, 256, obj.TYPE_REG, reg, 0) p = appendpp(p, s390x.ACMP, obj.TYPE_REG, reg, 0, obj.TYPE_REG, end, 0) p = appendpp(p, s390x.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0) gc.Patch(p, pl) cnt -= n } // Generate remaining clear instructions without a loop. for cnt > 0 { n := cnt // Can clear at most 256 bytes per instruction. if n > 256 { n = 256 } switch n { // Handle very small clears with move instructions. case 8, 4, 2, 1: ins := s390x.AMOVB switch n { case 8: ins = s390x.AMOVD case 4: ins = s390x.AMOVW case 2: ins = s390x.AMOVH } p = appendpp(p, ins, obj.TYPE_CONST, 0, 0, obj.TYPE_MEM, reg, offset) // Handle clears that would require multiple move instructions with XC. default: p = appendpp(p, s390x.AXC, obj.TYPE_MEM, reg, offset, obj.TYPE_MEM, reg, offset) p.From3 = new(obj.Addr) p.From3.Type = obj.TYPE_CONST p.From3.Offset = n } cnt -= n offset += n } return p }
func peep(firstp *obj.Prog) { g := (*gc.Graph)(gc.Flowstart(firstp, nil)) if g == nil { return } gactive = 0 var p *obj.Prog if false { // constant propagation // find MOV $con,R followed by // another MOV $con,R without // setting R in the interim for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { p = r.Prog switch p.As { case s390x.AMOVB, s390x.AMOVW, s390x.AMOVD: if regtyp(&p.To) { if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST { conprop(r) } } } } } var r *gc.Flow var t int loop1: // if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { // gc.Dumpit("loop1", g.Start, 0) // } t = 0 for r = g.Start; r != nil; r = r.Link { p = r.Prog // TODO(austin) Handle smaller moves. arm and amd64 // distinguish between moves that moves that *must* // sign/zero extend and moves that don't care so they // can eliminate moves that don't care without // breaking moves that do care. This might let us // simplify or remove the next peep loop, too. if p.As == s390x.AMOVD || p.As == s390x.AFMOVD { if regtyp(&p.To) { // Try to eliminate reg->reg moves if regtyp(&p.From) { if p.From.Type == p.To.Type { if copyprop(r) { excise(r) t++ } else if subprop(r) && copyprop(r) { excise(r) t++ } } } // Convert uses to $0 to uses of R0 and // propagate R0 if regzer(&p.From) != 0 { if p.To.Type == obj.TYPE_REG { p.From.Type = obj.TYPE_REG p.From.Reg = s390x.REGZERO if copyprop(r) { excise(r) t++ } else if subprop(r) && copyprop(r) { excise(r) t++ } } } } } } if t != 0 { goto loop1 } if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { gc.Dumpit("pass7 copyprop", g.Start, 0) } /* * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) */ var p1 *obj.Prog var r1 *gc.Flow for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { p = r.Prog switch p.As { default: continue case s390x.AMOVH, s390x.AMOVHZ, s390x.AMOVB, s390x.AMOVBZ, s390x.AMOVW, s390x.AMOVWZ: if p.To.Type != obj.TYPE_REG { continue } } r1 = r.Link if r1 == nil { continue } p1 = r1.Prog if p1.As != p.As { continue } if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { continue } if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { continue } excise(r1) } if gc.Debug['P'] > 1 { goto ret /* allow following code improvement to be suppressed */ } if gc.Debug['p'] == 0 { // load pipelining // push any load from memory as early as possible // to give it time to complete before use. for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { p = r.Prog switch p.As { case s390x.AMOVB, s390x.AMOVW, s390x.AMOVD: if regtyp(&p.To) && !regconsttyp(&p.From) { pushback(r) } } } if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { gc.Dumpit("pass8 push load as early as possible", g.Start, 0) } } /* * look for OP a, b, c; MOV c, d; -> OP a, b, d; */ for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { if (gc.Debugmergeopmv != -1) && (mergeopmv_cnt >= gc.Debugmergeopmv) { break } p = r.Prog switch p.As { case s390x.AADD, s390x.AADDC, s390x.AADDME, s390x.AADDE, s390x.AADDZE, s390x.AAND, s390x.AANDN, s390x.ADIVW, s390x.ADIVWU, s390x.ADIVD, s390x.ADIVDU, s390x.AMULLW, s390x.AMULHD, s390x.AMULHDU, s390x.AMULLD, s390x.ANAND, s390x.ANOR, s390x.AOR, s390x.AORN, s390x.AREM, s390x.AREMU, s390x.AREMD, s390x.AREMDU, s390x.ARLWMI, s390x.ARLWNM, s390x.ASLW, s390x.ASRAW, s390x.ASRW, s390x.ASLD, s390x.ASRAD, s390x.ASRD, s390x.ASUB, s390x.ASUBC, s390x.ASUBME, s390x.ASUBE, s390x.ASUBZE, s390x.AXOR: if p.To.Type != obj.TYPE_REG { continue } if p.Reg == 0 { // Only for 3 ops instruction continue } default: continue } r1 := r.Link for ; r1 != nil; r1 = r1.Link { if r1.Prog.As != obj.ANOP { break } } if r1 == nil { continue } p1 := r1.Prog switch p1.As { case s390x.AMOVD, s390x.AMOVW, s390x.AMOVWZ, s390x.AMOVH, s390x.AMOVHZ, s390x.AMOVB, s390x.AMOVBZ: if p1.To.Type != obj.TYPE_REG { continue } default: continue } if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { continue } if trymergeopmv(r1) { p.To = p1.To excise(r1) mergeopmv_cnt += 1 } } if gc.Debug['v'] != 0 { gc.Dumpit("Merge operation and move", g.Start, 0) } /* * look for CMP x, y; Branch -> Compare and branch */ if gc.Debugcnb == 0 { goto ret } for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { if (gc.Debugcnb != -1) && (cnb_cnt >= gc.Debugcnb) { break } p = r.Prog r1 = gc.Uniqs(r) if r1 == nil { continue } p1 = r1.Prog switch p.As { case s390x.ACMP: switch p1.As { case s390x.ABCL, s390x.ABC: continue case s390x.ABEQ: t = s390x.ACMPBEQ case s390x.ABGE: t = s390x.ACMPBGE case s390x.ABGT: t = s390x.ACMPBGT case s390x.ABLE: t = s390x.ACMPBLE case s390x.ABLT: t = s390x.ACMPBLT case s390x.ABNE: t = s390x.ACMPBNE default: continue } case s390x.ACMPU: switch p1.As { case s390x.ABCL, s390x.ABC: continue case s390x.ABEQ: t = s390x.ACMPUBEQ case s390x.ABGE: t = s390x.ACMPUBGE case s390x.ABGT: t = s390x.ACMPUBGT case s390x.ABLE: t = s390x.ACMPUBLE case s390x.ABLT: t = s390x.ACMPUBLT case s390x.ABNE: t = s390x.ACMPUBNE default: continue } case s390x.ACMPW, s390x.ACMPWU: continue default: continue } if gc.Debug['D'] != 0 { fmt.Printf("cnb %v; %v -> ", p, p1) } if p1.To.Sym != nil { continue } if p.To.Type == obj.TYPE_REG { p1.As = int16(t) p1.From = p.From p1.Reg = p.To.Reg p1.From3 = nil } else if p.To.Type == obj.TYPE_CONST { switch p.As { case s390x.ACMP, s390x.ACMPW: if (p.To.Offset < -(1 << 7)) || (p.To.Offset >= ((1 << 7) - 1)) { continue } case s390x.ACMPU, s390x.ACMPWU: if p.To.Offset >= (1 << 8) { continue } default: } p1.As = int16(t) p1.From = p.From p1.Reg = 0 p1.From3 = new(obj.Addr) *(p1.From3) = p.To } else { continue } if gc.Debug['D'] != 0 { fmt.Printf("%v\n", p1) } cnb_cnt += 1 excise(r) } if gc.Debug['v'] != 0 { gc.Dumpit("compare and branch", g.Start, 0) } ret: gc.Flowend(g) }