diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index 075724d493..e1253b9597 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -1,5 +1,6 @@ use crate::binemit::{Addend, Reloc}; use crate::ir::immediates::{Ieee32, Ieee64}; +use crate::ir::LibCall; use crate::ir::TrapCode; use crate::isa::x64::inst::args::*; use crate::isa::x64::inst::*; @@ -2988,6 +2989,33 @@ pub(crate) fn emit( Inst::EpiloguePlaceholder => { // Generate no code. } + + Inst::ElfTlsGetAddr { ref symbol } => { + // N.B.: Must be exactly this byte sequence; the linker requires it, + // because it must know how to rewrite the bytes. + + // data16 lea gv@tlsgd(%rip),%rdi + sink.put1(0x66); // data16 + sink.put1(0b01001000); // REX.W + sink.put1(0x8d); // LEA + sink.put1(0x3d); // ModRM byte + emit_reloc(sink, state, Reloc::ElfX86_64TlsGd, symbol, -4); + sink.put4(0); // offset + + // data16 data16 callq __tls_get_addr-4 + sink.put1(0x66); // data16 + sink.put1(0x66); // data16 + sink.put1(0b01001000); // REX.W + sink.put1(0xe8); // CALL + emit_reloc( + sink, + state, + Reloc::X86CallPLTRel4, + &ExternalName::LibCall(LibCall::ElfTlsGetAddr), + -4, + ); + sink.put4(0); // offset + } } state.clear_post_insn(); diff --git a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs index 42e38c9cd5..f1e74ae22a 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs @@ -3904,6 +3904,17 @@ fn test_x64_emit() { let trap_code = TrapCode::UnreachableCodeReached; insns.push((Inst::Ud2 { trap_code }, "0F0B", "ud2 unreachable")); + insns.push(( + Inst::ElfTlsGetAddr { + symbol: ExternalName::User { + namespace: 0, + index: 0, + }, + }, + "66488D3D00000000666648E800000000", + "elf_tls_get_addr User { namespace: 0, index: 0 }", + )); + // ======================================================== // Actually run the tests! let mut flag_builder = settings::builder(); diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs index 979c264231..edc7a65109 100644 --- a/cranelift/codegen/src/isa/x64/inst/mod.rs +++ b/cranelift/codegen/src/isa/x64/inst/mod.rs @@ -2,7 +2,9 @@ use crate::binemit::{CodeOffset, StackMap}; use crate::ir::{types, ExternalName, Opcode, SourceLoc, TrapCode, Type}; +use crate::isa::x64::abi::X64ABIMachineSpec; use crate::isa::x64::settings as x64_settings; +use crate::isa::CallConv; use crate::machinst::*; use crate::{settings, settings::Flags, CodegenError, CodegenResult}; use alloc::boxed::Box; @@ -474,6 +476,10 @@ pub enum Inst { /// reports its own `def`s/`use`s/`mod`s; this adds complexity (the instruction list is no /// longer flat) and requires knowledge about semantics and initial-value independence anyway. XmmUninitializedValue { dst: Writable }, + + /// A call to the `ElfTlsGetAddr` libcall. Returns address + /// of TLS symbol in rax. + ElfTlsGetAddr { symbol: ExternalName }, } pub(crate) fn low32_will_sign_extend_to_64(x: u64) -> bool { @@ -532,7 +538,8 @@ impl Inst { | Inst::XmmCmpRmR { .. } | Inst::XmmLoadConst { .. } | Inst::XmmMinMaxSeq { .. } - | Inst::XmmUninitializedValue { .. } => None, + | Inst::XmmUninitializedValue { .. } + | Inst::ElfTlsGetAddr { .. } => None, // These use dynamic SSE opcodes. Inst::GprToXmm { op, .. } @@ -1780,6 +1787,10 @@ impl PrettyPrint for Inst { Inst::Hlt => "hlt".into(), Inst::Ud2 { trap_code } => format!("ud2 {}", trap_code), + + Inst::ElfTlsGetAddr { ref symbol } => { + format!("elf_tls_get_addr {:?}", symbol) + } } } } @@ -2039,6 +2050,18 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) { | Inst::Fence { .. } => { // No registers are used. } + + Inst::ElfTlsGetAddr { .. } => { + // All caller-saves are clobbered. + // + // We use the SysV calling convention here because the + // pseudoinstruction (and relocation that it emits) is specific to + // ELF systems; other x86-64 targets with other conventions (i.e., + // Windows) use different TLS strategies. + for reg in X64ABIMachineSpec::get_regs_clobbered_by_call(CallConv::SystemV) { + collector.add_def(reg); + } + } } } @@ -2425,6 +2448,7 @@ fn x64_map_regs(inst: &mut Inst, mapper: &RUM) { | Inst::Ud2 { .. } | Inst::Hlt | Inst::AtomicRmwSeq { .. } + | Inst::ElfTlsGetAddr { .. } | Inst::Fence { .. } => { // Instruction doesn't explicitly mention any regs, so it can't have any virtual // regs that we'd need to remap. Hence no action required. diff --git a/cranelift/codegen/src/isa/x64/lower.rs b/cranelift/codegen/src/isa/x64/lower.rs index a25da666b3..da231120bb 100644 --- a/cranelift/codegen/src/isa/x64/lower.rs +++ b/cranelift/codegen/src/isa/x64/lower.rs @@ -12,7 +12,7 @@ use crate::isa::{x64::X64Backend, CallConv}; use crate::machinst::lower::*; use crate::machinst::*; use crate::result::CodegenResult; -use crate::settings::Flags; +use crate::settings::{Flags, TlsModel}; use alloc::boxed::Box; use alloc::vec::Vec; use cranelift_codegen_shared::condcodes::CondCode; @@ -5324,6 +5324,22 @@ fn lower_insn_to_regs>( ctx.emit(Inst::gen_move(dst_hi, src.regs()[1], types::I64)); } + Opcode::TlsValue => match flags.tls_model() { + TlsModel::ElfGd => { + let dst = get_output_reg(ctx, outputs[0]).only_reg().unwrap(); + let (name, _, _) = ctx.symbol_value(insn).unwrap(); + let symbol = name.clone(); + ctx.emit(Inst::ElfTlsGetAddr { symbol }); + ctx.emit(Inst::gen_move(dst, regs::rax(), types::I64)); + } + _ => { + todo!( + "Unimplemented TLS model in x64 backend: {:?}", + flags.tls_model() + ); + } + }, + Opcode::IaddImm | Opcode::ImulImm | Opcode::UdivImm diff --git a/cranelift/filetests/filetests/isa/x64/tls_elf.clif b/cranelift/filetests/filetests/isa/x64/tls_elf.clif new file mode 100644 index 0000000000..6136e98e7d --- /dev/null +++ b/cranelift/filetests/filetests/isa/x64/tls_elf.clif @@ -0,0 +1,19 @@ +test compile +set tls_model=elf_gd +target x86_64 +feature "experimental_x64" + +function u0:0(i32) -> i64 { +gv0 = symbol colocated tls u1:0 + +block0(v0: i32): + v1 = global_value.i64 gv0 + return v1 +} + +; check: pushq %rbp +; nextln: movq %rsp, %rbp +; nextln: elf_tls_get_addr User { namespace: 1, index: 0 } +; nextln: movq %rbp, %rsp +; nextln: popq %rbp +; nextln: ret