attempt benchmark platform

This commit is contained in:
Folkert 2021-08-24 23:57:39 +02:00
parent 0a7f7a2772
commit 63f89d73be
6 changed files with 185 additions and 61 deletions

View file

@ -74,10 +74,30 @@ pub fn build_file<'a>(
builtin_defs_map, builtin_defs_map,
)?; )?;
use target_lexicon::Architecture;
let emit_wasm = match target.architecture {
Architecture::X86_64 => false,
Architecture::Aarch64(_) => false,
Architecture::Wasm32 => true,
_ => panic!(
"TODO gracefully handle unsupported architecture: {:?}",
target.architecture
),
};
// TODO wasm host extension should be something else ideally
// .bc does not seem to work because
//
// > Non-Emscripten WebAssembly hasn't implemented __builtin_return_address
//
// and zig does not currently emit `.a` webassembly static libraries
let host_extension = if emit_wasm { "zig" } else { "o" };
let app_extension = if emit_wasm { "bc" } else { "o" };
let path_to_platform = loaded.platform_path.clone(); let path_to_platform = loaded.platform_path.clone();
let app_o_file = Builder::new() let app_o_file = Builder::new()
.prefix("roc_app") .prefix("roc_app")
.suffix(".o") .suffix(&format!(".{}", app_extension))
.tempfile() .tempfile()
.map_err(|err| { .map_err(|err| {
todo!("TODO Gracefully handle tempfile creation error {:?}", err); todo!("TODO Gracefully handle tempfile creation error {:?}", err);
@ -173,12 +193,13 @@ pub fn build_file<'a>(
let mut host_input_path = PathBuf::from(cwd); let mut host_input_path = PathBuf::from(cwd);
host_input_path.push(&*path_to_platform); host_input_path.push(&*path_to_platform);
host_input_path.push("host.o"); host_input_path.push("host");
host_input_path.set_extension(host_extension);
// TODO we should no longer need to do this once we have platforms on // TODO we should no longer need to do this once we have platforms on
// a package repository, as we can then get precompiled hosts from there. // a package repository, as we can then get precompiled hosts from there.
let rebuild_host_start = SystemTime::now(); let rebuild_host_start = SystemTime::now();
rebuild_host(host_input_path.as_path()); rebuild_host(target, host_input_path.as_path());
let rebuild_host_end = rebuild_host_start.elapsed().unwrap(); let rebuild_host_end = rebuild_host_start.elapsed().unwrap();
if emit_debug_info { if emit_debug_info {

View file

@ -130,6 +130,15 @@ pub fn build_app<'a>() -> App<'a> {
.requires(ROC_FILE) .requires(ROC_FILE)
.required(false), .required(false),
) )
.arg(
Arg::with_name(FLAG_BACKEND)
.long(FLAG_BACKEND)
.help("Choose a different backend")
// .requires(BACKEND)
.default_value("llvm")
.possible_values(&["llvm", "wasm", "asm"])
.required(false),
)
.arg( .arg(
Arg::with_name(ROC_FILE) Arg::with_name(ROC_FILE)
.help("The .roc file of an app to build and run") .help("The .roc file of an app to build and run")
@ -266,7 +275,14 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
Ok(outcome.status_code()) Ok(outcome.status_code())
} }
BuildAndRun { roc_file_arg_index } => { BuildAndRun { roc_file_arg_index } => {
let mut cmd = Command::new(binary_path); let mut cmd = match target.architecture {
Architecture::Wasm32 => Command::new("wasmtime"),
_ => Command::new(&binary_path),
};
if let Architecture::Wasm32 = target.architecture {
cmd.arg(binary_path);
}
// Forward all the arguments after the .roc file argument // Forward all the arguments after the .roc file argument
// to the new process. This way, you can do things like: // to the new process. This way, you can do things like:

View file

@ -60,7 +60,7 @@ fn find_zig_str_path() -> PathBuf {
} }
#[cfg(not(target_os = "macos"))] #[cfg(not(target_os = "macos"))]
pub fn build_zig_host( pub fn build_zig_host_native(
env_path: &str, env_path: &str,
env_home: &str, env_home: &str,
emit_bin: &str, emit_bin: &str,
@ -90,7 +90,7 @@ pub fn build_zig_host(
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub fn build_zig_host( pub fn build_zig_host_native(
env_path: &str, env_path: &str,
env_home: &str, env_home: &str,
emit_bin: &str, emit_bin: &str,
@ -162,21 +162,62 @@ pub fn build_zig_host(
.unwrap() .unwrap()
} }
pub fn rebuild_host(host_input_path: &Path) { pub fn build_zig_host_wasm32(
env_path: &str,
env_home: &str,
emit_bin: &str,
zig_host_src: &str,
zig_str_path: &str,
) -> Output {
// NOTE currently just to get compiler warnings if the host code is invalid.
// the produced artifact is not used
//
// NOTE we're emitting LLVM IR here (again, it is not actually used)
//
// we'd like to compile with `-target wasm32-wasi` but that is blocked on
//
// https://github.com/ziglang/zig/issues/9414
Command::new("zig")
.env_clear()
.env("PATH", env_path)
.env("HOME", env_home)
.args(&[
"build-obj",
zig_host_src,
emit_bin,
"--pkg-begin",
"str",
zig_str_path,
"--pkg-end",
// include the zig runtime
// "-fcompiler-rt",
// include libc
"--library",
"c",
"-target",
"i386-linux-musl",
// "wasm32-wasi",
// "-femit-llvm-ir=/home/folkertdev/roc/roc/examples/benchmarks/platform/host.ll",
])
.output()
.unwrap()
}
pub fn rebuild_host(target: &Triple, host_input_path: &Path) {
let c_host_src = host_input_path.with_file_name("host.c"); let c_host_src = host_input_path.with_file_name("host.c");
let c_host_dest = host_input_path.with_file_name("c_host.o"); let c_host_dest = host_input_path.with_file_name("c_host.o");
let zig_host_src = host_input_path.with_file_name("host.zig"); let zig_host_src = host_input_path.with_file_name("host.zig");
let rust_host_src = host_input_path.with_file_name("host.rs"); let rust_host_src = host_input_path.with_file_name("host.rs");
let rust_host_dest = host_input_path.with_file_name("rust_host.o"); let rust_host_dest = host_input_path.with_file_name("rust_host.o");
let cargo_host_src = host_input_path.with_file_name("Cargo.toml"); let cargo_host_src = host_input_path.with_file_name("Cargo.toml");
let host_dest = host_input_path.with_file_name("host.o"); let host_dest_native = host_input_path.with_file_name("host.o");
let host_dest_wasm = host_input_path.with_file_name("host.bc");
let env_path = env::var("PATH").unwrap_or_else(|_| "".to_string()); let env_path = env::var("PATH").unwrap_or_else(|_| "".to_string());
let env_home = env::var("HOME").unwrap_or_else(|_| "".to_string()); let env_home = env::var("HOME").unwrap_or_else(|_| "".to_string());
if zig_host_src.exists() { if zig_host_src.exists() {
// Compile host.zig // Compile host.zig
let emit_bin = format!("-femit-bin={}", host_dest.to_str().unwrap());
let zig_str_path = find_zig_str_path(); let zig_str_path = find_zig_str_path();
@ -186,17 +227,31 @@ pub fn rebuild_host(host_input_path: &Path) {
&zig_str_path &zig_str_path
); );
validate_output( let output = match target.architecture {
"host.zig", Architecture::Wasm32 => {
"zig", let emit_bin = format!("-femit-llvm-ir={}", host_dest_wasm.to_str().unwrap());
build_zig_host( build_zig_host_wasm32(
&env_path, &env_path,
&env_home, &env_home,
&emit_bin, &emit_bin,
zig_host_src.to_str().unwrap(), zig_host_src.to_str().unwrap(),
zig_str_path.to_str().unwrap(), zig_str_path.to_str().unwrap(),
), )
); }
Architecture::X86_64 => {
let emit_bin = format!("-femit-bin={}", host_dest_native.to_str().unwrap());
build_zig_host_native(
&env_path,
&env_home,
&emit_bin,
zig_host_src.to_str().unwrap(),
zig_str_path.to_str().unwrap(),
)
}
_ => panic!("Unsupported architecture {:?}", target.architecture),
};
validate_output("host.zig", "zig", output)
} else { } else {
// Compile host.c // Compile host.c
let output = Command::new("clang") let output = Command::new("clang")
@ -237,7 +292,7 @@ pub fn rebuild_host(host_input_path: &Path) {
c_host_dest.to_str().unwrap(), c_host_dest.to_str().unwrap(),
"-lhost", "-lhost",
"-o", "-o",
host_dest.to_str().unwrap(), host_dest_native.to_str().unwrap(),
]) ])
.output() .output()
.unwrap(); .unwrap();
@ -264,7 +319,7 @@ pub fn rebuild_host(host_input_path: &Path) {
c_host_dest.to_str().unwrap(), c_host_dest.to_str().unwrap(),
rust_host_dest.to_str().unwrap(), rust_host_dest.to_str().unwrap(),
"-o", "-o",
host_dest.to_str().unwrap(), host_dest_native.to_str().unwrap(),
]) ])
.output() .output()
.unwrap(); .unwrap();
@ -287,7 +342,7 @@ pub fn rebuild_host(host_input_path: &Path) {
// Clean up c_host.o // Clean up c_host.o
let output = Command::new("mv") let output = Command::new("mv")
.env_clear() .env_clear()
.args(&[c_host_dest, host_dest]) .args(&[c_host_dest, host_dest_native])
.output() .output()
.unwrap(); .unwrap();
@ -501,13 +556,33 @@ fn link_macos(
} }
fn link_wasm32( fn link_wasm32(
target: &Triple, _target: &Triple,
output_path: PathBuf, output_path: PathBuf,
input_paths: &[&str], input_paths: &[&str],
link_type: LinkType, _link_type: LinkType,
) -> io::Result<(Child, PathBuf)> { ) -> io::Result<(Child, PathBuf)> {
dbg!(target, output_path, input_paths, link_type); let zig_str_path = find_zig_str_path();
panic!()
let child =
Command::new("/home/folkertdev/Downloads/zig-linux-x86_64-0.9.0-dev.848+d5ef5da59/zig")
// .env_clear()
// .env("PATH", &env_path)
.args(&["build-exe"])
.args(input_paths)
.args([
&format!("-femit-bin={}", output_path.to_str().unwrap()),
// include libc
"-lc",
"-target",
"wasm32-wasi",
"--pkg-begin",
"str",
zig_str_path.to_str().unwrap(),
"--pkg-end",
])
.spawn()?;
Ok((child, output_path))
} }
#[cfg(feature = "llvm")] #[cfg(feature = "llvm")]

View file

@ -251,16 +251,28 @@ pub fn gen_from_mono_module(
}); });
} else { } else {
// Emit the .o file // Emit the .o file
use target_lexicon::Architecture;
match target.architecture {
Architecture::X86_64 | Architecture::Aarch64(_) => {
let reloc = RelocMode::Default; let reloc = RelocMode::Default;
let model = CodeModel::Default; let model = CodeModel::Default;
let target_machine = let target_machine =
target::target_machine(&target, convert_opt_level(opt_level), reloc, model).unwrap(); target::target_machine(target, convert_opt_level(opt_level), reloc, model)
.unwrap();
target_machine target_machine
.write_to_file(env.module, FileType::Object, app_o_file) .write_to_file(env.module, FileType::Object, app_o_file)
.expect("Writing .o file failed"); .expect("Writing .o file failed");
} }
Architecture::Wasm32 => {
module.write_bitcode_to_path(app_o_file);
}
_ => panic!(
"TODO gracefully handle unsupported architecture: {:?}",
target.architecture
),
}
}
let emit_o_file = emit_o_file_start.elapsed().unwrap(); let emit_o_file = emit_o_file_start.elapsed().unwrap();

View file

@ -14,8 +14,6 @@ const InPlace = enum(u8) {
Clone, Clone,
}; };
const HEL = [_]u8{ 'h', 'e', 'l' };
const SMALL_STR_MAX_LENGTH = small_string_size - 1; const SMALL_STR_MAX_LENGTH = small_string_size - 1;
const small_string_size = 2 * @sizeOf(usize); const small_string_size = 2 * @sizeOf(usize);
const blank_small_string: [@sizeOf(RocStr)]u8 = init_blank_small_string(small_string_size); const blank_small_string: [@sizeOf(RocStr)]u8 = init_blank_small_string(small_string_size);

View file

@ -30,22 +30,31 @@ extern fn roc__mainForHost_1_Fx_size() i64;
extern fn roc__mainForHost_1_Fx_result_size() i64; extern fn roc__mainForHost_1_Fx_result_size() i64;
extern fn malloc(size: usize) callconv(.C) ?*c_void; extern fn malloc(size: usize) callconv(.C) ?*c_void;
extern fn realloc(c_ptr: [*]align(@alignOf(u128)) u8, size: usize) callconv(.C) ?*c_void; extern fn realloc(c_ptr: [*]align(@alignOf(usize)) u8, size: usize) callconv(.C) ?*c_void;
extern fn free(c_ptr: [*]align(@alignOf(u128)) u8) callconv(.C) void; extern fn free(c_ptr: [*]align(@alignOf(usize)) u8) callconv(.C) void;
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void { export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void {
_ = alignment;
return malloc(size); return malloc(size);
} }
export fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void { export fn roc_realloc(c_ptr: *c_void, old_size: usize, new_size: usize, alignment: u32) callconv(.C) ?*c_void {
return realloc(@alignCast(16, @ptrCast([*]u8, c_ptr)), new_size); _ = old_size;
_ = alignment;
return realloc(@alignCast(@alignOf(usize), @ptrCast([*]u8, c_ptr)), new_size);
} }
export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void { export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
free(@alignCast(16, @ptrCast([*]u8, c_ptr))); _ = alignment;
const ptr = @alignCast(@alignOf(usize), @ptrCast([*]u8, c_ptr));
free(ptr);
} }
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void { export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
_ = tag_id;
const stderr = std.io.getStdErr().writer(); const stderr = std.io.getStdErr().writer();
const msg = @ptrCast([*:0]const u8, c_ptr); const msg = @ptrCast([*:0]const u8, c_ptr);
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable; stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
@ -54,34 +63,25 @@ export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
const Unit = extern struct {}; const Unit = extern struct {};
pub export fn main() u8 { const RocCallResult = extern struct { flag: u64, ptr: usize };
const stdout = std.io.getStdOut().writer();
const stderr = std.io.getStdErr().writer();
const size = @intCast(usize, roc__mainForHost_size()); pub fn main() u8 {
const raw_output = std.heap.c_allocator.alloc(u8, size) catch unreachable; var output = RocCallResult{ .flag = 0, .ptr = 0 };
var output = @ptrCast([*]u8, raw_output);
defer {
std.heap.c_allocator.free(raw_output);
}
var ts1: std.os.timespec = undefined; var ts1: std.os.timespec = undefined;
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts1) catch unreachable; std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts1) catch unreachable;
roc__mainForHost_1_exposed(output); roc__mainForHost_1_exposed(@ptrCast([*]u8, &output));
const elements = @ptrCast([*]u64, @alignCast(8, output)); if (output.flag == 0) {
var flag = elements[0];
if (flag == 0) {
// all is well // all is well
const closure_data_pointer = @ptrCast([*]u8, output[8..size]); const closure_data_pointer = @intToPtr([*]u8, output.ptr);
call_the_closure(closure_data_pointer); call_the_closure(closure_data_pointer);
} else { } else {
const msg = @intToPtr([*:0]const u8, elements[1]); const stderr = std.io.getStdErr().writer();
const msg = @intToPtr([*:0]const u8, @intCast(usize, output.ptr));
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable; stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
return 0; return 0;
@ -92,6 +92,8 @@ pub export fn main() u8 {
const delta = to_seconds(ts2) - to_seconds(ts1); const delta = to_seconds(ts2) - to_seconds(ts1);
// const stderr = std.io.getStdOut().writer();
const stderr = std.io.getStdErr().writer();
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable; stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
return 0; return 0;
@ -103,7 +105,7 @@ fn to_seconds(tms: std.os.timespec) f64 {
fn call_the_closure(closure_data_pointer: [*]u8) void { fn call_the_closure(closure_data_pointer: [*]u8) void {
const size = roc__mainForHost_1_Fx_result_size(); const size = roc__mainForHost_1_Fx_result_size();
const raw_output = std.heap.c_allocator.alloc(u8, @intCast(usize, size)) catch unreachable; const raw_output = std.heap.c_allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable;
var output = @ptrCast([*]u8, raw_output); var output = @ptrCast([*]u8, raw_output);
defer { defer {