mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Fix string argument loading and refcount functions
This commit is contained in:
parent
6205816db4
commit
a7725a81d5
5 changed files with 97 additions and 46 deletions
|
@ -68,6 +68,9 @@ pub struct AArch64Call {}
|
|||
const STACK_ALIGNMENT: u8 = 16;
|
||||
|
||||
impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||
const BASE_PTR_REG: AArch64GeneralReg = AArch64GeneralReg::FP;
|
||||
const STACK_PTR_REG: AArch64GeneralReg = AArch64GeneralReg::ZRSP;
|
||||
|
||||
const GENERAL_PARAM_REGS: &'static [AArch64GeneralReg] = &[
|
||||
AArch64GeneralReg::X0,
|
||||
AArch64GeneralReg::X1,
|
||||
|
@ -241,7 +244,8 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
|||
_symbol_map: &mut MutMap<Symbol, SymbolStorage<AArch64GeneralReg, AArch64FloatReg>>,
|
||||
_args: &'a [(Layout<'a>, Symbol)],
|
||||
_ret_layout: &Layout<'a>,
|
||||
) {
|
||||
mut _stack_size: u32,
|
||||
) -> u32 {
|
||||
unimplemented!("Loading args not yet implemented for AArch64");
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@ pub mod x86_64;
|
|||
const PTR_SIZE: u32 = 8;
|
||||
|
||||
pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||
const BASE_PTR_REG: GeneralReg;
|
||||
const STACK_PTR_REG: GeneralReg;
|
||||
|
||||
const GENERAL_PARAM_REGS: &'static [GeneralReg];
|
||||
const GENERAL_RETURN_REGS: &'static [GeneralReg];
|
||||
const GENERAL_DEFAULT_FREE_REGS: &'static [GeneralReg];
|
||||
|
@ -50,13 +53,15 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
|||
);
|
||||
|
||||
// load_args updates the symbol map to know where every arg is stored.
|
||||
// It returns the total stack space after loading the args.
|
||||
fn load_args<'a>(
|
||||
buf: &mut Vec<'a, u8>,
|
||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
||||
ret_layout: &Layout<'a>,
|
||||
);
|
||||
stack_size: u32,
|
||||
) -> u32;
|
||||
|
||||
// store_args stores the args in registers and on the stack for function calling.
|
||||
// It returns the amount of stack space needed to temporarily store the args.
|
||||
|
@ -316,6 +321,9 @@ impl<
|
|||
fn refcount_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)> {
|
||||
&mut self.refcount_proc_symbols
|
||||
}
|
||||
fn refcount_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)> {
|
||||
&self.refcount_proc_symbols
|
||||
}
|
||||
|
||||
fn reset(&mut self, name: String, is_self_recursive: SelfRecursive) {
|
||||
self.proc_name = Some(name);
|
||||
|
@ -361,7 +369,7 @@ impl<
|
|||
&mut self.free_map
|
||||
}
|
||||
|
||||
fn finalize(&mut self) -> (&'a [u8], &[Relocation]) {
|
||||
fn finalize(&mut self) -> (Vec<u8>, Vec<Relocation>) {
|
||||
let mut out = bumpalo::vec![in self.env.arena];
|
||||
|
||||
// Setup stack.
|
||||
|
@ -450,15 +458,16 @@ impl<
|
|||
Relocation::JmpToReturn { .. } => unreachable!(),
|
||||
}),
|
||||
);
|
||||
(out.into_bump_slice(), out_relocs.into_bump_slice())
|
||||
(out, out_relocs)
|
||||
}
|
||||
|
||||
fn load_args(&mut self, args: &'a [(Layout<'a>, Symbol)], ret_layout: &Layout<'a>) {
|
||||
CC::load_args(
|
||||
self.stack_size = CC::load_args(
|
||||
&mut self.buf,
|
||||
&mut self.symbol_storage_map,
|
||||
args,
|
||||
ret_layout,
|
||||
self.stack_size,
|
||||
);
|
||||
// Update used and free regs.
|
||||
for (sym, storage) in &self.symbol_storage_map {
|
||||
|
|
|
@ -60,6 +60,9 @@ pub struct X86_64SystemV {}
|
|||
const STACK_ALIGNMENT: u8 = 16;
|
||||
|
||||
impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||
const BASE_PTR_REG: X86_64GeneralReg = X86_64GeneralReg::RBP;
|
||||
const STACK_PTR_REG: X86_64GeneralReg = X86_64GeneralReg::RSP;
|
||||
|
||||
const GENERAL_PARAM_REGS: &'static [X86_64GeneralReg] = &[
|
||||
X86_64GeneralReg::RDI,
|
||||
X86_64GeneralReg::RSI,
|
||||
|
@ -183,8 +186,9 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
ret_layout: &Layout<'a>,
|
||||
) {
|
||||
let mut base_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
||||
mut stack_size: u32,
|
||||
) -> u32 {
|
||||
let mut arg_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
||||
let mut general_i = 0;
|
||||
let mut float_i = 0;
|
||||
if X86_64SystemV::returns_via_arg_pointer(ret_layout) {
|
||||
|
@ -204,11 +208,11 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
);
|
||||
general_i += 1;
|
||||
} else {
|
||||
base_offset += 8;
|
||||
arg_offset += 8;
|
||||
symbol_map.insert(
|
||||
*sym,
|
||||
SymbolStorage::Base {
|
||||
offset: base_offset,
|
||||
offset: arg_offset,
|
||||
size: 8,
|
||||
owned: true,
|
||||
},
|
||||
|
@ -223,11 +227,11 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
);
|
||||
float_i += 1;
|
||||
} else {
|
||||
base_offset += 8;
|
||||
arg_offset += 8;
|
||||
symbol_map.insert(
|
||||
*sym,
|
||||
SymbolStorage::Base {
|
||||
offset: base_offset,
|
||||
offset: arg_offset,
|
||||
size: 8,
|
||||
owned: true,
|
||||
},
|
||||
|
@ -236,16 +240,17 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
}
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
if general_i + 1 < Self::GENERAL_PARAM_REGS.len() {
|
||||
// Load the value to the param reg.
|
||||
let dst1 = Self::GENERAL_PARAM_REGS[general_i];
|
||||
let dst2 = Self::GENERAL_PARAM_REGS[general_i + 1];
|
||||
base_offset += 16;
|
||||
X86_64Assembler::mov_reg64_base32(buf, dst1, base_offset - 8);
|
||||
X86_64Assembler::mov_reg64_base32(buf, dst2, base_offset);
|
||||
// Load the value from the param reg into a useable base offset.
|
||||
let src1 = Self::GENERAL_PARAM_REGS[general_i];
|
||||
let src2 = Self::GENERAL_PARAM_REGS[general_i + 1];
|
||||
stack_size += 16;
|
||||
let offset = -(stack_size as i32);
|
||||
X86_64Assembler::mov_base32_reg64(buf, offset, src1);
|
||||
X86_64Assembler::mov_base32_reg64(buf, offset + 8, src2);
|
||||
symbol_map.insert(
|
||||
*sym,
|
||||
SymbolStorage::Base {
|
||||
offset: base_offset,
|
||||
offset,
|
||||
size: 16,
|
||||
owned: true,
|
||||
},
|
||||
|
@ -261,6 +266,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
}
|
||||
}
|
||||
}
|
||||
stack_size
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -444,6 +450,9 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
}
|
||||
|
||||
impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||
const BASE_PTR_REG: X86_64GeneralReg = X86_64GeneralReg::RBP;
|
||||
const STACK_PTR_REG: X86_64GeneralReg = X86_64GeneralReg::RSP;
|
||||
|
||||
const GENERAL_PARAM_REGS: &'static [X86_64GeneralReg] = &[
|
||||
X86_64GeneralReg::RCX,
|
||||
X86_64GeneralReg::RDX,
|
||||
|
@ -561,8 +570,9 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
ret_layout: &Layout<'a>,
|
||||
) {
|
||||
let mut base_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
||||
stack_size: u32,
|
||||
) -> u32 {
|
||||
let mut arg_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
||||
let mut i = 0;
|
||||
if X86_64WindowsFastcall::returns_via_arg_pointer(ret_layout) {
|
||||
symbol_map.insert(
|
||||
|
@ -595,7 +605,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
base_offset += match layout {
|
||||
arg_offset += match layout {
|
||||
single_register_builtins!() => 8,
|
||||
x => {
|
||||
unimplemented!("Loading args with layout {:?} not yet implemented", x);
|
||||
|
@ -604,13 +614,14 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
symbol_map.insert(
|
||||
*sym,
|
||||
SymbolStorage::Base {
|
||||
offset: base_offset,
|
||||
offset: arg_offset,
|
||||
size: 8,
|
||||
owned: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
stack_size
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
@ -56,7 +56,7 @@ impl<'a> Env<'a> {
|
|||
|
||||
// These relocations likely will need a length.
|
||||
// They may even need more definition, but this should be at least good enough for how we will use elf.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Relocation {
|
||||
LocalData {
|
||||
|
@ -93,6 +93,8 @@ where
|
|||
|
||||
fn refcount_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||
|
||||
fn refcount_proc_symbols(&self) -> &Vec<'a, (Symbol, ProcLayout<'a>)>;
|
||||
|
||||
/// reset resets any registers or other values that may be occupied at the end of a procedure.
|
||||
/// It also passes basic procedure information to the builder for setup of the next function.
|
||||
fn reset(&mut self, name: String, is_self_recursive: SelfRecursive);
|
||||
|
@ -101,7 +103,7 @@ where
|
|||
/// finalize does setup because things like stack size and jump locations are not know until the function is written.
|
||||
/// For example, this can store the frame pointer and setup stack space.
|
||||
/// finalize is run at the end of build_proc when all internal code is finalized.
|
||||
fn finalize(&mut self) -> (&'a [u8], &[Relocation]);
|
||||
fn finalize(&mut self) -> (Vec<u8>, Vec<Relocation>);
|
||||
|
||||
// load_args is used to let the backend know what the args are.
|
||||
// The backend should track these args so it can use them as needed.
|
||||
|
@ -111,7 +113,7 @@ where
|
|||
fn build_wrapped_jmp(&mut self) -> (&'a [u8], u64);
|
||||
|
||||
/// build_proc creates a procedure and outputs it to the wrapped object writer.
|
||||
fn build_proc(&mut self, proc: Proc<'a>) -> (&'a [u8], &[Relocation]) {
|
||||
fn build_proc(&mut self, proc: Proc<'a>) -> (Vec<u8>, Vec<Relocation>) {
|
||||
let layout_id = LayoutIds::default().get(proc.name, &proc.ret_layout);
|
||||
let proc_name = self.env().symbol_to_string(proc.name, layout_id);
|
||||
self.reset(proc_name, proc.is_self_recursive);
|
||||
|
@ -278,16 +280,8 @@ where
|
|||
let layout_id = LayoutIds::default().get(*func_sym, layout);
|
||||
let fn_name = self.env().symbol_to_string(*func_sym, layout_id);
|
||||
// Now that the arguments are needed, load them if they are literals.
|
||||
if fn_name == "#UserApp_#rcDec_str_0_1"
|
||||
|| fn_name == "#UserApp_#rcDec_str_1_1"
|
||||
|| fn_name == "#UserApp_#rcInc_str_0_1"
|
||||
{
|
||||
// Skip calling the function. For some reason it is currently being inlined.
|
||||
return;
|
||||
} else {
|
||||
self.load_literal_symbols(arguments);
|
||||
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
|
||||
}
|
||||
self.load_literal_symbols(arguments);
|
||||
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
|
||||
} else {
|
||||
self.build_inline_builtin(
|
||||
sym,
|
||||
|
|
|
@ -229,6 +229,7 @@ fn build_object<'a, B: Backend<'a>>(
|
|||
&mut output,
|
||||
&mut backend,
|
||||
&mut relocations,
|
||||
&mut layout_ids,
|
||||
data_section,
|
||||
fn_name,
|
||||
section_id,
|
||||
|
@ -260,15 +261,15 @@ fn build_object<'a, B: Backend<'a>>(
|
|||
|
||||
// Names and linker data for refcounting procedures
|
||||
for ((sym, layout), proc) in rc_symbols_and_layouts.into_iter().zip(rc_procs) {
|
||||
build_proc_symbol(
|
||||
&mut output,
|
||||
&mut layout_ids,
|
||||
&mut rc_names_symbols_procs,
|
||||
backend.env(),
|
||||
sym,
|
||||
layout,
|
||||
proc,
|
||||
)
|
||||
let layout_id = layout_ids.get_toplevel(sym, &layout);
|
||||
let fn_name = backend.env().symbol_to_string(sym, layout_id);
|
||||
if let Some(proc_id) = output.symbol_id(fn_name.as_bytes()) {
|
||||
if let SymbolSection::Section(section_id) = output.symbol(proc_id).section {
|
||||
rc_names_symbols_procs.push((fn_name, section_id, proc_id, proc));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
internal_error!("failed to create rc fn for symbol {:?}", sym);
|
||||
}
|
||||
|
||||
// Build refcounting procedures
|
||||
|
@ -277,6 +278,7 @@ fn build_object<'a, B: Backend<'a>>(
|
|||
&mut output,
|
||||
&mut backend,
|
||||
&mut relocations,
|
||||
&mut layout_ids,
|
||||
data_section,
|
||||
fn_name,
|
||||
section_id,
|
||||
|
@ -344,6 +346,7 @@ fn build_proc<'a, B: Backend<'a>>(
|
|||
output: &mut Object,
|
||||
backend: &mut B,
|
||||
relocations: &mut Vec<'a, (SectionId, object::write::Relocation)>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
data_section: SectionId,
|
||||
fn_name: String,
|
||||
section_id: SectionId,
|
||||
|
@ -352,8 +355,12 @@ fn build_proc<'a, B: Backend<'a>>(
|
|||
) {
|
||||
let mut local_data_index = 0;
|
||||
let (proc_data, relocs) = backend.build_proc(proc);
|
||||
let proc_offset = output.add_symbol_data(proc_id, section_id, proc_data, 16);
|
||||
for reloc in relocs {
|
||||
let proc_offset = output.add_symbol_data(proc_id, section_id, &proc_data, 16);
|
||||
// TODO: figure out the borrowing here and fix this hack.
|
||||
let relocs2 = relocs.to_vec();
|
||||
std::mem::drop(proc_data);
|
||||
std::mem::drop(relocs);
|
||||
for reloc in relocs2.iter() {
|
||||
let elfreloc = match reloc {
|
||||
Relocation::LocalData { offset, data } => {
|
||||
let data_symbol = write::Symbol {
|
||||
|
@ -409,6 +416,32 @@ fn build_proc<'a, B: Backend<'a>>(
|
|||
};
|
||||
output.add_symbol(builtin_symbol);
|
||||
}
|
||||
// If the symbol is an undefined reference counting procedure, we need to add it here.
|
||||
if output.symbol_id(name.as_bytes()) == None {
|
||||
for (sym, layout) in backend.refcount_proc_symbols().iter() {
|
||||
let layout_id = layout_ids.get_toplevel(*sym, layout);
|
||||
let rc_name = backend.env().symbol_to_string(*sym, layout_id);
|
||||
if name == &rc_name {
|
||||
let section_id = output.add_section(
|
||||
output.segment_name(StandardSegment::Text).to_vec(),
|
||||
format!(".text.{:x}", sym.as_u64()).as_bytes().to_vec(),
|
||||
SectionKind::Text,
|
||||
);
|
||||
|
||||
let rc_symbol = Symbol {
|
||||
name: name.as_bytes().to_vec(),
|
||||
value: 0,
|
||||
size: 0,
|
||||
kind: SymbolKind::Text,
|
||||
scope: SymbolScope::Linkage,
|
||||
weak: false,
|
||||
section: SymbolSection::Section(section_id),
|
||||
flags: SymbolFlags::None,
|
||||
};
|
||||
output.add_symbol(rc_symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(sym_id) = output.symbol_id(name.as_bytes()) {
|
||||
write::Relocation {
|
||||
offset: offset + proc_offset,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue