From 919a51137a71f1d46c9b68c569462e2f72d40947 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 27 Jan 2024 17:46:19 +0100 Subject: [PATCH 1/7] make ROC_DEV_WRITE_OBJ work in more cases --- crates/compiler/gen_dev/src/object_builder.rs | 22 +++++++++++++++++++ crates/compiler/test_gen/src/helpers/dev.rs | 6 ----- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/crates/compiler/gen_dev/src/object_builder.rs b/crates/compiler/gen_dev/src/object_builder.rs index 189c301c3f5..c86907cfdd7 100644 --- a/crates/compiler/gen_dev/src/object_builder.rs +++ b/crates/compiler/gen_dev/src/object_builder.rs @@ -29,6 +29,28 @@ pub fn build_module<'a, 'r>( layout_interner: &'r mut STLayoutInterner<'a>, target: &Triple, procedures: MutMap<(symbol::Symbol, ProcLayout<'a>), Proc<'a>>, +) -> Object<'a> { + let module_object = build_module_help(env, interns, layout_interner, target, procedures); + + if std::env::var("ROC_DEV_WRITE_OBJ").is_ok() { + let module_out = module_object + .write() + .expect("failed to build output object"); + + let file_path = std::env::temp_dir().join("app.o"); + println!("gen-test object file written to {}", file_path.display()); + std::fs::write(&file_path, module_out).expect("failed to write object to file"); + } + + module_object +} + +fn build_module_help<'a, 'r>( + env: &'r Env<'a>, + interns: &'r mut Interns, + layout_interner: &'r mut STLayoutInterner<'a>, + target: &Triple, + procedures: MutMap<(symbol::Symbol, ProcLayout<'a>), Proc<'a>>, ) -> Object<'a> { match target { Triple { diff --git a/crates/compiler/test_gen/src/helpers/dev.rs b/crates/compiler/test_gen/src/helpers/dev.rs index 9d90aaa5684..f5f2c2687f3 100644 --- a/crates/compiler/test_gen/src/helpers/dev.rs +++ b/crates/compiler/test_gen/src/helpers/dev.rs @@ -215,12 +215,6 @@ pub fn helper( let builtins_host_tempfile = roc_bitcode::host_tempfile().expect("failed to write host builtins object to tempfile"); - if std::env::var("ROC_DEV_WRITE_OBJ").is_ok() { - let file_path = std::env::temp_dir().join("app.o"); - println!("gen-test object file written to {}", file_path.display()); - std::fs::copy(&app_o_file, file_path).unwrap(); - } - let (mut child, dylib_path) = link( &target, app_o_file.clone(), From 2786e8ff7f9bd7510d39e50367f667f427286d99 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 27 Jan 2024 18:14:12 +0100 Subject: [PATCH 2/7] refactor glue roc calling logic --- crates/glue/src/load.rs | 75 +++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/crates/glue/src/load.rs b/crates/glue/src/load.rs index b329d1fefc7..bc24e910210 100644 --- a/crates/glue/src/load.rs +++ b/crates/glue/src/load.rs @@ -130,40 +130,13 @@ pub fn generate( } let lib = unsafe { Library::new(lib_path) }.unwrap(); - type MakeGlueReturnType = - roc_std::RocResult, roc_std::RocStr>; - type MakeGlue = unsafe extern "C" fn( - *mut RocCallResult, - &roc_std::RocList, - ); - - let make_glue: libloading::Symbol = unsafe { - lib.get("roc__makeGlueForHost_1_exposed_generic".as_bytes()) - .unwrap_or_else(|_| panic!("Unable to load glue function")) - }; let roc_types: roc_std::RocList = types.iter().map(|x| x.into()).collect(); - let mut files = - RocCallResult::new(roc_std::RocResult::err(roc_std::RocStr::empty())); - unsafe { make_glue(&mut files, &roc_types) }; - - // Roc will free data passed into it. So forget that data. - std::mem::forget(roc_types); - - let files: Result, roc_std::RocStr> = - match Result::from(files) { - Err((msg, tag)) => match tag { - CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#), - CrashTag::User => panic!(r#"User crash with message: "{msg}""#), - }, - Ok(x) => x.into(), - }; - - let files = files.unwrap_or_else(|err| { - eprintln!("Glue generation failed: {err}"); - - process::exit(1); - }); + + // NOTE: DO NOT DROP LIB! the return value will include static roc strings that + // are only kept alive when the dynamic library is not unloaded! + let files = call_roc_make_glue(&lib, backend, roc_types); + for roc_type::File { name, content } in &files { let valid_name = PathBuf::from(name.as_str()) .components() @@ -242,6 +215,44 @@ pub fn generate( } } +fn call_roc_make_glue( + lib: &Library, + backend: CodeGenBackend, + roc_types: roc_std::RocList, +) -> roc_std::RocList { + type MakeGlueReturnType = roc_std::RocResult, roc_std::RocStr>; + type MakeGlue = unsafe extern "C" fn( + *mut RocCallResult, + &roc_std::RocList, + ); + + let name_of_main = "roc__makeGlueForHost_1_exposed_generic"; + + let make_glue: libloading::Symbol = unsafe { + lib.get(name_of_main.as_bytes()) + .unwrap_or_else(|_| panic!("Unable to load glue function")) + }; + let mut files = RocCallResult::new(roc_std::RocResult::err(roc_std::RocStr::empty())); + unsafe { make_glue(&mut files, &roc_types) }; + + // Roc will free data passed into it. So forget that data. + std::mem::forget(roc_types); + + match Result::from(files) { + Err((msg, tag)) => match tag { + CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#), + CrashTag::User => panic!(r#"User crash with message: "{msg}""#), + }, + Ok(files_or_error) => match Result::from(files_or_error) { + Err(err) => { + eprintln!("Glue generation failed: {err}"); + process::exit(1); + } + Ok(files) => files, + }, + } +} + fn number_lambda_sets(subs: &Subs, initial: Variable) -> Vec { let mut lambda_sets = vec![]; let mut stack = vec![initial]; From 279434872246ded6abc27d3ff83b3ddb77e583e3 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 27 Jan 2024 21:58:56 +0100 Subject: [PATCH 3/7] fix bug in casting to u128/i128 --- crates/compiler/gen_dev/src/generic64/mod.rs | 18 ++++++++++++++++-- crates/compiler/test_gen/src/gen_num.rs | 5 +++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index d5d813fe1fb..ca319c61745 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -4871,14 +4871,28 @@ impl< // move a zero into the lower 8 bytes ASM::mov_reg64_imm64(buf, tmp_reg, 0x0); - ASM::mov_base32_reg64(buf, base_offset, tmp_reg); + ASM::mov_base32_reg64(buf, base_offset + 8, tmp_reg); - ASM::mov_base32_reg64(buf, base_offset + 8, src_reg); + ASM::mov_base32_reg64(buf, base_offset, src_reg); self.free_symbol(&tmp); return; } + (U128, I128) | (I128, U128) => { + let to_offset = self.storage_manager.claim_stack_area_layout( + self.layout_interner, + *dst, + Layout::from_int_width(target), + ); + + let (from_offset, size) = self.storage_manager.stack_offset_and_size(src); + + self.storage_manager + .copy_to_stack_offset(buf, size, from_offset, to_offset); + + return; + } _ => {} } diff --git a/crates/compiler/test_gen/src/gen_num.rs b/crates/compiler/test_gen/src/gen_num.rs index d77e6d13ea3..94aa1b1c9f8 100644 --- a/crates/compiler/test_gen/src/gen_num.rs +++ b/crates/compiler/test_gen/src/gen_num.rs @@ -2297,7 +2297,7 @@ num_conversion_tests! { to_i64_truncate_wraps, "10_000_000_000_000_000_000i128", -8446744073709551616 ) "Num.toI128", i128, ( - to_i128_same_width, "15u128", 15 + to_i128_same_width, "15u128", 15, ["gen-dev"] to_i128_extend, "15i8", 15 ) "Num.toU8", u8, ( @@ -2324,8 +2324,9 @@ num_conversion_tests! { to_u64_truncate_wraps, "10_000_000_000_000_000_000_000i128", 1864712049423024128 ) "Num.toU128", u128, ( - to_u128_same_width, "15i128", 15 + to_u128_same_width, "15i128", 15, ["gen-dev"] to_u128_extend, "15i8", 15 + to_u128_big, "11562537357600483583u64", 11562537357600483583, ["gen-dev"] ) "Num.toNat", usize, ( to_nat_same_width, "15i64", 15, ["gen-wasm", "gen-dev"] From 685025a2401d27be1f1bb3306214aa9a6f65ba20 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 27 Jan 2024 22:44:19 +0100 Subject: [PATCH 4/7] a working dev-backend RustGlue.roc with 2 caveates - list reallocation does not work (so we make fresh allocations instead - List.append in the glue main function causes a segfault. we use a concat with a single-element list instead --- crates/glue/src/load.rs | 102 ++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 31 deletions(-) diff --git a/crates/glue/src/load.rs b/crates/glue/src/load.rs index bc24e910210..2b6db205ea7 100644 --- a/crates/glue/src/load.rs +++ b/crates/glue/src/load.rs @@ -141,8 +141,8 @@ pub fn generate( let valid_name = PathBuf::from(name.as_str()) .components() .all(|comp| matches!(comp, Component::CurDir | Component::Normal(_))); - if !valid_name { - eprintln!("File name was invalid: {}", &name); + if !valid_name || name.is_empty() { + eprintln!("File name was invalid: {:?}", &name); process::exit(1); } @@ -220,36 +220,76 @@ fn call_roc_make_glue( backend: CodeGenBackend, roc_types: roc_std::RocList, ) -> roc_std::RocList { - type MakeGlueReturnType = roc_std::RocResult, roc_std::RocStr>; - type MakeGlue = unsafe extern "C" fn( - *mut RocCallResult, - &roc_std::RocList, - ); - - let name_of_main = "roc__makeGlueForHost_1_exposed_generic"; - - let make_glue: libloading::Symbol = unsafe { - lib.get(name_of_main.as_bytes()) - .unwrap_or_else(|_| panic!("Unable to load glue function")) - }; - let mut files = RocCallResult::new(roc_std::RocResult::err(roc_std::RocStr::empty())); - unsafe { make_glue(&mut files, &roc_types) }; - - // Roc will free data passed into it. So forget that data. - std::mem::forget(roc_types); - - match Result::from(files) { - Err((msg, tag)) => match tag { - CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#), - CrashTag::User => panic!(r#"User crash with message: "{msg}""#), - }, - Ok(files_or_error) => match Result::from(files_or_error) { - Err(err) => { - eprintln!("Glue generation failed: {err}"); - process::exit(1); + match backend { + CodeGenBackend::Assembly(_) => { + type MakeGlueReturnType = RocCallResult< + roc_std::RocResult, roc_std::RocStr>, + >; + type MakeGlue = + unsafe extern "C" fn(roc_std::RocList) -> MakeGlueReturnType; + + let name_of_main = "test_main"; + + let make_glue: libloading::Symbol = unsafe { + lib.get(name_of_main.as_bytes()) + .unwrap_or_else(|_| panic!("Unable to load glue function")) + }; + + let files = unsafe { make_glue(roc_types) }; + + // Roc will free data passed into it. So forget that data. + // std::mem::forget(roc_types); + + match Result::from(files) { + Err((msg, tag)) => match tag { + CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#), + CrashTag::User => panic!(r#"User crash with message: "{msg}""#), + }, + Ok(files_or_error) => match Result::from(files_or_error) { + Err(err) => { + eprintln!("Glue generation failed: {err}"); + process::exit(1); + } + Ok(files) => files, + }, } - Ok(files) => files, - }, + } + CodeGenBackend::Llvm(_) => { + type MakeGlueReturnType = + roc_std::RocResult, roc_std::RocStr>; + type MakeGlue = unsafe extern "C" fn( + *mut RocCallResult, + &roc_std::RocList, + ); + + let name_of_main = "roc__makeGlueForHost_1_exposed_generic"; + + let make_glue: libloading::Symbol = unsafe { + lib.get(name_of_main.as_bytes()) + .unwrap_or_else(|_| panic!("Unable to load glue function")) + }; + let mut files = RocCallResult::new(roc_std::RocResult::err(roc_std::RocStr::empty())); + unsafe { make_glue(&mut files, &roc_types) }; + + // Roc will free data passed into it. So forget that data. + std::mem::forget(roc_types); + + match Result::from(files) { + Err((msg, tag)) => match tag { + CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#), + CrashTag::User => panic!(r#"User crash with message: "{msg}""#), + }, + Ok(files_or_error) => match Result::from(files_or_error) { + Err(err) => { + eprintln!("Glue generation failed: {err}"); + process::exit(1); + } + Ok(files) => files, + }, + } + } + + CodeGenBackend::Wasm => todo!(), } } From bd0cb6338ef589e8ecc48c381a1dc3b5969e080c Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 28 Jan 2024 00:03:51 +0100 Subject: [PATCH 5/7] fix incorrect element width used for List.append in dev backend --- crates/compiler/gen_dev/src/generic64/mod.rs | 6 ++++- crates/compiler/test_gen/src/gen_list.rs | 24 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index ca319c61745..e33942dfeec 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -2949,7 +2949,11 @@ impl< self.load_layout_alignment(list_layout, Symbol::DEV_TMP); // Load element_width argument (usize). - self.load_layout_stack_size(*ret_layout, Symbol::DEV_TMP2); + let element_layout = match self.interner().get_repr(*ret_layout) { + LayoutRepr::Builtin(Builtin::List(e)) => e, + _ => unreachable!(), + }; + self.load_layout_stack_size(element_layout, Symbol::DEV_TMP2); // Load UpdateMode.Immutable argument (0u8) let u8_layout = Layout::U8; diff --git a/crates/compiler/test_gen/src/gen_list.rs b/crates/compiler/test_gen/src/gen_list.rs index 9ced967ecc9..fdf062621e6 100644 --- a/crates/compiler/test_gen/src/gen_list.rs +++ b/crates/compiler/test_gen/src/gen_list.rs @@ -821,6 +821,30 @@ fn list_append_longer_list() { ); } +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +fn list_append_record() { + assert_evals_to!( + indoc!( + r#" + [ + { name: "foo", content: "cfoo" }, + { name: "bar", content: "cbar" }, + { name: "baz", content: "cbaz" }, + ] + |> List.append { name: "spam", content: "cspam" } + "# + ), + RocList::from_slice(&[ + (RocStr::from("cfoo"), RocStr::from("foo"),), + (RocStr::from("cbar"), RocStr::from("bar"),), + (RocStr::from("cbaz"), RocStr::from("baz"),), + (RocStr::from("cspam"), RocStr::from("spam"),), + ]), + RocList<(RocStr, RocStr)> + ); +} + #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn list_prepend() { From 4fe383eb435edb87cfc436932c08cfa614c0fb5b Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 28 Jan 2024 00:12:06 +0100 Subject: [PATCH 6/7] cleanup --- crates/glue/src/load.rs | 49 ++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/crates/glue/src/load.rs b/crates/glue/src/load.rs index 2b6db205ea7..564f86a1e21 100644 --- a/crates/glue/src/load.rs +++ b/crates/glue/src/load.rs @@ -220,7 +220,7 @@ fn call_roc_make_glue( backend: CodeGenBackend, roc_types: roc_std::RocList, ) -> roc_std::RocList { - match backend { + let roc_call_result = match backend { CodeGenBackend::Assembly(_) => { type MakeGlueReturnType = RocCallResult< roc_std::RocResult, roc_std::RocStr>, @@ -235,24 +235,7 @@ fn call_roc_make_glue( .unwrap_or_else(|_| panic!("Unable to load glue function")) }; - let files = unsafe { make_glue(roc_types) }; - - // Roc will free data passed into it. So forget that data. - // std::mem::forget(roc_types); - - match Result::from(files) { - Err((msg, tag)) => match tag { - CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#), - CrashTag::User => panic!(r#"User crash with message: "{msg}""#), - }, - Ok(files_or_error) => match Result::from(files_or_error) { - Err(err) => { - eprintln!("Glue generation failed: {err}"); - process::exit(1); - } - Ok(files) => files, - }, - } + unsafe { make_glue(roc_types) } } CodeGenBackend::Llvm(_) => { type MakeGlueReturnType = @@ -274,22 +257,24 @@ fn call_roc_make_glue( // Roc will free data passed into it. So forget that data. std::mem::forget(roc_types); - match Result::from(files) { - Err((msg, tag)) => match tag { - CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#), - CrashTag::User => panic!(r#"User crash with message: "{msg}""#), - }, - Ok(files_or_error) => match Result::from(files_or_error) { - Err(err) => { - eprintln!("Glue generation failed: {err}"); - process::exit(1); - } - Ok(files) => files, - }, - } + files } CodeGenBackend::Wasm => todo!(), + }; + + match Result::from(roc_call_result) { + Err((msg, tag)) => match tag { + CrashTag::Roc => panic!(r#"Roc failed with message: "{msg}""#), + CrashTag::User => panic!(r#"User crash with message: "{msg}""#), + }, + Ok(files_or_error) => match Result::from(files_or_error) { + Err(err) => { + eprintln!("Glue generation failed: {err}"); + process::exit(1); + } + Ok(files) => files, + }, } } From 28ab81614cfd9341fdb741f4695d8426b92e95ab Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 28 Jan 2024 18:29:08 +0100 Subject: [PATCH 7/7] fix duplicate test that trips up llvm-wasm --- crates/compiler/test_gen/src/gen_str.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/compiler/test_gen/src/gen_str.rs b/crates/compiler/test_gen/src/gen_str.rs index 876075f3756..030fdd0776c 100644 --- a/crates/compiler/test_gen/src/gen_str.rs +++ b/crates/compiler/test_gen/src/gen_str.rs @@ -1741,11 +1741,11 @@ fn str_walk_utf8() { assert_evals_to!( indoc!( r#" - Str.walkUtf8WithIndex "abcd" [] (\list, byte, index -> List.append list (Pair index byte)) + Str.walkUtf8 "abcd" [] (\list, byte -> List.prepend list byte) "# ), - RocList::from_slice(&[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]), - RocList<(u32, char)> + RocList::from_slice(&[b'd', b'c', b'b', b'a']), + RocList ); }