Improve robustness of cache loading/storing (#974)
* Improve robustness of cache loading/storing
Today wasmtime incorrectly loads compiled compiled modules from the
global cache when toggling settings such as optimizations. For example
if you execute `wasmtime foo.wasm` that will cache globally an
unoptimized version of the wasm module. If you then execute `wasmtime -O
foo.wasm` it would then reload the unoptimized version from cache, not
realizing the compilation settings were different, and use that instead.
This can lead to very surprising behavior naturally!
This commit updates how the cache is managed in an attempt to make it
much more robust against these sorts of issues. This takes a leaf out of
rustc's playbook and models the cache with a function that looks like:
fn load<T: Hash>(
&self,
data: T,
compute: fn(T) -> CacheEntry,
) -> CacheEntry;
The goal here is that it guarantees that all the `data` necessary to
`compute` the result of the cache entry is hashable and stored into the
hash key entry. This was previously open-coded and manually managed
where items were hashed explicitly, but this construction guarantees
that everything reasonable `compute` could use to compile the module is
stored in `data`, which is itself hashable.
This refactoring then resulted in a few workarounds and a few fixes,
including the original issue:
* The `Module` type was split into `Module` and `ModuleLocal` where only
the latter is hashed. The previous hash function for a `Module` left
out items like the `start_func` and didn't hash items like the imports
of the module. Omitting the `start_func` was fine since compilation
didn't actually use it, but omitting imports seemed uncomfortable
because while compilation didn't use the import values it did use the
*number* of imports, which seems like it should then be put into the
cache key. The `ModuleLocal` type now derives `Hash` to guarantee that
all of its contents affect the hash key.
* The `ModuleTranslationState` from `cranelift-wasm` doesn't implement
`Hash` which means that we have a manual wrapper to work around that.
This will be fixed with an upstream implementation, since this state
affects the generated wasm code. Currently this is just a map of
signatures, which is present in `Module` anyway, so we should be good
for the time being.
* Hashing `dyn TargetIsa` was also added, where previously it was not
fully hashed. Previously only the target name was used as part of the
cache key, but crucially the flags of compilation were omitted (for
example the optimization flags). Unfortunately the trait object itself
is not hashable so we still have to manually write a wrapper to hash
it, but we likely want to add upstream some utilities to hash isa
objects into cranelift itself. For now though we can continue to add
hashed fields as necessary.
Overall the goal here was to use the compiler to expose what we're not
hashing, and then make sure we organize data and write the right code to
ensure everything is hashed, and nothing more.
* Update crates/environ/src/module.rs
Co-Authored-By: Peter Huene <peterhuene@protonmail.com>
* Fix lightbeam
* Fix compilation of tests
* Update the expected structure of the cache
* Revert "Update the expected structure of the cache"
This reverts commit 2b53fee426a4e411c313d8c1e424841ba304a9cd.
* Separate the cache dir a bit
* Add a test the cache is busted with opt levels
* rustfmt
Co-authored-by: Peter Huene <peterhuene@protonmail.com>
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1976,6 +1976,7 @@ dependencies = [
|
|||||||
"region",
|
"region",
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
|
"tempfile",
|
||||||
"wasi-common",
|
"wasi-common",
|
||||||
"wasmparser 0.51.2",
|
"wasmparser 0.51.2",
|
||||||
"wasmtime-environ",
|
"wasmtime-environ",
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ pretty_env_logger = "0.3.0"
|
|||||||
rayon = "1.2.1"
|
rayon = "1.2.1"
|
||||||
file-per-thread-logger = "0.1.1"
|
file-per-thread-logger = "0.1.1"
|
||||||
wat = "1.0"
|
wat = "1.0"
|
||||||
|
tempfile = "3.1"
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ impl GlobalFrameInfo {
|
|||||||
if end > max {
|
if end > max {
|
||||||
max = end;
|
max = end;
|
||||||
}
|
}
|
||||||
let func_index = module.module().func_index(i);
|
let func_index = module.module().local.func_index(i);
|
||||||
assert!(functions.insert(end, (start, func_index)).is_none());
|
assert!(functions.insert(end, (start, func_index)).is_none());
|
||||||
}
|
}
|
||||||
if functions.len() == 0 {
|
if functions.len() == 0 {
|
||||||
|
|||||||
@@ -507,3 +507,71 @@ fn _assert_send_sync() {
|
|||||||
_assert::<Engine>();
|
_assert::<Engine>();
|
||||||
_assert::<Config>();
|
_assert::<Config>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::Module;
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cache_accounts_for_opt_level() -> Result<()> {
|
||||||
|
let td = TempDir::new()?;
|
||||||
|
let config_path = td.path().join("config.toml");
|
||||||
|
std::fs::write(
|
||||||
|
&config_path,
|
||||||
|
&format!(
|
||||||
|
"
|
||||||
|
[cache]
|
||||||
|
enabled = true
|
||||||
|
directory = '{}'
|
||||||
|
",
|
||||||
|
td.path().join("cache").display()
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
let mut cfg = Config::new();
|
||||||
|
cfg.cranelift_opt_level(OptLevel::None)
|
||||||
|
.cache_config_load(&config_path)?;
|
||||||
|
let store = Store::new(&Engine::new(&cfg));
|
||||||
|
Module::new(&store, "(module (func))")?;
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_hits(), 0);
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_misses(), 1);
|
||||||
|
Module::new(&store, "(module (func))")?;
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_hits(), 1);
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_misses(), 1);
|
||||||
|
|
||||||
|
let mut cfg = Config::new();
|
||||||
|
cfg.cranelift_opt_level(OptLevel::Speed)
|
||||||
|
.cache_config_load(&config_path)?;
|
||||||
|
let store = Store::new(&Engine::new(&cfg));
|
||||||
|
Module::new(&store, "(module (func))")?;
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_hits(), 0);
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_misses(), 1);
|
||||||
|
Module::new(&store, "(module (func))")?;
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_hits(), 1);
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_misses(), 1);
|
||||||
|
|
||||||
|
let mut cfg = Config::new();
|
||||||
|
cfg.cranelift_opt_level(OptLevel::SpeedAndSize)
|
||||||
|
.cache_config_load(&config_path)?;
|
||||||
|
let store = Store::new(&Engine::new(&cfg));
|
||||||
|
Module::new(&store, "(module (func))")?;
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_hits(), 0);
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_misses(), 1);
|
||||||
|
Module::new(&store, "(module (func))")?;
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_hits(), 1);
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_misses(), 1);
|
||||||
|
|
||||||
|
let mut cfg = Config::new();
|
||||||
|
cfg.debug_info(true).cache_config_load(&config_path)?;
|
||||||
|
let store = Store::new(&Engine::new(&cfg));
|
||||||
|
Module::new(&store, "(module (func))")?;
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_hits(), 0);
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_misses(), 1);
|
||||||
|
Module::new(&store, "(module (func))")?;
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_hits(), 1);
|
||||||
|
assert_eq!(store.engine().config.cache_config.cache_misses(), 1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ pub(crate) fn create_handle(
|
|||||||
|
|
||||||
// Compute indices into the shared signature table.
|
// Compute indices into the shared signature table.
|
||||||
let signatures = module
|
let signatures = module
|
||||||
|
.local
|
||||||
.signatures
|
.signatures
|
||||||
.values()
|
.values()
|
||||||
.map(|sig| store.compiler().signatures().register(sig))
|
.map(|sig| store.compiler().signatures().register(sig))
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ unsafe extern "C" fn stub_fn(
|
|||||||
|
|
||||||
let (args, returns_len) = {
|
let (args, returns_len) = {
|
||||||
let module = instance.module_ref();
|
let module = instance.module_ref();
|
||||||
let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]];
|
let signature =
|
||||||
|
&module.local.signatures[module.local.functions[FuncIndex::new(call_id as usize)]];
|
||||||
|
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
for i in 2..signature.params.len() {
|
for i in 2..signature.params.len() {
|
||||||
@@ -101,7 +102,8 @@ unsafe extern "C" fn stub_fn(
|
|||||||
state.func.call(&args, &mut returns)?;
|
state.func.call(&args, &mut returns)?;
|
||||||
|
|
||||||
let module = instance.module_ref();
|
let module = instance.module_ref();
|
||||||
let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]];
|
let signature =
|
||||||
|
&module.local.signatures[module.local.functions[FuncIndex::new(call_id as usize)]];
|
||||||
for (i, ret) in returns.iter_mut().enumerate() {
|
for (i, ret) in returns.iter_mut().enumerate() {
|
||||||
if ret.ty().get_wasmtime_type() != Some(signature.returns[i].value_type) {
|
if ret.ty().get_wasmtime_type() != Some(signature.returns[i].value_type) {
|
||||||
return Err(Trap::new(
|
return Err(Trap::new(
|
||||||
@@ -266,8 +268,8 @@ pub fn create_handle_with_function(
|
|||||||
PrimaryMap::new();
|
PrimaryMap::new();
|
||||||
let mut code_memory = CodeMemory::new();
|
let mut code_memory = CodeMemory::new();
|
||||||
|
|
||||||
let sig_id = module.signatures.push(sig.clone());
|
let sig_id = module.local.signatures.push(sig.clone());
|
||||||
let func_id = module.functions.push(sig_id);
|
let func_id = module.local.functions.push(sig_id);
|
||||||
module
|
module
|
||||||
.exports
|
.exports
|
||||||
.insert("trampoline".to_string(), Export::Function(func_id));
|
.insert("trampoline".to_string(), Export::Function(func_id));
|
||||||
@@ -313,8 +315,8 @@ pub unsafe fn create_handle_with_raw_function(
|
|||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
let mut finished_functions = PrimaryMap::new();
|
let mut finished_functions = PrimaryMap::new();
|
||||||
|
|
||||||
let sig_id = module.signatures.push(sig.clone());
|
let sig_id = module.local.signatures.push(sig.clone());
|
||||||
let func_id = module.functions.push(sig_id);
|
let func_id = module.local.functions.push(sig_id);
|
||||||
module
|
module
|
||||||
.exports
|
.exports
|
||||||
.insert("trampoline".to_string(), Export::Function(func_id));
|
.insert("trampoline".to_string(), Export::Function(func_id));
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ pub fn create_global(store: &Store, gt: &GlobalType, val: Val) -> Result<Instanc
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
let global_id = module.globals.push(global);
|
let global_id = module.local.globals.push(global);
|
||||||
module.exports.insert(
|
module.exports.insert(
|
||||||
"global".to_string(),
|
"global".to_string(),
|
||||||
wasmtime_environ::Export::Global(global_id),
|
wasmtime_environ::Export::Global(global_id),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub fn create_handle_with_memory(store: &Store, memory: &MemoryType) -> Result<I
|
|||||||
let tunable = Default::default();
|
let tunable = Default::default();
|
||||||
|
|
||||||
let memory_plan = wasmtime_environ::MemoryPlan::for_memory(memory, &tunable);
|
let memory_plan = wasmtime_environ::MemoryPlan::for_memory(memory, &tunable);
|
||||||
let memory_id = module.memory_plans.push(memory_plan);
|
let memory_id = module.local.memory_plans.push(memory_plan);
|
||||||
module.exports.insert(
|
module.exports.insert(
|
||||||
"memory".to_string(),
|
"memory".to_string(),
|
||||||
wasmtime_environ::Export::Memory(memory_id),
|
wasmtime_environ::Export::Memory(memory_id),
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pub fn create_handle_with_table(store: &Store, table: &TableType) -> Result<Inst
|
|||||||
let tunable = Default::default();
|
let tunable = Default::default();
|
||||||
|
|
||||||
let table_plan = wasmtime_environ::TablePlan::for_table(table, &tunable);
|
let table_plan = wasmtime_environ::TablePlan::for_table(table, &tunable);
|
||||||
let table_id = module.table_plans.push(table_plan);
|
let table_id = module.local.table_plans.push(table_plan);
|
||||||
module.exports.insert(
|
module.exports.insert(
|
||||||
"table".to_string(),
|
"table".to_string(),
|
||||||
wasmtime_environ::Export::Table(table_id),
|
wasmtime_environ::Export::Table(table_id),
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let git_rev = match Command::new("git").args(&["rev-parse", "HEAD"]).output() {
|
let git_rev = match Command::new("git").args(&["rev-parse", "HEAD"]).output() {
|
||||||
Ok(output) => String::from_utf8(output.stdout).unwrap(),
|
Ok(output) => str::from_utf8(&output.stdout).unwrap().trim().to_string(),
|
||||||
Err(_) => String::from("git-not-found"),
|
Err(_) => env!("CARGO_PKG_VERSION").to_string(),
|
||||||
};
|
};
|
||||||
println!("cargo:rustc-env=GIT_REV={}", git_rev);
|
println!("cargo:rustc-env=GIT_REV={}", git_rev);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
use crate::address_map::{ModuleAddressMap, ValueLabelsRanges};
|
use crate::address_map::{ModuleAddressMap, ValueLabelsRanges};
|
||||||
use crate::compilation::{Compilation, Relocations, Traps};
|
use crate::compilation::{Compilation, Relocations, Traps};
|
||||||
use crate::module::Module;
|
use cranelift_codegen::ir;
|
||||||
use crate::module_environ::FunctionBodyData;
|
|
||||||
use cranelift_codegen::{ir, isa};
|
|
||||||
use cranelift_entity::PrimaryMap;
|
use cranelift_entity::PrimaryMap;
|
||||||
use cranelift_wasm::DefinedFuncIndex;
|
use cranelift_wasm::DefinedFuncIndex;
|
||||||
use log::{debug, trace, warn};
|
use log::{debug, trace, warn};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::hash::Hash;
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@@ -23,7 +22,7 @@ use worker::Worker;
|
|||||||
pub struct ModuleCacheEntry<'config>(Option<ModuleCacheEntryInner<'config>>);
|
pub struct ModuleCacheEntry<'config>(Option<ModuleCacheEntryInner<'config>>);
|
||||||
|
|
||||||
struct ModuleCacheEntryInner<'config> {
|
struct ModuleCacheEntryInner<'config> {
|
||||||
mod_cache_path: PathBuf,
|
root_path: PathBuf,
|
||||||
cache_config: &'config CacheConfig,
|
cache_config: &'config CacheConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,21 +50,10 @@ pub type ModuleCacheDataTupleType = (
|
|||||||
struct Sha256Hasher(Sha256);
|
struct Sha256Hasher(Sha256);
|
||||||
|
|
||||||
impl<'config> ModuleCacheEntry<'config> {
|
impl<'config> ModuleCacheEntry<'config> {
|
||||||
pub fn new<'data>(
|
pub fn new<'data>(compiler_name: &str, cache_config: &'config CacheConfig) -> Self {
|
||||||
module: &Module,
|
|
||||||
function_body_inputs: &PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
|
|
||||||
isa: &dyn isa::TargetIsa,
|
|
||||||
compiler_name: &str,
|
|
||||||
generate_debug_info: bool,
|
|
||||||
cache_config: &'config CacheConfig,
|
|
||||||
) -> Self {
|
|
||||||
if cache_config.enabled() {
|
if cache_config.enabled() {
|
||||||
Self(Some(ModuleCacheEntryInner::new(
|
Self(Some(ModuleCacheEntryInner::new(
|
||||||
module,
|
|
||||||
function_body_inputs,
|
|
||||||
isa,
|
|
||||||
compiler_name,
|
compiler_name,
|
||||||
generate_debug_info,
|
|
||||||
cache_config,
|
cache_config,
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
@@ -78,42 +66,47 @@ impl<'config> ModuleCacheEntry<'config> {
|
|||||||
Self(Some(inner))
|
Self(Some(inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_data(&self) -> Option<ModuleCacheData> {
|
pub fn get_data<T: Hash, E>(
|
||||||
if let Some(inner) = &self.0 {
|
&self,
|
||||||
inner.get_data().map(|val| {
|
state: T,
|
||||||
inner
|
compute: fn(T) -> Result<ModuleCacheDataTupleType, E>,
|
||||||
.cache_config
|
) -> Result<ModuleCacheData, E> {
|
||||||
.worker()
|
let mut hasher = Sha256Hasher(Sha256::new());
|
||||||
.on_cache_get_async(&inner.mod_cache_path); // call on success
|
state.hash(&mut hasher);
|
||||||
val
|
let hash: [u8; 32] = hasher.0.result().into();
|
||||||
})
|
// standard encoding uses '/' which can't be used for filename
|
||||||
} else {
|
let hash = base64::encode_config(&hash, base64::URL_SAFE_NO_PAD);
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_data(&self, data: &ModuleCacheData) {
|
let inner = match &self.0 {
|
||||||
if let Some(inner) = &self.0 {
|
Some(inner) => inner,
|
||||||
if inner.update_data(data).is_some() {
|
None => return compute(state).map(ModuleCacheData::from_tuple),
|
||||||
inner
|
};
|
||||||
.cache_config
|
|
||||||
.worker()
|
if let Some(cached_val) = inner.get_data(&hash) {
|
||||||
.on_cache_update_async(&inner.mod_cache_path); // call on success
|
let mod_cache_path = inner.root_path.join(&hash);
|
||||||
|
inner.cache_config.on_cache_get_async(&mod_cache_path); // call on success
|
||||||
|
return Ok(cached_val);
|
||||||
}
|
}
|
||||||
|
let val_to_cache = ModuleCacheData::from_tuple(compute(state)?);
|
||||||
|
if inner.update_data(&hash, &val_to_cache).is_some() {
|
||||||
|
let mod_cache_path = inner.root_path.join(&hash);
|
||||||
|
inner.cache_config.on_cache_update_async(&mod_cache_path); // call on success
|
||||||
}
|
}
|
||||||
|
Ok(val_to_cache)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'config> ModuleCacheEntryInner<'config> {
|
impl<'config> ModuleCacheEntryInner<'config> {
|
||||||
fn new<'data>(
|
fn new<'data>(compiler_name: &str, cache_config: &'config CacheConfig) -> Self {
|
||||||
module: &Module,
|
// If debug assertions are enabled then assume that we're some sort of
|
||||||
function_body_inputs: &PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
|
// local build. We don't want local builds to stomp over caches between
|
||||||
isa: &dyn isa::TargetIsa,
|
// builds, so just use a separate cache directory based on the mtime of
|
||||||
compiler_name: &str,
|
// our executable, which should roughly correlate with "you changed the
|
||||||
generate_debug_info: bool,
|
// source code so you get a different directory".
|
||||||
cache_config: &'config CacheConfig,
|
//
|
||||||
) -> Self {
|
// Otherwise if this is a release build we use the `GIT_REV` env var
|
||||||
let hash = Sha256Hasher::digest(module, function_body_inputs);
|
// which is either the git rev if installed from git or the crate
|
||||||
|
// version if installed from crates.io.
|
||||||
let compiler_dir = if cfg!(debug_assertions) {
|
let compiler_dir = if cfg!(debug_assertions) {
|
||||||
fn self_mtime() -> Option<String> {
|
fn self_mtime() -> Option<String> {
|
||||||
let path = std::env::current_exe().ok()?;
|
let path = std::env::current_exe().ok()?;
|
||||||
@@ -138,26 +131,18 @@ impl<'config> ModuleCacheEntryInner<'config> {
|
|||||||
comp_ver = env!("GIT_REV"),
|
comp_ver = env!("GIT_REV"),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let mod_filename = format!(
|
let root_path = cache_config.directory().join("modules").join(compiler_dir);
|
||||||
"mod-{mod_hash}{mod_dbg}",
|
|
||||||
mod_hash = base64::encode_config(&hash, base64::URL_SAFE_NO_PAD), // standard encoding uses '/' which can't be used for filename
|
|
||||||
mod_dbg = if generate_debug_info { ".d" } else { "" },
|
|
||||||
);
|
|
||||||
let mod_cache_path = cache_config
|
|
||||||
.directory()
|
|
||||||
.join(isa.triple().to_string())
|
|
||||||
.join(compiler_dir)
|
|
||||||
.join(mod_filename);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
mod_cache_path,
|
root_path,
|
||||||
cache_config,
|
cache_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_data(&self) -> Option<ModuleCacheData> {
|
fn get_data(&self, hash: &str) -> Option<ModuleCacheData> {
|
||||||
trace!("get_data() for path: {}", self.mod_cache_path.display());
|
let mod_cache_path = self.root_path.join(hash);
|
||||||
let compressed_cache_bytes = fs::read(&self.mod_cache_path).ok()?;
|
trace!("get_data() for path: {}", mod_cache_path.display());
|
||||||
|
let compressed_cache_bytes = fs::read(&mod_cache_path).ok()?;
|
||||||
let cache_bytes = zstd::decode_all(&compressed_cache_bytes[..])
|
let cache_bytes = zstd::decode_all(&compressed_cache_bytes[..])
|
||||||
.map_err(|err| warn!("Failed to decompress cached code: {}", err))
|
.map_err(|err| warn!("Failed to decompress cached code: {}", err))
|
||||||
.ok()?;
|
.ok()?;
|
||||||
@@ -166,8 +151,9 @@ impl<'config> ModuleCacheEntryInner<'config> {
|
|||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_data(&self, data: &ModuleCacheData) -> Option<()> {
|
fn update_data(&self, hash: &str, data: &ModuleCacheData) -> Option<()> {
|
||||||
trace!("update_data() for path: {}", self.mod_cache_path.display());
|
let mod_cache_path = self.root_path.join(hash);
|
||||||
|
trace!("update_data() for path: {}", mod_cache_path.display());
|
||||||
let serialized_data = bincode::serialize(&data)
|
let serialized_data = bincode::serialize(&data)
|
||||||
.map_err(|err| warn!("Failed to serialize cached code: {}", err))
|
.map_err(|err| warn!("Failed to serialize cached code: {}", err))
|
||||||
.ok()?;
|
.ok()?;
|
||||||
@@ -180,17 +166,17 @@ impl<'config> ModuleCacheEntryInner<'config> {
|
|||||||
|
|
||||||
// Optimize syscalls: first, try writing to disk. It should succeed in most cases.
|
// Optimize syscalls: first, try writing to disk. It should succeed in most cases.
|
||||||
// Otherwise, try creating the cache directory and retry writing to the file.
|
// Otherwise, try creating the cache directory and retry writing to the file.
|
||||||
if fs_write_atomic(&self.mod_cache_path, "mod", &compressed_data) {
|
if fs_write_atomic(&mod_cache_path, "mod", &compressed_data) {
|
||||||
return Some(());
|
return Some(());
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Attempting to create the cache directory, because \
|
"Attempting to create the cache directory, because \
|
||||||
failed to write cached code to disk, path: {}",
|
failed to write cached code to disk, path: {}",
|
||||||
self.mod_cache_path.display(),
|
mod_cache_path.display(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let cache_dir = self.mod_cache_path.parent().unwrap();
|
let cache_dir = mod_cache_path.parent().unwrap();
|
||||||
fs::create_dir_all(cache_dir)
|
fs::create_dir_all(cache_dir)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
warn!(
|
warn!(
|
||||||
@@ -201,7 +187,7 @@ impl<'config> ModuleCacheEntryInner<'config> {
|
|||||||
})
|
})
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
if fs_write_atomic(&self.mod_cache_path, "mod", &compressed_data) {
|
if fs_write_atomic(&mod_cache_path, "mod", &compressed_data) {
|
||||||
Some(())
|
Some(())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -233,17 +219,6 @@ impl ModuleCacheData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sha256Hasher {
|
|
||||||
pub fn digest<'data>(
|
|
||||||
module: &Module,
|
|
||||||
function_body_inputs: &PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
|
|
||||||
) -> [u8; 32] {
|
|
||||||
let mut hasher = Self(Sha256::new());
|
|
||||||
module.hash_for_cache(function_body_inputs, &mut hasher);
|
|
||||||
hasher.0.result().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hasher for Sha256Hasher {
|
impl Hasher for Sha256Hasher {
|
||||||
fn finish(&self) -> u64 {
|
fn finish(&self) -> u64 {
|
||||||
panic!("Sha256Hasher doesn't support finish!");
|
panic!("Sha256Hasher doesn't support finish!");
|
||||||
|
|||||||
31
crates/environ/src/cache/config.rs
vendored
31
crates/environ/src/cache/config.rs
vendored
@@ -11,6 +11,8 @@ use serde::{
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||||
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
// wrapped, so we have named section in config,
|
// wrapped, so we have named section in config,
|
||||||
@@ -88,6 +90,14 @@ pub struct CacheConfig {
|
|||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
worker: Option<Worker>,
|
worker: Option<Worker>,
|
||||||
|
#[serde(skip)]
|
||||||
|
state: Arc<CacheState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
struct CacheState {
|
||||||
|
hits: AtomicUsize,
|
||||||
|
misses: AtomicUsize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new configuration file at specified path, or default path if None is passed.
|
/// Creates a new configuration file at specified path, or default path if None is passed.
|
||||||
@@ -324,6 +334,7 @@ impl CacheConfig {
|
|||||||
file_count_limit_percent_if_deleting: None,
|
file_count_limit_percent_if_deleting: None,
|
||||||
files_total_size_limit_percent_if_deleting: None,
|
files_total_size_limit_percent_if_deleting: None,
|
||||||
worker: None,
|
worker: None,
|
||||||
|
state: Arc::new(CacheState::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,6 +377,26 @@ impl CacheConfig {
|
|||||||
self.worker.as_ref().unwrap()
|
self.worker.as_ref().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of cache hits seen so far
|
||||||
|
pub fn cache_hits(&self) -> usize {
|
||||||
|
self.state.hits.load(SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of cache misses seen so far
|
||||||
|
pub fn cache_misses(&self) -> usize {
|
||||||
|
self.state.misses.load(SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn on_cache_get_async(&self, path: impl AsRef<Path>) {
|
||||||
|
self.state.hits.fetch_add(1, SeqCst);
|
||||||
|
self.worker().on_cache_get_async(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn on_cache_update_async(&self, path: impl AsRef<Path>) {
|
||||||
|
self.state.misses.fetch_add(1, SeqCst);
|
||||||
|
self.worker().on_cache_update_async(path)
|
||||||
|
}
|
||||||
|
|
||||||
fn load_and_parse_file(config_file: Option<&Path>) -> Result<Self> {
|
fn load_and_parse_file(config_file: Option<&Path>) -> Result<Self> {
|
||||||
// get config file path
|
// get config file path
|
||||||
let (config_file, user_custom_file) = match config_file {
|
let (config_file, user_custom_file) = match config_file {
|
||||||
|
|||||||
302
crates/environ/src/cache/tests.rs
vendored
302
crates/environ/src/cache/tests.rs
vendored
@@ -1,20 +1,7 @@
|
|||||||
use super::config::tests::test_prolog;
|
use super::config::tests::test_prolog;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::address_map::{FunctionAddressMap, InstructionAddressMap};
|
use cranelift_entity::PrimaryMap;
|
||||||
use crate::compilation::{
|
|
||||||
CompiledFunction, CompiledFunctionUnwindInfo, Relocation, RelocationTarget, TrapInformation,
|
|
||||||
};
|
|
||||||
use crate::module::{MemoryPlan, MemoryStyle, Module};
|
|
||||||
use cranelift_codegen::{binemit, ir, isa, settings, ValueLocRange};
|
|
||||||
use cranelift_entity::EntityRef;
|
|
||||||
use cranelift_entity::{PrimaryMap, SecondaryMap};
|
|
||||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, Global, GlobalInit, Memory, SignatureIndex};
|
|
||||||
use rand::rngs::SmallRng;
|
|
||||||
use rand::{Rng, SeedableRng};
|
|
||||||
use std::cmp::min;
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::str::FromStr;
|
|
||||||
use target_lexicon::triple;
|
|
||||||
|
|
||||||
// Since cache system is a global thing, each test needs to be run in seperate process.
|
// Since cache system is a global thing, each test needs to be run in seperate process.
|
||||||
// So, init() tests are run as integration tests.
|
// So, init() tests are run as integration tests.
|
||||||
@@ -73,272 +60,45 @@ fn test_write_read_cache() {
|
|||||||
fs::canonicalize(cache_dir).unwrap()
|
fs::canonicalize(cache_dir).unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut rng = SmallRng::from_seed([
|
|
||||||
0x42, 0x04, 0xF3, 0x44, 0x11, 0x22, 0x33, 0x44, 0x67, 0x68, 0xFF, 0x00, 0x44, 0x23, 0x7F,
|
|
||||||
0x96,
|
|
||||||
]);
|
|
||||||
|
|
||||||
let mut code_container = Vec::new();
|
|
||||||
code_container.resize(0x4000, 0);
|
|
||||||
rng.fill(&mut code_container[..]);
|
|
||||||
|
|
||||||
let isa1 = new_isa("riscv64-unknown-unknown");
|
|
||||||
let isa2 = new_isa("i386");
|
|
||||||
let module1 = new_module(&mut rng);
|
|
||||||
let module2 = new_module(&mut rng);
|
|
||||||
let function_body_inputs1 = new_function_body_inputs(&mut rng, &code_container);
|
|
||||||
let function_body_inputs2 = new_function_body_inputs(&mut rng, &code_container);
|
|
||||||
let compiler1 = "test-1";
|
let compiler1 = "test-1";
|
||||||
let compiler2 = "test-2";
|
let compiler2 = "test-2";
|
||||||
|
|
||||||
let entry1 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new(
|
let entry1 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new(compiler1, &cache_config));
|
||||||
&module1,
|
let entry2 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new(compiler2, &cache_config));
|
||||||
&function_body_inputs1,
|
|
||||||
&*isa1,
|
|
||||||
compiler1,
|
|
||||||
false,
|
|
||||||
&cache_config,
|
|
||||||
));
|
|
||||||
assert!(entry1.0.is_some());
|
|
||||||
assert!(entry1.get_data().is_none());
|
|
||||||
let data1 = new_module_cache_data(&mut rng);
|
|
||||||
entry1.update_data(&data1);
|
|
||||||
assert_eq!(entry1.get_data().expect("Cache should be available"), data1);
|
|
||||||
|
|
||||||
let entry2 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new(
|
entry1.get_data(1, |_| new_module_cache_data()).unwrap();
|
||||||
&module2,
|
entry1.get_data::<_, i32>(1, |_| panic!()).unwrap();
|
||||||
&function_body_inputs1,
|
|
||||||
&*isa1,
|
|
||||||
compiler1,
|
|
||||||
false,
|
|
||||||
&cache_config,
|
|
||||||
));
|
|
||||||
let data2 = new_module_cache_data(&mut rng);
|
|
||||||
entry2.update_data(&data2);
|
|
||||||
assert_eq!(entry1.get_data().expect("Cache should be available"), data1);
|
|
||||||
assert_eq!(entry2.get_data().expect("Cache should be available"), data2);
|
|
||||||
|
|
||||||
let entry3 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new(
|
entry1.get_data(2, |_| new_module_cache_data()).unwrap();
|
||||||
&module1,
|
entry1.get_data::<_, i32>(1, |_| panic!()).unwrap();
|
||||||
&function_body_inputs2,
|
entry1.get_data::<_, i32>(2, |_| panic!()).unwrap();
|
||||||
&*isa1,
|
|
||||||
compiler1,
|
|
||||||
false,
|
|
||||||
&cache_config,
|
|
||||||
));
|
|
||||||
let data3 = new_module_cache_data(&mut rng);
|
|
||||||
entry3.update_data(&data3);
|
|
||||||
assert_eq!(entry1.get_data().expect("Cache should be available"), data1);
|
|
||||||
assert_eq!(entry2.get_data().expect("Cache should be available"), data2);
|
|
||||||
assert_eq!(entry3.get_data().expect("Cache should be available"), data3);
|
|
||||||
|
|
||||||
let entry4 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new(
|
entry1.get_data(3, |_| new_module_cache_data()).unwrap();
|
||||||
&module1,
|
entry1.get_data::<_, i32>(1, |_| panic!()).unwrap();
|
||||||
&function_body_inputs1,
|
entry1.get_data::<_, i32>(2, |_| panic!()).unwrap();
|
||||||
&*isa2,
|
entry1.get_data::<_, i32>(3, |_| panic!()).unwrap();
|
||||||
compiler1,
|
|
||||||
false,
|
|
||||||
&cache_config,
|
|
||||||
));
|
|
||||||
let data4 = new_module_cache_data(&mut rng);
|
|
||||||
entry4.update_data(&data4);
|
|
||||||
assert_eq!(entry1.get_data().expect("Cache should be available"), data1);
|
|
||||||
assert_eq!(entry2.get_data().expect("Cache should be available"), data2);
|
|
||||||
assert_eq!(entry3.get_data().expect("Cache should be available"), data3);
|
|
||||||
assert_eq!(entry4.get_data().expect("Cache should be available"), data4);
|
|
||||||
|
|
||||||
let entry5 = ModuleCacheEntry::from_inner(ModuleCacheEntryInner::new(
|
entry1.get_data(4, |_| new_module_cache_data()).unwrap();
|
||||||
&module1,
|
entry1.get_data::<_, i32>(1, |_| panic!()).unwrap();
|
||||||
&function_body_inputs1,
|
entry1.get_data::<_, i32>(2, |_| panic!()).unwrap();
|
||||||
&*isa1,
|
entry1.get_data::<_, i32>(3, |_| panic!()).unwrap();
|
||||||
compiler2,
|
entry1.get_data::<_, i32>(4, |_| panic!()).unwrap();
|
||||||
false,
|
|
||||||
&cache_config,
|
|
||||||
));
|
|
||||||
let data5 = new_module_cache_data(&mut rng);
|
|
||||||
entry5.update_data(&data5);
|
|
||||||
assert_eq!(entry1.get_data().expect("Cache should be available"), data1);
|
|
||||||
assert_eq!(entry2.get_data().expect("Cache should be available"), data2);
|
|
||||||
assert_eq!(entry3.get_data().expect("Cache should be available"), data3);
|
|
||||||
assert_eq!(entry4.get_data().expect("Cache should be available"), data4);
|
|
||||||
assert_eq!(entry5.get_data().expect("Cache should be available"), data5);
|
|
||||||
|
|
||||||
let data6 = new_module_cache_data(&mut rng);
|
entry2.get_data(1, |_| new_module_cache_data()).unwrap();
|
||||||
entry1.update_data(&data6);
|
entry1.get_data::<_, i32>(1, |_| panic!()).unwrap();
|
||||||
assert_eq!(entry1.get_data().expect("Cache should be available"), data6);
|
entry1.get_data::<_, i32>(2, |_| panic!()).unwrap();
|
||||||
assert_eq!(entry2.get_data().expect("Cache should be available"), data2);
|
entry1.get_data::<_, i32>(3, |_| panic!()).unwrap();
|
||||||
assert_eq!(entry3.get_data().expect("Cache should be available"), data3);
|
entry1.get_data::<_, i32>(4, |_| panic!()).unwrap();
|
||||||
assert_eq!(entry4.get_data().expect("Cache should be available"), data4);
|
entry2.get_data::<_, i32>(1, |_| panic!()).unwrap();
|
||||||
assert_eq!(entry5.get_data().expect("Cache should be available"), data5);
|
|
||||||
|
|
||||||
assert!(data1 != data2 && data1 != data3 && data1 != data4 && data1 != data5 && data1 != data6);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_isa(name: &str) -> Box<dyn isa::TargetIsa> {
|
fn new_module_cache_data() -> Result<ModuleCacheDataTupleType, ()> {
|
||||||
let shared_builder = settings::builder();
|
Ok((
|
||||||
let shared_flags = settings::Flags::new(shared_builder);
|
Compilation::new(PrimaryMap::new()),
|
||||||
isa::lookup(triple!(name))
|
PrimaryMap::new(),
|
||||||
.expect("can't find specified isa")
|
PrimaryMap::new(),
|
||||||
.finish(shared_flags)
|
PrimaryMap::new(),
|
||||||
}
|
PrimaryMap::new(),
|
||||||
|
PrimaryMap::new(),
|
||||||
fn new_module(rng: &mut impl Rng) -> Module {
|
|
||||||
// There are way too many fields. Just fill in some of them.
|
|
||||||
let mut m = Module::new();
|
|
||||||
|
|
||||||
if rng.gen_bool(0.5) {
|
|
||||||
m.signatures.push(ir::Signature {
|
|
||||||
params: vec![],
|
|
||||||
returns: vec![],
|
|
||||||
call_conv: isa::CallConv::Fast,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..rng.gen_range(1, 0x8) {
|
|
||||||
m.functions.push(SignatureIndex::new(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
if rng.gen_bool(0.8) {
|
|
||||||
m.memory_plans.push(MemoryPlan {
|
|
||||||
memory: Memory {
|
|
||||||
minimum: rng.gen(),
|
|
||||||
maximum: rng.gen(),
|
|
||||||
shared: rng.gen(),
|
|
||||||
},
|
|
||||||
style: MemoryStyle::Dynamic,
|
|
||||||
offset_guard_size: rng.gen(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if rng.gen_bool(0.4) {
|
|
||||||
m.globals.push(Global {
|
|
||||||
ty: ir::Type::int(16).unwrap(),
|
|
||||||
mutability: rng.gen(),
|
|
||||||
initializer: GlobalInit::I32Const(rng.gen()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
m
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_function_body_inputs<'data>(
|
|
||||||
rng: &mut impl Rng,
|
|
||||||
code_container: &'data Vec<u8>,
|
|
||||||
) -> PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>> {
|
|
||||||
let len = code_container.len();
|
|
||||||
let mut pos = rng.gen_range(0, code_container.len());
|
|
||||||
(2..rng.gen_range(4, 14))
|
|
||||||
.map(|j| {
|
|
||||||
let (old_pos, end) = (pos, min(pos + rng.gen_range(0x10, 0x200), len));
|
|
||||||
pos = end % len;
|
|
||||||
FunctionBodyData {
|
|
||||||
data: &code_container[old_pos..end],
|
|
||||||
module_offset: (rng.next_u64() + j) as usize,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_module_cache_data(rng: &mut impl Rng) -> ModuleCacheData {
|
|
||||||
let funcs = (0..rng.gen_range(0, 10))
|
|
||||||
.map(|i| {
|
|
||||||
let mut sm = SecondaryMap::new(); // doesn't implement from iterator
|
|
||||||
sm.resize(i as usize * 2);
|
|
||||||
sm.values_mut().enumerate().for_each(|(j, v)| {
|
|
||||||
if rng.gen_bool(0.33) {
|
|
||||||
*v = (j as u32) * 3 / 4
|
|
||||||
}
|
|
||||||
});
|
|
||||||
CompiledFunction {
|
|
||||||
body: (0..(i * 3 / 2)).collect(),
|
|
||||||
jt_offsets: sm,
|
|
||||||
unwind_info: CompiledFunctionUnwindInfo::Windows((0..(i * 3 / 2)).collect()),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let relocs = (0..rng.gen_range(1, 0x10))
|
|
||||||
.map(|i| {
|
|
||||||
vec![
|
|
||||||
Relocation {
|
|
||||||
reloc: binemit::Reloc::X86CallPCRel4,
|
|
||||||
reloc_target: RelocationTarget::UserFunc(FuncIndex::new(i as usize * 42)),
|
|
||||||
offset: i + rng.next_u32(),
|
|
||||||
addend: 0,
|
|
||||||
},
|
|
||||||
Relocation {
|
|
||||||
reloc: binemit::Reloc::Arm32Call,
|
|
||||||
reloc_target: RelocationTarget::LibCall(ir::LibCall::CeilF64),
|
|
||||||
offset: rng.gen_range(4, i + 55),
|
|
||||||
addend: (42 * i) as i64,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let trans = (4..rng.gen_range(4, 0x10))
|
|
||||||
.map(|i| FunctionAddressMap {
|
|
||||||
instructions: vec![InstructionAddressMap {
|
|
||||||
srcloc: ir::SourceLoc::new(rng.gen()),
|
|
||||||
code_offset: rng.gen(),
|
|
||||||
code_len: i,
|
|
||||||
}],
|
|
||||||
start_srcloc: ir::SourceLoc::new(rng.gen()),
|
|
||||||
end_srcloc: ir::SourceLoc::new(rng.gen()),
|
|
||||||
body_offset: rng.gen(),
|
|
||||||
body_len: 0x31337,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let value_ranges = (4..rng.gen_range(4, 0x10))
|
|
||||||
.map(|i| {
|
|
||||||
(i..i + rng.gen_range(4, 8))
|
|
||||||
.map(|k| {
|
|
||||||
(
|
|
||||||
ir::ValueLabel::new(k),
|
|
||||||
(0..rng.gen_range(0, 4))
|
|
||||||
.map(|_| ValueLocRange {
|
|
||||||
loc: ir::ValueLoc::Reg(rng.gen()),
|
|
||||||
start: rng.gen(),
|
|
||||||
end: rng.gen(),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let stack_slots = (0..rng.gen_range(0, 0x6))
|
|
||||||
.map(|_| {
|
|
||||||
let mut slots = ir::StackSlots::new();
|
|
||||||
slots.push(ir::StackSlotData {
|
|
||||||
kind: ir::StackSlotKind::SpillSlot,
|
|
||||||
size: rng.gen(),
|
|
||||||
offset: rng.gen(),
|
|
||||||
});
|
|
||||||
slots
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let traps = (0..rng.gen_range(0, 0xd))
|
|
||||||
.map(|i| {
|
|
||||||
(i..i + rng.gen_range(0, 4))
|
|
||||||
.map(|_| TrapInformation {
|
|
||||||
code_offset: rng.gen(),
|
|
||||||
source_loc: ir::SourceLoc::new(rng.gen()),
|
|
||||||
trap_code: ir::TrapCode::StackOverflow,
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
ModuleCacheData::from_tuple((
|
|
||||||
Compilation::new(funcs),
|
|
||||||
relocs,
|
|
||||||
trans,
|
|
||||||
value_ranges,
|
|
||||||
stack_slots,
|
|
||||||
traps,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
//! Support for compiling with Cranelift.
|
//! Support for compiling with Cranelift.
|
||||||
|
|
||||||
use crate::address_map::{FunctionAddressMap, InstructionAddressMap};
|
use crate::address_map::{FunctionAddressMap, InstructionAddressMap};
|
||||||
use crate::cache::{ModuleCacheData, ModuleCacheDataTupleType, ModuleCacheEntry};
|
use crate::cache::{ModuleCacheDataTupleType, ModuleCacheEntry};
|
||||||
use crate::compilation::{
|
use crate::compilation::{
|
||||||
Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Relocation,
|
Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Relocation,
|
||||||
RelocationTarget, TrapInformation,
|
RelocationTarget, TrapInformation,
|
||||||
};
|
};
|
||||||
use crate::func_environ::{get_func_name, FuncEnvironment};
|
use crate::func_environ::{get_func_name, FuncEnvironment};
|
||||||
use crate::module::Module;
|
use crate::module::{Module, ModuleLocal};
|
||||||
use crate::module_environ::FunctionBodyData;
|
use crate::module_environ::FunctionBodyData;
|
||||||
use crate::CacheConfig;
|
use crate::CacheConfig;
|
||||||
use cranelift_codegen::ir::{self, ExternalName};
|
use cranelift_codegen::ir::{self, ExternalName};
|
||||||
@@ -16,6 +16,7 @@ use cranelift_codegen::{binemit, isa, Context};
|
|||||||
use cranelift_entity::PrimaryMap;
|
use cranelift_entity::PrimaryMap;
|
||||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator, ModuleTranslationState};
|
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator, ModuleTranslationState};
|
||||||
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
/// Implementation of a relocation sink that just saves all the information for later
|
/// Implementation of a relocation sink that just saves all the information for later
|
||||||
pub struct RelocSink {
|
pub struct RelocSink {
|
||||||
@@ -160,26 +161,45 @@ pub struct Cranelift;
|
|||||||
impl crate::compilation::Compiler for Cranelift {
|
impl crate::compilation::Compiler for Cranelift {
|
||||||
/// Compile the module using Cranelift, producing a compilation result with
|
/// Compile the module using Cranelift, producing a compilation result with
|
||||||
/// associated relocations.
|
/// associated relocations.
|
||||||
fn compile_module<'data, 'module>(
|
fn compile_module(
|
||||||
module: &'module Module,
|
module: &Module,
|
||||||
module_translation: &ModuleTranslationState,
|
module_translation: &ModuleTranslationState,
|
||||||
function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
|
function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'_>>,
|
||||||
isa: &dyn isa::TargetIsa,
|
isa: &dyn isa::TargetIsa,
|
||||||
generate_debug_info: bool,
|
generate_debug_info: bool,
|
||||||
cache_config: &CacheConfig,
|
cache_config: &CacheConfig,
|
||||||
) -> Result<ModuleCacheDataTupleType, CompileError> {
|
) -> Result<ModuleCacheDataTupleType, CompileError> {
|
||||||
let cache_entry = ModuleCacheEntry::new(
|
let cache_entry = ModuleCacheEntry::new("cranelift", cache_config);
|
||||||
module,
|
|
||||||
&function_body_inputs,
|
|
||||||
isa,
|
|
||||||
"cranelift",
|
|
||||||
generate_debug_info,
|
|
||||||
cache_config,
|
|
||||||
);
|
|
||||||
|
|
||||||
let data = match cache_entry.get_data() {
|
let data = cache_entry.get_data(
|
||||||
Some(data) => data,
|
(
|
||||||
None => {
|
&module.local,
|
||||||
|
HashedModuleTranslationState(module_translation),
|
||||||
|
function_body_inputs,
|
||||||
|
Isa(isa),
|
||||||
|
generate_debug_info,
|
||||||
|
),
|
||||||
|
compile,
|
||||||
|
)?;
|
||||||
|
Ok(data.into_tuple())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile(
|
||||||
|
(
|
||||||
|
module,
|
||||||
|
HashedModuleTranslationState(module_translation),
|
||||||
|
function_body_inputs,
|
||||||
|
Isa(isa),
|
||||||
|
generate_debug_info,
|
||||||
|
): (
|
||||||
|
&ModuleLocal,
|
||||||
|
HashedModuleTranslationState<'_>,
|
||||||
|
PrimaryMap<DefinedFuncIndex, FunctionBodyData<'_>>,
|
||||||
|
Isa<'_, '_>,
|
||||||
|
bool,
|
||||||
|
),
|
||||||
|
) -> Result<ModuleCacheDataTupleType, CompileError> {
|
||||||
let mut functions = PrimaryMap::with_capacity(function_body_inputs.len());
|
let mut functions = PrimaryMap::with_capacity(function_body_inputs.len());
|
||||||
let mut relocations = PrimaryMap::with_capacity(function_body_inputs.len());
|
let mut relocations = PrimaryMap::with_capacity(function_body_inputs.len());
|
||||||
let mut address_transforms = PrimaryMap::with_capacity(function_body_inputs.len());
|
let mut address_transforms = PrimaryMap::with_capacity(function_body_inputs.len());
|
||||||
@@ -189,14 +209,13 @@ impl crate::compilation::Compiler for Cranelift {
|
|||||||
|
|
||||||
function_body_inputs
|
function_body_inputs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<Vec<(DefinedFuncIndex, &FunctionBodyData<'data>)>>()
|
.collect::<Vec<(DefinedFuncIndex, &FunctionBodyData<'_>)>>()
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map_init(FuncTranslator::new, |func_translator, (i, input)| {
|
.map_init(FuncTranslator::new, |func_translator, (i, input)| {
|
||||||
let func_index = module.func_index(*i);
|
let func_index = module.func_index(*i);
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
context.func.name = get_func_name(func_index);
|
context.func.name = get_func_name(func_index);
|
||||||
context.func.signature =
|
context.func.signature = module.signatures[module.functions[func_index]].clone();
|
||||||
module.signatures[module.functions[func_index]].clone();
|
|
||||||
context.func.collect_frame_layout_info();
|
context.func.collect_frame_layout_info();
|
||||||
if generate_debug_info {
|
if generate_debug_info {
|
||||||
context.func.collect_debug_info();
|
context.func.collect_debug_info();
|
||||||
@@ -236,13 +255,8 @@ impl crate::compilation::Compiler for Cranelift {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let ranges = if generate_debug_info {
|
let ranges = if generate_debug_info {
|
||||||
let ranges =
|
let ranges = context.build_value_labels_ranges(isa).map_err(|error| {
|
||||||
context.build_value_labels_ranges(isa).map_err(|error| {
|
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
||||||
CompileError::Codegen(pretty_error(
|
|
||||||
&context.func,
|
|
||||||
Some(isa),
|
|
||||||
error,
|
|
||||||
))
|
|
||||||
})?;
|
})?;
|
||||||
Some(ranges)
|
Some(ranges)
|
||||||
} else {
|
} else {
|
||||||
@@ -290,19 +304,48 @@ impl crate::compilation::Compiler for Cranelift {
|
|||||||
|
|
||||||
// TODO: Reorganize where we create the Vec for the resolved imports.
|
// TODO: Reorganize where we create the Vec for the resolved imports.
|
||||||
|
|
||||||
let data = ModuleCacheData::from_tuple((
|
Ok((
|
||||||
Compilation::new(functions),
|
Compilation::new(functions),
|
||||||
relocations,
|
relocations,
|
||||||
address_transforms,
|
address_transforms,
|
||||||
value_ranges,
|
value_ranges,
|
||||||
stack_slots,
|
stack_slots,
|
||||||
traps,
|
traps,
|
||||||
));
|
))
|
||||||
cache_entry.update_data(&data);
|
|
||||||
data
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
Ok(data.into_tuple())
|
/// This is a wrapper struct to hash the specific bits of `TargetIsa` that
|
||||||
|
/// affect the output we care about. The trait itself can't implement `Hash`
|
||||||
|
/// (it's not object safe) so we have to implement our own hashing.
|
||||||
|
struct Isa<'a, 'b>(&'a (dyn isa::TargetIsa + 'b));
|
||||||
|
|
||||||
|
impl Hash for Isa<'_, '_> {
|
||||||
|
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||||
|
self.0.triple().hash(hasher);
|
||||||
|
|
||||||
|
// TODO: if this `to_string()` is too expensive then we should upstream
|
||||||
|
// a native hashing ability of flags into cranelift itself, but
|
||||||
|
// compilation and/or cache loading is relatively expensive so seems
|
||||||
|
// unlikely.
|
||||||
|
self.0.flags().to_string().hash(hasher);
|
||||||
|
|
||||||
|
// TODO: ... and should we hash anything else? There's a lot of stuff in
|
||||||
|
// `TargetIsa`, like registers/encodings/etc. Should we be hashing that
|
||||||
|
// too? It seems like wasmtime doesn't configure it too too much, but
|
||||||
|
// this may become an issue at some point.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper struct around cranelift's `ModuleTranslationState` to implement
|
||||||
|
/// `Hash` since it's not `Hash` upstream yet.
|
||||||
|
///
|
||||||
|
/// TODO: we should upstream a `Hash` implementation, it would be very small! At
|
||||||
|
/// this moment though based on the definition it should be fine to not hash it
|
||||||
|
/// since we'll re-hash the signatures later.
|
||||||
|
struct HashedModuleTranslationState<'a>(&'a ModuleTranslationState);
|
||||||
|
|
||||||
|
impl Hash for HashedModuleTranslationState<'_> {
|
||||||
|
fn hash<H: Hasher>(&self, _hasher: &mut H) {
|
||||||
|
// nothing to hash right now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::module::{MemoryPlan, MemoryStyle, Module, TableStyle};
|
use crate::module::{MemoryPlan, MemoryStyle, ModuleLocal, TableStyle};
|
||||||
use crate::vmoffsets::VMOffsets;
|
use crate::vmoffsets::VMOffsets;
|
||||||
use crate::WASM_PAGE_SIZE;
|
use crate::WASM_PAGE_SIZE;
|
||||||
use cranelift_codegen::cursor::FuncCursor;
|
use cranelift_codegen::cursor::FuncCursor;
|
||||||
@@ -59,7 +59,7 @@ pub struct FuncEnvironment<'module_environment> {
|
|||||||
target_config: TargetFrontendConfig,
|
target_config: TargetFrontendConfig,
|
||||||
|
|
||||||
/// The module-level environment which this function-level environment belongs to.
|
/// The module-level environment which this function-level environment belongs to.
|
||||||
module: &'module_environment Module,
|
module: &'module_environment ModuleLocal,
|
||||||
|
|
||||||
/// The Cranelift global holding the vmctx address.
|
/// The Cranelift global holding the vmctx address.
|
||||||
vmctx: Option<ir::GlobalValue>,
|
vmctx: Option<ir::GlobalValue>,
|
||||||
@@ -77,7 +77,10 @@ pub struct FuncEnvironment<'module_environment> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'module_environment> FuncEnvironment<'module_environment> {
|
impl<'module_environment> FuncEnvironment<'module_environment> {
|
||||||
pub fn new(target_config: TargetFrontendConfig, module: &'module_environment Module) -> Self {
|
pub fn new(
|
||||||
|
target_config: TargetFrontendConfig,
|
||||||
|
module: &'module_environment ModuleLocal,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
target_config,
|
target_config,
|
||||||
module,
|
module,
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ pub use crate::func_environ::BuiltinFunctionIndex;
|
|||||||
#[cfg(feature = "lightbeam")]
|
#[cfg(feature = "lightbeam")]
|
||||||
pub use crate::lightbeam::Lightbeam;
|
pub use crate::lightbeam::Lightbeam;
|
||||||
pub use crate::module::{
|
pub use crate::module::{
|
||||||
Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle,
|
Export, MemoryPlan, MemoryStyle, Module, ModuleLocal, TableElements, TablePlan, TableStyle,
|
||||||
};
|
};
|
||||||
pub use crate::module_environ::{
|
pub use crate::module_environ::{
|
||||||
translate_signature, DataInitializer, DataInitializerLocation, FunctionBodyData,
|
translate_signature, DataInitializer, DataInitializerLocation, FunctionBodyData,
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ impl crate::compilation::Compiler for Lightbeam {
|
|||||||
return Err(CompileError::DebugInfoNotSupported);
|
return Err(CompileError::DebugInfoNotSupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
let env = FuncEnvironment::new(isa.frontend_config(), module);
|
let env = FuncEnvironment::new(isa.frontend_config(), &module.local);
|
||||||
let mut relocations = PrimaryMap::new();
|
let mut relocations = PrimaryMap::new();
|
||||||
let mut codegen_session: lightbeam::CodeGenSession<_> =
|
let mut codegen_session: lightbeam::CodeGenSession<_> =
|
||||||
lightbeam::CodeGenSession::new(function_body_inputs.len() as u32, &env);
|
lightbeam::CodeGenSession::new(function_body_inputs.len() as u32, &env);
|
||||||
|
|
||||||
for (i, function_body) in &function_body_inputs {
|
for (i, function_body) in &function_body_inputs {
|
||||||
let func_index = module.func_index(i);
|
let func_index = module.local.func_index(i);
|
||||||
let mut reloc_sink = RelocSink::new(func_index);
|
let mut reloc_sink = RelocSink::new(func_index);
|
||||||
|
|
||||||
lightbeam::translate_function(
|
lightbeam::translate_function(
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
//! Data structures for representing decoded wasm modules.
|
//! Data structures for representing decoded wasm modules.
|
||||||
|
|
||||||
use crate::module_environ::FunctionBodyData;
|
|
||||||
use crate::tunables::Tunables;
|
use crate::tunables::Tunables;
|
||||||
use crate::WASM_MAX_PAGES;
|
use crate::WASM_MAX_PAGES;
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
@@ -12,7 +11,6 @@ use cranelift_wasm::{
|
|||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use more_asserts::assert_ge;
|
use more_asserts::assert_ge;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||||
|
|
||||||
/// A WebAssembly table initializer.
|
/// A WebAssembly table initializer.
|
||||||
@@ -134,14 +132,16 @@ impl TablePlan {
|
|||||||
|
|
||||||
/// A translated WebAssembly module, excluding the function bodies and
|
/// A translated WebAssembly module, excluding the function bodies and
|
||||||
/// memory initializers.
|
/// memory initializers.
|
||||||
// WARNING: when modifying, make sure that `hash_for_cache` is still valid!
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
/// A unique identifier (within this process) for this module.
|
/// A unique identifier (within this process) for this module.
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
|
|
||||||
/// Unprocessed signatures exactly as provided by `declare_signature()`.
|
/// Local information about a module which is the bare minimum necessary to
|
||||||
pub signatures: PrimaryMap<SignatureIndex, ir::Signature>,
|
/// translate a function body. This is derived as `Hash` whereas this module
|
||||||
|
/// isn't, since it contains too much information needed to translate a
|
||||||
|
/// function.
|
||||||
|
pub local: ModuleLocal,
|
||||||
|
|
||||||
/// Names of imported functions, as well as the index of the import that
|
/// Names of imported functions, as well as the index of the import that
|
||||||
/// performed this import.
|
/// performed this import.
|
||||||
@@ -156,18 +156,6 @@ pub struct Module {
|
|||||||
/// Names of imported globals.
|
/// Names of imported globals.
|
||||||
pub imported_globals: PrimaryMap<GlobalIndex, (String, String, u32)>,
|
pub imported_globals: PrimaryMap<GlobalIndex, (String, String, u32)>,
|
||||||
|
|
||||||
/// Types of functions, imported and local.
|
|
||||||
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
|
|
||||||
|
|
||||||
/// WebAssembly tables.
|
|
||||||
pub table_plans: PrimaryMap<TableIndex, TablePlan>,
|
|
||||||
|
|
||||||
/// WebAssembly linear memory plans.
|
|
||||||
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
|
|
||||||
|
|
||||||
/// WebAssembly global variables.
|
|
||||||
pub globals: PrimaryMap<GlobalIndex, Global>,
|
|
||||||
|
|
||||||
/// Exported entities.
|
/// Exported entities.
|
||||||
pub exports: IndexMap<String, Export>,
|
pub exports: IndexMap<String, Export>,
|
||||||
|
|
||||||
@@ -181,6 +169,42 @@ pub struct Module {
|
|||||||
pub func_names: HashMap<FuncIndex, String>,
|
pub func_names: HashMap<FuncIndex, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Local information known about a wasm module, the bare minimum necessary to
|
||||||
|
/// translate function bodies.
|
||||||
|
///
|
||||||
|
/// This is stored within a `Module` and it implements `Hash`, unlike `Module`,
|
||||||
|
/// and is used as part of the cache key when we load compiled modules from the
|
||||||
|
/// global cache.
|
||||||
|
#[derive(Debug, Hash)]
|
||||||
|
pub struct ModuleLocal {
|
||||||
|
/// Unprocessed signatures exactly as provided by `declare_signature()`.
|
||||||
|
pub signatures: PrimaryMap<SignatureIndex, ir::Signature>,
|
||||||
|
|
||||||
|
/// Number of imported functions in the module.
|
||||||
|
pub num_imported_funcs: usize,
|
||||||
|
|
||||||
|
/// Number of imported tables in the module.
|
||||||
|
pub num_imported_tables: usize,
|
||||||
|
|
||||||
|
/// Number of imported memories in the module.
|
||||||
|
pub num_imported_memories: usize,
|
||||||
|
|
||||||
|
/// Number of imported globals in the module.
|
||||||
|
pub num_imported_globals: usize,
|
||||||
|
|
||||||
|
/// Types of functions, imported and local.
|
||||||
|
pub functions: PrimaryMap<FuncIndex, SignatureIndex>,
|
||||||
|
|
||||||
|
/// WebAssembly tables.
|
||||||
|
pub table_plans: PrimaryMap<TableIndex, TablePlan>,
|
||||||
|
|
||||||
|
/// WebAssembly linear memory plans.
|
||||||
|
pub memory_plans: PrimaryMap<MemoryIndex, MemoryPlan>,
|
||||||
|
|
||||||
|
/// WebAssembly global variables.
|
||||||
|
pub globals: PrimaryMap<GlobalIndex, Global>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
/// Allocates the module data structures.
|
/// Allocates the module data structures.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@@ -188,132 +212,115 @@ impl Module {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
id: NEXT_ID.fetch_add(1, SeqCst),
|
id: NEXT_ID.fetch_add(1, SeqCst),
|
||||||
signatures: PrimaryMap::new(),
|
|
||||||
imported_funcs: PrimaryMap::new(),
|
imported_funcs: PrimaryMap::new(),
|
||||||
imported_tables: PrimaryMap::new(),
|
imported_tables: PrimaryMap::new(),
|
||||||
imported_memories: PrimaryMap::new(),
|
imported_memories: PrimaryMap::new(),
|
||||||
imported_globals: PrimaryMap::new(),
|
imported_globals: PrimaryMap::new(),
|
||||||
functions: PrimaryMap::new(),
|
|
||||||
table_plans: PrimaryMap::new(),
|
|
||||||
memory_plans: PrimaryMap::new(),
|
|
||||||
globals: PrimaryMap::new(),
|
|
||||||
exports: IndexMap::new(),
|
exports: IndexMap::new(),
|
||||||
start_func: None,
|
start_func: None,
|
||||||
table_elements: Vec::new(),
|
table_elements: Vec::new(),
|
||||||
func_names: HashMap::new(),
|
func_names: HashMap::new(),
|
||||||
|
local: ModuleLocal {
|
||||||
|
num_imported_funcs: 0,
|
||||||
|
num_imported_tables: 0,
|
||||||
|
num_imported_memories: 0,
|
||||||
|
num_imported_globals: 0,
|
||||||
|
signatures: PrimaryMap::new(),
|
||||||
|
functions: PrimaryMap::new(),
|
||||||
|
table_plans: PrimaryMap::new(),
|
||||||
|
memory_plans: PrimaryMap::new(),
|
||||||
|
globals: PrimaryMap::new(),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ModuleLocal {
|
||||||
/// Convert a `DefinedFuncIndex` into a `FuncIndex`.
|
/// Convert a `DefinedFuncIndex` into a `FuncIndex`.
|
||||||
pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex {
|
pub fn func_index(&self, defined_func: DefinedFuncIndex) -> FuncIndex {
|
||||||
FuncIndex::new(self.imported_funcs.len() + defined_func.index())
|
FuncIndex::new(self.num_imported_funcs + defined_func.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a `FuncIndex` into a `DefinedFuncIndex`. Returns None if the
|
/// Convert a `FuncIndex` into a `DefinedFuncIndex`. Returns None if the
|
||||||
/// index is an imported function.
|
/// index is an imported function.
|
||||||
pub fn defined_func_index(&self, func: FuncIndex) -> Option<DefinedFuncIndex> {
|
pub fn defined_func_index(&self, func: FuncIndex) -> Option<DefinedFuncIndex> {
|
||||||
if func.index() < self.imported_funcs.len() {
|
if func.index() < self.num_imported_funcs {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(DefinedFuncIndex::new(
|
Some(DefinedFuncIndex::new(
|
||||||
func.index() - self.imported_funcs.len(),
|
func.index() - self.num_imported_funcs,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test whether the given function index is for an imported function.
|
/// Test whether the given function index is for an imported function.
|
||||||
pub fn is_imported_function(&self, index: FuncIndex) -> bool {
|
pub fn is_imported_function(&self, index: FuncIndex) -> bool {
|
||||||
index.index() < self.imported_funcs.len()
|
index.index() < self.num_imported_funcs
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a `DefinedTableIndex` into a `TableIndex`.
|
/// Convert a `DefinedTableIndex` into a `TableIndex`.
|
||||||
pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex {
|
pub fn table_index(&self, defined_table: DefinedTableIndex) -> TableIndex {
|
||||||
TableIndex::new(self.imported_tables.len() + defined_table.index())
|
TableIndex::new(self.num_imported_tables + defined_table.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a `TableIndex` into a `DefinedTableIndex`. Returns None if the
|
/// Convert a `TableIndex` into a `DefinedTableIndex`. Returns None if the
|
||||||
/// index is an imported table.
|
/// index is an imported table.
|
||||||
pub fn defined_table_index(&self, table: TableIndex) -> Option<DefinedTableIndex> {
|
pub fn defined_table_index(&self, table: TableIndex) -> Option<DefinedTableIndex> {
|
||||||
if table.index() < self.imported_tables.len() {
|
if table.index() < self.num_imported_tables {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(DefinedTableIndex::new(
|
Some(DefinedTableIndex::new(
|
||||||
table.index() - self.imported_tables.len(),
|
table.index() - self.num_imported_tables,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test whether the given table index is for an imported table.
|
/// Test whether the given table index is for an imported table.
|
||||||
pub fn is_imported_table(&self, index: TableIndex) -> bool {
|
pub fn is_imported_table(&self, index: TableIndex) -> bool {
|
||||||
index.index() < self.imported_tables.len()
|
index.index() < self.num_imported_tables
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a `DefinedMemoryIndex` into a `MemoryIndex`.
|
/// Convert a `DefinedMemoryIndex` into a `MemoryIndex`.
|
||||||
pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex {
|
pub fn memory_index(&self, defined_memory: DefinedMemoryIndex) -> MemoryIndex {
|
||||||
MemoryIndex::new(self.imported_memories.len() + defined_memory.index())
|
MemoryIndex::new(self.num_imported_memories + defined_memory.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a `MemoryIndex` into a `DefinedMemoryIndex`. Returns None if the
|
/// Convert a `MemoryIndex` into a `DefinedMemoryIndex`. Returns None if the
|
||||||
/// index is an imported memory.
|
/// index is an imported memory.
|
||||||
pub fn defined_memory_index(&self, memory: MemoryIndex) -> Option<DefinedMemoryIndex> {
|
pub fn defined_memory_index(&self, memory: MemoryIndex) -> Option<DefinedMemoryIndex> {
|
||||||
if memory.index() < self.imported_memories.len() {
|
if memory.index() < self.num_imported_memories {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(DefinedMemoryIndex::new(
|
Some(DefinedMemoryIndex::new(
|
||||||
memory.index() - self.imported_memories.len(),
|
memory.index() - self.num_imported_memories,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test whether the given memory index is for an imported memory.
|
/// Test whether the given memory index is for an imported memory.
|
||||||
pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
|
pub fn is_imported_memory(&self, index: MemoryIndex) -> bool {
|
||||||
index.index() < self.imported_memories.len()
|
index.index() < self.num_imported_memories
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a `DefinedGlobalIndex` into a `GlobalIndex`.
|
/// Convert a `DefinedGlobalIndex` into a `GlobalIndex`.
|
||||||
pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex {
|
pub fn global_index(&self, defined_global: DefinedGlobalIndex) -> GlobalIndex {
|
||||||
GlobalIndex::new(self.imported_globals.len() + defined_global.index())
|
GlobalIndex::new(self.num_imported_globals + defined_global.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a `GlobalIndex` into a `DefinedGlobalIndex`. Returns None if the
|
/// Convert a `GlobalIndex` into a `DefinedGlobalIndex`. Returns None if the
|
||||||
/// index is an imported global.
|
/// index is an imported global.
|
||||||
pub fn defined_global_index(&self, global: GlobalIndex) -> Option<DefinedGlobalIndex> {
|
pub fn defined_global_index(&self, global: GlobalIndex) -> Option<DefinedGlobalIndex> {
|
||||||
if global.index() < self.imported_globals.len() {
|
if global.index() < self.num_imported_globals {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(DefinedGlobalIndex::new(
|
Some(DefinedGlobalIndex::new(
|
||||||
global.index() - self.imported_globals.len(),
|
global.index() - self.num_imported_globals,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test whether the given global index is for an imported global.
|
/// Test whether the given global index is for an imported global.
|
||||||
pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
|
pub fn is_imported_global(&self, index: GlobalIndex) -> bool {
|
||||||
index.index() < self.imported_globals.len()
|
index.index() < self.num_imported_globals
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes hash of the module for the purpose of caching.
|
|
||||||
pub fn hash_for_cache<'data, H>(
|
|
||||||
&self,
|
|
||||||
function_body_inputs: &PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
|
|
||||||
state: &mut H,
|
|
||||||
) where
|
|
||||||
H: Hasher,
|
|
||||||
{
|
|
||||||
// There's no need to cache names (strings), start function
|
|
||||||
// and data initializers (for both memory and tables)
|
|
||||||
self.signatures.hash(state);
|
|
||||||
self.functions.hash(state);
|
|
||||||
self.table_plans.hash(state);
|
|
||||||
self.memory_plans.hash(state);
|
|
||||||
self.globals.hash(state);
|
|
||||||
// IndexMap (self.export) iterates over values in order of item inserts
|
|
||||||
// Let's actually sort the values.
|
|
||||||
let mut exports = self.exports.values().collect::<Vec<_>>();
|
|
||||||
exports.sort();
|
|
||||||
for val in exports {
|
|
||||||
val.hash(state);
|
|
||||||
}
|
|
||||||
function_body_inputs.hash(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ pub struct ModuleTranslation<'data> {
|
|||||||
impl<'data> ModuleTranslation<'data> {
|
impl<'data> ModuleTranslation<'data> {
|
||||||
/// Return a new `FuncEnvironment` for translating a function.
|
/// Return a new `FuncEnvironment` for translating a function.
|
||||||
pub fn func_env(&self) -> FuncEnvironment<'_> {
|
pub fn func_env(&self) -> FuncEnvironment<'_> {
|
||||||
FuncEnvironment::new(self.target_config, &self.module)
|
FuncEnvironment::new(self.target_config, &self.module.local)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +109,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
fn reserve_signatures(&mut self, num: u32) -> WasmResult<()> {
|
fn reserve_signatures(&mut self, num: u32) -> WasmResult<()> {
|
||||||
self.result
|
self.result
|
||||||
.module
|
.module
|
||||||
|
.local
|
||||||
.signatures
|
.signatures
|
||||||
.reserve_exact(usize::try_from(num).unwrap());
|
.reserve_exact(usize::try_from(num).unwrap());
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -117,7 +118,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
fn declare_signature(&mut self, sig: ir::Signature) -> WasmResult<()> {
|
fn declare_signature(&mut self, sig: ir::Signature) -> WasmResult<()> {
|
||||||
let sig = translate_signature(sig, self.pointer_type());
|
let sig = translate_signature(sig, self.pointer_type());
|
||||||
// TODO: Deduplicate signatures.
|
// TODO: Deduplicate signatures.
|
||||||
self.result.module.signatures.push(sig);
|
self.result.module.local.signatures.push(sig);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,35 +129,37 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
field: &str,
|
field: &str,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
self.result.module.functions.len(),
|
self.result.module.local.functions.len(),
|
||||||
self.result.module.imported_funcs.len(),
|
self.result.module.imported_funcs.len(),
|
||||||
"Imported functions must be declared first"
|
"Imported functions must be declared first"
|
||||||
);
|
);
|
||||||
self.result.module.functions.push(sig_index);
|
self.result.module.local.functions.push(sig_index);
|
||||||
|
|
||||||
self.result.module.imported_funcs.push((
|
self.result.module.imported_funcs.push((
|
||||||
String::from(module),
|
String::from(module),
|
||||||
String::from(field),
|
String::from(field),
|
||||||
self.imports,
|
self.imports,
|
||||||
));
|
));
|
||||||
|
self.result.module.local.num_imported_funcs += 1;
|
||||||
self.imports += 1;
|
self.imports += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_table_import(&mut self, table: Table, module: &str, field: &str) -> WasmResult<()> {
|
fn declare_table_import(&mut self, table: Table, module: &str, field: &str) -> WasmResult<()> {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
self.result.module.table_plans.len(),
|
self.result.module.local.table_plans.len(),
|
||||||
self.result.module.imported_tables.len(),
|
self.result.module.imported_tables.len(),
|
||||||
"Imported tables must be declared first"
|
"Imported tables must be declared first"
|
||||||
);
|
);
|
||||||
let plan = TablePlan::for_table(table, &self.result.tunables);
|
let plan = TablePlan::for_table(table, &self.result.tunables);
|
||||||
self.result.module.table_plans.push(plan);
|
self.result.module.local.table_plans.push(plan);
|
||||||
|
|
||||||
self.result.module.imported_tables.push((
|
self.result.module.imported_tables.push((
|
||||||
String::from(module),
|
String::from(module),
|
||||||
String::from(field),
|
String::from(field),
|
||||||
self.imports,
|
self.imports,
|
||||||
));
|
));
|
||||||
|
self.result.module.local.num_imported_tables += 1;
|
||||||
self.imports += 1;
|
self.imports += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -168,18 +171,19 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
field: &str,
|
field: &str,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
self.result.module.memory_plans.len(),
|
self.result.module.local.memory_plans.len(),
|
||||||
self.result.module.imported_memories.len(),
|
self.result.module.imported_memories.len(),
|
||||||
"Imported memories must be declared first"
|
"Imported memories must be declared first"
|
||||||
);
|
);
|
||||||
let plan = MemoryPlan::for_memory(memory, &self.result.tunables);
|
let plan = MemoryPlan::for_memory(memory, &self.result.tunables);
|
||||||
self.result.module.memory_plans.push(plan);
|
self.result.module.local.memory_plans.push(plan);
|
||||||
|
|
||||||
self.result.module.imported_memories.push((
|
self.result.module.imported_memories.push((
|
||||||
String::from(module),
|
String::from(module),
|
||||||
String::from(field),
|
String::from(field),
|
||||||
self.imports,
|
self.imports,
|
||||||
));
|
));
|
||||||
|
self.result.module.local.num_imported_memories += 1;
|
||||||
self.imports += 1;
|
self.imports += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -191,17 +195,18 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
field: &str,
|
field: &str,
|
||||||
) -> WasmResult<()> {
|
) -> WasmResult<()> {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
self.result.module.globals.len(),
|
self.result.module.local.globals.len(),
|
||||||
self.result.module.imported_globals.len(),
|
self.result.module.imported_globals.len(),
|
||||||
"Imported globals must be declared first"
|
"Imported globals must be declared first"
|
||||||
);
|
);
|
||||||
self.result.module.globals.push(global);
|
self.result.module.local.globals.push(global);
|
||||||
|
|
||||||
self.result.module.imported_globals.push((
|
self.result.module.imported_globals.push((
|
||||||
String::from(module),
|
String::from(module),
|
||||||
String::from(field),
|
String::from(field),
|
||||||
self.imports,
|
self.imports,
|
||||||
));
|
));
|
||||||
|
self.result.module.local.num_imported_globals += 1;
|
||||||
self.imports += 1;
|
self.imports += 1;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -217,6 +222,7 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
fn reserve_func_types(&mut self, num: u32) -> WasmResult<()> {
|
fn reserve_func_types(&mut self, num: u32) -> WasmResult<()> {
|
||||||
self.result
|
self.result
|
||||||
.module
|
.module
|
||||||
|
.local
|
||||||
.functions
|
.functions
|
||||||
.reserve_exact(usize::try_from(num).unwrap());
|
.reserve_exact(usize::try_from(num).unwrap());
|
||||||
self.result
|
self.result
|
||||||
@@ -226,13 +232,14 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> {
|
fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> {
|
||||||
self.result.module.functions.push(sig_index);
|
self.result.module.local.functions.push(sig_index);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reserve_tables(&mut self, num: u32) -> WasmResult<()> {
|
fn reserve_tables(&mut self, num: u32) -> WasmResult<()> {
|
||||||
self.result
|
self.result
|
||||||
.module
|
.module
|
||||||
|
.local
|
||||||
.table_plans
|
.table_plans
|
||||||
.reserve_exact(usize::try_from(num).unwrap());
|
.reserve_exact(usize::try_from(num).unwrap());
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -240,13 +247,14 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
|
|
||||||
fn declare_table(&mut self, table: Table) -> WasmResult<()> {
|
fn declare_table(&mut self, table: Table) -> WasmResult<()> {
|
||||||
let plan = TablePlan::for_table(table, &self.result.tunables);
|
let plan = TablePlan::for_table(table, &self.result.tunables);
|
||||||
self.result.module.table_plans.push(plan);
|
self.result.module.local.table_plans.push(plan);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reserve_memories(&mut self, num: u32) -> WasmResult<()> {
|
fn reserve_memories(&mut self, num: u32) -> WasmResult<()> {
|
||||||
self.result
|
self.result
|
||||||
.module
|
.module
|
||||||
|
.local
|
||||||
.memory_plans
|
.memory_plans
|
||||||
.reserve_exact(usize::try_from(num).unwrap());
|
.reserve_exact(usize::try_from(num).unwrap());
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -254,20 +262,21 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
|
|||||||
|
|
||||||
fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> {
|
fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> {
|
||||||
let plan = MemoryPlan::for_memory(memory, &self.result.tunables);
|
let plan = MemoryPlan::for_memory(memory, &self.result.tunables);
|
||||||
self.result.module.memory_plans.push(plan);
|
self.result.module.local.memory_plans.push(plan);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reserve_globals(&mut self, num: u32) -> WasmResult<()> {
|
fn reserve_globals(&mut self, num: u32) -> WasmResult<()> {
|
||||||
self.result
|
self.result
|
||||||
.module
|
.module
|
||||||
|
.local
|
||||||
.globals
|
.globals
|
||||||
.reserve_exact(usize::try_from(num).unwrap());
|
.reserve_exact(usize::try_from(num).unwrap());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn declare_global(&mut self, global: Global) -> WasmResult<()> {
|
fn declare_global(&mut self, global: Global) -> WasmResult<()> {
|
||||||
self.result.module.globals.push(global);
|
self.result.module.local.globals.push(global);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Offsets and sizes of various structs in wasmtime-runtime's vmcontext
|
//! Offsets and sizes of various structs in wasmtime-runtime's vmcontext
|
||||||
//! module.
|
//! module.
|
||||||
|
|
||||||
use crate::module::Module;
|
use crate::module::ModuleLocal;
|
||||||
use crate::BuiltinFunctionIndex;
|
use crate::BuiltinFunctionIndex;
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use cranelift_wasm::{
|
use cranelift_wasm::{
|
||||||
@@ -50,14 +50,14 @@ pub struct VMOffsets {
|
|||||||
|
|
||||||
impl VMOffsets {
|
impl VMOffsets {
|
||||||
/// Return a new `VMOffsets` instance, for a given pointer size.
|
/// Return a new `VMOffsets` instance, for a given pointer size.
|
||||||
pub fn new(pointer_size: u8, module: &Module) -> Self {
|
pub fn new(pointer_size: u8, module: &ModuleLocal) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pointer_size,
|
pointer_size,
|
||||||
num_signature_ids: cast_to_u32(module.signatures.len()),
|
num_signature_ids: cast_to_u32(module.signatures.len()),
|
||||||
num_imported_functions: cast_to_u32(module.imported_funcs.len()),
|
num_imported_functions: cast_to_u32(module.num_imported_funcs),
|
||||||
num_imported_tables: cast_to_u32(module.imported_tables.len()),
|
num_imported_tables: cast_to_u32(module.num_imported_tables),
|
||||||
num_imported_memories: cast_to_u32(module.imported_memories.len()),
|
num_imported_memories: cast_to_u32(module.num_imported_memories),
|
||||||
num_imported_globals: cast_to_u32(module.imported_globals.len()),
|
num_imported_globals: cast_to_u32(module.num_imported_globals),
|
||||||
num_defined_tables: cast_to_u32(module.table_plans.len()),
|
num_defined_tables: cast_to_u32(module.table_plans.len()),
|
||||||
num_defined_memories: cast_to_u32(module.memory_plans.len()),
|
num_defined_memories: cast_to_u32(module.memory_plans.len()),
|
||||||
num_defined_globals: cast_to_u32(module.globals.len()),
|
num_defined_globals: cast_to_u32(module.globals.len()),
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ impl Compiler {
|
|||||||
// Translate debug info (DWARF) only if at least one function is present.
|
// Translate debug info (DWARF) only if at least one function is present.
|
||||||
let dbg = if debug_data.is_some() && !allocated_functions.is_empty() {
|
let dbg = if debug_data.is_some() && !allocated_functions.is_empty() {
|
||||||
let target_config = self.isa.frontend_config();
|
let target_config = self.isa.frontend_config();
|
||||||
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module);
|
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module.local);
|
||||||
|
|
||||||
let mut funcs = Vec::new();
|
let mut funcs = Vec::new();
|
||||||
for (i, allocated) in allocated_functions.into_iter() {
|
for (i, allocated) in allocated_functions.into_iter() {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result<I
|
|||||||
signature,
|
signature,
|
||||||
vmctx,
|
vmctx,
|
||||||
} => {
|
} => {
|
||||||
let import_signature = &module.signatures[module.functions[index]];
|
let import_signature = &module.local.signatures[module.local.functions[index]];
|
||||||
if signature != *import_signature {
|
if signature != *import_signature {
|
||||||
// TODO: If the difference is in the calling convention,
|
// TODO: If the difference is in the calling convention,
|
||||||
// we could emit a wrapper function to fix it up.
|
// we could emit a wrapper function to fix it up.
|
||||||
@@ -68,7 +68,7 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result<I
|
|||||||
vmctx,
|
vmctx,
|
||||||
table,
|
table,
|
||||||
} => {
|
} => {
|
||||||
let import_table = &module.table_plans[index];
|
let import_table = &module.local.table_plans[index];
|
||||||
if !is_table_compatible(&table, import_table) {
|
if !is_table_compatible(&table, import_table) {
|
||||||
return Err(LinkError(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: incompatible import type: exported table incompatible with \
|
"{}/{}: incompatible import type: exported table incompatible with \
|
||||||
@@ -107,7 +107,7 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result<I
|
|||||||
vmctx,
|
vmctx,
|
||||||
memory,
|
memory,
|
||||||
} => {
|
} => {
|
||||||
let import_memory = &module.memory_plans[index];
|
let import_memory = &module.local.memory_plans[index];
|
||||||
if !is_memory_compatible(&memory, import_memory) {
|
if !is_memory_compatible(&memory, import_memory) {
|
||||||
return Err(LinkError(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: incompatible import type: exported memory incompatible with \
|
"{}/{}: incompatible import type: exported memory incompatible with \
|
||||||
@@ -167,7 +167,7 @@ pub fn resolve_imports(module: &Module, resolver: &mut dyn Resolver) -> Result<I
|
|||||||
vmctx,
|
vmctx,
|
||||||
global,
|
global,
|
||||||
} => {
|
} => {
|
||||||
let imported_global = module.globals[index];
|
let imported_global = module.local.globals[index];
|
||||||
if !is_global_compatible(&global, &imported_global) {
|
if !is_global_compatible(&global, &imported_global) {
|
||||||
return Err(LinkError(format!(
|
return Err(LinkError(format!(
|
||||||
"{}/{}: incompatible import type: exported global incompatible with \
|
"{}/{}: incompatible import type: exported global incompatible with \
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ impl<'data> RawCompiledModule<'data> {
|
|||||||
let signature_registry = compiler.signatures();
|
let signature_registry = compiler.signatures();
|
||||||
translation
|
translation
|
||||||
.module
|
.module
|
||||||
|
.local
|
||||||
.signatures
|
.signatures
|
||||||
.values()
|
.values()
|
||||||
.map(|sig| signature_registry.register(sig))
|
.map(|sig| signature_registry.register(sig))
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ pub fn link_module(
|
|||||||
for r in function_relocs {
|
for r in function_relocs {
|
||||||
use self::libcalls::*;
|
use self::libcalls::*;
|
||||||
let target_func_address: usize = match r.reloc_target {
|
let target_func_address: usize = match r.reloc_target {
|
||||||
RelocationTarget::UserFunc(index) => match module.defined_func_index(index) {
|
RelocationTarget::UserFunc(index) => match module.local.defined_func_index(index) {
|
||||||
Some(f) => {
|
Some(f) => {
|
||||||
let fatptr: *const [VMFunctionBody] = allocated_functions[f];
|
let fatptr: *const [VMFunctionBody] = allocated_functions[f];
|
||||||
fatptr as *const VMFunctionBody as usize
|
fatptr as *const VMFunctionBody as usize
|
||||||
@@ -45,7 +45,7 @@ pub fn link_module(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
RelocationTarget::JumpTable(func_index, jt) => {
|
RelocationTarget::JumpTable(func_index, jt) => {
|
||||||
match module.defined_func_index(func_index) {
|
match module.local.defined_func_index(func_index) {
|
||||||
Some(f) => {
|
Some(f) => {
|
||||||
let offset = *jt_offsets
|
let offset = *jt_offsets
|
||||||
.get(f)
|
.get(f)
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ pub fn layout_vmcontext(
|
|||||||
module: &Module,
|
module: &Module,
|
||||||
target_config: &TargetFrontendConfig,
|
target_config: &TargetFrontendConfig,
|
||||||
) -> (Box<[u8]>, Box<[TableRelocation]>) {
|
) -> (Box<[u8]>, Box<[TableRelocation]>) {
|
||||||
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module);
|
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module.local);
|
||||||
let out_len = ofs.size_of_vmctx() as usize;
|
let out_len = ofs.size_of_vmctx() as usize;
|
||||||
let mut out = vec![0; out_len];
|
let mut out = vec![0; out_len];
|
||||||
|
|
||||||
// Assign unique indicies to unique signatures.
|
// Assign unique indicies to unique signatures.
|
||||||
let mut signature_registry = HashMap::new();
|
let mut signature_registry = HashMap::new();
|
||||||
let mut signature_registry_len = signature_registry.len();
|
let mut signature_registry_len = signature_registry.len();
|
||||||
for (index, sig) in module.signatures.iter() {
|
for (index, sig) in module.local.signatures.iter() {
|
||||||
let offset = ofs.vmctx_vmshared_signature_id(index) as usize;
|
let offset = ofs.vmctx_vmshared_signature_id(index) as usize;
|
||||||
let target_index = match signature_registry.entry(sig) {
|
let target_index = match signature_registry.entry(sig) {
|
||||||
Entry::Occupied(o) => *o.get(),
|
Entry::Occupied(o) => *o.get(),
|
||||||
@@ -43,9 +43,9 @@ pub fn layout_vmcontext(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let num_tables_imports = module.imported_tables.len();
|
let num_tables_imports = module.imported_tables.len();
|
||||||
let mut table_relocs = Vec::with_capacity(module.table_plans.len() - num_tables_imports);
|
let mut table_relocs = Vec::with_capacity(module.local.table_plans.len() - num_tables_imports);
|
||||||
for (index, table) in module.table_plans.iter().skip(num_tables_imports) {
|
for (index, table) in module.local.table_plans.iter().skip(num_tables_imports) {
|
||||||
let def_index = module.defined_table_index(index).unwrap();
|
let def_index = module.local.defined_table_index(index).unwrap();
|
||||||
let offset = ofs.vmctx_vmtable_definition(def_index) as usize;
|
let offset = ofs.vmctx_vmtable_definition(def_index) as usize;
|
||||||
let current_elements = table.table.minimum;
|
let current_elements = table.table.minimum;
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -67,8 +67,8 @@ pub fn layout_vmcontext(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let num_globals_imports = module.imported_globals.len();
|
let num_globals_imports = module.imported_globals.len();
|
||||||
for (index, global) in module.globals.iter().skip(num_globals_imports) {
|
for (index, global) in module.local.globals.iter().skip(num_globals_imports) {
|
||||||
let def_index = module.defined_global_index(index).unwrap();
|
let def_index = module.local.defined_global_index(index).unwrap();
|
||||||
let offset = ofs.vmctx_vmglobal_definition(def_index) as usize;
|
let offset = ofs.vmctx_vmglobal_definition(def_index) as usize;
|
||||||
let to = unsafe { out.as_mut_ptr().add(offset) };
|
let to = unsafe { out.as_mut_ptr().add(offset) };
|
||||||
match global.initializer {
|
match global.initializer {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub fn declare_functions(
|
|||||||
obj.declare(string_name, Decl::function_import())?;
|
obj.declare(string_name, Decl::function_import())?;
|
||||||
}
|
}
|
||||||
for (i, _function_relocs) in relocations.iter().rev() {
|
for (i, _function_relocs) in relocations.iter().rev() {
|
||||||
let func_index = module.func_index(i);
|
let func_index = module.local.func_index(i);
|
||||||
let string_name = format!("_wasm_function_{}", func_index.index());
|
let string_name = format!("_wasm_function_{}", func_index.index());
|
||||||
obj.declare(string_name, Decl::function().global())?;
|
obj.declare(string_name, Decl::function().global())?;
|
||||||
}
|
}
|
||||||
@@ -43,14 +43,14 @@ pub fn emit_functions(
|
|||||||
|
|
||||||
for (i, _function_relocs) in relocations.iter() {
|
for (i, _function_relocs) in relocations.iter() {
|
||||||
let body = &compilation.get(i).body;
|
let body = &compilation.get(i).body;
|
||||||
let func_index = module.func_index(i);
|
let func_index = module.local.func_index(i);
|
||||||
let string_name = format!("_wasm_function_{}", func_index.index());
|
let string_name = format!("_wasm_function_{}", func_index.index());
|
||||||
|
|
||||||
obj.define(string_name, body.clone())?;
|
obj.define(string_name, body.clone())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, function_relocs) in relocations.iter() {
|
for (i, function_relocs) in relocations.iter() {
|
||||||
let func_index = module.func_index(i);
|
let func_index = module.local.func_index(i);
|
||||||
let string_name = format!("_wasm_function_{}", func_index.index());
|
let string_name = format!("_wasm_function_{}", func_index.index());
|
||||||
for r in function_relocs {
|
for r in function_relocs {
|
||||||
debug_assert_eq!(r.addend, 0);
|
debug_assert_eq!(r.addend, 0);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub fn emit_module(
|
|||||||
declare_data_segment(obj, initializer, i)?;
|
declare_data_segment(obj, initializer, i)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..module.table_plans.len() {
|
for i in 0..module.local.table_plans.len() {
|
||||||
declare_table(obj, i)?;
|
declare_table(obj, i)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ pub fn emit_module(
|
|||||||
emit_data_segment(obj, initializer, i)?;
|
emit_data_segment(obj, initializer, i)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..module.table_plans.len() {
|
for i in 0..module.local.table_plans.len() {
|
||||||
emit_table(obj, i)?;
|
emit_table(obj, i)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -273,9 +273,10 @@ impl Instance {
|
|||||||
pub fn lookup_by_declaration(&self, export: &wasmtime_environ::Export) -> Export {
|
pub fn lookup_by_declaration(&self, export: &wasmtime_environ::Export) -> Export {
|
||||||
match export {
|
match export {
|
||||||
wasmtime_environ::Export::Function(index) => {
|
wasmtime_environ::Export::Function(index) => {
|
||||||
let signature = self.module.signatures[self.module.functions[*index]].clone();
|
let signature =
|
||||||
|
self.module.local.signatures[self.module.local.functions[*index]].clone();
|
||||||
let (address, vmctx) =
|
let (address, vmctx) =
|
||||||
if let Some(def_index) = self.module.defined_func_index(*index) {
|
if let Some(def_index) = self.module.local.defined_func_index(*index) {
|
||||||
(
|
(
|
||||||
self.finished_functions[def_index] as *const _,
|
self.finished_functions[def_index] as *const _,
|
||||||
self.vmctx_ptr(),
|
self.vmctx_ptr(),
|
||||||
@@ -292,7 +293,7 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
wasmtime_environ::Export::Table(index) => {
|
wasmtime_environ::Export::Table(index) => {
|
||||||
let (definition, vmctx) =
|
let (definition, vmctx) =
|
||||||
if let Some(def_index) = self.module.defined_table_index(*index) {
|
if let Some(def_index) = self.module.local.defined_table_index(*index) {
|
||||||
(self.table_ptr(def_index), self.vmctx_ptr())
|
(self.table_ptr(def_index), self.vmctx_ptr())
|
||||||
} else {
|
} else {
|
||||||
let import = self.imported_table(*index);
|
let import = self.imported_table(*index);
|
||||||
@@ -301,12 +302,12 @@ impl Instance {
|
|||||||
Export::Table {
|
Export::Table {
|
||||||
definition,
|
definition,
|
||||||
vmctx,
|
vmctx,
|
||||||
table: self.module.table_plans[*index].clone(),
|
table: self.module.local.table_plans[*index].clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wasmtime_environ::Export::Memory(index) => {
|
wasmtime_environ::Export::Memory(index) => {
|
||||||
let (definition, vmctx) =
|
let (definition, vmctx) =
|
||||||
if let Some(def_index) = self.module.defined_memory_index(*index) {
|
if let Some(def_index) = self.module.local.defined_memory_index(*index) {
|
||||||
(self.memory_ptr(def_index), self.vmctx_ptr())
|
(self.memory_ptr(def_index), self.vmctx_ptr())
|
||||||
} else {
|
} else {
|
||||||
let import = self.imported_memory(*index);
|
let import = self.imported_memory(*index);
|
||||||
@@ -315,17 +316,18 @@ impl Instance {
|
|||||||
Export::Memory {
|
Export::Memory {
|
||||||
definition,
|
definition,
|
||||||
vmctx,
|
vmctx,
|
||||||
memory: self.module.memory_plans[*index].clone(),
|
memory: self.module.local.memory_plans[*index].clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wasmtime_environ::Export::Global(index) => Export::Global {
|
wasmtime_environ::Export::Global(index) => Export::Global {
|
||||||
definition: if let Some(def_index) = self.module.defined_global_index(*index) {
|
definition: if let Some(def_index) = self.module.local.defined_global_index(*index)
|
||||||
|
{
|
||||||
self.global_ptr(def_index)
|
self.global_ptr(def_index)
|
||||||
} else {
|
} else {
|
||||||
self.imported_global(*index).from
|
self.imported_global(*index).from
|
||||||
},
|
},
|
||||||
vmctx: self.vmctx_ptr(),
|
vmctx: self.vmctx_ptr(),
|
||||||
global: self.module.globals[*index],
|
global: self.module.local.globals[*index],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -351,7 +353,8 @@ impl Instance {
|
|||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (callee_address, callee_vmctx) = match self.module.defined_func_index(start_index) {
|
let (callee_address, callee_vmctx) = match self.module.local.defined_func_index(start_index)
|
||||||
|
{
|
||||||
Some(defined_index) => {
|
Some(defined_index) => {
|
||||||
let body = *self
|
let body = *self
|
||||||
.finished_functions
|
.finished_functions
|
||||||
@@ -574,7 +577,7 @@ impl InstanceHandle {
|
|||||||
|
|
||||||
let vmctx_globals = create_globals(&module);
|
let vmctx_globals = create_globals(&module);
|
||||||
|
|
||||||
let offsets = VMOffsets::new(mem::size_of::<*const u8>() as u8, &module);
|
let offsets = VMOffsets::new(mem::size_of::<*const u8>() as u8, &module.local);
|
||||||
|
|
||||||
let handle = {
|
let handle = {
|
||||||
let instance = Instance {
|
let instance = Instance {
|
||||||
@@ -829,7 +832,7 @@ fn get_memory_init_start(init: &DataInitializer<'_>, instance: &Instance) -> usi
|
|||||||
|
|
||||||
if let Some(base) = init.location.base {
|
if let Some(base) = init.location.base {
|
||||||
let val = unsafe {
|
let val = unsafe {
|
||||||
if let Some(def_index) = instance.module.defined_global_index(base) {
|
if let Some(def_index) = instance.module.local.defined_global_index(base) {
|
||||||
*instance.global(def_index).as_u32()
|
*instance.global(def_index).as_u32()
|
||||||
} else {
|
} else {
|
||||||
*(*instance.imported_global(base).from).as_u32()
|
*(*instance.imported_global(base).from).as_u32()
|
||||||
@@ -848,6 +851,7 @@ unsafe fn get_memory_slice<'instance>(
|
|||||||
) -> &'instance mut [u8] {
|
) -> &'instance mut [u8] {
|
||||||
let memory = if let Some(defined_memory_index) = instance
|
let memory = if let Some(defined_memory_index) = instance
|
||||||
.module
|
.module
|
||||||
|
.local
|
||||||
.defined_memory_index(init.location.memory_index)
|
.defined_memory_index(init.location.memory_index)
|
||||||
{
|
{
|
||||||
instance.memory(defined_memory_index)
|
instance.memory(defined_memory_index)
|
||||||
@@ -884,8 +888,8 @@ fn check_memory_init_bounds(
|
|||||||
fn create_tables(module: &Module) -> BoxedSlice<DefinedTableIndex, Table> {
|
fn create_tables(module: &Module) -> BoxedSlice<DefinedTableIndex, Table> {
|
||||||
let num_imports = module.imported_tables.len();
|
let num_imports = module.imported_tables.len();
|
||||||
let mut tables: PrimaryMap<DefinedTableIndex, _> =
|
let mut tables: PrimaryMap<DefinedTableIndex, _> =
|
||||||
PrimaryMap::with_capacity(module.table_plans.len() - num_imports);
|
PrimaryMap::with_capacity(module.local.table_plans.len() - num_imports);
|
||||||
for table in &module.table_plans.values().as_slice()[num_imports..] {
|
for table in &module.local.table_plans.values().as_slice()[num_imports..] {
|
||||||
tables.push(Table::new(table));
|
tables.push(Table::new(table));
|
||||||
}
|
}
|
||||||
tables.into_boxed_slice()
|
tables.into_boxed_slice()
|
||||||
@@ -897,7 +901,7 @@ fn get_table_init_start(init: &TableElements, instance: &Instance) -> usize {
|
|||||||
|
|
||||||
if let Some(base) = init.base {
|
if let Some(base) = init.base {
|
||||||
let val = unsafe {
|
let val = unsafe {
|
||||||
if let Some(def_index) = instance.module.defined_global_index(base) {
|
if let Some(def_index) = instance.module.local.defined_global_index(base) {
|
||||||
*instance.global(def_index).as_u32()
|
*instance.global(def_index).as_u32()
|
||||||
} else {
|
} else {
|
||||||
*(*instance.imported_global(base).from).as_u32()
|
*(*instance.imported_global(base).from).as_u32()
|
||||||
@@ -911,7 +915,7 @@ fn get_table_init_start(init: &TableElements, instance: &Instance) -> usize {
|
|||||||
|
|
||||||
/// Return a byte-slice view of a table's data.
|
/// Return a byte-slice view of a table's data.
|
||||||
fn get_table<'instance>(init: &TableElements, instance: &'instance Instance) -> &'instance Table {
|
fn get_table<'instance>(init: &TableElements, instance: &'instance Instance) -> &'instance Table {
|
||||||
if let Some(defined_table_index) = instance.module.defined_table_index(init.table_index) {
|
if let Some(defined_table_index) = instance.module.local.defined_table_index(init.table_index) {
|
||||||
&instance.tables[defined_table_index]
|
&instance.tables[defined_table_index]
|
||||||
} else {
|
} else {
|
||||||
let import = instance.imported_table(init.table_index);
|
let import = instance.imported_table(init.table_index);
|
||||||
@@ -931,9 +935,9 @@ fn initialize_tables(instance: &Instance) -> Result<(), InstantiationError> {
|
|||||||
let table = get_table(init, instance);
|
let table = get_table(init, instance);
|
||||||
|
|
||||||
for (i, func_idx) in init.elements.iter().enumerate() {
|
for (i, func_idx) in init.elements.iter().enumerate() {
|
||||||
let callee_sig = instance.module.functions[*func_idx];
|
let callee_sig = instance.module.local.functions[*func_idx];
|
||||||
let (callee_ptr, callee_vmctx) =
|
let (callee_ptr, callee_vmctx) =
|
||||||
if let Some(index) = instance.module.defined_func_index(*func_idx) {
|
if let Some(index) = instance.module.local.defined_func_index(*func_idx) {
|
||||||
(instance.finished_functions[index] as *const _, vmctx)
|
(instance.finished_functions[index] as *const _, vmctx)
|
||||||
} else {
|
} else {
|
||||||
let imported_func = instance.imported_function(*func_idx);
|
let imported_func = instance.imported_function(*func_idx);
|
||||||
@@ -962,8 +966,8 @@ fn create_memories(
|
|||||||
) -> Result<BoxedSlice<DefinedMemoryIndex, LinearMemory>, InstantiationError> {
|
) -> Result<BoxedSlice<DefinedMemoryIndex, LinearMemory>, InstantiationError> {
|
||||||
let num_imports = module.imported_memories.len();
|
let num_imports = module.imported_memories.len();
|
||||||
let mut memories: PrimaryMap<DefinedMemoryIndex, _> =
|
let mut memories: PrimaryMap<DefinedMemoryIndex, _> =
|
||||||
PrimaryMap::with_capacity(module.memory_plans.len() - num_imports);
|
PrimaryMap::with_capacity(module.local.memory_plans.len() - num_imports);
|
||||||
for plan in &module.memory_plans.values().as_slice()[num_imports..] {
|
for plan in &module.local.memory_plans.values().as_slice()[num_imports..] {
|
||||||
memories.push(LinearMemory::new(plan).map_err(InstantiationError::Resource)?);
|
memories.push(LinearMemory::new(plan).map_err(InstantiationError::Resource)?);
|
||||||
}
|
}
|
||||||
Ok(memories.into_boxed_slice())
|
Ok(memories.into_boxed_slice())
|
||||||
@@ -990,9 +994,9 @@ fn initialize_memories(
|
|||||||
/// with initializers applied.
|
/// with initializers applied.
|
||||||
fn create_globals(module: &Module) -> BoxedSlice<DefinedGlobalIndex, VMGlobalDefinition> {
|
fn create_globals(module: &Module) -> BoxedSlice<DefinedGlobalIndex, VMGlobalDefinition> {
|
||||||
let num_imports = module.imported_globals.len();
|
let num_imports = module.imported_globals.len();
|
||||||
let mut vmctx_globals = PrimaryMap::with_capacity(module.globals.len() - num_imports);
|
let mut vmctx_globals = PrimaryMap::with_capacity(module.local.globals.len() - num_imports);
|
||||||
|
|
||||||
for _ in &module.globals.values().as_slice()[num_imports..] {
|
for _ in &module.local.globals.values().as_slice()[num_imports..] {
|
||||||
vmctx_globals.push(VMGlobalDefinition::new());
|
vmctx_globals.push(VMGlobalDefinition::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1002,8 +1006,8 @@ fn create_globals(module: &Module) -> BoxedSlice<DefinedGlobalIndex, VMGlobalDef
|
|||||||
fn initialize_globals(instance: &Instance) {
|
fn initialize_globals(instance: &Instance) {
|
||||||
let module = Arc::clone(&instance.module);
|
let module = Arc::clone(&instance.module);
|
||||||
let num_imports = module.imported_globals.len();
|
let num_imports = module.imported_globals.len();
|
||||||
for (index, global) in module.globals.iter().skip(num_imports) {
|
for (index, global) in module.local.globals.iter().skip(num_imports) {
|
||||||
let def_index = module.defined_global_index(index).unwrap();
|
let def_index = module.local.defined_global_index(index).unwrap();
|
||||||
unsafe {
|
unsafe {
|
||||||
let to = instance.global_ptr(def_index);
|
let to = instance.global_ptr(def_index);
|
||||||
match global.initializer {
|
match global.initializer {
|
||||||
@@ -1013,7 +1017,7 @@ fn initialize_globals(instance: &Instance) {
|
|||||||
GlobalInit::F64Const(x) => *(*to).as_f64_bits_mut() = x,
|
GlobalInit::F64Const(x) => *(*to).as_f64_bits_mut() = x,
|
||||||
GlobalInit::V128Const(x) => *(*to).as_u128_bits_mut() = x.0,
|
GlobalInit::V128Const(x) => *(*to).as_u128_bits_mut() = x.0,
|
||||||
GlobalInit::GetGlobal(x) => {
|
GlobalInit::GetGlobal(x) => {
|
||||||
let from = if let Some(def_x) = module.defined_global_index(x) {
|
let from = if let Some(def_x) = module.local.defined_global_index(x) {
|
||||||
instance.global(def_x)
|
instance.global(def_x)
|
||||||
} else {
|
} else {
|
||||||
*instance.imported_global(x).from
|
*instance.imported_global(x).from
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ mod test_vmfunction_import {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_vmfunction_import_offsets() {
|
fn check_vmfunction_import_offsets() {
|
||||||
let module = Module::new();
|
let module = Module::new();
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMFunctionImport>(),
|
size_of::<VMFunctionImport>(),
|
||||||
usize::from(offsets.size_of_vmfunction_import())
|
usize::from(offsets.size_of_vmfunction_import())
|
||||||
@@ -83,7 +83,7 @@ mod test_vmtable_import {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_vmtable_import_offsets() {
|
fn check_vmtable_import_offsets() {
|
||||||
let module = Module::new();
|
let module = Module::new();
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMTableImport>(),
|
size_of::<VMTableImport>(),
|
||||||
usize::from(offsets.size_of_vmtable_import())
|
usize::from(offsets.size_of_vmtable_import())
|
||||||
@@ -121,7 +121,7 @@ mod test_vmmemory_import {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_vmmemory_import_offsets() {
|
fn check_vmmemory_import_offsets() {
|
||||||
let module = Module::new();
|
let module = Module::new();
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMMemoryImport>(),
|
size_of::<VMMemoryImport>(),
|
||||||
usize::from(offsets.size_of_vmmemory_import())
|
usize::from(offsets.size_of_vmmemory_import())
|
||||||
@@ -156,7 +156,7 @@ mod test_vmglobal_import {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_vmglobal_import_offsets() {
|
fn check_vmglobal_import_offsets() {
|
||||||
let module = Module::new();
|
let module = Module::new();
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMGlobalImport>(),
|
size_of::<VMGlobalImport>(),
|
||||||
usize::from(offsets.size_of_vmglobal_import())
|
usize::from(offsets.size_of_vmglobal_import())
|
||||||
@@ -191,7 +191,7 @@ mod test_vmmemory_definition {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_vmmemory_definition_offsets() {
|
fn check_vmmemory_definition_offsets() {
|
||||||
let module = Module::new();
|
let module = Module::new();
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMMemoryDefinition>(),
|
size_of::<VMMemoryDefinition>(),
|
||||||
usize::from(offsets.size_of_vmmemory_definition())
|
usize::from(offsets.size_of_vmmemory_definition())
|
||||||
@@ -235,7 +235,7 @@ mod test_vmtable_definition {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_vmtable_definition_offsets() {
|
fn check_vmtable_definition_offsets() {
|
||||||
let module = Module::new();
|
let module = Module::new();
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMTableDefinition>(),
|
size_of::<VMTableDefinition>(),
|
||||||
usize::from(offsets.size_of_vmtable_definition())
|
usize::from(offsets.size_of_vmtable_definition())
|
||||||
@@ -281,7 +281,7 @@ mod test_vmglobal_definition {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_vmglobal_definition_offsets() {
|
fn check_vmglobal_definition_offsets() {
|
||||||
let module = Module::new();
|
let module = Module::new();
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMGlobalDefinition>(),
|
size_of::<VMGlobalDefinition>(),
|
||||||
usize::from(offsets.size_of_vmglobal_definition())
|
usize::from(offsets.size_of_vmglobal_definition())
|
||||||
@@ -291,7 +291,7 @@ mod test_vmglobal_definition {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_vmglobal_begins_aligned() {
|
fn check_vmglobal_begins_aligned() {
|
||||||
let module = Module::new();
|
let module = Module::new();
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||||
assert_eq!(offsets.vmctx_globals_begin() % 16, 0);
|
assert_eq!(offsets.vmctx_globals_begin() % 16, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,7 +438,7 @@ mod test_vmshared_signature_index {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_vmshared_signature_index() {
|
fn check_vmshared_signature_index() {
|
||||||
let module = Module::new();
|
let module = Module::new();
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMSharedSignatureIndex>(),
|
size_of::<VMSharedSignatureIndex>(),
|
||||||
usize::from(offsets.size_of_vmshared_signature_index())
|
usize::from(offsets.size_of_vmshared_signature_index())
|
||||||
@@ -492,7 +492,7 @@ mod test_vmcaller_checked_anyfunc {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_vmcaller_checked_anyfunc_offsets() {
|
fn check_vmcaller_checked_anyfunc_offsets() {
|
||||||
let module = Module::new();
|
let module = Module::new();
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMCallerCheckedAnyfunc>(),
|
size_of::<VMCallerCheckedAnyfunc>(),
|
||||||
usize::from(offsets.size_of_vmcaller_checked_anyfunc())
|
usize::from(offsets.size_of_vmcaller_checked_anyfunc())
|
||||||
@@ -570,7 +570,7 @@ mod test_vm_invoke_argument {
|
|||||||
#[test]
|
#[test]
|
||||||
fn check_vmglobal_definition_offsets() {
|
fn check_vmglobal_definition_offsets() {
|
||||||
let module = Module::new();
|
let module = Module::new();
|
||||||
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module);
|
let offsets = VMOffsets::new(size_of::<*mut u8>() as u8, &module.local);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<VMInvokeArgument>(),
|
size_of::<VMInvokeArgument>(),
|
||||||
usize::from(offsets.size_of_vmglobal_definition())
|
usize::from(offsets.size_of_vmglobal_definition())
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ pub fn compile_to_obj(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let module_vmctx_info = {
|
let module_vmctx_info = {
|
||||||
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module);
|
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module.local);
|
||||||
ModuleVmctxInfo {
|
ModuleVmctxInfo {
|
||||||
memory_offset: if ofs.num_imported_memories > 0 {
|
memory_offset: if ofs.num_imported_memories > 0 {
|
||||||
ModuleMemoryOffset::Imported(ofs.vmctx_vmmemory_import(MemoryIndex::new(0)))
|
ModuleMemoryOffset::Imported(ofs.vmctx_vmmemory_import(MemoryIndex::new(0)))
|
||||||
|
|||||||
Reference in New Issue
Block a user