mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-14 14:45: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)",
|
"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]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.5.1"
|
version = "1.5.1"
|
||||||
@ -469,10 +491,16 @@ dependencies = [
|
|||||||
name = "llvm-backend"
|
name = "llvm-backend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"wabt 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"wasmer-runtime-core 0.1.2",
|
"wasmer-runtime-core 0.1.2",
|
||||||
"wasmparser 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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-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 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 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 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 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"
|
"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"
|
hashbrown = "0.1.8"
|
||||||
smallvec = "0.6.8"
|
smallvec = "0.6.8"
|
||||||
goblin = "0.0.20"
|
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]
|
[dev-dependencies]
|
||||||
wabt = "0.7.4"
|
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::{
|
use inkwell::{
|
||||||
module::Module,
|
module::Module,
|
||||||
execution_engine::{ExecutionEngine, JitFunction},
|
targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine},
|
||||||
|
OptimizationLevel,
|
||||||
};
|
};
|
||||||
use crate::intrinsics::Intrinsics;
|
use std::{io::Write, ptr::NonNull};
|
||||||
use std::ptr::NonNull;
|
use tempfile::NamedTempFile;
|
||||||
use wasmer_runtime_core::{
|
use wasmer_runtime_core::{
|
||||||
module::ModuleInner,
|
backend::FuncResolver, module::ModuleInner, structures::TypedIndex, types::LocalFuncIndex, vm,
|
||||||
types::LocalFuncIndex,
|
|
||||||
structures::TypedIndex,
|
|
||||||
backend::{FuncResolver, vm},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct LLVMBackend {
|
pub struct LLVMBackend {
|
||||||
exec_engine: ExecutionEngine,
|
tempfile: NamedTempFile,
|
||||||
|
library: Library,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LLVMBackend {
|
impl LLVMBackend {
|
||||||
pub fn new(module: Module, intrinsics: Intrinsics) -> Self {
|
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);
|
let memory_buffer = target_machine
|
||||||
exec_engine.add_global_mapping(&intrinsics.memory_grow_static_local, vmcalls::local_static_memory_grow as usize);
|
.write_to_memory_buffer(&module, FileType::Object)
|
||||||
exec_engine.add_global_mapping(&intrinsics.memory_grow_dynamic_import, vmcalls::imported_dynamic_memory_grow as usize);
|
.unwrap();
|
||||||
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);
|
|
||||||
|
|
||||||
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 {
|
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 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 {
|
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.
|
// Emit an unreachable instruction.
|
||||||
// If llvm cannot prove that this is never touched,
|
// If llvm cannot prove that this is never touched,
|
||||||
// it will emit a `ud2` instruction on x86_64 arches.
|
// it will emit a `ud2` instruction on x86_64 arches.
|
||||||
|
ctx.build_trap();
|
||||||
builder.build_unreachable();
|
builder.build_unreachable();
|
||||||
state.reachable = false;
|
state.reachable = false;
|
||||||
}
|
}
|
||||||
@ -623,7 +624,8 @@ fn parse_function(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operator::CallIndirect { index, table_index } => {
|
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 (table_base, table_bound) = ctx.table(TableIndex::new(table_index as usize));
|
||||||
let func_index = state.pop1()?.into_int_value();
|
let func_index = state.pop1()?.into_int_value();
|
||||||
|
|
||||||
@ -651,12 +653,10 @@ fn parse_function(
|
|||||||
"func_ptr",
|
"func_ptr",
|
||||||
)
|
)
|
||||||
.into_pointer_value(),
|
.into_pointer_value(),
|
||||||
builder
|
builder.build_load(
|
||||||
.build_load(
|
builder.build_struct_gep(anyfunc_struct_ptr, 1, "ctx_ptr_ptr"),
|
||||||
builder.build_struct_gep(anyfunc_struct_ptr, 1, "ctx_ptr_ptr"),
|
"ctx_ptr",
|
||||||
"ctx_ptr",
|
),
|
||||||
)
|
|
||||||
.into_pointer_value(),
|
|
||||||
builder
|
builder
|
||||||
.build_load(
|
.build_load(
|
||||||
builder.build_struct_gep(anyfunc_struct_ptr, 2, "sigindex_ptr"),
|
builder.build_struct_gep(anyfunc_struct_ptr, 2, "sigindex_ptr"),
|
||||||
@ -685,21 +685,49 @@ fn parse_function(
|
|||||||
)
|
)
|
||||||
.try_as_basic_value()
|
.try_as_basic_value()
|
||||||
.left()
|
.left()
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
.into_int_value();
|
||||||
|
|
||||||
let continue_block = context.append_basic_block(&function, "continue_block");
|
let continue_block = context.append_basic_block(&function, "continue_block");
|
||||||
let sigindices_notequal_block =
|
let sigindices_notequal_block =
|
||||||
context.append_basic_block(&function, "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);
|
builder.position_at_end(&sigindices_notequal_block);
|
||||||
|
ctx.build_trap();
|
||||||
builder.build_unreachable();
|
builder.build_unreachable();
|
||||||
builder.position_at_end(&continue_block);
|
builder.position_at_end(&continue_block);
|
||||||
|
|
||||||
println!("func ptr: {:#?}", func_ptr);
|
let wasmer_fn_sig = &info.signatures[sig_index];
|
||||||
println!("ctx ptr: {:#?}", ctx_ptr);
|
let fn_ty = signatures[sig_index];
|
||||||
println!("found dynamic sigindex: {:#?}", found_dynamic_sigindex);
|
|
||||||
|
|
||||||
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 copysign_f64: FunctionValue,
|
||||||
|
|
||||||
pub expect_i1: FunctionValue,
|
pub expect_i1: FunctionValue,
|
||||||
|
pub trap: FunctionValue,
|
||||||
|
|
||||||
pub void_ty: VoidType,
|
pub void_ty: VoidType,
|
||||||
pub i1_ty: IntType,
|
pub i1_ty: IntType,
|
||||||
@ -255,6 +256,7 @@ impl Intrinsics {
|
|||||||
copysign_f64: module.add_function("llvm.copysign.f64", ret_f64_take_f64_f64, None),
|
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),
|
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,
|
void_ty,
|
||||||
i1_ty,
|
i1_ty,
|
||||||
@ -695,7 +697,9 @@ impl<'a> CtxType<'a> {
|
|||||||
(imported_func_cache.func_ptr, imported_func_cache.ctx_ptr)
|
(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 {
|
// pub struct Ctx {
|
||||||
|
@ -10,11 +10,11 @@ use wasmer_runtime_core::{
|
|||||||
module::ModuleInner,
|
module::ModuleInner,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod backend;
|
||||||
mod code;
|
mod code;
|
||||||
mod intrinsics;
|
mod intrinsics;
|
||||||
mod read_info;
|
mod read_info;
|
||||||
mod state;
|
mod state;
|
||||||
// mod backend;
|
|
||||||
|
|
||||||
pub struct LLVMCompiler {
|
pub struct LLVMCompiler {
|
||||||
_private: (),
|
_private: (),
|
||||||
@ -28,13 +28,61 @@ impl LLVMCompiler {
|
|||||||
|
|
||||||
impl Compiler for LLVMCompiler {
|
impl Compiler for LLVMCompiler {
|
||||||
fn compile(&self, wasm: &[u8], _: Token) -> Result<ModuleInner, CompileError> {
|
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();
|
||||||
|
|
||||||
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<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> {
|
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
|
get_local 0
|
||||||
i32.const 0
|
i32.const 0
|
||||||
call_indirect (type $t0)
|
call_indirect (type $t0)
|
||||||
memory.grow
|
|
||||||
)
|
)
|
||||||
(func $foobar (type $t0)
|
(func $foobar (type $t0)
|
||||||
get_local 0
|
get_local 0
|
||||||
)
|
|
||||||
(func $bar (type $t0) (param i32) (result i32)
|
|
||||||
get_local 0
|
|
||||||
call $foo
|
|
||||||
))
|
))
|
||||||
"#;
|
"#;
|
||||||
let wasm = wat2wasm(wat).unwrap();
|
let wasm = wat2wasm(wat).unwrap();
|
||||||
@ -71,86 +114,50 @@ fn test_read_module() {
|
|||||||
|
|
||||||
let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap();
|
let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap();
|
||||||
|
|
||||||
{
|
// let backend = backend::LLVMBackend::new(module, intrinsics);
|
||||||
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 memory_buffer = target_machine
|
extern "C" {
|
||||||
.write_to_memory_buffer(&module, FileType::Object)
|
fn test_cpp();
|
||||||
.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()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let exec_engine = module
|
unsafe { test_cpp() };
|
||||||
.create_jit_execution_engine(OptimizationLevel::Default)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
exec_engine.add_global_mapping(
|
// let exec_engine = module
|
||||||
&intrinsics.memory_grow_dynamic_local,
|
// .create_jit_execution_engine(OptimizationLevel::Default)
|
||||||
vmcalls::local_dynamic_memory_grow as usize,
|
// .unwrap();
|
||||||
);
|
|
||||||
exec_engine.add_global_mapping(
|
// exec_engine.add_global_mapping(
|
||||||
&intrinsics.memory_grow_static_local,
|
// &intrinsics.memory_grow_dynamic_local,
|
||||||
vmcalls::local_static_memory_grow as usize,
|
// vmcalls::local_dynamic_memory_grow as usize,
|
||||||
);
|
// );
|
||||||
exec_engine.add_global_mapping(
|
// exec_engine.add_global_mapping(
|
||||||
&intrinsics.memory_grow_dynamic_import,
|
// &intrinsics.memory_grow_static_local,
|
||||||
vmcalls::imported_dynamic_memory_grow as usize,
|
// vmcalls::local_static_memory_grow as usize,
|
||||||
);
|
// );
|
||||||
exec_engine.add_global_mapping(
|
// exec_engine.add_global_mapping(
|
||||||
&intrinsics.memory_grow_static_import,
|
// &intrinsics.memory_grow_dynamic_import,
|
||||||
vmcalls::imported_static_memory_grow as usize,
|
// vmcalls::imported_dynamic_memory_grow as usize,
|
||||||
);
|
// );
|
||||||
exec_engine.add_global_mapping(
|
// exec_engine.add_global_mapping(
|
||||||
&intrinsics.memory_size_dynamic_local,
|
// &intrinsics.memory_grow_static_import,
|
||||||
vmcalls::local_dynamic_memory_size as usize,
|
// vmcalls::imported_static_memory_grow as usize,
|
||||||
);
|
// );
|
||||||
exec_engine.add_global_mapping(
|
// exec_engine.add_global_mapping(
|
||||||
&intrinsics.memory_size_static_local,
|
// &intrinsics.memory_size_dynamic_local,
|
||||||
vmcalls::local_static_memory_size as usize,
|
// vmcalls::local_dynamic_memory_size as usize,
|
||||||
);
|
// );
|
||||||
exec_engine.add_global_mapping(
|
// exec_engine.add_global_mapping(
|
||||||
&intrinsics.memory_size_dynamic_import,
|
// &intrinsics.memory_size_static_local,
|
||||||
vmcalls::imported_dynamic_memory_size as usize,
|
// vmcalls::local_static_memory_size as usize,
|
||||||
);
|
// );
|
||||||
exec_engine.add_global_mapping(
|
// exec_engine.add_global_mapping(
|
||||||
&intrinsics.memory_size_static_import,
|
// &intrinsics.memory_size_dynamic_import,
|
||||||
vmcalls::imported_static_memory_size as usize,
|
// 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 {
|
// unsafe {
|
||||||
// let func: JitFunction<unsafe extern fn(*mut u8, i32) -> i32> = exec_engine.get_function("fn0").unwrap();
|
// let func: JitFunction<unsafe extern fn(*mut u8, i32) -> i32> = exec_engine.get_function("fn0").unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user