mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41:12 +00:00
gen_dev: add ability to pass arguments to functions
This commit is contained in:
parent
01a86aaa9c
commit
95be1a1b6d
5 changed files with 590 additions and 81 deletions
|
@ -1,6 +1,9 @@
|
||||||
use crate::generic64::{Assembler, CallConv, RegTrait};
|
use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage};
|
||||||
use crate::Relocation;
|
use crate::Relocation;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
|
use roc_collections::all::MutMap;
|
||||||
|
use roc_module::symbol::Symbol;
|
||||||
|
use roc_mono::layout::Layout;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -138,10 +141,13 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
saved_regs: &[AArch64GeneralReg],
|
saved_regs: &[AArch64GeneralReg],
|
||||||
requested_stack_size: i32,
|
requested_stack_size: i32,
|
||||||
|
fn_call_stack_size: i32,
|
||||||
) -> Result<i32, String> {
|
) -> Result<i32, String> {
|
||||||
// Full size is upcast to i64 to make sure we don't overflow here.
|
// Full size is upcast to i64 to make sure we don't overflow here.
|
||||||
let full_stack_size = requested_stack_size
|
let full_stack_size = requested_stack_size
|
||||||
.checked_add(8 * saved_regs.len() as i32 + 8) // The extra 8 is space to store the frame pointer.
|
.checked_add(8 * saved_regs.len() as i32 + 8) // The extra 8 is space to store the frame pointer.
|
||||||
|
.ok_or("Ran out of stack space")?
|
||||||
|
.checked_add(fn_call_stack_size)
|
||||||
.ok_or("Ran out of stack space")?;
|
.ok_or("Ran out of stack space")?;
|
||||||
let alignment = if full_stack_size <= 0 {
|
let alignment = if full_stack_size <= 0 {
|
||||||
0
|
0
|
||||||
|
@ -155,6 +161,11 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||||
};
|
};
|
||||||
if let Some(aligned_stack_size) = full_stack_size.checked_add(offset as i32) {
|
if let Some(aligned_stack_size) = full_stack_size.checked_add(offset as i32) {
|
||||||
if aligned_stack_size > 0 {
|
if aligned_stack_size > 0 {
|
||||||
|
AArch64Assembler::mov_reg64_reg64(
|
||||||
|
buf,
|
||||||
|
AArch64GeneralReg::FP,
|
||||||
|
AArch64GeneralReg::ZRSP,
|
||||||
|
);
|
||||||
AArch64Assembler::sub_reg64_reg64_imm32(
|
AArch64Assembler::sub_reg64_reg64_imm32(
|
||||||
buf,
|
buf,
|
||||||
AArch64GeneralReg::ZRSP,
|
AArch64GeneralReg::ZRSP,
|
||||||
|
@ -168,9 +179,11 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||||
AArch64Assembler::mov_stack32_reg64(buf, offset, AArch64GeneralReg::LR);
|
AArch64Assembler::mov_stack32_reg64(buf, offset, AArch64GeneralReg::LR);
|
||||||
offset -= 8;
|
offset -= 8;
|
||||||
AArch64Assembler::mov_stack32_reg64(buf, offset, AArch64GeneralReg::FP);
|
AArch64Assembler::mov_stack32_reg64(buf, offset, AArch64GeneralReg::FP);
|
||||||
|
|
||||||
|
offset = aligned_stack_size - fn_call_stack_size;
|
||||||
for reg in saved_regs {
|
for reg in saved_regs {
|
||||||
offset -= 8;
|
offset -= 8;
|
||||||
AArch64Assembler::mov_stack32_reg64(buf, offset, *reg);
|
AArch64Assembler::mov_base32_reg64(buf, offset, *reg);
|
||||||
}
|
}
|
||||||
Ok(aligned_stack_size)
|
Ok(aligned_stack_size)
|
||||||
} else {
|
} else {
|
||||||
|
@ -186,6 +199,7 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
saved_regs: &[AArch64GeneralReg],
|
saved_regs: &[AArch64GeneralReg],
|
||||||
aligned_stack_size: i32,
|
aligned_stack_size: i32,
|
||||||
|
fn_call_stack_size: i32,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if aligned_stack_size > 0 {
|
if aligned_stack_size > 0 {
|
||||||
// All the following stores could be optimized by using `STP` to store pairs.
|
// All the following stores could be optimized by using `STP` to store pairs.
|
||||||
|
@ -194,9 +208,11 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||||
AArch64Assembler::mov_reg64_stack32(buf, AArch64GeneralReg::LR, offset);
|
AArch64Assembler::mov_reg64_stack32(buf, AArch64GeneralReg::LR, offset);
|
||||||
offset -= 8;
|
offset -= 8;
|
||||||
AArch64Assembler::mov_reg64_stack32(buf, AArch64GeneralReg::FP, offset);
|
AArch64Assembler::mov_reg64_stack32(buf, AArch64GeneralReg::FP, offset);
|
||||||
|
|
||||||
|
offset = aligned_stack_size - fn_call_stack_size;
|
||||||
for reg in saved_regs {
|
for reg in saved_regs {
|
||||||
offset -= 8;
|
offset -= 8;
|
||||||
AArch64Assembler::mov_reg64_stack32(buf, *reg, offset);
|
AArch64Assembler::mov_reg64_base32(buf, *reg, offset);
|
||||||
}
|
}
|
||||||
AArch64Assembler::add_reg64_reg64_imm32(
|
AArch64Assembler::add_reg64_reg64_imm32(
|
||||||
buf,
|
buf,
|
||||||
|
@ -207,6 +223,25 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn load_args<'a>(
|
||||||
|
_symbol_map: &mut MutMap<Symbol, SymbolStorage<AArch64GeneralReg, AArch64FloatReg>>,
|
||||||
|
_args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
) -> Result<(), String> {
|
||||||
|
Err("Loading args not yet implemented for AArch64".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn store_args<'a>(
|
||||||
|
_buf: &mut Vec<'a, u8>,
|
||||||
|
_symbol_map: &MutMap<Symbol, SymbolStorage<AArch64GeneralReg, AArch64FloatReg>>,
|
||||||
|
_args: &'a [Symbol],
|
||||||
|
_arg_layouts: &[Layout<'a>],
|
||||||
|
_ret_layout: &Layout<'a>,
|
||||||
|
) -> Result<u32, String> {
|
||||||
|
Err("Storing args not yet implemented for AArch64".to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
|
|
|
@ -36,12 +36,31 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
general_saved_regs: &[GeneralReg],
|
general_saved_regs: &[GeneralReg],
|
||||||
requested_stack_size: i32,
|
requested_stack_size: i32,
|
||||||
|
fn_call_stack_size: i32,
|
||||||
) -> Result<i32, String>;
|
) -> Result<i32, String>;
|
||||||
fn cleanup_stack<'a>(
|
fn cleanup_stack<'a>(
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
general_saved_regs: &[GeneralReg],
|
general_saved_regs: &[GeneralReg],
|
||||||
aligned_stack_size: i32,
|
aligned_stack_size: i32,
|
||||||
|
fn_call_stack_size: i32,
|
||||||
) -> Result<(), String>;
|
) -> Result<(), String>;
|
||||||
|
|
||||||
|
// load_args updates the symbol map to know where every arg is stored.
|
||||||
|
fn load_args<'a>(
|
||||||
|
symbol_map: &mut MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
|
||||||
|
args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
) -> Result<(), String>;
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
fn store_args<'a>(
|
||||||
|
buf: &mut Vec<'a, u8>,
|
||||||
|
symbol_map: &MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
|
||||||
|
args: &'a [Symbol],
|
||||||
|
arg_layouts: &[Layout<'a>],
|
||||||
|
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
) -> Result<u32, String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assembler contains calls to the backend assembly generator.
|
/// Assembler contains calls to the backend assembly generator.
|
||||||
|
@ -76,6 +95,7 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
fn mov_freg64_freg64(buf: &mut Vec<'_, u8>, dst: FloatReg, src: FloatReg);
|
fn mov_freg64_freg64(buf: &mut Vec<'_, u8>, dst: FloatReg, src: FloatReg);
|
||||||
fn mov_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
|
fn mov_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
|
||||||
|
|
||||||
|
// base32 is similar to stack based instructions but they reference the base/frame pointer.
|
||||||
fn mov_freg64_base32(buf: &mut Vec<'_, u8>, dst: FloatReg, offset: i32);
|
fn mov_freg64_base32(buf: &mut Vec<'_, u8>, dst: FloatReg, offset: i32);
|
||||||
fn mov_reg64_base32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32);
|
fn mov_reg64_base32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32);
|
||||||
fn mov_base32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg);
|
fn mov_base32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg);
|
||||||
|
@ -104,7 +124,7 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
enum SymbolStorage<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
pub enum SymbolStorage<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
// These may need layout, but I am not sure.
|
// These may need layout, but I am not sure.
|
||||||
// I think whenever a symbol would be used, we specify layout anyways.
|
// I think whenever a symbol would be used, we specify layout anyways.
|
||||||
GeneralReg(GeneralReg),
|
GeneralReg(GeneralReg),
|
||||||
|
@ -149,7 +169,9 @@ pub struct Backend64Bit<
|
||||||
general_used_callee_saved_regs: MutSet<GeneralReg>,
|
general_used_callee_saved_regs: MutSet<GeneralReg>,
|
||||||
float_used_callee_saved_regs: MutSet<FloatReg>,
|
float_used_callee_saved_regs: MutSet<FloatReg>,
|
||||||
|
|
||||||
stack_size: i32,
|
stack_size: u32,
|
||||||
|
// The ammount of stack space needed to pass args for function calling.
|
||||||
|
fn_call_stack_size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
|
@ -178,6 +200,7 @@ impl<
|
||||||
float_used_regs: bumpalo::vec![in env.arena],
|
float_used_regs: bumpalo::vec![in env.arena],
|
||||||
float_used_callee_saved_regs: MutSet::default(),
|
float_used_callee_saved_regs: MutSet::default(),
|
||||||
stack_size: 0,
|
stack_size: 0,
|
||||||
|
fn_call_stack_size: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,6 +210,7 @@ impl<
|
||||||
|
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
self.stack_size = 0;
|
self.stack_size = 0;
|
||||||
|
self.fn_call_stack_size = 0;
|
||||||
self.last_seen_map.clear();
|
self.last_seen_map.clear();
|
||||||
self.free_map.clear();
|
self.free_map.clear();
|
||||||
self.symbols_map.clear();
|
self.symbols_map.clear();
|
||||||
|
@ -225,14 +249,24 @@ impl<
|
||||||
// Setup stack.
|
// Setup stack.
|
||||||
let mut used_regs = bumpalo::vec![in self.env.arena];
|
let mut used_regs = bumpalo::vec![in self.env.arena];
|
||||||
used_regs.extend(&self.general_used_callee_saved_regs);
|
used_regs.extend(&self.general_used_callee_saved_regs);
|
||||||
let aligned_stack_size = CC::setup_stack(&mut out, &used_regs, self.stack_size)?;
|
let aligned_stack_size = CC::setup_stack(
|
||||||
|
&mut out,
|
||||||
|
&used_regs,
|
||||||
|
self.stack_size as i32,
|
||||||
|
self.fn_call_stack_size as i32,
|
||||||
|
)?;
|
||||||
let setup_offset = out.len();
|
let setup_offset = out.len();
|
||||||
|
|
||||||
// Add function body.
|
// Add function body.
|
||||||
out.extend(&self.buf);
|
out.extend(&self.buf);
|
||||||
|
|
||||||
// Cleanup stack.
|
// Cleanup stack.
|
||||||
CC::cleanup_stack(&mut out, &used_regs, aligned_stack_size)?;
|
CC::cleanup_stack(
|
||||||
|
&mut out,
|
||||||
|
&used_regs,
|
||||||
|
aligned_stack_size,
|
||||||
|
self.fn_call_stack_size as i32,
|
||||||
|
)?;
|
||||||
ASM::ret(&mut out);
|
ASM::ret(&mut out);
|
||||||
|
|
||||||
// Update relocs to include stack setup offset.
|
// Update relocs to include stack setup offset.
|
||||||
|
@ -255,12 +289,31 @@ 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> {
|
||||||
|
CC::load_args(&mut self.symbols_map, args)?;
|
||||||
|
// Update used and free regs.
|
||||||
|
for (sym, storage) in &self.symbols_map {
|
||||||
|
match storage {
|
||||||
|
SymbolStorage::GeneralReg(reg) | SymbolStorage::BaseAndGeneralReg(reg, _) => {
|
||||||
|
self.general_free_regs.retain(|r| *r != *reg);
|
||||||
|
self.general_used_regs.push((*reg, *sym));
|
||||||
|
}
|
||||||
|
SymbolStorage::FloatReg(reg) | SymbolStorage::BaseAndFloatReg(reg, _) => {
|
||||||
|
self.float_free_regs.retain(|r| *r != *reg);
|
||||||
|
self.float_used_regs.push((*reg, *sym));
|
||||||
|
}
|
||||||
|
SymbolStorage::Base(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn build_fn_call(
|
fn build_fn_call(
|
||||||
&mut self,
|
&mut self,
|
||||||
dst: &Symbol,
|
dst: &Symbol,
|
||||||
fn_name: String,
|
fn_name: String,
|
||||||
_args: &'a [Symbol],
|
args: &'a [Symbol],
|
||||||
_arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// Save used caller saved regs.
|
// Save used caller saved regs.
|
||||||
|
@ -288,7 +341,14 @@ impl<
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put values in param regs or on top of the stack.
|
// Put values in param regs or on top of the stack.
|
||||||
// TODO: deal with arg passing. This will be call conv specific.
|
let tmp_stack_size = CC::store_args(
|
||||||
|
&mut self.buf,
|
||||||
|
&self.symbols_map,
|
||||||
|
args,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout,
|
||||||
|
)?;
|
||||||
|
self.fn_call_stack_size = std::cmp::max(self.fn_call_stack_size, tmp_stack_size);
|
||||||
|
|
||||||
// Call function and generate reloc.
|
// Call function and generate reloc.
|
||||||
ASM::call(&mut self.buf, &mut self.relocs, fn_name);
|
ASM::call(&mut self.buf, &mut self.relocs, fn_name);
|
||||||
|
@ -479,17 +539,6 @@ impl<
|
||||||
.insert(*sym, SymbolStorage::GeneralReg(reg));
|
.insert(*sym, SymbolStorage::GeneralReg(reg));
|
||||||
Ok(reg)
|
Ok(reg)
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::FloatReg(_reg)) => {
|
|
||||||
Err("Cannot load floating point symbol into GeneralReg".to_string())
|
|
||||||
}
|
|
||||||
Some(SymbolStorage::BaseAndGeneralReg(reg, offset)) => {
|
|
||||||
self.symbols_map
|
|
||||||
.insert(*sym, SymbolStorage::BaseAndGeneralReg(reg, offset));
|
|
||||||
Ok(reg)
|
|
||||||
}
|
|
||||||
Some(SymbolStorage::BaseAndFloatReg(_reg, _offset)) => {
|
|
||||||
Err("Cannot load floating point symbol into GeneralReg".to_string())
|
|
||||||
}
|
|
||||||
Some(SymbolStorage::Base(offset)) => {
|
Some(SymbolStorage::Base(offset)) => {
|
||||||
let reg = self.claim_general_reg(sym)?;
|
let reg = self.claim_general_reg(sym)?;
|
||||||
self.symbols_map
|
self.symbols_map
|
||||||
|
@ -497,6 +546,14 @@ impl<
|
||||||
ASM::mov_reg64_base32(&mut self.buf, reg, offset as i32);
|
ASM::mov_reg64_base32(&mut self.buf, reg, offset as i32);
|
||||||
Ok(reg)
|
Ok(reg)
|
||||||
}
|
}
|
||||||
|
Some(SymbolStorage::BaseAndGeneralReg(reg, offset)) => {
|
||||||
|
self.symbols_map
|
||||||
|
.insert(*sym, SymbolStorage::BaseAndGeneralReg(reg, offset));
|
||||||
|
Ok(reg)
|
||||||
|
}
|
||||||
|
Some(SymbolStorage::FloatReg(_)) | Some(SymbolStorage::BaseAndFloatReg(_, _)) => {
|
||||||
|
Err("Cannot load floating point symbol into GeneralReg".to_string())
|
||||||
|
}
|
||||||
None => Err(format!("Unknown symbol: {}", sym)),
|
None => Err(format!("Unknown symbol: {}", sym)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -504,21 +561,10 @@ impl<
|
||||||
fn load_to_float_reg(&mut self, sym: &Symbol) -> Result<FloatReg, String> {
|
fn load_to_float_reg(&mut self, sym: &Symbol) -> Result<FloatReg, String> {
|
||||||
let val = self.symbols_map.remove(sym);
|
let val = self.symbols_map.remove(sym);
|
||||||
match val {
|
match val {
|
||||||
Some(SymbolStorage::GeneralReg(_reg)) => {
|
|
||||||
Err("Cannot load integer point symbol into FloatReg".to_string())
|
|
||||||
}
|
|
||||||
Some(SymbolStorage::FloatReg(reg)) => {
|
Some(SymbolStorage::FloatReg(reg)) => {
|
||||||
self.symbols_map.insert(*sym, SymbolStorage::FloatReg(reg));
|
self.symbols_map.insert(*sym, SymbolStorage::FloatReg(reg));
|
||||||
Ok(reg)
|
Ok(reg)
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::BaseAndGeneralReg(_reg, _offset)) => {
|
|
||||||
Err("Cannot load integer point symbol into FloatReg".to_string())
|
|
||||||
}
|
|
||||||
Some(SymbolStorage::BaseAndFloatReg(reg, offset)) => {
|
|
||||||
self.symbols_map
|
|
||||||
.insert(*sym, SymbolStorage::BaseAndFloatReg(reg, offset));
|
|
||||||
Ok(reg)
|
|
||||||
}
|
|
||||||
Some(SymbolStorage::Base(offset)) => {
|
Some(SymbolStorage::Base(offset)) => {
|
||||||
let reg = self.claim_float_reg(sym)?;
|
let reg = self.claim_float_reg(sym)?;
|
||||||
self.symbols_map
|
self.symbols_map
|
||||||
|
@ -526,6 +572,14 @@ impl<
|
||||||
ASM::mov_freg64_base32(&mut self.buf, reg, offset as i32);
|
ASM::mov_freg64_base32(&mut self.buf, reg, offset as i32);
|
||||||
Ok(reg)
|
Ok(reg)
|
||||||
}
|
}
|
||||||
|
Some(SymbolStorage::BaseAndFloatReg(reg, offset)) => {
|
||||||
|
self.symbols_map
|
||||||
|
.insert(*sym, SymbolStorage::BaseAndFloatReg(reg, offset));
|
||||||
|
Ok(reg)
|
||||||
|
}
|
||||||
|
Some(SymbolStorage::GeneralReg(_)) | Some(SymbolStorage::BaseAndGeneralReg(_, _)) => {
|
||||||
|
Err("Cannot load integer point symbol into FloatReg".to_string())
|
||||||
|
}
|
||||||
None => Err(format!("Unknown symbol: {}", sym)),
|
None => Err(format!("Unknown symbol: {}", sym)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -534,17 +588,23 @@ impl<
|
||||||
let val = self.symbols_map.remove(sym);
|
let val = self.symbols_map.remove(sym);
|
||||||
match val {
|
match val {
|
||||||
Some(SymbolStorage::GeneralReg(reg)) => {
|
Some(SymbolStorage::GeneralReg(reg)) => {
|
||||||
let offset = self.increase_stack_size(8)?;
|
let offset = self.increase_stack_size(8)? as i32;
|
||||||
// For base addresssing, use the negative offset.
|
// For base addresssing, use the negative offset - 8.
|
||||||
ASM::mov_base32_reg64(&mut self.buf, -offset, reg);
|
ASM::mov_base32_reg64(&mut self.buf, -offset - 8, reg);
|
||||||
self.symbols_map.insert(*sym, SymbolStorage::Base(-offset));
|
self.symbols_map
|
||||||
|
.insert(*sym, SymbolStorage::Base(-offset - 8));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::FloatReg(reg)) => {
|
Some(SymbolStorage::FloatReg(reg)) => {
|
||||||
let offset = self.increase_stack_size(8)?;
|
let offset = self.increase_stack_size(8)? as i32;
|
||||||
// For base addresssing, use the negative offset.
|
// For base addresssing, use the negative offset.
|
||||||
ASM::mov_base32_freg64(&mut self.buf, -offset, reg);
|
ASM::mov_base32_freg64(&mut self.buf, -offset - 8, reg);
|
||||||
self.symbols_map.insert(*sym, SymbolStorage::Base(-offset));
|
self.symbols_map
|
||||||
|
.insert(*sym, SymbolStorage::Base(-offset - 8));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Some(SymbolStorage::Base(offset)) => {
|
||||||
|
self.symbols_map.insert(*sym, SymbolStorage::Base(offset));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::BaseAndGeneralReg(_, offset)) => {
|
Some(SymbolStorage::BaseAndGeneralReg(_, offset)) => {
|
||||||
|
@ -555,21 +615,22 @@ impl<
|
||||||
self.symbols_map.insert(*sym, SymbolStorage::Base(offset));
|
self.symbols_map.insert(*sym, SymbolStorage::Base(offset));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Some(SymbolStorage::Base(offset)) => {
|
|
||||||
self.symbols_map.insert(*sym, SymbolStorage::Base(offset));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
None => Err(format!("Unknown symbol: {}", sym)),
|
None => Err(format!("Unknown symbol: {}", sym)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// increase_stack_size increase the current stack size and returns the offset of the stack.
|
/// increase_stack_size increase the current stack size and returns the offset of the stack.
|
||||||
fn increase_stack_size(&mut self, amount: i32) -> Result<i32, String> {
|
fn increase_stack_size(&mut self, amount: u32) -> Result<u32, String> {
|
||||||
debug_assert!(amount > 0);
|
debug_assert!(amount > 0);
|
||||||
let offset = self.stack_size;
|
let offset = self.stack_size;
|
||||||
if let Some(new_size) = self.stack_size.checked_add(amount) {
|
if let Some(new_size) = self.stack_size.checked_add(amount) {
|
||||||
|
// Since stack size is u32, but the max offset is i32, if we pass i32 max, we have overflowed.
|
||||||
|
if new_size > i32::MAX as u32 {
|
||||||
|
Err("Ran out of stack space".to_string())
|
||||||
|
} else {
|
||||||
self.stack_size = new_size;
|
self.stack_size = new_size;
|
||||||
Ok(offset)
|
Ok(offset)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("Ran out of stack space".to_string())
|
Err("Ran out of stack space".to_string())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
use crate::generic64::{Assembler, CallConv, RegTrait};
|
use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage};
|
||||||
use crate::Relocation;
|
use crate::Relocation;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
|
use roc_collections::all::MutMap;
|
||||||
|
use roc_module::symbol::Symbol;
|
||||||
|
use roc_mono::layout::{Builtin, Layout};
|
||||||
|
|
||||||
// Not sure exactly how I want to represent registers.
|
// Not sure exactly how I want to represent registers.
|
||||||
// If we want max speed, we would likely make them structs that impl the same trait to avoid ifs.
|
// If we want max speed, we would likely make them structs that impl the same trait to avoid ifs.
|
||||||
|
@ -145,8 +148,14 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
general_saved_regs: &[X86_64GeneralReg],
|
general_saved_regs: &[X86_64GeneralReg],
|
||||||
requested_stack_size: i32,
|
requested_stack_size: i32,
|
||||||
|
fn_call_stack_size: i32,
|
||||||
) -> Result<i32, String> {
|
) -> Result<i32, String> {
|
||||||
x86_64_generic_setup_stack(buf, general_saved_regs, requested_stack_size)
|
x86_64_generic_setup_stack(
|
||||||
|
buf,
|
||||||
|
general_saved_regs,
|
||||||
|
requested_stack_size,
|
||||||
|
fn_call_stack_size,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -154,8 +163,201 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
general_saved_regs: &[X86_64GeneralReg],
|
general_saved_regs: &[X86_64GeneralReg],
|
||||||
aligned_stack_size: i32,
|
aligned_stack_size: i32,
|
||||||
|
fn_call_stack_size: i32,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
x86_64_generic_cleanup_stack(buf, general_saved_regs, aligned_stack_size)
|
x86_64_generic_cleanup_stack(
|
||||||
|
buf,
|
||||||
|
general_saved_regs,
|
||||||
|
aligned_stack_size,
|
||||||
|
fn_call_stack_size,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn load_args<'a>(
|
||||||
|
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||||
|
args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
) -> Result<(), String> {
|
||||||
|
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 float_i = 0;
|
||||||
|
for (layout, sym) in args.iter() {
|
||||||
|
match layout {
|
||||||
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
|
if general_i < Self::GENERAL_PARAM_REGS.len() {
|
||||||
|
symbol_map.insert(
|
||||||
|
*sym,
|
||||||
|
SymbolStorage::GeneralReg(Self::GENERAL_PARAM_REGS[general_i]),
|
||||||
|
);
|
||||||
|
general_i += 1;
|
||||||
|
} else {
|
||||||
|
base_offset += 8;
|
||||||
|
symbol_map.insert(*sym, SymbolStorage::Base(base_offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout::Builtin(Builtin::Float64) => {
|
||||||
|
if float_i < Self::FLOAT_PARAM_REGS.len() {
|
||||||
|
symbol_map.insert(
|
||||||
|
*sym,
|
||||||
|
SymbolStorage::FloatReg(Self::FLOAT_PARAM_REGS[float_i]),
|
||||||
|
);
|
||||||
|
float_i += 1;
|
||||||
|
} else {
|
||||||
|
base_offset += 8;
|
||||||
|
symbol_map.insert(*sym, SymbolStorage::Base(base_offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x => {
|
||||||
|
return Err(format!(
|
||||||
|
"Loading args with layout {:?} not yet implementd",
|
||||||
|
x
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn store_args<'a>(
|
||||||
|
buf: &mut Vec<'a, u8>,
|
||||||
|
symbol_map: &MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||||
|
args: &'a [Symbol],
|
||||||
|
arg_layouts: &[Layout<'a>],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
) -> Result<u32, String> {
|
||||||
|
let mut stack_offset = Self::SHADOW_SPACE_SIZE as i32;
|
||||||
|
let mut general_i = 0;
|
||||||
|
let mut float_i = 0;
|
||||||
|
// For most return layouts we will do nothing.
|
||||||
|
// In some cases, we need to put the return address as the first arg.
|
||||||
|
match ret_layout {
|
||||||
|
Layout::Builtin(Builtin::Int64) => {}
|
||||||
|
Layout::Builtin(Builtin::Float64) => {}
|
||||||
|
x => {
|
||||||
|
return Err(format!(
|
||||||
|
"recieving return type, {:?}, is not yet implemented",
|
||||||
|
x
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i, layout) in arg_layouts.iter().enumerate() {
|
||||||
|
match layout {
|
||||||
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
|
if general_i < Self::GENERAL_PARAM_REGS.len() {
|
||||||
|
// Load the value to the param reg.
|
||||||
|
let dst = Self::GENERAL_PARAM_REGS[general_i];
|
||||||
|
match symbol_map
|
||||||
|
.get(&args[i])
|
||||||
|
.ok_or("function argument does not reference any symbol")?
|
||||||
|
{
|
||||||
|
SymbolStorage::GeneralReg(reg)
|
||||||
|
| SymbolStorage::BaseAndGeneralReg(reg, _) => {
|
||||||
|
X86_64Assembler::mov_reg64_reg64(buf, dst, *reg);
|
||||||
|
}
|
||||||
|
SymbolStorage::Base(offset) => {
|
||||||
|
X86_64Assembler::mov_reg64_base32(buf, dst, *offset);
|
||||||
|
}
|
||||||
|
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg(_, _) => {
|
||||||
|
return Err(
|
||||||
|
"Cannot load floating point symbol into GeneralReg".to_string()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
general_i += 1;
|
||||||
|
} else {
|
||||||
|
// Load the value to the stack.
|
||||||
|
match symbol_map
|
||||||
|
.get(&args[i])
|
||||||
|
.ok_or("function argument does not reference any symbol")?
|
||||||
|
{
|
||||||
|
SymbolStorage::GeneralReg(reg)
|
||||||
|
| SymbolStorage::BaseAndGeneralReg(reg, _) => {
|
||||||
|
X86_64Assembler::mov_stack32_reg64(buf, stack_offset, *reg);
|
||||||
|
}
|
||||||
|
SymbolStorage::Base(offset) => {
|
||||||
|
// Use RAX as a tmp reg because it will be free before function calls.
|
||||||
|
X86_64Assembler::mov_reg64_base32(
|
||||||
|
buf,
|
||||||
|
X86_64GeneralReg::RAX,
|
||||||
|
*offset,
|
||||||
|
);
|
||||||
|
X86_64Assembler::mov_stack32_reg64(
|
||||||
|
buf,
|
||||||
|
stack_offset,
|
||||||
|
X86_64GeneralReg::RAX,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg(_, _) => {
|
||||||
|
return Err(
|
||||||
|
"Cannot load floating point symbol into GeneralReg".to_string()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack_offset += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout::Builtin(Builtin::Float64) => {
|
||||||
|
if float_i < Self::FLOAT_PARAM_REGS.len() {
|
||||||
|
// Load the value to the param reg.
|
||||||
|
let dst = Self::FLOAT_PARAM_REGS[float_i];
|
||||||
|
match symbol_map
|
||||||
|
.get(&args[i])
|
||||||
|
.ok_or("function argument does not reference any symbol")?
|
||||||
|
{
|
||||||
|
SymbolStorage::FloatReg(reg)
|
||||||
|
| SymbolStorage::BaseAndFloatReg(reg, _) => {
|
||||||
|
X86_64Assembler::mov_freg64_freg64(buf, dst, *reg);
|
||||||
|
}
|
||||||
|
SymbolStorage::Base(offset) => {
|
||||||
|
X86_64Assembler::mov_freg64_base32(buf, dst, *offset);
|
||||||
|
}
|
||||||
|
SymbolStorage::GeneralReg(_)
|
||||||
|
| SymbolStorage::BaseAndGeneralReg(_, _) => {
|
||||||
|
return Err("Cannot load general symbol into FloatReg".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float_i += 1;
|
||||||
|
} else {
|
||||||
|
// Load the value to the stack.
|
||||||
|
match symbol_map
|
||||||
|
.get(&args[i])
|
||||||
|
.ok_or("function argument does not reference any symbol")?
|
||||||
|
{
|
||||||
|
SymbolStorage::FloatReg(reg)
|
||||||
|
| SymbolStorage::BaseAndFloatReg(reg, _) => {
|
||||||
|
X86_64Assembler::mov_stack32_freg64(buf, stack_offset, *reg);
|
||||||
|
}
|
||||||
|
SymbolStorage::Base(offset) => {
|
||||||
|
// Use XMM0 as a tmp reg because it will be free before function calls.
|
||||||
|
X86_64Assembler::mov_freg64_base32(
|
||||||
|
buf,
|
||||||
|
X86_64FloatReg::XMM0,
|
||||||
|
*offset,
|
||||||
|
);
|
||||||
|
X86_64Assembler::mov_stack32_freg64(
|
||||||
|
buf,
|
||||||
|
stack_offset,
|
||||||
|
X86_64FloatReg::XMM0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SymbolStorage::GeneralReg(_)
|
||||||
|
| SymbolStorage::BaseAndGeneralReg(_, _) => {
|
||||||
|
return Err("Cannot load general symbol into FloatReg".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack_offset += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x => {
|
||||||
|
return Err(format!(
|
||||||
|
"calling with arg type, {:?}, is not yet implemented",
|
||||||
|
x
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(stack_offset as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,8 +458,9 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
saved_regs: &[X86_64GeneralReg],
|
saved_regs: &[X86_64GeneralReg],
|
||||||
requested_stack_size: i32,
|
requested_stack_size: i32,
|
||||||
|
fn_call_stack_size: i32,
|
||||||
) -> Result<i32, String> {
|
) -> Result<i32, String> {
|
||||||
x86_64_generic_setup_stack(buf, saved_regs, requested_stack_size)
|
x86_64_generic_setup_stack(buf, saved_regs, requested_stack_size, fn_call_stack_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -265,8 +468,190 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
saved_regs: &[X86_64GeneralReg],
|
saved_regs: &[X86_64GeneralReg],
|
||||||
aligned_stack_size: i32,
|
aligned_stack_size: i32,
|
||||||
|
fn_call_stack_size: i32,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
x86_64_generic_cleanup_stack(buf, saved_regs, aligned_stack_size)
|
x86_64_generic_cleanup_stack(buf, saved_regs, aligned_stack_size, fn_call_stack_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn load_args<'a>(
|
||||||
|
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||||
|
args: &'a [(Layout<'a>, Symbol)],
|
||||||
|
) -> Result<(), String> {
|
||||||
|
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() {
|
||||||
|
if i < Self::GENERAL_PARAM_REGS.len() {
|
||||||
|
match layout {
|
||||||
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
|
symbol_map
|
||||||
|
.insert(*sym, SymbolStorage::GeneralReg(Self::GENERAL_PARAM_REGS[i]));
|
||||||
|
}
|
||||||
|
Layout::Builtin(Builtin::Float64) => {
|
||||||
|
symbol_map.insert(*sym, SymbolStorage::FloatReg(Self::FLOAT_PARAM_REGS[i]));
|
||||||
|
}
|
||||||
|
x => {
|
||||||
|
return Err(format!(
|
||||||
|
"Loading args with layout {:?} not yet implementd",
|
||||||
|
x
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
base_offset += match layout {
|
||||||
|
Layout::Builtin(Builtin::Int64) => 8,
|
||||||
|
Layout::Builtin(Builtin::Float64) => 8,
|
||||||
|
x => {
|
||||||
|
return Err(format!(
|
||||||
|
"Loading args with layout {:?} not yet implemented",
|
||||||
|
x
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
symbol_map.insert(*sym, SymbolStorage::Base(base_offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn store_args<'a>(
|
||||||
|
buf: &mut Vec<'a, u8>,
|
||||||
|
symbol_map: &MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||||
|
args: &'a [Symbol],
|
||||||
|
arg_layouts: &[Layout<'a>],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
) -> Result<u32, String> {
|
||||||
|
let mut stack_offset = Self::SHADOW_SPACE_SIZE as i32;
|
||||||
|
let mut reg_i = 0;
|
||||||
|
// For most return layouts we will do nothing.
|
||||||
|
// In some cases, we need to put the return address as the first arg.
|
||||||
|
match ret_layout {
|
||||||
|
Layout::Builtin(Builtin::Int64) => {}
|
||||||
|
Layout::Builtin(Builtin::Float64) => {}
|
||||||
|
x => {
|
||||||
|
return Err(format!(
|
||||||
|
"recieving return type, {:?}, is not yet implemented",
|
||||||
|
x
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i, layout) in arg_layouts.iter().enumerate() {
|
||||||
|
match layout {
|
||||||
|
Layout::Builtin(Builtin::Int64) => {
|
||||||
|
if i < Self::GENERAL_PARAM_REGS.len() {
|
||||||
|
// Load the value to the param reg.
|
||||||
|
let dst = Self::GENERAL_PARAM_REGS[reg_i];
|
||||||
|
match symbol_map
|
||||||
|
.get(&args[i])
|
||||||
|
.ok_or("function argument does not reference any symbol")?
|
||||||
|
{
|
||||||
|
SymbolStorage::GeneralReg(reg)
|
||||||
|
| SymbolStorage::BaseAndGeneralReg(reg, _) => {
|
||||||
|
X86_64Assembler::mov_reg64_reg64(buf, dst, *reg);
|
||||||
|
}
|
||||||
|
SymbolStorage::Base(offset) => {
|
||||||
|
X86_64Assembler::mov_reg64_base32(buf, dst, *offset);
|
||||||
|
}
|
||||||
|
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg(_, _) => {
|
||||||
|
return Err(
|
||||||
|
"Cannot load floating point symbol into GeneralReg".to_string()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reg_i += 1;
|
||||||
|
} else {
|
||||||
|
// Load the value to the stack.
|
||||||
|
match symbol_map
|
||||||
|
.get(&args[i])
|
||||||
|
.ok_or("function argument does not reference any symbol")?
|
||||||
|
{
|
||||||
|
SymbolStorage::GeneralReg(reg)
|
||||||
|
| SymbolStorage::BaseAndGeneralReg(reg, _) => {
|
||||||
|
X86_64Assembler::mov_stack32_reg64(buf, stack_offset, *reg);
|
||||||
|
}
|
||||||
|
SymbolStorage::Base(offset) => {
|
||||||
|
// Use RAX as a tmp reg because it will be free before function calls.
|
||||||
|
X86_64Assembler::mov_reg64_base32(
|
||||||
|
buf,
|
||||||
|
X86_64GeneralReg::RAX,
|
||||||
|
*offset,
|
||||||
|
);
|
||||||
|
X86_64Assembler::mov_stack32_reg64(
|
||||||
|
buf,
|
||||||
|
stack_offset,
|
||||||
|
X86_64GeneralReg::RAX,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg(_, _) => {
|
||||||
|
return Err(
|
||||||
|
"Cannot load floating point symbol into GeneralReg".to_string()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack_offset += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout::Builtin(Builtin::Float64) => {
|
||||||
|
if i < Self::FLOAT_PARAM_REGS.len() {
|
||||||
|
// Load the value to the param reg.
|
||||||
|
let dst = Self::FLOAT_PARAM_REGS[reg_i];
|
||||||
|
match symbol_map
|
||||||
|
.get(&args[i])
|
||||||
|
.ok_or("function argument does not reference any symbol")?
|
||||||
|
{
|
||||||
|
SymbolStorage::FloatReg(reg)
|
||||||
|
| SymbolStorage::BaseAndFloatReg(reg, _) => {
|
||||||
|
X86_64Assembler::mov_freg64_freg64(buf, dst, *reg);
|
||||||
|
}
|
||||||
|
SymbolStorage::Base(offset) => {
|
||||||
|
X86_64Assembler::mov_freg64_base32(buf, dst, *offset);
|
||||||
|
}
|
||||||
|
SymbolStorage::GeneralReg(_)
|
||||||
|
| SymbolStorage::BaseAndGeneralReg(_, _) => {
|
||||||
|
return Err("Cannot load general symbol into FloatReg".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reg_i += 1;
|
||||||
|
} else {
|
||||||
|
// Load the value to the stack.
|
||||||
|
match symbol_map
|
||||||
|
.get(&args[i])
|
||||||
|
.ok_or("function argument does not reference any symbol")?
|
||||||
|
{
|
||||||
|
SymbolStorage::FloatReg(reg)
|
||||||
|
| SymbolStorage::BaseAndFloatReg(reg, _) => {
|
||||||
|
X86_64Assembler::mov_stack32_freg64(buf, stack_offset, *reg);
|
||||||
|
}
|
||||||
|
SymbolStorage::Base(offset) => {
|
||||||
|
// Use XMM0 as a tmp reg because it will be free before function calls.
|
||||||
|
X86_64Assembler::mov_freg64_base32(
|
||||||
|
buf,
|
||||||
|
X86_64FloatReg::XMM0,
|
||||||
|
*offset,
|
||||||
|
);
|
||||||
|
X86_64Assembler::mov_stack32_freg64(
|
||||||
|
buf,
|
||||||
|
stack_offset,
|
||||||
|
X86_64FloatReg::XMM0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
SymbolStorage::GeneralReg(_)
|
||||||
|
| SymbolStorage::BaseAndGeneralReg(_, _) => {
|
||||||
|
return Err("Cannot load general symbol into FloatReg".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack_offset += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x => {
|
||||||
|
return Err(format!(
|
||||||
|
"calling with arg type, {:?}, is not yet implemented",
|
||||||
|
x
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(stack_offset as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,12 +660,15 @@ fn x86_64_generic_setup_stack<'a>(
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
saved_regs: &[X86_64GeneralReg],
|
saved_regs: &[X86_64GeneralReg],
|
||||||
requested_stack_size: i32,
|
requested_stack_size: i32,
|
||||||
|
fn_call_stack_size: i32,
|
||||||
) -> Result<i32, String> {
|
) -> Result<i32, String> {
|
||||||
X86_64Assembler::push_reg64(buf, X86_64GeneralReg::RBP);
|
X86_64Assembler::push_reg64(buf, X86_64GeneralReg::RBP);
|
||||||
X86_64Assembler::mov_reg64_reg64(buf, X86_64GeneralReg::RBP, X86_64GeneralReg::RSP);
|
X86_64Assembler::mov_reg64_reg64(buf, X86_64GeneralReg::RBP, X86_64GeneralReg::RSP);
|
||||||
|
|
||||||
let full_stack_size = requested_stack_size
|
let full_stack_size = requested_stack_size
|
||||||
.checked_add(8 * saved_regs.len() as i32)
|
.checked_add(8 * saved_regs.len() as i32)
|
||||||
|
.ok_or("Ran out of stack space")?
|
||||||
|
.checked_add(fn_call_stack_size)
|
||||||
.ok_or("Ran out of stack space")?;
|
.ok_or("Ran out of stack space")?;
|
||||||
let alignment = if full_stack_size <= 0 {
|
let alignment = if full_stack_size <= 0 {
|
||||||
0
|
0
|
||||||
|
@ -302,10 +690,10 @@ fn x86_64_generic_setup_stack<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Put values at the top of the stack to avoid conflicts with previously saved variables.
|
// Put values at the top of the stack to avoid conflicts with previously saved variables.
|
||||||
let mut offset = aligned_stack_size;
|
let mut offset = aligned_stack_size - fn_call_stack_size;
|
||||||
for reg in saved_regs {
|
for reg in saved_regs {
|
||||||
offset -= 8;
|
|
||||||
X86_64Assembler::mov_base32_reg64(buf, -offset, *reg);
|
X86_64Assembler::mov_base32_reg64(buf, -offset, *reg);
|
||||||
|
offset -= 8;
|
||||||
}
|
}
|
||||||
Ok(aligned_stack_size)
|
Ok(aligned_stack_size)
|
||||||
} else {
|
} else {
|
||||||
|
@ -321,12 +709,13 @@ fn x86_64_generic_cleanup_stack<'a>(
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
saved_regs: &[X86_64GeneralReg],
|
saved_regs: &[X86_64GeneralReg],
|
||||||
aligned_stack_size: i32,
|
aligned_stack_size: i32,
|
||||||
|
fn_call_stack_size: i32,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
if aligned_stack_size > 0 {
|
if aligned_stack_size > 0 {
|
||||||
let mut offset = aligned_stack_size;
|
let mut offset = aligned_stack_size - fn_call_stack_size;
|
||||||
for reg in saved_regs {
|
for reg in saved_regs {
|
||||||
offset -= 8;
|
|
||||||
X86_64Assembler::mov_reg64_base32(buf, *reg, -offset);
|
X86_64Assembler::mov_reg64_base32(buf, *reg, -offset);
|
||||||
|
offset -= 8;
|
||||||
}
|
}
|
||||||
X86_64Assembler::add_reg64_reg64_imm32(
|
X86_64Assembler::add_reg64_reg64_imm32(
|
||||||
buf,
|
buf,
|
||||||
|
@ -335,7 +724,7 @@ fn x86_64_generic_cleanup_stack<'a>(
|
||||||
aligned_stack_size,
|
aligned_stack_size,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
X86_64Assembler::mov_reg64_reg64(buf, X86_64GeneralReg::RSP, X86_64GeneralReg::RBP);
|
//X86_64Assembler::mov_reg64_reg64(buf, X86_64GeneralReg::RSP, X86_64GeneralReg::RBP);
|
||||||
X86_64Assembler::pop_reg64(buf, X86_64GeneralReg::RBP);
|
X86_64Assembler::pop_reg64(buf, X86_64GeneralReg::RBP);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -657,7 +1046,9 @@ fn mov_reg64_imm64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, imm: i64) {
|
||||||
/// `MOV r/m64,r64` -> Move r64 to r/m64.
|
/// `MOV r/m64,r64` -> Move r64 to r/m64.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn mov_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64GeneralReg) {
|
fn mov_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64GeneralReg) {
|
||||||
|
if dst != src {
|
||||||
binop_reg64_reg64(0x89, buf, dst, src);
|
binop_reg64_reg64(0x89, buf, dst, src);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MOV r64,r/m64` -> Move r/m64 to r64. where m64 references the base pionter.
|
/// `MOV r64,r/m64` -> Move r/m64 to r64. where m64 references the base pionter.
|
||||||
|
@ -996,26 +1387,27 @@ mod tests {
|
||||||
let arena = bumpalo::Bump::new();
|
let arena = bumpalo::Bump::new();
|
||||||
let mut buf = bumpalo::vec![in &arena];
|
let mut buf = bumpalo::vec![in &arena];
|
||||||
for ((dst, src), expected) in &[
|
for ((dst, src), expected) in &[
|
||||||
|
((X86_64GeneralReg::RAX, X86_64GeneralReg::RAX), vec![]),
|
||||||
(
|
(
|
||||||
(X86_64GeneralReg::RAX, X86_64GeneralReg::RAX),
|
(X86_64GeneralReg::RAX, X86_64GeneralReg::RCX),
|
||||||
[0x48, 0x89, 0xC0],
|
vec![0x48, 0x89, 0xC8],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
(X86_64GeneralReg::RAX, X86_64GeneralReg::R15),
|
(X86_64GeneralReg::RAX, X86_64GeneralReg::R15),
|
||||||
[0x4C, 0x89, 0xF8],
|
vec![0x4C, 0x89, 0xF8],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
(X86_64GeneralReg::R15, X86_64GeneralReg::RAX),
|
(X86_64GeneralReg::R15, X86_64GeneralReg::RAX),
|
||||||
[0x49, 0x89, 0xC7],
|
vec![0x49, 0x89, 0xC7],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
(X86_64GeneralReg::R15, X86_64GeneralReg::R15),
|
(X86_64GeneralReg::R15, X86_64GeneralReg::R14),
|
||||||
[0x4D, 0x89, 0xFF],
|
vec![0x4D, 0x89, 0xF7],
|
||||||
),
|
),
|
||||||
] {
|
] {
|
||||||
buf.clear();
|
buf.clear();
|
||||||
mov_reg64_reg64(&mut buf, *dst, *src);
|
mov_reg64_reg64(&mut buf, *dst, *src);
|
||||||
assert_eq!(expected, &buf[..]);
|
assert_eq!(&expected[..], &buf[..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,10 +70,14 @@ where
|
||||||
/// finalize is run at the end of build_proc when all internal code is finalized.
|
/// finalize is run at the end of build_proc when all internal code is finalized.
|
||||||
fn finalize(&mut self) -> Result<(&'a [u8], &[Relocation]), String>;
|
fn finalize(&mut self) -> Result<(&'a [u8], &[Relocation]), String>;
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
fn load_args(&mut self, args: &'a [(Layout<'a>, Symbol)]) -> 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();
|
||||||
// TODO: let the backend know of all the arguments.
|
self.load_args(&proc.args)?;
|
||||||
// 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();
|
||||||
|
@ -167,6 +171,8 @@ where
|
||||||
let fn_name = LayoutIds::default()
|
let fn_name = LayoutIds::default()
|
||||||
.get(*func_sym, layout)
|
.get(*func_sym, layout)
|
||||||
.to_symbol_string(*func_sym, &self.env().interns);
|
.to_symbol_string(*func_sym, &self.env().interns);
|
||||||
|
// Now that the arguments are needed, load them if they are literals.
|
||||||
|
self.load_literal_symbols(arguments)?;
|
||||||
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
|
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
|
||||||
}
|
}
|
||||||
x => Err(format!("the function, {:?}, is not yet implemented", x)),
|
x => Err(format!("the function, {:?}, is not yet implemented", x)),
|
||||||
|
|
|
@ -168,6 +168,36 @@ mod gen_num {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gen_wrap_add_nums() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
add2 = \num1, num2 -> num1 + num2
|
||||||
|
|
||||||
|
add2 4 5
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
9,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gen_wrap_add_nums_force_stack() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
add9 = \num1, num2, num3, num4, num5, num6, num7, num8, num9 -> num1 + num2 + num3 + num4 + num5 + num6 + num7 + num8 + num9
|
||||||
|
|
||||||
|
add9 1 2 3 4 5 6 7 8 9
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
45,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#[test]
|
#[test]
|
||||||
fn f64_sqrt() {
|
fn f64_sqrt() {
|
||||||
|
@ -250,21 +280,6 @@ mod gen_num {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gen_wrap_add_nums() {
|
|
||||||
assert_evals_to!(
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
add2 = \num1, num2 -> num1 + num2
|
|
||||||
|
|
||||||
add2 4 5
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
9,
|
|
||||||
i64
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gen_div_f64() {
|
fn gen_div_f64() {
|
||||||
// FIXME this works with normal types, but fails when checking uniqueness types
|
// FIXME this works with normal types, but fails when checking uniqueness types
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue