From 82b72085339149ac1571801badaec3c4f30d8fb2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 19 Nov 2020 21:18:13 +0100 Subject: [PATCH 01/10] add debugir tool to build program --- compiler/build/src/program.rs | 62 +++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/compiler/build/src/program.rs b/compiler/build/src/program.rs index fb5ebafbaa..2336d54dbe 100644 --- a/compiler/build/src/program.rs +++ b/compiler/build/src/program.rs @@ -149,6 +149,68 @@ pub fn gen_from_mono_module( // Uncomment this to see the module's optimized LLVM instruction output: // env.module.print_to_stderr(); + // annotate the LLVM IR output with debug info + // so errors are reported with the line number of the LLVM source + if true { + let mut app_ll_file = std::path::PathBuf::from(app_o_file); + app_ll_file.set_extension("ll"); + + let mut app_ll_dbg_file = std::path::PathBuf::from(app_o_file); + app_ll_dbg_file.set_extension("dbg.ll"); + + let mut app_bc_file = std::path::PathBuf::from(app_o_file); + app_bc_file.set_extension("bc"); + + use std::process::Command; + + // write the ll code to a file, so we can modify it + module.print_to_file(&app_ll_file).unwrap(); + + // run the debugir https://github.com/vaivaswatha/debugir tool + match Command::new("debugir") + .env_clear() + .args(&[app_ll_file.to_str().unwrap()]) + .output() + { + Ok(_) => {} + Err(error) => { + use std::io::ErrorKind; + match error.kind() { + ErrorKind::NotFound => panic!( + r"I could not find the `debugir` tool on the PATH, install it from https://github.com/vaivaswatha/debugir" + ), + _ => panic!("{:?}", error), + } + } + } + + // assemble the .ll into a .bc + let _ = Command::new("llvm-as-10") + .env_clear() + .args(&[ + app_ll_dbg_file.to_str().unwrap(), + "-o", + app_bc_file.to_str().unwrap(), + ]) + .output() + .unwrap(); + + // write the .o file. Note that this builds the .o for the local machine, + // and ignores the `target_machine` entirely. + let _ = Command::new("llc-10") + .env_clear() + .args(&[ + "-filetype=obj", + app_bc_file.to_str().unwrap(), + "-o", + app_o_file.to_str().unwrap(), + ]) + .output() + .unwrap(); + + return; + } + // Emit the .o file let reloc = RelocMode::Default; From 52faaea73f5d6da26597839f21f93513a062ea52 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 19 Nov 2020 21:22:22 +0100 Subject: [PATCH 02/10] add balance example --- examples/balance/Main.roc | 7 ++ examples/balance/platform/Cargo.lock | 23 +++++ examples/balance/platform/Cargo.toml | 13 +++ examples/balance/platform/Pkg-Config.roc | 13 +++ examples/balance/platform/host.c | 7 ++ examples/balance/platform/src/lib.rs | 118 +++++++++++++++++++++++ 6 files changed, 181 insertions(+) create mode 100644 examples/balance/Main.roc create mode 100644 examples/balance/platform/Cargo.lock create mode 100644 examples/balance/platform/Cargo.toml create mode 100644 examples/balance/platform/Pkg-Config.roc create mode 100644 examples/balance/platform/host.c create mode 100644 examples/balance/platform/src/lib.rs diff --git a/examples/balance/Main.roc b/examples/balance/Main.roc new file mode 100644 index 0000000000..bb87fb29b2 --- /dev/null +++ b/examples/balance/Main.roc @@ -0,0 +1,7 @@ +app Main provides [ rocMain ] imports [ Effect ] + +rocMain : Effect.Effect {} as Fx +rocMain = + when List.len (Str.split "hello" "JJJJ there") is + _ -> Effect.putLine "Yay" + diff --git a/examples/balance/platform/Cargo.lock b/examples/balance/platform/Cargo.lock new file mode 100644 index 0000000000..c386bb6c4a --- /dev/null +++ b/examples/balance/platform/Cargo.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "host" +version = "0.1.0" +dependencies = [ + "roc_std 0.1.0", +] + +[[package]] +name = "libc" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "roc_std" +version = "0.1.0" +dependencies = [ + "libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" diff --git a/examples/balance/platform/Cargo.toml b/examples/balance/platform/Cargo.toml new file mode 100644 index 0000000000..70f3c1f86c --- /dev/null +++ b/examples/balance/platform/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "host" +version = "0.1.0" +authors = ["Richard Feldman "] +edition = "2018" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +roc_std = { path = "../../../roc_std" } + +[workspace] diff --git a/examples/balance/platform/Pkg-Config.roc b/examples/balance/platform/Pkg-Config.roc new file mode 100644 index 0000000000..a463e3bbb9 --- /dev/null +++ b/examples/balance/platform/Pkg-Config.roc @@ -0,0 +1,13 @@ +platform folkertdev/foo + provides [ mainForHost ] + requires { main : Effect {} } + imports [] + effects Effect + { + putChar : Int -> Effect {}, + putLine : Str -> Effect {}, + getLine : Effect Str + } + +mainForHost : Effect {} as Fx +mainForHost = main diff --git a/examples/balance/platform/host.c b/examples/balance/platform/host.c new file mode 100644 index 0000000000..0378c69589 --- /dev/null +++ b/examples/balance/platform/host.c @@ -0,0 +1,7 @@ +#include + +extern int rust_main(); + +int main() { + return rust_main(); +} diff --git a/examples/balance/platform/src/lib.rs b/examples/balance/platform/src/lib.rs new file mode 100644 index 0000000000..61957ea485 --- /dev/null +++ b/examples/balance/platform/src/lib.rs @@ -0,0 +1,118 @@ +#![allow(non_snake_case)] + +use roc_std::alloca; +use roc_std::RocCallResult; +use roc_std::RocStr; +use std::alloc::Layout; +use std::time::SystemTime; + +extern "C" { + #[link_name = "Main_rocMain_1_exposed"] + fn roc_main(output: *mut u8) -> (); + + #[link_name = "Main_rocMain_1_size"] + fn roc_main_size() -> i64; + + #[link_name = "Main_rocMain_1_Fx_caller"] + fn call_Fx(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> (); + + #[link_name = "Main_rocMain_1_Fx_size"] + fn size_Fx() -> i64; +} + +#[no_mangle] +pub fn roc_fx_putChar(foo: i64) -> () { + let character = foo as u8 as char; + print!("{}", character); + + () +} + +#[no_mangle] +pub fn roc_fx_putLine(line: RocStr) -> () { + let bytes = line.as_slice(); + let string = unsafe { std::str::from_utf8_unchecked(bytes) }; + println!("{}", string); + + () +} + +#[no_mangle] +pub fn roc_fx_getLine() -> RocStr { + use std::io::{self, BufRead}; + + let stdin = io::stdin(); + let line1 = stdin.lock().lines().next().unwrap().unwrap(); + + RocStr::from_slice_with_capacity(line1.as_bytes(), line1.len()) +} + +unsafe fn call_the_closure(function_pointer: *const u8, closure_data_ptr: *const u8) -> i64 { + let size = size_Fx() as usize; + + alloca::with_stack_bytes(size, |buffer| { + let buffer: *mut std::ffi::c_void = buffer; + let buffer: *mut u8 = buffer as *mut u8; + + call_Fx( + function_pointer, + closure_data_ptr as *const u8, + buffer as *mut u8, + ); + + let output = &*(buffer as *mut RocCallResult); + + // match output.into() { + // Ok(v) => v, + // Err(e) => panic!("failed with {}", e), + // } + 32 + }) +} + +#[no_mangle] +pub fn rust_main() -> isize { + println!("Running Roc closure"); + let start_time = SystemTime::now(); + + let size = unsafe { roc_main_size() } as usize; + let layout = Layout::array::(size).unwrap(); + let answer = unsafe { + let buffer = std::alloc::alloc(layout); + + roc_main(buffer); + + let output = &*(buffer as *mut RocCallResult<()>); + + match output.into() { + Ok(()) => { + let function_pointer = { + // this is a pointer to the location where the function pointer is stored + // we pass just the function pointer + let temp = buffer.offset(8) as *const i64; + + (*temp) as *const u8 + }; + + let closure_data_ptr = buffer.offset(16); + + call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8) + } + Err(msg) => { + std::alloc::dealloc(buffer, layout); + + panic!("Roc failed with message: {}", msg); + } + } + }; + let end_time = SystemTime::now(); + let duration = end_time.duration_since(start_time).unwrap(); + + println!( + "Roc execution took {:.4} ms", + duration.as_secs_f64() * 1000.0, + ); + + // Exit code + 0 +} From 2dee43a3ca47c70a383933e76d4cbcbc3a9aa17e Mon Sep 17 00:00:00 2001 From: Pit Capitain Date: Thu, 19 Nov 2020 22:46:37 +0100 Subject: [PATCH 03/10] Fix llvm debug info errors * create subprogram for {}_catcher * create subprogram for {}_size * don't create a second subprogram for {}_exposed --- compiler/gen/src/llvm/build.rs | 45 +++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 704c7faf75..99a8cd7328 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -1682,10 +1682,7 @@ fn expose_function_to_host<'a, 'ctx, 'env>( ) { let c_function_name: String = format!("{}_exposed", roc_function.get_name().to_str().unwrap()); - let result = expose_function_to_host_help(env, roc_function, &c_function_name); - - let subprogram = env.new_subprogram(&c_function_name); - result.set_subprogram(subprogram); + expose_function_to_host_help(env, roc_function, &c_function_name); } fn expose_function_to_host_help<'a, 'ctx, 'env>( @@ -1771,10 +1768,30 @@ fn expose_function_to_host_help<'a, 'ctx, 'env>( Some(Linkage::External), ); + let subprogram = env.new_subprogram(&size_function_name); + size_function.set_subprogram(subprogram); + let entry = context.append_basic_block(size_function, "entry"); builder.position_at_end(entry); + let func_scope = size_function.get_subprogram().unwrap(); + let lexical_block = env.dibuilder.create_lexical_block( + /* scope */ func_scope.as_debug_info_scope(), + /* file */ env.compile_unit.get_file(), + /* line_no */ 0, + /* column_no */ 0, + ); + + let loc = env.dibuilder.create_debug_location( + env.context, + /* line */ 0, + /* column */ 0, + /* current_scope */ lexical_block.as_debug_info_scope(), + /* inlined_at */ None, + ); + builder.set_current_debug_location(env.context, loc); + let size: BasicValueEnum = return_type.size_of().unwrap().into(); builder.build_return(Some(&size)); @@ -1969,6 +1986,9 @@ fn make_exception_catching_wrapper<'a, 'ctx, 'env>( env.module .add_function(&wrapper_function_name, wrapper_function_type, None); + let subprogram = env.new_subprogram(wrapper_function_name); + wrapper_function.set_subprogram(subprogram); + // our exposed main function adheres to the C calling convention wrapper_function.set_call_conventions(FAST_CALL_CONV); @@ -1978,6 +1998,23 @@ fn make_exception_catching_wrapper<'a, 'ctx, 'env>( let basic_block = context.append_basic_block(wrapper_function, "entry"); builder.position_at_end(basic_block); + let func_scope = wrapper_function.get_subprogram().unwrap(); + let lexical_block = env.dibuilder.create_lexical_block( + /* scope */ func_scope.as_debug_info_scope(), + /* file */ env.compile_unit.get_file(), + /* line_no */ 0, + /* column_no */ 0, + ); + + let loc = env.dibuilder.create_debug_location( + env.context, + /* line */ 0, + /* column */ 0, + /* current_scope */ lexical_block.as_debug_info_scope(), + /* inlined_at */ None, + ); + builder.set_current_debug_location(env.context, loc); + let result = invoke_and_catch( env, wrapper_function, From afb60974fe7a6ec3a76d08d979aba026da7c39d4 Mon Sep 17 00:00:00 2001 From: Pit Capitain Date: Thu, 19 Nov 2020 23:08:19 +0100 Subject: [PATCH 04/10] Fix clippy things Copied from 4db48d9f139b227d168fe6bff58dba5255413930 --- compiler/can/src/pattern.rs | 2 +- compiler/load/src/docs.rs | 2 +- compiler/mono/src/ir.rs | 2 +- compiler/parse/src/type_annotation.rs | 2 +- roc_std/src/alloca.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/can/src/pattern.rs b/compiler/can/src/pattern.rs index c5cd46b202..00b06ee875 100644 --- a/compiler/can/src/pattern.rs +++ b/compiler/can/src/pattern.rs @@ -361,7 +361,7 @@ pub fn canonicalize_pattern<'a>( // If we encountered an erroneous pattern (e.g. one with shadowing), // use the resulting RuntimeError. Otherwise, return a successful record destructure. - opt_erroneous.unwrap_or_else(|| Pattern::RecordDestructure { + opt_erroneous.unwrap_or(Pattern::RecordDestructure { whole_var, ext_var, destructs, diff --git a/compiler/load/src/docs.rs b/compiler/load/src/docs.rs index cd403ecb37..b52d655cf4 100644 --- a/compiler/load/src/docs.rs +++ b/compiler/load/src/docs.rs @@ -137,7 +137,7 @@ fn comments_or_new_lines_to_docs<'a>( match comment_or_new_line { DocComment(doc_str) => { docs.push_str(doc_str); - docs.push_str("\n"); + docs.push('\n'); } Newline | LineComment(_) => {} } diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 0feb855f47..689eae51f0 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -1263,7 +1263,7 @@ fn patterns_to_when<'a>( // Even if the body was Ok, replace it with this Err. // If it was already an Err, leave it at that Err, so the first // RuntimeError we encountered remains the first. - body = body.and_then(|_| { + body = body.and({ Err(Located { region: pattern.region, value, diff --git a/compiler/parse/src/type_annotation.rs b/compiler/parse/src/type_annotation.rs index a1946bea00..7195b9862d 100644 --- a/compiler/parse/src/type_annotation.rs +++ b/compiler/parse/src/type_annotation.rs @@ -342,7 +342,7 @@ fn parse_concrete_type<'a>( // // If we made it this far and don't have a next_char, then necessarily // we have consumed a '.' char previously. - return malformed(next_char.or_else(|| Some('.')), arena, state, parts); + return malformed(next_char.or(Some('.')), arena, state, parts); } if part_buf.is_empty() { diff --git a/roc_std/src/alloca.rs b/roc_std/src/alloca.rs index 1fa9c26525..ef30a59b44 100644 --- a/roc_std/src/alloca.rs +++ b/roc_std/src/alloca.rs @@ -6,7 +6,7 @@ use libc::{c_void, size_t}; #[link(name = "alloca")] extern "C" { - #[no_mangle] + #[allow(dead_code)] fn c_alloca(_: size_t) -> *mut c_void; } From 81c005068319dc92216361fd4f70aaead4901238 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 26 Nov 2020 03:06:10 +0000 Subject: [PATCH 05/10] Fix linking on Aarch64 with nix --- compiler/build/src/link.rs | 41 +++++++++++++++++++++++++++--------- compiler/build/src/target.rs | 11 +++++++++- shell.nix | 5 ++++- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/compiler/build/src/link.rs b/compiler/build/src/link.rs index 0a99a1717d..915c76d1ce 100644 --- a/compiler/build/src/link.rs +++ b/compiler/build/src/link.rs @@ -5,6 +5,8 @@ use inkwell::targets::{CodeModel, FileType, RelocMode}; use libloading::{Error, Library}; use roc_gen::llvm::build::OptLevel; use std::io; +use std::env; +use std::collections::HashMap; use std::path::{Path, PathBuf}; use std::process::{Child, Command, Output}; use target_lexicon::{Architecture, OperatingSystem, Triple}; @@ -25,7 +27,6 @@ pub fn link( ) -> io::Result<(Child, PathBuf)> { match target { Triple { - architecture: Architecture::X86_64, operating_system: OperatingSystem::Linux, .. } => link_linux(target, output_path, input_paths, link_type), @@ -46,9 +47,11 @@ pub fn rebuild_host(host_input_path: &Path) { let cargo_host_src = host_input_path.with_file_name("Cargo.toml"); let host_dest = host_input_path.with_file_name("host.o"); + let env_path = env::var("PATH").unwrap_or_else(|_| "".to_string()); // Compile host.c let output = Command::new("clang") .env_clear() + .env("PATH", &env_path) .args(&[ "-c", c_host_src.to_str().unwrap(), @@ -75,6 +78,7 @@ pub fn rebuild_host(host_input_path: &Path) { let output = Command::new("ld") .env_clear() + .env("PATH", &env_path) .args(&[ "-r", "-L", @@ -103,6 +107,7 @@ pub fn rebuild_host(host_input_path: &Path) { let output = Command::new("ld") .env_clear() + .env("PATH", &env_path) .args(&[ "-r", c_host_dest.to_str().unwrap(), @@ -145,18 +150,29 @@ fn link_linux( input_paths: &[&str], link_type: LinkType, ) -> io::Result<(Child, PathBuf)> { - let libcrt_path = if Path::new("/usr/lib/x86_64-linux-gnu").exists() { - Path::new("/usr/lib/x86_64-linux-gnu") + let arch = arch_str(target); + let usr_lib_path = Path::new("/usr/lib").to_path_buf(); + let usr_lib_gnu_path = usr_lib_path.join(format!("{}-linux-gnu", arch)); + let lib_gnu_path = Path::new("/lib/").join(format!("{}-linux-gnu", arch)); + + let libcrt_path = if usr_lib_gnu_path.exists() { + &usr_lib_gnu_path } else { - Path::new("/usr/lib") + &usr_lib_path }; - let libgcc_path = if Path::new("/lib/x86_64-linux-gnu/libgcc_s.so.1").exists() { - Path::new("/lib/x86_64-linux-gnu/libgcc_s.so.1") - } else if Path::new("/usr/lib/x86_64-linux-gnu/libgcc_s.so.1").exists() { - Path::new("/usr/lib/x86_64-linux-gnu/libgcc_s.so.1") + let libgcc_name = "libgcc_s.so.1"; + let libgcc_path = if lib_gnu_path.join(libgcc_name).exists() { + lib_gnu_path.join(libgcc_name) + } else if usr_lib_gnu_path.join(libgcc_name).exists() { + usr_lib_gnu_path.join(libgcc_name) } else { - Path::new("/usr/lib/libgcc_s.so.1") + usr_lib_path.join(libgcc_name) + }; + let ld_linux = match target.architecture { + Architecture::X86_64 => "/lib64/ld-linux-x86-64.so.2", + Architecture::Aarch64(_) => "/lib/ld-linux-aarch64.so.1", + _ => panic!("TODO gracefully handle unsupported linux architecture: {:?}", target.architecture), }; let mut soname; @@ -194,12 +210,16 @@ fn link_linux( } }; + let env_path = env::var("PATH").unwrap_or_else(|_| "".to_string()); // NOTE: order of arguments to `ld` matters here! // The `-l` flags should go after the `.o` arguments Ok(( Command::new("ld") // Don't allow LD_ env vars to affect this .env_clear() + .env("PATH", &env_path) + // Keep NIX_ env vars + .envs(env::vars().filter(|&(ref k, _)| k.starts_with("NIX_")).collect::>()) .args(&[ "-arch", arch_str(target), @@ -207,7 +227,7 @@ fn link_linux( libcrt_path.join("crtn.o").to_str().unwrap(), ]) .args(&base_args) - .args(&["-dynamic-linker", "/lib64/ld-linux-x86-64.so.2"]) + .args(&["-dynamic-linker", ld_linux]) .args(input_paths) .args(&[ // Libraries - see https://github.com/rtfeldman/roc/pull/554#discussion_r496365925 @@ -220,6 +240,7 @@ fn link_linux( "-lutil", "-lc_nonshared", "-lc++", + "-lc++abi", "-lunwind", libgcc_path.to_str().unwrap(), // Output diff --git a/compiler/build/src/target.rs b/compiler/build/src/target.rs index ca7b87d704..6ebe0d392d 100644 --- a/compiler/build/src/target.rs +++ b/compiler/build/src/target.rs @@ -14,6 +14,11 @@ pub fn target_triple_str(target: &Triple) -> &'static str { operating_system: OperatingSystem::Linux, .. } => "x86_64-unknown-linux-gnu", + Triple { + architecture: Architecture::Aarch64(_), + operating_system: OperatingSystem::Linux, + .. + } => "aarch64-unknown-linux-gnu", Triple { architecture: Architecture::X86_64, operating_system: OperatingSystem::Darwin, @@ -36,6 +41,10 @@ pub fn arch_str(target: &Triple) -> &'static str { "x86-64" } + Architecture::Aarch64(_) => { + Target::initialize_aarch64(&InitializationConfig::default()); + "aarch64" + } Architecture::Arm(_) if cfg!(feature = "target-arm") => { // NOTE: why not enable arm and wasm by default? // @@ -67,7 +76,7 @@ pub fn target_machine( Target::from_name(arch).unwrap().create_target_machine( &TargetTriple::create(target_triple_str(target)), - arch, + "generic", "", // TODO: this probably should be TargetMachine::get_host_cpu_features() to enable all features. opt, reloc, diff --git a/shell.nix b/shell.nix index efda0cc7ed..c514d37c5e 100644 --- a/shell.nix +++ b/shell.nix @@ -66,6 +66,9 @@ let libffi libxml2 zlib + llvmPkgs.libcxx + llvmPkgs.libcxxabi + libunwind # faster builds - see https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#use-lld-for-the-linker llvmPkgs.lld # dev tools @@ -79,7 +82,7 @@ in mkShell { LLVM_SYS_100_PREFIX = "${llvmPkgs.llvm}"; APPEND_LIBRARY_PATH = stdenv.lib.makeLibraryPath - ([ pkgconfig llvmPkgs.libcxx llvmPkgs.libcxxabi libunwind ] ++ linux-only); + ([ pkg-config llvmPkgs.libcxx llvmPkgs.libcxxabi libunwind ] ++ linux-only); # Aliases don't work cross shell, so we do this shellHook = '' From 8a5d45a8a4fd5d06f8a4db139637c8ace42080ba Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 25 Nov 2020 21:32:40 -0800 Subject: [PATCH 06/10] Autoformat link.rs --- compiler/build/src/link.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/compiler/build/src/link.rs b/compiler/build/src/link.rs index 915c76d1ce..86dff79bb0 100644 --- a/compiler/build/src/link.rs +++ b/compiler/build/src/link.rs @@ -4,9 +4,9 @@ use inkwell::module::Module; use inkwell::targets::{CodeModel, FileType, RelocMode}; use libloading::{Error, Library}; use roc_gen::llvm::build::OptLevel; -use std::io; -use std::env; use std::collections::HashMap; +use std::env; +use std::io; use std::path::{Path, PathBuf}; use std::process::{Child, Command, Output}; use target_lexicon::{Architecture, OperatingSystem, Triple}; @@ -171,8 +171,11 @@ fn link_linux( }; let ld_linux = match target.architecture { Architecture::X86_64 => "/lib64/ld-linux-x86-64.so.2", - Architecture::Aarch64(_) => "/lib/ld-linux-aarch64.so.1", - _ => panic!("TODO gracefully handle unsupported linux architecture: {:?}", target.architecture), + Architecture::Aarch64(_) => "/lib/ld-linux-aarch64.so.1", + _ => panic!( + "TODO gracefully handle unsupported linux architecture: {:?}", + target.architecture + ), }; let mut soname; @@ -219,7 +222,11 @@ fn link_linux( .env_clear() .env("PATH", &env_path) // Keep NIX_ env vars - .envs(env::vars().filter(|&(ref k, _)| k.starts_with("NIX_")).collect::>()) + .envs( + env::vars() + .filter(|&(ref k, _)| k.starts_with("NIX_")) + .collect::>(), + ) .args(&[ "-arch", arch_str(target), From 4d0363456aedf08aef1ff04b593e051d4b328944 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 25 Nov 2020 22:45:32 -0800 Subject: [PATCH 07/10] Pick correct version of architecture name --- compiler/build/src/link.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/build/src/link.rs b/compiler/build/src/link.rs index 86dff79bb0..3bf07c1027 100644 --- a/compiler/build/src/link.rs +++ b/compiler/build/src/link.rs @@ -150,10 +150,9 @@ fn link_linux( input_paths: &[&str], link_type: LinkType, ) -> io::Result<(Child, PathBuf)> { - let arch = arch_str(target); let usr_lib_path = Path::new("/usr/lib").to_path_buf(); - let usr_lib_gnu_path = usr_lib_path.join(format!("{}-linux-gnu", arch)); - let lib_gnu_path = Path::new("/lib/").join(format!("{}-linux-gnu", arch)); + let usr_lib_gnu_path = usr_lib_path.join(format!("{}-linux-gnu", target.architecture)); + let lib_gnu_path = Path::new("/lib/").join(format!("{}-linux-gnu", target.architecture)); let libcrt_path = if usr_lib_gnu_path.exists() { &usr_lib_gnu_path From b3129482b5cdd90fc3aa0816ed75c029a7e45ef0 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Thu, 26 Nov 2020 10:18:56 +0100 Subject: [PATCH 08/10] added unit test generation plugin to editor-ideas --- editor/editor-ideas.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/editor/editor-ideas.md b/editor/editor-ideas.md index 135ff5b940..52a7d76eb9 100644 --- a/editor/editor-ideas.md +++ b/editor/editor-ideas.md @@ -70,6 +70,8 @@ Thoughts and ideas possibly taken from above inspirations or separate. * Makes sense for unit tests, keeps the test close to the source * Doesn't necessarily make sense for integration or e2e testing * Maybe easier to manually trigger a test related to exactly what code you're writing +* Ability to generate unit tests for a selected function in context menu + * A table should appear to enter input and expected output pairs quickly * "Error mode" where the editor jumps you to the next error * Similar in theory to diff tools that jump you to the next merge conflict * dependency recommendation From 1022b4ef42035bd07fbd32b6a5678d3b6feb34c9 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 26 Nov 2020 20:41:22 +0100 Subject: [PATCH 09/10] fix the effect Main.roc file and lib.rs file --- compiler/gen/src/llvm/build.rs | 8 +++++--- examples/effect/Main.roc | 24 ++---------------------- examples/effect/RBTree.roc | 2 +- examples/effect/platform/src/lib.rs | 12 ++++++------ 4 files changed, 14 insertions(+), 32 deletions(-) diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index defff8359e..59c1dd428b 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -1806,7 +1806,8 @@ fn expose_function_to_host_help<'a, 'ctx, 'env>( // STEP 3: build a {} -> u64 function that gives the size of the return type let size_function_type = env.context.i64_type().fn_type(&[], false); - let size_function_name: String = format!("{}_size", roc_function.get_name().to_str().unwrap()); + let size_function_name: String = + format!("roc_{}_size", roc_function.get_name().to_str().unwrap()); let size_function = env.module.add_function( size_function_name.as_str(), @@ -2119,8 +2120,9 @@ pub fn build_closure_caller<'a, 'ctx, 'env>( // STEP 1: build function header + // e.g. `roc__main_1_Fx_caller` let function_name = format!( - "{}_{}_caller", + "roc_{}_{}_caller", def_name, alias_symbol.ident_string(&env.interns) ); @@ -2200,7 +2202,7 @@ pub fn build_closure_caller<'a, 'ctx, 'env>( // STEP 3: build a {} -> u64 function that gives the size of the return type let size_function_type = env.context.i64_type().fn_type(&[], false); let size_function_name: String = format!( - "{}_{}_size", + "roc_{}_{}_size", def_name, alias_symbol.ident_string(&env.interns) ); diff --git a/examples/effect/Main.roc b/examples/effect/Main.roc index c13c2afb63..2e37d4be5b 100644 --- a/examples/effect/Main.roc +++ b/examples/effect/Main.roc @@ -1,31 +1,11 @@ -app "effect-example" provides [ main ] imports [ Effect, RBTree ] - -toAndFro : Int -toAndFro = - empty : RBTree.Dict Int {} - empty = RBTree.empty - - empty - |> (\d -> RBTree.insert 1 {} d) - |> RBTree.toList - |> List.len - - - +app "effect-example" imports [ Effect, RBTree ] provides [ main ] to "./platform" main : Effect.Effect {} as Fx main = - # if RBTree.isEmpty empty then - if toAndFro == 2 then + if RBTree.isEmpty (RBTree.insert 1 2 Empty) then Effect.putLine "Yay" |> Effect.after (\{} -> Effect.getLine) |> Effect.after (\line -> Effect.putLine line) else Effect.putLine "Nay" - -# Effect.always "Write a thing" -# |> Effect.map (\line -> Str.concat line "!") -# |> Effect.after (\line -> Effect.putLine line) -# |> Effect.after (\{} -> Effect.getLine) -# |> Effect.after (\line -> Effect.putLine line) diff --git a/examples/effect/RBTree.roc b/examples/effect/RBTree.roc index 6278d3eafd..1c792e5125 100644 --- a/examples/effect/RBTree.roc +++ b/examples/effect/RBTree.roc @@ -1,4 +1,4 @@ -interface RBTree exposes [ Dict, empty, size, singleton, isEmpty, insert, remove, update, fromList, toList ] imports [] +interface RBTree exposes [ Dict, empty, size, singleton, isEmpty, insert, remove, update, fromList, toList, balance ] imports [] # The color of a node. Leaves are considered Black. NodeColor : [ Red, Black ] diff --git a/examples/effect/platform/src/lib.rs b/examples/effect/platform/src/lib.rs index 1e8554f4c3..ba517b18c8 100644 --- a/examples/effect/platform/src/lib.rs +++ b/examples/effect/platform/src/lib.rs @@ -7,16 +7,16 @@ use std::alloc::Layout; use std::time::SystemTime; extern "C" { - #[link_name = "Main_main_1_exposed"] + #[link_name = "roc__main_1_exposed"] fn roc_main(output: *mut u8) -> (); - #[link_name = "Main_main_1_size"] + #[link_name = "roc__main_1_size"] fn roc_main_size() -> i64; - #[link_name = "Main_main_1_Fx_caller"] + #[link_name = "roc__main_1_Fx_caller"] fn call_Fx(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> (); - #[link_name = "Main_main_1_Fx_size"] + #[link_name = "roc__main_1_Fx_size"] fn size_Fx() -> i64; } @@ -60,10 +60,10 @@ unsafe fn call_the_closure(function_pointer: *const u8, closure_data_ptr: *const buffer as *mut u8, ); - let output = &*(buffer as *mut RocCallResult); + let output = &*(buffer as *mut RocCallResult<()>); match output.into() { - Ok(v) => v, + Ok(_) => 0, Err(e) => panic!("failed with {}", e), } }) From 5569e328f6dcde1daf176f55193db93af062a65e Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 26 Nov 2020 21:31:52 +0100 Subject: [PATCH 10/10] add --debug mode that annotates LLVM IR with debug info --- BUILDING_FROM_SOURCE.md | 2 ++ cli/src/build.rs | 2 ++ cli/src/lib.rs | 26 ++++++++++++++++++++++++-- compiler/build/src/program.rs | 26 ++++++++++++++------------ examples/effect/Main.roc | 21 ++++++++++++++------- examples/effect/platform/src/lib.rs | 7 ++++++- 6 files changed, 62 insertions(+), 22 deletions(-) diff --git a/BUILDING_FROM_SOURCE.md b/BUILDING_FROM_SOURCE.md index 688188c2f5..fef2f2a147 100644 --- a/BUILDING_FROM_SOURCE.md +++ b/BUILDING_FROM_SOURCE.md @@ -16,6 +16,8 @@ To run the test suite (via `cargo test`), you additionally need to install: * [`valgrind`](https://www.valgrind.org/) (needs special treatment to [install on macOS](https://stackoverflow.com/a/61359781) Alternatively, you can use `cargo test --no-fail-fast` or `cargo test -p specific_tests` to skip over the valgrind failures & tests. +For debugging LLVM IR, we use [DebugIR](https://github.com/vaivaswatha/debugir). This dependency is only required to build with the `--debug` flag, and for normal developtment you should be fine without it. + ### libunwind & libc++-dev MacOS systems should already have `libunwind`, but other systems will need to install it (On Ubuntu, this can be donw with `sudo apt-get install libunwind-dev`). diff --git a/cli/src/build.rs b/cli/src/build.rs index c4e2dc02ed..386b4324d5 100644 --- a/cli/src/build.rs +++ b/cli/src/build.rs @@ -24,6 +24,7 @@ pub fn build_file( src_dir: PathBuf, roc_file_path: PathBuf, opt_level: OptLevel, + emit_debug_info: bool, link_type: LinkType, ) -> Result { let compilation_start = SystemTime::now(); @@ -95,6 +96,7 @@ pub fn build_file( Triple::host(), &app_o_file, opt_level, + emit_debug_info, ); println!("\nSuccess! 🎉\n\n\t➡ {}\n", app_o_file.display()); diff --git a/cli/src/lib.rs b/cli/src/lib.rs index e21605045e..fee018e1ba 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -14,6 +14,7 @@ use target_lexicon::Triple; pub mod build; pub mod repl; +pub static FLAG_DEBUG: &str = "debug"; pub static FLAG_OPTIMIZE: &str = "optimize"; pub static FLAG_ROC_FILE: &str = "ROC_FILE"; pub static DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES"; @@ -34,6 +35,12 @@ pub fn build_app<'a>() -> App<'a> { .help("Optimize the compiled program to run faster. (Optimization takes time to complete.)") .required(false), ) + .arg( + Arg::with_name(FLAG_DEBUG) + .long(FLAG_DEBUG) + .help("Store LLVM debug information in the generated program") + .required(false), + ) ) .subcommand(App::new("run") .about("Build and run a program") @@ -48,6 +55,12 @@ pub fn build_app<'a>() -> App<'a> { .help("Optimize the compiled program to run faster. (Optimization takes time to complete.)") .required(false), ) + .arg( + Arg::with_name(FLAG_DEBUG) + .long(FLAG_DEBUG) + .help("Store LLVM debug information in the generated program") + .required(false), + ) ) .subcommand(App::new("repl") .about("Launch the interactive Read Eval Print Loop (REPL)") @@ -70,6 +83,8 @@ pub fn build(target: &Triple, matches: &ArgMatches, run_after_build: bool) -> io } else { OptLevel::Normal }; + let emit_debug_info = matches.is_present(FLAG_DEBUG); + let path = Path::new(filename).canonicalize().unwrap(); let src_dir = path.parent().unwrap().canonicalize().unwrap(); @@ -92,8 +107,15 @@ pub fn build(target: &Triple, matches: &ArgMatches, run_after_build: bool) -> io } }); - let binary_path = build::build_file(target, src_dir, path, opt_level, LinkType::Executable) - .expect("TODO gracefully handle build_file failing"); + let binary_path = build::build_file( + target, + src_dir, + path, + opt_level, + emit_debug_info, + LinkType::Executable, + ) + .expect("TODO gracefully handle build_file failing"); if run_after_build { // Run the compiled app diff --git a/compiler/build/src/program.rs b/compiler/build/src/program.rs index f053d7d2e2..48d8b01197 100644 --- a/compiler/build/src/program.rs +++ b/compiler/build/src/program.rs @@ -20,6 +20,7 @@ pub fn gen_from_mono_module( target: Triple, app_o_file: &Path, opt_level: OptLevel, + emit_debug_info: bool, ) { use roc_reporting::report::{ can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE, @@ -161,7 +162,9 @@ pub fn gen_from_mono_module( // annotate the LLVM IR output with debug info // so errors are reported with the line number of the LLVM source - if true { + if emit_debug_info { + module.strip_debug_info(); + let mut app_ll_file = std::path::PathBuf::from(app_o_file); app_ll_file.set_extension("ll"); @@ -217,19 +220,18 @@ pub fn gen_from_mono_module( ]) .output() .unwrap(); + } else { + // Emit the .o file - return; + let reloc = RelocMode::Default; + let model = CodeModel::Default; + let target_machine = + target::target_machine(&target, opt_level.into(), reloc, model).unwrap(); + + target_machine + .write_to_file(&env.module, FileType::Object, &app_o_file) + .expect("Writing .o file failed"); } - - // Emit the .o file - - let reloc = RelocMode::Default; - let model = CodeModel::Default; - let target_machine = target::target_machine(&target, opt_level.into(), reloc, model).unwrap(); - - target_machine - .write_to_file(&env.module, FileType::Object, &app_o_file) - .expect("Writing .o file failed"); } pub struct FunctionIterator<'ctx> { diff --git a/examples/effect/Main.roc b/examples/effect/Main.roc index 2e37d4be5b..2bbefaca8d 100644 --- a/examples/effect/Main.roc +++ b/examples/effect/Main.roc @@ -1,11 +1,18 @@ -app "effect-example" imports [ Effect, RBTree ] provides [ main ] to "./platform" +app "effect-example" imports [ Effect ] provides [ main ] to "./platform" + main : Effect.Effect {} as Fx main = - if RBTree.isEmpty (RBTree.insert 1 2 Empty) then - Effect.putLine "Yay" - |> Effect.after (\{} -> Effect.getLine) - |> Effect.after (\line -> Effect.putLine line) - else - Effect.putLine "Nay" + when if 1 == 1 then True 3 else False 3.14 is + True 3 -> Effect.putLine "Yay" + _ -> Effect.putLine "Yay" +# main : Effect.Effect {} as Fx +# main = +# if RBTree.isEmpty (RBTree.insert 1 2 Empty) then +# Effect.putLine "Yay" +# |> Effect.after (\{} -> Effect.getLine) +# |> Effect.after (\line -> Effect.putLine line) +# else +# Effect.putLine "Nay" +# diff --git a/examples/effect/platform/src/lib.rs b/examples/effect/platform/src/lib.rs index ba517b18c8..216595ca4c 100644 --- a/examples/effect/platform/src/lib.rs +++ b/examples/effect/platform/src/lib.rs @@ -95,7 +95,12 @@ pub fn rust_main() -> isize { let closure_data_ptr = buffer.offset(16); - call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8) + let result = + call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8); + + std::alloc::dealloc(buffer, layout); + + result } Err(msg) => { std::alloc::dealloc(buffer, layout);