func follow(ctxt *obj.Link, s *obj.LSym) { ctxt.Cursym = s firstp := ctxt.NewProg() lastp := firstp xfol(ctxt, s.Text, &lastp) lastp.Link = nil s.Text = firstp.Link }
func addnop(ctxt *obj.Link, p *obj.Prog) { q := ctxt.NewProg() // we want to use the canonical NOP (SLL $0,R0,R0) here, // however, as the assembler will always replace $0 // as R0, we have to resort to manually encode the SLL // instruction as WORD $0. q.As = AWORD q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Name = obj.NAME_NONE q.From.Offset = 0 q.Link = p.Link p.Link = q }
func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { var q *obj.Prog var r *obj.Prog var a int var i int loop: if p == nil { return } a = int(p.As) if a == AB { q = p.Pcond if q != nil && q.As != obj.ATEXT { p.Mark |= FOLL p = q if p.Mark&FOLL == 0 { goto loop } } } if p.Mark&FOLL != 0 { i = 0 q = p for ; i < 4; i, q = i+1, q.Link { if q == *last || q == nil { break } a = int(q.As) if a == obj.ANOP { i-- continue } if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { goto copy } if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) { continue } if a != ABEQ && a != ABNE { continue } copy: for { r = ctxt.NewProg() *r = *p if r.Mark&FOLL == 0 { fmt.Printf("can't happen 1\n") } r.Mark |= FOLL if p != q { p = p.Link (*last).Link = r *last = r continue } (*last).Link = r *last = r if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { return } r.As = ABNE if a == ABNE { r.As = ABEQ } r.Pcond = p.Link r.Link = p.Pcond if r.Link.Mark&FOLL == 0 { xfol(ctxt, r.Link, last) } if r.Pcond.Mark&FOLL == 0 { fmt.Printf("can't happen 2\n") } return } } a = AB q = ctxt.NewProg() q.As = int16(a) q.Lineno = p.Lineno q.To.Type = obj.TYPE_BRANCH q.To.Offset = p.Pc q.Pcond = p p = q } p.Mark |= FOLL (*last).Link = p *last = p if a == AB || (a == obj.ARET && p.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF { return } if p.Pcond != nil { if a != ABL && a != ABX && p.Link != nil { q = obj.Brchain(ctxt, p.Link) if a != obj.ATEXT { if q != nil && (q.Mark&FOLL != 0) { p.As = int16(relinv(a)) p.Link = p.Pcond p.Pcond = q } } xfol(ctxt, p.Link, last) q = obj.Brchain(ctxt, p.Pcond) if q == nil { q = p.Pcond } if q.Mark&FOLL != 0 { p.Pcond = q return } p = q goto loop } } p = p.Link goto loop }
func softfloat(ctxt *obj.Link, cursym *obj.LSym) { if ctxt.Goarm > 5 { return } symsfloat := obj.Linklookup(ctxt, "_sfloat", 0) wasfloat := 0 for p := cursym.Text; p != nil; p = p.Link { if p.Pcond != nil { p.Pcond.Mark |= LABEL } } var next *obj.Prog for p := cursym.Text; p != nil; p = p.Link { switch p.As { case AMOVW: if isfloatreg(&p.To) || isfloatreg(&p.From) { goto soft } goto notsoft case AMOVWD, AMOVWF, AMOVDW, AMOVFW, AMOVFD, AMOVDF, AMOVF, AMOVD, ACMPF, ACMPD, AADDF, AADDD, ASUBF, ASUBD, AMULF, AMULD, ADIVF, ADIVD, ASQRTF, ASQRTD, AABSF, AABSD: goto soft default: goto notsoft } soft: if wasfloat == 0 || (p.Mark&LABEL != 0) { next = ctxt.NewProg() *next = *p // BL _sfloat(SB) *p = obj.Prog{} p.Ctxt = ctxt p.Link = next p.As = ABL p.To.Type = obj.TYPE_BRANCH p.To.Sym = symsfloat p.Lineno = next.Lineno p = next wasfloat = 1 } continue notsoft: wasfloat = 0 } }
func preprocess(ctxt *obj.Link, cursym *obj.LSym) { ctxt.Cursym = cursym if cursym.Text == nil || cursym.Text.Link == nil { return } p := cursym.Text textstksiz := p.To.Offset aoffset := int32(textstksiz) cursym.Args = p.To.Val.(int32) cursym.Locals = int32(textstksiz) /* * find leaf subroutines * strip NOPs * expand RET */ ctxt.Bso.Flush() q := (*obj.Prog)(nil) var q1 *obj.Prog for p := cursym.Text; p != nil; p = p.Link { switch p.As { case obj.ATEXT: p.Mark |= LEAF case obj.ARET: break case obj.ANOP: q1 = p.Link q.Link = q1 /* q is non-nop */ q1.Mark |= p.Mark continue case ABL, obj.ADUFFZERO, obj.ADUFFCOPY: cursym.Text.Mark &^= LEAF fallthrough case ACBNZ, ACBZ, ACBNZW, ACBZW, ATBZ, ATBNZ, AB, ABEQ, ABNE, ABCS, ABHS, ABCC, ABLO, ABMI, ABPL, ABVS, ABVC, ABHI, ABLS, ABGE, ABLT, ABGT, ABLE, AADR, /* strange */ AADRP: q1 = p.Pcond if q1 != nil { for q1.As == obj.ANOP { q1 = q1.Link p.Pcond = q1 } } break } q = p } var o int var q2 *obj.Prog var retjmp *obj.LSym for p := cursym.Text; p != nil; p = p.Link { o = int(p.As) switch o { case obj.ATEXT: cursym.Text = p if textstksiz < 0 { ctxt.Autosize = 0 } else { ctxt.Autosize = int32(textstksiz + 8) } if (cursym.Text.Mark&LEAF != 0) && ctxt.Autosize <= 8 { ctxt.Autosize = 0 } else if ctxt.Autosize&(16-1) != 0 { // The frame includes an LR. // If the frame size is 8, it's only an LR, // so there's no potential for breaking references to // local variables by growing the frame size, // because there are no local variables. // But otherwise, if there is a non-empty locals section, // the author of the code is responsible for making sure // that the frame size is 8 mod 16. if ctxt.Autosize == 8 { ctxt.Autosize += 8 cursym.Locals += 8 } else { ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, ctxt.Autosize-8) } } p.To.Offset = int64(ctxt.Autosize) - 8 if ctxt.Autosize == 0 && !(cursym.Text.Mark&LEAF != 0) { if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Text.From.Sym.Name) } ctxt.Bso.Flush() cursym.Text.Mark |= LEAF } if !(p.From3.Offset&obj.NOSPLIT != 0) { p = stacksplit(ctxt, p, ctxt.Autosize) // emit split check } aoffset = ctxt.Autosize if aoffset > 0xF0 { aoffset = 0xF0 } if cursym.Text.Mark&LEAF != 0 { cursym.Leaf = 1 if ctxt.Autosize == 0 { break } aoffset = 0 } q = p if ctxt.Autosize > aoffset { q = ctxt.NewProg() q.As = ASUB q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = int64(ctxt.Autosize) - int64(aoffset) q.To.Type = obj.TYPE_REG q.To.Reg = REGSP q.Spadj = int32(q.From.Offset) q.Link = p.Link p.Link = q if cursym.Text.Mark&LEAF != 0 { break } } q1 = ctxt.NewProg() q1.As = AMOVD q1.Lineno = p.Lineno q1.From.Type = obj.TYPE_REG q1.From.Reg = REGLINK q1.To.Type = obj.TYPE_MEM q1.Scond = C_XPRE q1.To.Offset = int64(-aoffset) q1.To.Reg = REGSP q1.Link = q.Link q1.Spadj = aoffset q.Link = q1 if cursym.Text.From3.Offset&obj.WRAPPER != 0 { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOV g_panic(g), R1 // CMP ZR, R1 // BEQ end // MOV panic_argp(R1), R2 // ADD $(autosize+8), RSP, R3 // CMP R2, R3 // BNE end // ADD $8, RSP, R4 // MOVD R4, panic_argp(R1) // end: // NOP // // The NOP is needed to give the jumps somewhere to land. // It is a liblink NOP, not a ARM64 NOP: it encodes to 0 instruction bytes. q = q1 q = obj.Appendp(ctxt, q) q.As = AMOVD q.From.Type = obj.TYPE_MEM q.From.Reg = REGG q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic q.To.Type = obj.TYPE_REG q.To.Reg = REG_R1 q = obj.Appendp(ctxt, q) q.As = ACMP q.From.Type = obj.TYPE_REG q.From.Reg = REGZERO q.Reg = REG_R1 q = obj.Appendp(ctxt, q) q.As = ABEQ q.To.Type = obj.TYPE_BRANCH q1 = q q = obj.Appendp(ctxt, q) q.As = AMOVD q.From.Type = obj.TYPE_MEM q.From.Reg = REG_R1 q.From.Offset = 0 // Panic.argp q.To.Type = obj.TYPE_REG q.To.Reg = REG_R2 q = obj.Appendp(ctxt, q) q.As = AADD q.From.Type = obj.TYPE_CONST q.From.Offset = int64(ctxt.Autosize) + 8 q.Reg = REGSP q.To.Type = obj.TYPE_REG q.To.Reg = REG_R3 q = obj.Appendp(ctxt, q) q.As = ACMP q.From.Type = obj.TYPE_REG q.From.Reg = REG_R2 q.Reg = REG_R3 q = obj.Appendp(ctxt, q) q.As = ABNE q.To.Type = obj.TYPE_BRANCH q2 = q q = obj.Appendp(ctxt, q) q.As = AADD q.From.Type = obj.TYPE_CONST q.From.Offset = 8 q.Reg = REGSP q.To.Type = obj.TYPE_REG q.To.Reg = REG_R4 q = obj.Appendp(ctxt, q) q.As = AMOVD q.From.Type = obj.TYPE_REG q.From.Reg = REG_R4 q.To.Type = obj.TYPE_MEM q.To.Reg = REG_R1 q.To.Offset = 0 // Panic.argp q = obj.Appendp(ctxt, q) q.As = obj.ANOP q1.Pcond = q q2.Pcond = q } case obj.ARET: nocache(p) if p.From.Type == obj.TYPE_CONST { ctxt.Diag("using BECOME (%v) is not supported!", p) break } retjmp = p.To.Sym p.To = obj.Addr{} if cursym.Text.Mark&LEAF != 0 { if ctxt.Autosize != 0 { p.As = AADD p.From.Type = obj.TYPE_CONST p.From.Offset = int64(ctxt.Autosize) p.To.Type = obj.TYPE_REG p.To.Reg = REGSP p.Spadj = -ctxt.Autosize } } else { /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/ aoffset = ctxt.Autosize if aoffset > 0xF0 { aoffset = 0xF0 } p.As = AMOVD p.From.Type = obj.TYPE_MEM p.Scond = C_XPOST p.From.Offset = int64(aoffset) p.From.Reg = REGSP p.To.Type = obj.TYPE_REG p.To.Reg = REGLINK p.Spadj = -aoffset if ctxt.Autosize > aoffset { q = ctxt.NewProg() q.As = AADD q.From.Type = obj.TYPE_CONST q.From.Offset = int64(ctxt.Autosize) - int64(aoffset) q.To.Type = obj.TYPE_REG q.To.Reg = REGSP q.Link = p.Link q.Spadj = int32(-q.From.Offset) q.Lineno = p.Lineno p.Link = q p = q } } if p.As != obj.ARET { q = ctxt.NewProg() q.Lineno = p.Lineno q.Link = p.Link p.Link = q p = q } if retjmp != nil { // retjmp p.As = AB p.To.Type = obj.TYPE_BRANCH p.To.Sym = retjmp p.Spadj = +ctxt.Autosize break } p.As = obj.ARET p.To.Type = obj.TYPE_MEM p.To.Offset = 0 p.To.Reg = REGLINK p.Spadj = +ctxt.Autosize case AADD, ASUB: if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { if p.As == AADD { p.Spadj = int32(-p.From.Offset) } else { p.Spadj = int32(+p.From.Offset) } } break } } }
func preprocess(ctxt *obj.Link, cursym *obj.LSym) { // TODO(minux): add morestack short-cuts with small fixed frame-size. ctxt.Cursym = cursym // a switch for enabling/disabling instruction scheduling nosched := true if cursym.Text == nil || cursym.Text.Link == nil { return } p := cursym.Text textstksiz := p.To.Offset cursym.Args = p.To.Val.(int32) cursym.Locals = int32(textstksiz) /* * find leaf subroutines * strip NOPs * expand RET * expand BECOME pseudo */ if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f noops\n", obj.Cputime()) } ctxt.Bso.Flush() var q *obj.Prog var q1 *obj.Prog for p := cursym.Text; p != nil; p = p.Link { switch p.As { /* too hard, just leave alone */ case obj.ATEXT: q = p p.Mark |= LABEL | LEAF | SYNC if p.Link != nil { p.Link.Mark |= LABEL } /* too hard, just leave alone */ case AMOVW, AMOVV: q = p if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL { p.Mark |= LABEL | SYNC break } if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL { p.Mark |= LABEL | SYNC } /* too hard, just leave alone */ case ASYSCALL, AWORD, ATLBWR, ATLBWI, ATLBP, ATLBR: q = p p.Mark |= LABEL | SYNC case ANOR: q = p if p.To.Type == obj.TYPE_REG { if p.To.Reg == REGZERO { p.Mark |= LABEL | SYNC } } case ABGEZAL, ABLTZAL, AJAL, obj.ADUFFZERO, obj.ADUFFCOPY: cursym.Text.Mark &^= LEAF fallthrough case AJMP, ABEQ, ABGEZ, ABGTZ, ABLEZ, ABLTZ, ABNE, ABFPT, ABFPF: if p.As == ABFPT || p.As == ABFPF { // We don't treat ABFPT and ABFPF as branches here, // so that we will always fill nop (0x0) in their // delay slot during assembly. // This is to workaround a kernel FPU emulator bug // where it uses the user stack to simulate the // instruction in the delay slot if it's not 0x0, // and somehow that leads to SIGSEGV when the kernel // jump to the stack. p.Mark |= SYNC } else { p.Mark |= BRANCH } q = p q1 = p.Pcond if q1 != nil { for q1.As == obj.ANOP { q1 = q1.Link p.Pcond = q1 } if q1.Mark&LEAF == 0 { q1.Mark |= LABEL } } //else { // p.Mark |= LABEL //} q1 = p.Link if q1 != nil { q1.Mark |= LABEL } continue case ARET: q = p if p.Link != nil { p.Link.Mark |= LABEL } continue case obj.ANOP: q1 = p.Link q.Link = q1 /* q is non-nop */ q1.Mark |= p.Mark continue default: q = p continue } } autosize := int32(0) var o int var p1 *obj.Prog var p2 *obj.Prog for p := cursym.Text; p != nil; p = p.Link { o = int(p.As) switch o { case obj.ATEXT: autosize = int32(textstksiz + 8) if (p.Mark&LEAF != 0) && autosize <= 8 { autosize = 0 } else if autosize&4 != 0 { autosize += 4 } p.To.Offset = int64(autosize) - 8 if p.From3.Offset&obj.NOSPLIT == 0 { p = stacksplit(ctxt, p, autosize) // emit split check } q = p if autosize != 0 { q = obj.Appendp(ctxt, p) q.As = AADDV q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = int64(-autosize) q.To.Type = obj.TYPE_REG q.To.Reg = REGSP q.Spadj = +autosize } else if cursym.Text.Mark&LEAF == 0 { if cursym.Text.From3.Offset&obj.NOSPLIT != 0 { if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name) ctxt.Bso.Flush() } cursym.Text.Mark |= LEAF } } if cursym.Text.Mark&LEAF != 0 { cursym.Leaf = 1 break } q = obj.Appendp(ctxt, q) q.As = AMOVV q.Lineno = p.Lineno q.From.Type = obj.TYPE_REG q.From.Reg = REGLINK q.To.Type = obj.TYPE_MEM q.To.Offset = int64(0) q.To.Reg = REGSP if cursym.Text.From3.Offset&obj.WRAPPER != 0 { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVV g_panic(g), R1 // BEQ R1, end // MOVV panic_argp(R1), R2 // ADDV $(autosize+8), R29, R3 // BNE R2, R3, end // ADDV $8, R29, R2 // MOVV R2, panic_argp(R1) // end: // NOP // // The NOP is needed to give the jumps somewhere to land. // It is a liblink NOP, not an mips NOP: it encodes to 0 instruction bytes. q = obj.Appendp(ctxt, q) q.As = AMOVV q.From.Type = obj.TYPE_MEM q.From.Reg = REGG q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic q.To.Type = obj.TYPE_REG q.To.Reg = REG_R1 q = obj.Appendp(ctxt, q) q.As = ABEQ q.From.Type = obj.TYPE_REG q.From.Reg = REG_R1 q.To.Type = obj.TYPE_BRANCH q.Mark |= BRANCH p1 = q q = obj.Appendp(ctxt, q) q.As = AMOVV q.From.Type = obj.TYPE_MEM q.From.Reg = REG_R1 q.From.Offset = 0 // Panic.argp q.To.Type = obj.TYPE_REG q.To.Reg = REG_R2 q = obj.Appendp(ctxt, q) q.As = AADDV q.From.Type = obj.TYPE_CONST q.From.Offset = int64(autosize) + 8 q.Reg = REGSP q.To.Type = obj.TYPE_REG q.To.Reg = REG_R3 q = obj.Appendp(ctxt, q) q.As = ABNE q.From.Type = obj.TYPE_REG q.From.Reg = REG_R2 q.Reg = REG_R3 q.To.Type = obj.TYPE_BRANCH q.Mark |= BRANCH p2 = q q = obj.Appendp(ctxt, q) q.As = AADDV q.From.Type = obj.TYPE_CONST q.From.Offset = 8 q.Reg = REGSP q.To.Type = obj.TYPE_REG q.To.Reg = REG_R2 q = obj.Appendp(ctxt, q) q.As = AMOVV q.From.Type = obj.TYPE_REG q.From.Reg = REG_R2 q.To.Type = obj.TYPE_MEM q.To.Reg = REG_R1 q.To.Offset = 0 // Panic.argp q = obj.Appendp(ctxt, q) q.As = obj.ANOP p1.Pcond = q p2.Pcond = q } case ARET: if p.From.Type == obj.TYPE_CONST { ctxt.Diag("using BECOME (%v) is not supported!", p) break } if p.To.Sym != nil { // retjmp p.As = AJMP p.To.Type = obj.TYPE_BRANCH break } if cursym.Text.Mark&LEAF != 0 { if autosize == 0 { p.As = AJMP p.From = obj.Addr{} p.To.Type = obj.TYPE_MEM p.To.Offset = 0 p.To.Reg = REGLINK p.Mark |= BRANCH break } p.As = AADDV p.From.Type = obj.TYPE_CONST p.From.Offset = int64(autosize) p.To.Type = obj.TYPE_REG p.To.Reg = REGSP p.Spadj = -autosize q = ctxt.NewProg() q.As = AJMP q.Lineno = p.Lineno q.To.Type = obj.TYPE_MEM q.To.Offset = 0 q.To.Reg = REGLINK q.Mark |= BRANCH q.Spadj = +autosize q.Link = p.Link p.Link = q break } p.As = AMOVV p.From.Type = obj.TYPE_MEM p.From.Offset = 0 p.From.Reg = REGSP p.To.Type = obj.TYPE_REG p.To.Reg = REG_R4 if false { // Debug bad returns q = ctxt.NewProg() q.As = AMOVV q.Lineno = p.Lineno q.From.Type = obj.TYPE_MEM q.From.Offset = 0 q.From.Reg = REG_R4 q.To.Type = obj.TYPE_REG q.To.Reg = REGTMP q.Link = p.Link p.Link = q p = q } if autosize != 0 { q = ctxt.NewProg() q.As = AADDV q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = int64(autosize) q.To.Type = obj.TYPE_REG q.To.Reg = REGSP q.Spadj = -autosize q.Link = p.Link p.Link = q } q1 = ctxt.NewProg() q1.As = AJMP q1.Lineno = p.Lineno q1.To.Type = obj.TYPE_MEM q1.To.Offset = 0 q1.To.Reg = REG_R4 q1.Mark |= BRANCH q1.Spadj = +autosize q1.Link = q.Link q.Link = q1 case AADDV, AADDVU: if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { p.Spadj = int32(-p.From.Offset) } } } if nosched { // if we don't do instruction scheduling, simply add // NOP after each branch instruction. for p = cursym.Text; p != nil; p = p.Link { if p.Mark&BRANCH != 0 { addnop(ctxt, p) } } return } // instruction scheduling q = nil // p - 1 q1 = cursym.Text // top of block o = 0 // count of instructions for p = cursym.Text; p != nil; p = p1 { p1 = p.Link o++ if p.Mark&NOSCHED != 0 { if q1 != p { sched(ctxt, q1, q) } for ; p != nil; p = p.Link { if p.Mark&NOSCHED == 0 { break } q = p } p1 = p q1 = p o = 0 continue } if p.Mark&(LABEL|SYNC) != 0 { if q1 != p { sched(ctxt, q1, q) } q1 = p o = 1 } if p.Mark&(BRANCH|SYNC) != 0 { sched(ctxt, q1, p) q1 = p1 o = 0 } if o >= NSCHED { sched(ctxt, q1, p) q1 = p1 o = 0 } q = p } }
func span0(ctxt *obj.Link, cursym *obj.LSym) { p := cursym.Text if p == nil || p.Link == nil { // handle external functions and ELF section symbols return } ctxt.Cursym = cursym ctxt.Autosize = int32(p.To.Offset + 8) if oprange[AOR&obj.AMask].start == nil { buildop(ctxt) } c := int64(0) p.Pc = c var m int var o *Optab for p = p.Link; p != nil; p = p.Link { ctxt.Curp = p p.Pc = c o = oplook(ctxt, p) m = int(o.size) if m == 0 { if p.As != obj.ANOP && p.As != obj.AFUNCDATA && p.As != obj.APCDATA && p.As != obj.AUSEFIELD { ctxt.Diag("zero-width instruction\n%v", p) } continue } c += int64(m) } cursym.Size = c /* * if any procedure is large enough to * generate a large SBRA branch, then * generate extra passes putting branches * around jmps to fix. this is rare. */ bflag := 1 var otxt int64 var q *obj.Prog for bflag != 0 { if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f span1\n", obj.Cputime()) } bflag = 0 c = 0 for p = cursym.Text.Link; p != nil; p = p.Link { p.Pc = c o = oplook(ctxt, p) // very large conditional branches if o.type_ == 6 && p.Pcond != nil { otxt = p.Pcond.Pc - c if otxt < -(1<<17)+10 || otxt >= (1<<17)-10 { q = ctxt.NewProg() q.Link = p.Link p.Link = q q.As = AJMP q.Lineno = p.Lineno q.To.Type = obj.TYPE_BRANCH q.Pcond = p.Pcond p.Pcond = q q = ctxt.NewProg() q.Link = p.Link p.Link = q q.As = AJMP q.Lineno = p.Lineno q.To.Type = obj.TYPE_BRANCH q.Pcond = q.Link.Link addnop(ctxt, p.Link) addnop(ctxt, p) bflag = 1 } } m = int(o.size) if m == 0 { if p.As != obj.ANOP && p.As != obj.AFUNCDATA && p.As != obj.APCDATA && p.As != obj.AUSEFIELD { ctxt.Diag("zero-width instruction\n%v", p) } continue } c += int64(m) } cursym.Size = c } c += -c & (FuncAlign - 1) cursym.Size = c /* * lay out the code, emitting code and data relocations. */ obj.Symgrow(ctxt, cursym, cursym.Size) bp := cursym.P var i int32 var out [4]uint32 for p := cursym.Text.Link; p != nil; p = p.Link { ctxt.Pc = p.Pc ctxt.Curp = p o = oplook(ctxt, p) if int(o.size) > 4*len(out) { log.Fatalf("out array in span0 is too small, need at least %d for %v", o.size/4, p) } asmout(ctxt, p, o, out[:]) for i = 0; i < int32(o.size/4); i++ { ctxt.Arch.ByteOrder.PutUint32(bp, out[i]) bp = bp[4:] } } }
func preprocess(ctxt *obj.Link, cursym *obj.LSym) { // TODO(minux): add morestack short-cuts with small fixed frame-size. ctxt.Cursym = cursym if cursym.Text == nil || cursym.Text.Link == nil { return } p := cursym.Text textstksiz := p.To.Offset if textstksiz == -8 { // Compatibility hack. p.From3.Offset |= obj.NOFRAME textstksiz = 0 } if textstksiz%8 != 0 { ctxt.Diag("frame size %d not a multiple of 8", textstksiz) } if p.From3.Offset&obj.NOFRAME != 0 { if textstksiz != 0 { ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz) } } cursym.Args = p.To.Val.(int32) cursym.Locals = int32(textstksiz) /* * find leaf subroutines * strip NOPs * expand RET * expand BECOME pseudo */ if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "%5.2f noops\n", obj.Cputime()) } ctxt.Bso.Flush() var q *obj.Prog var q1 *obj.Prog for p := cursym.Text; p != nil; p = p.Link { switch p.As { /* too hard, just leave alone */ case obj.ATEXT: q = p p.Mark |= LABEL | LEAF | SYNC if p.Link != nil { p.Link.Mark |= LABEL } case ANOR: q = p if p.To.Type == obj.TYPE_REG { if p.To.Reg == REGZERO { p.Mark |= LABEL | SYNC } } case ALWAR, ASTWCCC, AECIWX, AECOWX, AEIEIO, AICBI, AISYNC, ATLBIE, ATLBIEL, ASLBIA, ASLBIE, ASLBMFEE, ASLBMFEV, ASLBMTE, ADCBF, ADCBI, ADCBST, ADCBT, ADCBTST, ADCBZ, ASYNC, ATLBSYNC, APTESYNC, ATW, AWORD, ARFI, ARFCI, ARFID, AHRFID: q = p p.Mark |= LABEL | SYNC continue case AMOVW, AMOVWZ, AMOVD: q = p if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL { p.Mark |= LABEL | SYNC } continue case AFABS, AFABSCC, AFADD, AFADDCC, AFCTIW, AFCTIWCC, AFCTIWZ, AFCTIWZCC, AFDIV, AFDIVCC, AFMADD, AFMADDCC, AFMOVD, AFMOVDU, /* case AFMOVDS: */ AFMOVS, AFMOVSU, /* case AFMOVSD: */ AFMSUB, AFMSUBCC, AFMUL, AFMULCC, AFNABS, AFNABSCC, AFNEG, AFNEGCC, AFNMADD, AFNMADDCC, AFNMSUB, AFNMSUBCC, AFRSP, AFRSPCC, AFSUB, AFSUBCC: q = p p.Mark |= FLOAT continue case ABL, ABCL, obj.ADUFFZERO, obj.ADUFFCOPY: cursym.Text.Mark &^= LEAF fallthrough case ABC, ABEQ, ABGE, ABGT, ABLE, ABLT, ABNE, ABR, ABVC, ABVS: p.Mark |= BRANCH q = p q1 = p.Pcond if q1 != nil { for q1.As == obj.ANOP { q1 = q1.Link p.Pcond = q1 } if q1.Mark&LEAF == 0 { q1.Mark |= LABEL } } else { p.Mark |= LABEL } q1 = p.Link if q1 != nil { q1.Mark |= LABEL } continue case AFCMPO, AFCMPU: q = p p.Mark |= FCMP | FLOAT continue case obj.ARET: q = p if p.Link != nil { p.Link.Mark |= LABEL } continue case obj.ANOP: q1 = p.Link q.Link = q1 /* q is non-nop */ q1.Mark |= p.Mark continue default: q = p continue } } autosize := int32(0) var aoffset int var mov int var o int var p1 *obj.Prog var p2 *obj.Prog for p := cursym.Text; p != nil; p = p.Link { o = int(p.As) switch o { case obj.ATEXT: mov = AMOVD aoffset = 0 autosize = int32(textstksiz) if p.Mark&LEAF != 0 && autosize == 0 && p.From3.Offset&obj.NOFRAME == 0 { // A leaf function with no locals has no frame. p.From3.Offset |= obj.NOFRAME } if p.From3.Offset&obj.NOFRAME == 0 { // If there is a stack frame at all, it includes // space to save the LR. autosize += int32(ctxt.FixedFrameSize()) } p.To.Offset = int64(autosize) q = p if ctxt.Flag_shared != 0 && cursym.Name != "runtime.duffzero" && cursym.Name != "runtime.duffcopy" && cursym.Name != "runtime.stackBarrier" { // When compiling Go into PIC, all functions must start // with instructions to load the TOC pointer into r2: // // addis r2, r12, .TOC.-func@ha // addi r2, r2, .TOC.-func@l+4 // // We could probably skip this prologue in some situations // but it's a bit subtle. However, it is both safe and // necessary to leave the prologue off duffzero and // duffcopy as we rely on being able to jump to a specific // instruction offset for them, and stackBarrier is only // ever called from an overwritten LR-save slot on the // stack (when r12 will not be remotely the right thing) // but fortunately does not access global data. // // These are AWORDS because there is no (afaict) way to // generate the addis instruction except as part of the // load of a large constant, and in that case there is no // way to use r12 as the source. q = obj.Appendp(ctxt, q) q.As = AWORD q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = 0x3c4c0000 q = obj.Appendp(ctxt, q) q.As = AWORD q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = 0x38420000 rel := obj.Addrel(ctxt.Cursym) rel.Off = 0 rel.Siz = 8 rel.Sym = obj.Linklookup(ctxt, ".TOC.", 0) rel.Type = obj.R_ADDRPOWER_PCREL } if cursym.Text.From3.Offset&obj.NOSPLIT == 0 { q = stacksplit(ctxt, q, autosize) // emit split check } if autosize != 0 { /* use MOVDU to adjust R1 when saving R31, if autosize is small */ if cursym.Text.Mark&LEAF == 0 && autosize >= -BIG && autosize <= BIG { mov = AMOVDU aoffset = int(-autosize) } else { q = obj.Appendp(ctxt, q) q.As = AADD q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = int64(-autosize) q.To.Type = obj.TYPE_REG q.To.Reg = REGSP q.Spadj = +autosize } } else if cursym.Text.Mark&LEAF == 0 { // A very few functions that do not return to their caller // (e.g. gogo) are not identified as leaves but still have // no frame. cursym.Text.Mark |= LEAF } if cursym.Text.Mark&LEAF != 0 { cursym.Leaf = 1 break } q = obj.Appendp(ctxt, q) q.As = AMOVD q.Lineno = p.Lineno q.From.Type = obj.TYPE_REG q.From.Reg = REG_LR q.To.Type = obj.TYPE_REG q.To.Reg = REGTMP q = obj.Appendp(ctxt, q) q.As = int16(mov) q.Lineno = p.Lineno q.From.Type = obj.TYPE_REG q.From.Reg = REGTMP q.To.Type = obj.TYPE_MEM q.To.Offset = int64(aoffset) q.To.Reg = REGSP if q.As == AMOVDU { q.Spadj = int32(-aoffset) } if ctxt.Flag_shared != 0 { q = obj.Appendp(ctxt, q) q.As = AMOVD q.Lineno = p.Lineno q.From.Type = obj.TYPE_REG q.From.Reg = REG_R2 q.To.Type = obj.TYPE_MEM q.To.Reg = REGSP q.To.Offset = 24 } if cursym.Text.From3.Offset&obj.WRAPPER != 0 { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVD g_panic(g), R3 // CMP R0, R3 // BEQ end // MOVD panic_argp(R3), R4 // ADD $(autosize+8), R1, R5 // CMP R4, R5 // BNE end // ADD $8, R1, R6 // MOVD R6, panic_argp(R3) // end: // NOP // // The NOP is needed to give the jumps somewhere to land. // It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes. q = obj.Appendp(ctxt, q) q.As = AMOVD q.From.Type = obj.TYPE_MEM q.From.Reg = REGG q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic q.To.Type = obj.TYPE_REG q.To.Reg = REG_R3 q = obj.Appendp(ctxt, q) q.As = ACMP q.From.Type = obj.TYPE_REG q.From.Reg = REG_R0 q.To.Type = obj.TYPE_REG q.To.Reg = REG_R3 q = obj.Appendp(ctxt, q) q.As = ABEQ q.To.Type = obj.TYPE_BRANCH p1 = q q = obj.Appendp(ctxt, q) q.As = AMOVD q.From.Type = obj.TYPE_MEM q.From.Reg = REG_R3 q.From.Offset = 0 // Panic.argp q.To.Type = obj.TYPE_REG q.To.Reg = REG_R4 q = obj.Appendp(ctxt, q) q.As = AADD q.From.Type = obj.TYPE_CONST q.From.Offset = int64(autosize) + ctxt.FixedFrameSize() q.Reg = REGSP q.To.Type = obj.TYPE_REG q.To.Reg = REG_R5 q = obj.Appendp(ctxt, q) q.As = ACMP q.From.Type = obj.TYPE_REG q.From.Reg = REG_R4 q.To.Type = obj.TYPE_REG q.To.Reg = REG_R5 q = obj.Appendp(ctxt, q) q.As = ABNE q.To.Type = obj.TYPE_BRANCH p2 = q q = obj.Appendp(ctxt, q) q.As = AADD q.From.Type = obj.TYPE_CONST q.From.Offset = ctxt.FixedFrameSize() q.Reg = REGSP q.To.Type = obj.TYPE_REG q.To.Reg = REG_R6 q = obj.Appendp(ctxt, q) q.As = AMOVD q.From.Type = obj.TYPE_REG q.From.Reg = REG_R6 q.To.Type = obj.TYPE_MEM q.To.Reg = REG_R3 q.To.Offset = 0 // Panic.argp q = obj.Appendp(ctxt, q) q.As = obj.ANOP p1.Pcond = q p2.Pcond = q } case obj.ARET: if p.From.Type == obj.TYPE_CONST { ctxt.Diag("using BECOME (%v) is not supported!", p) break } retTarget := p.To.Sym if cursym.Text.Mark&LEAF != 0 { if autosize == 0 { p.As = ABR p.From = obj.Addr{} if retTarget == nil { p.To.Type = obj.TYPE_REG p.To.Reg = REG_LR } else { p.To.Type = obj.TYPE_BRANCH p.To.Sym = retTarget } p.Mark |= BRANCH break } p.As = AADD p.From.Type = obj.TYPE_CONST p.From.Offset = int64(autosize) p.To.Type = obj.TYPE_REG p.To.Reg = REGSP p.Spadj = -autosize q = ctxt.NewProg() q.As = ABR q.Lineno = p.Lineno q.To.Type = obj.TYPE_REG q.To.Reg = REG_LR q.Mark |= BRANCH q.Spadj = +autosize q.Link = p.Link p.Link = q break } p.As = AMOVD p.From.Type = obj.TYPE_MEM p.From.Offset = 0 p.From.Reg = REGSP p.To.Type = obj.TYPE_REG p.To.Reg = REGTMP q = ctxt.NewProg() q.As = AMOVD q.Lineno = p.Lineno q.From.Type = obj.TYPE_REG q.From.Reg = REGTMP q.To.Type = obj.TYPE_REG q.To.Reg = REG_LR q.Link = p.Link p.Link = q p = q if false { // Debug bad returns q = ctxt.NewProg() q.As = AMOVD q.Lineno = p.Lineno q.From.Type = obj.TYPE_MEM q.From.Offset = 0 q.From.Reg = REGTMP q.To.Type = obj.TYPE_REG q.To.Reg = REGTMP q.Link = p.Link p.Link = q p = q } if autosize != 0 { q = ctxt.NewProg() q.As = AADD q.Lineno = p.Lineno q.From.Type = obj.TYPE_CONST q.From.Offset = int64(autosize) q.To.Type = obj.TYPE_REG q.To.Reg = REGSP q.Spadj = -autosize q.Link = p.Link p.Link = q } q1 = ctxt.NewProg() q1.As = ABR q1.Lineno = p.Lineno if retTarget == nil { q1.To.Type = obj.TYPE_REG q1.To.Reg = REG_LR } else { q1.To.Type = obj.TYPE_BRANCH q1.To.Sym = retTarget } q1.Mark |= BRANCH q1.Spadj = +autosize q1.Link = q.Link q.Link = q1 case AADD: if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { p.Spadj = int32(-p.From.Offset) } } } }
func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { var q *obj.Prog var r *obj.Prog var a int var b int var i int loop: if p == nil { return } a = int(p.As) if a == ABR { q = p.Pcond if (p.Mark&NOSCHED != 0) || q != nil && (q.Mark&NOSCHED != 0) { p.Mark |= FOLL (*last).Link = p *last = p p = p.Link xfol(ctxt, p, last) p = q if p != nil && p.Mark&FOLL == 0 { goto loop } return } if q != nil { p.Mark |= FOLL p = q if p.Mark&FOLL == 0 { goto loop } } } if p.Mark&FOLL != 0 { i = 0 q = p for ; i < 4; i, q = i+1, q.Link { if q == *last || (q.Mark&NOSCHED != 0) { break } b = 0 /* set */ a = int(q.As) if a == obj.ANOP { i-- continue } if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID { goto copy } if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) { continue } b = relinv(a) if b == 0 { continue } copy: for { r = ctxt.NewProg() *r = *p if r.Mark&FOLL == 0 { fmt.Printf("cant happen 1\n") } r.Mark |= FOLL if p != q { p = p.Link (*last).Link = r *last = r continue } (*last).Link = r *last = r if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID { return } r.As = int16(b) r.Pcond = p.Link r.Link = p.Pcond if r.Link.Mark&FOLL == 0 { xfol(ctxt, r.Link, last) } if r.Pcond.Mark&FOLL == 0 { fmt.Printf("cant happen 2\n") } return } } a = ABR q = ctxt.NewProg() q.As = int16(a) q.Lineno = p.Lineno q.To.Type = obj.TYPE_BRANCH q.To.Offset = p.Pc q.Pcond = p p = q } p.Mark |= FOLL (*last).Link = p *last = p if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID { if p.Mark&NOSCHED != 0 { p = p.Link goto loop } return } if p.Pcond != nil { if a != ABL && p.Link != nil { xfol(ctxt, p.Link, last) p = p.Pcond if p == nil || (p.Mark&FOLL != 0) { return } goto loop } } p = p.Link goto loop }