use unimplemented! and internal_error! instead of result in dev backend

This commit is contained in:
Brendan Hansknecht 2021-11-27 12:38:29 -08:00
parent dd1245dee6
commit a63dd1eb61
9 changed files with 381 additions and 578 deletions

View file

@ -5,6 +5,7 @@ use packed_struct::prelude::*;
use roc_collections::all::MutMap;
use roc_module::symbol::Symbol;
use roc_mono::layout::Layout;
use roc_reporting::internal_error;
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[allow(dead_code)]
@ -151,13 +152,15 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
saved_regs: &[AArch64GeneralReg],
requested_stack_size: i32,
fn_call_stack_size: i32,
) -> Result<i32, String> {
) -> i32 {
// 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 = match requested_stack_size
.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")?;
.and_then(|size| size.checked_add(fn_call_stack_size))
{
Some(size) => size,
_ => internal_error!("Ran out of stack space"),
};
let alignment = if full_stack_size <= 0 {
0
} else {
@ -194,12 +197,12 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
offset -= 8;
AArch64Assembler::mov_base32_reg64(buf, offset, *reg);
}
Ok(aligned_stack_size)
aligned_stack_size
} else {
Ok(0)
0
}
} else {
Err("Ran out of stack space".to_string())
internal_error!("Ran out of stack space");
}
}
@ -209,7 +212,7 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
saved_regs: &[AArch64GeneralReg],
aligned_stack_size: i32,
fn_call_stack_size: i32,
) -> Result<(), String> {
) {
if aligned_stack_size > 0 {
// All the following stores could be optimized by using `STP` to store pairs.
let mut offset = aligned_stack_size;
@ -230,7 +233,6 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
aligned_stack_size,
);
}
Ok(())
}
#[inline(always)]
@ -239,8 +241,8 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
_symbol_map: &mut MutMap<Symbol, SymbolStorage<AArch64GeneralReg, AArch64FloatReg>>,
_args: &'a [(Layout<'a>, Symbol)],
_ret_layout: &Layout<'a>,
) -> Result<(), String> {
Err("Loading args not yet implemented for AArch64".to_string())
) {
unimplemented!("Loading args not yet implemented for AArch64");
}
#[inline(always)]
@ -250,8 +252,8 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
_args: &'a [Symbol],
_arg_layouts: &[Layout<'a>],
_ret_layout: &Layout<'a>,
) -> Result<u32, String> {
Err("Storing args not yet implemented for AArch64".to_string())
) -> u32 {
unimplemented!("Storing args not yet implemented for AArch64");
}
fn return_struct<'a>(
@ -260,12 +262,12 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
_struct_size: u32,
_field_layouts: &[Layout<'a>],
_ret_reg: Option<AArch64GeneralReg>,
) -> Result<(), String> {
Err("Returning structs not yet implemented for AArch64".to_string())
) {
unimplemented!("Returning structs not yet implemented for AArch64");
}
fn returns_via_arg_pointer(_ret_layout: &Layout) -> Result<bool, String> {
Err("Returning via arg pointer not yet implemented for AArch64".to_string())
fn returns_via_arg_pointer(_ret_layout: &Layout) -> bool {
unimplemented!("Returning via arg pointer not yet implemented for AArch64");
}
}

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,7 @@ use roc_builtins::bitcode::{FloatWidth, IntWidth};
use roc_collections::all::MutMap;
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout};
use roc_reporting::internal_error;
// 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.
@ -152,7 +153,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
general_saved_regs: &[X86_64GeneralReg],
requested_stack_size: i32,
fn_call_stack_size: i32,
) -> Result<i32, String> {
) -> i32 {
x86_64_generic_setup_stack(
buf,
general_saved_regs,
@ -167,7 +168,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
general_saved_regs: &[X86_64GeneralReg],
aligned_stack_size: i32,
fn_call_stack_size: i32,
) -> Result<(), String> {
) {
x86_64_generic_cleanup_stack(
buf,
general_saved_regs,
@ -182,11 +183,11 @@ 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>,
) -> 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;
if X86_64SystemV::returns_via_arg_pointer(ret_layout)? {
if X86_64SystemV::returns_via_arg_pointer(ret_layout) {
symbol_map.insert(
Symbol::RET_POINTER,
SymbolStorage::GeneralReg(Self::GENERAL_PARAM_REGS[general_i]),
@ -251,21 +252,15 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
);
general_i += 2;
} else {
return Err(
"loading strings args on the stack is not yet implemented".to_string()
);
unimplemented!("loading strings args on the stack is not yet implemented");
}
}
Layout::Struct(&[]) => {}
x => {
return Err(format!(
"Loading args with layout {:?} not yet implemented",
x
));
unimplemented!("Loading args with layout {:?} not yet implemented", x);
}
}
}
Ok(())
}
#[inline(always)]
@ -275,7 +270,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
args: &'a [Symbol],
arg_layouts: &[Layout<'a>],
ret_layout: &Layout<'a>,
) -> Result<u32, String> {
) -> u32 {
let mut stack_offset = Self::SHADOW_SPACE_SIZE as i32;
let mut general_i = 0;
let mut float_i = 0;
@ -284,22 +279,22 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
match ret_layout {
Layout::Builtin(single_register_builtins!() | Builtin::Str) => {}
x => {
return Err(format!(
"receiving return type, {:?}, is not yet implemented",
x
));
unimplemented!("receiving return type, {:?}, is not yet implemented", x);
}
}
for (i, layout) in arg_layouts.iter().enumerate() {
match layout {
Layout::Builtin(single_register_integers!()) => {
let storage = match symbol_map.get(&args[i]) {
Some(storage) => storage,
None => {
internal_error!("function argument does not reference any symbol")
}
};
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")?
{
match storage {
SymbolStorage::GeneralReg(reg)
| SymbolStorage::BaseAndGeneralReg { reg, .. } => {
X86_64Assembler::mov_reg64_reg64(buf, dst, *reg);
@ -308,18 +303,13 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
X86_64Assembler::mov_reg64_base32(buf, dst, *offset);
}
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => {
return Err(
"Cannot load floating point symbol into GeneralReg".to_string()
)
internal_error!("Cannot load floating point symbol into GeneralReg")
}
}
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")?
{
match storage {
SymbolStorage::GeneralReg(reg)
| SymbolStorage::BaseAndGeneralReg { reg, .. } => {
X86_64Assembler::mov_stack32_reg64(buf, stack_offset, *reg);
@ -338,22 +328,23 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
);
}
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => {
return Err(
"Cannot load floating point symbol into GeneralReg".to_string()
)
internal_error!("Cannot load floating point symbol into GeneralReg")
}
}
stack_offset += 8;
}
}
Layout::Builtin(single_register_floats!()) => {
let storage = match symbol_map.get(&args[i]) {
Some(storage) => storage,
None => {
internal_error!("function argument does not reference any symbol")
}
};
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")?
{
match storage {
SymbolStorage::FloatReg(reg)
| SymbolStorage::BaseAndFloatReg { reg, .. } => {
X86_64Assembler::mov_freg64_freg64(buf, dst, *reg);
@ -363,16 +354,13 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
}
SymbolStorage::GeneralReg(_)
| SymbolStorage::BaseAndGeneralReg { .. } => {
return Err("Cannot load general symbol into FloatReg".to_string())
internal_error!("Cannot load general symbol into FloatReg")
}
}
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")?
{
match storage {
SymbolStorage::FloatReg(reg)
| SymbolStorage::BaseAndFloatReg { reg, .. } => {
X86_64Assembler::mov_stack32_freg64(buf, stack_offset, *reg);
@ -392,48 +380,48 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
}
SymbolStorage::GeneralReg(_)
| SymbolStorage::BaseAndGeneralReg { .. } => {
return Err("Cannot load general symbol into FloatReg".to_string())
internal_error!("Cannot load general symbol into FloatReg")
}
}
stack_offset += 8;
}
}
Layout::Builtin(Builtin::Str) => {
let storage = match symbol_map.get(&args[i]) {
Some(storage) => storage,
None => {
internal_error!("function argument does not reference any symbol")
}
};
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];
match symbol_map
.get(&args[i])
.ok_or("function argument does not reference any symbol")?
{
match storage {
SymbolStorage::Base { offset, .. } => {
X86_64Assembler::mov_reg64_base32(buf, dst1, *offset);
X86_64Assembler::mov_reg64_base32(buf, dst2, *offset + 8);
}
_ => {
return Err("Strings only support being loaded from base offsets"
.to_string());
internal_error!(
"Strings only support being loaded from base offsets"
);
}
}
general_i += 2;
} else {
return Err(
unimplemented!(
"calling functions with strings on the stack is not yet implemented"
.to_string(),
);
}
}
Layout::Struct(&[]) => {}
x => {
return Err(format!(
"calling with arg type, {:?}, is not yet implemented",
x
));
unimplemented!("calling with arg type, {:?}, is not yet implemented", x);
}
}
}
Ok(stack_offset as u32)
stack_offset as u32
}
fn return_struct<'a>(
@ -442,14 +430,14 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
_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())
) {
unimplemented!("Returning structs not yet implemented for X86_64");
}
fn returns_via_arg_pointer(ret_layout: &Layout) -> Result<bool, String> {
fn returns_via_arg_pointer(ret_layout: &Layout) -> bool {
// 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
Ok(ret_layout.stack_size(PTR_SIZE) > 16)
ret_layout.stack_size(PTR_SIZE) > 16
}
}
@ -551,7 +539,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
saved_regs: &[X86_64GeneralReg],
requested_stack_size: i32,
fn_call_stack_size: i32,
) -> Result<i32, String> {
) -> i32 {
x86_64_generic_setup_stack(buf, saved_regs, requested_stack_size, fn_call_stack_size)
}
@ -561,7 +549,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
saved_regs: &[X86_64GeneralReg],
aligned_stack_size: i32,
fn_call_stack_size: i32,
) -> Result<(), String> {
) {
x86_64_generic_cleanup_stack(buf, saved_regs, aligned_stack_size, fn_call_stack_size)
}
@ -571,10 +559,10 @@ 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>,
) -> Result<(), String> {
) {
let mut base_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)? {
if X86_64WindowsFastcall::returns_via_arg_pointer(ret_layout) {
symbol_map.insert(
Symbol::RET_POINTER,
SymbolStorage::GeneralReg(Self::GENERAL_PARAM_REGS[i]),
@ -595,27 +583,20 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
}
Layout::Builtin(Builtin::Str) => {
// I think this just needs to be passed on the stack, so not a huge deal.
return Err(
unimplemented!(
"Passing str args with Windows fast call not yet implemented."
.to_string(),
);
}
Layout::Struct(&[]) => {}
x => {
return Err(format!(
"Loading args with layout {:?} not yet implemented",
x
));
unimplemented!("Loading args with layout {:?} not yet implemented", x);
}
}
} else {
base_offset += match layout {
Layout::Builtin(single_register_builtins!()) => 8,
x => {
return Err(format!(
"Loading args with layout {:?} not yet implemented",
x
));
unimplemented!("Loading args with layout {:?} not yet implemented", x);
}
};
symbol_map.insert(
@ -628,7 +609,6 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
);
}
}
Ok(())
}
#[inline(always)]
@ -638,29 +618,29 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
args: &'a [Symbol],
arg_layouts: &[Layout<'a>],
ret_layout: &Layout<'a>,
) -> Result<u32, String> {
) -> u32 {
let mut stack_offset = Self::SHADOW_SPACE_SIZE as i32;
// 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(single_register_builtins!()) => {}
x => {
return Err(format!(
"receiving return type, {:?}, is not yet implemented",
x
));
unimplemented!("receiving return type, {:?}, is not yet implemented", x);
}
}
for (i, layout) in arg_layouts.iter().enumerate() {
match layout {
Layout::Builtin(single_register_integers!()) => {
let storage = match symbol_map.get(&args[i]) {
Some(storage) => storage,
None => {
internal_error!("function argument does not reference any symbol")
}
};
if i < Self::GENERAL_PARAM_REGS.len() {
// Load the value to the param reg.
let dst = Self::GENERAL_PARAM_REGS[i];
match symbol_map
.get(&args[i])
.ok_or("function argument does not reference any symbol")?
{
match storage {
SymbolStorage::GeneralReg(reg)
| SymbolStorage::BaseAndGeneralReg { reg, .. } => {
X86_64Assembler::mov_reg64_reg64(buf, dst, *reg);
@ -669,17 +649,12 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
X86_64Assembler::mov_reg64_base32(buf, dst, *offset);
}
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => {
return Err(
"Cannot load floating point symbol into GeneralReg".to_string()
)
internal_error!("Cannot load floating point symbol into GeneralReg")
}
}
} else {
// Load the value to the stack.
match symbol_map
.get(&args[i])
.ok_or("function argument does not reference any symbol")?
{
match storage {
SymbolStorage::GeneralReg(reg)
| SymbolStorage::BaseAndGeneralReg { reg, .. } => {
X86_64Assembler::mov_stack32_reg64(buf, stack_offset, *reg);
@ -698,22 +673,23 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
);
}
SymbolStorage::FloatReg(_) | SymbolStorage::BaseAndFloatReg { .. } => {
return Err(
"Cannot load floating point symbol into GeneralReg".to_string()
)
internal_error!("Cannot load floating point symbol into GeneralReg")
}
}
stack_offset += 8;
}
}
Layout::Builtin(single_register_floats!()) => {
let storage = match symbol_map.get(&args[i]) {
Some(storage) => storage,
None => {
internal_error!("function argument does not reference any symbol")
}
};
if i < Self::FLOAT_PARAM_REGS.len() {
// Load the value to the param reg.
let dst = Self::FLOAT_PARAM_REGS[i];
match symbol_map
.get(&args[i])
.ok_or("function argument does not reference any symbol")?
{
match storage {
SymbolStorage::FloatReg(reg)
| SymbolStorage::BaseAndFloatReg { reg, .. } => {
X86_64Assembler::mov_freg64_freg64(buf, dst, *reg);
@ -723,15 +699,12 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
}
SymbolStorage::GeneralReg(_)
| SymbolStorage::BaseAndGeneralReg { .. } => {
return Err("Cannot load general symbol into FloatReg".to_string())
unimplemented!("Cannot load general symbol into FloatReg")
}
}
} else {
// Load the value to the stack.
match symbol_map
.get(&args[i])
.ok_or("function argument does not reference any symbol")?
{
match storage {
SymbolStorage::FloatReg(reg)
| SymbolStorage::BaseAndFloatReg { reg, .. } => {
X86_64Assembler::mov_stack32_freg64(buf, stack_offset, *reg);
@ -751,7 +724,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
}
SymbolStorage::GeneralReg(_)
| SymbolStorage::BaseAndGeneralReg { .. } => {
return Err("Cannot load general symbol into FloatReg".to_string())
unimplemented!("Cannot load general symbol into FloatReg")
}
}
stack_offset += 8;
@ -759,20 +732,15 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
}
Layout::Builtin(Builtin::Str) => {
// I think this just needs to be passed on the stack, so not a huge deal.
return Err(
"Passing str args with Windows fast call not yet implemented.".to_string(),
);
unimplemented!("Passing str args with Windows fast call not yet implemented.");
}
Layout::Struct(&[]) => {}
x => {
return Err(format!(
"calling with arg type, {:?}, is not yet implemented",
x
));
unimplemented!("calling with arg type, {:?}, is not yet implemented", x);
}
}
}
Ok(stack_offset as u32)
stack_offset as u32
}
fn return_struct<'a>(
@ -781,14 +749,14 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
_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())
) {
unimplemented!("Returning structs not yet implemented for X86_64WindowsFastCall");
}
fn returns_via_arg_pointer(ret_layout: &Layout) -> Result<bool, String> {
fn returns_via_arg_pointer(ret_layout: &Layout) -> bool {
// 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
Ok(ret_layout.stack_size(PTR_SIZE) > 8)
ret_layout.stack_size(PTR_SIZE) > 8
}
}
@ -798,15 +766,17 @@ fn x86_64_generic_setup_stack<'a>(
saved_regs: &[X86_64GeneralReg],
requested_stack_size: i32,
fn_call_stack_size: i32,
) -> Result<i32, String> {
) -> i32 {
X86_64Assembler::push_reg64(buf, X86_64GeneralReg::RBP);
X86_64Assembler::mov_reg64_reg64(buf, X86_64GeneralReg::RBP, X86_64GeneralReg::RSP);
let full_stack_size = requested_stack_size
let full_stack_size = match requested_stack_size
.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")?;
.and_then(|size| size.checked_add(fn_call_stack_size))
{
Some(size) => size,
_ => internal_error!("Ran out of stack space"),
};
let alignment = if full_stack_size <= 0 {
0
} else {
@ -832,12 +802,12 @@ fn x86_64_generic_setup_stack<'a>(
X86_64Assembler::mov_base32_reg64(buf, -offset, *reg);
offset -= 8;
}
Ok(aligned_stack_size)
aligned_stack_size
} else {
Ok(0)
0
}
} else {
Err("Ran out of stack space".to_string())
internal_error!("Ran out of stack space");
}
}
@ -848,7 +818,7 @@ fn x86_64_generic_cleanup_stack<'a>(
saved_regs: &[X86_64GeneralReg],
aligned_stack_size: i32,
fn_call_stack_size: i32,
) -> Result<(), String> {
) {
if aligned_stack_size > 0 {
let mut offset = aligned_stack_size - fn_call_stack_size;
for reg in saved_regs {
@ -864,7 +834,6 @@ fn x86_64_generic_cleanup_stack<'a>(
}
//X86_64Assembler::mov_reg64_reg64(buf, X86_64GeneralReg::RSP, X86_64GeneralReg::RBP);
X86_64Assembler::pop_reg64(buf, X86_64GeneralReg::RBP);
Ok(())
}
impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {

View file

@ -13,6 +13,7 @@ use roc_mono::ir::{
SelfRecursive, Stmt,
};
use roc_mono::layout::{Builtin, Layout, LayoutIds};
use roc_reporting::internal_error;
mod generic64;
mod object_builder;
@ -58,7 +59,7 @@ where
Self: Sized,
{
/// new creates a new backend that will output to the specific Object.
fn new(env: &'a Env) -> Result<Self, String>;
fn new(env: &'a Env) -> Self;
fn env(&self) -> &'a Env<'a>;
@ -70,55 +71,48 @@ 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) -> Result<(&'a [u8], &[Relocation]), String>;
fn finalize(&mut self) -> (&'a [u8], &[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.
fn load_args(
&mut self,
args: &'a [(Layout<'a>, Symbol)],
ret_layout: &Layout<'a>,
) -> Result<(), String>;
fn load_args(&mut self, args: &'a [(Layout<'a>, Symbol)], ret_layout: &Layout<'a>);
/// Used for generating wrappers for malloc/realloc/free
fn build_wrapped_jmp(&mut self) -> Result<(&'a [u8], u64), String>;
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>) -> Result<(&'a [u8], &[Relocation]), String> {
fn build_proc(&mut self, proc: Proc<'a>) -> (&'a [u8], &[Relocation]) {
let proc_name = LayoutIds::default()
.get(proc.name, &proc.ret_layout)
.to_symbol_string(proc.name, &self.env().interns);
self.reset(proc_name, proc.is_self_recursive);
self.load_args(proc.args, &proc.ret_layout)?;
self.load_args(proc.args, &proc.ret_layout);
for (layout, sym) in proc.args {
self.set_layout_map(*sym, layout)?;
self.set_layout_map(*sym, layout);
}
self.scan_ast(&proc.body);
self.create_free_map();
self.build_stmt(&proc.body, &proc.ret_layout)?;
self.build_stmt(&proc.body, &proc.ret_layout);
self.finalize()
}
/// build_stmt builds a statement and outputs at the end of the buffer.
fn build_stmt(&mut self, stmt: &Stmt<'a>, ret_layout: &Layout<'a>) -> Result<(), String> {
fn build_stmt(&mut self, stmt: &Stmt<'a>, ret_layout: &Layout<'a>) {
match stmt {
Stmt::Let(sym, expr, layout, following) => {
self.build_expr(sym, expr, layout)?;
self.set_layout_map(*sym, layout)?;
self.free_symbols(stmt)?;
self.build_stmt(following, ret_layout)?;
Ok(())
self.build_expr(sym, expr, layout);
self.set_layout_map(*sym, layout);
self.free_symbols(stmt);
self.build_stmt(following, ret_layout);
}
Stmt::Ret(sym) => {
self.load_literal_symbols(&[*sym])?;
self.return_symbol(sym, ret_layout)?;
self.free_symbols(stmt)?;
Ok(())
self.load_literal_symbols(&[*sym]);
self.return_symbol(sym, ret_layout);
self.free_symbols(stmt);
}
Stmt::Refcounting(_modify, following) => {
// TODO: actually deal with refcounting. For hello world, we just skipped it.
self.build_stmt(following, ret_layout)?;
Ok(())
self.build_stmt(following, ret_layout);
}
Stmt::Switch {
cond_symbol,
@ -127,16 +121,15 @@ where
default_branch,
ret_layout,
} => {
self.load_literal_symbols(&[*cond_symbol])?;
self.load_literal_symbols(&[*cond_symbol]);
self.build_switch(
cond_symbol,
cond_layout,
branches,
default_branch,
ret_layout,
)?;
self.free_symbols(stmt)?;
Ok(())
);
self.free_symbols(stmt);
}
Stmt::Join {
id,
@ -145,11 +138,10 @@ where
remainder,
} => {
for param in parameters.iter() {
self.set_layout_map(param.symbol, &param.layout)?;
self.set_layout_map(param.symbol, &param.layout);
}
self.build_join(id, parameters, body, remainder, ret_layout)?;
self.free_symbols(stmt)?;
Ok(())
self.build_join(id, parameters, body, remainder, ret_layout);
self.free_symbols(stmt);
}
Stmt::Jump(id, args) => {
let mut arg_layouts: bumpalo::collections::Vec<Layout<'a>> =
@ -160,14 +152,13 @@ where
if let Some(layout) = layout_map.get(arg) {
arg_layouts.push(*layout);
} else {
return Err(format!("the argument, {:?}, has no know layout", arg));
internal_error!("the argument, {:?}, has no know layout", arg);
}
}
self.build_jump(id, args, arg_layouts.into_bump_slice(), ret_layout)?;
self.free_symbols(stmt)?;
Ok(())
self.build_jump(id, args, arg_layouts.into_bump_slice(), ret_layout);
self.free_symbols(stmt);
}
x => Err(format!("the statement, {:?}, is not yet implemented", x)),
x => unimplemented!("the statement, {:?}, is not yet implemented", x),
}
}
// build_switch generates a instructions for a switch statement.
@ -178,7 +169,7 @@ where
branches: &'a [(u64, BranchInfo<'a>, Stmt<'a>)],
default_branch: &(BranchInfo<'a>, &'a Stmt<'a>),
ret_layout: &Layout<'a>,
) -> Result<(), String>;
);
// build_join generates a instructions for a join statement.
fn build_join(
@ -188,7 +179,7 @@ where
body: &'a Stmt<'a>,
remainder: &'a Stmt<'a>,
ret_layout: &Layout<'a>,
) -> Result<(), String>;
);
// build_jump generates a instructions for a jump statement.
fn build_jump(
@ -197,24 +188,18 @@ where
args: &'a [Symbol],
arg_layouts: &[Layout<'a>],
ret_layout: &Layout<'a>,
) -> Result<(), String>;
);
/// build_expr builds the expressions for the specified symbol.
/// The builder must keep track of the symbol because it may be referred to later.
fn build_expr(
&mut self,
sym: &Symbol,
expr: &Expr<'a>,
layout: &Layout<'a>,
) -> Result<(), String> {
fn build_expr(&mut self, sym: &Symbol, expr: &Expr<'a>, layout: &Layout<'a>) {
match expr {
Expr::Literal(lit) => {
if self.env().lazy_literals {
self.literal_map().insert(*sym, *lit);
} else {
self.load_literal(sym, lit)?;
self.load_literal(sym, lit);
}
Ok(())
}
Expr::Call(roc_mono::ir::Call {
call_type,
@ -244,13 +229,10 @@ where
.get(*func_sym, layout)
.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.load_literal_symbols(arguments);
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
} else {
Err(format!(
"the function, {:?}, is not yet implemented",
func_sym
))
unimplemented!("the function, {:?}, is not yet implemented", func_sym)
}
}
@ -263,7 +245,7 @@ where
if let Some(layout) = layout_map.get(arg) {
arg_layouts.push(*layout);
} else {
return Err(format!("the argument, {:?}, has no know layout", arg));
internal_error!("the argument, {:?}, has no know layout", arg);
}
}
self.build_run_low_level(
@ -274,19 +256,21 @@ where
layout,
)
}
x => Err(format!("the call type, {:?}, is not yet implemented", x)),
x => unimplemented!("the call type, {:?}, is not yet implemented", x),
}
}
Expr::Struct(fields) => {
self.load_literal_symbols(fields)?;
self.create_struct(sym, layout, fields)
self.load_literal_symbols(fields);
self.create_struct(sym, layout, fields);
}
Expr::StructAtIndex {
index,
field_layouts,
structure,
} => self.load_struct_at_index(sym, structure, *index, field_layouts),
x => Err(format!("the expression, {:?}, is not yet implemented", x)),
} => {
self.load_struct_at_index(sym, structure, *index, field_layouts);
}
x => unimplemented!("the expression, {:?}, is not yet implemented", x),
}
}
@ -299,9 +283,9 @@ where
args: &'a [Symbol],
arg_layouts: &[Layout<'a>],
ret_layout: &Layout<'a>,
) -> Result<(), String> {
) {
// Now that the arguments are needed, load them if they are literals.
self.load_literal_symbols(args)?;
self.load_literal_symbols(args);
match lowlevel {
LowLevel::NumAbs => {
debug_assert_eq!(
@ -464,7 +448,7 @@ where
arg_layouts,
ret_layout,
),
x => Err(format!("low level, {:?}. is not yet implemented", x)),
x => unimplemented!("low level, {:?}. is not yet implemented", x),
}
}
@ -477,99 +461,47 @@ where
args: &'a [Symbol],
arg_layouts: &[Layout<'a>],
ret_layout: &Layout<'a>,
) -> Result<(), String>;
);
/// build_num_abs stores the absolute value of src into dst.
fn build_num_abs(
&mut self,
dst: &Symbol,
src: &Symbol,
layout: &Layout<'a>,
) -> Result<(), String>;
fn build_num_abs(&mut self, dst: &Symbol, src: &Symbol, layout: &Layout<'a>);
/// build_num_add stores the sum of src1 and src2 into dst.
fn build_num_add(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
layout: &Layout<'a>,
) -> Result<(), String>;
fn build_num_add(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &Layout<'a>);
/// build_num_mul stores `src1 * src2` into dst.
fn build_num_mul(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
layout: &Layout<'a>,
) -> Result<(), String>;
fn build_num_mul(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &Layout<'a>);
/// build_num_neg stores the negated value of src into dst.
fn build_num_neg(
&mut self,
dst: &Symbol,
src: &Symbol,
layout: &Layout<'a>,
) -> Result<(), String>;
fn build_num_neg(&mut self, dst: &Symbol, src: &Symbol, layout: &Layout<'a>);
/// build_num_sub stores the `src1 - src2` difference into dst.
fn build_num_sub(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
layout: &Layout<'a>,
) -> Result<(), String>;
fn build_num_sub(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &Layout<'a>);
/// build_eq stores the result of `src1 == src2` into dst.
fn build_eq(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
arg_layout: &Layout<'a>,
) -> Result<(), String>;
fn build_eq(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &Layout<'a>);
/// build_neq stores the result of `src1 != src2` into dst.
fn build_neq(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
arg_layout: &Layout<'a>,
) -> Result<(), String>;
fn build_neq(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &Layout<'a>);
/// build_num_lt stores the result of `src1 < src2` into dst.
fn build_num_lt(
&mut self,
dst: &Symbol,
src1: &Symbol,
src2: &Symbol,
arg_layout: &Layout<'a>,
) -> Result<(), String>;
fn build_num_lt(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &Layout<'a>);
/// literal_map gets the map from symbol to literal, used for lazy loading and literal folding.
fn literal_map(&mut self) -> &mut MutMap<Symbol, Literal<'a>>;
fn load_literal_symbols(&mut self, syms: &[Symbol]) -> Result<(), String> {
fn load_literal_symbols(&mut self, syms: &[Symbol]) {
if self.env().lazy_literals {
for sym in syms {
if let Some(lit) = self.literal_map().remove(sym) {
self.load_literal(sym, &lit)?;
self.load_literal(sym, &lit);
}
}
}
Ok(())
}
/// create_struct creates a struct with the elements specified loaded into it as data.
fn create_struct(
&mut self,
sym: &Symbol,
layout: &Layout<'a>,
fields: &'a [Symbol],
) -> Result<(), String>;
fn create_struct(&mut self, sym: &Symbol, layout: &Layout<'a>, fields: &'a [Symbol]);
/// load_struct_at_index loads into `sym` the value at `index` in `structure`.
fn load_struct_at_index(
@ -578,27 +510,26 @@ where
structure: &Symbol,
index: u64,
field_layouts: &'a [Layout<'a>],
) -> Result<(), String>;
);
/// load_literal sets a symbol to be equal to a literal.
fn load_literal(&mut self, sym: &Symbol, lit: &Literal<'a>) -> Result<(), String>;
fn load_literal(&mut self, sym: &Symbol, lit: &Literal<'a>);
/// return_symbol moves a symbol to the correct return location for the backend and adds a jump to the end of the function.
fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>) -> Result<(), String>;
fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>);
/// free_symbols will free all symbols for the given statement.
fn free_symbols(&mut self, stmt: &Stmt<'a>) -> Result<(), String> {
fn free_symbols(&mut self, stmt: &Stmt<'a>) {
if let Some(syms) = self.free_map().remove(&(stmt as *const Stmt<'a>)) {
for sym in syms {
// println!("Freeing symbol: {:?}", sym);
self.free_symbol(&sym)?;
self.free_symbol(&sym);
}
}
Ok(())
}
/// free_symbol frees any registers or stack space used to hold a symbol.
fn free_symbol(&mut self, sym: &Symbol) -> Result<(), String>;
fn free_symbol(&mut self, sym: &Symbol);
/// set_last_seen sets the statement a symbol was last seen in.
fn set_last_seen(
@ -617,20 +548,18 @@ where
fn last_seen_map(&mut self) -> &mut MutMap<Symbol, *const Stmt<'a>>;
/// set_layout_map sets the layout for a specific symbol.
fn set_layout_map(&mut self, sym: Symbol, layout: &Layout<'a>) -> Result<(), String> {
fn set_layout_map(&mut self, sym: Symbol, layout: &Layout<'a>) {
if let Some(old_layout) = self.layout_map().insert(sym, *layout) {
// Layout map already contains the symbol. We should never need to overwrite.
// If the layout is not the same, that is a bug.
if &old_layout != layout {
Err(format!(
"Overwriting layout for symbol, {:?}. This should never happen. got {:?}, want {:?}",
sym, layout, old_layout
))
} else {
Ok(())
internal_error!(
"Overwriting layout for symbol, {:?}: got {:?}, want {:?}",
sym,
layout,
old_layout
)
}
} else {
Ok(())
}
}

View file

@ -10,6 +10,7 @@ use object::{
use roc_collections::all::MutMap;
use roc_module::symbol;
use roc_mono::ir::{Proc, ProcLayout};
use roc_reporting::internal_error;
use target_lexicon::{Architecture as TargetArch, BinaryFormat as TargetBF, Triple};
// This is used by some code below which is currently commented out.
@ -22,7 +23,7 @@ pub fn build_module<'a>(
env: &'a Env,
target: &Triple,
procedures: MutMap<(symbol::Symbol, ProcLayout<'a>), Proc<'a>>,
) -> Result<Object, String> {
) -> Object {
match target {
Triple {
architecture: TargetArch::X86_64,
@ -34,7 +35,7 @@ pub fn build_module<'a>(
x86_64::X86_64FloatReg,
x86_64::X86_64Assembler,
x86_64::X86_64SystemV,
> = Backend::new(env)?;
> = Backend::new(env);
build_object(
env,
procedures,
@ -52,7 +53,7 @@ pub fn build_module<'a>(
x86_64::X86_64FloatReg,
x86_64::X86_64Assembler,
x86_64::X86_64SystemV,
> = Backend::new(env)?;
> = Backend::new(env);
build_object(
env,
procedures,
@ -74,7 +75,7 @@ pub fn build_module<'a>(
aarch64::AArch64FloatReg,
aarch64::AArch64Assembler,
aarch64::AArch64Call,
> = Backend::new(env)?;
> = Backend::new(env);
build_object(
env,
procedures,
@ -92,7 +93,7 @@ pub fn build_module<'a>(
aarch64::AArch64FloatReg,
aarch64::AArch64Assembler,
aarch64::AArch64Call,
> = Backend::new(env)?;
> = Backend::new(env);
build_object(
env,
procedures,
@ -104,9 +105,7 @@ pub fn build_module<'a>(
),
)
}
x => Err(format! {
"the target, {:?}, is not yet implemented",
x}),
x => unimplemented!("the target, {:?}, is not yet implemented", x),
}
}
@ -115,7 +114,7 @@ fn generate_wrapper<'a, B: Backend<'a>>(
output: &mut Object,
wrapper_name: String,
wraps: String,
) -> Result<(), String> {
) {
let text_section = output.section_id(StandardSection::Text);
let proc_symbol = Symbol {
name: wrapper_name.as_bytes().to_vec(),
@ -128,7 +127,7 @@ fn generate_wrapper<'a, B: Backend<'a>>(
flags: SymbolFlags::None,
};
let proc_id = output.add_symbol(proc_symbol);
let (proc_data, offset) = backend.build_wrapped_jmp()?;
let (proc_data, offset) = backend.build_wrapped_jmp();
let proc_offset = output.add_symbol_data(proc_id, text_section, proc_data, 16);
let name = wraps.as_str().as_bytes();
@ -154,13 +153,12 @@ fn generate_wrapper<'a, B: Backend<'a>>(
addend: -4,
};
output
.add_relocation(text_section, reloc)
.map_err(|e| format!("{:?}", e))?;
Ok(())
match output.add_relocation(text_section, reloc) {
Ok(obj) => obj,
Err(e) => internal_error!("{:?}", e),
}
} else {
Err(format!("failed to find fn symbol for {:?}", wraps))
unimplemented!("failed to find fn symbol for {:?}", wraps);
}
}
@ -169,7 +167,7 @@ fn build_object<'a, B: Backend<'a>>(
procedures: MutMap<(symbol::Symbol, ProcLayout<'a>), Proc<'a>>,
mut backend: B,
mut output: Object,
) -> Result<Object, String> {
) -> Object {
let data_section = output.section_id(StandardSection::Data);
/*
@ -188,25 +186,25 @@ fn build_object<'a, B: Backend<'a>>(
&mut output,
"roc_alloc".into(),
"malloc".into(),
)?;
);
generate_wrapper(
&mut backend,
&mut output,
"roc_realloc".into(),
"realloc".into(),
)?;
);
generate_wrapper(
&mut backend,
&mut output,
"roc_dealloc".into(),
"free".into(),
)?;
);
generate_wrapper(
&mut backend,
&mut output,
"roc_panic".into(),
"roc_builtins.utils.test_panic".into(),
)?;
);
}
// Setup layout_ids for procedure calls.
@ -253,7 +251,7 @@ fn build_object<'a, B: Backend<'a>>(
let mut relocations = bumpalo::vec![in env.arena];
for (fn_name, section_id, proc_id, proc) in procs {
let mut local_data_index = 0;
let (proc_data, relocs) = backend.build_proc(proc)?;
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 elfreloc = match reloc {
@ -293,7 +291,7 @@ fn build_object<'a, B: Backend<'a>>(
addend: -4,
}
} else {
return Err(format!("failed to find data symbol for {:?}", name));
internal_error!("failed to find data symbol for {:?}", name);
}
}
Relocation::LinkedFunction { offset, name } => {
@ -323,7 +321,7 @@ fn build_object<'a, B: Backend<'a>>(
addend: -4,
}
} else {
return Err(format!("failed to find fn symbol for {:?}", name));
internal_error!("failed to find fn symbol for {:?}", name);
}
}
Relocation::JmpToReturn { .. } => unreachable!(),
@ -332,9 +330,10 @@ fn build_object<'a, B: Backend<'a>>(
}
}
for (section_id, reloc) in relocations {
output
.add_relocation(section_id, reloc)
.map_err(|e| format!("{:?}", e))?;
match output.add_relocation(section_id, reloc) {
Ok(obj) => obj,
Err(e) => internal_error!("{:?}", e),
}
}
Ok(output)
output
}