Skip to content

Commit cdeb4cf

Browse files
committed
Add ELF TLS support in new x64 backend.
This follows the implementation in the legacy x86 backend, including hardcoded sequence that is compatible with what the linker expects. We could potentially do better here, but it is likely not necessary. Thanks to @bjorn3 for a bugfix to an earlier version of this.
1 parent f579d08 commit cdeb4cf

5 files changed

Lines changed: 110 additions & 2 deletions

File tree

cranelift/codegen/src/isa/x64/inst/emit.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::binemit::{Addend, Reloc};
22
use crate::ir::immediates::{Ieee32, Ieee64};
3+
use crate::ir::LibCall;
34
use crate::ir::TrapCode;
45
use crate::isa::x64::inst::args::*;
56
use crate::isa::x64::inst::*;
@@ -2937,6 +2938,27 @@ pub(crate) fn emit(
29372938
Inst::EpiloguePlaceholder => {
29382939
// Generate no code.
29392940
}
2941+
2942+
Inst::ElfTlsGetAddr { ref symbol } => {
2943+
sink.put1(0x66);
2944+
sink.put1(0b01001000);
2945+
sink.put1(0x8d);
2946+
sink.put1(0x3d);
2947+
emit_reloc(sink, state, Reloc::ElfX86_64TlsGd, symbol, -4);
2948+
sink.put4(0);
2949+
sink.put1(0x66);
2950+
sink.put1(0x66);
2951+
sink.put1(0b01001000);
2952+
sink.put1(0xe8);
2953+
emit_reloc(
2954+
sink,
2955+
state,
2956+
Reloc::X86CallPLTRel4,
2957+
&ExternalName::LibCall(LibCall::ElfTlsGetAddr),
2958+
-4,
2959+
);
2960+
sink.put4(0);
2961+
}
29402962
}
29412963

29422964
state.clear_post_insn();

cranelift/codegen/src/isa/x64/inst/emit_tests.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3779,6 +3779,17 @@ fn test_x64_emit() {
37793779
let trap_code = TrapCode::UnreachableCodeReached;
37803780
insns.push((Inst::Ud2 { trap_code }, "0F0B", "ud2 unreachable"));
37813781

3782+
insns.push((
3783+
Inst::ElfTlsGetAddr {
3784+
symbol: ExternalName::User {
3785+
namespace: 0,
3786+
index: 0,
3787+
},
3788+
},
3789+
"66488D3D00000000666648E800000000",
3790+
"elf_tls_get_addr User { namespace: 0, index: 0 }",
3791+
));
3792+
37823793
// ========================================================
37833794
// Actually run the tests!
37843795
let flags = settings::Flags::new(settings::builder());

cranelift/codegen/src/isa/x64/inst/mod.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,10 @@ pub enum Inst {
471471
/// reports its own `def`s/`use`s/`mod`s; this adds complexity (the instruction list is no
472472
/// longer flat) and requires knowledge about semantics and initial-value independence anyway.
473473
XmmUninitializedValue { dst: Writable<Reg> },
474+
475+
/// A call to the `ElfTlsGetAddr` libcall. Returns address
476+
/// of TLS symbol in rax.
477+
ElfTlsGetAddr { symbol: ExternalName },
474478
}
475479

476480
pub(crate) fn low32_will_sign_extend_to_64(x: u64) -> bool {
@@ -529,7 +533,8 @@ impl Inst {
529533
| Inst::XmmCmpRmR { .. }
530534
| Inst::XmmLoadConst { .. }
531535
| Inst::XmmMinMaxSeq { .. }
532-
| Inst::XmmUninitializedValue { .. } => None,
536+
| Inst::XmmUninitializedValue { .. }
537+
| Inst::ElfTlsGetAddr { .. } => None,
533538

534539
// These use dynamic SSE opcodes.
535540
Inst::GprToXmm { op, .. }
@@ -1759,6 +1764,10 @@ impl PrettyPrint for Inst {
17591764
Inst::Hlt => "hlt".into(),
17601765

17611766
Inst::Ud2 { trap_code } => format!("ud2 {}", trap_code),
1767+
1768+
Inst::ElfTlsGetAddr { ref symbol } => {
1769+
format!("elf_tls_get_addr {:?}", symbol)
1770+
}
17621771
}
17631772
}
17641773
}
@@ -2018,6 +2027,36 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
20182027
| Inst::Fence { .. } => {
20192028
// No registers are used.
20202029
}
2030+
2031+
Inst::ElfTlsGetAddr { .. } => {
2032+
// All caller-saves are clobbered.
2033+
collector.add_def(Writable::from_reg(regs::rsi()));
2034+
collector.add_def(Writable::from_reg(regs::rdi()));
2035+
collector.add_def(Writable::from_reg(regs::rax()));
2036+
collector.add_def(Writable::from_reg(regs::rcx()));
2037+
collector.add_def(Writable::from_reg(regs::rdx()));
2038+
collector.add_def(Writable::from_reg(regs::r8()));
2039+
collector.add_def(Writable::from_reg(regs::r9()));
2040+
collector.add_def(Writable::from_reg(regs::r10()));
2041+
collector.add_def(Writable::from_reg(regs::r11()));
2042+
2043+
collector.add_def(Writable::from_reg(regs::xmm0()));
2044+
collector.add_def(Writable::from_reg(regs::xmm1()));
2045+
collector.add_def(Writable::from_reg(regs::xmm2()));
2046+
collector.add_def(Writable::from_reg(regs::xmm3()));
2047+
collector.add_def(Writable::from_reg(regs::xmm4()));
2048+
collector.add_def(Writable::from_reg(regs::xmm5()));
2049+
collector.add_def(Writable::from_reg(regs::xmm6()));
2050+
collector.add_def(Writable::from_reg(regs::xmm7()));
2051+
collector.add_def(Writable::from_reg(regs::xmm8()));
2052+
collector.add_def(Writable::from_reg(regs::xmm9()));
2053+
collector.add_def(Writable::from_reg(regs::xmm10()));
2054+
collector.add_def(Writable::from_reg(regs::xmm11()));
2055+
collector.add_def(Writable::from_reg(regs::xmm12()));
2056+
collector.add_def(Writable::from_reg(regs::xmm13()));
2057+
collector.add_def(Writable::from_reg(regs::xmm14()));
2058+
collector.add_def(Writable::from_reg(regs::xmm15()));
2059+
}
20212060
}
20222061
}
20232062

