From 44b8d0b1ee4efe198d9ca200e6210c31a7a973ae Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Mon, 25 Feb 2019 18:07:22 -0800 Subject: [PATCH] Start work on object loader using llvm's RuntimeDyld api. --- Cargo.lock | 30 ++++ lib/llvm-backend/Cargo.toml | 8 + lib/llvm-backend/build.rs | 203 +++++++++++++++++++++++++ lib/llvm-backend/cpp/object_loader.cpp | 96 ++++++++++++ lib/llvm-backend/cpp/object_loader.hh | 40 +++++ lib/llvm-backend/src/backend.rs | 79 +++++++--- lib/llvm-backend/src/code.rs | 52 +++++-- lib/llvm-backend/src/intrinsics.rs | 6 +- lib/llvm-backend/src/lib.rs | 177 ++++++++++----------- 9 files changed, 569 insertions(+), 122 deletions(-) create mode 100644 lib/llvm-backend/build.rs create mode 100644 lib/llvm-backend/cpp/object_loader.cpp create mode 100644 lib/llvm-backend/cpp/object_loader.hh diff --git a/Cargo.lock b/Cargo.lock index ca2129890..9d743e96c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -262,6 +262,28 @@ dependencies = [ "generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dlopen" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dlopen_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dlopen_derive" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "either" version = "1.5.1" @@ -469,10 +491,16 @@ dependencies = [ name = "llvm-backend" version = "0.1.0" dependencies = [ + "cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "dlopen 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "goblin 0.0.20 (registry+https://github.com/rust-lang/crates.io-index)", "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm7-0)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-runtime-core 0.1.2", "wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1362,6 +1390,8 @@ dependencies = [ "checksum cranelift-native 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "474bee81d620a473bf43411a3d6f10ffbf7965141dc5e5b76d8d2151dde3285d" "checksum cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49723365dab9a48b354bdc24cb6d9d5719bc1d3b858ffd2ea179d0d7d885804a" "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" +"checksum dlopen 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8c301a18a3404a48d5d078e86b011ac834a6c4c742217e9aa8ceadd8a7e09e0" +"checksum dlopen_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6e16e4e343d6090ba47113c55bf6aa94e9b1eb5ab0c5abc9510d4c15074f30e9" "checksum either 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c67353c641dc847124ea1902d69bd753dee9bb3beff9aa3662ecf86c971d1fac" "checksum enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7798e7da2d4cb0d6d6fc467e8d6b5bf247e9e989f786dde1732d79899c32bb10" "checksum env_logger 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "afb070faf94c85d17d50ca44f6ad076bce18ae92f0037d350947240a36e9d42e" diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index 8ffac375f..3ec83d77c 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -11,6 +11,14 @@ inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm7-0" } hashbrown = "0.1.8" smallvec = "0.6.8" goblin = "0.0.20" +dlopen = "0.1.6" +tempfile = "3.0.7" + +[build-dependencies] +cc = "1.0" +lazy_static = "1.2.0" +regex = "1.1.0" +semver = "0.9" [dev-dependencies] wabt = "0.7.4" diff --git a/lib/llvm-backend/build.rs b/lib/llvm-backend/build.rs new file mode 100644 index 000000000..e38cec034 --- /dev/null +++ b/lib/llvm-backend/build.rs @@ -0,0 +1,203 @@ +//! This file was mostly taken from the llvm-sys crate. +//! (https://bitbucket.org/tari/llvm-sys.rs/src/21ab524ec4df1450035df895209c3f8fbeb8775f/build.rs?at=default&fileviewer=file-view-default) + +use regex::Regex; +use lazy_static::lazy_static; +use semver::Version; +use std::env; +use std::ffi::OsStr; +use std::io::{self, ErrorKind}; +use std::path::PathBuf; +use std::process::Command; + +lazy_static! { + /// LLVM version used by this version of the crate. + static ref CRATE_VERSION: Version = { + let crate_version = Version::parse(env!("CARGO_PKG_VERSION")) + .expect("Crate version is somehow not valid semver"); + Version { + major: crate_version.major / 10, + minor: crate_version.major % 10, + .. crate_version + } + }; + + static ref LLVM_CONFIG_BINARY_NAMES: Vec = { + vec![ + "llvm-config".into(), + // format!("llvm-config-{}", CRATE_VERSION.major), + // format!("llvm-config-{}.{}", CRATE_VERSION.major, CRATE_VERSION.minor), + ] + }; + + /// Filesystem path to an llvm-config binary for the correct version. + static ref LLVM_CONFIG_PATH: PathBuf = { + // Try llvm-config via PATH first. + if let Some(name) = locate_system_llvm_config() { + return name.into(); + } else { + println!("Didn't find usable system-wide LLVM."); + } + + // Did the user give us a binary path to use? If yes, try + // to use that and fail if it doesn't work. + let binary_prefix_var = "LLVM_SYS_70_PREFIX"; + + let path = if let Some(path) = env::var_os(&binary_prefix_var) { + Some(path.to_str().unwrap().to_owned()) + } else if let Ok(mut file) = std::fs::File::open(".llvmenv") { + use std::io::Read; + let mut s = String::new(); + file.read_to_string(&mut s).unwrap(); + Some(s) + } else { + None + }; + + if let Some(path) = path { + for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() { + let mut pb: PathBuf = path.clone().into(); + pb.push("bin"); + pb.push(binary_name); + + let ver = llvm_version(&pb) + .expect(&format!("Failed to execute {:?}", &pb)); + if is_compatible_llvm(&ver) { + return pb; + } else { + println!("LLVM binaries specified by {} are the wrong version. + (Found {}, need {}.)", binary_prefix_var, ver, *CRATE_VERSION); + } + } + } + + println!("No suitable version of LLVM was found system-wide or pointed + to by {}. + + Consider using `llvmenv` to compile an appropriate copy of LLVM, and + refer to the llvm-sys documentation for more information. + + llvm-sys: https://crates.io/crates/llvm-sys + llvmenv: https://crates.io/crates/llvmenv", binary_prefix_var); + panic!("Could not find a compatible version of LLVM"); + }; +} + +/// Try to find a system-wide version of llvm-config that is compatible with +/// this crate. +/// +/// Returns None on failure. +fn locate_system_llvm_config() -> Option<&'static str> { + for binary_name in LLVM_CONFIG_BINARY_NAMES.iter() { + match llvm_version(binary_name) { + Ok(ref version) if is_compatible_llvm(version) => { + // Compatible version found. Nice. + return Some(binary_name); + } + Ok(version) => { + // Version mismatch. Will try further searches, but warn that + // we're not using the system one. + println!("Found LLVM version {} on PATH, but need {}.", + version, + *CRATE_VERSION); + } + Err(ref e) if e.kind() == ErrorKind::NotFound => { + // Looks like we failed to execute any llvm-config. Keep + // searching. + } + // Some other error, probably a weird failure. Give up. + Err(e) => panic!("Failed to search PATH for llvm-config: {}", e), + } + } + + None +} + +/// Check whether the given LLVM version is compatible with this version of +/// the crate. +fn is_compatible_llvm(llvm_version: &Version) -> bool { + let strict = env::var_os(format!("LLVM_SYS_{}_STRICT_VERSIONING", + env!("CARGO_PKG_VERSION_MAJOR"))) + .is_some() || cfg!(feature = "strict-versioning"); + if strict { + llvm_version.major == CRATE_VERSION.major && llvm_version.minor == CRATE_VERSION.minor + } else { + llvm_version.major >= CRATE_VERSION.major || + (llvm_version.major == CRATE_VERSION.major && llvm_version.minor >= CRATE_VERSION.minor) + } +} + +/// Get the output from running `llvm-config` with the given argument. +/// +/// Lazily searches for or compiles LLVM as configured by the environment +/// variables. +fn llvm_config(arg: &str) -> String { + llvm_config_ex(&*LLVM_CONFIG_PATH, arg).expect("Surprising failure from llvm-config") +} + +/// Invoke the specified binary as llvm-config. +/// +/// Explicit version of the `llvm_config` function that bubbles errors +/// up. +fn llvm_config_ex>(binary: S, arg: &str) -> io::Result { + Command::new(binary) + .arg(arg) + .arg("--link-static") // Don't use dylib for >= 3.9 + .output() + .map(|output| String::from_utf8(output.stdout) + .expect("Output from llvm-config was not valid UTF-8")) +} + +/// Get the LLVM version using llvm-config. +fn llvm_version>(binary: S) -> io::Result { + let version_str = llvm_config_ex(binary.as_ref(), "--version")?; + + // LLVM isn't really semver and uses version suffixes to build + // version strings like '3.8.0svn', so limit what we try to parse + // to only the numeric bits. + let re = Regex::new(r"^(?P\d+)\.(?P\d+)(?:\.(?P\d+))??").unwrap(); + let c = re.captures(&version_str).expect("Could not determine LLVM version from llvm-config."); + + // some systems don't have a patch number but Version wants it so we just append .0 if it isn't + // there + let s = match c.name("patch") { + None => format!("{}.0", &c[0]), + Some(_) => c[0].to_string(), + }; + Ok(Version::parse(&s).unwrap()) +} + +fn get_llvm_cxxflags() -> String { + let output = llvm_config("--cxxflags"); + + // llvm-config includes cflags from its own compilation with --cflags that + // may not be relevant to us. In particularly annoying cases, these might + // include flags that aren't understood by the default compiler we're + // using. Unless requested otherwise, clean CFLAGS of options that are + // known to be possibly-harmful. + let no_clean = env::var_os(format!("LLVM_SYS_{}_NO_CLEAN_CFLAGS", + env!("CARGO_PKG_VERSION_MAJOR"))) + .is_some(); + if no_clean || cfg!(target_env = "msvc") { + // MSVC doesn't accept -W... options, so don't try to strip them and + // possibly strip something that should be retained. Also do nothing if + // the user requests it. + return output; + } + + output + .split(&[' ', '\n'][..]) + .filter(|word| !word.starts_with("-W")) + .collect::>() + .join(" ") +} + +fn main() { + std::env::set_var("CXXFLAGS", get_llvm_cxxflags()); + cc::Build::new() + .cpp(true) + .file("cpp/object_loader.cpp") + .compile("llvm-backend"); + + println!("cargo:rustc-link-lib=static=llvm-backend"); +} \ No newline at end of file diff --git a/lib/llvm-backend/cpp/object_loader.cpp b/lib/llvm-backend/cpp/object_loader.cpp new file mode 100644 index 000000000..9ab36f4c3 --- /dev/null +++ b/lib/llvm-backend/cpp/object_loader.cpp @@ -0,0 +1,96 @@ +#include "object_loader.hh" +#include +#include + +class MemoryManager : llvm::RuntimeDyld::MemoryManager { +public: + MemoryManager() {} + + virtual ~MemoryManager() {} + + virtual uint8_t* allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, llvm::StringRef SectionName) override { + return nullptr; + } + + virtual uint8_t* allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, llvm::StringRef SectionName, bool isReadOnly) override { + return nullptr; + } + + virtual void reserveAllocationSpace( + uintptr_t code_size, + uint32_t code_align, + uintptr_t read_data_size, + uint32_t read_data_align, + uintptr_t read_write_data_size, + uint32_t read_write_data_align + ) override { + + } + + /* Turn on the `reserveAllocationSpace` callback. */ + virtual bool needsToReserveAllocationSpace() override { + return true; + } + + virtual void registerEHFrames(uint8_t* Addr, uint64_t LoadAddr, size_t Size) override { + + } + + virtual void deregisterEHFrames() override { + + } + + virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override { + + + return false; + } + + virtual void notifyObjectLoaded(llvm::RuntimeDyld &RTDyld, const llvm::object::ObjectFile &Obj) override { + + } +private: + struct Section { + uint8_t* base; + size_t num_pages; + size_t num_commited_bytes; + }; + + uint8_t *image_base; + size_t num_allocated_pages; + + Section code_section, read_section, readwrite_section; + + +}; + +class SymbolLookup : llvm::JITSymbolResolver { +public: + virtual llvm::Expected lookup(const LookupSet& symbols) override { + LookupResult result; + + for (auto symbol : symbols) { + result.emplace(symbol, symbol_lookup(symbol)); + } + + return result; + } + + virtual llvm::Expected lookupFlags(const LookupSet& symbols) override { + LookupFlagsResult result; + + for (auto symbol : symbols) { + result.emplace(symbol, symbol_lookup(symbol).getFlags()); + } + + return result; + } + +private: + llvm::JITEvaluatedSymbol symbol_lookup(llvm::StringRef name) { + std::cout << "symbol name: " << (std::string)name << std::endl; + uint64_t addr = 0; + + return llvm::JITEvaluatedSymbol(addr, llvm::JITSymbolFlags::None); + } +}; \ No newline at end of file diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh new file mode 100644 index 000000000..50b618b38 --- /dev/null +++ b/lib/llvm-backend/cpp/object_loader.hh @@ -0,0 +1,40 @@ +#include +#include + +typedef enum { + PROTECT_NONE, + PROTECT_READ, + PROTECT_READ_WRITE, + PROTECT_READ_EXECUTE, +} mem_protect_t; + +typedef enum { + RESULT_OK, + RESULT_ALLOCATE_FAILURE, + RESULT_PROTECT_FAILURE, + RESULT_DEALLOC_FAILURE, + RESULT_OBJECT_LOAD_FAILURE, +} result_t; + +typedef result_t (*alloc_memory_t)(size_t size, mem_protect_t protect, uint8_t** ptr_out, size_t* size_out); +typedef result_t (*protect_memory_t)(uint8_t* ptr, size_t size, mem_protect_t protect); +typedef result_t (*dealloc_memory_t)(uint8_t* ptr, size_t size); +typedef uintptr_t (*lookup_vm_symbol_t)(char* name_ptr, size_t name_size); + +typedef struct { + /* Memory management. */ + alloc_memory_t alloc_memory; + protect_memory_t protect_memory; + dealloc_memory_t dealloc_memory; + + lookup_vm_symbol_t lookup_vm_symbol; +} callbacks_t; + +extern "C" { + result_t object_load(uint8_t* mem_ptr, size_t mem_size, callbacks_t* callbacks) { + return RESULT_OK; + } + + void test_cpp() { + } +} \ No newline at end of file diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index 9821eabf6..2b4bb7023 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -1,45 +1,76 @@ +use crate::intrinsics::Intrinsics; +use dlopen::symbor::Library; use inkwell::{ module::Module, - execution_engine::{ExecutionEngine, JitFunction}, + targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine}, + OptimizationLevel, }; -use crate::intrinsics::Intrinsics; -use std::ptr::NonNull; +use std::{io::Write, ptr::NonNull}; +use tempfile::NamedTempFile; use wasmer_runtime_core::{ - module::ModuleInner, - types::LocalFuncIndex, - structures::TypedIndex, - backend::{FuncResolver, vm}, + backend::FuncResolver, module::ModuleInner, structures::TypedIndex, types::LocalFuncIndex, vm, }; pub struct LLVMBackend { - exec_engine: ExecutionEngine, + tempfile: NamedTempFile, + library: Library, } impl LLVMBackend { pub fn new(module: Module, intrinsics: Intrinsics) -> Self { - let exec_engine = module.create_jit_execution_engine(OptimizationLevel::Default).unwrap(); + Target::initialize_x86(&InitializationConfig { + asm_parser: true, + asm_printer: true, + base: true, + disassembler: true, + info: true, + machine_code: true, + }); + let triple = TargetMachine::get_default_triple().to_string(); + let target = Target::from_triple(&triple).unwrap(); + let target_machine = target + .create_target_machine( + &triple, + &TargetMachine::get_host_cpu_name().to_string(), + &TargetMachine::get_host_cpu_features().to_string(), + OptimizationLevel::Default, + RelocMode::PIC, + CodeModel::Default, + ) + .unwrap(); - exec_engine.add_global_mapping(&intrinsics.memory_grow_dynamic_local, vmcalls::local_dynamic_memory_grow as usize); - exec_engine.add_global_mapping(&intrinsics.memory_grow_static_local, vmcalls::local_static_memory_grow as usize); - exec_engine.add_global_mapping(&intrinsics.memory_grow_dynamic_import, vmcalls::imported_dynamic_memory_grow as usize); - exec_engine.add_global_mapping(&intrinsics.memory_grow_static_import, vmcalls::imported_static_memory_grow as usize); - exec_engine.add_global_mapping(&intrinsics.memory_size_dynamic_local, vmcalls::local_dynamic_memory_size as usize); - exec_engine.add_global_mapping(&intrinsics.memory_size_static_local, vmcalls::local_static_memory_size as usize); - exec_engine.add_global_mapping(&intrinsics.memory_size_dynamic_import, vmcalls::imported_dynamic_memory_size as usize); - exec_engine.add_global_mapping(&intrinsics.memory_size_static_import, vmcalls::imported_static_memory_size as usize); + let memory_buffer = target_machine + .write_to_memory_buffer(&module, FileType::Object) + .unwrap(); - Self { exec_engine } + let mut tempfile = NamedTempFile::new().unwrap(); + tempfile.write_all(memory_buffer.as_slice()).unwrap(); + tempfile.flush().unwrap(); + + let library = Library::open(tempfile.path()).unwrap(); + + Self { tempfile, library } } } impl FuncResolver for LLVMBackend { - fn get(&self, module: &ModuleInner, local_func_index: LocalFuncIndex) -> Option> { + fn get( + &self, + module: &ModuleInner, + local_func_index: LocalFuncIndex, + ) -> Option> { let index = module.info.imported_functions.len() + local_func_index.index(); - let name = format!("fn{}", index); - + let name = if cfg!(macos) { + format!("_fn{}", index) + } else { + format!("fn{}", index) + }; + unsafe { - let func: JitFunction = self.exec_engine.get_function(&name).ok()?; - + self.library + .symbol::>(&name) + .ok() + .map(|symbol| *symbol) } } -} \ No newline at end of file +} diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index d72659fd9..e0f4d984f 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -481,6 +481,7 @@ fn parse_function( // Emit an unreachable instruction. // If llvm cannot prove that this is never touched, // it will emit a `ud2` instruction on x86_64 arches. + ctx.build_trap(); builder.build_unreachable(); state.reachable = false; } @@ -623,7 +624,8 @@ fn parse_function( } } Operator::CallIndirect { index, table_index } => { - let expected_dynamic_sigindex = ctx.dynamic_sigindex(SigIndex::new(index as usize)); + let sig_index = SigIndex::new(index as usize); + let expected_dynamic_sigindex = ctx.dynamic_sigindex(sig_index); let (table_base, table_bound) = ctx.table(TableIndex::new(table_index as usize)); let func_index = state.pop1()?.into_int_value(); @@ -651,12 +653,10 @@ fn parse_function( "func_ptr", ) .into_pointer_value(), - builder - .build_load( - builder.build_struct_gep(anyfunc_struct_ptr, 1, "ctx_ptr_ptr"), - "ctx_ptr", - ) - .into_pointer_value(), + builder.build_load( + builder.build_struct_gep(anyfunc_struct_ptr, 1, "ctx_ptr_ptr"), + "ctx_ptr", + ), builder .build_load( builder.build_struct_gep(anyfunc_struct_ptr, 2, "sigindex_ptr"), @@ -685,21 +685,49 @@ fn parse_function( ) .try_as_basic_value() .left() - .unwrap(); + .unwrap() + .into_int_value(); let continue_block = context.append_basic_block(&function, "continue_block"); let sigindices_notequal_block = context.append_basic_block(&function, "sigindices_notequal_block"); + builder.build_conditional_branch( + sigindices_equal, + &continue_block, + &sigindices_notequal_block, + ); builder.position_at_end(&sigindices_notequal_block); + ctx.build_trap(); builder.build_unreachable(); builder.position_at_end(&continue_block); - println!("func ptr: {:#?}", func_ptr); - println!("ctx ptr: {:#?}", ctx_ptr); - println!("found dynamic sigindex: {:#?}", found_dynamic_sigindex); + let wasmer_fn_sig = &info.signatures[sig_index]; + let fn_ty = signatures[sig_index]; - unimplemented!("{}, {}", index, table_index); + let pushed_args = state.popn_save(wasmer_fn_sig.params().len())?; + + let args: Vec<_> = std::iter::once(ctx_ptr) + .chain(pushed_args.into_iter()) + .collect(); + println!("args: {:?}", args); + + let typed_func_ptr = builder.build_pointer_cast( + func_ptr, + fn_ty.ptr_type(AddressSpace::Generic), + "typed_func_ptr", + ); + + let call_site = builder.build_call(typed_func_ptr, &args, "indirect_call"); + + match wasmer_fn_sig.returns() { + [] => {} + [_] => { + let value = call_site.try_as_basic_value().left().unwrap(); + state.push1(value); + } + returns @ _ => unimplemented!("multi-value returns"), + } } /*************************** diff --git a/lib/llvm-backend/src/intrinsics.rs b/lib/llvm-backend/src/intrinsics.rs index ccc0081bf..f2ec8fd05 100644 --- a/lib/llvm-backend/src/intrinsics.rs +++ b/lib/llvm-backend/src/intrinsics.rs @@ -64,6 +64,7 @@ pub struct Intrinsics { pub copysign_f64: FunctionValue, pub expect_i1: FunctionValue, + pub trap: FunctionValue, pub void_ty: VoidType, pub i1_ty: IntType, @@ -255,6 +256,7 @@ impl Intrinsics { copysign_f64: module.add_function("llvm.copysign.f64", ret_f64_take_f64_f64, None), expect_i1: module.add_function("llvm.expect.i1", ret_i1_take_i1_i1, None), + trap: module.add_function("llvm.trap", void_ty.fn_type(&[], false), None), void_ty, i1_ty, @@ -695,7 +697,9 @@ impl<'a> CtxType<'a> { (imported_func_cache.func_ptr, imported_func_cache.ctx_ptr) } - // pub fn table(&mut self, table_index: TableIndex, elem_index: IntValue) -> + pub fn build_trap(&self) { + self.builder.build_call(self.intrinsics.trap, &[], "trap"); + } } // pub struct Ctx { diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index e70af96c0..e06b081af 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -10,11 +10,11 @@ use wasmer_runtime_core::{ module::ModuleInner, }; +mod backend; mod code; mod intrinsics; mod read_info; mod state; -// mod backend; pub struct LLVMCompiler { _private: (), @@ -28,13 +28,61 @@ impl LLVMCompiler { impl Compiler for LLVMCompiler { fn compile(&self, wasm: &[u8], _: Token) -> Result { - let (_info, _code_reader) = read_info::read_module(wasm).unwrap(); + let (info, code_reader) = read_info::read_module(wasm).unwrap(); + let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); - unimplemented!() + let backend = backend::LLVMBackend::new(module, intrinsics); + + // Create placeholder values here. + let (protected_caller, cache_gen) = { + use wasmer_runtime_core::backend::{ + sys::Memory, CacheGen, ProtectedCaller, UserTrapper, + }; + use wasmer_runtime_core::cache::Error as CacheError; + use wasmer_runtime_core::error::RuntimeResult; + use wasmer_runtime_core::module::ModuleInfo; + use wasmer_runtime_core::types::{FuncIndex, Value}; + use wasmer_runtime_core::vm; + struct Placeholder; + impl ProtectedCaller for Placeholder { + fn call( + &self, + _module: &ModuleInner, + _func_index: FuncIndex, + _params: &[Value], + _import_backing: &vm::ImportBacking, + _vmctx: *mut vm::Ctx, + _: Token, + ) -> RuntimeResult> { + Ok(vec![]) + } + fn get_early_trapper(&self) -> Box { + unimplemented!() + } + } + impl CacheGen for Placeholder { + fn generate_cache( + &self, + module: &ModuleInner, + ) -> Result<(Box, Box<[u8]>, Memory), CacheError> { + unimplemented!() + } + } + + (Box::new(Placeholder), Box::new(Placeholder)) + }; + + Ok(ModuleInner { + func_resolver: Box::new(backend), + protected_caller, + cache_gen, + + info, + }) } unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result { - unimplemented!() + unimplemented!("the llvm backend doesn't support caching yet") } } @@ -55,14 +103,9 @@ fn test_read_module() { get_local 0 i32.const 0 call_indirect (type $t0) - memory.grow ) (func $foobar (type $t0) get_local 0 - ) - (func $bar (type $t0) (param i32) (result i32) - get_local 0 - call $foo )) "#; let wasm = wat2wasm(wat).unwrap(); @@ -71,86 +114,50 @@ fn test_read_module() { let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap(); - { - Target::initialize_x86(&InitializationConfig { - asm_parser: true, - asm_printer: true, - base: true, - disassembler: true, - info: true, - machine_code: true, - }); - let triple = TargetMachine::get_default_triple().to_string(); - let target = Target::from_triple(&triple).unwrap(); - let target_machine = target - .create_target_machine( - &triple, - &TargetMachine::get_host_cpu_name().to_string(), - &TargetMachine::get_host_cpu_features().to_string(), - OptimizationLevel::Default, - RelocMode::PIC, - CodeModel::Default, - ) - .unwrap(); + // let backend = backend::LLVMBackend::new(module, intrinsics); - let memory_buffer = target_machine - .write_to_memory_buffer(&module, FileType::Object) - .unwrap(); - // std::fs::write("memory_buffer", memory_buffer.as_slice()).unwrap(); - let mem_buf_slice = memory_buffer.as_slice(); - - let macho = goblin::mach::MachO::parse(mem_buf_slice, 0).unwrap(); - let symbols = macho.symbols.as_ref().unwrap(); - let relocations = macho.relocations().unwrap(); - for (_, reloc_iter, section) in relocations.into_iter() { - println!("section: {:#?}", section); - for reloc_info in reloc_iter { - let reloc_info = reloc_info.unwrap(); - println!("\treloc_info: {:#?}", reloc_info); - println!( - "\tsymbol: {:#?}", - symbols.get(reloc_info.r_symbolnum()).unwrap() - ); - } - } + extern "C" { + fn test_cpp(); } - let exec_engine = module - .create_jit_execution_engine(OptimizationLevel::Default) - .unwrap(); + unsafe { test_cpp() }; - exec_engine.add_global_mapping( - &intrinsics.memory_grow_dynamic_local, - vmcalls::local_dynamic_memory_grow as usize, - ); - exec_engine.add_global_mapping( - &intrinsics.memory_grow_static_local, - vmcalls::local_static_memory_grow as usize, - ); - exec_engine.add_global_mapping( - &intrinsics.memory_grow_dynamic_import, - vmcalls::imported_dynamic_memory_grow as usize, - ); - exec_engine.add_global_mapping( - &intrinsics.memory_grow_static_import, - vmcalls::imported_static_memory_grow as usize, - ); - exec_engine.add_global_mapping( - &intrinsics.memory_size_dynamic_local, - vmcalls::local_dynamic_memory_size as usize, - ); - exec_engine.add_global_mapping( - &intrinsics.memory_size_static_local, - vmcalls::local_static_memory_size as usize, - ); - exec_engine.add_global_mapping( - &intrinsics.memory_size_dynamic_import, - vmcalls::imported_dynamic_memory_size as usize, - ); - exec_engine.add_global_mapping( - &intrinsics.memory_size_static_import, - vmcalls::imported_static_memory_size as usize, - ); + // let exec_engine = module + // .create_jit_execution_engine(OptimizationLevel::Default) + // .unwrap(); + + // exec_engine.add_global_mapping( + // &intrinsics.memory_grow_dynamic_local, + // vmcalls::local_dynamic_memory_grow as usize, + // ); + // exec_engine.add_global_mapping( + // &intrinsics.memory_grow_static_local, + // vmcalls::local_static_memory_grow as usize, + // ); + // exec_engine.add_global_mapping( + // &intrinsics.memory_grow_dynamic_import, + // vmcalls::imported_dynamic_memory_grow as usize, + // ); + // exec_engine.add_global_mapping( + // &intrinsics.memory_grow_static_import, + // vmcalls::imported_static_memory_grow as usize, + // ); + // exec_engine.add_global_mapping( + // &intrinsics.memory_size_dynamic_local, + // vmcalls::local_dynamic_memory_size as usize, + // ); + // exec_engine.add_global_mapping( + // &intrinsics.memory_size_static_local, + // vmcalls::local_static_memory_size as usize, + // ); + // exec_engine.add_global_mapping( + // &intrinsics.memory_size_dynamic_import, + // vmcalls::imported_dynamic_memory_size as usize, + // ); + // exec_engine.add_global_mapping( + // &intrinsics.memory_size_static_import, + // vmcalls::imported_static_memory_size as usize, + // ); // unsafe { // let func: JitFunction i32> = exec_engine.get_function("fn0").unwrap();