func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { if p.As == ALEAL || p.As == ALEAQ { return } if a.Reg == REG_BP { ctxt.Diag("invalid address: %v", p) return } if a.Reg == REG_TLS { a.Reg = REG_BP } if a.Type == obj.TYPE_MEM && a.Name == obj.NAME_NONE { switch a.Reg { // all ok case REG_BP, REG_SP, REG_R15: break default: if a.Index != REG_NONE { ctxt.Diag("invalid address %v", p) } a.Index = a.Reg if a.Index != REG_NONE { a.Scale = 1 } a.Reg = REG_R15 } } }
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 initdiv(ctxt *obj.Link) { if ctxt.Sym_div != nil { return } ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0) ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0) ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0) ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0) }
func oplook(ctxt *obj.Link, p *obj.Prog) *Optab { if oprange[AOR&obj.AMask].start == nil { buildop(ctxt) } a1 := int(p.Optab) if a1 != 0 { return &optab[a1-1:][0] } a1 = int(p.From.Class) if a1 == 0 { a1 = aclass(ctxt, &p.From) + 1 p.From.Class = int8(a1) } a1-- a3 := int(p.To.Class) if a3 == 0 { a3 = aclass(ctxt, &p.To) + 1 p.To.Class = int8(a3) } a3-- a2 := C_NONE if p.Reg != 0 { a2 = C_REG } //print("oplook %P %d %d %d\n", p, a1, a2, a3); r0 := p.As & obj.AMask o := oprange[r0].start if o == nil { o = oprange[r0].stop /* just generate an error */ } e := oprange[r0].stop c1 := xcmp[a1][:] c3 := xcmp[a3][:] for ; -cap(o) < -cap(e); o = o[1:] { if int(o[0].a2) == a2 { if c1[o[0].a1] != 0 { if c3[o[0].a3] != 0 { p.Optab = uint16((-cap(o) + cap(optab)) + 1) return &o[0] } } } } ctxt.Diag("illegal combination %v %v %v %v", obj.Aconv(int(p.As)), DRconv(a1), DRconv(a2), DRconv(a3)) prasm(p) if o == nil { o = optab } return &o[0] }
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 oplook(ctxt *obj.Link, p *obj.Prog) *Optab { if oprange[AOR&obj.AMask] == nil { buildop(ctxt) } a1 := int(p.Optab) if a1 != 0 { return &optab[a1-1] } a1 = int(p.From.Class) if a1 == 0 { a1 = aclass(ctxt, &p.From) + 1 p.From.Class = int8(a1) } a1-- a3 := int(p.To.Class) if a3 == 0 { a3 = aclass(ctxt, &p.To) + 1 p.To.Class = int8(a3) } a3-- a2 := C_NONE if p.Reg != 0 { a2 = C_REG } //print("oplook %P %d %d %d\n", p, a1, a2, a3); ops := oprange[p.As&obj.AMask] c1 := &xcmp[a1] c3 := &xcmp[a3] for i := range ops { op := &ops[i] if int(op.a2) == a2 && c1[op.a1] && c3[op.a3] { p.Optab = uint16(cap(optab) - cap(ops) + i + 1) return op } } ctxt.Diag("illegal combination %v %v %v %v", obj.Aconv(p.As), DRconv(a1), DRconv(a2), DRconv(a3)) prasm(p) if ops == nil { ops = optab } return &ops[0] }
func preprocess(ctxt *obj.Link, cursym *obj.LSym) { if ctxt.Headtype == obj.Hplan9 && ctxt.Plan9privates == nil { ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0) } ctxt.Cursym = cursym if cursym.Text == nil || cursym.Text.Link == nil { return } p := cursym.Text autoffset := int32(p.To.Offset) if autoffset < 0 { autoffset = 0 } var bpsize int if p.Mode == 64 && ctxt.Framepointer_enabled && autoffset > 0 && p.From3.Offset&obj.NOFRAME == 0 { // Make room for to save a base pointer. If autoffset == 0, // this might do something special like a tail jump to // another function, so in that case we omit this. bpsize = ctxt.Arch.PtrSize autoffset += int32(bpsize) p.To.Offset += int64(bpsize) } else { bpsize = 0 } textarg := int64(p.To.Val.(int32)) cursym.Args = int32(textarg) cursym.Locals = int32(p.To.Offset) // TODO(rsc): Remove. if p.Mode == 32 && cursym.Locals < 0 { cursym.Locals = 0 } // TODO(rsc): Remove 'p.Mode == 64 &&'. if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 { leaf := true LeafSearch: for q := p; q != nil; q = q.Link { switch q.As { case obj.ACALL: // Treat common runtime calls that take no arguments // the same as duffcopy and duffzero. if !isZeroArgRuntimeCall(q.To.Sym) { leaf = false break LeafSearch } fallthrough case obj.ADUFFCOPY, obj.ADUFFZERO: if autoffset >= obj.StackSmall-8 { leaf = false break LeafSearch } } } if leaf { p.From3.Offset |= obj.NOSPLIT } } if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 { p = obj.Appendp(ctxt, p) p = load_g_cx(ctxt, p) // load g into CX } if cursym.Text.From3Offset()&obj.NOSPLIT == 0 { p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check } if autoffset != 0 { if autoffset%int32(ctxt.Arch.RegSize) != 0 { ctxt.Diag("unaligned stack size %d", autoffset) } p = obj.Appendp(ctxt, p) p.As = AADJSP p.From.Type = obj.TYPE_CONST p.From.Offset = int64(autoffset) p.Spadj = autoffset } deltasp := autoffset if bpsize > 0 { // Save caller's BP p = obj.Appendp(ctxt, p) p.As = AMOVQ p.From.Type = obj.TYPE_REG p.From.Reg = REG_BP p.To.Type = obj.TYPE_MEM p.To.Reg = REG_SP p.To.Scale = 1 p.To.Offset = int64(autoffset) - int64(bpsize) // Move current frame to BP p = obj.Appendp(ctxt, p) p.As = ALEAQ p.From.Type = obj.TYPE_MEM p.From.Reg = REG_SP p.From.Scale = 1 p.From.Offset = int64(autoffset) - int64(bpsize) p.To.Type = obj.TYPE_REG p.To.Reg = REG_BP } if cursym.Text.From3Offset()&obj.WRAPPER != 0 { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVQ g_panic(CX), BX // TESTQ BX, BX // JEQ end // LEAQ (autoffset+8)(SP), DI // CMPQ panic_argp(BX), DI // JNE end // MOVQ SP, panic_argp(BX) // end: // NOP // // The NOP is needed to give the jumps somewhere to land. // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes. p = obj.Appendp(ctxt, p) p.As = AMOVQ p.From.Type = obj.TYPE_MEM p.From.Reg = REG_CX p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic p.To.Type = obj.TYPE_REG p.To.Reg = REG_BX if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { p.As = AMOVL p.From.Type = obj.TYPE_MEM p.From.Reg = REG_R15 p.From.Scale = 1 p.From.Index = REG_CX } if p.Mode == 32 { p.As = AMOVL } p = obj.Appendp(ctxt, p) p.As = ATESTQ p.From.Type = obj.TYPE_REG p.From.Reg = REG_BX p.To.Type = obj.TYPE_REG p.To.Reg = REG_BX if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { p.As = ATESTL } p = obj.Appendp(ctxt, p) p.As = AJEQ p.To.Type = obj.TYPE_BRANCH p1 := p p = obj.Appendp(ctxt, p) p.As = ALEAQ p.From.Type = obj.TYPE_MEM p.From.Reg = REG_SP p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize) p.To.Type = obj.TYPE_REG p.To.Reg = REG_DI if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { p.As = ALEAL } p = obj.Appendp(ctxt, p) p.As = ACMPQ p.From.Type = obj.TYPE_MEM p.From.Reg = REG_BX p.From.Offset = 0 // Panic.argp p.To.Type = obj.TYPE_REG p.To.Reg = REG_DI if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { p.As = ACMPL p.From.Type = obj.TYPE_MEM p.From.Reg = REG_R15 p.From.Scale = 1 p.From.Index = REG_BX } if p.Mode == 32 { p.As = ACMPL } p = obj.Appendp(ctxt, p) p.As = AJNE p.To.Type = obj.TYPE_BRANCH p2 := p p = obj.Appendp(ctxt, p) p.As = AMOVQ p.From.Type = obj.TYPE_REG p.From.Reg = REG_SP p.To.Type = obj.TYPE_MEM p.To.Reg = REG_BX p.To.Offset = 0 // Panic.argp if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { p.As = AMOVL p.To.Type = obj.TYPE_MEM p.To.Reg = REG_R15 p.To.Scale = 1 p.To.Index = REG_BX } if p.Mode == 32 { p.As = AMOVL } p = obj.Appendp(ctxt, p) p.As = obj.ANOP p1.Pcond = p p2.Pcond = p } for ; p != nil; p = p.Link { pcsize := int(p.Mode) / 8 switch p.From.Name { case obj.NAME_AUTO: p.From.Offset += int64(deltasp) - int64(bpsize) case obj.NAME_PARAM: p.From.Offset += int64(deltasp) + int64(pcsize) } if p.From3 != nil { switch p.From3.Name { case obj.NAME_AUTO: p.From3.Offset += int64(deltasp) - int64(bpsize) case obj.NAME_PARAM: p.From3.Offset += int64(deltasp) + int64(pcsize) } } switch p.To.Name { case obj.NAME_AUTO: p.To.Offset += int64(deltasp) - int64(bpsize) case obj.NAME_PARAM: p.To.Offset += int64(deltasp) + int64(pcsize) } switch p.As { default: continue case APUSHL, APUSHFL: deltasp += 4 p.Spadj = 4 continue case APUSHQ, APUSHFQ: deltasp += 8 p.Spadj = 8 continue case APUSHW, APUSHFW: deltasp += 2 p.Spadj = 2 continue case APOPL, APOPFL: deltasp -= 4 p.Spadj = -4 continue case APOPQ, APOPFQ: deltasp -= 8 p.Spadj = -8 continue case APOPW, APOPFW: deltasp -= 2 p.Spadj = -2 continue case obj.ARET: // do nothing } if autoffset != deltasp { ctxt.Diag("unbalanced PUSH/POP") } if autoffset != 0 { if bpsize > 0 { // Restore caller's BP p.As = AMOVQ p.From.Type = obj.TYPE_MEM p.From.Reg = REG_SP p.From.Scale = 1 p.From.Offset = int64(autoffset) - int64(bpsize) p.To.Type = obj.TYPE_REG p.To.Reg = REG_BP p = obj.Appendp(ctxt, p) } p.As = AADJSP p.From.Type = obj.TYPE_CONST p.From.Offset = int64(-autoffset) p.Spadj = -autoffset p = obj.Appendp(ctxt, p) p.As = obj.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 = obj.AJMP } } }
// Rewrite p, if necessary, to access global data via the global offset table. func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { var add, lea, mov obj.As var reg int16 if p.Mode == 64 { add = AADDQ lea = ALEAQ mov = AMOVQ reg = REG_R15 } else { add = AADDL lea = ALEAL mov = AMOVL reg = REG_CX } if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { // ADUFFxxx $offset // becomes // $MOV runtime.duffxxx@GOT, $reg // $ADD $offset, $reg // CALL $reg var sym *obj.LSym if p.As == obj.ADUFFZERO { sym = obj.Linklookup(ctxt, "runtime.duffzero", 0) } else { sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0) } offset := p.To.Offset p.As = mov p.From.Type = obj.TYPE_MEM p.From.Name = obj.NAME_GOTREF p.From.Sym = sym p.To.Type = obj.TYPE_REG p.To.Reg = reg p.To.Offset = 0 p.To.Sym = nil p1 := obj.Appendp(ctxt, p) p1.As = add p1.From.Type = obj.TYPE_CONST p1.From.Offset = offset p1.To.Type = obj.TYPE_REG p1.To.Reg = reg p2 := obj.Appendp(ctxt, p1) p2.As = obj.ACALL p2.To.Type = obj.TYPE_REG p2.To.Reg = reg } // We only care about global data: NAME_EXTERN means a global // symbol in the Go sense, and p.Sym.Local is true for a few // internally defined symbols. if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { // $LEA sym, Rx becomes $MOV $sym, Rx which will be rewritten below p.As = mov p.From.Type = obj.TYPE_ADDR } if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { // $MOV $sym, Rx becomes $MOV sym@GOT, Rx // $MOV $sym+<off>, Rx becomes $MOV sym@GOT, Rx; $LEA <off>(Rx), Rx // On 386 only, more complicated things like PUSHL $sym become $MOV sym@GOT, CX; PUSHL CX cmplxdest := false pAs := p.As var dest obj.Addr if p.To.Type != obj.TYPE_REG || pAs != mov { if p.Mode == 64 { ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p) } cmplxdest = true dest = p.To p.As = mov p.To.Type = obj.TYPE_REG p.To.Reg = REG_CX p.To.Sym = nil p.To.Name = obj.NAME_NONE } p.From.Type = obj.TYPE_MEM p.From.Name = obj.NAME_GOTREF q := p if p.From.Offset != 0 { q = obj.Appendp(ctxt, p) q.As = lea q.From.Type = obj.TYPE_MEM q.From.Reg = p.To.Reg q.From.Offset = p.From.Offset q.To = p.To p.From.Offset = 0 } if cmplxdest { q = obj.Appendp(ctxt, q) q.As = pAs q.To = dest q.From.Type = obj.TYPE_REG q.From.Reg = REG_CX } } if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN { ctxt.Diag("don't know how to handle %v with -dynlink", p) } var source *obj.Addr // MOVx sym, Ry becomes $MOV sym@GOT, R15; MOVx (R15), Ry // MOVx Ry, sym becomes $MOV sym@GOT, R15; MOVx Ry, (R15) // An addition may be inserted between the two MOVs if there is an offset. if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) } source = &p.From } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { source = &p.To } else { return } if p.As == obj.ACALL { // When dynlinking on 386, almost any call might end up being a call // to a PLT, so make sure the GOT pointer is loaded into BX. // RegTo2 is set on the replacement call insn to stop it being // processed when it is in turn passed to progedit. if p.Mode == 64 || (p.To.Sym != nil && p.To.Sym.Local) || p.RegTo2 != 0 { return } p1 := obj.Appendp(ctxt, p) p2 := obj.Appendp(ctxt, p1) p1.As = ALEAL p1.From.Type = obj.TYPE_MEM p1.From.Name = obj.NAME_STATIC p1.From.Sym = obj.Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0) p1.To.Type = obj.TYPE_REG p1.To.Reg = REG_BX p2.As = p.As p2.Scond = p.Scond p2.From = p.From p2.From3 = p.From3 p2.Reg = p.Reg p2.To = p.To // p.To.Type was set to TYPE_BRANCH above, but that makes checkaddr // in ../pass.go complain, so set it back to TYPE_MEM here, until p2 // itself gets passed to progedit. p2.To.Type = obj.TYPE_MEM p2.RegTo2 = 1 obj.Nopout(p) return } if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP { return } if source.Type != obj.TYPE_MEM { ctxt.Diag("don't know how to handle %v with -dynlink", p) } p1 := obj.Appendp(ctxt, p) p2 := obj.Appendp(ctxt, p1) p1.As = mov p1.From.Type = obj.TYPE_MEM p1.From.Sym = source.Sym p1.From.Name = obj.NAME_GOTREF p1.To.Type = obj.TYPE_REG p1.To.Reg = reg p2.As = p.As p2.From = p.From p2.To = p.To if p.From.Name == obj.NAME_EXTERN { p2.From.Reg = reg p2.From.Name = obj.NAME_NONE p2.From.Sym = nil } else if p.To.Name == obj.NAME_EXTERN { p2.To.Reg = reg p2.To.Name = obj.NAME_NONE p2.To.Sym = nil } else { return } obj.Nopout(p) }
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 aclass(ctxt *obj.Link, a *obj.Addr) int { switch a.Type { case obj.TYPE_NONE: return C_NONE case obj.TYPE_REG: if REG_R0 <= a.Reg && a.Reg <= REG_R31 { return C_REG } if REG_F0 <= a.Reg && a.Reg <= REG_F31 { return C_FREG } if REG_M0 <= a.Reg && a.Reg <= REG_M31 { return C_MREG } if REG_FCR0 <= a.Reg && a.Reg <= REG_FCR31 { return C_FCREG } if a.Reg == REG_LO { return C_LO } if a.Reg == REG_HI { return C_HI } return C_GOK case obj.TYPE_MEM: switch a.Name { case obj.NAME_EXTERN, obj.NAME_STATIC: if a.Sym == nil { break } ctxt.Instoffset = a.Offset if a.Sym != nil { // use relocation return C_ADDR } return C_LEXT case obj.NAME_AUTO: ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset if ctxt.Instoffset >= -BIG && ctxt.Instoffset < BIG { return C_SAUTO } return C_LAUTO case obj.NAME_PARAM: ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset + 8 if ctxt.Instoffset >= -BIG && ctxt.Instoffset < BIG { return C_SAUTO } return C_LAUTO case obj.NAME_NONE: ctxt.Instoffset = a.Offset if ctxt.Instoffset == 0 { return C_ZOREG } if ctxt.Instoffset >= -BIG && ctxt.Instoffset < BIG { return C_SOREG } return C_LOREG } return C_GOK case obj.TYPE_TEXTSIZE: return C_TEXTSIZE case obj.TYPE_CONST, obj.TYPE_ADDR: switch a.Name { case obj.NAME_NONE: ctxt.Instoffset = a.Offset if a.Reg != 0 { if -BIG <= ctxt.Instoffset && ctxt.Instoffset <= BIG { return C_SACON } if isint32(ctxt.Instoffset) { return C_LACON } return C_DACON } goto consize case obj.NAME_EXTERN, obj.NAME_STATIC: s := a.Sym if s == nil { break } if s.Type == obj.SCONST { ctxt.Instoffset = s.Value + a.Offset goto consize } ctxt.Instoffset = s.Value + a.Offset /* not sure why this barfs */ return C_LCON case obj.NAME_AUTO: ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset if ctxt.Instoffset >= -BIG && ctxt.Instoffset < BIG { return C_SACON } return C_LACON case obj.NAME_PARAM: ctxt.Instoffset = int64(ctxt.Autosize) + a.Offset + 8 if ctxt.Instoffset >= -BIG && ctxt.Instoffset < BIG { return C_SACON } return C_LACON } return C_GOK consize: if ctxt.Instoffset >= 0 { if ctxt.Instoffset == 0 { return C_ZCON } if ctxt.Instoffset <= 0x7fff { return C_SCON } if ctxt.Instoffset <= 0xffff { return C_ANDCON } if ctxt.Instoffset&0xffff == 0 && isuint32(uint64(ctxt.Instoffset)) { /* && (instoffset & (1<<31)) == 0) */ return C_UCON } if isint32(ctxt.Instoffset) || isuint32(uint64(ctxt.Instoffset)) { return C_LCON } return C_LCON // C_DCON } if ctxt.Instoffset >= -0x8000 { return C_ADDCON } if ctxt.Instoffset&0xffff == 0 && isint32(ctxt.Instoffset) { return C_UCON } if isint32(ctxt.Instoffset) { return C_LCON } return C_LCON // C_DCON case obj.TYPE_BRANCH: return C_SBRA } return C_GOK }
func opirr(ctxt *obj.Link, a int) uint32 { switch a { case AADD: return SP(1, 0) case AADDU: return SP(1, 1) case ASGT: return SP(1, 2) case ASGTU: return SP(1, 3) case AAND: return SP(1, 4) case AOR: return SP(1, 5) case AXOR: return SP(1, 6) case ALAST: return SP(1, 7) /* lui */ case ASLL: return OP(0, 0) case ASRL: return OP(0, 2) case ASRA: return OP(0, 3) case AADDV: return SP(3, 0) case AADDVU: return SP(3, 1) case AJMP: return SP(0, 2) case AJAL, obj.ADUFFZERO, obj.ADUFFCOPY: return SP(0, 3) case ABEQ: return SP(0, 4) case ABEQ + ALAST: return SP(2, 4) /* likely */ case ABNE: return SP(0, 5) case ABNE + ALAST: return SP(2, 5) /* likely */ case ABGEZ: return SP(0, 1) | BCOND(0, 1) case ABGEZ + ALAST: return SP(0, 1) | BCOND(0, 3) /* likely */ case ABGEZAL: return SP(0, 1) | BCOND(2, 1) case ABGEZAL + ALAST: return SP(0, 1) | BCOND(2, 3) /* likely */ case ABGTZ: return SP(0, 7) case ABGTZ + ALAST: return SP(2, 7) /* likely */ case ABLEZ: return SP(0, 6) case ABLEZ + ALAST: return SP(2, 6) /* likely */ case ABLTZ: return SP(0, 1) | BCOND(0, 0) case ABLTZ + ALAST: return SP(0, 1) | BCOND(0, 2) /* likely */ case ABLTZAL: return SP(0, 1) | BCOND(2, 0) case ABLTZAL + ALAST: return SP(0, 1) | BCOND(2, 2) /* likely */ case ABFPT: return SP(2, 1) | (257 << 16) case ABFPT + ALAST: return SP(2, 1) | (259 << 16) /* likely */ case ABFPF: return SP(2, 1) | (256 << 16) case ABFPF + ALAST: return SP(2, 1) | (258 << 16) /* likely */ case AMOVB, AMOVBU: return SP(5, 0) case AMOVH, AMOVHU: return SP(5, 1) case AMOVW, AMOVWU: return SP(5, 3) case AMOVV: return SP(7, 7) case AMOVF: return SP(7, 1) case AMOVD: return SP(7, 5) case AMOVWL: return SP(5, 2) case AMOVWR: return SP(5, 6) case AMOVVL: return SP(5, 4) case AMOVVR: return SP(5, 5) case ABREAK: return SP(5, 7) case AMOVWL + ALAST: return SP(4, 2) case AMOVWR + ALAST: return SP(4, 6) case AMOVVL + ALAST: return SP(3, 2) case AMOVVR + ALAST: return SP(3, 3) case AMOVB + ALAST: return SP(4, 0) case AMOVBU + ALAST: return SP(4, 4) case AMOVH + ALAST: return SP(4, 1) case AMOVHU + ALAST: return SP(4, 5) case AMOVW + ALAST: return SP(4, 3) case AMOVWU + ALAST: return SP(4, 7) case AMOVV + ALAST: return SP(6, 7) case AMOVF + ALAST: return SP(6, 1) case AMOVD + ALAST: return SP(6, 5) case ASLLV: return OP(7, 0) case ASRLV: return OP(7, 2) case ASRAV: return OP(7, 3) case ASLLV + ALAST: return OP(7, 4) case ASRLV + ALAST: return OP(7, 6) case ASRAV + ALAST: return OP(7, 7) } if a >= ALAST { ctxt.Diag("bad irr opcode %v+ALAST", obj.Aconv(a-ALAST)) } else { ctxt.Diag("bad irr opcode %v", obj.Aconv(a)) } return 0 }
func vregoff(ctxt *obj.Link, a *obj.Addr) int64 { ctxt.Instoffset = 0 aclass(ctxt, a) return ctxt.Instoffset }
func preprocess(ctxt *obj.Link, cursym *obj.LSym) { autosize := int32(0) ctxt.Cursym = cursym if cursym.Text == nil || cursym.Text.Link == nil { return } softfloat(ctxt, cursym) p := cursym.Text autoffset := int32(p.To.Offset) if autoffset < 0 { autoffset = 0 } cursym.Locals = autoffset cursym.Args = p.To.Val.(int32) /* * find leaf subroutines * strip NOPs * expand RET * expand BECOME pseudo */ var q1 *obj.Prog var q *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 ADIV, ADIVU, AMOD, AMODU: q = p if ctxt.Sym_div == nil { initdiv(ctxt) } cursym.Text.Mark &^= LEAF continue case obj.ANOP: q1 = p.Link q.Link = q1 /* q is non-nop */ if q1 != nil { q1.Mark |= p.Mark } continue case ABL, ABX, obj.ADUFFZERO, obj.ADUFFCOPY: cursym.Text.Mark &^= LEAF fallthrough case 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 == obj.ANOP { q1 = q1.Link p.Pcond = q1 } } } q = p } var o int var p1 *obj.Prog var p2 *obj.Prog var q2 *obj.Prog for p := cursym.Text; p != nil; p = p.Link { o = int(p.As) switch o { case obj.ATEXT: autosize = int32(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) ctxt.Bso.Flush() } cursym.Text.Mark |= LEAF } if cursym.Text.Mark&LEAF != 0 { cursym.Leaf = 1 if autosize == 0 { break } } if p.From3.Offset&obj.NOSPLIT == 0 { p = stacksplit(ctxt, p, autosize) // emit split check } // MOVW.W R14,$-autosize(SP) p = obj.Appendp(ctxt, p) p.As = AMOVW p.Scond |= C_WBIT p.From.Type = obj.TYPE_REG p.From.Reg = REGLINK p.To.Type = obj.TYPE_MEM p.To.Offset = int64(-autosize) p.To.Reg = REGSP p.Spadj = autosize if cursym.Text.From3.Offset&obj.WRAPPER != 0 { // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // // MOVW g_panic(g), R1 // CMP $0, R1 // B.EQ end // MOVW panic_argp(R1), R2 // ADD $(autosize+4), R13, R3 // CMP R2, R3 // B.NE end // ADD $4, R13, R4 // MOVW R4, panic_argp(R1) // end: // NOP // // The NOP is needed to give the jumps somewhere to land. // It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes. p = obj.Appendp(ctxt, p) p.As = AMOVW p.From.Type = obj.TYPE_MEM p.From.Reg = REGG p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic p.To.Type = obj.TYPE_REG p.To.Reg = REG_R1 p = obj.Appendp(ctxt, p) p.As = ACMP p.From.Type = obj.TYPE_CONST p.From.Offset = 0 p.Reg = REG_R1 p = obj.Appendp(ctxt, p) p.As = ABEQ p.To.Type = obj.TYPE_BRANCH p1 = p p = obj.Appendp(ctxt, p) p.As = AMOVW p.From.Type = obj.TYPE_MEM p.From.Reg = REG_R1 p.From.Offset = 0 // Panic.argp p.To.Type = obj.TYPE_REG p.To.Reg = REG_R2 p = obj.Appendp(ctxt, p) p.As = AADD p.From.Type = obj.TYPE_CONST p.From.Offset = int64(autosize) + 4 p.Reg = REG_R13 p.To.Type = obj.TYPE_REG p.To.Reg = REG_R3 p = obj.Appendp(ctxt, p) p.As = ACMP p.From.Type = obj.TYPE_REG p.From.Reg = REG_R2 p.Reg = REG_R3 p = obj.Appendp(ctxt, p) p.As = ABNE p.To.Type = obj.TYPE_BRANCH p2 = p p = obj.Appendp(ctxt, p) p.As = AADD p.From.Type = obj.TYPE_CONST p.From.Offset = 4 p.Reg = REG_R13 p.To.Type = obj.TYPE_REG p.To.Reg = REG_R4 p = obj.Appendp(ctxt, p) p.As = AMOVW p.From.Type = obj.TYPE_REG p.From.Reg = REG_R4 p.To.Type = obj.TYPE_MEM p.To.Reg = REG_R1 p.To.Offset = 0 // Panic.argp p = obj.Appendp(ctxt, p) p.As = obj.ANOP p1.Pcond = p p2.Pcond = p } case obj.ARET: obj.Nocache(p) if cursym.Text.Mark&LEAF != 0 { if autosize == 0 { p.As = AB p.From = obj.Addr{} if p.To.Sym != nil { // retjmp p.To.Type = obj.TYPE_BRANCH } else { p.To.Type = obj.TYPE_MEM p.To.Offset = 0 p.To.Reg = REGLINK } break } } p.As = AMOVW p.Scond |= C_PBIT p.From.Type = obj.TYPE_MEM p.From.Offset = int64(autosize) p.From.Reg = REGSP p.To.Type = obj.TYPE_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 = obj.Appendp(ctxt, p) q2.As = AB q2.To.Type = obj.TYPE_BRANCH q2.To.Sym = p.To.Sym p.To.Sym = nil p = q2 } case AADD: if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { p.Spadj = int32(-p.From.Offset) } case ASUB: if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { p.Spadj = int32(p.From.Offset) } case ADIV, ADIVU, AMOD, AMODU: if cursym.Text.From3.Offset&obj.NOSPLIT != 0 { ctxt.Diag("cannot divide in NOSPLIT function") } if ctxt.Debugdivmod != 0 { break } if p.From.Type != obj.TYPE_REG { break } if p.To.Type != obj.TYPE_REG { break } // Make copy because we overwrite p below. q1 := *p if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP { ctxt.Diag("div already using REGTMP: %v", p) } /* MOV m(g),REGTMP */ p.As = AMOVW p.Lineno = q1.Lineno p.From.Type = obj.TYPE_MEM p.From.Reg = REGG p.From.Offset = 6 * 4 // offset of g.m p.Reg = 0 p.To.Type = obj.TYPE_REG p.To.Reg = REGTMP /* MOV a,m_divmod(REGTMP) */ p = obj.Appendp(ctxt, p) p.As = AMOVW p.Lineno = q1.Lineno p.From.Type = obj.TYPE_REG p.From.Reg = q1.From.Reg p.To.Type = obj.TYPE_MEM p.To.Reg = REGTMP p.To.Offset = 8 * 4 // offset of m.divmod /* MOV b,REGTMP */ p = obj.Appendp(ctxt, p) p.As = AMOVW p.Lineno = q1.Lineno p.From.Type = obj.TYPE_REG p.From.Reg = q1.Reg if q1.Reg == 0 { p.From.Reg = q1.To.Reg } p.To.Type = obj.TYPE_REG p.To.Reg = REGTMP p.To.Offset = 0 /* CALL appropriate */ p = obj.Appendp(ctxt, p) p.As = ABL p.Lineno = q1.Lineno p.To.Type = obj.TYPE_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 } /* MOV REGTMP, b */ p = obj.Appendp(ctxt, p) p.As = AMOVW p.Lineno = q1.Lineno p.From.Type = obj.TYPE_REG p.From.Reg = REGTMP p.From.Offset = 0 p.To.Type = obj.TYPE_REG p.To.Reg = q1.To.Reg case AMOVW: if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP { p.Spadj = int32(-p.To.Offset) } if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC { p.Spadj = int32(-p.From.Offset) } if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP { p.Spadj = int32(-p.From.Offset) } } } }
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" { // 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. // // 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) } } } }
// Rewrite p, if necessary, to access global data via the global offset table. func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { // ADUFFxxx $offset // becomes // MOVD runtime.duffxxx@GOT, R12 // ADD $offset, R12 // MOVD R12, CTR // BL (CTR) var sym *obj.LSym if p.As == obj.ADUFFZERO { sym = obj.Linklookup(ctxt, "runtime.duffzero", 0) } else { sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0) } offset := p.To.Offset p.As = AMOVD p.From.Type = obj.TYPE_MEM p.From.Name = obj.NAME_GOTREF p.From.Sym = sym p.To.Type = obj.TYPE_REG p.To.Reg = REG_R12 p.To.Name = obj.NAME_NONE p.To.Offset = 0 p.To.Sym = nil p1 := obj.Appendp(ctxt, p) p1.As = AADD p1.From.Type = obj.TYPE_CONST p1.From.Offset = offset p1.To.Type = obj.TYPE_REG p1.To.Reg = REG_R12 p2 := obj.Appendp(ctxt, p1) p2.As = AMOVD p2.From.Type = obj.TYPE_REG p2.From.Reg = REG_R12 p2.To.Type = obj.TYPE_REG p2.To.Reg = REG_CTR p3 := obj.Appendp(ctxt, p2) p3.As = obj.ACALL p3.From.Type = obj.TYPE_REG p3.From.Reg = REG_R12 p3.To.Type = obj.TYPE_REG p3.To.Reg = REG_CTR } // We only care about global data: NAME_EXTERN means a global // symbol in the Go sense, and p.Sym.Local is true for a few // internally defined symbols. if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { // MOVD $sym, Rx becomes MOVD sym@GOT, Rx // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx if p.As != AMOVD { ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) } if p.To.Type != obj.TYPE_REG { ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) } p.From.Type = obj.TYPE_MEM p.From.Name = obj.NAME_GOTREF if p.From.Offset != 0 { q := obj.Appendp(ctxt, p) q.As = AADD q.From.Type = obj.TYPE_CONST q.From.Offset = p.From.Offset q.To = p.To p.From.Offset = 0 } } if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN { ctxt.Diag("don't know how to handle %v with -dynlink", p) } var source *obj.Addr // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP) // An addition may be inserted between the two MOVs if there is an offset. if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) } source = &p.From } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { source = &p.To } else { return } if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { return } if source.Sym.Type == obj.STLSBSS { return } if source.Type != obj.TYPE_MEM { ctxt.Diag("don't know how to handle %v with -dynlink", p) } p1 := obj.Appendp(ctxt, p) p2 := obj.Appendp(ctxt, p1) p1.As = AMOVD p1.From.Type = obj.TYPE_MEM p1.From.Sym = source.Sym p1.From.Name = obj.NAME_GOTREF p1.To.Type = obj.TYPE_REG p1.To.Reg = REGTMP p2.As = p.As p2.From = p.From p2.To = p.To if p.From.Name == obj.NAME_EXTERN { p2.From.Reg = REGTMP p2.From.Name = obj.NAME_NONE p2.From.Sym = nil } else if p.To.Name == obj.NAME_EXTERN { p2.To.Reg = REGTMP p2.To.Name = obj.NAME_NONE p2.To.Sym = nil } else { return } obj.Nopout(p) }
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 }
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 */ 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 q2 *obj.Prog var retjmp *obj.LSym for p := cursym.Text; p != nil; p = p.Link { o := 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 { ctxt.Logf("save suppressed in: %s\n", cursym.Text.From.Sym.Name) } 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 = true 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 opirr(ctxt *obj.Link, a obj.As) uint32 { switch a { case AADD: return SP(1, 0) case AADDU: return SP(1, 1) case ASGT: return SP(1, 2) case ASGTU: return SP(1, 3) case AAND: return SP(1, 4) case AOR: return SP(1, 5) case AXOR: return SP(1, 6) case ALUI: return SP(1, 7) case ASLL: return OP(0, 0) case ASRL: return OP(0, 2) case ASRA: return OP(0, 3) case AADDV: return SP(3, 0) case AADDVU: return SP(3, 1) case AJMP: return SP(0, 2) case AJAL, obj.ADUFFZERO, obj.ADUFFCOPY: return SP(0, 3) case ABEQ: return SP(0, 4) case -ABEQ: return SP(2, 4) /* likely */ case ABNE: return SP(0, 5) case -ABNE: return SP(2, 5) /* likely */ case ABGEZ: return SP(0, 1) | BCOND(0, 1) case -ABGEZ: return SP(0, 1) | BCOND(0, 3) /* likely */ case ABGEZAL: return SP(0, 1) | BCOND(2, 1) case -ABGEZAL: return SP(0, 1) | BCOND(2, 3) /* likely */ case ABGTZ: return SP(0, 7) case -ABGTZ: return SP(2, 7) /* likely */ case ABLEZ: return SP(0, 6) case -ABLEZ: return SP(2, 6) /* likely */ case ABLTZ: return SP(0, 1) | BCOND(0, 0) case -ABLTZ: return SP(0, 1) | BCOND(0, 2) /* likely */ case ABLTZAL: return SP(0, 1) | BCOND(2, 0) case -ABLTZAL: return SP(0, 1) | BCOND(2, 2) /* likely */ case ABFPT: return SP(2, 1) | (257 << 16) case -ABFPT: return SP(2, 1) | (259 << 16) /* likely */ case ABFPF: return SP(2, 1) | (256 << 16) case -ABFPF: return SP(2, 1) | (258 << 16) /* likely */ case AMOVB, AMOVBU: return SP(5, 0) case AMOVH, AMOVHU: return SP(5, 1) case AMOVW, AMOVWU: return SP(5, 3) case AMOVV: return SP(7, 7) case AMOVF: return SP(7, 1) case AMOVD: return SP(7, 5) case AMOVWL: return SP(5, 2) case AMOVWR: return SP(5, 6) case AMOVVL: return SP(5, 4) case AMOVVR: return SP(5, 5) case ABREAK: return SP(5, 7) case -AMOVWL: return SP(4, 2) case -AMOVWR: return SP(4, 6) case -AMOVVL: return SP(3, 2) case -AMOVVR: return SP(3, 3) case -AMOVB: return SP(4, 0) case -AMOVBU: return SP(4, 4) case -AMOVH: return SP(4, 1) case -AMOVHU: return SP(4, 5) case -AMOVW: return SP(4, 3) case -AMOVWU: return SP(4, 7) case -AMOVV: return SP(6, 7) case -AMOVF: return SP(6, 1) case -AMOVD: return SP(6, 5) case ASLLV: return OP(7, 0) case ASRLV: return OP(7, 2) case ASRAV: return OP(7, 3) case -ASLLV: return OP(7, 4) case -ASRLV: return OP(7, 6) case -ASRAV: return OP(7, 7) case ATEQ: return OP(6, 4) case ATNE: return OP(6, 6) case -ALL: return SP(6, 0) case ASC: return SP(7, 0) } if a < 0 { ctxt.Diag("bad irr opcode -%v", -a) } else { ctxt.Diag("bad irr opcode %v", a) } return 0 }
func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) { o1 := uint32(0) o2 := uint32(0) o3 := uint32(0) o4 := uint32(0) add := AADDU if ctxt.Mode&Mips64 != 0 { add = AADDVU } switch o.type_ { default: ctxt.Diag("unknown type %d %v", o.type_) prasm(p) case 0: /* pseudo ops */ break case 1: /* mov r1,r2 ==> OR r1,r0,r2 */ a := AOR if p.As == AMOVW && ctxt.Mode&Mips64 != 0 { a = AADDU // sign-extended to high 32 bits } o1 = OP_RRR(oprrr(ctxt, a), uint32(REGZERO), uint32(p.From.Reg), uint32(p.To.Reg)) case 2: /* add/sub r1,[r2],r3 */ r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o1 = OP_RRR(oprrr(ctxt, p.As), uint32(p.From.Reg), uint32(r), uint32(p.To.Reg)) case 3: /* mov $soreg, r ==> or/add $i,o,r */ v := regoff(ctxt, &p.From) r := int(p.From.Reg) if r == 0 { r = int(o.param) } a := add if o.a1 == C_ANDCON { a = AOR } o1 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(r), uint32(p.To.Reg)) case 4: /* add $scon,[r1],r2 */ v := regoff(ctxt, &p.From) r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o1 = OP_IRR(opirr(ctxt, p.As), uint32(v), uint32(r), uint32(p.To.Reg)) case 5: /* syscall */ o1 = oprrr(ctxt, p.As) case 6: /* beq r1,[r2],sbra */ v := int32(0) if p.Pcond == nil { v = int32(-4) >> 2 } else { v = int32(p.Pcond.Pc-p.Pc-4) >> 2 } if (v<<16)>>16 != v { ctxt.Diag("short branch too far\n%v", p) } o1 = OP_IRR(opirr(ctxt, p.As), uint32(v), uint32(p.From.Reg), uint32(p.Reg)) // for ABFPT and ABFPF only: always fill delay slot with 0 // see comments in func preprocess for details. o2 = 0 case 7: /* mov r, soreg ==> sw o(r) */ r := int(p.To.Reg) if r == 0 { r = int(o.param) } v := regoff(ctxt, &p.To) o1 = OP_IRR(opirr(ctxt, p.As), uint32(v), uint32(r), uint32(p.From.Reg)) case 8: /* mov soreg, r ==> lw o(r) */ r := int(p.From.Reg) if r == 0 { r = int(o.param) } v := regoff(ctxt, &p.From) o1 = OP_IRR(opirr(ctxt, -p.As), uint32(v), uint32(r), uint32(p.To.Reg)) case 9: /* sll r1,[r2],r3 */ r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o1 = OP_RRR(oprrr(ctxt, p.As), uint32(r), uint32(p.From.Reg), uint32(p.To.Reg)) case 10: /* add $con,[r1],r2 ==> mov $con, t; add t,[r1],r2 */ v := regoff(ctxt, &p.From) a := AOR if v < 0 { a = AADDU } o1 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(0), uint32(REGTMP)) r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o2 = OP_RRR(oprrr(ctxt, p.As), uint32(REGTMP), uint32(r), uint32(p.To.Reg)) case 11: /* jmp lbra */ v := int32(0) if aclass(ctxt, &p.To) == C_SBRA && p.To.Sym == nil && p.As == AJMP { // use PC-relative branch for short branches // BEQ R0, R0, sbra if p.Pcond == nil { v = int32(-4) >> 2 } else { v = int32(p.Pcond.Pc-p.Pc-4) >> 2 } if (v<<16)>>16 == v { o1 = OP_IRR(opirr(ctxt, ABEQ), uint32(v), uint32(REGZERO), uint32(REGZERO)) break } } if p.Pcond == nil { v = int32(p.Pc) >> 2 } else { v = int32(p.Pcond.Pc) >> 2 } o1 = OP_JMP(opirr(ctxt, p.As), uint32(v)) if p.To.Sym == nil { p.To.Sym = ctxt.Cursym.Text.From.Sym p.To.Offset = p.Pcond.Pc } rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc) rel.Siz = 4 rel.Sym = p.To.Sym rel.Add = p.To.Offset if p.As == AJAL { rel.Type = obj.R_CALLMIPS } else { rel.Type = obj.R_JMPMIPS } case 12: /* movbs r,r */ v := 16 if p.As == AMOVB { v = 24 } o1 = OP_SRR(opirr(ctxt, ASLL), uint32(v), uint32(p.From.Reg), uint32(p.To.Reg)) o2 = OP_SRR(opirr(ctxt, ASRA), uint32(v), uint32(p.To.Reg), uint32(p.To.Reg)) case 13: /* movbu r,r */ if p.As == AMOVBU { o1 = OP_IRR(opirr(ctxt, AAND), uint32(0xff), uint32(p.From.Reg), uint32(p.To.Reg)) } else { o1 = OP_IRR(opirr(ctxt, AAND), uint32(0xffff), uint32(p.From.Reg), uint32(p.To.Reg)) } case 14: /* movwu r,r */ o1 = OP_SRR(opirr(ctxt, -ASLLV), uint32(0), uint32(p.From.Reg), uint32(p.To.Reg)) o2 = OP_SRR(opirr(ctxt, -ASRLV), uint32(0), uint32(p.To.Reg), uint32(p.To.Reg)) case 15: /* teq $c r,r */ v := regoff(ctxt, &p.From) r := int(p.Reg) if r == 0 { r = REGZERO } /* only use 10 bits of trap code */ o1 = OP_IRR(opirr(ctxt, p.As), (uint32(v)&0x3FF)<<6, uint32(p.Reg), uint32(p.To.Reg)) case 16: /* sll $c,[r1],r2 */ v := regoff(ctxt, &p.From) r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } /* OP_SRR will use only the low 5 bits of the shift value */ if v >= 32 && vshift(p.As) { o1 = OP_SRR(opirr(ctxt, -p.As), uint32(v-32), uint32(r), uint32(p.To.Reg)) } else { o1 = OP_SRR(opirr(ctxt, p.As), uint32(v), uint32(r), uint32(p.To.Reg)) } case 17: o1 = OP_RRR(oprrr(ctxt, p.As), uint32(REGZERO), uint32(p.From.Reg), uint32(p.To.Reg)) case 18: /* jmp [r1],0(r2) */ r := int(p.Reg) if r == 0 { r = int(o.param) } o1 = OP_RRR(oprrr(ctxt, p.As), uint32(0), uint32(p.To.Reg), uint32(r)) rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc) rel.Siz = 0 rel.Type = obj.R_CALLIND case 19: /* mov $lcon,r ==> lu+or */ v := regoff(ctxt, &p.From) o1 = OP_IRR(opirr(ctxt, ALUI), uint32(v>>16), uint32(REGZERO), uint32(p.To.Reg)) o2 = OP_IRR(opirr(ctxt, AOR), uint32(v), uint32(p.To.Reg), uint32(p.To.Reg)) case 20: /* mov lo/hi,r */ a := OP(2, 0) /* mfhi */ if p.From.Reg == REG_LO { a = OP(2, 2) /* mflo */ } o1 = OP_RRR(a, uint32(REGZERO), uint32(REGZERO), uint32(p.To.Reg)) case 21: /* mov r,lo/hi */ a := OP(2, 1) /* mthi */ if p.To.Reg == REG_LO { a = OP(2, 3) /* mtlo */ } o1 = OP_RRR(a, uint32(REGZERO), uint32(p.From.Reg), uint32(REGZERO)) case 22: /* mul r1,r2 [r3]*/ if p.To.Reg != 0 { r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } a := SP(3, 4) | 2 /* mul */ o1 = OP_RRR(a, uint32(p.From.Reg), uint32(r), uint32(p.To.Reg)) } else { o1 = OP_RRR(oprrr(ctxt, p.As), uint32(p.From.Reg), uint32(p.Reg), uint32(REGZERO)) } case 23: /* add $lcon,r1,r2 ==> lu+or+add */ v := regoff(ctxt, &p.From) o1 = OP_IRR(opirr(ctxt, ALUI), uint32(v>>16), uint32(REGZERO), uint32(REGTMP)) o2 = OP_IRR(opirr(ctxt, AOR), uint32(v), uint32(REGTMP), uint32(REGTMP)) r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o3 = OP_RRR(oprrr(ctxt, p.As), uint32(REGTMP), uint32(r), uint32(p.To.Reg)) case 24: /* mov $ucon,r ==> lu r */ v := regoff(ctxt, &p.From) o1 = OP_IRR(opirr(ctxt, ALUI), uint32(v>>16), uint32(REGZERO), uint32(p.To.Reg)) case 25: /* add/and $ucon,[r1],r2 ==> lu $con,t; add t,[r1],r2 */ v := regoff(ctxt, &p.From) o1 = OP_IRR(opirr(ctxt, ALUI), uint32(v>>16), uint32(REGZERO), uint32(REGTMP)) r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o2 = OP_RRR(oprrr(ctxt, p.As), uint32(REGTMP), uint32(r), uint32(p.To.Reg)) case 26: /* mov $lsext/auto/oreg,r ==> lu+or+add */ v := regoff(ctxt, &p.From) o1 = OP_IRR(opirr(ctxt, ALUI), uint32(v>>16), uint32(REGZERO), uint32(REGTMP)) o2 = OP_IRR(opirr(ctxt, AOR), uint32(v), uint32(REGTMP), uint32(REGTMP)) r := int(p.From.Reg) if r == 0 { r = int(o.param) } o3 = OP_RRR(oprrr(ctxt, add), uint32(REGTMP), uint32(r), uint32(p.To.Reg)) case 27: /* mov [sl]ext/auto/oreg,fr ==> lwc1 o(r) */ v := regoff(ctxt, &p.From) r := int(p.From.Reg) if r == 0 { r = int(o.param) } a := -AMOVF if p.As == AMOVD { a = -AMOVD } switch o.size { case 12: o1 = OP_IRR(opirr(ctxt, ALUI), uint32((v+1<<15)>>16), uint32(REGZERO), uint32(REGTMP)) o2 = OP_RRR(oprrr(ctxt, add), uint32(r), uint32(REGTMP), uint32(REGTMP)) o3 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(REGTMP), uint32(p.To.Reg)) case 4: o1 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(r), uint32(p.To.Reg)) } case 28: /* mov fr,[sl]ext/auto/oreg ==> swc1 o(r) */ v := regoff(ctxt, &p.To) r := int(p.To.Reg) if r == 0 { r = int(o.param) } a := AMOVF if p.As == AMOVD { a = AMOVD } switch o.size { case 12: o1 = OP_IRR(opirr(ctxt, ALUI), uint32((v+1<<15)>>16), uint32(REGZERO), uint32(REGTMP)) o2 = OP_RRR(oprrr(ctxt, add), uint32(r), uint32(REGTMP), uint32(REGTMP)) o3 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(REGTMP), uint32(p.From.Reg)) case 4: o1 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(r), uint32(p.From.Reg)) } case 30: /* movw r,fr */ a := SP(2, 1) | (4 << 21) /* mtc1 */ o1 = OP_RRR(a, uint32(p.From.Reg), uint32(0), uint32(p.To.Reg)) case 31: /* movw fr,r */ a := SP(2, 1) | (0 << 21) /* mtc1 */ o1 = OP_RRR(a, uint32(p.To.Reg), uint32(0), uint32(p.From.Reg)) case 32: /* fadd fr1,[fr2],fr3 */ r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o1 = OP_FRRR(oprrr(ctxt, p.As), uint32(p.From.Reg), uint32(r), uint32(p.To.Reg)) case 33: /* fabs fr1, fr3 */ o1 = OP_FRRR(oprrr(ctxt, p.As), uint32(0), uint32(p.From.Reg), uint32(p.To.Reg)) case 34: /* mov $con,fr ==> or/add $i,t; mov t,fr */ v := regoff(ctxt, &p.From) a := AADDU if o.a1 == C_ANDCON { a = AOR } o1 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(0), uint32(REGTMP)) o2 = OP_RRR(SP(2, 1)|(4<<21), uint32(REGTMP), uint32(0), uint32(p.To.Reg)) /* mtc1 */ case 35: /* mov r,lext/auto/oreg ==> sw o(REGTMP) */ v := regoff(ctxt, &p.To) r := int(p.To.Reg) if r == 0 { r = int(o.param) } o1 = OP_IRR(opirr(ctxt, ALUI), uint32((v+1<<15)>>16), uint32(REGZERO), uint32(REGTMP)) o2 = OP_RRR(oprrr(ctxt, add), uint32(r), uint32(REGTMP), uint32(REGTMP)) o3 = OP_IRR(opirr(ctxt, p.As), uint32(v), uint32(REGTMP), uint32(p.From.Reg)) case 36: /* mov lext/auto/oreg,r ==> lw o(REGTMP) */ v := regoff(ctxt, &p.From) r := int(p.From.Reg) if r == 0 { r = int(o.param) } o1 = OP_IRR(opirr(ctxt, ALUI), uint32((v+1<<15)>>16), uint32(REGZERO), uint32(REGTMP)) o2 = OP_RRR(oprrr(ctxt, add), uint32(r), uint32(REGTMP), uint32(REGTMP)) o3 = OP_IRR(opirr(ctxt, -p.As), uint32(v), uint32(REGTMP), uint32(p.To.Reg)) case 37: /* movw r,mr */ a := SP(2, 0) | (4 << 21) /* mtc0 */ if p.As == AMOVV { a = SP(2, 0) | (5 << 21) /* dmtc0 */ } o1 = OP_RRR(a, uint32(p.From.Reg), uint32(0), uint32(p.To.Reg)) case 38: /* movw mr,r */ a := SP(2, 0) | (0 << 21) /* mfc0 */ if p.As == AMOVV { a = SP(2, 0) | (1 << 21) /* dmfc0 */ } o1 = OP_RRR(a, uint32(p.To.Reg), uint32(0), uint32(p.From.Reg)) case 40: /* word */ o1 = uint32(regoff(ctxt, &p.From)) case 41: /* movw f,fcr */ o1 = OP_RRR(SP(2, 1)|(2<<21), uint32(REGZERO), uint32(0), uint32(p.To.Reg)) /* mfcc1 */ o2 = OP_RRR(SP(2, 1)|(6<<21), uint32(p.From.Reg), uint32(0), uint32(p.To.Reg)) /* mtcc1 */ case 42: /* movw fcr,r */ o1 = OP_RRR(SP(2, 1)|(2<<21), uint32(p.To.Reg), uint32(0), uint32(p.From.Reg)) /* mfcc1 */ case 47: /* movv r,fr */ a := SP(2, 1) | (5 << 21) /* dmtc1 */ o1 = OP_RRR(a, uint32(p.From.Reg), uint32(0), uint32(p.To.Reg)) case 48: /* movv fr,r */ a := SP(2, 1) | (1 << 21) /* dmtc1 */ o1 = OP_RRR(a, uint32(p.To.Reg), uint32(0), uint32(p.From.Reg)) case 49: /* undef */ o1 = 52 /* trap -- teq r0, r0 */ /* relocation operations */ case 50: /* mov r,addr ==> lu + add REGSB, REGTMP + sw o(REGTMP) */ o1 = OP_IRR(opirr(ctxt, ALUI), uint32(0), uint32(REGZERO), uint32(REGTMP)) rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc) rel.Siz = 4 rel.Sym = p.To.Sym rel.Add = p.To.Offset rel.Type = obj.R_ADDRMIPSU o2 = OP_IRR(opirr(ctxt, p.As), uint32(0), uint32(REGTMP), uint32(p.From.Reg)) rel2 := obj.Addrel(ctxt.Cursym) rel2.Off = int32(ctxt.Pc + 4) rel2.Siz = 4 rel2.Sym = p.To.Sym rel2.Add = p.To.Offset rel2.Type = obj.R_ADDRMIPS if o.size == 12 { o3 = o2 o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(REGSB), uint32(REGTMP), uint32(REGTMP)) rel2.Off += 4 } case 51: /* mov addr,r ==> lu + add REGSB, REGTMP + lw o(REGTMP) */ o1 = OP_IRR(opirr(ctxt, ALUI), uint32(0), uint32(REGZERO), uint32(REGTMP)) rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc) rel.Siz = 4 rel.Sym = p.From.Sym rel.Add = p.From.Offset rel.Type = obj.R_ADDRMIPSU o2 = OP_IRR(opirr(ctxt, -p.As), uint32(0), uint32(REGTMP), uint32(p.To.Reg)) rel2 := obj.Addrel(ctxt.Cursym) rel2.Off = int32(ctxt.Pc + 4) rel2.Siz = 4 rel2.Sym = p.From.Sym rel2.Add = p.From.Offset rel2.Type = obj.R_ADDRMIPS if o.size == 12 { o3 = o2 o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(REGSB), uint32(REGTMP), uint32(REGTMP)) rel2.Off += 4 } case 52: /* mov $lext, r ==> lu + add REGSB, r + add */ o1 = OP_IRR(opirr(ctxt, ALUI), uint32(0), uint32(REGZERO), uint32(p.To.Reg)) rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc) rel.Siz = 4 rel.Sym = p.From.Sym rel.Add = p.From.Offset rel.Type = obj.R_ADDRMIPSU o2 = OP_IRR(opirr(ctxt, add), uint32(0), uint32(p.To.Reg), uint32(p.To.Reg)) rel2 := obj.Addrel(ctxt.Cursym) rel2.Off = int32(ctxt.Pc + 4) rel2.Siz = 4 rel2.Sym = p.From.Sym rel2.Add = p.From.Offset rel2.Type = obj.R_ADDRMIPS if o.size == 12 { o3 = o2 o2 = OP_RRR(oprrr(ctxt, AADDVU), uint32(REGSB), uint32(p.To.Reg), uint32(p.To.Reg)) rel2.Off += 4 } case 53: /* mov r, tlsvar ==> rdhwr + sw o(r3) */ // clobbers R3 ! // load thread pointer with RDHWR, R3 is used for fast kernel emulation on Linux o1 = (037<<26 + 073) | (29 << 11) | (3 << 16) // rdhwr $29, r3 o2 = OP_IRR(opirr(ctxt, p.As), uint32(0), uint32(REG_R3), uint32(p.From.Reg)) rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc + 4) rel.Siz = 4 rel.Sym = p.To.Sym rel.Add = p.To.Offset rel.Type = obj.R_ADDRMIPSTLS case 54: /* mov tlsvar, r ==> rdhwr + lw o(r3) */ // clobbers R3 ! o1 = (037<<26 + 073) | (29 << 11) | (3 << 16) // rdhwr $29, r3 o2 = OP_IRR(opirr(ctxt, -p.As), uint32(0), uint32(REG_R3), uint32(p.To.Reg)) rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc + 4) rel.Siz = 4 rel.Sym = p.From.Sym rel.Add = p.From.Offset rel.Type = obj.R_ADDRMIPSTLS case 55: /* mov $tlsvar, r ==> rdhwr + add */ // clobbers R3 ! o1 = (037<<26 + 073) | (29 << 11) | (3 << 16) // rdhwr $29, r3 o2 = OP_IRR(opirr(ctxt, add), uint32(0), uint32(REG_R3), uint32(p.To.Reg)) rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc + 4) rel.Siz = 4 rel.Sym = p.From.Sym rel.Add = p.From.Offset rel.Type = obj.R_ADDRMIPSTLS } out[0] = o1 out[1] = o2 out[2] = o3 out[3] = o4 return }
func progedit(ctxt *obj.Link, p *obj.Prog) { // Maintain information about code generation mode. if ctxt.Mode == 0 { ctxt.Mode = ctxt.Arch.Regsize * 8 } p.Mode = int8(ctxt.Mode) switch p.As { case AMODE: if p.From.Type == obj.TYPE_CONST || (p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_NONE) { switch int(p.From.Offset) { case 16, 32, 64: ctxt.Mode = int(p.From.Offset) } } obj.Nopout(p) } // 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 0(AX)(TLS*1), CX // load g 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 0(TLS), CX // load g into CX // // The 2-instruction and 1-instruction forms correspond to the two code // sequences for loading a TLS variable in the local exec model given in "ELF // Handling For Thread-Local Storage". // // We apply this rewrite on systems that support the 1-instruction form. // The decision is made using only the operating system and the -shared flag, // 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. // // When -shared is passed, we leave the code in the 2-instruction form but // assemble (and relocate) them in different ways to generate the initial // exec code sequence. It's a bit of a fluke that this is possible without // rewriting the instructions more comprehensively, and it only does because // we only support a single TLS variable (g). if CanUse1InsnTLS(ctxt) { // Reduce 2-instruction sequence to 1-instruction sequence. // 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.Type == obj.TYPE_REG && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 && ctxt.Headtype != obj.Hsolaris { obj.Nopout(p) } if p.From.Type == obj.TYPE_MEM && p.From.Index == REG_TLS && REG_AX <= p.From.Reg && p.From.Reg <= REG_R15 { p.From.Reg = REG_TLS p.From.Scale = 0 p.From.Index = REG_NONE } if p.To.Type == obj.TYPE_MEM && p.To.Index == REG_TLS && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 { p.To.Reg = REG_TLS p.To.Scale = 0 p.To.Index = REG_NONE } } else { // load_g_cx, below, always inserts the 1-instruction sequence. Rewrite it // as the 2-instruction sequence if necessary. // MOVQ 0(TLS), BX // becomes // MOVQ TLS, BX // MOVQ 0(BX)(TLS*1), BX if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 { q := obj.Appendp(ctxt, p) q.As = p.As q.From = p.From q.From.Type = obj.TYPE_MEM q.From.Reg = p.To.Reg q.From.Index = REG_TLS q.From.Scale = 2 // TODO: use 1 q.To = p.To p.From.Type = obj.TYPE_REG p.From.Reg = REG_TLS p.From.Index = REG_NONE p.From.Offset = 0 } } // TODO: Remove. if ctxt.Headtype == obj.Hwindows && p.Mode == 64 || ctxt.Headtype == obj.Hplan9 { if p.From.Scale == 1 && p.From.Index == REG_TLS { p.From.Scale = 2 } if p.To.Scale == 1 && p.To.Index == REG_TLS { p.To.Scale = 2 } } // Rewrite 0 to $0 in 3rd argument to CMPPS etc. // That's what the tables expect. switch p.As { case ACMPPD, ACMPPS, ACMPSD, ACMPSS: if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE && p.To.Reg == REG_NONE && p.To.Index == REG_NONE && p.To.Sym == nil { p.To.Type = obj.TYPE_CONST } } // Rewrite CALL/JMP/RET to symbol as TYPE_BRANCH. switch p.As { case obj.ACALL, obj.AJMP, obj.ARET: if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil { p.To.Type = obj.TYPE_BRANCH } } // Rewrite MOVL/MOVQ $XXX(FP/SP) as LEAL/LEAQ. if p.From.Type == obj.TYPE_ADDR && (ctxt.Arch.Thechar == '6' || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) { switch p.As { case AMOVL: p.As = ALEAL p.From.Type = obj.TYPE_MEM case AMOVQ: p.As = ALEAQ p.From.Type = obj.TYPE_MEM } } if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { if p.From3 != nil { nacladdr(ctxt, p, p.From3) } nacladdr(ctxt, p, &p.From) nacladdr(ctxt, p, &p.To) } // Rewrite float constants to values stored in memory. switch p.As { // Convert AMOVSS $(0), Xx to AXORPS Xx, Xx case AMOVSS: if p.From.Type == obj.TYPE_FCONST { // f == 0 can't be used here due to -0, so use Float64bits if f := p.From.Val.(float64); math.Float64bits(f) == 0 { if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 { p.As = AXORPS p.From = p.To break } } } fallthrough case AFMOVF, AFADDF, AFSUBF, AFSUBRF, AFMULF, AFDIVF, AFDIVRF, AFCOMF, AFCOMFP, AADDSS, ASUBSS, AMULSS, ADIVSS, ACOMISS, AUCOMISS: if p.From.Type == obj.TYPE_FCONST { f32 := float32(p.From.Val.(float64)) i32 := math.Float32bits(f32) literal := fmt.Sprintf("$f32.%08x", i32) s := obj.Linklookup(ctxt, literal, 0) p.From.Type = obj.TYPE_MEM p.From.Name = obj.NAME_EXTERN p.From.Sym = s p.From.Sym.Local = true p.From.Offset = 0 } case AMOVSD: // Convert AMOVSD $(0), Xx to AXORPS Xx, Xx if p.From.Type == obj.TYPE_FCONST { // f == 0 can't be used here due to -0, so use Float64bits if f := p.From.Val.(float64); math.Float64bits(f) == 0 { if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 { p.As = AXORPS p.From = p.To break } } } fallthrough case AFMOVD, AFADDD, AFSUBD, AFSUBRD, AFMULD, AFDIVD, AFDIVRD, AFCOMD, AFCOMDP, AADDSD, ASUBSD, AMULSD, ADIVSD, ACOMISD, AUCOMISD: if p.From.Type == obj.TYPE_FCONST { i64 := math.Float64bits(p.From.Val.(float64)) literal := fmt.Sprintf("$f64.%016x", i64) s := obj.Linklookup(ctxt, literal, 0) p.From.Type = obj.TYPE_MEM p.From.Name = obj.NAME_EXTERN p.From.Sym = s p.From.Sym.Local = true p.From.Offset = 0 } } if ctxt.Flag_dynlink { rewriteToUseGot(ctxt, p) } if ctxt.Flag_shared != 0 && p.Mode == 32 { rewriteToPcrel(ctxt, p) } }
func progedit(ctxt *obj.Link, p *obj.Prog) { p.From.Class = 0 p.To.Class = 0 // Rewrite B/BL to symbol as TYPE_BRANCH. switch p.As { case AB, ABL, obj.ADUFFZERO, obj.ADUFFCOPY: if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil { p.To.Type = obj.TYPE_BRANCH } } // Replace TLS register fetches on older ARM procesors. switch p.As { // Treat MRC 15, 0, <reg>, C13, C0, 3 specially. case AMRC: if p.To.Offset&0xffff0fff == 0xee1d0f70 { // Because the instruction might be rewriten to a BL which returns in R0 // the register must be zero. if p.To.Offset&0xf000 != 0 { ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line()) } if ctxt.Goarm < 7 { // Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension. if progedit_tlsfallback == nil { progedit_tlsfallback = obj.Linklookup(ctxt, "runtime.read_tls_fallback", 0) } // MOVW LR, R11 p.As = AMOVW p.From.Type = obj.TYPE_REG p.From.Reg = REGLINK p.To.Type = obj.TYPE_REG p.To.Reg = REGTMP // BL runtime.read_tls_fallback(SB) p = obj.Appendp(ctxt, p) p.As = ABL p.To.Type = obj.TYPE_BRANCH p.To.Sym = progedit_tlsfallback p.To.Offset = 0 // MOVW R11, LR p = obj.Appendp(ctxt, p) p.As = AMOVW p.From.Type = obj.TYPE_REG p.From.Reg = REGTMP p.To.Type = obj.TYPE_REG p.To.Reg = REGLINK break } } // Otherwise, MRC/MCR instructions need no further treatment. p.As = AWORD } // Rewrite float constants to values stored in memory. switch p.As { case AMOVF: if p.From.Type == obj.TYPE_FCONST && chipfloat5(ctxt, p.From.Val.(float64)) < 0 && (chipzero5(ctxt, p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { f32 := float32(p.From.Val.(float64)) i32 := math.Float32bits(f32) literal := fmt.Sprintf("$f32.%08x", i32) s := obj.Linklookup(ctxt, literal, 0) p.From.Type = obj.TYPE_MEM p.From.Sym = s p.From.Name = obj.NAME_EXTERN p.From.Offset = 0 } case AMOVD: if p.From.Type == obj.TYPE_FCONST && chipfloat5(ctxt, p.From.Val.(float64)) < 0 && (chipzero5(ctxt, p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { i64 := math.Float64bits(p.From.Val.(float64)) literal := fmt.Sprintf("$f64.%016x", i64) s := obj.Linklookup(ctxt, literal, 0) p.From.Type = obj.TYPE_MEM p.From.Sym = s p.From.Name = obj.NAME_EXTERN p.From.Offset = 0 } } }
func buildop(ctxt *obj.Link) { var n int for i := 0; i < C_NCLASS; i++ { for n = 0; n < C_NCLASS; n++ { if cmp(n, i) { xcmp[i][n] = 1 } } } for n = 0; optab[n].as != obj.AXXX; n++ { } sort.Sort(ocmp(optab[:n])) for i := 0; i < n; i++ { r := optab[i].as r0 := r & obj.AMask oprange[r0].start = optab[i:] for optab[i].as == r { i++ } oprange[r0].stop = optab[i:] i-- switch r { default: ctxt.Diag("unknown op in build: %v", obj.Aconv(int(r))) log.Fatalf("bad code") case AABSF: opset(AMOVFD, r0) opset(AMOVDF, r0) opset(AMOVWF, r0) opset(AMOVFW, r0) opset(AMOVWD, r0) opset(AMOVDW, r0) opset(ANEGF, r0) opset(ANEGD, r0) opset(AABSD, r0) opset(ATRUNCDW, r0) opset(ATRUNCFW, r0) opset(ATRUNCDV, r0) opset(ATRUNCFV, r0) opset(AMOVVF, r0) opset(AMOVFV, r0) opset(AMOVVD, r0) opset(AMOVDV, r0) case AADD: opset(ASGT, r0) opset(ASGTU, r0) opset(AADDU, r0) opset(AADDV, r0) opset(AADDVU, r0) case AADDF: opset(ADIVF, r0) opset(ADIVD, r0) opset(AMULF, r0) opset(AMULD, r0) opset(ASUBF, r0) opset(ASUBD, r0) opset(AADDD, r0) case AAND: opset(AOR, r0) opset(AXOR, r0) case ABEQ: opset(ABNE, r0) case ABLEZ: opset(ABGEZ, r0) opset(ABGEZAL, r0) opset(ABLTZ, r0) opset(ABLTZAL, r0) opset(ABGTZ, r0) case AMOVB: opset(AMOVH, r0) case AMOVBU: opset(AMOVHU, r0) case AMUL: opset(AREM, r0) opset(AREMU, r0) opset(ADIVU, r0) opset(AMULU, r0) opset(ADIV, r0) opset(ADIVV, r0) opset(ADIVVU, r0) opset(AMULV, r0) opset(AMULVU, r0) opset(AREMV, r0) opset(AREMVU, r0) case ASLL: opset(ASRL, r0) opset(ASRA, r0) opset(ASLLV, r0) opset(ASRAV, r0) opset(ASRLV, r0) case ASUB: opset(ASUBU, r0) opset(ASUBV, r0) opset(ASUBVU, r0) opset(ANOR, r0) case ASYSCALL: opset(ATLBP, r0) opset(ATLBR, r0) opset(ATLBWI, r0) opset(ATLBWR, r0) case ACMPEQF: opset(ACMPGTF, r0) opset(ACMPGTD, r0) opset(ACMPGEF, r0) opset(ACMPGED, r0) opset(ACMPEQD, r0) case ABFPT: opset(ABFPF, r0) case AMOVWL: opset(AMOVWR, r0) opset(AMOVVR, r0) opset(AMOVVL, r0) case AMOVW, AMOVD, AMOVF, AMOVV, ABREAK, ARFE, AJAL, AJMP, AMOVWU, AWORD, obj.ANOP, obj.ATEXT, obj.AUNDEF, obj.AUSEFIELD, obj.AFUNCDATA, obj.APCDATA, obj.ADUFFZERO, obj.ADUFFCOPY: break } } }
func oprrr(ctxt *obj.Link, a int) uint32 { switch a { case AADD: return OP(4, 0) case AADDU: return OP(4, 1) case ASGT: return OP(5, 2) case ASGTU: return OP(5, 3) case AAND: return OP(4, 4) case AOR: return OP(4, 5) case AXOR: return OP(4, 6) case ASUB: return OP(4, 2) case ASUBU: return OP(4, 3) case ANOR: return OP(4, 7) case ASLL: return OP(0, 4) case ASRL: return OP(0, 6) case ASRA: return OP(0, 7) case ASLLV: return OP(2, 4) case ASRLV: return OP(2, 6) case ASRAV: return OP(2, 7) case AADDV: return OP(5, 4) case AADDVU: return OP(5, 5) case ASUBV: return OP(5, 6) case ASUBVU: return OP(5, 7) case AREM, ADIV: return OP(3, 2) case AREMU, ADIVU: return OP(3, 3) case AMUL: return OP(3, 0) case AMULU: return OP(3, 1) case AREMV, ADIVV: return OP(3, 6) case AREMVU, ADIVVU: return OP(3, 7) case AMULV: return OP(3, 4) case AMULVU: return OP(3, 5) case AJMP: return OP(1, 0) case AJAL: return OP(1, 1) case ABREAK: return OP(1, 5) case ASYSCALL: return OP(1, 4) case ATLBP: return MMU(1, 0) case ATLBR: return MMU(0, 1) case ATLBWI: return MMU(0, 2) case ATLBWR: return MMU(0, 6) case ARFE: return MMU(2, 0) case ADIVF: return FPF(0, 3) case ADIVD: return FPD(0, 3) case AMULF: return FPF(0, 2) case AMULD: return FPD(0, 2) case ASUBF: return FPF(0, 1) case ASUBD: return FPD(0, 1) case AADDF: return FPF(0, 0) case AADDD: return FPD(0, 0) case ATRUNCFV: return FPF(1, 1) case ATRUNCDV: return FPD(1, 1) case ATRUNCFW: return FPF(1, 5) case ATRUNCDW: return FPD(1, 5) case AMOVFV: return FPF(4, 5) case AMOVDV: return FPD(4, 5) case AMOVVF: return FPV(4, 0) case AMOVVD: return FPV(4, 1) case AMOVFW: return FPF(4, 4) case AMOVDW: return FPD(4, 4) case AMOVWF: return FPW(4, 0) case AMOVDF: return FPD(4, 0) case AMOVWD: return FPW(4, 1) case AMOVFD: return FPF(4, 1) case AABSF: return FPF(0, 5) case AABSD: return FPD(0, 5) case AMOVF: return FPF(0, 6) case AMOVD: return FPD(0, 6) case ANEGF: return FPF(0, 7) case ANEGD: return FPD(0, 7) case ACMPEQF: return FPF(6, 2) case ACMPEQD: return FPD(6, 2) case ACMPGTF: return FPF(7, 4) case ACMPGTD: return FPD(7, 4) case ACMPGEF: return FPF(7, 6) case ACMPGED: return FPD(7, 6) } if a >= ALAST { ctxt.Diag("bad rrr opcode %v+ALAST", obj.Aconv(a-ALAST)) } else { ctxt.Diag("bad rrr opcode %v", obj.Aconv(a)) } return 0 }
func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { var q *obj.Prog var i int var a obj.As loop: if p == nil { return } if p.As == obj.AJMP { q = p.Pcond if q != nil && q.As != obj.ATEXT { /* mark instruction as done and continue layout at target of jump */ p.Mark |= DONE p = q if p.Mark&DONE == 0 { goto loop } } } if p.Mark&DONE != 0 { /* * p goes here, but already used it elsewhere. * copy up to 4 instructions or else branch to other copy. */ i = 0 q = p for ; i < 4; i, q = i+1, q.Link { if q == nil { break } if q == *last { break } a = q.As if a == obj.ANOP { i-- continue } if nofollow(a) || pushpop(a) { break // NOTE(rsc): arm does goto copy } if q.Pcond == nil || q.Pcond.Mark&DONE != 0 { continue } if a == obj.ACALL || a == ALOOP { continue } for { if p.As == obj.ANOP { p = p.Link continue } q = obj.Copyp(ctxt, p) p = p.Link q.Mark |= DONE (*last).Link = q *last = q if q.As != a || q.Pcond == nil || q.Pcond.Mark&DONE != 0 { continue } q.As = relinv(q.As) p = q.Pcond q.Pcond = q.Link q.Link = p xfol(ctxt, q.Link, last) p = q.Link if p.Mark&DONE != 0 { return } goto loop /* */ } } q = ctxt.NewProg() q.As = obj.AJMP q.Lineno = p.Lineno q.To.Type = obj.TYPE_BRANCH q.To.Offset = p.Pc q.Pcond = p p = q } /* emit p */ p.Mark |= DONE (*last).Link = p *last = p a = p.As /* continue loop with what comes after p */ if nofollow(a) { return } if p.Pcond != nil && a != obj.ACALL { /* * some kind of conditional branch. * recurse to follow one path. * continue loop on the other. */ q = obj.Brchain(ctxt, p.Pcond) if q != nil { p.Pcond = q } q = obj.Brchain(ctxt, p.Link) if q != nil { p.Link = q } if p.From.Type == obj.TYPE_CONST { if p.From.Offset == 1 { /* * expect conditional jump to be taken. * rewrite so that's the fall-through case. */ p.As = relinv(a) q = p.Link p.Link = p.Pcond p.Pcond = q } } else { q = p.Link if q.Mark&DONE != 0 { if a != ALOOP { p.As = relinv(a) p.Link = p.Pcond p.Pcond = q } } } xfol(ctxt, p.Link, last) if p.Pcond.Mark&DONE != 0 { return } p = p.Pcond goto loop } p = p.Link goto loop }
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 // 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 { ctxt.Logf("%5.2f noops\n", obj.Cputime()) } 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 } } var mov, add obj.As if ctxt.Mode&Mips64 != 0 { add = AADDV mov = AMOVV } else { add = AADDU mov = AMOVW } autosize := int32(0) var p1 *obj.Prog var p2 *obj.Prog for p := cursym.Text; p != nil; p = p.Link { o := p.As switch o { case obj.ATEXT: autosize = int32(textstksiz + ctxt.FixedFrameSize()) if (p.Mark&LEAF != 0) && autosize <= int32(ctxt.FixedFrameSize()) { autosize = 0 } else if autosize&4 != 0 && ctxt.Mode&Mips64 != 0 { autosize += 4 } p.To.Offset = int64(autosize) - ctxt.FixedFrameSize() if p.From3.Offset&obj.NOSPLIT == 0 { p = stacksplit(ctxt, p, autosize) // emit split check } q = p if autosize != 0 { // Make sure to save link register for non-empty frame, even if // it is a leaf function, so that traceback works. // Store link register before decrement SP, so if a signal comes // during the execution of the function prologue, the traceback // code will not see a half-updated stack frame. q = obj.Appendp(ctxt, q) q.As = mov q.Lineno = p.Lineno q.From.Type = obj.TYPE_REG q.From.Reg = REGLINK q.To.Type = obj.TYPE_MEM q.To.Offset = int64(-autosize) q.To.Reg = REGSP q = obj.Appendp(ctxt, q) q.As = add 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 { ctxt.Logf("save suppressed in: %s\n", cursym.Name) } cursym.Text.Mark |= LEAF } } if cursym.Text.Mark&LEAF != 0 { cursym.Set(obj.AttrLeaf, true) break } 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 // BEQ R1, end // MOV panic_argp(R1), R2 // ADD $(autosize+FIXED_FRAME), R29, R3 // BNE R2, R3, end // ADD $FIXED_FRAME, R29, R2 // MOV 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 = mov 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 = mov 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 = add 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_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 = add q.From.Type = obj.TYPE_CONST q.From.Offset = ctxt.FixedFrameSize() q.Reg = REGSP q.To.Type = obj.TYPE_REG q.To.Reg = REG_R2 q = obj.Appendp(ctxt, q) q.As = mov 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 } retSym := p.To.Sym p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction p.To.Sym = nil if cursym.Text.Mark&LEAF != 0 { if autosize == 0 { p.As = AJMP p.From = obj.Addr{} if retSym != nil { // retjmp p.To.Type = obj.TYPE_BRANCH p.To.Name = obj.NAME_EXTERN p.To.Sym = retSym } else { p.To.Type = obj.TYPE_MEM p.To.Reg = REGLINK p.To.Offset = 0 } p.Mark |= BRANCH break } p.As = add 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 = mov 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 retSym != nil { // retjmp from non-leaf, need to restore LINK register p.To.Reg = REGLINK } if autosize != 0 { q = ctxt.NewProg() q.As = add 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 if retSym != nil { // retjmp q1.To.Type = obj.TYPE_BRANCH q1.To.Name = obj.NAME_EXTERN q1.To.Sym = retSym } else { 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 AADD, AADDU, 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 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 progedit(ctxt *obj.Link, p *obj.Prog) { // Maintain information about code generation mode. if ctxt.Mode == 0 { switch ctxt.Arch.Family { default: ctxt.Diag("unsupported arch family") case sys.MIPS: ctxt.Mode = Mips32 case sys.MIPS64: ctxt.Mode = Mips64 } } p.From.Class = 0 p.To.Class = 0 // Rewrite JMP/JAL to symbol as TYPE_BRANCH. switch p.As { case AJMP, AJAL, ARET, obj.ADUFFZERO, obj.ADUFFCOPY: if p.To.Sym != nil { p.To.Type = obj.TYPE_BRANCH } } // Rewrite float constants to values stored in memory. switch p.As { case AMOVF: if p.From.Type == obj.TYPE_FCONST { f32 := float32(p.From.Val.(float64)) i32 := math.Float32bits(f32) if i32 == 0 { p.As = AMOVW p.From.Type = obj.TYPE_REG p.From.Reg = REGZERO break } literal := fmt.Sprintf("$f32.%08x", i32) s := obj.Linklookup(ctxt, literal, 0) s.Size = 4 p.From.Type = obj.TYPE_MEM p.From.Sym = s p.From.Name = obj.NAME_EXTERN p.From.Offset = 0 } case AMOVD: if p.From.Type == obj.TYPE_FCONST { i64 := math.Float64bits(p.From.Val.(float64)) if i64 == 0 && ctxt.Mode&Mips64 != 0 { p.As = AMOVV p.From.Type = obj.TYPE_REG p.From.Reg = REGZERO break } literal := fmt.Sprintf("$f64.%016x", i64) s := obj.Linklookup(ctxt, literal, 0) s.Size = 8 p.From.Type = obj.TYPE_MEM p.From.Sym = s p.From.Name = obj.NAME_EXTERN p.From.Offset = 0 } // Put >32-bit constants in memory and load them case AMOVV: if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == 0 && int64(int32(p.From.Offset)) != p.From.Offset { literal := fmt.Sprintf("$i64.%016x", uint64(p.From.Offset)) s := obj.Linklookup(ctxt, literal, 0) s.Size = 8 p.From.Type = obj.TYPE_MEM p.From.Sym = s p.From.Name = obj.NAME_EXTERN p.From.Offset = 0 } } // Rewrite SUB constants into ADD. switch p.As { case ASUB: if p.From.Type == obj.TYPE_CONST { p.From.Offset = -p.From.Offset p.As = AADD } case ASUBU: if p.From.Type == obj.TYPE_CONST { p.From.Offset = -p.From.Offset p.As = AADDU } case ASUBV: if p.From.Type == obj.TYPE_CONST { p.From.Offset = -p.From.Offset p.As = AADDV } case ASUBVU: if p.From.Type == obj.TYPE_CONST { p.From.Offset = -p.From.Offset p.As = AADDVU } } }
func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) { o1 := uint32(0) o2 := uint32(0) o3 := uint32(0) o4 := uint32(0) switch o.type_ { default: ctxt.Diag("unknown type %d %v", o.type_) prasm(p) case 0: /* pseudo ops */ break case 1: /* mov r1,r2 ==> OR r1,r0,r2 */ o1 = OP_RRR(oprrr(ctxt, AOR), uint32(p.From.Reg), uint32(REGZERO), uint32(p.To.Reg)) case 2: /* add/sub r1,[r2],r3 */ r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o1 = OP_RRR(oprrr(ctxt, int(p.As)), uint32(p.From.Reg), uint32(r), uint32(p.To.Reg)) case 3: /* mov $soreg, r ==> or/add $i,o,r */ v := regoff(ctxt, &p.From) r := int(p.From.Reg) if r == 0 { r = int(o.param) } a := AADDVU if o.a1 == C_ANDCON { a = AOR } o1 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(r), uint32(p.To.Reg)) case 4: /* add $scon,[r1],r2 */ v := regoff(ctxt, &p.From) r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o1 = OP_IRR(opirr(ctxt, int(p.As)), uint32(v), uint32(r), uint32(p.To.Reg)) case 5: /* syscall */ o1 = uint32(oprrr(ctxt, int(p.As))) case 6: /* beq r1,[r2],sbra */ v := int32(0) if p.Pcond == nil { v = int32(-4) >> 2 } else { v = int32(p.Pcond.Pc-p.Pc-4) >> 2 } if (v<<16)>>16 != v { ctxt.Diag("short branch too far\n%v", p) } o1 = OP_IRR(opirr(ctxt, int(p.As)), uint32(v), uint32(p.From.Reg), uint32(p.Reg)) // for ABFPT and ABFPF only: always fill delay slot with 0 // see comments in func preprocess for details. o2 = 0 case 7: /* mov r, soreg ==> sw o(r) */ r := int(p.To.Reg) if r == 0 { r = int(o.param) } v := regoff(ctxt, &p.To) o1 = OP_IRR(opirr(ctxt, int(p.As)), uint32(v), uint32(r), uint32(p.From.Reg)) case 8: /* mov soreg, r ==> lw o(r) */ r := int(p.From.Reg) if r == 0 { r = int(o.param) } v := regoff(ctxt, &p.From) o1 = OP_IRR(opirr(ctxt, int(p.As)+ALAST), uint32(v), uint32(r), uint32(p.To.Reg)) case 9: /* sll r1,[r2],r3 */ r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o1 = OP_RRR(oprrr(ctxt, int(p.As)), uint32(r), uint32(p.From.Reg), uint32(p.To.Reg)) case 10: /* add $con,[r1],r2 ==> mov $con, t; add t,[r1],r2 */ v := regoff(ctxt, &p.From) a := AOR if v < 0 { a = AADDU } o1 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(0), uint32(REGTMP)) r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o2 = OP_RRR(oprrr(ctxt, int(p.As)), uint32(REGTMP), uint32(r), uint32(p.To.Reg)) case 11: /* jmp lbra */ v := int32(0) if aclass(ctxt, &p.To) == C_SBRA && p.To.Sym == nil && p.As == AJMP { // use PC-relative branch for short branches // BEQ R0, R0, sbra if p.Pcond == nil { v = int32(-4) >> 2 } else { v = int32(p.Pcond.Pc-p.Pc-4) >> 2 } if (v<<16)>>16 == v { o1 = OP_IRR(opirr(ctxt, ABEQ), uint32(v), uint32(REGZERO), uint32(REGZERO)) break } } if p.Pcond == nil { v = int32(p.Pc) >> 2 } else { v = int32(p.Pcond.Pc) >> 2 } o1 = OP_JMP(opirr(ctxt, int(p.As)), uint32(v)) if p.To.Sym == nil { p.To.Sym = ctxt.Cursym.Text.From.Sym p.To.Offset = p.Pcond.Pc } rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc) rel.Siz = 4 rel.Sym = p.To.Sym rel.Add = p.To.Offset if p.As == AJAL { rel.Type = obj.R_CALLMIPS } else { rel.Type = obj.R_JMPMIPS } case 12: /* movbs r,r */ v := 16 if p.As == AMOVB { v = 24 } o1 = OP_SRR(opirr(ctxt, ASLL), uint32(v), uint32(p.From.Reg), uint32(p.To.Reg)) o2 = OP_SRR(opirr(ctxt, ASRA), uint32(v), uint32(p.To.Reg), uint32(p.To.Reg)) case 13: /* movbu r,r */ if p.As == AMOVBU { o1 = OP_IRR(opirr(ctxt, AAND), uint32(0xff), uint32(p.From.Reg), uint32(p.To.Reg)) } else { o1 = OP_IRR(opirr(ctxt, AAND), uint32(0xffff), uint32(p.From.Reg), uint32(p.To.Reg)) } case 14: /* movwu r,r */ o1 = OP_SRR(opirr(ctxt, ASLLV+ALAST), uint32(0), uint32(p.From.Reg), uint32(p.To.Reg)) if p.As == AMOVWU { o2 = OP_SRR(opirr(ctxt, ASRLV+ALAST), uint32(0), uint32(p.To.Reg), uint32(p.To.Reg)) } else { o2 = OP_SRR(opirr(ctxt, ASRAV+ALAST), uint32(0), uint32(p.To.Reg), uint32(p.To.Reg)) } case 16: /* sll $c,[r1],r2 */ v := regoff(ctxt, &p.From) r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } /* OP_SRR will use only the low 5 bits of the shift value */ if v >= 32 && vshift(p.As) { o1 = OP_SRR(opirr(ctxt, int(p.As)+ALAST), uint32(v-32), uint32(r), uint32(p.To.Reg)) } else { o1 = OP_SRR(opirr(ctxt, int(p.As)), uint32(v), uint32(r), uint32(p.To.Reg)) } case 18: /* jmp [r1],0(r2) */ r := int(p.Reg) if r == 0 { r = int(o.param) } o1 = OP_RRR(oprrr(ctxt, int(p.As)), uint32(0), uint32(p.To.Reg), uint32(r)) rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc) rel.Siz = 0 rel.Type = obj.R_CALLIND case 19: /* mov $lcon,r ==> lu+or */ v := regoff(ctxt, &p.From) o1 = OP_IRR(opirr(ctxt, ALAST), uint32(v>>16), uint32(REGZERO), uint32(p.To.Reg)) o2 = OP_IRR(opirr(ctxt, AOR), uint32(v), uint32(p.To.Reg), uint32(p.To.Reg)) if p.From.Sym != nil { rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc) rel.Siz = 8 rel.Sym = p.From.Sym rel.Add = p.From.Offset rel.Type = obj.R_ADDRMIPS } case 20: /* mov lo/hi,r */ a := OP(2, 0) /* mfhi */ if p.From.Reg == REG_LO { a = OP(2, 2) /* mflo */ } o1 = OP_RRR(a, uint32(REGZERO), uint32(REGZERO), uint32(p.To.Reg)) case 21: /* mov r,lo/hi */ a := OP(2, 1) /* mthi */ if p.To.Reg == REG_LO { a = OP(2, 3) /* mtlo */ } o1 = OP_RRR(a, uint32(REGZERO), uint32(p.From.Reg), uint32(REGZERO)) case 22: /* mul r1,r2 */ o1 = OP_RRR(oprrr(ctxt, int(p.As)), uint32(p.From.Reg), uint32(p.Reg), uint32(REGZERO)) case 23: /* add $lcon,r1,r2 ==> lu+or+add */ v := regoff(ctxt, &p.From) o1 = OP_IRR(opirr(ctxt, ALAST), uint32(v>>16), uint32(REGZERO), uint32(REGTMP)) o2 = OP_IRR(opirr(ctxt, AOR), uint32(v), uint32(REGTMP), uint32(REGTMP)) r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o3 = OP_RRR(oprrr(ctxt, int(p.As)), uint32(REGTMP), uint32(r), uint32(p.To.Reg)) case 24: /* mov $ucon,r ==> lu r */ v := regoff(ctxt, &p.From) o1 = OP_IRR(opirr(ctxt, ALAST), uint32(v>>16), uint32(REGZERO), uint32(p.To.Reg)) case 25: /* add/and $ucon,[r1],r2 ==> lu $con,t; add t,[r1],r2 */ v := regoff(ctxt, &p.From) o1 = OP_IRR(opirr(ctxt, ALAST), uint32(v>>16), uint32(REGZERO), uint32(REGTMP)) r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o2 = OP_RRR(oprrr(ctxt, int(p.As)), uint32(REGTMP), uint32(r), uint32(p.To.Reg)) case 26: /* mov $lsext/auto/oreg,r ==> lu+or+add */ v := regoff(ctxt, &p.From) o1 = OP_IRR(opirr(ctxt, ALAST), uint32(v>>16), uint32(REGZERO), uint32(REGTMP)) o2 = OP_IRR(opirr(ctxt, AOR), uint32(v), uint32(REGTMP), uint32(REGTMP)) r := int(p.From.Reg) if r == 0 { r = int(o.param) } o3 = OP_RRR(oprrr(ctxt, AADDVU), uint32(REGTMP), uint32(r), uint32(p.To.Reg)) case 27: /* mov [sl]ext/auto/oreg,fr ==> lwc1 o(r) */ v := regoff(ctxt, &p.From) r := int(p.From.Reg) if r == 0 { r = int(o.param) } a := AMOVF + ALAST if p.As == AMOVD { a = AMOVD + ALAST } switch o.size { case 16: o1 = OP_IRR(opirr(ctxt, ALAST), uint32(v>>16), uint32(REGZERO), uint32(REGTMP)) o2 = OP_IRR(opirr(ctxt, AOR), uint32(v), uint32(REGTMP), uint32(REGTMP)) o3 = OP_RRR(opirr(ctxt, AADDVU), uint32(r), uint32(REGTMP), uint32(REGTMP)) o4 = OP_IRR(opirr(ctxt, a), uint32(0), uint32(r), uint32(p.To.Reg)) case 4: o1 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(r), uint32(p.To.Reg)) } case 28: /* mov fr,[sl]ext/auto/oreg ==> swc1 o(r) */ v := regoff(ctxt, &p.To) r := int(p.To.Reg) if r == 0 { r = int(o.param) } a := AMOVF if p.As == AMOVD { a = AMOVD } switch o.size { case 16: o1 = OP_IRR(opirr(ctxt, ALAST), uint32(v>>16), uint32(REGZERO), uint32(REGTMP)) o2 = OP_IRR(opirr(ctxt, AOR), uint32(v), uint32(REGTMP), uint32(REGTMP)) o3 = OP_RRR(opirr(ctxt, AADDVU), uint32(r), uint32(REGTMP), uint32(REGTMP)) o4 = OP_IRR(opirr(ctxt, a), uint32(0), uint32(REGTMP), uint32(p.From.Reg)) case 4: o1 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(r), uint32(p.From.Reg)) } case 30: /* movw r,fr */ a := SP(2, 1) | (4 << 21) /* mtc1 */ o1 = OP_RRR(a, uint32(p.From.Reg), uint32(0), uint32(p.To.Reg)) case 31: /* movw fr,r */ a := SP(2, 1) | (0 << 21) /* mtc1 */ o1 = OP_RRR(a, uint32(p.To.Reg), uint32(0), uint32(p.From.Reg)) case 32: /* fadd fr1,[fr2],fr3 */ r := int(p.Reg) if r == 0 { r = int(p.To.Reg) } o1 = OP_FRRR(oprrr(ctxt, int(p.As)), uint32(p.From.Reg), uint32(r), uint32(p.To.Reg)) case 33: /* fabs fr1, fr3 */ o1 = OP_FRRR(oprrr(ctxt, int(p.As)), uint32(0), uint32(p.From.Reg), uint32(p.To.Reg)) case 34: /* mov $con,fr ==> or/add $i,t; mov t,fr */ v := regoff(ctxt, &p.From) a := AADDU if o.a1 == C_ANDCON { a = AOR } o1 = OP_IRR(opirr(ctxt, a), uint32(v), uint32(0), uint32(REGTMP)) o2 = OP_RRR(SP(2, 1)|(4<<21), uint32(REGTMP), uint32(0), uint32(p.To.Reg)) /* mtc1 */ case 35: /* mov r,lext/auto/oreg ==> sw o(r) */ v := regoff(ctxt, &p.To) r := int(p.To.Reg) if r == 0 { r = int(o.param) } o1 = OP_IRR(opirr(ctxt, ALAST), uint32(v>>16), uint32(REGZERO), uint32(REGTMP)) o2 = OP_IRR(opirr(ctxt, AOR), uint32(v), uint32(REGTMP), uint32(REGTMP)) o3 = OP_RRR(oprrr(ctxt, AADDVU), uint32(r), uint32(REGTMP), uint32(REGTMP)) o4 = OP_IRR(opirr(ctxt, int(p.As)), uint32(0), uint32(REGTMP), uint32(p.From.Reg)) case 36: /* mov lext/auto/oreg,r ==> lw o(r30) */ v := regoff(ctxt, &p.From) r := int(p.From.Reg) if r == 0 { r = int(o.param) } o1 = OP_IRR(opirr(ctxt, ALAST), uint32(v>>16), uint32(REGZERO), uint32(REGTMP)) o2 = OP_IRR(opirr(ctxt, AOR), uint32(v), uint32(REGTMP), uint32(REGTMP)) o3 = OP_RRR(oprrr(ctxt, AADDVU), uint32(r), uint32(REGTMP), uint32(REGTMP)) o4 = OP_IRR(opirr(ctxt, int(p.As)+ALAST), uint32(0), uint32(REGTMP), uint32(p.To.Reg)) case 37: /* movw r,mr */ a := SP(2, 0) | (4 << 21) /* mtc0 */ if p.As == AMOVV { a = SP(2, 0) | (5 << 21) /* dmtc0 */ } o1 = OP_RRR(a, uint32(p.From.Reg), uint32(0), uint32(p.To.Reg)) case 38: /* movw mr,r */ a := SP(2, 0) | (0 << 21) /* mfc0 */ if p.As == AMOVV { a = SP(2, 0) | (1 << 21) /* dmfc0 */ } o1 = OP_RRR(a, uint32(p.To.Reg), uint32(0), uint32(p.From.Reg)) case 40: /* word */ o1 = uint32(regoff(ctxt, &p.From)) case 41: /* movw f,fcr */ o1 = OP_RRR(SP(2, 1)|(2<<21), uint32(REGZERO), uint32(0), uint32(p.To.Reg)) /* mfcc1 */ o2 = OP_RRR(SP(2, 1)|(6<<21), uint32(p.From.Reg), uint32(0), uint32(p.To.Reg)) /* mtcc1 */ case 42: /* movw fcr,r */ o1 = OP_RRR(SP(2, 1)|(2<<21), uint32(p.To.Reg), uint32(0), uint32(p.From.Reg)) /* mfcc1 */ case 47: /* movv r,fr */ a := SP(2, 1) | (5 << 21) /* dmtc1 */ o1 = OP_RRR(a, uint32(p.From.Reg), uint32(0), uint32(p.To.Reg)) case 48: /* movv fr,r */ a := SP(2, 1) | (1 << 21) /* dmtc1 */ o1 = OP_RRR(a, uint32(p.To.Reg), uint32(0), uint32(p.From.Reg)) case 49: /* undef */ o1 = 8 /* JMP (R0) */ /* relocation operations */ case 50: /* mov r,addr ==> lu + or + sw (REGTMP) */ o1 = OP_IRR(opirr(ctxt, ALAST), uint32(0), uint32(REGZERO), uint32(REGTMP)) o2 = OP_IRR(opirr(ctxt, AOR), uint32(0), uint32(REGTMP), uint32(REGTMP)) rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc) rel.Siz = 8 rel.Sym = p.To.Sym rel.Add = p.To.Offset rel.Type = obj.R_ADDRMIPS o3 = OP_IRR(opirr(ctxt, int(p.As)), uint32(0), uint32(REGTMP), uint32(p.From.Reg)) case 51: /* mov addr,r ==> lu + or + lw (REGTMP) */ o1 = OP_IRR(opirr(ctxt, ALAST), uint32(0), uint32(REGZERO), uint32(REGTMP)) o2 = OP_IRR(opirr(ctxt, AOR), uint32(0), uint32(REGTMP), uint32(REGTMP)) rel := obj.Addrel(ctxt.Cursym) rel.Off = int32(ctxt.Pc) rel.Siz = 8 rel.Sym = p.From.Sym rel.Add = p.From.Offset rel.Type = obj.R_ADDRMIPS o3 = OP_IRR(opirr(ctxt, int(p.As)+ALAST), uint32(0), uint32(REGTMP), uint32(p.To.Reg)) } out[0] = o1 out[1] = o2 out[2] = o3 out[3] = o4 return }
func progedit(ctxt *obj.Link, p *obj.Prog) { p.From.Class = 0 p.To.Class = 0 // Rewrite B/BL to symbol as TYPE_BRANCH. switch p.As { case AB, ABL, obj.ADUFFZERO, obj.ADUFFCOPY: if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil { p.To.Type = obj.TYPE_BRANCH } } // Replace TLS register fetches on older ARM procesors. switch p.As { // Treat MRC 15, 0, <reg>, C13, C0, 3 specially. case AMRC: if p.To.Offset&0xffff0fff == 0xee1d0f70 { // Because the instruction might be rewriten to a BL which returns in R0 // the register must be zero. if p.To.Offset&0xf000 != 0 { ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line()) } if ctxt.Goarm < 7 { // Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension. if progedit_tlsfallback == nil { progedit_tlsfallback = obj.Linklookup(ctxt, "runtime.read_tls_fallback", 0) } // MOVW LR, R11 p.As = AMOVW p.From.Type = obj.TYPE_REG p.From.Reg = REGLINK p.To.Type = obj.TYPE_REG p.To.Reg = REGTMP // BL runtime.read_tls_fallback(SB) p = obj.Appendp(ctxt, p) p.As = ABL p.To.Type = obj.TYPE_BRANCH p.To.Sym = progedit_tlsfallback p.To.Offset = 0 // MOVW R11, LR p = obj.Appendp(ctxt, p) p.As = AMOVW p.From.Type = obj.TYPE_REG p.From.Reg = REGTMP p.To.Type = obj.TYPE_REG p.To.Reg = REGLINK break } } // Otherwise, MRC/MCR instructions need no further treatment. p.As = AWORD } // Rewrite float constants to values stored in memory. switch p.As { case AMOVF: if p.From.Type == obj.TYPE_FCONST && chipfloat5(ctxt, p.From.U.Dval) < 0 && (chipzero5(ctxt, p.From.U.Dval) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { f32 := float32(p.From.U.Dval) i32 := math.Float32bits(f32) literal := fmt.Sprintf("$f32.%08x", i32) s := obj.Linklookup(ctxt, literal, 0) if s.Type == 0 { s.Type = obj.SRODATA obj.Adduint32(ctxt, s, i32) s.Reachable = 0 } p.From.Type = obj.TYPE_MEM p.From.Sym = s p.From.Name = obj.NAME_EXTERN p.From.Offset = 0 } case AMOVD: if p.From.Type == obj.TYPE_FCONST && chipfloat5(ctxt, p.From.U.Dval) < 0 && (chipzero5(ctxt, p.From.U.Dval) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { i64 := math.Float64bits(p.From.U.Dval) literal := fmt.Sprintf("$f64.%016x", i64) s := obj.Linklookup(ctxt, literal, 0) if s.Type == 0 { s.Type = obj.SRODATA obj.Adduint64(ctxt, s, i64) s.Reachable = 0 } p.From.Type = obj.TYPE_MEM p.From.Sym = s p.From.Name = obj.NAME_EXTERN p.From.Offset = 0 } } 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 = obj.Linklookup(ctxt, "runtime.tlsg", 0) } if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && p.From.Sym == ctxt.Tlsg { p.From.Type = obj.TYPE_MEM } if p.To.Type == obj.TYPE_ADDR && p.To.Name == obj.NAME_EXTERN && p.To.Sym == ctxt.Tlsg { p.To.Type = obj.TYPE_MEM } } }