mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
Add StrCat support and update hello-zig to support dev backend
This commit is contained in:
parent
2f24067267
commit
d1021d652d
6 changed files with 186 additions and 94 deletions
|
@ -3,6 +3,7 @@ use roc_build::{
|
|||
link::{link, rebuild_host, LinkType},
|
||||
program,
|
||||
};
|
||||
use roc_builtins::bitcode;
|
||||
use roc_can::builtins::builtin_defs_map;
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_load::file::LoadingProblem;
|
||||
|
@ -240,11 +241,19 @@ pub fn build_file<'a>(
|
|||
})?;
|
||||
BuildOutcome::NoProblems
|
||||
} else {
|
||||
let mut inputs = vec![
|
||||
host_input_path.as_path().to_str().unwrap(),
|
||||
app_o_file.to_str().unwrap(),
|
||||
];
|
||||
if matches!(opt_level, OptLevel::Development) {
|
||||
inputs.push(bitcode::OBJ_PATH);
|
||||
}
|
||||
|
||||
let (mut child, _) = // TODO use lld
|
||||
link(
|
||||
target,
|
||||
binary_path.clone(),
|
||||
&[host_input_path.as_path().to_str().unwrap(), app_o_file.to_str().unwrap()],
|
||||
&inputs,
|
||||
link_type
|
||||
)
|
||||
.map_err(|_| {
|
||||
|
|
|
@ -226,6 +226,7 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
|||
|
||||
#[inline(always)]
|
||||
fn load_args<'a>(
|
||||
_buf: &mut Vec<'a, u8>,
|
||||
_symbol_map: &mut MutMap<Symbol, SymbolStorage<AArch64GeneralReg, AArch64FloatReg>>,
|
||||
_args: &'a [(Layout<'a>, Symbol)],
|
||||
_ret_layout: &Layout<'a>,
|
||||
|
|
|
@ -48,6 +48,7 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
|||
|
||||
// load_args updates the symbol map to know where every arg is stored.
|
||||
fn load_args<'a>(
|
||||
buf: &mut Vec<'a, u8>,
|
||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
||||
|
@ -422,7 +423,12 @@ impl<
|
|||
args: &'a [(Layout<'a>, Symbol)],
|
||||
ret_layout: &Layout<'a>,
|
||||
) -> Result<(), String> {
|
||||
CC::load_args(&mut self.symbol_storage_map, args, ret_layout)?;
|
||||
CC::load_args(
|
||||
&mut self.buf,
|
||||
&mut self.symbol_storage_map,
|
||||
args,
|
||||
ret_layout,
|
||||
)?;
|
||||
// Update used and free regs.
|
||||
for (sym, storage) in &self.symbol_storage_map {
|
||||
match storage {
|
||||
|
@ -1063,7 +1069,7 @@ impl<
|
|||
Layout::Builtin(Builtin::Str) => {
|
||||
if self.symbol_storage_map.contains_key(&Symbol::RET_POINTER) {
|
||||
// This will happen on windows, return via pointer here.
|
||||
Err("Returning strings via pointer not yet implemented".to_string())
|
||||
return Err("Returning strings via pointer not yet implemented".to_string());
|
||||
} else {
|
||||
ASM::mov_reg64_base32(&mut self.buf, CC::GENERAL_RETURN_REGS[0], *offset);
|
||||
ASM::mov_reg64_base32(
|
||||
|
@ -1071,7 +1077,6 @@ impl<
|
|||
CC::GENERAL_RETURN_REGS[1],
|
||||
*offset + 8,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Layout::Struct(field_layouts) => {
|
||||
|
|
|
@ -177,6 +177,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
|
||||
#[inline(always)]
|
||||
fn load_args<'a>(
|
||||
buf: &mut Vec<'a, u8>,
|
||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
ret_layout: &Layout<'a>,
|
||||
|
@ -231,6 +232,29 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
);
|
||||
}
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
if general_i + 1 < Self::GENERAL_PARAM_REGS.len() {
|
||||
// Load the value to the param reg.
|
||||
let dst1 = Self::GENERAL_PARAM_REGS[general_i];
|
||||
let dst2 = Self::GENERAL_PARAM_REGS[general_i + 1];
|
||||
base_offset += 16;
|
||||
X86_64Assembler::mov_reg64_base32(buf, dst1, base_offset - 8);
|
||||
X86_64Assembler::mov_reg64_base32(buf, dst2, base_offset);
|
||||
symbol_map.insert(
|
||||
*sym,
|
||||
SymbolStorage::Base {
|
||||
offset: base_offset,
|
||||
size: 16,
|
||||
owned: true,
|
||||
},
|
||||
);
|
||||
general_i += 2;
|
||||
} else {
|
||||
return Err(
|
||||
"loading strings args on the stack is not yet implemented".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
Layout::Struct(&[]) => {}
|
||||
x => {
|
||||
return Err(format!(
|
||||
|
@ -257,7 +281,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
// 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!()) => {}
|
||||
Layout::Builtin(single_register_builtins!() | Builtin::Str) => {}
|
||||
x => {
|
||||
return Err(format!(
|
||||
"receiving return type, {:?}, is not yet implemented",
|
||||
|
@ -373,6 +397,32 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
|||
stack_offset += 8;
|
||||
}
|
||||
}
|
||||
Layout::Builtin(Builtin::Str) => {
|
||||
if general_i + 1 < Self::GENERAL_PARAM_REGS.len() {
|
||||
// Load the value to the param reg.
|
||||
let dst1 = Self::GENERAL_PARAM_REGS[general_i];
|
||||
let dst2 = Self::GENERAL_PARAM_REGS[general_i + 1];
|
||||
match symbol_map
|
||||
.get(&args[i])
|
||||
.ok_or("function argument does not reference any symbol")?
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
general_i += 2;
|
||||
} else {
|
||||
return Err(
|
||||
"calling functions with strings on the stack is not yet implemented"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Layout::Struct(&[]) => {}
|
||||
x => {
|
||||
return Err(format!(
|
||||
|
@ -516,6 +566,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
|
||||
#[inline(always)]
|
||||
fn load_args<'a>(
|
||||
_buf: &mut Vec<'a, u8>,
|
||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
ret_layout: &Layout<'a>,
|
||||
|
@ -535,9 +586,18 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
Layout::Builtin(single_register_integers!()) => {
|
||||
symbol_map
|
||||
.insert(*sym, SymbolStorage::GeneralReg(Self::GENERAL_PARAM_REGS[i]));
|
||||
i += 1;
|
||||
}
|
||||
Layout::Builtin(single_register_floats!()) => {
|
||||
symbol_map.insert(*sym, SymbolStorage::FloatReg(Self::FLOAT_PARAM_REGS[i]));
|
||||
i += 1;
|
||||
}
|
||||
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(),
|
||||
);
|
||||
}
|
||||
Layout::Struct(&[]) => {}
|
||||
x => {
|
||||
|
@ -547,7 +607,6 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
));
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
} else {
|
||||
base_offset += match layout {
|
||||
Layout::Builtin(single_register_builtins!()) => 8,
|
||||
|
@ -580,7 +639,6 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
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 {
|
||||
|
@ -597,7 +655,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
Layout::Builtin(single_register_integers!()) => {
|
||||
if i < Self::GENERAL_PARAM_REGS.len() {
|
||||
// Load the value to the param reg.
|
||||
let dst = Self::GENERAL_PARAM_REGS[reg_i];
|
||||
let dst = Self::GENERAL_PARAM_REGS[i];
|
||||
match symbol_map
|
||||
.get(&args[i])
|
||||
.ok_or("function argument does not reference any symbol")?
|
||||
|
@ -615,7 +673,6 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
)
|
||||
}
|
||||
}
|
||||
reg_i += 1;
|
||||
} else {
|
||||
// Load the value to the stack.
|
||||
match symbol_map
|
||||
|
@ -651,7 +708,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
Layout::Builtin(single_register_floats!()) => {
|
||||
if i < Self::FLOAT_PARAM_REGS.len() {
|
||||
// Load the value to the param reg.
|
||||
let dst = Self::FLOAT_PARAM_REGS[reg_i];
|
||||
let dst = Self::FLOAT_PARAM_REGS[i];
|
||||
match symbol_map
|
||||
.get(&args[i])
|
||||
.ok_or("function argument does not reference any symbol")?
|
||||
|
@ -668,7 +725,6 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
return Err("Cannot load general symbol into FloatReg".to_string())
|
||||
}
|
||||
}
|
||||
reg_i += 1;
|
||||
} else {
|
||||
// Load the value to the stack.
|
||||
match symbol_map
|
||||
|
@ -700,6 +756,12 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
|||
stack_offset += 8;
|
||||
}
|
||||
}
|
||||
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(),
|
||||
);
|
||||
}
|
||||
Layout::Struct(&[]) => {}
|
||||
x => {
|
||||
return Err(format!(
|
||||
|
|
|
@ -93,12 +93,8 @@ where
|
|||
for (layout, sym) in proc.args {
|
||||
self.set_layout_map(*sym, layout)?;
|
||||
}
|
||||
// let start = std::time::Instant::now();
|
||||
self.scan_ast(&proc.body);
|
||||
self.create_free_map();
|
||||
// let duration = start.elapsed();
|
||||
// println!("Time to calculate lifetimes: {:?}", duration);
|
||||
// println!("{:?}", self.last_seen_map());
|
||||
self.build_stmt(&proc.body, &proc.ret_layout)?;
|
||||
self.finalize()
|
||||
}
|
||||
|
@ -119,6 +115,11 @@ where
|
|||
self.free_symbols(stmt)?;
|
||||
Ok(())
|
||||
}
|
||||
Stmt::Refcounting(_modify, following) => {
|
||||
// TODO: actually deal with refcounting. For hello world, we just skipped it.
|
||||
self.build_stmt(following, ret_layout)?;
|
||||
Ok(())
|
||||
}
|
||||
Stmt::Switch {
|
||||
cond_symbol,
|
||||
cond_layout,
|
||||
|
@ -298,6 +299,13 @@ where
|
|||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
Symbol::STR_CONCAT => self.build_run_low_level(
|
||||
sym,
|
||||
&LowLevel::StrConcat,
|
||||
arguments,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
x if x
|
||||
.module_string(&self.env().interns)
|
||||
.starts_with(ModuleName::APP) =>
|
||||
|
@ -470,6 +478,13 @@ where
|
|||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
LowLevel::StrConcat => self.build_fn_call(
|
||||
sym,
|
||||
bitcode::STR_CONCAT.to_string(),
|
||||
args,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
),
|
||||
x => Err(format!("low level, {:?}. is not yet implemented", x)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// use roc_std::{RocList, RocStr};
|
||||
#[macro_use]
|
||||
extern crate indoc;
|
||||
// #[macro_use]
|
||||
// extern crate indoc;
|
||||
|
||||
#[macro_use]
|
||||
mod helpers;
|
||||
|
||||
#[cfg(all(test, any(target_os = "linux", target_os = "macos"), any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
|
||||
mod dev_str {
|
||||
// use roc_std::{RocList, RocStr};
|
||||
// #[test]
|
||||
// fn str_split_bigger_delimiter_small_str() {
|
||||
// assert_evals_to!(
|
||||
|
@ -261,7 +261,7 @@ mod dev_str {
|
|||
// // Verifies that we zero out unused bytes in the string.
|
||||
// // This is important so that string equality tests don't randomly
|
||||
// // fail due to unused memory being there!
|
||||
// assert_llvm_evals_to!(
|
||||
// assert_evals_to!(
|
||||
// "\"J\"",
|
||||
// [
|
||||
// 0x4a,
|
||||
|
@ -285,57 +285,57 @@ mod dev_str {
|
|||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn small_str_concat_empty_first_arg() {
|
||||
// assert_llvm_evals_to!(
|
||||
// r#"Str.concat "" "JJJJJJJJJJJJJJJ""#,
|
||||
// [
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0b1000_1111
|
||||
// ],
|
||||
// [u8; 16]
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn small_str_concat_empty_first_arg() {
|
||||
assert_evals_to!(
|
||||
r#"Str.concat "" "JJJJJJJJJJJJJJJ""#,
|
||||
[
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0b1000_1111
|
||||
],
|
||||
[u8; 16]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn small_str_concat_empty_second_arg() {
|
||||
// assert_llvm_evals_to!(
|
||||
// r#"Str.concat "JJJJJJJJJJJJJJJ" """#,
|
||||
// [
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0b1000_1111
|
||||
// ],
|
||||
// [u8; 16]
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn small_str_concat_empty_second_arg() {
|
||||
assert_evals_to!(
|
||||
r#"Str.concat "JJJJJJJJJJJJJJJ" """#,
|
||||
[
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0b1000_1111
|
||||
],
|
||||
[u8; 16]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn small_str_concat_small_to_big() {
|
||||
|
@ -346,31 +346,31 @@ mod dev_str {
|
|||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn small_str_concat_small_to_small_staying_small() {
|
||||
// assert_llvm_evals_to!(
|
||||
// r#"Str.concat "J" "JJJJJJJJJJJJJJ""#,
|
||||
// [
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0x4a,
|
||||
// 0b1000_1111
|
||||
// ],
|
||||
// [u8; 16]
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn small_str_concat_small_to_small_staying_small() {
|
||||
assert_evals_to!(
|
||||
r#"Str.concat "J" "JJJJJJJJJJJJJJ""#,
|
||||
[
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0x4a,
|
||||
0b1000_1111
|
||||
],
|
||||
[u8; 16]
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn small_str_concat_small_to_small_overflow_to_big() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue