mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-13 22:25:40 +00:00
Start work on object loader using llvm's RuntimeDyld api.
This commit is contained in:
parent
2a913f5663
commit
4f833876e0
30
Cargo.lock
generated
30
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
||||
|
213
lib/llvm-backend/build.rs
Normal file
213
lib/llvm-backend/build.rs
Normal file
@ -0,0 +1,213 @@
|
||||
//! 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 lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
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<String> = {
|
||||
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<S: AsRef<OsStr>>(binary: S, arg: &str) -> io::Result<String> {
|
||||
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<S: AsRef<OsStr>>(binary: S) -> io::Result<Version> {
|
||||
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<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\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::<Vec<_>>()
|
||||
.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");
|
||||
}
|
96
lib/llvm-backend/cpp/object_loader.cpp
Normal file
96
lib/llvm-backend/cpp/object_loader.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
#include "object_loader.hh"
|
||||
#include <llvm/ExecutionEngine/RuntimeDyld.h>
|
||||
#include <iostream>
|
||||
|
||||
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<LookupResult> lookup(const LookupSet& symbols) override {
|
||||
LookupResult result;
|
||||
|
||||
for (auto symbol : symbols) {
|
||||
result.emplace(symbol, symbol_lookup(symbol));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual llvm::Expected<LookupFlagsResult> 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);
|
||||
}
|
||||
};
|
40
lib/llvm-backend/cpp/object_loader.hh
Normal file
40
lib/llvm-backend/cpp/object_loader.hh
Normal file
@ -0,0 +1,40 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
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() {
|
||||
}
|
||||
}
|
@ -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<NonNull<vm::Func>> {
|
||||
fn get(
|
||||
&self,
|
||||
module: &ModuleInner,
|
||||
local_func_index: LocalFuncIndex,
|
||||
) -> Option<NonNull<vm::Func>> {
|
||||
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<unsafe extern fn()> = self.exec_engine.get_function(&name).ok()?;
|
||||
|
||||
self.library
|
||||
.symbol::<NonNull<vm::Func>>(&name)
|
||||
.ok()
|
||||
.map(|symbol| *symbol)
|
||||
}
|
||||
}
|
||||
}
|
@ -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_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, 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"),
|
||||
}
|
||||
}
|
||||
|
||||
/***************************
|
||||
|
@ -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 {
|
||||
|
@ -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<ModuleInner, CompileError> {
|
||||
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();
|
||||
|
||||
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<Vec<Value>> {
|
||||
Ok(vec![])
|
||||
}
|
||||
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl CacheGen for Placeholder {
|
||||
fn generate_cache(
|
||||
&self,
|
||||
module: &ModuleInner,
|
||||
) -> Result<(Box<ModuleInfo>, 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<ModuleInner, CacheError> {
|
||||
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<unsafe extern fn(*mut u8, i32) -> i32> = exec_engine.get_function("fn0").unwrap();
|
||||
|
Loading…
Reference in New Issue
Block a user