func progedit(ctxt *liblink.Link, p *liblink.Prog) { var literal string var s *liblink.LSym var q *liblink.Prog // Thread-local storage references use the TLS pseudo-register. // As a register, TLS refers to the thread-local storage base, and it // can only be loaded into another register: // // MOVQ TLS, AX // // An offset from the thread-local storage base is written off(reg)(TLS*1). // Semantically it is off(reg), but the (TLS*1) annotation marks this as // indexing from the loaded TLS base. This emits a relocation so that // if the linker needs to adjust the offset, it can. For example: // // MOVQ TLS, AX // MOVQ 8(AX)(TLS*1), CX // load m into CX // // On systems that support direct access to the TLS memory, this // pair of instructions can be reduced to a direct TLS memory reference: // // MOVQ 8(TLS), CX // load m into CX // // The 2-instruction and 1-instruction forms correspond roughly to // ELF TLS initial exec mode and ELF TLS local exec mode, respectively. // // We applies this rewrite on systems that support the 1-instruction form. // The decision is made using only the operating system (and probably // the -shared flag, eventually), not the link mode. If some link modes // on a particular operating system require the 2-instruction form, // then all builds for that operating system will use the 2-instruction // form, so that the link mode decision can be delayed to link time. // // In this way, all supported systems use identical instructions to // access TLS, and they are rewritten appropriately first here in // liblink and then finally using relocations in the linker. if canuselocaltls(ctxt) { // Reduce TLS initial exec model to TLS local exec model. // Sequences like // MOVQ TLS, BX // ... off(BX)(TLS*1) ... // become // NOP // ... off(TLS) ... // // TODO(rsc): Remove the Hsolaris special case. It exists only to // guarantee we are producing byte-identical binaries as before this code. // But it should be unnecessary. if (p.As == AMOVQ || p.As == AMOVL) && p.From.Typ == D_TLS && D_AX <= p.To.Typ && p.To.Typ <= D_R15 && ctxt.Headtype != liblink.Hsolaris { nopout(p) } if p.From.Index == D_TLS && D_INDIR+D_AX <= p.From.Typ && p.From.Typ <= D_INDIR+D_R15 { p.From.Typ = D_INDIR + D_TLS p.From.Scale = 0 p.From.Index = D_NONE } if p.To.Index == D_TLS && D_INDIR+D_AX <= p.To.Typ && p.To.Typ <= D_INDIR+D_R15 { p.To.Typ = D_INDIR + D_TLS p.To.Scale = 0 p.To.Index = D_NONE } } else { // As a courtesy to the C compilers, rewrite TLS local exec load as TLS initial exec load. // The instruction // MOVQ off(TLS), BX // becomes the sequence // MOVQ TLS, BX // MOVQ off(BX)(TLS*1), BX // This allows the C compilers to emit references to m and g using the direct off(TLS) form. if (p.As == AMOVQ || p.As == AMOVL) && p.From.Typ == D_INDIR+D_TLS && D_AX <= p.To.Typ && p.To.Typ <= D_R15 { q = liblink.Appendp(ctxt, p) q.As = p.As q.From = p.From q.From.Typ = D_INDIR + p.To.Typ q.From.Index = D_TLS q.From.Scale = 2 // TODO: use 1 q.To = p.To p.From.Typ = D_TLS p.From.Index = D_NONE p.From.Offset = 0 } } // TODO: Remove. if ctxt.Headtype == liblink.Hwindows || ctxt.Headtype == liblink.Hplan9 { if p.From.Scale == 1 && p.From.Index == D_TLS { p.From.Scale = 2 } if p.To.Scale == 1 && p.To.Index == D_TLS { p.To.Scale = 2 } } if ctxt.Headtype == liblink.Hnacl { nacladdr(ctxt, p, &p.From) nacladdr(ctxt, p, &p.To) } // Maintain information about code generation mode. if ctxt.Mode == 0 { ctxt.Mode = 64 } p.Mode = ctxt.Mode switch p.As { case AMODE: if p.From.Typ == D_CONST || p.From.Typ == D_INDIR+D_NONE { switch int(p.From.Offset) { case 16, 32, 64: ctxt.Mode = int(p.From.Offset) break } } nopout(p) break } // Rewrite CALL/JMP/RET to symbol as D_BRANCH. switch p.As { case ACALL, AJMP, ARET: if (p.To.Typ == D_EXTERN || p.To.Typ == D_STATIC) && p.To.Sym != nil { p.To.Typ = D_BRANCH } break } // Rewrite float constants to values stored in memory. switch p.As { case AFMOVF, AFADDF, AFSUBF, AFSUBRF, AFMULF, AFDIVF, AFDIVRF, AFCOMF, AFCOMFP, AMOVSS, AADDSS, ASUBSS, AMULSS, ADIVSS, ACOMISS, AUCOMISS: if p.From.Typ == D_FCONST { var i32 uint32 var f32 float32 f32 = float32(p.From.U.Dval) i32 = math.Float32bits(f32) literal = fmt.Sprintf("$f32.%08x", uint32(i32)) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint32(ctxt, s, i32) s.Reachable = 0 } p.From.Typ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } case AFMOVD, AFADDD, AFSUBD, AFSUBRD, AFMULD, AFDIVD, AFDIVRD, AFCOMD, AFCOMDP, AMOVSD, AADDSD, ASUBSD, AMULSD, ADIVSD, ACOMISD, AUCOMISD: if p.From.Typ == D_FCONST { var i64 uint64 i64 = math.Float64bits(p.From.U.Dval) literal = fmt.Sprintf("$f64.%016x", uint64(i64)) s = liblink.Linklookup(ctxt, literal, 0) if s.Typ == 0 { s.Typ = liblink.SRODATA liblink.Adduint64(ctxt, s, i64) s.Reachable = 0 } p.From.Typ = D_EXTERN p.From.Sym = s p.From.Offset = 0 } break } }