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:
@@ -340,7 +340,7 @@ impl InstructionBuilder {
|
|||||||
let polymorphic_info =
|
let polymorphic_info =
|
||||||
verify_polymorphic(&operands_in, &operands_out, &self.format, &value_opnums);
|
verify_polymorphic(&operands_in, &operands_out, &self.format, &value_opnums);
|
||||||
|
|
||||||
// Infer from output operands whether an instruciton clobbers CPU flags or not.
|
// Infer from output operands whether an instruction clobbers CPU flags or not.
|
||||||
let writes_cpu_flags = operands_out.iter().any(|op| op.is_cpu_flags());
|
let writes_cpu_flags = operands_out.iter().any(|op| op.is_cpu_flags());
|
||||||
|
|
||||||
let camel_name = camel_case(&self.name);
|
let camel_name = camel_case(&self.name);
|
||||||
|
|||||||
@@ -2407,5 +2407,14 @@ pub(crate) fn define(
|
|||||||
define_control_flow(&mut e, shared_defs, settings, r);
|
define_control_flow(&mut e, shared_defs, settings, r);
|
||||||
define_reftypes(&mut e, shared_defs, r);
|
define_reftypes(&mut e, shared_defs, r);
|
||||||
|
|
||||||
|
let x86_elf_tls_get_addr = x86.by_name("x86_elf_tls_get_addr");
|
||||||
|
let x86_macho_tls_get_addr = x86.by_name("x86_macho_tls_get_addr");
|
||||||
|
|
||||||
|
let rec_elf_tls_get_addr = r.recipe("elf_tls_get_addr");
|
||||||
|
let rec_macho_tls_get_addr = r.recipe("macho_tls_get_addr");
|
||||||
|
|
||||||
|
e.enc64_rec(x86_elf_tls_get_addr, rec_elf_tls_get_addr, 0);
|
||||||
|
e.enc64_rec(x86_macho_tls_get_addr, rec_macho_tls_get_addr, 0);
|
||||||
|
|
||||||
e
|
e
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use crate::cdsl::operands::Operand;
|
|||||||
use crate::cdsl::types::ValueType;
|
use crate::cdsl::types::ValueType;
|
||||||
use crate::cdsl::typevar::{Interval, TypeSetBuilder, TypeVar};
|
use crate::cdsl::typevar::{Interval, TypeSetBuilder, TypeVar};
|
||||||
|
|
||||||
|
use crate::shared::entities::EntityRefs;
|
||||||
use crate::shared::formats::Formats;
|
use crate::shared::formats::Formats;
|
||||||
use crate::shared::immediates::Immediates;
|
use crate::shared::immediates::Immediates;
|
||||||
use crate::shared::types;
|
use crate::shared::types;
|
||||||
@@ -16,6 +17,7 @@ pub(crate) fn define(
|
|||||||
mut all_instructions: &mut AllInstructions,
|
mut all_instructions: &mut AllInstructions,
|
||||||
formats: &Formats,
|
formats: &Formats,
|
||||||
immediates: &Immediates,
|
immediates: &Immediates,
|
||||||
|
entities: &EntityRefs,
|
||||||
) -> InstructionGroup {
|
) -> InstructionGroup {
|
||||||
let mut ig = InstructionGroupBuilder::new(&mut all_instructions);
|
let mut ig = InstructionGroupBuilder::new(&mut all_instructions);
|
||||||
|
|
||||||
@@ -542,5 +544,39 @@ pub(crate) fn define(
|
|||||||
.operands_out(vec![a]),
|
.operands_out(vec![a]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let i64_t = &TypeVar::new(
|
||||||
|
"i64_t",
|
||||||
|
"A scalar 64bit integer",
|
||||||
|
TypeSetBuilder::new().ints(64..64).build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let GV = &Operand::new("GV", &entities.global_value);
|
||||||
|
let addr = &Operand::new("addr", i64_t);
|
||||||
|
|
||||||
|
ig.push(
|
||||||
|
Inst::new(
|
||||||
|
"x86_elf_tls_get_addr",
|
||||||
|
r#"
|
||||||
|
Elf tls get addr -- This implements the GD TLS model for ELF. The clobber output should
|
||||||
|
not be used.
|
||||||
|
"#,
|
||||||
|
&formats.unary_global_value,
|
||||||
|
)
|
||||||
|
.operands_in(vec![GV])
|
||||||
|
.operands_out(vec![addr]),
|
||||||
|
);
|
||||||
|
ig.push(
|
||||||
|
Inst::new(
|
||||||
|
"x86_macho_tls_get_addr",
|
||||||
|
r#"
|
||||||
|
Mach-O tls get addr -- This implements TLS access for Mach-O. The clobber output should
|
||||||
|
not be used.
|
||||||
|
"#,
|
||||||
|
&formats.unary_global_value,
|
||||||
|
)
|
||||||
|
.operands_in(vec![GV])
|
||||||
|
.operands_out(vec![addr]),
|
||||||
|
);
|
||||||
|
|
||||||
ig.build()
|
ig.build()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ pub(crate) fn define(shared: &mut SharedDefinitions, x86_instructions: &Instruct
|
|||||||
let shuffle = insts.by_name("shuffle");
|
let shuffle = insts.by_name("shuffle");
|
||||||
let srem = insts.by_name("srem");
|
let srem = insts.by_name("srem");
|
||||||
let sshr = insts.by_name("sshr");
|
let sshr = insts.by_name("sshr");
|
||||||
|
let tls_value = insts.by_name("tls_value");
|
||||||
let trueif = insts.by_name("trueif");
|
let trueif = insts.by_name("trueif");
|
||||||
let udiv = insts.by_name("udiv");
|
let udiv = insts.by_name("udiv");
|
||||||
let umax = insts.by_name("umax");
|
let umax = insts.by_name("umax");
|
||||||
@@ -326,6 +327,8 @@ pub(crate) fn define(shared: &mut SharedDefinitions, x86_instructions: &Instruct
|
|||||||
|
|
||||||
group.custom_legalize(ineg, "convert_ineg");
|
group.custom_legalize(ineg, "convert_ineg");
|
||||||
|
|
||||||
|
group.custom_legalize(tls_value, "expand_tls_value");
|
||||||
|
|
||||||
group.build_and_add_to(&mut shared.transform_groups);
|
group.build_and_add_to(&mut shared.transform_groups);
|
||||||
|
|
||||||
let mut narrow = TransformGroupBuilder::new(
|
let mut narrow = TransformGroupBuilder::new(
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
|
|||||||
&mut shared_defs.all_instructions,
|
&mut shared_defs.all_instructions,
|
||||||
&shared_defs.formats,
|
&shared_defs.formats,
|
||||||
&shared_defs.imm,
|
&shared_defs.imm,
|
||||||
|
&shared_defs.entities,
|
||||||
);
|
);
|
||||||
legalize::define(shared_defs, &inst_group);
|
legalize::define(shared_defs, &inst_group);
|
||||||
|
|
||||||
|
|||||||
@@ -3259,10 +3259,73 @@ pub(crate) fn define<'shared>(
|
|||||||
recipes.add_recipe(
|
recipes.add_recipe(
|
||||||
EncodingRecipeBuilder::new("safepoint", &formats.multiary, 0).emit(
|
EncodingRecipeBuilder::new("safepoint", &formats.multiary, 0).emit(
|
||||||
r#"
|
r#"
|
||||||
sink.add_stackmap(args, func, isa);
|
sink.add_stackmap(args, func, isa);
|
||||||
"#,
|
"#,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Both `elf_tls_get_addr` and `macho_tls_get_addr` require all caller-saved registers to be spilled.
|
||||||
|
// This is currently special cased in `regalloc/spilling.rs` in the `visit_inst` function.
|
||||||
|
|
||||||
|
recipes.add_recipe(
|
||||||
|
EncodingRecipeBuilder::new("elf_tls_get_addr", &formats.unary_global_value, 16)
|
||||||
|
// FIXME Correct encoding for non rax registers
|
||||||
|
.operands_out(vec![reg_rax])
|
||||||
|
.emit(
|
||||||
|
r#"
|
||||||
|
// output %rax
|
||||||
|
// clobbers %rdi
|
||||||
|
|
||||||
|
// Those data16 prefixes are necessary to pad to 16 bytes.
|
||||||
|
|
||||||
|
// data16 lea gv@tlsgd(%rip),%rdi
|
||||||
|
sink.put1(0x66); // data16
|
||||||
|
sink.put1(0b01001000); // rex.w
|
||||||
|
const LEA: u8 = 0x8d;
|
||||||
|
sink.put1(LEA); // lea
|
||||||
|
modrm_riprel(0b111/*out_reg0*/, sink); // 0x3d
|
||||||
|
sink.reloc_external(Reloc::ElfX86_64TlsGd,
|
||||||
|
&func.global_values[global_value].symbol_name(),
|
||||||
|
-4);
|
||||||
|
sink.put4(0);
|
||||||
|
|
||||||
|
// data16 data16 callq __tls_get_addr-4
|
||||||
|
sink.put1(0x66); // data16
|
||||||
|
sink.put1(0x66); // data16
|
||||||
|
sink.put1(0b01001000); // rex.w
|
||||||
|
sink.put1(0xe8); // call
|
||||||
|
sink.reloc_external(Reloc::X86CallPLTRel4,
|
||||||
|
&ExternalName::LibCall(LibCall::ElfTlsGetAddr),
|
||||||
|
-4);
|
||||||
|
sink.put4(0);
|
||||||
|
"#,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
recipes.add_recipe(
|
||||||
|
EncodingRecipeBuilder::new("macho_tls_get_addr", &formats.unary_global_value, 9)
|
||||||
|
// FIXME Correct encoding for non rax registers
|
||||||
|
.operands_out(vec![reg_rax])
|
||||||
|
.emit(
|
||||||
|
r#"
|
||||||
|
// output %rax
|
||||||
|
// clobbers %rdi
|
||||||
|
|
||||||
|
// movq gv@tlv(%rip), %rdi
|
||||||
|
sink.put1(0x48); // rex
|
||||||
|
sink.put1(0x8b); // mov
|
||||||
|
modrm_riprel(0b111/*out_reg0*/, sink); // 0x3d
|
||||||
|
sink.reloc_external(Reloc::MachOX86_64Tlv,
|
||||||
|
&func.global_values[global_value].symbol_name(),
|
||||||
|
-4);
|
||||||
|
sink.put4(0);
|
||||||
|
|
||||||
|
// callq *(%rdi)
|
||||||
|
sink.put1(0xff);
|
||||||
|
sink.put1(0x17);
|
||||||
|
"#,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
recipes
|
recipes
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1127,6 +1127,18 @@ pub(crate) fn define(
|
|||||||
.operands_out(vec![a]),
|
.operands_out(vec![a]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ig.push(
|
||||||
|
Inst::new(
|
||||||
|
"tls_value",
|
||||||
|
r#"
|
||||||
|
Compute the value of global GV, which is a TLS (thread local storage) value.
|
||||||
|
"#,
|
||||||
|
&formats.unary_global_value,
|
||||||
|
)
|
||||||
|
.operands_in(vec![GV])
|
||||||
|
.operands_out(vec![a]),
|
||||||
|
);
|
||||||
|
|
||||||
let HeapOffset = &TypeVar::new(
|
let HeapOffset = &TypeVar::new(
|
||||||
"HeapOffset",
|
"HeapOffset",
|
||||||
"An unsigned heap offset",
|
"An unsigned heap offset",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Shared definitions for the Cranelift intermediate language.
|
//! Shared definitions for the Cranelift intermediate language.
|
||||||
|
|
||||||
mod entities;
|
pub mod entities;
|
||||||
pub mod formats;
|
pub mod formats;
|
||||||
pub mod immediates;
|
pub mod immediates;
|
||||||
pub mod instructions;
|
pub mod instructions;
|
||||||
@@ -28,6 +28,7 @@ pub(crate) struct Definitions {
|
|||||||
pub imm: Immediates,
|
pub imm: Immediates,
|
||||||
pub formats: Formats,
|
pub formats: Formats,
|
||||||
pub transform_groups: TransformGroups,
|
pub transform_groups: TransformGroups,
|
||||||
|
pub entities: EntityRefs,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn define() -> Definitions {
|
pub(crate) fn define() -> Definitions {
|
||||||
@@ -47,6 +48,7 @@ pub(crate) fn define() -> Definitions {
|
|||||||
imm: immediates,
|
imm: immediates,
|
||||||
formats,
|
formats,
|
||||||
transform_groups,
|
transform_groups,
|
||||||
|
entities,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,6 +131,14 @@ pub(crate) fn define() -> SettingGroup {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
settings.add_enum(
|
||||||
|
"tls_model",
|
||||||
|
r#"
|
||||||
|
Defines the model used to perform TLS accesses.
|
||||||
|
"#,
|
||||||
|
vec!["none", "elf_gd", "macho", "coff"],
|
||||||
|
);
|
||||||
|
|
||||||
// Settings specific to the `baldrdash` calling convention.
|
// Settings specific to the `baldrdash` calling convention.
|
||||||
|
|
||||||
settings.add_enum(
|
settings.add_enum(
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ pub enum Reloc {
|
|||||||
Arm64Call,
|
Arm64Call,
|
||||||
/// RISC-V call target
|
/// RISC-V call target
|
||||||
RiscvCall,
|
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 {
|
impl fmt::Display for Reloc {
|
||||||
@@ -71,6 +77,9 @@ impl fmt::Display for Reloc {
|
|||||||
Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
|
Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
|
||||||
Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
|
Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
|
||||||
Self::Arm32Call | Self::Arm64Call | Self::RiscvCall => write!(f, "Call"),
|
Self::Arm32Call | Self::Arm64Call | Self::RiscvCall => write!(f, "Call"),
|
||||||
|
|
||||||
|
Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
|
||||||
|
Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ pub enum GlobalValueData {
|
|||||||
/// away, after linking? If so, references to it can avoid going through a GOT. Note that
|
/// away, after linking? If so, references to it can avoid going through a GOT. Note that
|
||||||
/// symbols meant to be preemptible cannot be colocated.
|
/// symbols meant to be preemptible cannot be colocated.
|
||||||
colocated: bool,
|
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,
|
ref name,
|
||||||
offset,
|
offset,
|
||||||
colocated,
|
colocated,
|
||||||
|
tls,
|
||||||
} => {
|
} => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"symbol {}{}",
|
"symbol {}{}{}",
|
||||||
if colocated { "colocated " } else { "" },
|
if colocated { "colocated " } else { "" },
|
||||||
|
if tls { "tls " } else { "" },
|
||||||
name
|
name
|
||||||
)?;
|
)?;
|
||||||
let offset_val: i64 = offset.into();
|
let offset_val: i64 = offset.into();
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ pub enum LibCall {
|
|||||||
Memset,
|
Memset,
|
||||||
/// libc.memmove
|
/// libc.memmove
|
||||||
Memmove,
|
Memmove,
|
||||||
|
|
||||||
|
/// Elf __tls_get_addr
|
||||||
|
ElfTlsGetAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for LibCall {
|
impl fmt::Display for LibCall {
|
||||||
@@ -71,6 +74,8 @@ impl FromStr for LibCall {
|
|||||||
"Memcpy" => Ok(Self::Memcpy),
|
"Memcpy" => Ok(Self::Memcpy),
|
||||||
"Memset" => Ok(Self::Memset),
|
"Memset" => Ok(Self::Memset),
|
||||||
"Memmove" => Ok(Self::Memmove),
|
"Memmove" => Ok(Self::Memmove),
|
||||||
|
|
||||||
|
"ElfTlsGetAddr" => Ok(Self::ElfTlsGetAddr),
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ use super::enc_tables::{needs_offset, needs_sib_byte};
|
|||||||
use super::registers::RU;
|
use super::registers::RU;
|
||||||
use crate::binemit::{bad_encoding, CodeSink, Reloc};
|
use crate::binemit::{bad_encoding, CodeSink, Reloc};
|
||||||
use crate::ir::condcodes::{CondCode, FloatCC, IntCC};
|
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::isa::{RegUnit, StackBase, StackBaseMask, StackRef, TargetIsa};
|
||||||
use crate::regalloc::RegDiversions;
|
use crate::regalloc::RegDiversions;
|
||||||
|
|
||||||
|
|||||||
@@ -1288,3 +1288,40 @@ fn convert_ineg(
|
|||||||
unreachable!()
|
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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ pub fn expand_global_value(
|
|||||||
global_type,
|
global_type,
|
||||||
readonly,
|
readonly,
|
||||||
} => load_addr(inst, func, base, offset, global_type, readonly, isa),
|
} => 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.
|
/// 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();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,7 +267,11 @@ impl<'a> Context<'a> {
|
|||||||
// If inst is a call, spill all register values that are live across the call.
|
// 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.
|
// This means that we don't currently take advantage of callee-saved registers.
|
||||||
// TODO: Be more sophisticated.
|
// 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 {
|
for lv in throughs {
|
||||||
if lv.affinity.is_reg() && !self.spills.contains(&lv.value) {
|
if lv.affinity.is_reg() && !self.spills.contains(&lv.value) {
|
||||||
self.spill_reg(lv.value);
|
self.spill_reg(lv.value);
|
||||||
|
|||||||
@@ -379,6 +379,7 @@ mod tests {
|
|||||||
f.to_string(),
|
f.to_string(),
|
||||||
"[shared]\n\
|
"[shared]\n\
|
||||||
opt_level = \"none\"\n\
|
opt_level = \"none\"\n\
|
||||||
|
tls_model = \"none\"\n\
|
||||||
libcall_call_conv = \"isa_default\"\n\
|
libcall_call_conv = \"isa_default\"\n\
|
||||||
baldrdash_prologue_words = 0\n\
|
baldrdash_prologue_words = 0\n\
|
||||||
probestack_size_log2 = 12\n\
|
probestack_size_log2 = 12\n\
|
||||||
|
|||||||
@@ -136,8 +136,10 @@ impl Backend for FaerieBackend {
|
|||||||
name: &str,
|
name: &str,
|
||||||
linkage: Linkage,
|
linkage: Linkage,
|
||||||
writable: bool,
|
writable: bool,
|
||||||
|
tls: bool,
|
||||||
align: Option<u8>,
|
align: Option<u8>,
|
||||||
) {
|
) {
|
||||||
|
assert!(!tls, "Faerie doesn't yet support TLS");
|
||||||
self.artifact
|
self.artifact
|
||||||
.declare(name, translate_data_linkage(linkage, writable, align))
|
.declare(name, translate_data_linkage(linkage, writable, align))
|
||||||
.expect("inconsistent declarations");
|
.expect("inconsistent declarations");
|
||||||
@@ -231,10 +233,12 @@ impl Backend for FaerieBackend {
|
|||||||
_id: DataId,
|
_id: DataId,
|
||||||
name: &str,
|
name: &str,
|
||||||
_writable: bool,
|
_writable: bool,
|
||||||
|
tls: bool,
|
||||||
_align: Option<u8>,
|
_align: Option<u8>,
|
||||||
data_ctx: &DataContext,
|
data_ctx: &DataContext,
|
||||||
namespace: &ModuleNamespace<Self>,
|
namespace: &ModuleNamespace<Self>,
|
||||||
) -> ModuleResult<FaerieCompiledData> {
|
) -> ModuleResult<FaerieCompiledData> {
|
||||||
|
assert!(!tls, "Faerie doesn't yet support TLS");
|
||||||
let &DataDescription {
|
let &DataDescription {
|
||||||
ref init,
|
ref init,
|
||||||
ref function_decls,
|
ref function_decls,
|
||||||
|
|||||||
18
cranelift/filetests/filetests/isa/x86/tls_elf.clif
Normal file
18
cranelift/filetests/filetests/isa/x86/tls_elf.clif
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
test regalloc
|
||||||
|
set tls_model=elf_gd
|
||||||
|
target x86_64
|
||||||
|
|
||||||
|
function u0:0(i32) -> i32, i64 {
|
||||||
|
gv0 = symbol colocated tls u1:0
|
||||||
|
|
||||||
|
block0(v0: i32):
|
||||||
|
; check: block0(v2: i32 [%rdi]):
|
||||||
|
; nextln: [RexOp1spillSib32#89,ss0] v0 = spill v2
|
||||||
|
v1 = global_value.i64 gv0
|
||||||
|
; nextln: [elf_tls_get_addr#00,%rax] v1 = x86_elf_tls_get_addr gv0
|
||||||
|
; nextln: [RexOp1fillSib32#8b,%r15] v3 = fill v0
|
||||||
|
return v0, v1
|
||||||
|
; nextln: [RexOp1rmov#8089] regmove v1, %rax -> %rdx
|
||||||
|
; nextln: [RexOp1rmov#89] regmove v3, %r15 -> %rax
|
||||||
|
; nextln: [Op1ret#c3] return v3, v1
|
||||||
|
}
|
||||||
11
cranelift/filetests/filetests/isa/x86/tls_enc.clif
Normal file
11
cranelift/filetests/filetests/isa/x86/tls_enc.clif
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
test binemit
|
||||||
|
target x86_64
|
||||||
|
|
||||||
|
function u0:0() -> i64, i64 {
|
||||||
|
gv0 = symbol colocated tls u1:0
|
||||||
|
|
||||||
|
block0:
|
||||||
|
[-, %rax] v0 = x86_elf_tls_get_addr gv0 ; bin: 66 48 8d 3d ElfX86_64TlsGd(u1:0-4) 00000000 66 66 48 e8 CallPLTRel4(%ElfTlsGetAddr-4) 00000000
|
||||||
|
[-, %rax] v1 = x86_macho_tls_get_addr gv0; bin: 48 8b 3d MachOX86_64Tlv(u1:0-4) 00000000 ff 17
|
||||||
|
return v0, v1
|
||||||
|
}
|
||||||
18
cranelift/filetests/filetests/isa/x86/tls_macho.clif
Normal file
18
cranelift/filetests/filetests/isa/x86/tls_macho.clif
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
test regalloc
|
||||||
|
set tls_model=macho
|
||||||
|
target x86_64
|
||||||
|
|
||||||
|
function u0:0(i32) -> i32, i64 {
|
||||||
|
gv0 = symbol colocated tls u1:0
|
||||||
|
|
||||||
|
block0(v0: i32):
|
||||||
|
; check: block0(v2: i32 [%rdi]):
|
||||||
|
; nextln: [RexOp1spillSib32#89,ss0] v0 = spill v2
|
||||||
|
v1 = global_value.i64 gv0
|
||||||
|
; nextln: [macho_tls_get_addr#00,%rax] v1 = x86_macho_tls_get_addr gv0
|
||||||
|
; nextln: [RexOp1fillSib32#8b,%r15] v3 = fill v0
|
||||||
|
return v0, v1
|
||||||
|
; nextln: [RexOp1rmov#8089] regmove v1, %rax -> %rdx
|
||||||
|
; nextln: [RexOp1rmov#89] regmove v3, %r15 -> %rax
|
||||||
|
; nextln: [Op1ret#c3] return v3, v1
|
||||||
|
}
|
||||||
@@ -72,6 +72,7 @@ where
|
|||||||
name: &str,
|
name: &str,
|
||||||
linkage: Linkage,
|
linkage: Linkage,
|
||||||
writable: bool,
|
writable: bool,
|
||||||
|
tls: bool,
|
||||||
align: Option<u8>,
|
align: Option<u8>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -107,6 +108,7 @@ where
|
|||||||
id: DataId,
|
id: DataId,
|
||||||
name: &str,
|
name: &str,
|
||||||
writable: bool,
|
writable: bool,
|
||||||
|
tls: bool,
|
||||||
align: Option<u8>,
|
align: Option<u8>,
|
||||||
data_ctx: &DataContext,
|
data_ctx: &DataContext,
|
||||||
namespace: &ModuleNamespace<Self>,
|
namespace: &ModuleNamespace<Self>,
|
||||||
@@ -188,5 +190,7 @@ pub fn default_libcall_names() -> Box<dyn Fn(ir::LibCall) -> String> {
|
|||||||
ir::LibCall::Memcpy => "memcpy".to_owned(),
|
ir::LibCall::Memcpy => "memcpy".to_owned(),
|
||||||
ir::LibCall::Memset => "memset".to_owned(),
|
ir::LibCall::Memset => "memset".to_owned(),
|
||||||
ir::LibCall::Memmove => "memmove".to_owned(),
|
ir::LibCall::Memmove => "memmove".to_owned(),
|
||||||
|
|
||||||
|
ir::LibCall::ElfTlsGetAddr => "__tls_get_addr".to_owned(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ pub struct DataDeclaration {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub linkage: Linkage,
|
pub linkage: Linkage,
|
||||||
pub writable: bool,
|
pub writable: bool,
|
||||||
|
pub tls: bool,
|
||||||
pub align: Option<u8>,
|
pub align: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,10 +207,14 @@ impl<B> ModuleData<B>
|
|||||||
where
|
where
|
||||||
B: Backend,
|
B: Backend,
|
||||||
{
|
{
|
||||||
fn merge(&mut self, linkage: Linkage, writable: bool, align: Option<u8>) {
|
fn merge(&mut self, linkage: Linkage, writable: bool, tls: bool, align: Option<u8>) {
|
||||||
self.decl.linkage = Linkage::merge(self.decl.linkage, linkage);
|
self.decl.linkage = Linkage::merge(self.decl.linkage, linkage);
|
||||||
self.decl.writable = self.decl.writable || writable;
|
self.decl.writable = self.decl.writable || writable;
|
||||||
self.decl.align = self.decl.align.max(align);
|
self.decl.align = self.decl.align.max(align);
|
||||||
|
assert_eq!(
|
||||||
|
self.decl.tls, tls,
|
||||||
|
"Can't change TLS data object to normal or in the opposite way",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,6 +465,7 @@ where
|
|||||||
name: &str,
|
name: &str,
|
||||||
linkage: Linkage,
|
linkage: Linkage,
|
||||||
writable: bool,
|
writable: bool,
|
||||||
|
tls: bool,
|
||||||
align: Option<u8>, // An alignment bigger than 128 is unlikely
|
align: Option<u8>, // An alignment bigger than 128 is unlikely
|
||||||
) -> ModuleResult<DataId> {
|
) -> ModuleResult<DataId> {
|
||||||
// TODO: Can we avoid allocating names so often?
|
// TODO: Can we avoid allocating names so often?
|
||||||
@@ -468,12 +474,13 @@ where
|
|||||||
Occupied(entry) => match *entry.get() {
|
Occupied(entry) => match *entry.get() {
|
||||||
FuncOrDataId::Data(id) => {
|
FuncOrDataId::Data(id) => {
|
||||||
let existing = &mut self.contents.data_objects[id];
|
let existing = &mut self.contents.data_objects[id];
|
||||||
existing.merge(linkage, writable, align);
|
existing.merge(linkage, writable, tls, align);
|
||||||
self.backend.declare_data(
|
self.backend.declare_data(
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
existing.decl.linkage,
|
existing.decl.linkage,
|
||||||
existing.decl.writable,
|
existing.decl.writable,
|
||||||
|
existing.decl.tls,
|
||||||
existing.decl.align,
|
existing.decl.align,
|
||||||
);
|
);
|
||||||
Ok(id)
|
Ok(id)
|
||||||
@@ -489,13 +496,14 @@ where
|
|||||||
name: name.to_owned(),
|
name: name.to_owned(),
|
||||||
linkage,
|
linkage,
|
||||||
writable,
|
writable,
|
||||||
|
tls,
|
||||||
align,
|
align,
|
||||||
},
|
},
|
||||||
compiled: None,
|
compiled: None,
|
||||||
});
|
});
|
||||||
entry.insert(FuncOrDataId::Data(id));
|
entry.insert(FuncOrDataId::Data(id));
|
||||||
self.backend
|
self.backend
|
||||||
.declare_data(id, name, linkage, writable, align);
|
.declare_data(id, name, linkage, writable, tls, align);
|
||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -526,6 +534,7 @@ where
|
|||||||
name: ir::ExternalName::user(1, data.as_u32()),
|
name: ir::ExternalName::user(1, data.as_u32()),
|
||||||
offset: ir::immediates::Imm64::new(0),
|
offset: ir::immediates::Imm64::new(0),
|
||||||
colocated,
|
colocated,
|
||||||
|
tls: decl.tls,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,6 +643,7 @@ where
|
|||||||
data,
|
data,
|
||||||
&info.decl.name,
|
&info.decl.name,
|
||||||
info.decl.writable,
|
info.decl.writable,
|
||||||
|
info.decl.tls,
|
||||||
info.decl.align,
|
info.decl.align,
|
||||||
data_ctx,
|
data_ctx,
|
||||||
&ModuleNamespace::<B> {
|
&ModuleNamespace::<B> {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ edition = "2018"
|
|||||||
cranelift-module = { path = "../cranelift-module", version = "0.59.0" }
|
cranelift-module = { path = "../cranelift-module", version = "0.59.0" }
|
||||||
object = { version = "0.17", default-features = false, features = ["write"] }
|
object = { version = "0.17", default-features = false, features = ["write"] }
|
||||||
target-lexicon = "0.10"
|
target-lexicon = "0.10"
|
||||||
|
goblin = "0.1.0"
|
||||||
|
|
||||||
[dependencies.cranelift-codegen]
|
[dependencies.cranelift-codegen]
|
||||||
path = "../cranelift-codegen"
|
path = "../cranelift-codegen"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use object::write::{
|
|||||||
use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope};
|
use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use target_lexicon::PointerWidth;
|
use target_lexicon::{BinaryFormat, PointerWidth};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// Setting to enable collection of traps. Setting this to `Enabled` in
|
/// Setting to enable collection of traps. Setting this to `Enabled` in
|
||||||
@@ -151,12 +151,19 @@ impl Backend for ObjectBackend {
|
|||||||
name: &str,
|
name: &str,
|
||||||
linkage: Linkage,
|
linkage: Linkage,
|
||||||
_writable: bool,
|
_writable: bool,
|
||||||
|
tls: bool,
|
||||||
_align: Option<u8>,
|
_align: Option<u8>,
|
||||||
) {
|
) {
|
||||||
|
let kind = if tls {
|
||||||
|
SymbolKind::Tls
|
||||||
|
} else {
|
||||||
|
SymbolKind::Data
|
||||||
|
};
|
||||||
let (scope, weak) = translate_linkage(linkage);
|
let (scope, weak) = translate_linkage(linkage);
|
||||||
|
|
||||||
if let Some(data) = self.data_objects[id] {
|
if let Some(data) = self.data_objects[id] {
|
||||||
let symbol = self.object.symbol_mut(data);
|
let symbol = self.object.symbol_mut(data);
|
||||||
|
symbol.kind = kind;
|
||||||
symbol.scope = scope;
|
symbol.scope = scope;
|
||||||
symbol.weak = weak;
|
symbol.weak = weak;
|
||||||
} else {
|
} else {
|
||||||
@@ -164,7 +171,7 @@ impl Backend for ObjectBackend {
|
|||||||
name: name.as_bytes().to_vec(),
|
name: name.as_bytes().to_vec(),
|
||||||
value: 0,
|
value: 0,
|
||||||
size: 0,
|
size: 0,
|
||||||
kind: SymbolKind::Data,
|
kind,
|
||||||
scope,
|
scope,
|
||||||
weak,
|
weak,
|
||||||
section: SymbolSection::Undefined,
|
section: SymbolSection::Undefined,
|
||||||
@@ -183,7 +190,7 @@ impl Backend for ObjectBackend {
|
|||||||
code_size: u32,
|
code_size: u32,
|
||||||
) -> ModuleResult<ObjectCompiledFunction> {
|
) -> ModuleResult<ObjectCompiledFunction> {
|
||||||
let mut code: Vec<u8> = vec![0; code_size as usize];
|
let mut code: Vec<u8> = vec![0; code_size as usize];
|
||||||
let mut reloc_sink = ObjectRelocSink::default();
|
let mut reloc_sink = ObjectRelocSink::new(self.object.format());
|
||||||
let mut trap_sink = ObjectTrapSink::default();
|
let mut trap_sink = ObjectTrapSink::default();
|
||||||
let mut stackmap_sink = NullStackmapSink {};
|
let mut stackmap_sink = NullStackmapSink {};
|
||||||
|
|
||||||
@@ -248,6 +255,7 @@ impl Backend for ObjectBackend {
|
|||||||
data_id: DataId,
|
data_id: DataId,
|
||||||
_name: &str,
|
_name: &str,
|
||||||
writable: bool,
|
writable: bool,
|
||||||
|
tls: bool,
|
||||||
align: Option<u8>,
|
align: Option<u8>,
|
||||||
data_ctx: &DataContext,
|
data_ctx: &DataContext,
|
||||||
_namespace: &ModuleNamespace<Self>,
|
_namespace: &ModuleNamespace<Self>,
|
||||||
@@ -289,7 +297,13 @@ impl Backend for ObjectBackend {
|
|||||||
|
|
||||||
let symbol = self.data_objects[data_id].unwrap();
|
let symbol = self.data_objects[data_id].unwrap();
|
||||||
let section_kind = if let Init::Zeros { .. } = *init {
|
let section_kind = if let Init::Zeros { .. } = *init {
|
||||||
StandardSection::UninitializedData
|
if tls {
|
||||||
|
StandardSection::UninitializedTls
|
||||||
|
} else {
|
||||||
|
StandardSection::UninitializedData
|
||||||
|
}
|
||||||
|
} else if tls {
|
||||||
|
StandardSection::Tls
|
||||||
} else if writable {
|
} else if writable {
|
||||||
StandardSection::Data
|
StandardSection::Data
|
||||||
} else if relocs.is_empty() {
|
} else if relocs.is_empty() {
|
||||||
@@ -519,11 +533,20 @@ struct RelocRecord {
|
|||||||
addend: Addend,
|
addend: Addend,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct ObjectRelocSink {
|
struct ObjectRelocSink {
|
||||||
|
format: BinaryFormat,
|
||||||
relocs: Vec<RelocRecord>,
|
relocs: Vec<RelocRecord>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ObjectRelocSink {
|
||||||
|
fn new(format: BinaryFormat) -> Self {
|
||||||
|
Self {
|
||||||
|
format,
|
||||||
|
relocs: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl RelocSink for ObjectRelocSink {
|
impl RelocSink for ObjectRelocSink {
|
||||||
fn reloc_block(&mut self, _offset: CodeOffset, _reloc: Reloc, _block_offset: CodeOffset) {
|
fn reloc_block(&mut self, _offset: CodeOffset, _reloc: Reloc, _block_offset: CodeOffset) {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
@@ -534,7 +557,7 @@ impl RelocSink for ObjectRelocSink {
|
|||||||
offset: CodeOffset,
|
offset: CodeOffset,
|
||||||
reloc: Reloc,
|
reloc: Reloc,
|
||||||
name: &ir::ExternalName,
|
name: &ir::ExternalName,
|
||||||
addend: Addend,
|
mut addend: Addend,
|
||||||
) {
|
) {
|
||||||
let (kind, encoding, size) = match reloc {
|
let (kind, encoding, size) = match reloc {
|
||||||
Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32),
|
Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32),
|
||||||
@@ -549,6 +572,35 @@ impl RelocSink for ObjectRelocSink {
|
|||||||
32,
|
32,
|
||||||
),
|
),
|
||||||
Reloc::X86GOTPCRel4 => (RelocationKind::GotRelative, RelocationEncoding::Generic, 32),
|
Reloc::X86GOTPCRel4 => (RelocationKind::GotRelative, RelocationEncoding::Generic, 32),
|
||||||
|
|
||||||
|
Reloc::ElfX86_64TlsGd => {
|
||||||
|
assert_eq!(
|
||||||
|
self.format,
|
||||||
|
BinaryFormat::Elf,
|
||||||
|
"ElfX86_64TlsGd is not supported for this file format"
|
||||||
|
);
|
||||||
|
(
|
||||||
|
RelocationKind::Elf(goblin::elf64::reloc::R_X86_64_TLSGD),
|
||||||
|
RelocationEncoding::Generic,
|
||||||
|
32,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Reloc::MachOX86_64Tlv => {
|
||||||
|
assert_eq!(
|
||||||
|
self.format,
|
||||||
|
BinaryFormat::Macho,
|
||||||
|
"MachOX86_64Tlv is not supported for this file format"
|
||||||
|
);
|
||||||
|
addend += 4; // X86_64_RELOC_TLV has an implicit addend of -4
|
||||||
|
(
|
||||||
|
RelocationKind::MachO {
|
||||||
|
value: goblin::mach::relocation::X86_64_RELOC_TLV,
|
||||||
|
relative: true,
|
||||||
|
},
|
||||||
|
RelocationEncoding::Generic,
|
||||||
|
32,
|
||||||
|
)
|
||||||
|
}
|
||||||
// FIXME
|
// FIXME
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -204,6 +204,7 @@ impl<'a> Context<'a> {
|
|||||||
name: ExternalName::testcase(""),
|
name: ExternalName::testcase(""),
|
||||||
offset: Imm64::new(0),
|
offset: Imm64::new(0),
|
||||||
colocated: false,
|
colocated: false,
|
||||||
|
tls: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.function.global_values[gv] = data;
|
self.function.global_values[gv] = data;
|
||||||
@@ -1443,12 +1444,14 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
"symbol" => {
|
"symbol" => {
|
||||||
let colocated = self.optional(Token::Identifier("colocated"));
|
let colocated = self.optional(Token::Identifier("colocated"));
|
||||||
|
let tls = self.optional(Token::Identifier("tls"));
|
||||||
let name = self.parse_external_name()?;
|
let name = self.parse_external_name()?;
|
||||||
let offset = self.optional_offset_imm64()?;
|
let offset = self.optional_offset_imm64()?;
|
||||||
GlobalValueData::Symbol {
|
GlobalValueData::Symbol {
|
||||||
name,
|
name,
|
||||||
offset,
|
offset,
|
||||||
colocated,
|
colocated,
|
||||||
|
tls,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
other => return err!(self.loc, "Unknown global value kind '{}'", other),
|
other => return err!(self.loc, "Unknown global value kind '{}'", other),
|
||||||
|
|||||||
@@ -264,8 +264,10 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
|
|||||||
_name: &str,
|
_name: &str,
|
||||||
_linkage: Linkage,
|
_linkage: Linkage,
|
||||||
_writable: bool,
|
_writable: bool,
|
||||||
|
tls: bool,
|
||||||
_align: Option<u8>,
|
_align: Option<u8>,
|
||||||
) {
|
) {
|
||||||
|
assert!(!tls, "SimpleJIT doesn't yet support TLS");
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,10 +343,13 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
|
|||||||
_id: DataId,
|
_id: DataId,
|
||||||
_name: &str,
|
_name: &str,
|
||||||
writable: bool,
|
writable: bool,
|
||||||
|
tls: bool,
|
||||||
align: Option<u8>,
|
align: Option<u8>,
|
||||||
data: &DataContext,
|
data: &DataContext,
|
||||||
_namespace: &ModuleNamespace<Self>,
|
_namespace: &ModuleNamespace<Self>,
|
||||||
) -> ModuleResult<Self::CompiledData> {
|
) -> ModuleResult<Self::CompiledData> {
|
||||||
|
assert!(!tls, "SimpleJIT doesn't yet support TLS");
|
||||||
|
|
||||||
let &DataDescription {
|
let &DataDescription {
|
||||||
ref init,
|
ref init,
|
||||||
ref function_decls,
|
ref function_decls,
|
||||||
|
|||||||
Reference in New Issue
Block a user