mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
WIP:struct
This commit is contained in:
parent
e56c46ff54
commit
2fc46b8752
5 changed files with 115 additions and 34 deletions
|
@ -228,6 +228,7 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||||
fn load_args<'a>(
|
fn load_args<'a>(
|
||||||
_symbol_map: &mut MutMap<Symbol, SymbolStorage<AArch64GeneralReg, AArch64FloatReg>>,
|
_symbol_map: &mut MutMap<Symbol, SymbolStorage<AArch64GeneralReg, AArch64FloatReg>>,
|
||||||
_args: &'a [(Layout<'a>, Symbol)],
|
_args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
_ret_layout: &Layout<'a>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
Err("Loading args not yet implemented for AArch64".to_string())
|
Err("Loading args not yet implemented for AArch64".to_string())
|
||||||
}
|
}
|
||||||
|
@ -242,6 +243,16 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||||
) -> Result<u32, String> {
|
) -> Result<u32, String> {
|
||||||
Err("Storing args not yet implemented for AArch64".to_string())
|
Err("Storing args not yet implemented for AArch64".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn return_struct<'a>(
|
||||||
|
_buf: &mut Vec<'a, u8>,
|
||||||
|
_struct_offset: i32,
|
||||||
|
_struct_size: u32,
|
||||||
|
_field_layouts: &[Layout<'a>],
|
||||||
|
_ret_reg: Option<AArch64GeneralReg>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
Err("Returning structs not yet implemented for AArch64".to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
|
|
|
@ -51,6 +51,8 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
fn load_args<'a>(
|
fn load_args<'a>(
|
||||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
|
symbol_map: &mut MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
|
||||||
args: &'a [(Layout<'a>, Symbol)],
|
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>,
|
||||||
) -> Result<(), String>;
|
) -> Result<(), String>;
|
||||||
|
|
||||||
// store_args stores the args in registers and on the stack for function calling.
|
// store_args stores the args in registers and on the stack for function calling.
|
||||||
|
@ -63,6 +65,16 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
) -> Result<u32, String>;
|
) -> Result<u32, String>;
|
||||||
|
|
||||||
|
// return_struct returns a struct currently on the stack at `struct_offset`.
|
||||||
|
// It does so using registers and stack as necessary.
|
||||||
|
fn return_struct<'a>(
|
||||||
|
buf: &mut Vec<'a, u8>,
|
||||||
|
struct_offset: i32,
|
||||||
|
struct_size: u32,
|
||||||
|
field_layouts: &[Layout<'a>],
|
||||||
|
ret_reg: Option<GeneralReg>,
|
||||||
|
) -> Result<(), String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assembler contains calls to the backend assembly generator.
|
/// Assembler contains calls to the backend assembly generator.
|
||||||
|
@ -309,8 +321,12 @@ impl<
|
||||||
Ok((out.into_bump_slice(), out_relocs.into_bump_slice()))
|
Ok((out.into_bump_slice(), out_relocs.into_bump_slice()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_args(&mut self, args: &'a [(Layout<'a>, Symbol)]) -> Result<(), String> {
|
fn load_args(
|
||||||
CC::load_args(&mut self.symbol_storage_map, args)?;
|
&mut self,
|
||||||
|
args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
CC::load_args(&mut self.symbol_storage_map, args, ret_layout)?;
|
||||||
// Update used and free regs.
|
// Update used and free regs.
|
||||||
for (sym, storage) in &self.symbol_storage_map {
|
for (sym, storage) in &self.symbol_storage_map {
|
||||||
match storage {
|
match storage {
|
||||||
|
@ -625,36 +641,29 @@ impl<
|
||||||
ASM::mov_freg64_base32(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *offset);
|
ASM::mov_freg64_base32(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *offset);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Layout::Struct(
|
Layout::Struct(field_layouts) => {
|
||||||
&[Layout::Builtin(Builtin::Int64), Layout::Builtin(Builtin::Int64)],
|
|
||||||
) => {
|
|
||||||
if let Some(SymbolStorage::Base(struct_offset)) =
|
|
||||||
self.symbol_storage_map.get(sym)
|
|
||||||
{
|
|
||||||
ASM::mov_reg64_base32(
|
|
||||||
&mut self.buf,
|
|
||||||
CC::GENERAL_RETURN_REGS[0],
|
|
||||||
*struct_offset,
|
|
||||||
);
|
|
||||||
ASM::mov_reg64_base32(
|
|
||||||
&mut self.buf,
|
|
||||||
CC::GENERAL_RETURN_REGS[1],
|
|
||||||
*struct_offset + 8,
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(format!("unknown struct: {:?}", sym))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Layout::Struct(_field_layouts) => {
|
|
||||||
let struct_size = layout.stack_size(PTR_SIZE);
|
let struct_size = layout.stack_size(PTR_SIZE);
|
||||||
if struct_size > 0 {
|
if struct_size > 0 {
|
||||||
// Need at actually dispatch to call conv here since struct return is specific to that.
|
let struct_offset = if let Some(SymbolStorage::Base(offset)) =
|
||||||
// CC::return_struct()
|
self.symbol_storage_map.get(sym)
|
||||||
Err(format!(
|
{
|
||||||
"Returning struct with layout, {:?}, is not yet implemented",
|
Ok(*offset)
|
||||||
layout
|
} else {
|
||||||
))
|
Err(format!("unknown struct: {:?}", sym))
|
||||||
|
}?;
|
||||||
|
let ret_reg = if self.symbol_storage_map.contains_key(&Symbol::RET_POINTER)
|
||||||
|
{
|
||||||
|
Some(self.load_to_general_reg(&Symbol::RET_POINTER)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
CC::return_struct(
|
||||||
|
&mut self.buf,
|
||||||
|
struct_offset,
|
||||||
|
struct_size,
|
||||||
|
field_layouts,
|
||||||
|
ret_reg,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// Nothing to do for empty struct
|
// Nothing to do for empty struct
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage};
|
use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage, PTR_SIZE};
|
||||||
use crate::Relocation;
|
use crate::Relocation;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
|
@ -55,6 +55,22 @@ pub struct X86_64SystemV {}
|
||||||
|
|
||||||
const STACK_ALIGNMENT: u8 = 16;
|
const STACK_ALIGNMENT: u8 = 16;
|
||||||
|
|
||||||
|
impl X86_64SystemV {
|
||||||
|
fn returns_via_arg_pointer<'a>(ret_layout: &Layout<'a>) -> Result<bool, String> {
|
||||||
|
// TODO: This may need to be more complex/extended to fully support the calling convention.
|
||||||
|
// details here: https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf
|
||||||
|
return Ok(ret_layout.stack_size(PTR_SIZE) > 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl X86_64WindowsFastcall {
|
||||||
|
fn returns_via_arg_pointer<'a>(ret_layout: &Layout<'a>) -> Result<bool, String> {
|
||||||
|
// TODO: This is not fully correct there are some exceptions for "vector" types.
|
||||||
|
// details here: https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-160#return-values
|
||||||
|
return Ok(ret_layout.stack_size(PTR_SIZE) > 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
const GENERAL_PARAM_REGS: &'static [X86_64GeneralReg] = &[
|
const GENERAL_PARAM_REGS: &'static [X86_64GeneralReg] = &[
|
||||||
X86_64GeneralReg::RDI,
|
X86_64GeneralReg::RDI,
|
||||||
|
@ -177,10 +193,18 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
fn load_args<'a>(
|
fn load_args<'a>(
|
||||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||||
args: &'a [(Layout<'a>, Symbol)],
|
args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let mut base_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
let mut base_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
||||||
let mut general_i = 0;
|
let mut general_i = 0;
|
||||||
let mut float_i = 0;
|
let mut float_i = 0;
|
||||||
|
if X86_64SystemV::returns_via_arg_pointer(ret_layout)? {
|
||||||
|
symbol_map.insert(
|
||||||
|
Symbol::RET_POINTER,
|
||||||
|
SymbolStorage::GeneralReg(Self::GENERAL_PARAM_REGS[general_i]),
|
||||||
|
);
|
||||||
|
general_i += 1;
|
||||||
|
}
|
||||||
for (layout, sym) in args.iter() {
|
for (layout, sym) in args.iter() {
|
||||||
match layout {
|
match layout {
|
||||||
Layout::Builtin(Builtin::Int64) => {
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
|
@ -359,6 +383,16 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
}
|
}
|
||||||
Ok(stack_offset as u32)
|
Ok(stack_offset as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn return_struct<'a>(
|
||||||
|
_buf: &mut Vec<'a, u8>,
|
||||||
|
_struct_offset: i32,
|
||||||
|
_struct_size: u32,
|
||||||
|
_field_layouts: &[Layout<'a>],
|
||||||
|
_ret_reg: Option<X86_64GeneralReg>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
Err("Returning structs not yet implemented for X86_64".to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
|
@ -477,9 +511,18 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
fn load_args<'a>(
|
fn load_args<'a>(
|
||||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||||
args: &'a [(Layout<'a>, Symbol)],
|
args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let mut base_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
let mut base_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
||||||
for (i, (layout, sym)) in args.iter().enumerate() {
|
let mut i = 0;
|
||||||
|
if X86_64WindowsFastcall::returns_via_arg_pointer(ret_layout)? {
|
||||||
|
symbol_map.insert(
|
||||||
|
Symbol::RET_POINTER,
|
||||||
|
SymbolStorage::GeneralReg(Self::GENERAL_PARAM_REGS[i]),
|
||||||
|
);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
for (layout, sym) in args.iter() {
|
||||||
if i < Self::GENERAL_PARAM_REGS.len() {
|
if i < Self::GENERAL_PARAM_REGS.len() {
|
||||||
match layout {
|
match layout {
|
||||||
Layout::Builtin(Builtin::Int64) => {
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
|
@ -496,6 +539,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
i += 1;
|
||||||
} else {
|
} else {
|
||||||
base_offset += match layout {
|
base_offset += match layout {
|
||||||
Layout::Builtin(Builtin::Int64) => 8,
|
Layout::Builtin(Builtin::Int64) => 8,
|
||||||
|
@ -653,6 +697,16 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
}
|
}
|
||||||
Ok(stack_offset as u32)
|
Ok(stack_offset as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn return_struct<'a>(
|
||||||
|
_buf: &mut Vec<'a, u8>,
|
||||||
|
_struct_offset: i32,
|
||||||
|
_struct_size: u32,
|
||||||
|
_field_layouts: &[Layout<'a>],
|
||||||
|
_ret_reg: Option<X86_64GeneralReg>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
Err("Returning structs not yet implemented for X86_64WindowsFastCall".to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -65,12 +65,16 @@ where
|
||||||
|
|
||||||
// load_args is used to let the backend know what the args are.
|
// 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.
|
// The backend should track these args so it can use them as needed.
|
||||||
fn load_args(&mut self, args: &'a [(Layout<'a>, Symbol)]) -> Result<(), String>;
|
fn load_args(
|
||||||
|
&mut self,
|
||||||
|
args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
) -> Result<(), String>;
|
||||||
|
|
||||||
/// build_proc creates a procedure and outputs it to the wrapped object writer.
|
/// build_proc creates a procedure and outputs it to the wrapped object writer.
|
||||||
fn build_proc(&mut self, proc: Proc<'a>) -> Result<(&'a [u8], &[Relocation]), String> {
|
fn build_proc(&mut self, proc: Proc<'a>) -> Result<(&'a [u8], &[Relocation]), String> {
|
||||||
self.reset();
|
self.reset();
|
||||||
self.load_args(&proc.args)?;
|
self.load_args(&proc.args, &proc.ret_layout)?;
|
||||||
// let start = std::time::Instant::now();
|
// let start = std::time::Instant::now();
|
||||||
self.scan_ast(&proc.body);
|
self.scan_ast(&proc.body);
|
||||||
self.create_free_map();
|
self.create_free_map();
|
||||||
|
|
|
@ -759,6 +759,9 @@ define_builtins! {
|
||||||
|
|
||||||
// a caller (wrapper) for comparison
|
// a caller (wrapper) for comparison
|
||||||
21 GENERIC_COMPARE_REF: "#generic_compare_ref"
|
21 GENERIC_COMPARE_REF: "#generic_compare_ref"
|
||||||
|
|
||||||
|
// used by the dev backend to store the pointer to where to store large return types
|
||||||
|
22 RET_POINTER: "#ret_pointer"
|
||||||
}
|
}
|
||||||
1 NUM: "Num" => {
|
1 NUM: "Num" => {
|
||||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue