func progedit(ctxt *liblink.Link, p *liblink.Prog) { var literal string var s *liblink.LSym var q *liblink.Prog // See obj6.c for discussion of TLS. if canuselocaltls(ctxt) != 0 { // Reduce TLS initial exec model to TLS local exec model. // Sequences like // MOVL TLS, BX // ... off(BX)(TLS*1) ... // become // NOP // ... off(TLS) ... if p.As == AMOVL && p.From.Type_ == D_TLS && D_AX <= p.To.Type_ && p.To.Type_ <= D_DI { p.As = ANOP p.From.Type_ = D_NONE p.To.Type_ = D_NONE } if p.From.Index == D_TLS && D_INDIR+D_AX <= p.From.Type_ && p.From.Type_ <= D_INDIR+D_DI { p.From.Type_ = D_INDIR + D_TLS p.From.Scale = 0 p.From.Index = D_NONE } if p.To.Index == D_TLS && D_INDIR+D_AX <= p.To.Type_ && p.To.Type_ <= D_INDIR+D_DI { p.To.Type_ = D_INDIR + D_TLS p.To.Scale = 0 p.To.Index = D_NONE } } else { // As a courtesy to the C compilers, rewrite TLS local exec load as TLS initial exec load. // The instruction // MOVL off(TLS), BX // becomes the sequence // MOVL TLS, BX // MOVL off(BX)(TLS*1), BX // This allows the C compilers to emit references to m and g using the direct off(TLS) form. if p.As == AMOVL && p.From.Type_ == D_INDIR+D_TLS && D_AX <= p.To.Type_ && p.To.Type_ <= D_DI { q = liblink.Appendp(ctxt, p) q.As = p.As q.From = p.From q.From.Type_ = D_INDIR + p.To.Type_ q.From.Index = D_TLS q.From.Scale = 2 // TODO: use 1 q.To = p.To p.From.Type_ = D_TLS p.From.Index = D_NONE p.From.Offset = 0 } } // TODO: Remove. if ctxt.Headtype == liblink.Hplan9 { if p.From.Scale == 1 && p.From.Index == D_TLS { p.From.Scale = 2 } if p.To.Scale == 1 && p.To.Index == D_TLS { p.To.Scale = 2 } } // Rewrite CALL/JMP/RET to symbol as D_BRANCH. switch p.As { case ACALL, AJMP, ARET: if (p.To.Type_ == D_EXTERN || p.To.Type_ == D_STATIC) && p.To.Sym != nil { p.To.Type_ = D_BRANCH } break } // 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_ == D_FCONST { if p.From.U.Dval == 0 { if p.To.Type_ >= D_X0 { if p.To.Type_ <= D_X7 { p.As = AXORPS p.From.Type_ = p.To.Type_ p.From.Index = p.To.Index break } } } } fallthrough // fallthrough case AFMOVF, AFADDF, AFSUBF, AFSUBRF, AFMULF, AFDIVF, AFDIVRF, AFCOMF, AFCOMFP, AADDSS, ASUBSS, AMULSS, ADIVSS, ACOMISS, AUCOMISS: if p.From.Type_ == D_FCONST { var i32 uint32 var f32 float32 f32 = float32(p.From.U.Dval) i32 = math.Float32bits(f32) literal = fmt.Sprintf("$f32.%08x", i32) s = liblink.Linklookup(ctxt, literal, 0) if s.Type_ == 0 { s.Type_ = liblink.SRODATA liblink.Adduint32(ctxt, s, i32) s.Reachable = 0 } p.From.Type_ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } // Convert AMOVSD $(0), Xx to AXORPS Xx, Xx case AMOVSD: if p.From.Type_ == D_FCONST { if p.From.U.Dval == 0 { if p.To.Type_ >= D_X0 { if p.To.Type_ <= D_X7 { p.As = AXORPS p.From.Type_ = p.To.Type_ p.From.Index = p.To.Index break } } } } fallthrough // fallthrough case AFMOVD, AFADDD, AFSUBD, AFSUBRD, AFMULD, AFDIVD, AFDIVRD, AFCOMD, AFCOMDP, AADDSD, ASUBSD, AMULSD, ADIVSD, ACOMISD, AUCOMISD: if p.From.Type_ == D_FCONST { var i64 uint64 i64 = math.Float64bits(p.From.U.Dval) literal = fmt.Sprintf("$f64.%016x", i64) s = liblink.Linklookup(ctxt, literal, 0) if s.Type_ == 0 { s.Type_ = liblink.SRODATA liblink.Adduint64(ctxt, s, i64) s.Reachable = 0 } p.From.Type_ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } break } }
func progedit(ctxt *liblink.Link, p *liblink.Prog) { var literal string var s *liblink.LSym var tlsfallback *liblink.LSym p.From.Class = 0 p.To.Class = 0 // Rewrite B/BL to symbol as D_BRANCH. switch p.As { case AB, ABL, ADUFFZERO, ADUFFCOPY: if p.To.Type_ == D_OREG && (p.To.Name == D_EXTERN || p.To.Name == D_STATIC) && p.To.Sym != nil { p.To.Type_ = D_BRANCH } break } // 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("%L: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Lineno) } if ctxt.Goarm < 7 { // Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension. if tlsfallback == nil { tlsfallback = liblink.Linklookup(ctxt, "runtime.read_tls_fallback", 0) } // MOVW LR, R11 p.As = AMOVW p.From.Type_ = D_REG p.From.Reg = REGLINK p.To.Type_ = D_REG p.To.Reg = REGTMP // BL runtime.read_tls_fallback(SB) p = liblink.Appendp(ctxt, p) p.As = ABL p.To.Type_ = D_BRANCH p.To.Sym = tlsfallback p.To.Offset = 0 // MOVW R11, LR p = liblink.Appendp(ctxt, p) p.As = AMOVW p.From.Type_ = D_REG p.From.Reg = REGTMP p.To.Type_ = D_REG p.To.Reg = REGLINK break } } // Otherwise, MRC/MCR instructions need no further treatment. p.As = AWORD break } // Rewrite float constants to values stored in memory. switch p.As { case AMOVF: if p.From.Type_ == D_FCONST && chipfloat5(ctxt, p.From.U.Dval) < 0 && (chipzero5(ctxt, p.From.U.Dval) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { var i32 uint32 var f32 float32 f32 = float32(p.From.U.Dval) i32 = math.Float32bits(f32) literal = fmt.Sprintf("$f32.%08x", i32) s = liblink.Linklookup(ctxt, literal, 0) if s.Type_ == 0 { s.Type_ = liblink.SRODATA liblink.Adduint32(ctxt, s, i32) s.Reachable = 0 } p.From.Type_ = D_OREG p.From.Sym = s p.From.Name = D_EXTERN p.From.Offset = 0 } case AMOVD: if p.From.Type_ == D_FCONST && chipfloat5(ctxt, p.From.U.Dval) < 0 && (chipzero5(ctxt, p.From.U.Dval) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) { var i64 uint64 i64 = math.Float64bits(p.From.U.Dval) literal = fmt.Sprintf("$f64.%016x", i64) s = liblink.Linklookup(ctxt, literal, 0) if s.Type_ == 0 { s.Type_ = liblink.SRODATA liblink.Adduint64(ctxt, s, i64) s.Reachable = 0 } p.From.Type_ = D_OREG p.From.Sym = s p.From.Name = D_EXTERN p.From.Offset = 0 } break } if ctxt.Flag_shared != 0 { // Shared libraries use R_ARM_TLS_IE32 instead of // R_ARM_TLS_LE32, replacing the link time constant TLS offset in // runtime.tlsg with an address to a GOT entry containing the // offset. Rewrite $runtime.tlsg(SB) to runtime.tlsg(SB) to // compensate. if ctxt.Tlsg == nil { ctxt.Tlsg = liblink.Linklookup(ctxt, "runtime.tlsg", 0) } if p.From.Type_ == D_CONST && p.From.Name == D_EXTERN && p.From.Sym == ctxt.Tlsg { p.From.Type_ = D_OREG } if p.To.Type_ == D_CONST && p.To.Name == D_EXTERN && p.To.Sym == ctxt.Tlsg { p.To.Type_ = D_OREG } } }
func progedit(ctxt *liblink.Link, p *liblink.Prog) { var literal string var s *liblink.LSym var q *liblink.Prog // Thread-local storage references use the TLS pseudo-register. // As a register, TLS refers to the thread-local storage base, and it // can only be loaded into another register: // // MOVQ TLS, AX // // An offset from the thread-local storage base is written off(reg)(TLS*1). // Semantically it is off(reg), but the (TLS*1) annotation marks this as // indexing from the loaded TLS base. This emits a relocation so that // if the linker needs to adjust the offset, it can. For example: // // MOVQ TLS, AX // MOVQ 8(AX)(TLS*1), CX // load m into CX // // On systems that support direct access to the TLS memory, this // pair of instructions can be reduced to a direct TLS memory reference: // // MOVQ 8(TLS), CX // load m into CX // // The 2-instruction and 1-instruction forms correspond roughly to // ELF TLS initial exec mode and ELF TLS local exec mode, respectively. // // We applies this rewrite on systems that support the 1-instruction form. // The decision is made using only the operating system (and probably // the -shared flag, eventually), not the link mode. If some link modes // on a particular operating system require the 2-instruction form, // then all builds for that operating system will use the 2-instruction // form, so that the link mode decision can be delayed to link time. // // In this way, all supported systems use identical instructions to // access TLS, and they are rewritten appropriately first here in // liblink and then finally using relocations in the linker. if canuselocaltls(ctxt) != 0 { // Reduce TLS initial exec model to TLS local exec model. // Sequences like // MOVQ TLS, BX // ... off(BX)(TLS*1) ... // become // NOP // ... off(TLS) ... // // TODO(rsc): Remove the Hsolaris special case. It exists only to // guarantee we are producing byte-identical binaries as before this code. // But it should be unnecessary. if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type_ == D_TLS && D_AX <= p.To.Type_ && p.To.Type_ <= D_R15 && ctxt.Headtype != liblink.Hsolaris { nopout(p) } if p.From.Index == D_TLS && D_INDIR+D_AX <= p.From.Type_ && p.From.Type_ <= D_INDIR+D_R15 { p.From.Type_ = D_INDIR + D_TLS p.From.Scale = 0 p.From.Index = D_NONE } if p.To.Index == D_TLS && D_INDIR+D_AX <= p.To.Type_ && p.To.Type_ <= D_INDIR+D_R15 { p.To.Type_ = D_INDIR + D_TLS p.To.Scale = 0 p.To.Index = D_NONE } } else { // As a courtesy to the C compilers, rewrite TLS local exec load as TLS initial exec load. // The instruction // MOVQ off(TLS), BX // becomes the sequence // MOVQ TLS, BX // MOVQ off(BX)(TLS*1), BX // This allows the C compilers to emit references to m and g using the direct off(TLS) form. if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type_ == D_INDIR+D_TLS && D_AX <= p.To.Type_ && p.To.Type_ <= D_R15 { q = liblink.Appendp(ctxt, p) q.As = p.As q.From = p.From q.From.Type_ = D_INDIR + p.To.Type_ q.From.Index = D_TLS q.From.Scale = 2 // TODO: use 1 q.To = p.To p.From.Type_ = D_TLS p.From.Index = D_NONE p.From.Offset = 0 } } // TODO: Remove. if ctxt.Headtype == liblink.Hwindows || ctxt.Headtype == liblink.Hplan9 { if p.From.Scale == 1 && p.From.Index == D_TLS { p.From.Scale = 2 } if p.To.Scale == 1 && p.To.Index == D_TLS { p.To.Scale = 2 } } if ctxt.Headtype == liblink.Hnacl { nacladdr(ctxt, p, &p.From) nacladdr(ctxt, p, &p.To) } // Maintain information about code generation mode. if ctxt.Mode == 0 { ctxt.Mode = 64 } p.Mode = int8(ctxt.Mode) switch p.As { case AMODE: if p.From.Type_ == D_CONST || p.From.Type_ == D_INDIR+D_NONE { switch int(p.From.Offset) { case 16, 32, 64: ctxt.Mode = int(p.From.Offset) break } } nopout(p) break } // Rewrite CALL/JMP/RET to symbol as D_BRANCH. switch p.As { case ACALL, AJMP, ARET: if (p.To.Type_ == D_EXTERN || p.To.Type_ == D_STATIC) && p.To.Sym != nil { p.To.Type_ = D_BRANCH } break } // 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_ == D_FCONST { if p.From.U.Dval == 0 { if p.To.Type_ >= D_X0 { if p.To.Type_ <= D_X15 { p.As = AXORPS p.From.Type_ = p.To.Type_ p.From.Index = p.To.Index break } } } } fallthrough // fallthrough case AFMOVF, AFADDF, AFSUBF, AFSUBRF, AFMULF, AFDIVF, AFDIVRF, AFCOMF, AFCOMFP, AADDSS, ASUBSS, AMULSS, ADIVSS, ACOMISS, AUCOMISS: if p.From.Type_ == D_FCONST { var i32 uint32 var f32 float32 f32 = float32(p.From.U.Dval) i32 = math.Float32bits(f32) literal = fmt.Sprintf("$f32.%08x", i32) s = liblink.Linklookup(ctxt, literal, 0) if s.Type_ == 0 { s.Type_ = liblink.SRODATA liblink.Adduint32(ctxt, s, i32) s.Reachable = 0 } p.From.Type_ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } // Convert AMOVSD $(0), Xx to AXORPS Xx, Xx case AMOVSD: if p.From.Type_ == D_FCONST { if p.From.U.Dval == 0 { if p.To.Type_ >= D_X0 { if p.To.Type_ <= D_X15 { p.As = AXORPS p.From.Type_ = p.To.Type_ p.From.Index = p.To.Index break } } } } fallthrough // fallthrough case AFMOVD, AFADDD, AFSUBD, AFSUBRD, AFMULD, AFDIVD, AFDIVRD, AFCOMD, AFCOMDP, AADDSD, ASUBSD, AMULSD, ADIVSD, ACOMISD, AUCOMISD: if p.From.Type_ == D_FCONST { var i64 uint64 i64 = math.Float64bits(p.From.U.Dval) literal = fmt.Sprintf("$f64.%016x", i64) s = liblink.Linklookup(ctxt, literal, 0) if s.Type_ == 0 { s.Type_ = liblink.SRODATA liblink.Adduint64(ctxt, s, i64) s.Reachable = 0 } p.From.Type_ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } break } }