@@ -2393,6 +2432,7 @@ fn x64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
23932432
| Inst::Ud2 { .. }
23942433
| Inst::Hlt
23952434
| Inst::AtomicRmwSeq { .. }
2435+
| Inst::ElfTlsGetAddr { .. }
23962436
| Inst::Fence { .. } => {
23972437
// Instruction doesn't explicitly mention any regs, so it can't have any virtual
23982438
// regs that we'd need to remap. Hence no action required.

cranelift/codegen/src/isa/x64/lower.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::isa::{x64::X64Backend, CallConv};
1212
use crate::machinst::lower::*;
1313
use crate::machinst::*;
1414
use crate::result::CodegenResult;
15-
use crate::settings::Flags;
15+
use crate::settings::{Flags, TlsModel};
1616
use alloc::boxed::Box;
1717
use alloc::vec::Vec;
1818
use cranelift_codegen_shared::condcodes::CondCode;
@@ -4238,6 +4238,22 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
42384238
}
42394239
}
42404240

4241+
Opcode::TlsValue => match flags.tls_model() {
4242+
TlsModel::ElfGd => {
4243+
let dst = get_output_reg(ctx, outputs[0]).only_reg().unwrap();
4244+
let (name, _, _) = ctx.symbol_value(insn).unwrap();
4245+
let symbol = name.clone();
4246+
ctx.emit(Inst::ElfTlsGetAddr { symbol });
4247+
ctx.emit(Inst::gen_move(dst, regs::rax(), types::I64));
4248+
}
4249+
_ => {
4250+
todo!(
4251+
"Unimplemented TLS model in x64 backend: {:?}",
4252+
flags.tls_model()
4253+
);
4254+
}
4255+
},
4256+
42414257
Opcode::IaddImm
42424258
| Opcode::ImulImm
42434259
| Opcode::UdivImm
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
test compile
2+
set tls_model=elf_gd
3+
target x86_64
4+
feature "experimental_x64"
5+
6+
function u0:0(i32) -> i64 {
7+
gv0 = symbol colocated tls u1:0
8+
9+
block0(v0: i32):
10+
v1 = global_value.i64 gv0
11+
return v1
12+
}
13+
14+
; check: pushq %rbp
15+
; nextln: movq %rsp, %rbp
16+
; nextln: elf_tls_get_addr User { namespace: 1, index: 0 }
17+
; nextln: movq %rbp, %rsp
18+
; nextln: popq %rbp
19+
; nextln: ret

0 commit comments

Comments
 (0)