func follow(ctxt *liblink.Link, s *liblink.LSym) { var firstp *liblink.Prog var lastp *liblink.Prog ctxt.Cursym = s firstp = ctxt.Prg() lastp = firstp xfol(ctxt, s.Text, &lastp) lastp.Link = nil s.Text = firstp.Link }
func progedit(ctxt *liblink.Link, p *liblink.Prog) { var literal string var s *liblink.LSym var q *liblink.Prog // Thread-local storage references use the TLS pseudo-register. // As a register, TLS refers to the thread-local storage base, and it // can only be loaded into another register: // // MOVQ TLS, AX // // An offset from the thread-local storage base is written off(reg)(TLS*1). // Semantically it is off(reg), but the (TLS*1) annotation marks this as // indexing from the loaded TLS base. This emits a relocation so that // if the linker needs to adjust the offset, it can. For example: // // MOVQ TLS, AX // MOVQ 8(AX)(TLS*1), CX // load m into CX // // On systems that support direct access to the TLS memory, this // pair of instructions can be reduced to a direct TLS memory reference: // // MOVQ 8(TLS), CX // load m into CX // // The 2-instruction and 1-instruction forms correspond roughly to // ELF TLS initial exec mode and ELF TLS local exec mode, respectively. // // We applies this rewrite on systems that support the 1-instruction form. // The decision is made using only the operating system (and probably // the -shared flag, eventually), not the link mode. If some link modes // on a particular operating system require the 2-instruction form, // then all builds for that operating system will use the 2-instruction // form, so that the link mode decision can be delayed to link time. // // In this way, all supported systems use identical instructions to // access TLS, and they are rewritten appropriately first here in // liblink and then finally using relocations in the linker. if canuselocaltls(ctxt) { // Reduce TLS initial exec model to TLS local exec model. // Sequences like // MOVQ TLS, BX // ... off(BX)(TLS*1) ... // become // NOP // ... off(TLS) ... // // TODO(rsc): Remove the Hsolaris special case. It exists only to // guarantee we are producing byte-identical binaries as before this code. // But it should be unnecessary. if (p.As == AMOVQ || p.As == AMOVL) && p.From.Typ == D_TLS && D_AX <= p.To.Typ && p.To.Typ <= D_R15 && ctxt.Headtype != liblink.Hsolaris { nopout(p) } if p.From.Index == D_TLS && D_INDIR+D_AX <= p.From.Typ && p.From.Typ <= D_INDIR+D_R15 { p.From.Typ = D_INDIR + D_TLS p.From.Scale = 0 p.From.Index = D_NONE } if p.To.Index == D_TLS && D_INDIR+D_AX <= p.To.Typ && p.To.Typ <= D_INDIR+D_R15 { p.To.Typ = D_INDIR + D_TLS p.To.Scale = 0 p.To.Index = D_NONE } } else { // As a courtesy to the C compilers, rewrite TLS local exec load as TLS initial exec load. // The instruction // MOVQ off(TLS), BX // becomes the sequence // MOVQ TLS, BX // MOVQ off(BX)(TLS*1), BX // This allows the C compilers to emit references to m and g using the direct off(TLS) form. if (p.As == AMOVQ || p.As == AMOVL) && p.From.Typ == D_INDIR+D_TLS && D_AX <= p.To.Typ && p.To.Typ <= D_R15 { q = liblink.Appendp(ctxt, p) q.As = p.As q.From = p.From q.From.Typ = D_INDIR + p.To.Typ q.From.Index = D_TLS q.From.Scale = 2 // TODO: use 1 q.To = p.To p.From.Typ = D_TLS p.From.Index = D_NONE p.From.Offset = 0 } } // TODO: Remove. if ctxt.Headtype == liblink.Hwindows || ctxt.Headtype == liblink.Hplan9 { if p.From.Scale == 1 && p.From.Index == D_TLS { p.From.Scale = 2 } if p.To.Scale == 1 && p.To.Index == D_TLS { p.To.Scale = 2 } } if ctxt.Headtype == liblink.Hnacl { nacladdr(ctxt, p, &p.From) nacladdr(ctxt, p, &p.To) } // Maintain information about code generation mode. if ctxt.Mode == 0 { ctxt.Mode = 64 } p.Mode = ctxt.Mode switch p.As { case AMODE: if p.From.Typ == D_CONST || p.From.Typ == D_INDIR+D_NONE { switch int(p.From.Offset) { case 16, 32, 64: ctxt.Mode = int(p.From.Offset) break } } nopout(p) break } // Rewrite CALL/JMP/RET to symbol as D_BRANCH. switch p.As { case ACALL, AJMP, ARET: if (p.To.Typ == D_EXTERN || p.To.Typ == D_STATIC) && p.To.Sym != nil { p.To.Typ = D_BRANCH } break } // Rewrite float constants to values stored in memory. switch p.As { case AFMOVF, AFADDF, AFSUBF, AFSUBRF, AFMULF, AFDIVF, AFDIVRF, AFCOMF, AFCOMFP, AMOVSS, AADDSS, ASUBSS, AMULSS, ADIVSS, ACOMISS, AUCOMISS: if p.From.Typ == D_FCONST { var i32 uint32 var f32 float32 f32 = float32(p.From.U.Dval) i32 = math.Float32bits(f32) literal = fmt.Sprintf("$f32.%08x", uint32(i32)) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint32(ctxt, s, i32) s.Reachable = 0 } p.From.Typ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } case AFMOVD, AFADDD, AFSUBD, AFSUBRD, AFMULD, AFDIVD, AFDIVRD, AFCOMD, AFCOMDP, AMOVSD, AADDSD, ASUBSD, AMULSD, ADIVSD, ACOMISD, AUCOMISD: if p.From.Typ == D_FCONST { var i64 uint64 i64 = math.Float64bits(p.From.U.Dval) literal = fmt.Sprintf("$f64.%016x", uint64(i64)) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint64(ctxt, s, i64) s.Reachable = 0 } p.From.Typ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } break } }
func readsym(b *bufio.Reader, s *liblink.LSym) { if !undef[s] { panic("double-def") } delete(undef, s) s.Name = rdstring(b) s.Extname = rdstring(b) s.Typ = int(rdint(b)) s.Version = uint32(rdint(b)) s.Dupok = int(rdint(b)) s.External = uint8(rdint(b)) s.Nosplit = uint8(rdint(b)) s.Reachable = uint8(rdint(b)) s.Cgoexport = uint8(rdint(b)) s.Special = uint8(rdint(b)) s.Stkcheck = uint8(rdint(b)) s.Hide = uint8(rdint(b)) s.Leaf = uint8(rdint(b)) s.Fnptr = uint8(rdint(b)) s.Seenglobl = uint8(rdint(b)) s.Onlist = uint8(rdint(b)) s.Symid = int16(rdint(b)) s.Dynid = int(rdint(b)) s.Sig = int(rdint(b)) s.Plt = int(rdint(b)) s.Got = int(rdint(b)) s.Align = int(rdint(b)) s.Elfsym = int(rdint(b)) s.Args = int(rdint(b)) s.Locals = rdint(b) s.Value = rdint(b) s.Size = rdint(b) s.Hash = rdsym(b) s.Allsym = rdsym(b) s.Next = rdsym(b) s.Sub = rdsym(b) s.Outer = rdsym(b) s.Gotype = rdsym(b) s.Reachparent = rdsym(b) s.Queue = rdsym(b) s.File = rdstring(b) s.Dynimplib = rdstring(b) s.Dynimpvers = rdstring(b) s.Text = rdprog(b) s.Etext = rdprog(b) n := int(rdint(b)) if n > 0 { s.P = make([]byte, n) io.ReadFull(b, s.P) } s.R = make([]liblink.Reloc, int(rdint(b))) for i := range s.R { r := &s.R[i] r.Off = rdint(b) r.Siz = uint8(rdint(b)) r.Done = uint8(rdint(b)) r.Typ = int(rdint(b)) r.Add = rdint(b) r.Xadd = rdint(b) r.Sym = rdsym(b) r.Xsym = rdsym(b) } }
func progedit(ctxt *liblink.Link, p *liblink.Prog) { var literal string var s *liblink.LSym var q *liblink.Prog // See obj6.c for discussion of TLS. if canuselocaltls(ctxt) { // Reduce TLS initial exec model to TLS local exec model. // Sequences like // MOVL TLS, BX // ... off(BX)(TLS*1) ... // become // NOP // ... off(TLS) ... if p.As == AMOVL && p.From.Typ == D_TLS && D_AX <= p.To.Typ && p.To.Typ <= D_DI { p.As = ANOP p.From.Typ = D_NONE p.To.Typ = D_NONE } if p.From.Index == D_TLS && D_INDIR+D_AX <= p.From.Typ && p.From.Typ <= D_INDIR+D_DI { p.From.Typ = D_INDIR + D_TLS p.From.Scale = 0 p.From.Index = D_NONE } if p.To.Index == D_TLS && D_INDIR+D_AX <= p.To.Typ && p.To.Typ <= D_INDIR+D_DI { p.To.Typ = D_INDIR + D_TLS p.To.Scale = 0 p.To.Index = D_NONE } } else { // As a courtesy to the C compilers, rewrite TLS local exec load as TLS initial exec load. // The instruction // MOVL off(TLS), BX // becomes the sequence // MOVL TLS, BX // MOVL off(BX)(TLS*1), BX // This allows the C compilers to emit references to m and g using the direct off(TLS) form. if p.As == AMOVL && p.From.Typ == D_INDIR+D_TLS && D_AX <= p.To.Typ && p.To.Typ <= D_DI { q = liblink.Appendp(ctxt, p) q.As = p.As q.From = p.From q.From.Typ = D_INDIR + p.To.Typ q.From.Index = D_TLS q.From.Scale = 2 // TODO: use 1 q.To = p.To p.From.Typ = D_TLS p.From.Index = D_NONE p.From.Offset = 0 } } // TODO: Remove. if ctxt.Headtype == liblink.Hplan9 { if p.From.Scale == 1 && p.From.Index == D_TLS { p.From.Scale = 2 } if p.To.Scale == 1 && p.To.Index == D_TLS { p.To.Scale = 2 } } // Rewrite CALL/JMP/RET to symbol as D_BRANCH. switch p.As { case ACALL, AJMP, ARET: if (p.To.Typ == D_EXTERN || p.To.Typ == D_STATIC) && p.To.Sym != nil { p.To.Typ = D_BRANCH } break } // Rewrite float constants to values stored in memory. switch p.As { case AFMOVF, AFADDF, AFSUBF, AFSUBRF, AFMULF, AFDIVF, AFDIVRF, AFCOMF, AFCOMFP, AMOVSS, AADDSS, ASUBSS, AMULSS, ADIVSS, ACOMISS, AUCOMISS: if p.From.Typ == D_FCONST { var i32 uint32 var f32 float32 f32 = float32(p.From.U.Dval) i32 = math.Float32bits(f32) literal = fmt.Sprintf("$f32.%08x", uint32(i32)) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint32(ctxt, s, i32) s.Reachable = 0 } p.From.Typ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } case AFMOVD, AFADDD, AFSUBD, AFSUBRD, AFMULD, AFDIVD, AFDIVRD, AFCOMD, AFCOMDP, AMOVSD, AADDSD, ASUBSD, AMULSD, ADIVSD, ACOMISD, AUCOMISD: if p.From.Typ == D_FCONST { var i64 uint64 i64 = math.Float64bits(p.From.U.Dval) literal = fmt.Sprintf("$f64.%016x", uint64(i64)) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint64(ctxt, s, i64) s.Reachable = 0 } p.From.Typ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } break } }
func addstacksplit(ctxt *liblink.Link, cursym *liblink.LSym) { var p *liblink.Prog var q *liblink.Prog var autoffset int64 var deltasp int64 var a int if ctxt.Symmorestack[0] == nil { ctxt.Symmorestack[0] = liblink.Linklookup(ctxt, "runtime.morestack", 0) ctxt.Symmorestack[1] = liblink.Linklookup(ctxt, "runtime.morestack_noctxt", 0) } if ctxt.Headtype == liblink.Hplan9 && ctxt.Plan9privates == nil { ctxt.Plan9privates = liblink.Linklookup(ctxt, "_privates", 0) } ctxt.Cursym = cursym if cursym.Text == nil || cursym.Text.Link == nil { return } p = cursym.Text autoffset = p.To.Offset if autoffset < 0 { autoffset = 0 } cursym.Locals = autoffset cursym.Args = p.To.Offset2 q = nil if p.From.Scale&liblink.NOSPLIT == 0 || (p.From.Scale&liblink.WRAPPER != 0) { p = liblink.Appendp(ctxt, p) p = load_g_cx(ctxt, p) // load g into CX } if cursym.Text.From.Scale&liblink.NOSPLIT == 0 { p = stacksplit(ctxt, p, autoffset, bool2int(cursym.Text.From.Scale&liblink.NEEDCTXT == 0), &q) // emit split check } if autoffset != 0 { p = liblink.Appendp(ctxt, p) p.As = AADJSP p.From.Typ = D_CONST p.From.Offset = autoffset p.Spadj = autoffset } else { // zero-byte stack adjustment. // Insert a fake non-zero adjustment so that stkcheck can // recognize the end of the stack-splitting prolog. p = liblink.Appendp(ctxt, p) p.As = ANOP p.Spadj = -ctxt.Arch.Ptrsize p = liblink.Appendp(ctxt, p) p.As = ANOP p.Spadj = ctxt.Arch.Ptrsize } if q != nil { q.Pcond = p } deltasp = autoffset if cursym.Text.From.Scale&liblink.WRAPPER != 0 { // g->panicwrap += autoffset + ctxt->arch->ptrsize; p = liblink.Appendp(ctxt, p) p.As = AADDL p.From.Typ = D_CONST p.From.Offset = autoffset + ctxt.Arch.Ptrsize p.To.Typ = D_INDIR + D_CX p.To.Offset = 2 * ctxt.Arch.Ptrsize } if ctxt.Debugzerostack != 0 && autoffset != 0 && cursym.Text.From.Scale&liblink.NOSPLIT == 0 { // 8l -Z means zero the stack frame on entry. // This slows down function calls but can help avoid // false positives in garbage collection. p = liblink.Appendp(ctxt, p) p.As = AMOVL p.From.Typ = D_SP p.To.Typ = D_DI p = liblink.Appendp(ctxt, p) p.As = AMOVL p.From.Typ = D_CONST p.From.Offset = autoffset / 4 p.To.Typ = D_CX p = liblink.Appendp(ctxt, p) p.As = AMOVL p.From.Typ = D_CONST p.From.Offset = 0 p.To.Typ = D_AX p = liblink.Appendp(ctxt, p) p.As = AREP p = liblink.Appendp(ctxt, p) p.As = ASTOSL } for ; p != nil; p = p.Link { a = p.From.Typ if a == D_AUTO { p.From.Offset += deltasp } if a == D_PARAM { p.From.Offset += deltasp + 4 } a = p.To.Typ if a == D_AUTO { p.To.Offset += deltasp } if a == D_PARAM { p.To.Offset += deltasp + 4 } switch p.As { default: continue case APUSHL, APUSHFL: deltasp += 4 p.Spadj = 4 continue case APUSHW, APUSHFW: deltasp += 2 p.Spadj = 2 continue case APOPL, APOPFL: deltasp -= 4 p.Spadj = -4 continue case APOPW, APOPFW: deltasp -= 2 p.Spadj = -2 continue case ARET: break } if autoffset != deltasp { ctxt.Diag("unbalanced PUSH/POP") } if cursym.Text.From.Scale&liblink.WRAPPER != 0 { p = load_g_cx(ctxt, p) p = liblink.Appendp(ctxt, p) // g->panicwrap -= autoffset + ctxt->arch->ptrsize; p.As = ASUBL p.From.Typ = D_CONST p.From.Offset = autoffset + ctxt.Arch.Ptrsize p.To.Typ = D_INDIR + D_CX p.To.Offset = 2 * ctxt.Arch.Ptrsize p = liblink.Appendp(ctxt, p) p.As = ARET } if autoffset != 0 { p.As = AADJSP p.From.Typ = D_CONST p.From.Offset = -autoffset p.Spadj = -autoffset p = liblink.Appendp(ctxt, p) p.As = ARET // If there are instructions following // this ARET, they come from a branch // with the same stackframe, so undo // the cleanup. p.Spadj = +autoffset } if p.To.Sym != nil { // retjmp p.As = AJMP } } }
func progedit(ctxt *liblink.Link, p *liblink.Prog) { var literal string var s *liblink.LSym var tlsfallback *liblink.LSym p.From.Class = 0 p.To.Class = 0 // Rewrite B/BL to symbol as D_BRANCH. switch p.As { case AB, ABL, ADUFFZERO, ADUFFCOPY: if p.To.Typ == D_OREG && (p.To.Name == D_EXTERN || p.To.Name == D_STATIC) && p.To.Sym != nil { p.To.Typ = D_BRANCH } break } // Replace TLS register fetches on older ARM procesors. switch p.As { // If the instruction matches MRC 15, 0, <reg>, C13, C0, 3, replace it. case AMRC: if ctxt.Goarm < 7 && p.To.Offset&0xffff0fff == 0xee1d0f70 { tlsfallback = liblink.Linklookup(ctxt, "runtime.read_tls_fallback", 0) // BL runtime.read_tls_fallback(SB) p.As = ABL p.To.Typ = D_BRANCH p.To.Sym = tlsfallback p.To.Offset = 0 } else { // Otherwise, MRC/MCR instructions need no further treatment. p.As = AWORD } break } // Rewrite float constants to values stored in memory. switch p.As { case AMOVF: if p.From.Typ == D_FCONST && chipfloat5(ctxt, p.From.U.Dval) < 0 && (chipzero5(ctxt, p.From.U.Dval) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { var i32 uint32 var f32 float32 f32 = float32(p.From.U.Dval) i32 = math.Float32bits(f32) literal = fmt.Sprintf("$f32.%08x", i32) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint32(ctxt, s, i32) s.Reachable = 0 } p.From.Typ = D_OREG p.From.Sym = s p.From.Name = D_EXTERN p.From.Offset = 0 } case AMOVD: if p.From.Typ == D_FCONST && chipfloat5(ctxt, p.From.U.Dval) < 0 && (chipzero5(ctxt, p.From.U.Dval) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { var i64 uint64 i64 = math.Float64bits(p.From.U.Dval) literal = fmt.Sprintf("$f64.%016x", uint64(i64)) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint64(ctxt, s, i64) s.Reachable = 0 } p.From.Typ = D_OREG p.From.Sym = s p.From.Name = D_EXTERN p.From.Offset = 0 } break } if ctxt.Flag_shared != 0 { // Shared libraries use R_ARM_TLS_IE32 instead of // R_ARM_TLS_LE32, replacing the link time constant TLS offset in // runtime.tlsg with an address to a GOT entry containing the // offset. Rewrite $runtime.tlsg(SB) to runtime.tlsg(SB) to // compensate. if ctxt.Tlsg == nil { ctxt.Tlsg = liblink.Linklookup(ctxt, "runtime.tlsg", 0) } if p.From.Typ == D_CONST && p.From.Name == D_EXTERN && p.From.Sym == ctxt.Tlsg { p.From.Typ = D_OREG } if p.To.Typ == D_CONST && p.To.Name == D_EXTERN && p.To.Sym == ctxt.Tlsg { p.To.Typ = D_OREG } } }
func addstacksplit(ctxt *liblink.Link, cursym *liblink.LSym) { var p *liblink.Prog var pl *liblink.Prog var q *liblink.Prog var q1 *liblink.Prog var q2 *liblink.Prog var o int var autosize int64 var autoffset int64 autosize = 0 if ctxt.Symmorestack[0] == nil { ctxt.Symmorestack[0] = liblink.Linklookup(ctxt, "runtime.morestack", 0) ctxt.Symmorestack[1] = liblink.Linklookup(ctxt, "runtime.morestack_noctxt", 0) } q = nil ctxt.Cursym = cursym if cursym.Text == nil || cursym.Text.Link == nil { return } softfloat(ctxt, cursym) p = cursym.Text autoffset = p.To.Offset if autoffset < 0 { autoffset = 0 } cursym.Locals = autoffset cursym.Args = p.To.Offset2 if ctxt.Debugzerostack != 0 { if autoffset != 0 && p.Reg&liblink.NOSPLIT == 0 { // MOVW $4(R13), R1 p = liblink.Appendp(ctxt, p) p.As = AMOVW p.From.Typ = D_CONST p.From.Reg = 13 p.From.Offset = 4 p.To.Typ = D_REG p.To.Reg = 1 // MOVW $n(R13), R2 p = liblink.Appendp(ctxt, p) p.As = AMOVW p.From.Typ = D_CONST p.From.Reg = 13 p.From.Offset = 4 + autoffset p.To.Typ = D_REG p.To.Reg = 2 // MOVW $0, R3 p = liblink.Appendp(ctxt, p) p.As = AMOVW p.From.Typ = D_CONST p.From.Offset = 0 p.To.Typ = D_REG p.To.Reg = 3 // L: // MOVW.nil R3, 0(R1) +4 // CMP R1, R2 // BNE L pl = liblink.Appendp(ctxt, p) p = pl p.As = AMOVW p.From.Typ = D_REG p.From.Reg = 3 p.To.Typ = D_OREG p.To.Reg = 1 p.To.Offset = 4 p.Scond |= C_PBIT p = liblink.Appendp(ctxt, p) p.As = ACMP p.From.Typ = D_REG p.From.Reg = 1 p.Reg = 2 p = liblink.Appendp(ctxt, p) p.As = ABNE p.To.Typ = D_BRANCH p.Pcond = pl } } /* * find leaf subroutines * strip NOPs * expand RET * expand BECOME pseudo */ for p = cursym.Text; p != nil; p = p.Link { switch p.As { case ACASE: if ctxt.Flag_shared != 0 { linkcase(p) } case ATEXT: p.Mark |= LEAF case ARET: break case ADIV, ADIVU, AMOD, AMODU: q = p if ctxt.Sym_div == nil { initdiv(ctxt) } cursym.Text.Mark &^= LEAF continue case ANOP: q1 = p.Link q.Link = q1 /* q is non-nop */ if q1 != nil { q1.Mark |= p.Mark } continue case ABL, ABX, ADUFFZERO, ADUFFCOPY: cursym.Text.Mark &^= LEAF fallthrough case ABCASE, AB, ABEQ, ABNE, ABCS, ABHS, ABCC, ABLO, ABMI, ABPL, ABVS, ABVC, ABHI, ABLS, ABGE, ABLT, ABGT, ABLE: q1 = p.Pcond if q1 != nil { for q1.As == ANOP { q1 = q1.Link p.Pcond = q1 } } break } q = p } for p = cursym.Text; p != nil; p = p.Link { o = p.As switch o { case ATEXT: autosize = p.To.Offset + 4 if autosize <= 4 { if cursym.Text.Mark&LEAF != 0 { p.To.Offset = -4 autosize = 0 } } if autosize == 0 && cursym.Text.Mark&LEAF == 0 { if ctxt.Debugvlog != 0 { fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name) liblink.Bflush(ctxt.Bso) } cursym.Text.Mark |= LEAF } if cursym.Text.Mark&LEAF != 0 { cursym.Leaf = 1 if autosize == 0 { break } } if p.Reg&liblink.NOSPLIT == 0 { p = stacksplit(ctxt, p, autosize, bool2int(cursym.Text.Reg&liblink.NEEDCTXT == 0)) // emit split check } // MOVW.W R14,$-autosize(SP) p = liblink.Appendp(ctxt, p) p.As = AMOVW p.Scond |= C_WBIT p.From.Typ = D_REG p.From.Reg = REGLINK p.To.Typ = D_OREG p.To.Offset = -autosize p.To.Reg = REGSP p.Spadj = autosize if cursym.Text.Reg&liblink.WRAPPER != 0 { // g->panicwrap += autosize; // MOVW panicwrap_offset(g), R3 // ADD $autosize, R3 // MOVW R3 panicwrap_offset(g) p = liblink.Appendp(ctxt, p) p.As = AMOVW p.From.Typ = D_OREG p.From.Reg = REGG p.From.Offset = 2 * ctxt.Arch.Ptrsize p.To.Typ = D_REG p.To.Reg = 3 p = liblink.Appendp(ctxt, p) p.As = AADD p.From.Typ = D_CONST p.From.Offset = autosize p.To.Typ = D_REG p.To.Reg = 3 p = liblink.Appendp(ctxt, p) p.As = AMOVW p.From.Typ = D_REG p.From.Reg = 3 p.To.Typ = D_OREG p.To.Reg = REGG p.To.Offset = 2 * ctxt.Arch.Ptrsize } case ARET: nocache_obj5(p) if cursym.Text.Mark&LEAF != 0 { if autosize == 0 { p.As = AB p.From = zprg_obj5.From if p.To.Sym != nil { // retjmp p.To.Typ = D_BRANCH } else { p.To.Typ = D_OREG p.To.Offset = 0 p.To.Reg = REGLINK } break } } if cursym.Text.Reg&liblink.WRAPPER != 0 { var scond int // Preserve original RET's cond, to allow RET.EQ // in the implementation of reflect.call. scond = p.Scond p.Scond = C_SCOND_NONE // g->panicwrap -= autosize; // MOVW panicwrap_offset(g), R3 // SUB $autosize, R3 // MOVW R3 panicwrap_offset(g) p.As = AMOVW p.From.Typ = D_OREG p.From.Reg = REGG p.From.Offset = 2 * ctxt.Arch.Ptrsize p.To.Typ = D_REG p.To.Reg = 3 p = liblink.Appendp(ctxt, p) p.As = ASUB p.From.Typ = D_CONST p.From.Offset = autosize p.To.Typ = D_REG p.To.Reg = 3 p = liblink.Appendp(ctxt, p) p.As = AMOVW p.From.Typ = D_REG p.From.Reg = 3 p.To.Typ = D_OREG p.To.Reg = REGG p.To.Offset = 2 * ctxt.Arch.Ptrsize p = liblink.Appendp(ctxt, p) p.Scond = scond } p.As = AMOVW p.Scond |= C_PBIT p.From.Typ = D_OREG p.From.Offset = autosize p.From.Reg = REGSP p.To.Typ = D_REG p.To.Reg = REGPC // If there are instructions following // this ARET, they come from a branch // with the same stackframe, so no spadj. if p.To.Sym != nil { // retjmp p.To.Reg = REGLINK q2 = liblink.Appendp(ctxt, p) q2.As = AB q2.To.Typ = D_BRANCH q2.To.Sym = p.To.Sym p.To.Sym = nil p = q2 } case AADD: if p.From.Typ == D_CONST && p.From.Reg == NREG && p.To.Typ == D_REG && p.To.Reg == REGSP { p.Spadj = -p.From.Offset } case ASUB: if p.From.Typ == D_CONST && p.From.Reg == NREG && p.To.Typ == D_REG && p.To.Reg == REGSP { p.Spadj = p.From.Offset } case ADIV, ADIVU, AMOD, AMODU: if ctxt.Debugdivmod != 0 { break } if p.From.Typ != D_REG { break } if p.To.Typ != D_REG { break } q1 = p /* MOV a,4(SP) */ p = liblink.Appendp(ctxt, p) p.As = AMOVW p.Lineno = q1.Lineno p.From.Typ = D_REG p.From.Reg = q1.From.Reg p.To.Typ = D_OREG p.To.Reg = REGSP p.To.Offset = 4 /* MOV b,REGTMP */ p = liblink.Appendp(ctxt, p) p.As = AMOVW p.Lineno = q1.Lineno p.From.Typ = D_REG p.From.Reg = q1.Reg if q1.Reg == NREG { p.From.Reg = q1.To.Reg } p.To.Typ = D_REG p.To.Reg = REGTMP p.To.Offset = 0 /* CALL appropriate */ p = liblink.Appendp(ctxt, p) p.As = ABL p.Lineno = q1.Lineno p.To.Typ = D_BRANCH switch o { case ADIV: p.To.Sym = ctxt.Sym_div case ADIVU: p.To.Sym = ctxt.Sym_divu case AMOD: p.To.Sym = ctxt.Sym_mod case AMODU: p.To.Sym = ctxt.Sym_modu break } /* MOV REGTMP, b */ p = liblink.Appendp(ctxt, p) p.As = AMOVW p.Lineno = q1.Lineno p.From.Typ = D_REG p.From.Reg = REGTMP p.From.Offset = 0 p.To.Typ = D_REG p.To.Reg = q1.To.Reg /* ADD $8,SP */ p = liblink.Appendp(ctxt, p) p.As = AADD p.Lineno = q1.Lineno p.From.Typ = D_CONST p.From.Reg = NREG p.From.Offset = 8 p.Reg = NREG p.To.Typ = D_REG p.To.Reg = REGSP p.Spadj = -8 /* Keep saved LR at 0(SP) after SP change. */ /* MOVW 0(SP), REGTMP; MOVW REGTMP, -8!(SP) */ /* TODO: Remove SP adjustments; see issue 6699. */ q1.As = AMOVW q1.From.Typ = D_OREG q1.From.Reg = REGSP q1.From.Offset = 0 q1.Reg = NREG q1.To.Typ = D_REG q1.To.Reg = REGTMP /* SUB $8,SP */ q1 = liblink.Appendp(ctxt, q1) q1.As = AMOVW q1.From.Typ = D_REG q1.From.Reg = REGTMP q1.Reg = NREG q1.To.Typ = D_OREG q1.To.Reg = REGSP q1.To.Offset = -8 q1.Scond |= C_WBIT q1.Spadj = 8 case AMOVW: if (p.Scond&C_WBIT != 0) && p.To.Typ == D_OREG && p.To.Reg == REGSP { p.Spadj = -p.To.Offset } if (p.Scond&C_PBIT != 0) && p.From.Typ == D_OREG && p.From.Reg == REGSP && p.To.Reg != REGPC { p.Spadj = -p.From.Offset } if p.From.Typ == D_CONST && p.From.Reg == REGSP && p.To.Typ == D_REG && p.To.Reg == REGSP { p.Spadj = -p.From.Offset } break } } }