Add TLS support for ELF and MachO (#1174)

* Add TLS support
* Add binemit and legalize tests
* Spill all caller-saved registers when necessary
This commit is contained in:
bjorn3
2020-02-26 02:50:04 +01:00
committed by GitHub
parent 0a11736fbf
commit 0a1bb3ba6c
27 changed files with 354 additions and 19 deletions

View File

@@ -56,6 +56,12 @@ pub enum Reloc {
Arm64Call,
/// RISC-V call target
RiscvCall,
/// Elf x86_64 32 bit signed PC relative offset to two GOT entries for GD symbol.
ElfX86_64TlsGd,
/// Mach-O x86_64 32 bit signed PC relative offset to a `__thread_vars` entry.
MachOX86_64Tlv,
}
impl fmt::Display for Reloc {
@@ -71,6 +77,9 @@ impl fmt::Display for Reloc {
Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
Self::Arm32Call | Self::Arm64Call | Self::RiscvCall => write!(f, "Call"),
Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
}
}
}

View File

@@ -63,6 +63,9 @@ pub enum GlobalValueData {
/// away, after linking? If so, references to it can avoid going through a GOT. Note that
/// symbols meant to be preemptible cannot be colocated.
colocated: bool,
/// Does this symbol refer to a thread local storage value?
tls: bool,
},
}
@@ -110,11 +113,13 @@ impl fmt::Display for GlobalValueData {
ref name,
offset,
colocated,
tls,
} => {
write!(
f,
"symbol {}{}",
"symbol {}{}{}",
if colocated { "colocated " } else { "" },
if tls { "tls " } else { "" },
name
)?;
let offset_val: i64 = offset.into();

View File

@@ -46,6 +46,9 @@ pub enum LibCall {
Memset,
/// libc.memmove
Memmove,
/// Elf __tls_get_addr
ElfTlsGetAddr,
}
impl fmt::Display for LibCall {
@@ -71,6 +74,8 @@ impl FromStr for LibCall {
"Memcpy" => Ok(Self::Memcpy),
"Memset" => Ok(Self::Memset),
"Memmove" => Ok(Self::Memmove),
"ElfTlsGetAddr" => Ok(Self::ElfTlsGetAddr),
_ => Err(()),
}
}

View File

@@ -4,7 +4,10 @@ use super::enc_tables::{needs_offset, needs_sib_byte};
use super::registers::RU;
use crate::binemit::{bad_encoding, CodeSink, Reloc};
use crate::ir::condcodes::{CondCode, FloatCC, IntCC};
use crate::ir::{Block, Constant, Function, Inst, InstructionData, JumpTable, Opcode, TrapCode};
use crate::ir::{
Block, Constant, ExternalName, Function, Inst, InstructionData, JumpTable, LibCall, Opcode,
TrapCode,
};
use crate::isa::{RegUnit, StackBase, StackBaseMask, StackRef, TargetIsa};
use crate::regalloc::RegDiversions;

View File

@@ -1288,3 +1288,40 @@ fn convert_ineg(
unreachable!()
}
}
fn expand_tls_value(
inst: ir::Inst,
func: &mut ir::Function,
_cfg: &mut ControlFlowGraph,
isa: &dyn TargetIsa,
) {
use crate::settings::TlsModel;
assert!(
isa.triple().architecture == target_lexicon::Architecture::X86_64,
"Not yet implemented for {:?}",
isa.triple(),
);
if let ir::InstructionData::UnaryGlobalValue {
opcode: ir::Opcode::TlsValue,
global_value,
} = func.dfg[inst]
{
let ctrl_typevar = func.dfg.ctrl_typevar(inst);
assert_eq!(ctrl_typevar, ir::types::I64);
match isa.flags().tls_model() {
TlsModel::None => panic!("tls_model flag is not set."),
TlsModel::ElfGd => {
func.dfg.replace(inst).x86_elf_tls_get_addr(global_value);
}
TlsModel::Macho => {
func.dfg.replace(inst).x86_macho_tls_get_addr(global_value);
}
model => unimplemented!("tls_value for tls model {:?}", model),
}
} else {
unreachable!();
}
}

View File

@@ -40,7 +40,7 @@ pub fn expand_global_value(
global_type,
readonly,
} => load_addr(inst, func, base, offset, global_type, readonly, isa),
ir::GlobalValueData::Symbol { .. } => symbol(inst, func, gv, isa),
ir::GlobalValueData::Symbol { tls, .. } => symbol(inst, func, gv, isa, tls),
}
}
@@ -123,7 +123,18 @@ fn load_addr(
}
/// Expand a `global_value` instruction for a symbolic name global.
fn symbol(inst: ir::Inst, func: &mut ir::Function, gv: ir::GlobalValue, isa: &dyn TargetIsa) {
fn symbol(
inst: ir::Inst,
func: &mut ir::Function,
gv: ir::GlobalValue,
isa: &dyn TargetIsa,
tls: bool,
) {
let ptr_ty = isa.pointer_type();
func.dfg.replace(inst).symbol_value(ptr_ty, gv);
if tls {
func.dfg.replace(inst).tls_value(ptr_ty, gv);
} else {
func.dfg.replace(inst).symbol_value(ptr_ty, gv);
}
}

View File

@@ -267,7 +267,11 @@ impl<'a> Context<'a> {
// If inst is a call, spill all register values that are live across the call.
// This means that we don't currently take advantage of callee-saved registers.
// TODO: Be more sophisticated.
if call_sig.is_some() {
let opcode = self.cur.func.dfg[inst].opcode();
if call_sig.is_some()
|| opcode == crate::ir::Opcode::X86ElfTlsGetAddr
|| opcode == crate::ir::Opcode::X86MachoTlsGetAddr
{
for lv in throughs {
if lv.affinity.is_reg() && !self.spills.contains(&lv.value) {
self.spill_reg(lv.value);

View File

@@ -379,6 +379,7 @@ mod tests {
f.to_string(),
"[shared]\n\
opt_level = \"none\"\n\
tls_model = \"none\"\n\
libcall_call_conv = \"isa_default\"\n\
baldrdash_prologue_words = 0\n\
probestack_size_log2 = 12\n\