From 4f97ab29656d2d0f184a66d893971224b93dbf76 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 24 Jun 2023 10:22:23 -0700 Subject: [PATCH 01/17] remove old compiler-rt workarounds for macos --- .../fixtures/multi-dep-str/platform/host.zig | 14 --- .../multi-dep-thunk/platform/host.zig | 14 --- .../tests/fixtures/packages/platform/host.zig | 14 --- .../algorithms/fibonacci-platform/host.zig | 14 --- .../algorithms/quicksort-platform/host.zig | 14 --- .../benchmarks/platform/host.zig | 14 --- .../expects/zig-platform/host.zig | 14 --- crates/compiler/build/src/link.rs | 95 +------------------ crates/compiler/build/src/target.rs | 4 +- .../builtins/bitcode/src/compiler_rt.zig | 45 +++++++++ crates/linker/src/pe.rs | 1 + crates/valgrind/zig-platform/host.zig | 14 --- examples/cli/effects-platform/host.zig | 14 --- examples/cli/tui-platform/host.zig | 14 --- .../platform-switching/zig-platform/host.zig | 14 --- 15 files changed, 49 insertions(+), 250 deletions(-) diff --git a/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig b/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig index 4248e171a8c..2a4fb112849 100644 --- a/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig +++ b/crates/cli/tests/fixtures/multi-dep-str/platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig b/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig index 18bb5d00749..a7cff59a0b1 100644 --- a/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig +++ b/crates/cli/tests/fixtures/multi-dep-thunk/platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli/tests/fixtures/packages/platform/host.zig b/crates/cli/tests/fixtures/packages/platform/host.zig index 4248e171a8c..2a4fb112849 100644 --- a/crates/cli/tests/fixtures/packages/platform/host.zig +++ b/crates/cli/tests/fixtures/packages/platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig b/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig index c27916c26f2..6e2d2e6b393 100644 --- a/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig +++ b/crates/cli_testing_examples/algorithms/fibonacci-platform/host.zig @@ -5,20 +5,6 @@ const expectEqual = testing.expectEqual; const expect = testing.expect; const maxInt = std.math.maxInt; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig b/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig index c402674a231..1ae4b1cc0e6 100644 --- a/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig +++ b/crates/cli_testing_examples/algorithms/quicksort-platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli_testing_examples/benchmarks/platform/host.zig b/crates/cli_testing_examples/benchmarks/platform/host.zig index 85f343c061b..7902302f451 100644 --- a/crates/cli_testing_examples/benchmarks/platform/host.zig +++ b/crates/cli_testing_examples/benchmarks/platform/host.zig @@ -7,20 +7,6 @@ const expectEqual = testing.expectEqual; const expect = testing.expect; const maxInt = std.math.maxInt; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/crates/cli_testing_examples/expects/zig-platform/host.zig b/crates/cli_testing_examples/expects/zig-platform/host.zig index 20c09320d90..33c1995d8df 100644 --- a/crates/cli_testing_examples/expects/zig-platform/host.zig +++ b/crates/cli_testing_examples/expects/zig-platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const Align = 2 * @alignOf(usize); extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque; extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque; diff --git a/crates/compiler/build/src/link.rs b/crates/compiler/build/src/link.rs index 2c69ced034f..a48e57c1732 100644 --- a/crates/compiler/build/src/link.rs +++ b/crates/compiler/build/src/link.rs @@ -126,7 +126,7 @@ fn find_wasi_libc_path() -> PathBuf { internal_error!("cannot find `wasi-libc.a`") } -#[cfg(all(unix, not(target_os = "macos")))] +#[cfg(unix)] #[allow(clippy::too_many_arguments)] pub fn build_zig_host_native( env_path: &str, @@ -257,99 +257,6 @@ pub fn build_zig_host_native( zig_cmd } -#[cfg(target_os = "macos")] -#[allow(clippy::too_many_arguments)] -pub fn build_zig_host_native( - env_path: &str, - env_home: &str, - emit_bin: &str, - zig_host_src: &str, - _target: &str, - opt_level: OptLevel, - shared_lib_path: Option<&Path>, - builtins_host_path: &Path, - // For compatibility with the non-macOS def above. Keep these in sync. -) -> Command { - use serde_json::Value; - - // Run `zig env` to find the location of zig's std/ directory - let zig_env_output = zig().args(["env"]).output().unwrap(); - - let zig_env_json = if zig_env_output.status.success() { - std::str::from_utf8(&zig_env_output.stdout).unwrap_or_else(|utf8_err| { - internal_error!( - "`zig env` failed; its stderr output was invalid utf8 ({:?})", - utf8_err - ); - }) - } else { - match std::str::from_utf8(&zig_env_output.stderr) { - Ok(stderr) => internal_error!("`zig env` failed - stderr output was: {:?}", stderr), - Err(utf8_err) => internal_error!( - "`zig env` failed; its stderr output was invalid utf8 ({:?})", - utf8_err - ), - } - }; - - let mut zig_compiler_rt_path = match serde_json::from_str(zig_env_json) { - Ok(Value::Object(map)) => match map.get("std_dir") { - Some(Value::String(std_dir)) => PathBuf::from(Path::new(std_dir)), - _ => { - internal_error!("Expected JSON containing a `std_dir` String field from `zig env`, but got: {:?}", zig_env_json); - } - }, - _ => { - internal_error!( - "Expected JSON containing a `std_dir` field from `zig env`, but got: {:?}", - zig_env_json - ); - } - }; - - zig_compiler_rt_path.push("special"); - zig_compiler_rt_path.push("compiler_rt.zig"); - - let mut zig_cmd = zig(); - zig_cmd - .env_clear() - .env("PATH", env_path) - .env("HOME", env_home); - if let Some(shared_lib_path) = shared_lib_path { - zig_cmd.args([ - "build-exe", - "-fPIE", - shared_lib_path.to_str().unwrap(), - builtins_host_path.to_str().unwrap(), - ]); - } else { - zig_cmd.args(["build-obj"]); - } - zig_cmd.args([ - zig_host_src, - &format!("-femit-bin={}", emit_bin), - "--pkg-begin", - "glue", - find_zig_glue_path().to_str().unwrap(), - "--pkg-end", - // include the zig runtime - "--pkg-begin", - "compiler_rt", - zig_compiler_rt_path.to_str().unwrap(), - "--pkg-end", - // include libc - "--library", - "c", - ]); - if matches!(opt_level, OptLevel::Optimize) { - zig_cmd.args(["-O", "ReleaseSafe"]); - } else if matches!(opt_level, OptLevel::Size) { - zig_cmd.args(["-O", "ReleaseSmall"]); - } - - zig_cmd -} - pub fn build_zig_host_wasm32( env_path: &str, env_home: &str, diff --git a/crates/compiler/build/src/target.rs b/crates/compiler/build/src/target.rs index 6abe50cb349..4acd2ce8c4a 100644 --- a/crates/compiler/build/src/target.rs +++ b/crates/compiler/build/src/target.rs @@ -87,12 +87,12 @@ pub fn target_zig_str(target: &Triple) -> &'static str { architecture: Architecture::X86_64, operating_system: OperatingSystem::Darwin, .. - } => "x86_64-apple-darwin", + } => "x86_64-macos-gnu", Triple { architecture: Architecture::Aarch64(_), operating_system: OperatingSystem::Darwin, .. - } => "aarch64-apple-darwin", + } => "aarch64-macos-gnu", _ => internal_error!("TODO gracefully handle unsupported target: {:?}", target), } } diff --git a/crates/compiler/builtins/bitcode/src/compiler_rt.zig b/crates/compiler/builtins/bitcode/src/compiler_rt.zig index b00961fa218..e4bc02da9bf 100644 --- a/crates/compiler/builtins/bitcode/src/compiler_rt.zig +++ b/crates/compiler/builtins/bitcode/src/compiler_rt.zig @@ -18,6 +18,7 @@ const v2u64 = @Vector(2, u64); // Export it as weak incase it is already linked in by something else. comptime { @export(__muloti4, .{ .name = "__muloti4", .linkage = .Weak }); + @export(__lshrti3, .{ .name = "__lshrti3", .linkage = .Weak }); if (want_windows_v2u64_abi) { @export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = .Weak }); @export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = .Weak }); @@ -440,3 +441,47 @@ pub inline fn floatFractionalBits(comptime T: type) comptime_int { else => @compileError("unknown floating point type " ++ @typeName(T)), }; } + +pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 { + return lshrXi3(i128, a, b); +} + +// Logical shift right: shift in 0 from left to right +// Precondition: 0 <= b < T.bit_count +inline fn lshrXi3(comptime T: type, a: T, b: i32) T { + const word_t = HalveInt(T, false); + const S = std.math.Log2Int(word_t.HalfT); + + const input = word_t{ .all = a }; + var output: word_t = undefined; + + if (b >= word_t.bits) { + output.s.high = 0; + output.s.low = input.s.high >> @intCast(S, b - word_t.bits); + } else if (b == 0) { + return a; + } else { + output.s.high = input.s.high >> @intCast(S, b); + output.s.low = input.s.high << @intCast(S, word_t.bits - b); + output.s.low |= input.s.low >> @intCast(S, b); + } + + return output.all; +} + +/// Allows to access underlying bits as two equally sized lower and higher +/// signed or unsigned integers. +fn HalveInt(comptime T: type, comptime signed_half: bool) type { + return extern union { + pub const bits = @divExact(@typeInfo(T).Int.bits, 2); + pub const HalfTU = std.meta.Int(.unsigned, bits); + pub const HalfTS = std.meta.Int(.signed, bits); + pub const HalfT = if (signed_half) HalfTS else HalfTU; + + all: T, + s: if (native_endian == .Little) + extern struct { low: HalfT, high: HalfT } + else + extern struct { high: HalfT, low: HalfT }, + }; +} diff --git a/crates/linker/src/pe.rs b/crates/linker/src/pe.rs index 0332aaf7144..8848172e1d9 100644 --- a/crates/linker/src/pe.rs +++ b/crates/linker/src/pe.rs @@ -444,6 +444,7 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b "__fixsfti", "__fixunsdfti", "__fixunssfti", + "__lshrti3", "memcpy_decision", ] .contains(&name.as_str()); diff --git a/crates/valgrind/zig-platform/host.zig b/crates/valgrind/zig-platform/host.zig index 6025d07d157..8ba1179e4ff 100644 --- a/crates/valgrind/zig-platform/host.zig +++ b/crates/valgrind/zig-platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const Align = 2 * @alignOf(usize); extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque; extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque; diff --git a/examples/cli/effects-platform/host.zig b/examples/cli/effects-platform/host.zig index a0780797b31..e0e100278ff 100644 --- a/examples/cli/effects-platform/host.zig +++ b/examples/cli/effects-platform/host.zig @@ -7,20 +7,6 @@ const expectEqual = testing.expectEqual; const expect = testing.expect; const maxInt = std.math.maxInt; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/examples/cli/tui-platform/host.zig b/examples/cli/tui-platform/host.zig index 0eb4717d8c7..6e1c1869e93 100644 --- a/examples/cli/tui-platform/host.zig +++ b/examples/cli/tui-platform/host.zig @@ -7,20 +7,6 @@ const expectEqual = testing.expectEqual; const expect = testing.expect; const maxInt = std.math.maxInt; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const mem = std.mem; const Allocator = mem.Allocator; diff --git a/examples/platform-switching/zig-platform/host.zig b/examples/platform-switching/zig-platform/host.zig index 6025d07d157..8ba1179e4ff 100644 --- a/examples/platform-switching/zig-platform/host.zig +++ b/examples/platform-switching/zig-platform/host.zig @@ -6,20 +6,6 @@ const testing = std.testing; const expectEqual = testing.expectEqual; const expect = testing.expect; -comptime { - // This is a workaround for https://github.com/ziglang/zig/issues/8218 - // which is only necessary on macOS. - // - // Once that issue is fixed, we can undo the changes in - // 177cf12e0555147faa4d436e52fc15175c2c4ff0 and go back to passing - // -fcompiler-rt in link.rs instead of doing this. Note that this - // workaround is present in many host.zig files, so make sure to undo - // it everywhere! - if (builtin.os.tag == .macos) { - _ = @import("compiler_rt"); - } -} - const Align = 2 * @alignOf(usize); extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque; extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque; From 416d8e21b07a060e869f66fb72ba9d746d259a5e Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 26 Jun 2023 09:45:33 -0400 Subject: [PATCH 02/17] Have RustGlue bundle roc_std --- crates/glue/src/RustGlue.roc | 33 ++++++++++++++++++++++++++++++--- crates/glue/static/Cargo.toml | 8 ++++++++ crates/roc_std/Cargo.toml | 3 +-- 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 crates/glue/static/Cargo.toml diff --git a/crates/glue/src/RustGlue.roc b/crates/glue/src/RustGlue.roc index 5e3fcc71089..2e6dc6c91dd 100644 --- a/crates/glue/src/RustGlue.roc +++ b/crates/glue/src/RustGlue.roc @@ -1,6 +1,17 @@ app "rust-glue" packages { pf: "../platform/main.roc" } - imports [pf.Types.{ Types }, pf.Shape.{ Shape, RocFn }, pf.File.{ File }, pf.TypeId.{ TypeId }] + imports [ + pf.Types.{ Types }, pf.Shape.{ Shape, RocFn }, pf.File.{ File }, pf.TypeId.{ TypeId }, + "../static/Cargo.toml" as rocAppCargoToml : Str, + "../../roc_std/Cargo.toml" as rocStdCargoToml : Str, + "../../roc_std/src/lib.rs" as rocStdLib : Str, + "../../roc_std/src/roc_box.rs" as rocStdBox : Str, + "../../roc_std/src/roc_list.rs" as rocStdList : Str, + "../../roc_std/src/roc_dict.rs" as rocStdDict : Str, + "../../roc_std/src/roc_set.rs" as rocStdSet : Str, + "../../roc_std/src/roc_str.rs" as rocStdStr : Str, + "../../roc_std/src/storage.rs" as rocStdStorage : Str, + ] provides [makeGlue] to pf makeGlue : List Types -> Result (List File) Str @@ -22,9 +33,25 @@ makeGlue = \typesByArch -> typesByArch |> List.map convertTypesToFile - |> List.append { name: "mod.rs", content: modFileContent } + |> List.append { name: "roc_app/src/lib.rs", content: modFileContent } + |> List.concat staticFiles |> Ok +## These are always included, and don't depend on the specifices of the app. +staticFiles : List File +staticFiles = + [ + { name: "roc_app/Cargo.toml", content: rocAppCargoToml }, + { name: "roc_std/Cargo.toml", content: rocStdCargoToml }, + { name: "roc_std/src/lib.rs", content: rocStdLib }, + { name: "roc_std/src/roc_box.rs", content: rocStdBox }, + { name: "roc_std/src/roc_list.rs", content: rocStdList }, + { name: "roc_std/src/roc_dict.rs", content: rocStdDict }, + { name: "roc_std/src/roc_set.rs", content: rocStdSet }, + { name: "roc_std/src/roc_str.rs", content: rocStdStr }, + { name: "roc_std/src/storage.rs", content: rocStdStorage }, + ] + convertTypesToFile : Types -> File convertTypesToFile = \types -> content = @@ -94,7 +121,7 @@ convertTypesToFile = \types -> archStr = archName arch { - name: "\(archStr).rs", + name: "roc_app/src/\(archStr).rs", content: content |> generateEntryPoints types, } diff --git a/crates/glue/static/Cargo.toml b/crates/glue/static/Cargo.toml new file mode 100644 index 00000000000..ff900959690 --- /dev/null +++ b/crates/glue/static/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "roc_app" +description = "This was generated by `roc glue`. It provides glue between a specific Roc platform and a Rust host." +version = "1.0.0" +edition = "2021" + +[dependencies] +roc_std = { path = "../roc_std" } diff --git a/crates/roc_std/Cargo.toml b/crates/roc_std/Cargo.toml index 8c7b41cb9df..8318bdad415 100644 --- a/crates/roc_std/Cargo.toml +++ b/crates/roc_std/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "roc_std" description = "Rust representations of Roc data structures" -readme = "README.md" authors = ["The Roc Contributors"] edition = "2021" @@ -26,4 +25,4 @@ serde = ["dep:serde"] std = [] [package.metadata.cargo-udeps.ignore] -development = ["quickcheck_macros", "serde_json"] \ No newline at end of file +development = ["quickcheck_macros", "serde_json"] From ff4d47f2934ef2c6ae90522735b94f57ea0d4f7e Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 26 Jun 2023 14:49:46 -0400 Subject: [PATCH 03/17] Fix typo --- crates/glue/src/RustGlue.roc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/glue/src/RustGlue.roc b/crates/glue/src/RustGlue.roc index 2e6dc6c91dd..0a862eb7ab8 100644 --- a/crates/glue/src/RustGlue.roc +++ b/crates/glue/src/RustGlue.roc @@ -37,7 +37,7 @@ makeGlue = \typesByArch -> |> List.concat staticFiles |> Ok -## These are always included, and don't depend on the specifices of the app. +## These are always included, and don't depend on the specifics of the app. staticFiles : List File staticFiles = [ From abaed603261673b820e62df4bc83f83c6de71ff9 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 26 Jun 2023 15:54:26 -0400 Subject: [PATCH 04/17] Improve debug_assert message --- crates/glue/src/types.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index d5b6b79fd84..1f7fde4239b 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -2229,7 +2229,13 @@ fn single_tag_payload_fields<'a, 'b>( env.glue_procs_by_layout.get(&layout).is_some(), env.layout_cache .interner - .has_varying_stack_size(in_layout, env.arena) + .has_varying_stack_size(in_layout, env.arena), + "glue_procs_by_layout for {:?} was {:?}, but the layout cache said its has_varying_stack_size was {}", + &layout, + env.glue_procs_by_layout.get(&layout), + env.layout_cache + .interner + .has_varying_stack_size(in_layout, env.arena) ); let (tag_name, payload_vars) = single_tag_payload(union_tags, subs); From 28591da79ad6618d02e36bcbf03268d39e0655e3 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 26 Jun 2023 16:28:10 -0400 Subject: [PATCH 05/17] Update glue tests --- .../tests/fixture-templates/rust/Cargo.toml | 3 ++- .../advanced-recursive-union/src/lib.rs | 5 ++--- crates/glue/tests/fixtures/arguments/src/lib.rs | 4 ++-- .../glue/tests/fixtures/basic-record/src/lib.rs | 4 ++-- .../fixtures/basic-recursive-union/src/lib.rs | 11 ++--------- .../glue/tests/fixtures/enumeration/src/lib.rs | 8 ++++---- .../fixtures/list-recursive-union/src/lib.rs | 17 +++-------------- .../tests/fixtures/multiple-modules/src/lib.rs | 5 ++--- .../tests/fixtures/nested-record/src/lib.rs | 4 ++-- .../fixtures/nonnullable-unwrapped/src/lib.rs | 4 ++-- .../fixtures/nullable-unwrapped/src/lib.rs | 11 +++-------- .../tests/fixtures/nullable-wrapped/src/lib.rs | 6 +++--- crates/glue/tests/fixtures/option/src/lib.rs | 6 +++--- .../tests/fixtures/return-function/src/lib.rs | 4 ++-- crates/glue/tests/fixtures/rocresult/src/lib.rs | 6 +++--- .../tests/fixtures/single-tag-union/src/lib.rs | 6 +++--- .../fixtures/union-with-padding/src/lib.rs | 6 +++--- .../fixtures/union-without-padding/src/lib.rs | 14 +++++++------- crates/glue/tests/test_glue_cli.rs | 2 +- 19 files changed, 51 insertions(+), 75 deletions(-) diff --git a/crates/glue/tests/fixture-templates/rust/Cargo.toml b/crates/glue/tests/fixture-templates/rust/Cargo.toml index 9697079aa5a..56f73f42ff6 100644 --- a/crates/glue/tests/fixture-templates/rust/Cargo.toml +++ b/crates/glue/tests/fixture-templates/rust/Cargo.toml @@ -27,7 +27,8 @@ name = "host" path = "src/main.rs" [dependencies] -roc_std = { path = "../../../../roc_std" } +roc_std = { path = "test_glue/roc_std" } +roc_app = { path = "test_glue/roc_app" } libc = "0.2" indoc = "1.0.6" diff --git a/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs b/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs index e5c6e0c71f8..85af141dcbd 100644 --- a/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs +++ b/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs @@ -1,14 +1,13 @@ -mod test_glue; +use roc_app; use indoc::indoc; -// use test_glue::Rbt; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/arguments/src/lib.rs b/crates/glue/tests/fixtures/arguments/src/lib.rs index e20cb959598..9b5d52ac9b3 100644 --- a/crates/glue/tests/fixtures/arguments/src/lib.rs +++ b/crates/glue/tests/fixtures/arguments/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let answer = test_glue::mainForHost(42i64); + let answer = roc_app::mainForHost(42i64); println!("Answer was: {:?}", answer); // Debug diff --git a/crates/glue/tests/fixtures/basic-record/src/lib.rs b/crates/glue/tests/fixtures/basic-record/src/lib.rs index 4adf2de4c6f..d5cc2ea1e0b 100644 --- a/crates/glue/tests/fixtures/basic-record/src/lib.rs +++ b/crates/glue/tests/fixtures/basic-record/src/lib.rs @@ -1,11 +1,11 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let record = test_glue::mainForHost(); + let record = roc_app::mainForHost(); // Verify that the record has all the expected traits. diff --git a/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs b/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs index 299f9c802c6..ff0293bf68d 100644 --- a/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs +++ b/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs @@ -1,19 +1,12 @@ -mod test_glue; - use indoc::indoc; -use test_glue::Expr; - -extern "C" { - #[link_name = "roc__mainForHost_1_exposed_generic"] - fn roc_main(_: *mut Expr); -} +use roc_app::{self, Expr}; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/enumeration/src/lib.rs b/crates/glue/tests/fixtures/enumeration/src/lib.rs index 395b1296433..59704b6be9c 100644 --- a/crates/glue/tests/fixtures/enumeration/src/lib.rs +++ b/crates/glue/tests/fixtures/enumeration/src/lib.rs @@ -1,11 +1,11 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. @@ -28,8 +28,8 @@ pub extern "C" fn rust_main() -> i32 { println!( "tag_union was: {:?}, Bar is: {:?}, Baz is: {:?}", tag_union, - test_glue::MyEnum::Bar, - test_glue::MyEnum::Baz, + roc_app::MyEnum::Bar, + roc_app::MyEnum::Baz, ); // Debug // Exit code diff --git a/crates/glue/tests/fixtures/list-recursive-union/src/lib.rs b/crates/glue/tests/fixtures/list-recursive-union/src/lib.rs index e494b94e25b..0b598c19d93 100644 --- a/crates/glue/tests/fixtures/list-recursive-union/src/lib.rs +++ b/crates/glue/tests/fixtures/list-recursive-union/src/lib.rs @@ -1,25 +1,14 @@ -mod test_glue; +use roc_app; use indoc::indoc; -use test_glue::Rbt; - -extern "C" { - #[link_name = "roc__mainForHost_1_exposed_generic"] - fn roc_main(_: *mut Rbt); -} +use roc_app::Rbt; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = unsafe { - let mut ret: core::mem::MaybeUninit = core::mem::MaybeUninit::uninit(); - - roc_main(ret.as_mut_ptr()); - - ret.assume_init() - }; + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/multiple-modules/src/lib.rs b/crates/glue/tests/fixtures/multiple-modules/src/lib.rs index 74c44d6f528..e26415e7101 100644 --- a/crates/glue/tests/fixtures/multiple-modules/src/lib.rs +++ b/crates/glue/tests/fixtures/multiple-modules/src/lib.rs @@ -1,13 +1,12 @@ -mod test_glue; - use indoc::indoc; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/nested-record/src/lib.rs b/crates/glue/tests/fixtures/nested-record/src/lib.rs index 161b103780b..92deb035b25 100644 --- a/crates/glue/tests/fixtures/nested-record/src/lib.rs +++ b/crates/glue/tests/fixtures/nested-record/src/lib.rs @@ -1,10 +1,10 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; - let outer = test_glue::mainForHost(); + let outer = roc_app::mainForHost(); // Verify that `inner` has all the expected traits. { diff --git a/crates/glue/tests/fixtures/nonnullable-unwrapped/src/lib.rs b/crates/glue/tests/fixtures/nonnullable-unwrapped/src/lib.rs index ed7286e47ae..fcae9742c22 100644 --- a/crates/glue/tests/fixtures/nonnullable-unwrapped/src/lib.rs +++ b/crates/glue/tests/fixtures/nonnullable-unwrapped/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; use indoc::indoc; +use roc_app::StrRoseTree; use roc_std::{RocList, RocStr}; -use test_glue::StrRoseTree; extern "C" { #[link_name = "roc__mainForHost_1_exposed_generic"] diff --git a/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs b/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs index 22ece28b599..db461e6339f 100644 --- a/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs +++ b/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs @@ -1,19 +1,14 @@ -mod test_glue; +use roc_app; use indoc::indoc; -use test_glue::StrConsList; - -extern "C" { - #[link_name = "roc__mainForHost_1_exposed_generic"] - fn roc_main(_: *mut StrConsList); -} +use roc_app::StrConsList; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs b/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs index 8a288f4d304..7cb25db13be 100644 --- a/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs +++ b/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; use indoc::indoc; +use roc_app::StrFingerTree; use roc_std::RocStr; -use test_glue::StrFingerTree; extern "C" { #[link_name = "roc__mainForHost_1_exposed_generic"] @@ -14,7 +14,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Eq assert!(StrFingerTree::Empty() == StrFingerTree::Empty()); diff --git a/crates/glue/tests/fixtures/option/src/lib.rs b/crates/glue/tests/fixtures/option/src/lib.rs index 697013032c6..3f7ee45b88e 100644 --- a/crates/glue/tests/fixtures/option/src/lib.rs +++ b/crates/glue/tests/fixtures/option/src/lib.rs @@ -1,11 +1,11 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let string = test_glue::mainForHost(true); + let string = roc_app::mainForHost(true); println!("Answer was: {:?}", string.unwrap_Some()); // Debug // - let integer = test_glue::mainForHost(false); + let integer = roc_app::mainForHost(false); println!("Answer was: {:?}", integer.discriminant()); // Debug // Exit code diff --git a/crates/glue/tests/fixtures/return-function/src/lib.rs b/crates/glue/tests/fixtures/return-function/src/lib.rs index fad8f088059..396bed19b2e 100644 --- a/crates/glue/tests/fixtures/return-function/src/lib.rs +++ b/crates/glue/tests/fixtures/return-function/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let record = test_glue::mainForHost(); + let record = roc_app::mainForHost(); let answer1 = record.f.force_thunk(42i64, 1); let answer2 = record.g.force_thunk(42i64, 1); diff --git a/crates/glue/tests/fixtures/rocresult/src/lib.rs b/crates/glue/tests/fixtures/rocresult/src/lib.rs index dad518783b5..e07fbee768e 100644 --- a/crates/glue/tests/fixtures/rocresult/src/lib.rs +++ b/crates/glue/tests/fixtures/rocresult/src/lib.rs @@ -1,11 +1,11 @@ -mod test_glue; +use roc_app; #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let string = test_glue::mainForHost(true); + let string = roc_app::mainForHost(true); println!("Answer was: {:?}", string); // Debug // - let integer = test_glue::mainForHost(false); + let integer = roc_app::mainForHost(false); println!("Answer was: {:?}", integer); // Debug // Exit code diff --git a/crates/glue/tests/fixtures/single-tag-union/src/lib.rs b/crates/glue/tests/fixtures/single-tag-union/src/lib.rs index a5b8aa1e3e9..cc2e1ae01da 100644 --- a/crates/glue/tests/fixtures/single-tag-union/src/lib.rs +++ b/crates/glue/tests/fixtures/single-tag-union/src/lib.rs @@ -1,14 +1,14 @@ -mod test_glue; +use roc_app; use indoc::indoc; -use test_glue::SingleTagUnion; +use roc_app::SingleTagUnion; #[no_mangle] pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/union-with-padding/src/lib.rs b/crates/glue/tests/fixtures/union-with-padding/src/lib.rs index 31f209efa99..0c22b185fbe 100644 --- a/crates/glue/tests/fixtures/union-with-padding/src/lib.rs +++ b/crates/glue/tests/fixtures/union-with-padding/src/lib.rs @@ -1,6 +1,6 @@ -mod test_glue; +use roc_app; -use test_glue::NonRecursive; +use roc_app::NonRecursive; extern "C" { #[link_name = "roc__mainForHost_1_exposed_generic"] @@ -12,7 +12,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/union-without-padding/src/lib.rs b/crates/glue/tests/fixtures/union-without-padding/src/lib.rs index 6e5bbb2a2f1..e4fb9a53047 100644 --- a/crates/glue/tests/fixtures/union-without-padding/src/lib.rs +++ b/crates/glue/tests/fixtures/union-without-padding/src/lib.rs @@ -1,8 +1,8 @@ -mod test_glue; +use roc_app; extern "C" { #[link_name = "roc__mainForHost_1_exposed_generic"] - fn roc_main(_: *mut test_glue::NonRecursive); + fn roc_main(_: *mut roc_app::NonRecursive); } #[no_mangle] @@ -10,7 +10,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = test_glue::mainForHost(()); + let tag_union = roc_app::mainForHost(()); // Verify that it has all the expected traits. @@ -23,10 +23,10 @@ pub extern "C" fn rust_main() -> i32 { println!( "tag_union was: {:?}\n`Foo \"small str\"` is: {:?}\n`Bar 123` is: {:?}\n`Baz` is: {:?}\n`Blah 456` is: {:?}", tag_union, - test_glue::NonRecursive::Foo("small str".into()), - test_glue::NonRecursive::Bar(123), - test_glue::NonRecursive::Baz(), - test_glue::NonRecursive::Blah(456), + roc_app::NonRecursive::Foo("small str".into()), + roc_app::NonRecursive::Bar(123), + roc_app::NonRecursive::Baz(), + roc_app::NonRecursive::Blah(456), ); // Debug let mut set = HashSet::new(); diff --git a/crates/glue/tests/test_glue_cli.rs b/crates/glue/tests/test_glue_cli.rs index f61189f033e..c1f54d1aa93 100644 --- a/crates/glue/tests/test_glue_cli.rs +++ b/crates/glue/tests/test_glue_cli.rs @@ -173,7 +173,7 @@ mod glue_cli_run { args: I, ) -> Out { let platform_module_path = platform_dir.join("platform.roc"); - let glue_dir = platform_dir.join("src").join("test_glue"); + let glue_dir = platform_dir.join("test_glue"); let fixture_templates_dir = platform_dir .parent() .unwrap() From fb17e57f7c87de9fb8d9ffc0a50705276809e000 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 26 Jun 2023 16:30:19 -0400 Subject: [PATCH 06/17] Mark disciminant enums as pub in RustGlue --- crates/glue/src/RustGlue.roc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/glue/src/RustGlue.roc b/crates/glue/src/RustGlue.roc index 0a862eb7ab8..66ef96d1db4 100644 --- a/crates/glue/src/RustGlue.roc +++ b/crates/glue/src/RustGlue.roc @@ -1315,7 +1315,7 @@ generateNullableUnwrapped = \buf, types, tagUnionid, name, nullTag, nonNullTag, FirstTagIsNull -> """ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - enum discriminant_\(name) { + pub enum discriminant_\(name) { \(nullTag) = 0, \(nonNullTag) = 1, } @@ -1324,7 +1324,7 @@ generateNullableUnwrapped = \buf, types, tagUnionid, name, nullTag, nonNullTag, SecondTagIsNull -> """ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - enum discriminant_\(name) { + pub enum discriminant_\(name) { \(nonNullTag) = 0, \(nullTag) = 1, } From 0af548a66f73a20962cb2badf8c24d324f666375 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 26 Jun 2023 16:30:33 -0400 Subject: [PATCH 07/17] Drop unnecessary thunk (plus it's not FFI-safe) --- crates/glue/tests/fixtures/nullable-unwrapped/platform.roc | 4 ++-- crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/glue/tests/fixtures/nullable-unwrapped/platform.roc b/crates/glue/tests/fixtures/nullable-unwrapped/platform.roc index c5e54d6f594..13992876507 100644 --- a/crates/glue/tests/fixtures/nullable-unwrapped/platform.roc +++ b/crates/glue/tests/fixtures/nullable-unwrapped/platform.roc @@ -7,5 +7,5 @@ platform "test-platform" StrConsList : [Nil, Cons Str StrConsList] -mainForHost : {} -> StrConsList -mainForHost = \{} -> main +mainForHost : StrConsList +mainForHost = main diff --git a/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs b/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs index db461e6339f..f2bf716c7fd 100644 --- a/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs +++ b/crates/glue/tests/fixtures/nullable-unwrapped/src/lib.rs @@ -8,7 +8,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = roc_app::mainForHost(()); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. From 8738c95d6f80115f17cd9551a4c6534141c19039 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 26 Jun 2023 23:51:26 +0200 Subject: [PATCH 08/17] give `0` as a value to our enum attributes --- crates/compiler/build/src/program.rs | 2 +- crates/compiler/gen_llvm/src/llvm/bitcode.rs | 8 ++++---- crates/compiler/gen_llvm/src/llvm/build.rs | 4 ++-- crates/compiler/gen_llvm/src/llvm/lowlevel.rs | 6 +++--- crates/compiler/test_gen/src/helpers/llvm.rs | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index 09f8123f261..6b183d093f6 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -166,7 +166,7 @@ fn gen_from_mono_module_llvm<'a>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let enum_attr = context.create_enum_attribute(kind_id, 1); + let enum_attr = context.create_enum_attribute(kind_id, 0); for function in module.get_functions() { let name = function.get_name().to_str().unwrap(); diff --git a/crates/compiler/gen_llvm/src/llvm/bitcode.rs b/crates/compiler/gen_llvm/src/llvm/bitcode.rs index 4762076da89..64dac3814e0 100644 --- a/crates/compiler/gen_llvm/src/llvm/bitcode.rs +++ b/crates/compiler/gen_llvm/src/llvm/bitcode.rs @@ -225,7 +225,7 @@ fn build_transform_caller_help<'a, 'ctx>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = env.context.create_enum_attribute(kind_id, 1); + let attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, attr); let entry = env.context.append_basic_block(function_value, "entry"); @@ -408,7 +408,7 @@ fn build_rc_wrapper<'a, 'ctx>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = env.context.create_enum_attribute(kind_id, 1); + let attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, attr); let entry = env.context.append_basic_block(function_value, "entry"); @@ -497,7 +497,7 @@ pub fn build_eq_wrapper<'a, 'ctx>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = env.context.create_enum_attribute(kind_id, 1); + let attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, attr); let entry = env.context.append_basic_block(function_value, "entry"); @@ -598,7 +598,7 @@ pub fn build_compare_wrapper<'a, 'ctx>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = env.context.create_enum_attribute(kind_id, 1); + let attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, attr); let entry = env.context.append_basic_block(function_value, "entry"); diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 38a06b4dac7..4d9cf61b795 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -5207,14 +5207,14 @@ fn build_proc_header<'a, 'ctx>( if false { let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let enum_attr = env.context.create_enum_attribute(kind_id, 1); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); fn_val.add_attribute(AttributeLoc::Function, enum_attr); } if false { let kind_id = Attribute::get_named_enum_kind_id("noinline"); debug_assert!(kind_id > 0); - let enum_attr = env.context.create_enum_attribute(kind_id, 1); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); fn_val.add_attribute(AttributeLoc::Function, enum_attr); } diff --git a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs index 63a9a15e740..285f4b929a8 100644 --- a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs +++ b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs @@ -1844,19 +1844,19 @@ fn throw_because_overflow<'ctx>(env: &Env<'_, 'ctx, '_>, message: &str) { // prevent inlining of this function let kind_id = Attribute::get_named_enum_kind_id("noinline"); debug_assert!(kind_id > 0); - let enum_attr = env.context.create_enum_attribute(kind_id, 1); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, enum_attr); // calling this function is unlikely let kind_id = Attribute::get_named_enum_kind_id("cold"); debug_assert!(kind_id > 0); - let enum_attr = env.context.create_enum_attribute(kind_id, 1); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, enum_attr); // this function never returns let kind_id = Attribute::get_named_enum_kind_id("noreturn"); debug_assert!(kind_id > 0); - let enum_attr = env.context.create_enum_attribute(kind_id, 1); + let enum_attr = env.context.create_enum_attribute(kind_id, 0); function_value.add_attribute(AttributeLoc::Function, enum_attr); // Add a basic block for the entry point diff --git a/crates/compiler/test_gen/src/helpers/llvm.rs b/crates/compiler/test_gen/src/helpers/llvm.rs index 2ec40d96245..6ad7cc29a37 100644 --- a/crates/compiler/test_gen/src/helpers/llvm.rs +++ b/crates/compiler/test_gen/src/helpers/llvm.rs @@ -192,7 +192,7 @@ fn create_llvm_module<'a>( let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); debug_assert!(kind_id > 0); - let attr = context.create_enum_attribute(kind_id, 1); + let attr = context.create_enum_attribute(kind_id, 0); for function in module.get_functions() { let name = function.get_name().to_str().unwrap(); From 7311c565f17c98c92cd03b4a4fa40ac17391e7cb Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 26 Jun 2023 23:54:03 +0200 Subject: [PATCH 09/17] use updated llvm type signatures --- crates/compiler/gen_llvm/src/llvm/build.rs | 25 ++++++++----- crates/compiler/gen_llvm/src/llvm/struct_.rs | 37 ++++++++++---------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 4d9cf61b795..3870d5ba388 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -1941,8 +1941,12 @@ fn tag_pointer_set_tag_id<'ctx>( // NOTE: assumes the lower bits of `cast_pointer` are all 0 let indexed_pointer = unsafe { - env.builder - .build_in_bounds_gep(cast_pointer, &[tag_id_intval], "indexed_pointer") + env.builder.new_build_in_bounds_gep( + env.context.i8_type(), + cast_pointer, + &[tag_id_intval], + "indexed_pointer", + ) }; env.builder @@ -1994,7 +1998,14 @@ pub fn tag_pointer_clear_tag_id<'ctx>( "cast_to_i8_ptr", ); - let indexed_pointer = unsafe { env.builder.build_gep(cast_pointer, &[index], "new_ptr") }; + let indexed_pointer = unsafe { + env.builder.new_build_in_bounds_gep( + env.context.i8_type(), + cast_pointer, + &[index], + "new_ptr", + ) + }; env.builder .build_pointer_cast(indexed_pointer, pointer.get_type(), "cast_from_i8_ptr") @@ -3954,11 +3965,9 @@ fn expose_function_to_host_help_c_abi_gen_test<'a, 'ctx>( arguments_for_call.push(*arg); } else { match layout_interner.get_repr(*layout) { - LayoutRepr::Builtin(Builtin::List(_)) => { - let list_type = arg_type - .into_pointer_type() - .get_element_type() - .into_struct_type(); + repr @ LayoutRepr::Builtin(Builtin::List(_)) => { + let list_type = basic_type_from_layout(env, layout_interner, repr); + let loaded = env.builder.new_build_load( list_type, arg.into_pointer_value(), diff --git a/crates/compiler/gen_llvm/src/llvm/struct_.rs b/crates/compiler/gen_llvm/src/llvm/struct_.rs index 30da7eb55e0..64226445868 100644 --- a/crates/compiler/gen_llvm/src/llvm/struct_.rs +++ b/crates/compiler/gen_llvm/src/llvm/struct_.rs @@ -92,7 +92,15 @@ impl<'ctx> RocStruct<'ctx> { index_struct_value(env, layout_interner, field_layouts, *argument, index) } (Self::ByReference(ptr), LayoutRepr::Struct(field_layouts)) => { - index_struct_ptr(env, layout_interner, field_layouts, *ptr, index) + let struct_type = basic_type_from_layout(env, layout_interner, struct_layout); + index_struct_ptr( + env, + layout_interner, + struct_type.into_struct_type(), + field_layouts, + *ptr, + index, + ) } (other, layout) => { unreachable!( @@ -135,26 +143,26 @@ fn index_struct_value<'a, 'ctx>( fn index_struct_ptr<'a, 'ctx>( env: &Env<'a, 'ctx, '_>, layout_interner: &STLayoutInterner<'a>, + struct_type: StructType<'ctx>, field_layouts: &[InLayout<'a>], ptr: PointerValue<'ctx>, index: u64, ) -> BasicValueEnum<'ctx> { debug_assert!(!field_layouts.is_empty()); - let field_value = get_field_from_ptr( - env, - ptr, - index as _, - env.arena - .alloc(format!("struct_field_access_record_{}", index)), - ); - let field_layout = field_layouts[index as usize]; + let field_repr = layout_interner.get_repr(field_layout); + + let name = format!("struct_field_access_record_{}", index); + let field_value = env + .builder + .new_build_struct_gep(struct_type, ptr, index as u32, &name) + .unwrap(); load_roc_value( env, layout_interner, - layout_interner.get_repr(field_layout), + field_repr, field_value, "struct_field", ) @@ -171,15 +179,6 @@ fn get_field_from_value<'ctx>( .unwrap() } -fn get_field_from_ptr<'ctx>( - env: &Env<'_, 'ctx, '_>, - ptr: PointerValue<'ctx>, - index: u32, - name: &str, -) -> PointerValue<'ctx> { - env.builder.build_struct_gep(ptr, index, name).unwrap() -} - struct BuildStruct<'ctx> { struct_type: StructType<'ctx>, struct_val: StructValue<'ctx>, From 7c9c3d829cf10ad02ee90527809f7bb435dfc330 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 27 Jun 2023 00:37:09 +0200 Subject: [PATCH 10/17] use 32-bit GEP indices where easily possible --- crates/compiler/gen_llvm/src/llvm/build.rs | 17 +++++++++++------ .../compiler/gen_llvm/src/llvm/refcounting.rs | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 3870d5ba388..6bcae600c44 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -1931,7 +1931,7 @@ fn tag_pointer_set_tag_id<'ctx>( // we only have 3 bits, so can encode only 0..7 (or on 32-bit targets, 2 bits to encode 0..3) debug_assert!((tag_id as u32) < env.target_info.ptr_width() as u32); - let tag_id_intval = env.ptr_int().const_int(tag_id as u64, false); + let tag_id_intval = env.context.i32_type().const_int(tag_id as u64, false); let cast_pointer = env.builder.build_pointer_cast( pointer, @@ -1991,6 +1991,9 @@ pub fn tag_pointer_clear_tag_id<'ctx>( let current_tag_id = env.builder.build_and(as_int, mask, "masked"); let index = env.builder.build_int_neg(current_tag_id, "index"); + let index = env + .builder + .build_int_cast(index, env.context.i32_type(), "to_i32"); let cast_pointer = env.builder.build_pointer_cast( pointer, @@ -2444,8 +2447,9 @@ fn list_literal<'a, 'ctx>( // all elements are constants, so we can use the memory in the constants section directly // here we make a pointer to the first actual element (skipping the 0 bytes that // represent the refcount) - let zero = env.ptr_int().const_zero(); - let offset = env.ptr_int().const_int(zero_elements as _, false); + let i32_type = env.context.i32_type(); + let zero = i32_type.const_zero(); + let offset = i32_type.const_int(zero_elements as _, false); let ptr = unsafe { env.builder.new_build_in_bounds_gep( @@ -2474,7 +2478,7 @@ fn list_literal<'a, 'ctx>( // then replace the `undef`s with the values that we evaluate at runtime for (index, val) in runtime_evaluated_elements { - let index_val = ctx.i64_type().const_int(index as u64, false); + let index_val = ctx.i32_type().const_int(index as u64, false); let elem_ptr = unsafe { builder.new_build_in_bounds_gep(element_type, ptr, &[index_val], "index") }; @@ -2495,7 +2499,7 @@ fn list_literal<'a, 'ctx>( } ListLiteralElement::Symbol(symbol) => scope.load_symbol(symbol), }; - let index_val = ctx.i64_type().const_int(index as u64, false); + let index_val = ctx.i32_type().const_int(index as u64, false); let elem_ptr = unsafe { builder.new_build_in_bounds_gep(element_type, ptr, &[index_val], "index") }; @@ -6259,7 +6263,8 @@ fn define_global_str_literal_ptr<'ctx>( env.context.i8_type(), ptr, &[env - .ptr_int() + .context + .i32_type() .const_int(env.target_info.ptr_width() as u64, false)], "get_rc_ptr", ) diff --git a/crates/compiler/gen_llvm/src/llvm/refcounting.rs b/crates/compiler/gen_llvm/src/llvm/refcounting.rs index 06f9e73bf1e..6d03fa54c29 100644 --- a/crates/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/crates/compiler/gen_llvm/src/llvm/refcounting.rs @@ -61,7 +61,7 @@ impl<'ctx> PointerToRefcount<'ctx> { builder.build_pointer_cast(data_ptr, refcount_ptr_type, "as_usize_ptr"); // get a pointer to index -1 - let index_intvalue = refcount_type.const_int(-1_i64 as u64, false); + let index_intvalue = env.context.i32_type().const_int(-1_i64 as u64, false); let refcount_ptr = unsafe { builder.new_build_in_bounds_gep( env.ptr_int(), From ca0de5241d7aff7e0ec173bd1b8166315dff5ceb Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 26 Jun 2023 19:35:40 -0400 Subject: [PATCH 11/17] Remove some unnecessary dbg! uses --- crates/ast/src/solve_type.rs | 5 ++++- examples/glue/rust-platform/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/ast/src/solve_type.rs b/crates/ast/src/solve_type.rs index 90e562ad2a0..96873f37f9a 100644 --- a/crates/ast/src/solve_type.rs +++ b/crates/ast/src/solve_type.rs @@ -606,7 +606,10 @@ fn solve<'a>( let result = offenders.len(); if result > 0 { - dbg!(&subs, &offenders, &let_con.def_types); + eprintln!( + "subs: {:?}\n\noffenders: {:?}\n\nlet_con.def_types: {:?}\n", + &subs, &offenders, &let_con.def_types + ); } result diff --git a/examples/glue/rust-platform/src/lib.rs b/examples/glue/rust-platform/src/lib.rs index a91550458b1..fe5398b6475 100644 --- a/examples/glue/rust-platform/src/lib.rs +++ b/examples/glue/rust-platform/src/lib.rs @@ -87,7 +87,7 @@ pub extern "C" fn rust_main() -> i32 { let mut op: Op = roc_main(); loop { - match dbg!(op.discriminant()) { + match op.discriminant() { StdoutWrite => { let stdout_write = op.get_StdoutWrite(); let output: RocStr = stdout_write.f0; From b26ef289b62d2c4ca4896997d8cecf51ce57237b Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 26 Jun 2023 19:38:58 -0400 Subject: [PATCH 12/17] Add basic webserver platform --- examples/webserver/.gitignore | 2 + examples/webserver/Http.roc | 4 + examples/webserver/HttpInternal.roc | 4 + examples/webserver/build.sh | 2 + examples/webserver/main.roc | 6 ++ examples/webserver/platform/Cargo.toml | 38 +++++++ examples/webserver/platform/build.rs | 9 ++ examples/webserver/platform/host.c | 14 +++ examples/webserver/platform/main.roc | 9 ++ examples/webserver/platform/src/lib.rs | 128 ++++++++++++++++++++++++ examples/webserver/platform/src/main.rs | 3 + 11 files changed, 219 insertions(+) create mode 100644 examples/webserver/.gitignore create mode 100644 examples/webserver/Http.roc create mode 100644 examples/webserver/HttpInternal.roc create mode 100755 examples/webserver/build.sh create mode 100644 examples/webserver/main.roc create mode 100644 examples/webserver/platform/Cargo.toml create mode 100644 examples/webserver/platform/build.rs create mode 100644 examples/webserver/platform/host.c create mode 100644 examples/webserver/platform/main.roc create mode 100644 examples/webserver/platform/src/lib.rs create mode 100644 examples/webserver/platform/src/main.rs diff --git a/examples/webserver/.gitignore b/examples/webserver/.gitignore new file mode 100644 index 00000000000..d0e2c82835f --- /dev/null +++ b/examples/webserver/.gitignore @@ -0,0 +1,2 @@ +platform/glue +app diff --git a/examples/webserver/Http.roc b/examples/webserver/Http.roc new file mode 100644 index 00000000000..bdb8a14eba3 --- /dev/null +++ b/examples/webserver/Http.roc @@ -0,0 +1,4 @@ +interface Http exposes [request] imports [HttpInternal] + +request : Str -> Str +request = \req -> HttpInternal.request req diff --git a/examples/webserver/HttpInternal.roc b/examples/webserver/HttpInternal.roc new file mode 100644 index 00000000000..0201a2d2723 --- /dev/null +++ b/examples/webserver/HttpInternal.roc @@ -0,0 +1,4 @@ +interface HttpInternal exposes [request] imports [] + +request : Str -> Str +request = \req -> req diff --git a/examples/webserver/build.sh b/examples/webserver/build.sh new file mode 100755 index 00000000000..46790466f83 --- /dev/null +++ b/examples/webserver/build.sh @@ -0,0 +1,2 @@ +roc glue ../../crates/glue/src/RustGlue.roc platform/glue platform/main.roc +roc build diff --git a/examples/webserver/main.roc b/examples/webserver/main.roc new file mode 100644 index 00000000000..d5660386553 --- /dev/null +++ b/examples/webserver/main.roc @@ -0,0 +1,6 @@ +app "app" + packages { pf: "platform/main.roc" } + imports [] + provides [main] to pf + +main = \str -> "hi, \(str)!!" diff --git a/examples/webserver/platform/Cargo.toml b/examples/webserver/platform/Cargo.toml new file mode 100644 index 00000000000..29062b4c73f --- /dev/null +++ b/examples/webserver/platform/Cargo.toml @@ -0,0 +1,38 @@ +# ⚠️ READ THIS BEFORE MODIFYING THIS FILE! ⚠️ +# +# This file is a fixture template. If the file you're looking at is +# in the fixture-templates/ directory, then you're all set - go ahead +# and modify it, and it will modify all the fixture tests. +# +# If this file is in the fixtures/ directory, on the other hand, then +# it is gitignored and will be overwritten the next time tests run. +# So you probably don't want to modify it by hand! Instead, modify the +# file with the same name in the fixture-templates/ directory. + +[package] +name = "host" +version = "0.0.1" +authors = ["The Roc Contributors"] +license = "UPL-1.0" +edition = "2018" +links = "app" + +[lib] +name = "host" +path = "src/lib.rs" +crate-type = ["staticlib", "rlib"] + +[[bin]] +name = "host" +path = "src/main.rs" + +[dependencies] +roc_std = { path = "glue/roc_std" } +roc_app = { path = "glue/roc_app" } +libc = "0.2" +hyper = { version = "0.14", features= ["http1", "http2", "client", "server", "runtime", "backports", "deprecated"] } +tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } +futures = "0.3" +bytes = "1.0" + +[workspace] diff --git a/examples/webserver/platform/build.rs b/examples/webserver/platform/build.rs new file mode 100644 index 00000000000..47763b34c39 --- /dev/null +++ b/examples/webserver/platform/build.rs @@ -0,0 +1,9 @@ +fn main() { + #[cfg(not(windows))] + println!("cargo:rustc-link-lib=dylib=app"); + + #[cfg(windows)] + println!("cargo:rustc-link-lib=dylib=libapp"); + + println!("cargo:rustc-link-search=."); +} diff --git a/examples/webserver/platform/host.c b/examples/webserver/platform/host.c new file mode 100644 index 00000000000..3914d3f6eed --- /dev/null +++ b/examples/webserver/platform/host.c @@ -0,0 +1,14 @@ +// ⚠️ READ THIS BEFORE MODIFYING THIS FILE! ⚠️ +// +// This file is a fixture template. If the file you're looking at is +// in the fixture-templates/ directory, then you're all set - go ahead +// and modify it, and it will modify all the fixture tests. +// +// If this file is in the fixtures/ directory, on the other hand, then +// it is gitignored and will be overwritten the next time tests run. +// So you probably don't want to modify it by hand! Instead, modify the +// file with the same name in the fixture-templates/ directory. + +extern int rust_main(); + +int main() { return rust_main(); } diff --git a/examples/webserver/platform/main.roc b/examples/webserver/platform/main.roc new file mode 100644 index 00000000000..c01047b7575 --- /dev/null +++ b/examples/webserver/platform/main.roc @@ -0,0 +1,9 @@ +platform "webserver-platform" + requires {} { main : _ } + exposes [] + packages {} + imports [] + provides [mainForHost] + +mainForHost : Str -> Str +mainForHost = \str -> main str diff --git a/examples/webserver/platform/src/lib.rs b/examples/webserver/platform/src/lib.rs new file mode 100644 index 00000000000..6cbdb4f27fe --- /dev/null +++ b/examples/webserver/platform/src/lib.rs @@ -0,0 +1,128 @@ +use futures::{Future, FutureExt}; +use hyper::{Body, Request, Response, Server, StatusCode}; +use std::convert::Infallible; +use std::net::SocketAddr; +use std::panic::AssertUnwindSafe; +use tokio::task::spawn_blocking; +use roc_app; +use std::time::Instant; + +const LISTEN_ON_PORT: u16 = 8000; + +fn call_roc(req_bytes: &[u8]) -> (StatusCode, Vec) { + // TODO setjmp (both for signal handlers and for roc_panic, bc calling Rust panics from FFI code is UB) + // TODO install signal handlers + // TODO convert roc_bytes to RocList, call roc_mainForHost, and convert from its RocList response + let req_str: &str = std::str::from_utf8(req_bytes).unwrap(); // TODO don't unwrap + + (StatusCode::OK, roc_app::mainForHost(req_str.into()).as_str().into()) +} + +async fn handle(req: Request) -> Response { + match hyper::body::to_bytes(req.into_body()).await { + Ok(req_body) => { + spawn_blocking(move || { + let (status_code, resp_bytes) = call_roc(&req_body); + + Response::builder() + .status(status_code) // TODO get status code from Roc too + .body(Body::from(resp_bytes)) + .unwrap() // TODO don't unwrap() here + }) + .then(|resp| async { + resp.unwrap() // TODO don't unwrap here + }) + .await + } + Err(_) => todo!(), // TODO + } +} + +/// Translate Rust panics in the given Future into 500 errors +async fn handle_panics( + fut: impl Future>, +) -> Result, Infallible> { + match AssertUnwindSafe(fut).catch_unwind().await { + Ok(response) => Ok(response), + Err(_panic) => { + let error = Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body("Panic detected!".into()) + .unwrap(); // TODO don't unwrap here + + Ok(error) + } + } +} + +#[no_mangle] +pub extern "C" fn rust_main() -> i32 { + let start_time = Instant::now(); + let addr = SocketAddr::from(([127, 0, 0, 1], LISTEN_ON_PORT)); + + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() // TODO print and error and return nonzero + .block_on(async { + let server = Server::bind(&addr).serve(hyper::service::make_service_fn(|_conn| async { + Ok::<_, Infallible>(hyper::service::service_fn(|req| handle_panics(handle(req)))) + })); + + let elapsed_time = start_time.elapsed(); + + println!("Server started up and listening on {:?} in {} ms", addr, elapsed_time.as_millis()); + + match server.await { + Ok(_) => 0, + Err(err) => { + eprintln!("Error initializing Rust `hyper` server: {}", err); // TODO improve this + 1 + } + } + }) +} + +// Externs required by roc_std and by the Roc app + +use core::ffi::c_void; +use std::ffi::CStr; +use std::os::raw::c_char; + +#[no_mangle] +pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { + return libc::malloc(size); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_realloc( + c_ptr: *mut c_void, + new_size: usize, + _old_size: usize, + _alignment: u32, +) -> *mut c_void { + return libc::realloc(c_ptr, new_size); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { + return libc::free(c_ptr); +} + +#[no_mangle] +pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) { + match tag_id { + 0 => { + let slice = CStr::from_ptr(c_ptr as *const c_char); + let string = slice.to_str().unwrap(); + eprintln!("Roc hit a panic: {}", string); + std::process::exit(1); + } + _ => todo!(), + } +} + +#[no_mangle] +pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { + libc::memset(dst, c, n) +} diff --git a/examples/webserver/platform/src/main.rs b/examples/webserver/platform/src/main.rs new file mode 100644 index 00000000000..0765384f29f --- /dev/null +++ b/examples/webserver/platform/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + std::process::exit(host::rust_main() as _); +} From 1fff1cd2a132d1f1cfffd1c12a1ade8354bd53e6 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 26 Jun 2023 19:35:52 -0400 Subject: [PATCH 13/17] Fix some glue fixtures --- crates/glue/tests/fixture-templates/rust/build.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/glue/tests/fixture-templates/rust/build.rs b/crates/glue/tests/fixture-templates/rust/build.rs index 7dcf8da4e95..6c70d142f01 100644 --- a/crates/glue/tests/fixture-templates/rust/build.rs +++ b/crates/glue/tests/fixture-templates/rust/build.rs @@ -10,6 +10,11 @@ // file with the same name in the fixture-templates/ directory. fn main() { + #[cfg(not(windows))] println!("cargo:rustc-link-lib=dylib=app"); + + #[cfg(windows)] + println!("cargo:rustc-link-lib=dylib=libapp"); + println!("cargo:rustc-link-search=."); } From db8d222c52f6f15093e01965dd2d4d09adb85b09 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 26 Jun 2023 21:12:49 -0400 Subject: [PATCH 14/17] Refactor some webserver example init stuff --- examples/webserver/platform/src/lib.rs | 55 ++++++++++++++------------ 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/examples/webserver/platform/src/lib.rs b/examples/webserver/platform/src/lib.rs index 6cbdb4f27fe..09d5b6de134 100644 --- a/examples/webserver/platform/src/lib.rs +++ b/examples/webserver/platform/src/lib.rs @@ -5,9 +5,8 @@ use std::net::SocketAddr; use std::panic::AssertUnwindSafe; use tokio::task::spawn_blocking; use roc_app; -use std::time::Instant; -const LISTEN_ON_PORT: u16 = 8000; +const DEFAULT_PORT: u16 = 8000; fn call_roc(req_bytes: &[u8]) -> (StatusCode, Vec) { // TODO setjmp (both for signal handlers and for roc_panic, bc calling Rust panics from FFI code is UB) @@ -55,32 +54,36 @@ async fn handle_panics( } } +const LOCALHOST: [u8; 4] = [127, 0, 0, 1]; + +async fn run_server(port: u16) -> i32 { + let addr = SocketAddr::from((LOCALHOST, port)); + let server = Server::bind(&addr).serve(hyper::service::make_service_fn(|_conn| async { + Ok::<_, Infallible>(hyper::service::service_fn(|req| handle_panics(handle(req)))) + })); + + println!("Listening on "); + + match server.await { + Ok(_) => 0, + Err(err) => { + eprintln!("Error initializing Rust `hyper` server: {}", err); // TODO improve this + + 1 + } + } +} + #[no_mangle] pub extern "C" fn rust_main() -> i32 { - let start_time = Instant::now(); - let addr = SocketAddr::from(([127, 0, 0, 1], LISTEN_ON_PORT)); - - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap() // TODO print and error and return nonzero - .block_on(async { - let server = Server::bind(&addr).serve(hyper::service::make_service_fn(|_conn| async { - Ok::<_, Infallible>(hyper::service::service_fn(|req| handle_panics(handle(req)))) - })); - - let elapsed_time = start_time.elapsed(); - - println!("Server started up and listening on {:?} in {} ms", addr, elapsed_time.as_millis()); - - match server.await { - Ok(_) => 0, - Err(err) => { - eprintln!("Error initializing Rust `hyper` server: {}", err); // TODO improve this - 1 - } - } - }) + match tokio::runtime::Builder::new_multi_thread().enable_all().build() { + Ok(runtime) => runtime.block_on(async { run_server(DEFAULT_PORT).await }), + Err(err) => { + eprintln!("Error initializing tokio multithreaded runtime: {}", err); // TODO improve this + + 1 + } + } } // Externs required by roc_std and by the Roc app From 7df7d5100627b85973093fd15a80d08e67186b1f Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 26 Jun 2023 22:13:59 -0400 Subject: [PATCH 15/17] webserver example setjmp/longjmp error handling --- examples/webserver/platform/Cargo.toml | 4 +- examples/webserver/platform/src/lib.rs | 151 +++++++++++++++++++------ 2 files changed, 117 insertions(+), 38 deletions(-) diff --git a/examples/webserver/platform/Cargo.toml b/examples/webserver/platform/Cargo.toml index 29062b4c73f..867603fafc2 100644 --- a/examples/webserver/platform/Cargo.toml +++ b/examples/webserver/platform/Cargo.toml @@ -27,10 +27,10 @@ name = "host" path = "src/main.rs" [dependencies] -roc_std = { path = "glue/roc_std" } +roc_std = { path = "glue/roc_std", features = ["std"] } roc_app = { path = "glue/roc_app" } libc = "0.2" -hyper = { version = "0.14", features= ["http1", "http2", "client", "server", "runtime", "backports", "deprecated"] } +hyper = { version = "0.14", features = ["http1", "http2", "client", "server", "runtime", "backports", "deprecated"] } tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } futures = "0.3" bytes = "1.0" diff --git a/examples/webserver/platform/src/lib.rs b/examples/webserver/platform/src/lib.rs index 09d5b6de134..ad54ace3592 100644 --- a/examples/webserver/platform/src/lib.rs +++ b/examples/webserver/platform/src/lib.rs @@ -1,37 +1,114 @@ use futures::{Future, FutureExt}; use hyper::{Body, Request, Response, Server, StatusCode}; +use roc_app; +use roc_std::RocStr; +use std::cell::RefCell; use std::convert::Infallible; use std::net::SocketAddr; +use std::os::raw::{c_int, c_long, c_void}; use std::panic::AssertUnwindSafe; use tokio::task::spawn_blocking; -use roc_app; +use libc::{sigaction, siginfo_t, sigemptyset, SIGBUS, SIGFPE, SIGILL, SIGSEGV, sighandler_t, sigset_t, SA_SIGINFO, SIG_DFL}; const DEFAULT_PORT: u16 = 8000; -fn call_roc(req_bytes: &[u8]) -> (StatusCode, Vec) { - // TODO setjmp (both for signal handlers and for roc_panic, bc calling Rust panics from FFI code is UB) - // TODO install signal handlers - // TODO convert roc_bytes to RocList, call roc_mainForHost, and convert from its RocList response - let req_str: &str = std::str::from_utf8(req_bytes).unwrap(); // TODO don't unwrap +// If we have a roc_panic or a segfault, these will be used to record where to jump back to +// (a point at which we can return a different response). +thread_local! { + // 64 is the biggest jmp_buf in setjmp.h + static SETJMP_ENV: RefCell<[c_long; 64]> = RefCell::new([0 as c_long; 64]); + static ROC_CRASH_MSG: RefCell = RefCell::new(RocStr::empty()); + static SIGNAL_CAUGHT: RefCell = RefCell::new(0); +} + +extern "C" { + #[link_name = "setjmp"] + pub fn setjmp(env: *mut c_void) -> c_int; + + #[link_name = "longjmp"] + pub fn longjmp(env: *mut c_void, val: c_int); +} + +unsafe extern "C" fn signal_handler(sig: c_int, _: *mut siginfo_t, _: *mut libc::c_void) { + SIGNAL_CAUGHT.with(|val| { + *val.borrow_mut() = sig; + }); + + SETJMP_ENV.with(|env| { + longjmp(env.borrow_mut().as_mut_ptr().cast(), 1); + }); +} + +fn setup_signal(sig: c_int) { + let sa = libc::sigaction { + sa_sigaction: signal_handler as sighandler_t, + sa_mask: sigset_t::default(), + sa_flags: SA_SIGINFO, + }; + + let mut old_sa = libc::sigaction { + sa_sigaction: SIG_DFL, + sa_mask: sigset_t::default(), + sa_flags: 0, + }; + + unsafe { + sigemptyset(&mut old_sa.sa_mask as *mut sigset_t); + sigaction(sig, &sa, &mut old_sa); + } +} + +fn call_roc(req_bytes: &[u8]) -> Response { + let mut setjmp_result = 0; + + SETJMP_ENV.with(|env| { + setjmp_result = unsafe { setjmp(env.borrow_mut().as_mut_ptr().cast()) }; + }); + + if setjmp_result == 0 { + setup_signal(SIGSEGV); + setup_signal(SIGILL); + setup_signal(SIGFPE); + setup_signal(SIGBUS); + + let req_str: &str = std::str::from_utf8(req_bytes).unwrap(); // TODO don't unwrap + let resp: String = roc_app::mainForHost(req_str.into()).as_str().into(); + + Response::builder() + .status(StatusCode::OK) // TODO get status code from Roc too + .body(Body::from(resp)) + .unwrap() // TODO don't unwrap() here + } else { + let mut crash_msg: String = String::new(); + let mut sig: c_int = 0; + + SIGNAL_CAUGHT.with(|val| { + sig = *val.borrow(); + }); + + if sig == 0 { + ROC_CRASH_MSG.with(|env| { + crash_msg = env.borrow().as_str().into(); + }); + } else { + crash_msg = "Roc crashed with signal {sig}".into(); // TODO print the name of the signal + } - (StatusCode::OK, roc_app::mainForHost(req_str.into()).as_str().into()) + Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::from(crash_msg)) + .unwrap() // TODO don't unwrap() here + } } -async fn handle(req: Request) -> Response { +async fn handle_req(req: Request) -> Response { match hyper::body::to_bytes(req.into_body()).await { Ok(req_body) => { - spawn_blocking(move || { - let (status_code, resp_bytes) = call_roc(&req_body); - - Response::builder() - .status(status_code) // TODO get status code from Roc too - .body(Body::from(resp_bytes)) - .unwrap() // TODO don't unwrap() here - }) - .then(|resp| async { - resp.unwrap() // TODO don't unwrap here - }) - .await + spawn_blocking(move || call_roc(&req_body)) + .then(|resp| async { + resp.unwrap() // TODO don't unwrap here + }) + .await } Err(_) => todo!(), // TODO } @@ -59,7 +136,7 @@ const LOCALHOST: [u8; 4] = [127, 0, 0, 1]; async fn run_server(port: u16) -> i32 { let addr = SocketAddr::from((LOCALHOST, port)); let server = Server::bind(&addr).serve(hyper::service::make_service_fn(|_conn| async { - Ok::<_, Infallible>(hyper::service::service_fn(|req| handle_panics(handle(req)))) + Ok::<_, Infallible>(hyper::service::service_fn(|req| handle_panics(handle_req(req)))) })); println!("Listening on "); @@ -76,7 +153,10 @@ async fn run_server(port: u16) -> i32 { #[no_mangle] pub extern "C" fn rust_main() -> i32 { - match tokio::runtime::Builder::new_multi_thread().enable_all().build() { + match tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + { Ok(runtime) => runtime.block_on(async { run_server(DEFAULT_PORT).await }), Err(err) => { eprintln!("Error initializing tokio multithreaded runtime: {}", err); // TODO improve this @@ -88,10 +168,6 @@ pub extern "C" fn rust_main() -> i32 { // Externs required by roc_std and by the Roc app -use core::ffi::c_void; -use std::ffi::CStr; -use std::os::raw::c_char; - #[no_mangle] pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { return libc::malloc(size); @@ -113,16 +189,19 @@ pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { } #[no_mangle] -pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) { - match tag_id { - 0 => { - let slice = CStr::from_ptr(c_ptr as *const c_char); - let string = slice.to_str().unwrap(); - eprintln!("Roc hit a panic: {}", string); - std::process::exit(1); - } - _ => todo!(), - } +pub unsafe extern "C" fn roc_panic(msg: RocStr) { + // Set the last caught signal to 0, so we don't mistake this for a signal. + SIGNAL_CAUGHT.with(|val| { + *val.borrow_mut() = 0; + }); + + ROC_CRASH_MSG.with(|val| { + *val.borrow_mut() = msg; + }); + + SETJMP_ENV.with(|env| { + longjmp(env.borrow_mut().as_mut_ptr().cast(), 1); + }); } #[no_mangle] From 1c52c23c5faae9582d2cede9c0e6bf57b180283b Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 27 Jun 2023 11:02:24 +0200 Subject: [PATCH 16/17] Revert "use 32-bit GEP indices where easily possible" This reverts commit 7c9c3d829cf10ad02ee90527809f7bb435dfc330. --- crates/compiler/gen_llvm/src/llvm/build.rs | 17 ++++++----------- .../compiler/gen_llvm/src/llvm/refcounting.rs | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 6bcae600c44..3870d5ba388 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -1931,7 +1931,7 @@ fn tag_pointer_set_tag_id<'ctx>( // we only have 3 bits, so can encode only 0..7 (or on 32-bit targets, 2 bits to encode 0..3) debug_assert!((tag_id as u32) < env.target_info.ptr_width() as u32); - let tag_id_intval = env.context.i32_type().const_int(tag_id as u64, false); + let tag_id_intval = env.ptr_int().const_int(tag_id as u64, false); let cast_pointer = env.builder.build_pointer_cast( pointer, @@ -1991,9 +1991,6 @@ pub fn tag_pointer_clear_tag_id<'ctx>( let current_tag_id = env.builder.build_and(as_int, mask, "masked"); let index = env.builder.build_int_neg(current_tag_id, "index"); - let index = env - .builder - .build_int_cast(index, env.context.i32_type(), "to_i32"); let cast_pointer = env.builder.build_pointer_cast( pointer, @@ -2447,9 +2444,8 @@ fn list_literal<'a, 'ctx>( // all elements are constants, so we can use the memory in the constants section directly // here we make a pointer to the first actual element (skipping the 0 bytes that // represent the refcount) - let i32_type = env.context.i32_type(); - let zero = i32_type.const_zero(); - let offset = i32_type.const_int(zero_elements as _, false); + let zero = env.ptr_int().const_zero(); + let offset = env.ptr_int().const_int(zero_elements as _, false); let ptr = unsafe { env.builder.new_build_in_bounds_gep( @@ -2478,7 +2474,7 @@ fn list_literal<'a, 'ctx>( // then replace the `undef`s with the values that we evaluate at runtime for (index, val) in runtime_evaluated_elements { - let index_val = ctx.i32_type().const_int(index as u64, false); + let index_val = ctx.i64_type().const_int(index as u64, false); let elem_ptr = unsafe { builder.new_build_in_bounds_gep(element_type, ptr, &[index_val], "index") }; @@ -2499,7 +2495,7 @@ fn list_literal<'a, 'ctx>( } ListLiteralElement::Symbol(symbol) => scope.load_symbol(symbol), }; - let index_val = ctx.i32_type().const_int(index as u64, false); + let index_val = ctx.i64_type().const_int(index as u64, false); let elem_ptr = unsafe { builder.new_build_in_bounds_gep(element_type, ptr, &[index_val], "index") }; @@ -6263,8 +6259,7 @@ fn define_global_str_literal_ptr<'ctx>( env.context.i8_type(), ptr, &[env - .context - .i32_type() + .ptr_int() .const_int(env.target_info.ptr_width() as u64, false)], "get_rc_ptr", ) diff --git a/crates/compiler/gen_llvm/src/llvm/refcounting.rs b/crates/compiler/gen_llvm/src/llvm/refcounting.rs index 6d03fa54c29..06f9e73bf1e 100644 --- a/crates/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/crates/compiler/gen_llvm/src/llvm/refcounting.rs @@ -61,7 +61,7 @@ impl<'ctx> PointerToRefcount<'ctx> { builder.build_pointer_cast(data_ptr, refcount_ptr_type, "as_usize_ptr"); // get a pointer to index -1 - let index_intvalue = env.context.i32_type().const_int(-1_i64 as u64, false); + let index_intvalue = refcount_type.const_int(-1_i64 as u64, false); let refcount_ptr = unsafe { builder.new_build_in_bounds_gep( env.ptr_int(), From 8bdbc22f295e4a32311f78e267a4794818bf9aab Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 27 Jun 2023 16:11:54 +0200 Subject: [PATCH 17/17] basic-cli old docs site Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- www/build.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/www/build.sh b/www/build.sh index e2df683f4b8..cdd14f3cfd2 100755 --- a/www/build.sh +++ b/www/build.sh @@ -115,4 +115,13 @@ mkdir -p $BASIC_CLI_PACKAGE_DIR rm generated-docs/*.* # we already copied over the *.js and *.css files earlier, so just drop these. mv generated-docs/* $BASIC_CLI_PACKAGE_DIR # move all the folders to build/packages/basic-cli +# set up docs for basic-cli 0.3.2 +BASIC_CLI_DIR_0_3_2=$BASIC_CLI_PACKAGE_DIR/0-3-2 +mkdir -p $BASIC_CLI_DIR_0_3_2 +curl -fL --output $BASIC_CLI_DIR_0_3_2/docs.tar.gz https://github.com/roc-lang/basic-cli/releases/download/0.3.2/docs.tar.gz +tar -xf $BASIC_CLI_DIR_0_3_2/docs.tar.gz -C $BASIC_CLI_DIR_0_3_2/ +rm $BASIC_CLI_DIR_0_3_2/docs.tar.gz +mv $BASIC_CLI_DIR_0_3_2/generated-docs/* $BASIC_CLI_DIR_0_3_2 +rm -rf $BASIC_CLI_DIR_0_3_2/generated-docs + popd