mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
proof of concept
This commit is contained in:
parent
1f5e5bdc16
commit
72e6a34a0d
4 changed files with 939 additions and 36 deletions
738
Cargo.lock
generated
738
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -34,8 +34,8 @@ target-lexicon = "0.12.2"
|
||||||
libloading = "0.6"
|
libloading = "0.6"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
wasmer = "2.0.0"
|
||||||
|
wasmer-wasi = "2.0.0"
|
||||||
maplit = "1.0.1"
|
maplit = "1.0.1"
|
||||||
quickcheck = "0.8"
|
quickcheck = "0.8"
|
||||||
quickcheck_macros = "0.8"
|
quickcheck_macros = "0.8"
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use crate::assert_evals_to;
|
use crate::assert_evals_to;
|
||||||
use crate::assert_llvm_evals_to;
|
use crate::assert_llvm_evals_to;
|
||||||
use crate::assert_non_opt_evals_to;
|
use crate::assert_non_opt_evals_to;
|
||||||
|
use crate::helpers::eval;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use roc_std::RocStr;
|
use roc_std::RocStr;
|
||||||
|
|
||||||
|
@ -2778,3 +2779,33 @@ fn value_not_exposed_hits_panic() {
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wasm_test() {
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use inkwell::context::Context;
|
||||||
|
|
||||||
|
let arena = Bump::new();
|
||||||
|
let context = Context::create();
|
||||||
|
|
||||||
|
// NOTE the stdlib must be in the arena; just taking a reference will segfault
|
||||||
|
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||||
|
|
||||||
|
let source = indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
main : I32
|
||||||
|
main = 32
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
// src: &str,
|
||||||
|
// stdlib: &'a roc_builtins::std::StdLib,
|
||||||
|
// is_gen_test: bool,
|
||||||
|
// ignore_problems: bool,
|
||||||
|
// context: &'a inkwell::context::Context,
|
||||||
|
|
||||||
|
let arena = bumpalo::Bump::new();
|
||||||
|
eval::helper_wasm(&arena, source, stdlib, true, false, &context);
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use inkwell::module::Module;
|
||||||
use libloading::Library;
|
use libloading::Library;
|
||||||
use roc_build::link::module_to_dylib;
|
use roc_build::link::module_to_dylib;
|
||||||
use roc_build::program::FunctionIterator;
|
use roc_build::program::FunctionIterator;
|
||||||
|
@ -8,6 +9,7 @@ use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::ir::OptLevel;
|
use roc_mono::ir::OptLevel;
|
||||||
use roc_types::subs::VarStore;
|
use roc_types::subs::VarStore;
|
||||||
|
use target_lexicon::Triple;
|
||||||
|
|
||||||
fn promote_expr_to_module(src: &str) -> String {
|
fn promote_expr_to_module(src: &str) -> String {
|
||||||
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
||||||
|
@ -27,16 +29,17 @@ pub fn test_builtin_defs(symbol: Symbol, var_store: &mut VarStore) -> Option<Def
|
||||||
|
|
||||||
// this is not actually dead code, but only used by cfg_test modules
|
// this is not actually dead code, but only used by cfg_test modules
|
||||||
// so "normally" it is dead, only at testing time is it used
|
// so "normally" it is dead, only at testing time is it used
|
||||||
#[allow(dead_code)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[inline(never)]
|
fn create_llvm_module<'a>(
|
||||||
pub fn helper<'a>(
|
|
||||||
arena: &'a bumpalo::Bump,
|
arena: &'a bumpalo::Bump,
|
||||||
src: &str,
|
src: &str,
|
||||||
stdlib: &'a roc_builtins::std::StdLib,
|
stdlib: &'a roc_builtins::std::StdLib,
|
||||||
is_gen_test: bool,
|
is_gen_test: bool,
|
||||||
ignore_problems: bool,
|
ignore_problems: bool,
|
||||||
context: &'a inkwell::context::Context,
|
context: &'a inkwell::context::Context,
|
||||||
) -> (&'static str, String, Library) {
|
target: &Triple,
|
||||||
|
opt_level: OptLevel,
|
||||||
|
) -> (&'static str, String, &'a Module<'a>) {
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
let filename = PathBuf::from("Test.roc");
|
let filename = PathBuf::from("Test.roc");
|
||||||
|
@ -53,7 +56,6 @@ pub fn helper<'a>(
|
||||||
module_src = &temp;
|
module_src = &temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = target_lexicon::Triple::host();
|
|
||||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||||
|
|
||||||
let exposed_types = MutMap::default();
|
let exposed_types = MutMap::default();
|
||||||
|
@ -171,12 +173,6 @@ pub fn helper<'a>(
|
||||||
let builder = context.create_builder();
|
let builder = context.create_builder();
|
||||||
let module = roc_gen_llvm::llvm::build::module_from_builtins(context, "app", ptr_bytes);
|
let module = roc_gen_llvm::llvm::build::module_from_builtins(context, "app", ptr_bytes);
|
||||||
|
|
||||||
let opt_level = if cfg!(debug_assertions) {
|
|
||||||
OptLevel::Normal
|
|
||||||
} else {
|
|
||||||
OptLevel::Optimize
|
|
||||||
};
|
|
||||||
|
|
||||||
let module = arena.alloc(module);
|
let module = arena.alloc(module);
|
||||||
let (module_pass, function_pass) =
|
let (module_pass, function_pass) =
|
||||||
roc_gen_llvm::llvm::build::construct_optimization_passes(module, opt_level);
|
roc_gen_llvm::llvm::build::construct_optimization_passes(module, opt_level);
|
||||||
|
@ -255,10 +251,188 @@ pub fn helper<'a>(
|
||||||
// Uncomment this to see the module's optimized LLVM instruction output:
|
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||||
// env.module.print_to_stderr();
|
// env.module.print_to_stderr();
|
||||||
|
|
||||||
let lib = module_to_dylib(env.module, &target, opt_level)
|
(main_fn_name, delayed_errors.join("\n"), env.module)
|
||||||
.expect("Error loading compiled dylib for test");
|
}
|
||||||
|
|
||||||
(main_fn_name, delayed_errors.join("\n"), lib)
|
#[allow(dead_code)]
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn helper<'a>(
|
||||||
|
arena: &'a bumpalo::Bump,
|
||||||
|
src: &str,
|
||||||
|
stdlib: &'a roc_builtins::std::StdLib,
|
||||||
|
is_gen_test: bool,
|
||||||
|
ignore_problems: bool,
|
||||||
|
context: &'a inkwell::context::Context,
|
||||||
|
) -> (&'static str, String, Library) {
|
||||||
|
let target = target_lexicon::Triple::host();
|
||||||
|
|
||||||
|
let opt_level = if cfg!(debug_assertions) {
|
||||||
|
OptLevel::Normal
|
||||||
|
} else {
|
||||||
|
OptLevel::Optimize
|
||||||
|
};
|
||||||
|
|
||||||
|
let (main_fn_name, delayed_errors, module) = create_llvm_module(
|
||||||
|
arena,
|
||||||
|
src,
|
||||||
|
stdlib,
|
||||||
|
is_gen_test,
|
||||||
|
ignore_problems,
|
||||||
|
context,
|
||||||
|
&target,
|
||||||
|
opt_level,
|
||||||
|
);
|
||||||
|
|
||||||
|
let lib =
|
||||||
|
module_to_dylib(module, &target, opt_level).expect("Error loading compiled dylib for test");
|
||||||
|
|
||||||
|
(main_fn_name, delayed_errors, lib)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn wasm32_target_tripple() -> Triple {
|
||||||
|
use target_lexicon::{Architecture, BinaryFormat};
|
||||||
|
|
||||||
|
let mut triple = Triple::unknown();
|
||||||
|
|
||||||
|
triple.architecture = Architecture::Wasm32;
|
||||||
|
triple.binary_format = BinaryFormat::Wasm;
|
||||||
|
|
||||||
|
triple
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn helper_wasm<'a>(
|
||||||
|
arena: &'a bumpalo::Bump,
|
||||||
|
src: &str,
|
||||||
|
stdlib: &'a roc_builtins::std::StdLib,
|
||||||
|
is_gen_test: bool,
|
||||||
|
ignore_problems: bool,
|
||||||
|
context: &'a inkwell::context::Context,
|
||||||
|
) -> (&'static str, String, Library) {
|
||||||
|
let target = wasm32_target_tripple();
|
||||||
|
|
||||||
|
let opt_level = if cfg!(debug_assertions) {
|
||||||
|
OptLevel::Normal
|
||||||
|
} else {
|
||||||
|
OptLevel::Optimize
|
||||||
|
};
|
||||||
|
|
||||||
|
let (main_fn_name, delayed_errors, llvm_module) = create_llvm_module(
|
||||||
|
arena,
|
||||||
|
src,
|
||||||
|
stdlib,
|
||||||
|
is_gen_test,
|
||||||
|
ignore_problems,
|
||||||
|
context,
|
||||||
|
&target,
|
||||||
|
opt_level,
|
||||||
|
);
|
||||||
|
|
||||||
|
use inkwell::targets::{InitializationConfig, Target, TargetTriple};
|
||||||
|
|
||||||
|
Target::initialize_webassembly(&InitializationConfig::default());
|
||||||
|
// let target = Target::from_name("wasm32-unknown-unknown").unwrap();
|
||||||
|
|
||||||
|
// let triple = TargetTriple::create("wasm32-wasi");
|
||||||
|
let triple = TargetTriple::create("wasm32-wasi");
|
||||||
|
let target_machine = Target::from_triple(&triple)
|
||||||
|
.unwrap()
|
||||||
|
.create_target_machine(
|
||||||
|
&triple,
|
||||||
|
"generic",
|
||||||
|
"", // TODO: this probably should be TargetMachine::get_host_cpu_features() to enable all features.
|
||||||
|
inkwell::OptimizationLevel::None,
|
||||||
|
inkwell::targets::RelocMode::PIC,
|
||||||
|
inkwell::targets::CodeModel::Default,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// let file_type = inkwell::targets::FileType::Object;
|
||||||
|
// let bytes = target_machine
|
||||||
|
// .write_to_file(
|
||||||
|
// llvm_module,
|
||||||
|
// file_type,
|
||||||
|
// std::path::Path::new("/home/folkertdev/roc/roc/test.wasm"),
|
||||||
|
// )
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// let file_type = inkwell::targets::FileType::Object;
|
||||||
|
// let bytes = target_machine
|
||||||
|
// .write_to_memory_buffer(llvm_module, file_type)
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
{
|
||||||
|
use wasmer::{imports, Function, Instance, Module, Store, Value};
|
||||||
|
|
||||||
|
let store = Store::default();
|
||||||
|
// let module = Module::new(&store, &module_wat).unwrap();
|
||||||
|
let module = Module::from_file(&store, "/home/folkertdev/roc/wasm/main.wasm").unwrap();
|
||||||
|
// The module doesn't import anything, so we create an empty import object.
|
||||||
|
// let import_object = imports! {
|
||||||
|
// "wasi_snapshot_preview1" => {
|
||||||
|
// "proc_exit" => Function::new_native(&store, foo),
|
||||||
|
// "args_get" => Function::new_native(&store, bar),
|
||||||
|
// "args_sizes_get" => Function::new_native(&store, bar),
|
||||||
|
// "environ_get" => Function::new_native(&store, bar),
|
||||||
|
// "environ_sizes_get" => Function::new_native(&store, bar),
|
||||||
|
// "clock_res_get" => Function::new_native(&store, bar),
|
||||||
|
// },
|
||||||
|
// "env" => {
|
||||||
|
// "main" => Function::new_native(&store, bar)
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
|
||||||
|
// First, we create the `WasiEnv`
|
||||||
|
use wasmer_wasi::WasiState;
|
||||||
|
let mut wasi_env = WasiState::new("hello")
|
||||||
|
// .args(&["world"])
|
||||||
|
// .env("KEY", "Value")
|
||||||
|
.finalize()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("Instantiating module with WASI imports...");
|
||||||
|
// Then, we get the import object related to our WASI
|
||||||
|
// and attach it to the Wasm instance.
|
||||||
|
let mut import_object = wasi_env.import_object(&module).unwrap();
|
||||||
|
|
||||||
|
let main_function = Function::new_native(&store, bar);
|
||||||
|
let ext = wasmer::Extern::Function(main_function);
|
||||||
|
let mut exts = wasmer::Exports::new();
|
||||||
|
exts.insert("main", ext);
|
||||||
|
import_object.register("env", exts);
|
||||||
|
|
||||||
|
let instance = Instance::new(&module, &import_object).unwrap();
|
||||||
|
|
||||||
|
let memory = instance.exports.get_memory("memory").unwrap();
|
||||||
|
|
||||||
|
let add_one = instance.exports.get_function("test_wrapper").unwrap();
|
||||||
|
let result = add_one.call(&[]).unwrap();
|
||||||
|
let address = match result[0] {
|
||||||
|
Value::I32(a) => a,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ptr: wasmer::WasmPtr<i32, wasmer::Item> = wasmer::WasmPtr::new(address as u32 + 8);
|
||||||
|
dbg!(ptr.deref(&memory));
|
||||||
|
assert_eq!(result[0], Value::I32(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar(_: u32, _: u32) -> u32 {
|
||||||
|
println!("we are in main!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(value: u32) {
|
||||||
|
println!("value: {}", value);
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn roc__verify(_: u32) {
|
||||||
|
panic!("verify went wrong");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue