diff --git a/Cargo.lock b/Cargo.lock
index 4c84df1ef3..b29cd4b615 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3564,7 +3564,6 @@ dependencies = [
"bincode",
"cfg-if 1.0.0",
"cpp_demangle",
- "cranelift-native",
"indexmap",
"lazy_static",
"libc",
@@ -3700,12 +3699,16 @@ dependencies = [
name = "wasmtime-cranelift"
version = "0.29.0"
dependencies = [
+ "anyhow",
"cranelift-codegen",
"cranelift-entity",
"cranelift-frontend",
+ "cranelift-native",
"cranelift-wasm",
+ "gimli",
"target-lexicon",
"wasmparser",
+ "wasmtime-debug",
"wasmtime-environ",
]
@@ -3727,6 +3730,7 @@ dependencies = [
name = "wasmtime-environ"
version = "0.29.0"
dependencies = [
+ "anyhow",
"cfg-if 1.0.0",
"cranelift-codegen",
"cranelift-entity",
@@ -3736,6 +3740,7 @@ dependencies = [
"log",
"more-asserts",
"serde",
+ "target-lexicon",
"thiserror",
"wasmparser",
]
@@ -3806,7 +3811,6 @@ dependencies = [
"thiserror",
"wasmparser",
"wasmtime-cranelift",
- "wasmtime-debug",
"wasmtime-environ",
"wasmtime-lightbeam",
"wasmtime-obj",
@@ -3819,8 +3823,11 @@ dependencies = [
name = "wasmtime-lightbeam"
version = "0.29.0"
dependencies = [
+ "anyhow",
"cranelift-codegen",
+ "gimli",
"lightbeam",
+ "target-lexicon",
"wasmparser",
"wasmtime-environ",
]
@@ -3833,7 +3840,6 @@ dependencies = [
"more-asserts",
"object",
"target-lexicon",
- "wasmtime-debug",
"wasmtime-environ",
]
diff --git a/build.rs b/build.rs
index 278bf8b018..ad7d10f465 100644
--- a/build.rs
+++ b/build.rs
@@ -199,9 +199,6 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
// for reference counts on `externref`, but the old backend does not
// implement atomic instructions.
("reference_types", _) if cfg!(feature = "old-x86-backend") => return true,
- // Skip all SIMD tests on old backend, there are instructions not
- // implemented there and support is generally not maintained.
- ("simd", _) if cfg!(feature = "old-x86-backend") => return true,
// No simd support yet for s390x.
("simd", _) if platform_is_s390x() => return true,
_ => {}
diff --git a/cranelift/wasm/src/environ/dummy.rs b/cranelift/wasm/src/environ/dummy.rs
index c26e2b5f8f..53ecf5d259 100644
--- a/cranelift/wasm/src/environ/dummy.rs
+++ b/cranelift/wasm/src/environ/dummy.rs
@@ -284,7 +284,14 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
Ok(GlobalVariable::Memory {
gv: vmctx,
offset,
- ty: self.mod_info.globals[index].entity.ty,
+ ty: match self.mod_info.globals[index].entity.wasm_ty {
+ WasmType::I32 => ir::types::I32,
+ WasmType::I64 => ir::types::I64,
+ WasmType::F32 => ir::types::F32,
+ WasmType::F64 => ir::types::F64,
+ WasmType::V128 => ir::types::I8X16,
+ WasmType::ExnRef | WasmType::FuncRef | WasmType::ExternRef => ir::types::R64,
+ },
})
}
diff --git a/cranelift/wasm/src/environ/spec.rs b/cranelift/wasm/src/environ/spec.rs
index 333deac4c1..a4a0e2ea3e 100644
--- a/cranelift/wasm/src/environ/spec.rs
+++ b/cranelift/wasm/src/environ/spec.rs
@@ -706,7 +706,7 @@ pub trait FuncEnvironment: TargetEnvironment {
/// An object satisfying the `ModuleEnvironment` trait can be passed as argument to the
/// [`translate_module`](fn.translate_module.html) function. These methods should not be called
/// by the user, they are only for `cranelift-wasm` internal use.
-pub trait ModuleEnvironment<'data>: TargetEnvironment {
+pub trait ModuleEnvironment<'data> {
/// Provides the number of types up front. By default this does nothing, but
/// implementations can use this to preallocate memory if desired.
fn reserve_types(&mut self, _num: u32) -> WasmResult<()> {
diff --git a/cranelift/wasm/src/sections_translator.rs b/cranelift/wasm/src/sections_translator.rs
index eebca487ea..aeb8e1e55f 100644
--- a/cranelift/wasm/src/sections_translator.rs
+++ b/cranelift/wasm/src/sections_translator.rs
@@ -10,9 +10,8 @@
use crate::environ::{Alias, ModuleEnvironment, WasmError, WasmResult};
use crate::state::ModuleTranslationState;
use crate::translation_utils::{
- tabletype_to_type, type_to_type, DataIndex, ElemIndex, EntityIndex, EntityType, FuncIndex,
- Global, GlobalIndex, GlobalInit, InstanceIndex, Memory, MemoryIndex, ModuleIndex, Table,
- TableElementType, TableIndex, Tag, TagIndex, TypeIndex,
+ DataIndex, ElemIndex, EntityIndex, EntityType, FuncIndex, Global, GlobalIndex, GlobalInit,
+ InstanceIndex, Memory, MemoryIndex, ModuleIndex, Table, TableIndex, Tag, TagIndex, TypeIndex,
};
use crate::wasm_unsupported;
use core::convert::TryFrom;
@@ -46,10 +45,8 @@ fn entity_type(
}
ImportSectionEntryType::Memory(ty) => EntityType::Memory(memory(ty)),
ImportSectionEntryType::Tag(t) => EntityType::Tag(tag(t)),
- ImportSectionEntryType::Global(ty) => {
- EntityType::Global(global(ty, environ, GlobalInit::Import)?)
- }
- ImportSectionEntryType::Table(ty) => EntityType::Table(table(ty, environ)?),
+ ImportSectionEntryType::Global(ty) => EntityType::Global(global(ty, GlobalInit::Import)?),
+ ImportSectionEntryType::Table(ty) => EntityType::Table(table(ty)?),
})
}
@@ -68,26 +65,17 @@ fn tag(e: TagType) -> Tag {
}
}
-fn table(ty: TableType, environ: &mut dyn ModuleEnvironment<'_>) -> WasmResult
{
+fn table(ty: TableType) -> WasmResult {
Ok(Table {
wasm_ty: ty.element_type.try_into()?,
- ty: match tabletype_to_type(ty.element_type, environ)? {
- Some(t) => TableElementType::Val(t),
- None => TableElementType::Func,
- },
minimum: ty.initial,
maximum: ty.maximum,
})
}
-fn global(
- ty: GlobalType,
- environ: &mut dyn ModuleEnvironment<'_>,
- initializer: GlobalInit,
-) -> WasmResult {
+fn global(ty: GlobalType, initializer: GlobalInit) -> WasmResult {
Ok(Global {
wasm_ty: ty.content_type.try_into()?,
- ty: type_to_type(ty.content_type, environ).unwrap(),
mutability: ty.mutable,
initializer,
})
@@ -175,11 +163,11 @@ pub fn parse_import_section<'data>(
environ.declare_tag_import(tag(e), import.module, import.field)?;
}
ImportSectionEntryType::Global(ty) => {
- let ty = global(ty, environ, GlobalInit::Import)?;
+ let ty = global(ty, GlobalInit::Import)?;
environ.declare_global_import(ty, import.module, import.field)?;
}
ImportSectionEntryType::Table(ty) => {
- let ty = table(ty, environ)?;
+ let ty = table(ty)?;
environ.declare_table_import(ty, import.module, import.field)?;
}
}
@@ -218,7 +206,7 @@ pub fn parse_table_section(
environ.reserve_tables(tables.get_count())?;
for entry in tables {
- let ty = table(entry?, environ)?;
+ let ty = table(entry?)?;
environ.declare_table(ty)?;
}
@@ -287,7 +275,7 @@ pub fn parse_global_section(
));
}
};
- let ty = global(ty, environ, initializer)?;
+ let ty = global(ty, initializer)?;
environ.declare_global(ty)?;
}
diff --git a/cranelift/wasm/src/translation_utils.rs b/cranelift/wasm/src/translation_utils.rs
index 6d89ad398e..0a1d43c9eb 100644
--- a/cranelift/wasm/src/translation_utils.rs
+++ b/cranelift/wasm/src/translation_utils.rs
@@ -165,8 +165,6 @@ pub enum EntityType {
pub struct Global {
/// The Wasm type of the value stored in the global.
pub wasm_ty: crate::WasmType,
- /// The Cranelift IR type of the value stored in the global.
- pub ty: ir::Type,
/// A flag indicating whether the value may change at runtime.
pub mutability: bool,
/// The source of the initial value.
@@ -203,8 +201,6 @@ pub enum GlobalInit {
pub struct Table {
/// The table elements' Wasm type.
pub wasm_ty: WasmType,
- /// The table elements' Cranelift type.
- pub ty: TableElementType,
/// The minimum number of elements in the table.
pub minimum: u32,
/// The maximum number of elements in the table.
diff --git a/crates/cranelift/Cargo.toml b/crates/cranelift/Cargo.toml
index 5df4ea5f38..7b5aa944be 100644
--- a/crates/cranelift/Cargo.toml
+++ b/crates/cranelift/Cargo.toml
@@ -11,13 +11,17 @@ keywords = ["webassembly", "wasm"]
edition = "2018"
[dependencies]
+anyhow = "1.0"
wasmtime-environ = { path = "../environ", version = "0.29.0" }
cranelift-wasm = { path = "../../cranelift/wasm", version = "0.76.0" }
cranelift-codegen = { path = "../../cranelift/codegen", version = "0.76.0" }
cranelift-frontend = { path = "../../cranelift/frontend", version = "0.76.0" }
cranelift-entity = { path = "../../cranelift/entity", version = "0.76.0" }
+cranelift-native = { path = '../../cranelift/native', version = '0.76.0' }
+wasmtime-debug = { path = '../debug', version = '0.29.0' }
wasmparser = "0.80.0"
target-lexicon = "0.12"
+gimli = "0.25.0"
[features]
all-arch = ["cranelift-codegen/all-arch"]
diff --git a/crates/cranelift/src/builder.rs b/crates/cranelift/src/builder.rs
new file mode 100644
index 0000000000..f7f6c1dba2
--- /dev/null
+++ b/crates/cranelift/src/builder.rs
@@ -0,0 +1,113 @@
+//! Implementation of a "compiler builder" for cranelift
+//!
+//! This module contains the implementation of how Cranelift is configured, as
+//! well as providing a function to return the default configuration to build.
+
+use anyhow::Result;
+use cranelift_codegen::isa;
+use cranelift_codegen::settings::{self, Configurable, SetError};
+use std::fmt;
+use wasmtime_environ::{CompilerBuilder, Setting, SettingKind};
+
+#[derive(Clone)]
+struct Builder {
+ flags: settings::Builder,
+ isa_flags: isa::Builder,
+}
+
+pub fn builder() -> Box {
+ let mut flags = settings::builder();
+
+ // There are two possible traps for division, and this way
+ // we get the proper one if code traps.
+ flags
+ .enable("avoid_div_traps")
+ .expect("should be valid flag");
+
+ // We don't use probestack as a stack limit mechanism
+ flags
+ .set("enable_probestack", "false")
+ .expect("should be valid flag");
+
+ Box::new(Builder {
+ flags,
+ isa_flags: cranelift_native::builder().expect("host machine is not a supported target"),
+ })
+}
+
+impl CompilerBuilder for Builder {
+ fn triple(&self) -> &target_lexicon::Triple {
+ self.isa_flags.triple()
+ }
+
+ fn clone(&self) -> Box {
+ Box::new(Clone::clone(self))
+ }
+
+ fn target(&mut self, target: target_lexicon::Triple) -> Result<()> {
+ self.isa_flags = isa::lookup(target)?;
+ Ok(())
+ }
+
+ fn set(&mut self, name: &str, value: &str) -> Result<()> {
+ if let Err(err) = self.flags.set(name, value) {
+ match err {
+ SetError::BadName(_) => {
+ // Try the target-specific flags.
+ self.isa_flags.set(name, value)?;
+ }
+ _ => return Err(err.into()),
+ }
+ }
+ Ok(())
+ }
+
+ fn enable(&mut self, name: &str) -> Result<()> {
+ if let Err(err) = self.flags.enable(name) {
+ match err {
+ SetError::BadName(_) => {
+ // Try the target-specific flags.
+ self.isa_flags.enable(name)?;
+ }
+ _ => return Err(err.into()),
+ }
+ }
+ Ok(())
+ }
+
+ fn build(&self) -> Box {
+ let isa = self
+ .isa_flags
+ .clone()
+ .finish(settings::Flags::new(self.flags.clone()));
+ Box::new(crate::compiler::Compiler::new(isa))
+ }
+
+ fn settings(&self) -> Vec {
+ self.isa_flags
+ .iter()
+ .map(|s| Setting {
+ description: s.description,
+ name: s.name,
+ values: s.values,
+ kind: match s.kind {
+ settings::SettingKind::Preset => SettingKind::Preset,
+ settings::SettingKind::Enum => SettingKind::Enum,
+ settings::SettingKind::Num => SettingKind::Num,
+ settings::SettingKind::Bool => SettingKind::Bool,
+ },
+ })
+ .collect()
+ }
+}
+
+impl fmt::Debug for Builder {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Builder")
+ .field(
+ "flags",
+ &settings::Flags::new(self.flags.clone()).to_string(),
+ )
+ .finish()
+ }
+}
diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs
new file mode 100644
index 0000000000..fe0f4cdb2a
--- /dev/null
+++ b/crates/cranelift/src/compiler.rs
@@ -0,0 +1,660 @@
+use crate::func_environ::{get_func_name, FuncEnvironment};
+use crate::{blank_sig, func_signature, indirect_signature, value_type, wasmtime_call_conv};
+use anyhow::Result;
+use cranelift_codegen::ir::{self, ExternalName, InstBuilder, MemFlags};
+use cranelift_codegen::isa::TargetIsa;
+use cranelift_codegen::print_errors::pretty_error;
+use cranelift_codegen::settings;
+use cranelift_codegen::MachSrcLoc;
+use cranelift_codegen::{binemit, Context};
+use cranelift_frontend::FunctionBuilder;
+use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator, WasmFuncType};
+use std::cmp;
+use std::collections::HashMap;
+use std::convert::TryFrom;
+use std::mem;
+use std::sync::Mutex;
+use wasmtime_environ::{
+ CompileError, CompiledFunction, CompiledFunctions, DebugInfoData, DwarfSection, FlagValue,
+ FunctionAddressMap, FunctionBodyData, InstructionAddressMap, ModuleMemoryOffset,
+ ModuleTranslation, Relocation, RelocationTarget, StackMapInformation, TrapInformation,
+ Tunables, TypeTables,
+};
+
+/// A compiler that compiles a WebAssembly module with Compiler, translating
+/// the Wasm to Compiler IR, optimizing it and then translating to assembly.
+pub(crate) struct Compiler {
+ translators: Mutex>,
+ isa: Box,
+}
+
+impl Compiler {
+ pub(crate) fn new(isa: Box) -> Compiler {
+ Compiler {
+ translators: Default::default(),
+ isa,
+ }
+ }
+
+ fn take_translator(&self) -> FuncTranslator {
+ let candidate = self.translators.lock().unwrap().pop();
+ candidate.unwrap_or_else(FuncTranslator::new)
+ }
+
+ fn save_translator(&self, translator: FuncTranslator) {
+ self.translators.lock().unwrap().push(translator);
+ }
+
+ fn get_function_address_map(
+ &self,
+ context: &Context,
+ data: &FunctionBodyData<'_>,
+ body_len: u32,
+ ) -> FunctionAddressMap {
+ // Generate artificial srcloc for function start/end to identify boundary
+ // within module.
+ let data = data.body.get_binary_reader();
+ let offset = data.original_position();
+ let len = data.bytes_remaining();
+ assert!((offset + len) <= u32::max_value() as usize);
+ let start_srcloc = ir::SourceLoc::new(offset as u32);
+ let end_srcloc = ir::SourceLoc::new((offset + len) as u32);
+
+ let instructions = if let Some(ref mcr) = &context.mach_compile_result {
+ // New-style backend: we have a `MachCompileResult` that will give us `MachSrcLoc` mapping
+ // tuples.
+ collect_address_maps(
+ body_len,
+ mcr.buffer
+ .get_srclocs_sorted()
+ .into_iter()
+ .map(|&MachSrcLoc { start, end, loc }| (loc, start, (end - start))),
+ )
+ } else {
+ // Old-style backend: we need to traverse the instruction/encoding info in the function.
+ let func = &context.func;
+ let mut blocks = func.layout.blocks().collect::>();
+ blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase
+
+ let encinfo = self.isa.encoding_info();
+ collect_address_maps(
+ body_len,
+ blocks
+ .into_iter()
+ .flat_map(|block| func.inst_offsets(block, &encinfo))
+ .map(|(offset, inst, size)| (func.srclocs[inst], offset, size)),
+ )
+ };
+
+ FunctionAddressMap {
+ instructions: instructions.into(),
+ start_srcloc,
+ end_srcloc,
+ body_offset: 0,
+ body_len,
+ }
+ }
+}
+
+impl wasmtime_environ::Compiler for Compiler {
+ fn compile_function(
+ &self,
+ translation: &ModuleTranslation<'_>,
+ func_index: DefinedFuncIndex,
+ mut input: FunctionBodyData<'_>,
+ tunables: &Tunables,
+ types: &TypeTables,
+ ) -> Result {
+ let isa = &*self.isa;
+ let module = &translation.module;
+ let func_index = module.func_index(func_index);
+ let mut context = Context::new();
+ context.func.name = get_func_name(func_index);
+ context.func.signature = func_signature(isa, module, types, func_index);
+ if tunables.generate_native_debuginfo {
+ context.func.collect_debug_info();
+ }
+
+ let mut func_env = FuncEnvironment::new(isa, module, types, tunables);
+
+ // We use these as constant offsets below in
+ // `stack_limit_from_arguments`, so assert their values here. This
+ // allows the closure below to get coerced to a function pointer, as
+ // needed by `ir::Function`.
+ //
+ // Otherwise our stack limit is specially calculated from the vmctx
+ // argument, where we need to load the `*const VMInterrupts`
+ // pointer, and then from that pointer we need to load the stack
+ // limit itself. Note that manual register allocation is needed here
+ // too due to how late in the process this codegen happens.
+ //
+ // For more information about interrupts and stack checks, see the
+ // top of this file.
+ let vmctx = context
+ .func
+ .create_global_value(ir::GlobalValueData::VMContext);
+ let interrupts_ptr = context.func.create_global_value(ir::GlobalValueData::Load {
+ base: vmctx,
+ offset: i32::try_from(func_env.offsets.vmctx_interrupts())
+ .unwrap()
+ .into(),
+ global_type: isa.pointer_type(),
+ readonly: true,
+ });
+ let stack_limit = context.func.create_global_value(ir::GlobalValueData::Load {
+ base: interrupts_ptr,
+ offset: i32::try_from(func_env.offsets.vminterrupts_stack_limit())
+ .unwrap()
+ .into(),
+ global_type: isa.pointer_type(),
+ readonly: false,
+ });
+ context.func.stack_limit = Some(stack_limit);
+ let mut func_translator = self.take_translator();
+ func_translator.translate_body(
+ &mut input.validator,
+ input.body.clone(),
+ &mut context.func,
+ &mut func_env,
+ )?;
+ self.save_translator(func_translator);
+
+ let mut code_buf: Vec = Vec::new();
+ let mut reloc_sink = RelocSink::new(func_index);
+ let mut trap_sink = TrapSink::new();
+ let mut stack_map_sink = StackMapSink::default();
+ context
+ .compile_and_emit(
+ isa,
+ &mut code_buf,
+ &mut reloc_sink,
+ &mut trap_sink,
+ &mut stack_map_sink,
+ )
+ .map_err(|error| {
+ CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
+ })?;
+
+ let unwind_info = context.create_unwind_info(isa).map_err(|error| {
+ CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
+ })?;
+
+ let address_transform =
+ self.get_function_address_map(&context, &input, code_buf.len() as u32);
+
+ let ranges = if tunables.generate_native_debuginfo {
+ let ranges = context.build_value_labels_ranges(isa).map_err(|error| {
+ CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
+ })?;
+ Some(ranges)
+ } else {
+ None
+ };
+
+ Ok(CompiledFunction {
+ body: code_buf,
+ jt_offsets: context.func.jt_offsets,
+ relocations: reloc_sink.func_relocs,
+ address_map: address_transform,
+ value_labels_ranges: ranges.unwrap_or(Default::default()),
+ stack_slots: context.func.stack_slots,
+ traps: trap_sink.traps,
+ unwind_info,
+ stack_maps: stack_map_sink.finish(),
+ })
+ }
+
+ fn host_to_wasm_trampoline(&self, ty: &WasmFuncType) -> Result {
+ let isa = &*self.isa;
+ let value_size = mem::size_of::();
+ let pointer_type = isa.pointer_type();
+
+ // The wasm signature we're calling in this trampoline has the actual
+ // ABI of the function signature described by `ty`
+ let wasm_signature = indirect_signature(isa, ty);
+
+ // The host signature has the `VMTrampoline` signature where the ABI is
+ // fixed.
+ let mut host_signature = blank_sig(isa, wasmtime_call_conv(isa));
+ host_signature.params.push(ir::AbiParam::new(pointer_type));
+ host_signature.params.push(ir::AbiParam::new(pointer_type));
+
+ let mut func_translator = self.take_translator();
+ let mut context = Context::new();
+ context.func = ir::Function::with_name_signature(ExternalName::user(0, 0), host_signature);
+
+ // This trampoline will load all the parameters from the `values_vec`
+ // that is passed in and then call the real function (also passed
+ // indirectly) with the specified ABI.
+ //
+ // All the results are then stored into the same `values_vec`.
+ let mut builder = FunctionBuilder::new(&mut context.func, func_translator.context());
+ let block0 = builder.create_block();
+
+ builder.append_block_params_for_function_params(block0);
+ builder.switch_to_block(block0);
+ builder.seal_block(block0);
+
+ let (vmctx_ptr_val, caller_vmctx_ptr_val, callee_value, values_vec_ptr_val) = {
+ let params = builder.func.dfg.block_params(block0);
+ (params[0], params[1], params[2], params[3])
+ };
+
+ // Load the argument values out of `values_vec`.
+ let mflags = ir::MemFlags::trusted();
+ let callee_args = wasm_signature
+ .params
+ .iter()
+ .enumerate()
+ .map(|(i, r)| {
+ match i {
+ 0 => vmctx_ptr_val,
+ 1 => caller_vmctx_ptr_val,
+ _ =>
+ // i - 2 because vmctx and caller vmctx aren't passed through `values_vec`.
+ {
+ builder.ins().load(
+ r.value_type,
+ mflags,
+ values_vec_ptr_val,
+ ((i - 2) * value_size) as i32,
+ )
+ }
+ }
+ })
+ .collect::>();
+
+ // Call the indirect function pointer we were given
+ let new_sig = builder.import_signature(wasm_signature);
+ let call = builder
+ .ins()
+ .call_indirect(new_sig, callee_value, &callee_args);
+ let results = builder.func.dfg.inst_results(call).to_vec();
+
+ // Store the return values into `values_vec`.
+ let mflags = ir::MemFlags::trusted();
+ for (i, r) in results.iter().enumerate() {
+ builder
+ .ins()
+ .store(mflags, *r, values_vec_ptr_val, (i * value_size) as i32);
+ }
+ builder.ins().return_(&[]);
+ builder.finalize();
+
+ let func = self.finish_trampoline(context, isa)?;
+ self.save_translator(func_translator);
+ Ok(func)
+ }
+
+ fn wasm_to_host_trampoline(
+ &self,
+ ty: &WasmFuncType,
+ host_fn: usize,
+ ) -> Result {
+ let isa = &*self.isa;
+ let pointer_type = isa.pointer_type();
+ let wasm_signature = indirect_signature(isa, ty);
+ // The host signature has an added parameter for the `values_vec` input
+ // and output.
+ let mut host_signature = blank_sig(isa, wasmtime_call_conv(isa));
+ host_signature.params.push(ir::AbiParam::new(pointer_type));
+
+ // Compute the size of the values vector. The vmctx and caller vmctx are passed separately.
+ let value_size = mem::size_of::();
+ let values_vec_len = (value_size * cmp::max(ty.params.len(), ty.returns.len())) as u32;
+
+ let mut context = Context::new();
+ context.func =
+ ir::Function::with_name_signature(ir::ExternalName::user(0, 0), wasm_signature);
+
+ let ss = context.func.create_stack_slot(ir::StackSlotData::new(
+ ir::StackSlotKind::ExplicitSlot,
+ values_vec_len,
+ ));
+
+ let mut func_translator = self.take_translator();
+ let mut builder = FunctionBuilder::new(&mut context.func, func_translator.context());
+ let block0 = builder.create_block();
+
+ builder.append_block_params_for_function_params(block0);
+ builder.switch_to_block(block0);
+ builder.seal_block(block0);
+
+ let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0);
+ let mflags = MemFlags::trusted();
+ for i in 0..ty.params.len() {
+ let val = builder.func.dfg.block_params(block0)[i + 2];
+ builder
+ .ins()
+ .store(mflags, val, values_vec_ptr_val, (i * value_size) as i32);
+ }
+
+ let block_params = builder.func.dfg.block_params(block0);
+ let vmctx_ptr_val = block_params[0];
+ let caller_vmctx_ptr_val = block_params[1];
+
+ let callee_args = vec![vmctx_ptr_val, caller_vmctx_ptr_val, values_vec_ptr_val];
+
+ let new_sig = builder.import_signature(host_signature);
+
+ let callee_value = builder.ins().iconst(pointer_type, host_fn as i64);
+ builder
+ .ins()
+ .call_indirect(new_sig, callee_value, &callee_args);
+
+ let mflags = MemFlags::trusted();
+ let mut results = Vec::new();
+ for (i, r) in ty.returns.iter().enumerate() {
+ let load = builder.ins().load(
+ value_type(isa, *r),
+ mflags,
+ values_vec_ptr_val,
+ (i * value_size) as i32,
+ );
+ results.push(load);
+ }
+ builder.ins().return_(&results);
+ builder.finalize();
+
+ let func = self.finish_trampoline(context, isa)?;
+ self.save_translator(func_translator);
+ Ok(func)
+ }
+
+ fn emit_dwarf(
+ &self,
+ debuginfo_data: &DebugInfoData,
+ funcs: &CompiledFunctions,
+ memory_offset: &ModuleMemoryOffset,
+ ) -> Result> {
+ wasmtime_debug::emit_dwarf(&*self.isa, debuginfo_data, funcs, memory_offset)
+ }
+
+ fn triple(&self) -> &target_lexicon::Triple {
+ self.isa.triple()
+ }
+
+ fn create_systemv_cie(&self) -> Option {
+ self.isa.create_systemv_cie()
+ }
+
+ fn flags(&self) -> HashMap {
+ self.isa
+ .flags()
+ .iter()
+ .map(|val| (val.name.to_string(), to_flag_value(&val)))
+ .collect()
+ }
+
+ fn isa_flags(&self) -> HashMap {
+ self.isa
+ .isa_flags()
+ .iter()
+ .map(|val| (val.name.to_string(), to_flag_value(val)))
+ .collect()
+ }
+}
+
+fn to_flag_value(v: &settings::Value) -> FlagValue {
+ match v.kind() {
+ settings::SettingKind::Enum => FlagValue::Enum(v.as_enum().unwrap().into()),
+ settings::SettingKind::Num => FlagValue::Num(v.as_num().unwrap()),
+ settings::SettingKind::Bool => FlagValue::Bool(v.as_bool().unwrap()),
+ settings::SettingKind::Preset => unreachable!(),
+ }
+}
+
+impl Compiler {
+ fn finish_trampoline(
+ &self,
+ mut context: Context,
+ isa: &dyn TargetIsa,
+ ) -> Result {
+ let mut code_buf = Vec::new();
+ let mut reloc_sink = TrampolineRelocSink::default();
+ let mut trap_sink = binemit::NullTrapSink {};
+ let mut stack_map_sink = binemit::NullStackMapSink {};
+ context
+ .compile_and_emit(
+ isa,
+ &mut code_buf,
+ &mut reloc_sink,
+ &mut trap_sink,
+ &mut stack_map_sink,
+ )
+ .map_err(|error| {
+ CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
+ })?;
+
+ let unwind_info = context.create_unwind_info(isa).map_err(|error| {
+ CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
+ })?;
+
+ Ok(CompiledFunction {
+ body: code_buf,
+ jt_offsets: context.func.jt_offsets,
+ unwind_info,
+ relocations: reloc_sink.relocs,
+ stack_maps: Default::default(),
+ stack_slots: Default::default(),
+ traps: Default::default(),
+ value_labels_ranges: Default::default(),
+ address_map: Default::default(),
+ })
+ }
+}
+
+// Collects an iterator of `InstructionAddressMap` into a `Vec` for insertion
+// into a `FunctionAddressMap`. This will automatically coalesce adjacent
+// instructions which map to the same original source position.
+fn collect_address_maps(
+ code_size: u32,
+ iter: impl IntoIterator- ,
+) -> Vec {
+ let mut iter = iter.into_iter();
+ let (mut cur_loc, mut cur_offset, mut cur_len) = match iter.next() {
+ Some(i) => i,
+ None => return Vec::new(),
+ };
+ let mut ret = Vec::new();
+ for (loc, offset, len) in iter {
+ // If this instruction is adjacent to the previous and has the same
+ // source location then we can "coalesce" it with the current
+ // instruction.
+ if cur_offset + cur_len == offset && loc == cur_loc {
+ cur_len += len;
+ continue;
+ }
+
+ // Push an entry for the previous source item.
+ ret.push(InstructionAddressMap {
+ srcloc: cur_loc,
+ code_offset: cur_offset,
+ });
+ // And push a "dummy" entry if necessary to cover the span of ranges,
+ // if any, between the previous source offset and this one.
+ if cur_offset + cur_len != offset {
+ ret.push(InstructionAddressMap {
+ srcloc: ir::SourceLoc::default(),
+ code_offset: cur_offset + cur_len,
+ });
+ }
+ // Update our current location to get extended later or pushed on at
+ // the end.
+ cur_loc = loc;
+ cur_offset = offset;
+ cur_len = len;
+ }
+ ret.push(InstructionAddressMap {
+ srcloc: cur_loc,
+ code_offset: cur_offset,
+ });
+ if cur_offset + cur_len != code_size {
+ ret.push(InstructionAddressMap {
+ srcloc: ir::SourceLoc::default(),
+ code_offset: cur_offset + cur_len,
+ });
+ }
+
+ return ret;
+}
+
+/// Implementation of a relocation sink that just saves all the information for later
+struct RelocSink {
+ /// Current function index.
+ func_index: FuncIndex,
+
+ /// Relocations recorded for the function.
+ func_relocs: Vec,
+}
+
+impl binemit::RelocSink for RelocSink {
+ fn reloc_external(
+ &mut self,
+ offset: binemit::CodeOffset,
+ _srcloc: ir::SourceLoc,
+ reloc: binemit::Reloc,
+ name: &ExternalName,
+ addend: binemit::Addend,
+ ) {
+ let reloc_target = if let ExternalName::User { namespace, index } = *name {
+ debug_assert_eq!(namespace, 0);
+ RelocationTarget::UserFunc(FuncIndex::from_u32(index))
+ } else if let ExternalName::LibCall(libcall) = *name {
+ RelocationTarget::LibCall(libcall)
+ } else {
+ panic!("unrecognized external name")
+ };
+ self.func_relocs.push(Relocation {
+ reloc,
+ reloc_target,
+ offset,
+ addend,
+ });
+ }
+
+ fn reloc_constant(
+ &mut self,
+ _code_offset: binemit::CodeOffset,
+ _reloc: binemit::Reloc,
+ _constant_offset: ir::ConstantOffset,
+ ) {
+ // Do nothing for now: cranelift emits constant data after the function code and also emits
+ // function code with correct relative offsets to the constant data.
+ }
+
+ fn reloc_jt(&mut self, offset: binemit::CodeOffset, reloc: binemit::Reloc, jt: ir::JumpTable) {
+ self.func_relocs.push(Relocation {
+ reloc,
+ reloc_target: RelocationTarget::JumpTable(self.func_index, jt),
+ offset,
+ addend: 0,
+ });
+ }
+}
+
+impl RelocSink {
+ /// Return a new `RelocSink` instance.
+ fn new(func_index: FuncIndex) -> Self {
+ Self {
+ func_index,
+ func_relocs: Vec::new(),
+ }
+ }
+}
+
+/// Implementation of a trap sink that simply stores all trap info in-memory
+#[derive(Default)]
+struct TrapSink {
+ /// The in-memory vector of trap info
+ traps: Vec,
+}
+
+impl TrapSink {
+ /// Create a new `TrapSink`
+ fn new() -> Self {
+ Self::default()
+ }
+}
+
+impl binemit::TrapSink for TrapSink {
+ fn trap(
+ &mut self,
+ code_offset: binemit::CodeOffset,
+ _source_loc: ir::SourceLoc,
+ trap_code: ir::TrapCode,
+ ) {
+ self.traps.push(TrapInformation {
+ code_offset,
+ trap_code,
+ });
+ }
+}
+
+#[derive(Default)]
+struct StackMapSink {
+ infos: Vec,
+}
+
+impl binemit::StackMapSink for StackMapSink {
+ fn add_stack_map(&mut self, code_offset: binemit::CodeOffset, stack_map: binemit::StackMap) {
+ self.infos.push(StackMapInformation {
+ code_offset,
+ stack_map,
+ });
+ }
+}
+
+impl StackMapSink {
+ fn finish(mut self) -> Vec {
+ self.infos.sort_unstable_by_key(|info| info.code_offset);
+ self.infos
+ }
+}
+
+/// We don't expect trampoline compilation to produce many relocations, so
+/// this `RelocSink` just asserts that it doesn't recieve most of them, but
+/// handles libcall ones.
+#[derive(Default)]
+struct TrampolineRelocSink {
+ relocs: Vec,
+}
+
+impl binemit::RelocSink for TrampolineRelocSink {
+ fn reloc_external(
+ &mut self,
+ offset: binemit::CodeOffset,
+ _srcloc: ir::SourceLoc,
+ reloc: binemit::Reloc,
+ name: &ir::ExternalName,
+ addend: binemit::Addend,
+ ) {
+ let reloc_target = if let ir::ExternalName::LibCall(libcall) = *name {
+ RelocationTarget::LibCall(libcall)
+ } else {
+ panic!("unrecognized external name")
+ };
+ self.relocs.push(Relocation {
+ reloc,
+ reloc_target,
+ offset,
+ addend,
+ });
+ }
+ fn reloc_constant(
+ &mut self,
+ _code_offset: binemit::CodeOffset,
+ _reloc: binemit::Reloc,
+ _constant_offset: ir::ConstantOffset,
+ ) {
+ panic!("trampoline compilation should not produce constant relocs");
+ }
+ fn reloc_jt(
+ &mut self,
+ _offset: binemit::CodeOffset,
+ _reloc: binemit::Reloc,
+ _jt: ir::JumpTable,
+ ) {
+ panic!("trampoline compilation should not produce jump table relocs");
+ }
+}
diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs
index 5fad01a24d..209a537853 100644
--- a/crates/cranelift/src/func_environ.rs
+++ b/crates/cranelift/src/func_environ.rs
@@ -1267,7 +1267,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
Ok(GlobalVariable::Memory {
gv,
offset: offset.into(),
- ty: self.module.globals[index].ty,
+ ty: super::value_type(self.isa, self.module.globals[index].wasm_ty),
})
}
diff --git a/crates/cranelift/src/lib.rs b/crates/cranelift/src/lib.rs
index 601cd4a496..29924451cb 100644
--- a/crates/cranelift/src/lib.rs
+++ b/crates/cranelift/src/lib.rs
@@ -1,7 +1,7 @@
//! Support for compiling with Cranelift.
//!
-//! This crate provides an implementation of [`Compiler`] in the form of
-//! [`Cranelift`].
+//! This crate provides an implementation of the `wasmtime_environ::Compiler`
+//! and `wasmtime_environ::CompilerBuilder` traits.
// # How does Wasmtime prevent stack overflow?
//
@@ -88,573 +88,23 @@
// also need to actually catch stack overflow, so for now 32k is chosen and it's
// assume no valid stack pointer will ever be `usize::max_value() - 32k`.
-use crate::func_environ::{get_func_name, FuncEnvironment};
-use cranelift_codegen::ir::{self, ExternalName, InstBuilder, MemFlags};
+use cranelift_codegen::ir;
use cranelift_codegen::isa::{CallConv, TargetIsa};
-use cranelift_codegen::print_errors::pretty_error;
-use cranelift_codegen::MachSrcLoc;
-use cranelift_codegen::{binemit, isa, Context};
-use cranelift_frontend::FunctionBuilder;
-use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator, WasmFuncType, WasmType};
-use std::cmp;
-use std::convert::TryFrom;
-use std::mem;
-use std::sync::Mutex;
+use cranelift_wasm::{FuncIndex, WasmFuncType, WasmType};
use target_lexicon::CallingConvention;
-use wasmtime_environ::{
- CompileError, CompiledFunction, Compiler, FunctionAddressMap, FunctionBodyData,
- InstructionAddressMap, Module, ModuleTranslation, Relocation, RelocationTarget,
- StackMapInformation, TrapInformation, Tunables, TypeTables,
-};
+use wasmtime_environ::{Module, TypeTables};
+pub use builder::builder;
+
+mod builder;
+mod compiler;
mod func_environ;
-/// Implementation of a relocation sink that just saves all the information for later
-struct RelocSink {
- /// Current function index.
- func_index: FuncIndex,
-
- /// Relocations recorded for the function.
- func_relocs: Vec,
-}
-
-impl binemit::RelocSink for RelocSink {
- fn reloc_external(
- &mut self,
- offset: binemit::CodeOffset,
- _srcloc: ir::SourceLoc,
- reloc: binemit::Reloc,
- name: &ExternalName,
- addend: binemit::Addend,
- ) {
- let reloc_target = if let ExternalName::User { namespace, index } = *name {
- debug_assert_eq!(namespace, 0);
- RelocationTarget::UserFunc(FuncIndex::from_u32(index))
- } else if let ExternalName::LibCall(libcall) = *name {
- RelocationTarget::LibCall(libcall)
- } else {
- panic!("unrecognized external name")
- };
- self.func_relocs.push(Relocation {
- reloc,
- reloc_target,
- offset,
- addend,
- });
- }
-
- fn reloc_constant(
- &mut self,
- _code_offset: binemit::CodeOffset,
- _reloc: binemit::Reloc,
- _constant_offset: ir::ConstantOffset,
- ) {
- // Do nothing for now: cranelift emits constant data after the function code and also emits
- // function code with correct relative offsets to the constant data.
- }
-
- fn reloc_jt(&mut self, offset: binemit::CodeOffset, reloc: binemit::Reloc, jt: ir::JumpTable) {
- self.func_relocs.push(Relocation {
- reloc,
- reloc_target: RelocationTarget::JumpTable(self.func_index, jt),
- offset,
- addend: 0,
- });
- }
-}
-
-impl RelocSink {
- /// Return a new `RelocSink` instance.
- fn new(func_index: FuncIndex) -> Self {
- Self {
- func_index,
- func_relocs: Vec::new(),
- }
- }
-}
-
-/// Implementation of a trap sink that simply stores all trap info in-memory
-#[derive(Default)]
-struct TrapSink {
- /// The in-memory vector of trap info
- traps: Vec,
-}
-
-impl TrapSink {
- /// Create a new `TrapSink`
- fn new() -> Self {
- Self::default()
- }
-}
-
-impl binemit::TrapSink for TrapSink {
- fn trap(
- &mut self,
- code_offset: binemit::CodeOffset,
- _source_loc: ir::SourceLoc,
- trap_code: ir::TrapCode,
- ) {
- self.traps.push(TrapInformation {
- code_offset,
- trap_code,
- });
- }
-}
-
-#[derive(Default)]
-struct StackMapSink {
- infos: Vec,
-}
-
-impl binemit::StackMapSink for StackMapSink {
- fn add_stack_map(&mut self, code_offset: binemit::CodeOffset, stack_map: binemit::StackMap) {
- self.infos.push(StackMapInformation {
- code_offset,
- stack_map,
- });
- }
-}
-
-impl StackMapSink {
- fn finish(mut self) -> Vec {
- self.infos.sort_unstable_by_key(|info| info.code_offset);
- self.infos
- }
-}
-
-fn get_function_address_map<'data>(
- context: &Context,
- data: &FunctionBodyData<'data>,
- body_len: u32,
- isa: &dyn isa::TargetIsa,
-) -> FunctionAddressMap {
- // Generate artificial srcloc for function start/end to identify boundary
- // within module.
- let data = data.body.get_binary_reader();
- let offset = data.original_position();
- let len = data.bytes_remaining();
- assert!((offset + len) <= u32::max_value() as usize);
- let start_srcloc = ir::SourceLoc::new(offset as u32);
- let end_srcloc = ir::SourceLoc::new((offset + len) as u32);
-
- let instructions = if let Some(ref mcr) = &context.mach_compile_result {
- // New-style backend: we have a `MachCompileResult` that will give us `MachSrcLoc` mapping
- // tuples.
- collect_address_maps(
- body_len,
- mcr.buffer
- .get_srclocs_sorted()
- .into_iter()
- .map(|&MachSrcLoc { start, end, loc }| (loc, start, (end - start))),
- )
- } else {
- // Old-style backend: we need to traverse the instruction/encoding info in the function.
- let func = &context.func;
- let mut blocks = func.layout.blocks().collect::>();
- blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase
-
- let encinfo = isa.encoding_info();
- collect_address_maps(
- body_len,
- blocks
- .into_iter()
- .flat_map(|block| func.inst_offsets(block, &encinfo))
- .map(|(offset, inst, size)| (func.srclocs[inst], offset, size)),
- )
- };
-
- FunctionAddressMap {
- instructions: instructions.into(),
- start_srcloc,
- end_srcloc,
- body_offset: 0,
- body_len,
- }
-}
-
-// Collects an iterator of `InstructionAddressMap` into a `Vec` for insertion
-// into a `FunctionAddressMap`. This will automatically coalesce adjacent
-// instructions which map to the same original source position.
-fn collect_address_maps(
- code_size: u32,
- iter: impl IntoIterator
- ,
-) -> Vec {
- let mut iter = iter.into_iter();
- let (mut cur_loc, mut cur_offset, mut cur_len) = match iter.next() {
- Some(i) => i,
- None => return Vec::new(),
- };
- let mut ret = Vec::new();
- for (loc, offset, len) in iter {
- // If this instruction is adjacent to the previous and has the same
- // source location then we can "coalesce" it with the current
- // instruction.
- if cur_offset + cur_len == offset && loc == cur_loc {
- cur_len += len;
- continue;
- }
-
- // Push an entry for the previous source item.
- ret.push(InstructionAddressMap {
- srcloc: cur_loc,
- code_offset: cur_offset,
- });
- // And push a "dummy" entry if necessary to cover the span of ranges,
- // if any, between the previous source offset and this one.
- if cur_offset + cur_len != offset {
- ret.push(InstructionAddressMap {
- srcloc: ir::SourceLoc::default(),
- code_offset: cur_offset + cur_len,
- });
- }
- // Update our current location to get extended later or pushed on at
- // the end.
- cur_loc = loc;
- cur_offset = offset;
- cur_len = len;
- }
- ret.push(InstructionAddressMap {
- srcloc: cur_loc,
- code_offset: cur_offset,
- });
- if cur_offset + cur_len != code_size {
- ret.push(InstructionAddressMap {
- srcloc: ir::SourceLoc::default(),
- code_offset: cur_offset + cur_len,
- });
- }
-
- return ret;
-}
-
-/// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR,
-/// optimizing it and then translating to assembly.
-#[derive(Default)]
-pub struct Cranelift {
- translators: Mutex>,
-}
-
-impl Cranelift {
- fn take_translator(&self) -> FuncTranslator {
- let candidate = self.translators.lock().unwrap().pop();
- candidate.unwrap_or_else(FuncTranslator::new)
- }
-
- fn save_translator(&self, translator: FuncTranslator) {
- self.translators.lock().unwrap().push(translator);
- }
-}
-
-impl Compiler for Cranelift {
- fn compile_function(
- &self,
- translation: &ModuleTranslation<'_>,
- func_index: DefinedFuncIndex,
- mut input: FunctionBodyData<'_>,
- isa: &dyn isa::TargetIsa,
- tunables: &Tunables,
- types: &TypeTables,
- ) -> Result {
- let module = &translation.module;
- let func_index = module.func_index(func_index);
- let mut context = Context::new();
- context.func.name = get_func_name(func_index);
- context.func.signature = func_signature(isa, module, types, func_index);
- if tunables.generate_native_debuginfo {
- context.func.collect_debug_info();
- }
-
- let mut func_env = FuncEnvironment::new(isa, module, types, tunables);
-
- // We use these as constant offsets below in
- // `stack_limit_from_arguments`, so assert their values here. This
- // allows the closure below to get coerced to a function pointer, as
- // needed by `ir::Function`.
- //
- // Otherwise our stack limit is specially calculated from the vmctx
- // argument, where we need to load the `*const VMInterrupts`
- // pointer, and then from that pointer we need to load the stack
- // limit itself. Note that manual register allocation is needed here
- // too due to how late in the process this codegen happens.
- //
- // For more information about interrupts and stack checks, see the
- // top of this file.
- let vmctx = context
- .func
- .create_global_value(ir::GlobalValueData::VMContext);
- let interrupts_ptr = context.func.create_global_value(ir::GlobalValueData::Load {
- base: vmctx,
- offset: i32::try_from(func_env.offsets.vmctx_interrupts())
- .unwrap()
- .into(),
- global_type: isa.pointer_type(),
- readonly: true,
- });
- let stack_limit = context.func.create_global_value(ir::GlobalValueData::Load {
- base: interrupts_ptr,
- offset: i32::try_from(func_env.offsets.vminterrupts_stack_limit())
- .unwrap()
- .into(),
- global_type: isa.pointer_type(),
- readonly: false,
- });
- context.func.stack_limit = Some(stack_limit);
- let mut func_translator = self.take_translator();
- func_translator.translate_body(
- &mut input.validator,
- input.body.clone(),
- &mut context.func,
- &mut func_env,
- )?;
- self.save_translator(func_translator);
-
- let mut code_buf: Vec = Vec::new();
- let mut reloc_sink = RelocSink::new(func_index);
- let mut trap_sink = TrapSink::new();
- let mut stack_map_sink = StackMapSink::default();
- context
- .compile_and_emit(
- isa,
- &mut code_buf,
- &mut reloc_sink,
- &mut trap_sink,
- &mut stack_map_sink,
- )
- .map_err(|error| {
- CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
- })?;
-
- let unwind_info = context.create_unwind_info(isa).map_err(|error| {
- CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
- })?;
-
- let address_transform =
- get_function_address_map(&context, &input, code_buf.len() as u32, isa);
-
- let ranges = if tunables.generate_native_debuginfo {
- let ranges = context.build_value_labels_ranges(isa).map_err(|error| {
- CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
- })?;
- Some(ranges)
- } else {
- None
- };
-
- Ok(CompiledFunction {
- body: code_buf,
- jt_offsets: context.func.jt_offsets,
- relocations: reloc_sink.func_relocs,
- address_map: address_transform,
- value_labels_ranges: ranges.unwrap_or(Default::default()),
- stack_slots: context.func.stack_slots,
- traps: trap_sink.traps,
- unwind_info,
- stack_maps: stack_map_sink.finish(),
- })
- }
-
- fn host_to_wasm_trampoline(
- &self,
- isa: &dyn isa::TargetIsa,
- ty: &WasmFuncType,
- ) -> Result {
- let value_size = mem::size_of::();
- let pointer_type = isa.pointer_type();
-
- // The wasm signature we're calling in this trampoline has the actual
- // ABI of the function signature described by `ty`
- let wasm_signature = indirect_signature(isa, ty);
-
- // The host signature has the `VMTrampoline` signature where the ABI is
- // fixed.
- let mut host_signature = blank_sig(isa, wasmtime_call_conv(isa));
- host_signature.params.push(ir::AbiParam::new(pointer_type));
- host_signature.params.push(ir::AbiParam::new(pointer_type));
-
- let mut func_translator = self.take_translator();
- let mut context = Context::new();
- context.func = ir::Function::with_name_signature(ExternalName::user(0, 0), host_signature);
-
- // This trampoline will load all the parameters from the `values_vec`
- // that is passed in and then call the real function (also passed
- // indirectly) with the specified ABI.
- //
- // All the results are then stored into the same `values_vec`.
- let mut builder = FunctionBuilder::new(&mut context.func, func_translator.context());
- let block0 = builder.create_block();
-
- builder.append_block_params_for_function_params(block0);
- builder.switch_to_block(block0);
- builder.seal_block(block0);
-
- let (vmctx_ptr_val, caller_vmctx_ptr_val, callee_value, values_vec_ptr_val) = {
- let params = builder.func.dfg.block_params(block0);
- (params[0], params[1], params[2], params[3])
- };
-
- // Load the argument values out of `values_vec`.
- let mflags = ir::MemFlags::trusted();
- let callee_args = wasm_signature
- .params
- .iter()
- .enumerate()
- .map(|(i, r)| {
- match i {
- 0 => vmctx_ptr_val,
- 1 => caller_vmctx_ptr_val,
- _ =>
- // i - 2 because vmctx and caller vmctx aren't passed through `values_vec`.
- {
- builder.ins().load(
- r.value_type,
- mflags,
- values_vec_ptr_val,
- ((i - 2) * value_size) as i32,
- )
- }
- }
- })
- .collect::>();
-
- // Call the indirect function pointer we were given
- let new_sig = builder.import_signature(wasm_signature);
- let call = builder
- .ins()
- .call_indirect(new_sig, callee_value, &callee_args);
- let results = builder.func.dfg.inst_results(call).to_vec();
-
- // Store the return values into `values_vec`.
- let mflags = ir::MemFlags::trusted();
- for (i, r) in results.iter().enumerate() {
- builder
- .ins()
- .store(mflags, *r, values_vec_ptr_val, (i * value_size) as i32);
- }
- builder.ins().return_(&[]);
- builder.finalize();
-
- let func = self.finish_trampoline(context, isa)?;
- self.save_translator(func_translator);
- Ok(func)
- }
-
- fn wasm_to_host_trampoline(
- &self,
- isa: &dyn isa::TargetIsa,
- ty: &WasmFuncType,
- host_fn: usize,
- ) -> Result {
- let pointer_type = isa.pointer_type();
- let wasm_signature = indirect_signature(isa, ty);
- // The host signature has an added parameter for the `values_vec` input
- // and output.
- let mut host_signature = blank_sig(isa, wasmtime_call_conv(isa));
- host_signature.params.push(ir::AbiParam::new(pointer_type));
-
- // Compute the size of the values vector. The vmctx and caller vmctx are passed separately.
- let value_size = mem::size_of::();
- let values_vec_len = (value_size * cmp::max(ty.params.len(), ty.returns.len())) as u32;
-
- let mut context = Context::new();
- context.func =
- ir::Function::with_name_signature(ir::ExternalName::user(0, 0), wasm_signature);
-
- let ss = context.func.create_stack_slot(ir::StackSlotData::new(
- ir::StackSlotKind::ExplicitSlot,
- values_vec_len,
- ));
-
- let mut func_translator = self.take_translator();
- let mut builder = FunctionBuilder::new(&mut context.func, func_translator.context());
- let block0 = builder.create_block();
-
- builder.append_block_params_for_function_params(block0);
- builder.switch_to_block(block0);
- builder.seal_block(block0);
-
- let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0);
- let mflags = MemFlags::trusted();
- for i in 0..ty.params.len() {
- let val = builder.func.dfg.block_params(block0)[i + 2];
- builder
- .ins()
- .store(mflags, val, values_vec_ptr_val, (i * value_size) as i32);
- }
-
- let block_params = builder.func.dfg.block_params(block0);
- let vmctx_ptr_val = block_params[0];
- let caller_vmctx_ptr_val = block_params[1];
-
- let callee_args = vec![vmctx_ptr_val, caller_vmctx_ptr_val, values_vec_ptr_val];
-
- let new_sig = builder.import_signature(host_signature);
-
- let callee_value = builder.ins().iconst(pointer_type, host_fn as i64);
- builder
- .ins()
- .call_indirect(new_sig, callee_value, &callee_args);
-
- let mflags = MemFlags::trusted();
- let mut results = Vec::new();
- for (i, r) in ty.returns.iter().enumerate() {
- let load = builder.ins().load(
- value_type(isa, *r),
- mflags,
- values_vec_ptr_val,
- (i * value_size) as i32,
- );
- results.push(load);
- }
- builder.ins().return_(&results);
- builder.finalize();
-
- let func = self.finish_trampoline(context, isa)?;
- self.save_translator(func_translator);
- Ok(func)
- }
-}
-
-impl Cranelift {
- fn finish_trampoline(
- &self,
- mut context: Context,
- isa: &dyn TargetIsa,
- ) -> Result {
- let mut code_buf = Vec::new();
- let mut reloc_sink = TrampolineRelocSink::default();
- let mut trap_sink = binemit::NullTrapSink {};
- let mut stack_map_sink = binemit::NullStackMapSink {};
- context
- .compile_and_emit(
- isa,
- &mut code_buf,
- &mut reloc_sink,
- &mut trap_sink,
- &mut stack_map_sink,
- )
- .map_err(|error| {
- CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
- })?;
-
- let unwind_info = context.create_unwind_info(isa).map_err(|error| {
- CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
- })?;
-
- Ok(CompiledFunction {
- body: code_buf,
- jt_offsets: context.func.jt_offsets,
- unwind_info,
- relocations: reloc_sink.relocs,
- stack_maps: Default::default(),
- stack_slots: Default::default(),
- traps: Default::default(),
- value_labels_ranges: Default::default(),
- address_map: Default::default(),
- })
- }
-}
-
-pub fn blank_sig(isa: &dyn TargetIsa, call_conv: CallConv) -> ir::Signature {
+/// Creates a new cranelift `Signature` with no wasm params/results for the
+/// given calling convention.
+///
+/// This will add the default vmctx/etc parameters to the signature returned.
+fn blank_sig(isa: &dyn TargetIsa, call_conv: CallConv) -> ir::Signature {
let pointer_type = isa.pointer_type();
let mut sig = ir::Signature::new(call_conv);
// Add the caller/callee `vmctx` parameters.
@@ -666,7 +116,10 @@ pub fn blank_sig(isa: &dyn TargetIsa, call_conv: CallConv) -> ir::Signature {
return sig;
}
-pub fn wasmtime_call_conv(isa: &dyn TargetIsa) -> CallConv {
+/// Returns the default calling convention for the `isa` provided.
+///
+/// Note that this calling convention is used for exported functions.
+fn wasmtime_call_conv(isa: &dyn TargetIsa) -> CallConv {
match isa.triple().default_calling_convention() {
Ok(CallingConvention::AppleAarch64) => CallConv::WasmtimeAppleAarch64,
Ok(CallingConvention::SystemV) | Err(()) => CallConv::WasmtimeSystemV,
@@ -675,12 +128,18 @@ pub fn wasmtime_call_conv(isa: &dyn TargetIsa) -> CallConv {
}
}
-pub fn push_types(isa: &dyn TargetIsa, sig: &mut ir::Signature, wasm: &WasmFuncType) {
+/// Appends the types of the `wasm` function signature into the `sig` signature
+/// provided.
+///
+/// Typically the `sig` signature will have been created from [`blank_sig`]
+/// above.
+fn push_types(isa: &dyn TargetIsa, sig: &mut ir::Signature, wasm: &WasmFuncType) {
let cvt = |ty: &WasmType| ir::AbiParam::new(value_type(isa, *ty));
sig.params.extend(wasm.params.iter().map(&cvt));
sig.returns.extend(wasm.returns.iter().map(&cvt));
}
+/// Returns the corresponding cranelift type for the provided wasm type.
fn value_type(isa: &dyn TargetIsa, ty: WasmType) -> ir::types::Type {
match ty {
WasmType::I32 => ir::types::I32,
@@ -695,13 +154,25 @@ fn value_type(isa: &dyn TargetIsa, ty: WasmType) -> ir::types::Type {
}
}
-pub fn indirect_signature(isa: &dyn TargetIsa, wasm: &WasmFuncType) -> ir::Signature {
+/// Returns a cranelift signature suitable to indirectly call the wasm signature
+/// specified by `wasm`.
+///
+/// This will implicitly use the default calling convention for `isa` since to
+/// indirectly call a wasm function it must be possibly exported somehow (e.g.
+/// this assumes the function target to call doesn't use the "fast" calling
+/// convention).
+fn indirect_signature(isa: &dyn TargetIsa, wasm: &WasmFuncType) -> ir::Signature {
let mut sig = blank_sig(isa, wasmtime_call_conv(isa));
push_types(isa, &mut sig, wasm);
return sig;
}
-pub fn func_signature(
+/// Returns the cranelift fucntion signature of the function specified.
+///
+/// Note that this will determine the calling convention for the function, and
+/// namely includes an optimization where functions never exported from a module
+/// use a custom theoretically faster calling convention instead of the default.
+fn func_signature(
isa: &dyn TargetIsa,
module: &Module,
types: &TypeTables,
@@ -727,50 +198,3 @@ pub fn func_signature(
);
return sig;
}
-
-/// We don't expect trampoline compilation to produce many relocations, so
-/// this `RelocSink` just asserts that it doesn't recieve most of them, but
-/// handles libcall ones.
-#[derive(Default)]
-struct TrampolineRelocSink {
- relocs: Vec,
-}
-
-impl binemit::RelocSink for TrampolineRelocSink {
- fn reloc_external(
- &mut self,
- offset: binemit::CodeOffset,
- _srcloc: ir::SourceLoc,
- reloc: binemit::Reloc,
- name: &ir::ExternalName,
- addend: binemit::Addend,
- ) {
- let reloc_target = if let ir::ExternalName::LibCall(libcall) = *name {
- RelocationTarget::LibCall(libcall)
- } else {
- panic!("unrecognized external name")
- };
- self.relocs.push(Relocation {
- reloc,
- reloc_target,
- offset,
- addend,
- });
- }
- fn reloc_constant(
- &mut self,
- _code_offset: binemit::CodeOffset,
- _reloc: binemit::Reloc,
- _constant_offset: ir::ConstantOffset,
- ) {
- panic!("trampoline compilation should not produce constant relocs");
- }
- fn reloc_jt(
- &mut self,
- _offset: binemit::CodeOffset,
- _reloc: binemit::Reloc,
- _jt: ir::JumpTable,
- ) {
- panic!("trampoline compilation should not produce jump table relocs");
- }
-}
diff --git a/crates/debug/src/lib.rs b/crates/debug/src/lib.rs
index db9e664834..954674f3c0 100644
--- a/crates/debug/src/lib.rs
+++ b/crates/debug/src/lib.rs
@@ -2,221 +2,8 @@
#![allow(clippy::cast_ptr_alignment)]
-use anyhow::{bail, ensure, Error};
-use object::endian::{BigEndian, Endian, Endianness, LittleEndian};
-use object::{RelocationEncoding, RelocationKind};
-use std::collections::HashMap;
-
pub use crate::write_debuginfo::{emit_dwarf, DwarfSection, DwarfSectionRelocTarget};
mod gc;
mod transform;
mod write_debuginfo;
-
-pub fn create_gdbjit_image(
- mut bytes: Vec,
- code_region: (*const u8, usize),
- defined_funcs_offset: usize,
- funcs: &[*const u8],
-) -> Result, Error> {
- let e = ensure_supported_elf_format(&bytes)?;
-
- // patch relocs
- relocate_dwarf_sections(&bytes, defined_funcs_offset, funcs)?;
-
- // elf is still missing details...
- match e {
- Endianness::Little => {
- convert_object_elf_to_loadable_file::(&mut bytes, code_region)
- }
- Endianness::Big => {
- convert_object_elf_to_loadable_file::(&mut bytes, code_region)
- }
- }
-
- // let mut file = ::std::fs::File::create(::std::path::Path::new("test.o")).expect("file");
- // ::std::io::Write::write_all(&mut file, &bytes).expect("write");
-
- Ok(bytes)
-}
-
-fn relocate_dwarf_sections(
- bytes: &[u8],
- defined_funcs_offset: usize,
- funcs: &[*const u8],
-) -> Result<(), Error> {
- use object::read::{File, Object, ObjectSection, ObjectSymbol, RelocationTarget};
-
- let obj = File::parse(bytes)?;
- let mut func_symbols = HashMap::new();
- for sym in obj.symbols() {
- match (sym.name(), sym.section_index()) {
- (Ok(name), Some(_section_index)) if name.starts_with("_wasm_function_") => {
- let index = name["_wasm_function_".len()..].parse::()?;
- let data = funcs[index - defined_funcs_offset];
- func_symbols.insert(sym.index(), data);
- }
- _ => (),
- }
- }
-
- for section in obj.sections() {
- for (off, r) in section.relocations() {
- if r.kind() != RelocationKind::Absolute
- || r.encoding() != RelocationEncoding::Generic
- || r.size() != 64
- {
- continue;
- }
-
- let data = match r.target() {
- RelocationTarget::Symbol(ref index) => func_symbols.get(index),
- _ => None,
- };
- let data: *const u8 = match data {
- Some(data) => *data,
- None => {
- continue;
- }
- };
-
- let target = (data as u64).wrapping_add(r.addend() as u64);
-
- let entry_ptr = section.data_range(off, 8).unwrap().unwrap().as_ptr();
- unsafe {
- std::ptr::write(entry_ptr as *mut u64, target);
- }
- }
- }
- Ok(())
-}
-
-fn ensure_supported_elf_format(bytes: &[u8]) -> Result {
- use object::elf::*;
- use object::read::elf::*;
- use std::mem::size_of;
-
- let kind = match object::FileKind::parse(bytes) {
- Ok(file) => file,
- Err(err) => {
- bail!("Failed to parse file: {}", err);
- }
- };
- let header = match kind {
- object::FileKind::Elf64 => match object::elf::FileHeader64::::parse(bytes) {
- Ok(header) => header,
- Err(err) => {
- bail!("Unsupported ELF file: {}", err);
- }
- },
- _ => {
- bail!("only 64-bit ELF files currently supported")
- }
- };
- let e = header.endian().unwrap();
-
- match header.e_machine.get(e) {
- EM_AARCH64 => (),
- EM_X86_64 => (),
- EM_S390 => (),
- machine => {
- bail!("Unsupported ELF target machine: {:x}", machine);
- }
- }
- ensure!(
- header.e_phoff.get(e) == 0 && header.e_phnum.get(e) == 0,
- "program header table is empty"
- );
- let e_shentsize = header.e_shentsize.get(e);
- let req_shentsize = match e {
- Endianness::Little => size_of::>(),
- Endianness::Big => size_of::>(),
- };
- ensure!(e_shentsize as usize == req_shentsize, "size of sh");
- Ok(e)
-}
-
-fn convert_object_elf_to_loadable_file(
- bytes: &mut Vec,
- code_region: (*const u8, usize),
-) {
- use object::elf::*;
- use std::ffi::CStr;
- use std::mem::size_of;
- use std::os::raw::c_char;
-
- let e = E::default();
- let header: &FileHeader64 = unsafe { &*(bytes.as_mut_ptr() as *const FileHeader64<_>) };
-
- let e_shentsize = header.e_shentsize.get(e);
- let e_shoff = header.e_shoff.get(e);
- let e_shnum = header.e_shnum.get(e);
- let mut shstrtab_off = 0;
- for i in 0..e_shnum {
- let off = e_shoff as isize + i as isize * e_shentsize as isize;
- let section: &SectionHeader64 =
- unsafe { &*(bytes.as_ptr().offset(off) as *const SectionHeader64<_>) };
- if section.sh_type.get(e) != SHT_STRTAB {
- continue;
- }
- shstrtab_off = section.sh_offset.get(e);
- }
- let mut segment: Option<_> = None;
- for i in 0..e_shnum {
- let off = e_shoff as isize + i as isize * e_shentsize as isize;
- let section: &mut SectionHeader64 =
- unsafe { &mut *(bytes.as_mut_ptr().offset(off) as *mut SectionHeader64<_>) };
- if section.sh_type.get(e) != SHT_PROGBITS {
- continue;
- }
- // It is a SHT_PROGBITS, but we need to check sh_name to ensure it is our function
- let sh_name_off = section.sh_name.get(e);
- let sh_name = unsafe {
- CStr::from_ptr(
- bytes
- .as_ptr()
- .offset((shstrtab_off + sh_name_off as u64) as isize)
- as *const c_char,
- )
- .to_str()
- .expect("name")
- };
- if sh_name != ".text" {
- continue;
- }
-
- assert!(segment.is_none());
- // Patch vaddr, and save file location and its size.
- section.sh_addr.set(e, code_region.0 as u64);
- let sh_offset = section.sh_offset.get(e);
- let sh_size = section.sh_size.get(e);
- segment = Some((sh_offset, sh_size));
- }
-
- // LLDB wants segment with virtual address set, placing them at the end of ELF.
- let ph_off = bytes.len();
- let e_phentsize = size_of::>();
- let e_phnum = 1;
- bytes.resize(ph_off + e_phentsize * e_phnum, 0);
- if let Some((sh_offset, sh_size)) = segment {
- let (v_offset, size) = code_region;
- let program: &mut ProgramHeader64 =
- unsafe { &mut *(bytes.as_ptr().add(ph_off) as *mut ProgramHeader64<_>) };
- program.p_type.set(e, PT_LOAD);
- program.p_offset.set(e, sh_offset);
- program.p_vaddr.set(e, v_offset as u64);
- program.p_paddr.set(e, v_offset as u64);
- program.p_filesz.set(e, sh_size as u64);
- program.p_memsz.set(e, size as u64);
- } else {
- unreachable!();
- }
-
- // It is somewhat loadable ELF file at this moment.
- let header: &mut FileHeader64 =
- unsafe { &mut *(bytes.as_mut_ptr() as *mut FileHeader64<_>) };
- header.e_type.set(e, ET_DYN);
- header.e_phoff.set(e, ph_off as u64);
- header.e_phentsize.set(e, e_phentsize as u16);
- header.e_phnum.set(e, e_phnum as u16);
-}
diff --git a/crates/debug/src/write_debuginfo.rs b/crates/debug/src/write_debuginfo.rs
index 491267b495..3d7b1451da 100644
--- a/crates/debug/src/write_debuginfo.rs
+++ b/crates/debug/src/write_debuginfo.rs
@@ -6,25 +6,7 @@ use wasmtime_environ::ir::Endianness;
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
use wasmtime_environ::{CompiledFunctions, DebugInfoData, ModuleMemoryOffset};
-#[derive(Clone)]
-pub enum DwarfSectionRelocTarget {
- Func(usize),
- Section(&'static str),
-}
-
-#[derive(Clone)]
-pub struct DwarfSectionReloc {
- pub target: DwarfSectionRelocTarget,
- pub offset: u32,
- pub addend: i32,
- pub size: u8,
-}
-
-pub struct DwarfSection {
- pub name: &'static str,
- pub body: Vec,
- pub relocs: Vec,
-}
+pub use wasmtime_environ::{DwarfSection, DwarfSectionReloc, DwarfSectionRelocTarget};
fn emit_dwarf_sections(
isa: &dyn TargetIsa,
diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml
index 7aec79ce10..b8a5e9869b 100644
--- a/crates/environ/Cargo.toml
+++ b/crates/environ/Cargo.toml
@@ -11,6 +11,7 @@ keywords = ["webassembly", "wasm"]
edition = "2018"
[dependencies]
+anyhow = "1.0"
cranelift-codegen = { path = "../../cranelift/codegen", version = "0.76.0", features = ["enable-serde"] }
cranelift-entity = { path = "../../cranelift/entity", version = "0.76.0", features = ["enable-serde"] }
cranelift-wasm = { path = "../../cranelift/wasm", version = "0.76.0", features = ["enable-serde"] }
@@ -22,6 +23,7 @@ log = { version = "0.4.8", default-features = false }
more-asserts = "0.2.1"
cfg-if = "1.0"
gimli = "0.25.0"
+target-lexicon = "0.12"
[badges]
maintenance = { status = "actively-developed" }
diff --git a/crates/environ/src/compilation.rs b/crates/environ/src/compilation.rs
index 5490e55cd9..551804aba7 100644
--- a/crates/environ/src/compilation.rs
+++ b/crates/environ/src/compilation.rs
@@ -2,10 +2,14 @@
//! module.
use crate::{FunctionAddressMap, FunctionBodyData, ModuleTranslation, Tunables, TypeTables};
-use cranelift_codegen::{binemit, ir, isa, isa::unwind::UnwindInfo};
+use anyhow::Result;
+use cranelift_codegen::{binemit, ir, isa::unwind::UnwindInfo};
use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmError, WasmFuncType};
use serde::{Deserialize, Serialize};
+use std::borrow::Cow;
+use std::collections::HashMap;
+use std::fmt;
use thiserror::Error;
#[allow(missing_docs)]
@@ -93,37 +97,173 @@ pub enum CompileError {
DebugInfoNotSupported,
}
-/// An implementation of a compiler from parsed WebAssembly module to native
-/// code.
+/// Abstract trait representing the ability to create a `Compiler` below.
+///
+/// This is used in Wasmtime to separate compiler implementations, currently
+/// mostly used to separate Cranelift from Wasmtime itself.
+pub trait CompilerBuilder: Send + Sync + fmt::Debug {
+ /// Like the `Clone` trait, but for the boxed trait object.
+ fn clone(&self) -> Box;
+
+ /// Sets the target of compilation to the target specified.
+ fn target(&mut self, target: target_lexicon::Triple) -> Result<()>;
+
+ /// Returns the currently configured target triple that compilation will
+ /// produce artifacts for.
+ fn triple(&self) -> &target_lexicon::Triple;
+
+ /// Compiler-specific method to configure various settings in the compiler
+ /// itself.
+ ///
+ /// This is expected to be defined per-compiler. Compilers should return
+ /// errors for unknown names/values.
+ fn set(&mut self, name: &str, val: &str) -> Result<()>;
+
+ /// Compiler-specific method for configuring settings.
+ ///
+ /// Same as [`CompilerBuilder::set`] except for enabling boolean flags.
+ /// Currently cranelift uses this to sometimes enable a family of settings.
+ fn enable(&mut self, name: &str) -> Result<()>;
+
+ /// Returns a list of all possible settings that can be configured with
+ /// [`CompilerBuilder::set`] and [`CompilerBuilder::enable`].
+ fn settings(&self) -> Vec;
+
+ /// Builds a new [`Compiler`] object from this configuration.
+ fn build(&self) -> Box;
+}
+
+/// Description of compiler settings returned by [`CompilerBuilder::settings`].
+#[derive(Clone, Copy, Debug)]
+pub struct Setting {
+ /// The name of the setting.
+ pub name: &'static str,
+ /// The description of the setting.
+ pub description: &'static str,
+ /// The kind of the setting.
+ pub kind: SettingKind,
+ /// The supported values of the setting (for enum values).
+ pub values: Option<&'static [&'static str]>,
+}
+
+/// Different kinds of [`Setting`] values that can be configured in a
+/// [`CompilerBuilder`]
+#[derive(Clone, Copy, Debug)]
+pub enum SettingKind {
+ /// The setting is an enumeration, meaning it's one of a set of values.
+ Enum,
+ /// The setting is a number.
+ Num,
+ /// The setting is a boolean.
+ Bool,
+ /// The setting is a preset.
+ Preset,
+}
+
+/// An implementation of a compiler which can compile WebAssembly functions to
+/// machine code and perform other miscellaneous tasks needed by the JIT runtime.
pub trait Compiler: Send + Sync {
- /// Compile a function with the given `TargetIsa`.
+ /// Compiles the function `index` within `translation`.
+ ///
+ /// The body of the function is available in `data` and configuration
+ /// values are also passed in via `tunables`. Type information in
+ /// `translation` is all relative to `types`.
fn compile_function(
&self,
translation: &ModuleTranslation<'_>,
index: DefinedFuncIndex,
data: FunctionBodyData<'_>,
- isa: &dyn isa::TargetIsa,
tunables: &Tunables,
types: &TypeTables,
) -> Result;
- /// Creates a trampoline which the host can use to enter wasm. The
- /// trampoline has type `VMTrampoline` and will call a function of type `ty`
- /// specified.
- fn host_to_wasm_trampoline(
- &self,
- isa: &dyn isa::TargetIsa,
- ty: &WasmFuncType,
- ) -> Result;
+ /// Creates a trampoline which the host can use to enter wasm.
+ ///
+ /// The generated trampoline will have the type `VMTrampoline` and will
+ /// call a function of type `ty` specified.
+ fn host_to_wasm_trampoline(&self, ty: &WasmFuncType) -> Result;
/// Creates a trampoline suitable for a wasm module to import.
///
/// The trampoline has the type specified by `ty` and will call the function
- /// `host_fn` which has type `VMTrampoline`.
+ /// `host_fn` which has type `VMTrampoline`. Note that `host_fn` is
+ /// directly embedded into the generated code so this is not suitable for a
+ /// cached value or if `host_fn` does not live as long as the compiled
+ /// function.
+ ///
+ /// This is primarily used for `Func::new` in `wasmtime`.
fn wasm_to_host_trampoline(
&self,
- isa: &dyn isa::TargetIsa,
ty: &WasmFuncType,
host_fn: usize,
) -> Result;
+
+ /// Creates DWARF debugging data for a compilation unit.
+ ///
+ /// This function maps DWARF information found in a wasm module to native
+ /// DWARF debugging information. This is currently implemented by the
+ /// `wasmtime-debug` crate.
+ fn emit_dwarf(
+ &self,
+ debuginfo_data: &crate::DebugInfoData,
+ funcs: &CompiledFunctions,
+ memory_offset: &crate::ModuleMemoryOffset,
+ ) -> Result>;
+
+ /// Returns the target triple that this compiler is compiling for.
+ fn triple(&self) -> &target_lexicon::Triple;
+
+ /// If supported by the target creates a SystemV CIE used for dwarf
+ /// unwinding information.
+ fn create_systemv_cie(&self) -> Option;
+
+ /// Returns a list of configured settings for this compiler.
+ fn flags(&self) -> HashMap;
+
+ /// Same as [`Compiler::flags`], but ISA-specific (a cranelift-ism)
+ fn isa_flags(&self) -> HashMap;
+}
+
+#[allow(missing_docs)]
+pub struct DwarfSection {
+ pub name: &'static str,
+ pub body: Vec,
+ pub relocs: Vec,
+}
+
+#[allow(missing_docs)]
+#[derive(Clone)]
+pub struct DwarfSectionReloc {
+ pub target: DwarfSectionRelocTarget,
+ pub offset: u32,
+ pub addend: i32,
+ pub size: u8,
+}
+
+#[allow(missing_docs)]
+#[derive(Clone)]
+pub enum DwarfSectionRelocTarget {
+ Func(usize),
+ Section(&'static str),
+}
+
+/// Value of a configured setting for a [`Compiler`]
+#[derive(Serialize, Deserialize, Hash, Eq, PartialEq)]
+pub enum FlagValue {
+ /// Name of the value that has been configured for this setting.
+ Enum(Cow<'static, str>),
+ /// The numerical value of the configured settings.
+ Num(u8),
+ /// Whether the setting is on or off.
+ Bool(bool),
+}
+
+impl fmt::Display for FlagValue {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Enum(v) => v.fmt(f),
+ Self::Num(v) => v.fmt(f),
+ Self::Bool(v) => v.fmt(f),
+ }
+ }
}
diff --git a/crates/environ/src/data_structures.rs b/crates/environ/src/data_structures.rs
index 29e87cb8d1..7f6d23d4e4 100644
--- a/crates/environ/src/data_structures.rs
+++ b/crates/environ/src/data_structures.rs
@@ -9,12 +9,6 @@ pub mod ir {
pub use cranelift_codegen::{ValueLabelsRanges, ValueLocRange};
}
-pub mod settings {
- pub use cranelift_codegen::settings::{
- builder, Builder, Configurable, Flags, OptLevel, SetError, Setting, SettingKind, Value,
- };
-}
-
pub mod isa {
pub use cranelift_codegen::isa::{
lookup, unwind, Builder, CallConv, RegUnit, TargetFrontendConfig, TargetIsa,
diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs
index a404701370..320d647f29 100644
--- a/crates/environ/src/module_environ.rs
+++ b/crates/environ/src/module_environ.rs
@@ -3,15 +3,13 @@ use crate::module::{
ModuleSignature, ModuleType, ModuleUpvar, TableInitializer, TablePlan, TypeTables,
};
use crate::tunables::Tunables;
-use cranelift_codegen::ir;
-use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_codegen::packed_option::ReservedValue;
use cranelift_entity::PrimaryMap;
use cranelift_wasm::{
self, translate_module, Alias, DataIndex, DefinedFuncIndex, ElemIndex, EntityIndex, EntityType,
FuncIndex, Global, GlobalIndex, GlobalInit, InstanceIndex, InstanceTypeIndex, Memory,
- MemoryIndex, ModuleIndex, ModuleTypeIndex, SignatureIndex, Table, TableIndex,
- TargetEnvironment, TypeIndex, WasmError, WasmFuncType, WasmResult,
+ MemoryIndex, ModuleIndex, ModuleTypeIndex, SignatureIndex, Table, TableIndex, TypeIndex,
+ WasmError, WasmFuncType, WasmResult,
};
use std::collections::{hash_map::Entry, HashMap};
use std::convert::TryFrom;
@@ -45,7 +43,6 @@ pub struct ModuleEnvironment<'data> {
// Various bits and pieces of configuration
features: WasmFeatures,
- target_config: TargetFrontendConfig,
tunables: Tunables,
first_module: bool,
}
@@ -134,18 +131,13 @@ pub struct FunctionMetadata {
impl<'data> ModuleEnvironment<'data> {
/// Allocates the environment data structures.
- pub fn new(
- target_config: TargetFrontendConfig,
- tunables: &Tunables,
- features: &WasmFeatures,
- ) -> Self {
+ pub fn new(tunables: &Tunables, features: &WasmFeatures) -> Self {
Self {
result: ModuleTranslation::default(),
results: Vec::with_capacity(1),
in_progress: Vec::new(),
modules_to_be: 1,
types: Default::default(),
- target_config,
tunables: tunables.clone(),
features: *features,
first_module: true,
@@ -153,10 +145,6 @@ impl<'data> ModuleEnvironment<'data> {
}
}
- fn pointer_type(&self) -> ir::Type {
- self.target_config.pointer_type()
- }
-
/// Translate a wasm module using this environment.
///
/// This consumes the `ModuleEnvironment` and produces a list of
@@ -378,16 +366,6 @@ impl<'data> ModuleEnvironment<'data> {
}
}
-impl<'data> TargetEnvironment for ModuleEnvironment<'data> {
- fn target_config(&self) -> TargetFrontendConfig {
- self.target_config
- }
-
- fn reference_type(&self, ty: cranelift_wasm::WasmType) -> ir::Type {
- crate::reference_type(ty, self.pointer_type())
- }
-}
-
/// This trait is useful for `translate_module` because it tells how to translate
/// environment-dependent wasm instructions. These functions should not be called by the user.
impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data> {
diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml
index 5c96a98c23..c7db67080f 100644
--- a/crates/jit/Cargo.toml
+++ b/crates/jit/Cargo.toml
@@ -15,7 +15,6 @@ wasmtime-environ = { path = "../environ", version = "0.29.0" }
wasmtime-runtime = { path = "../runtime", version = "0.29.0" }
wasmtime-cranelift = { path = "../cranelift", version = "0.29.0" }
wasmtime-lightbeam = { path = "../lightbeam/wasmtime", version = "0.29.0", optional = true }
-wasmtime-debug = { path = "../debug", version = "0.29.0" }
wasmtime-profiling = { path = "../profiling", version = "0.29.0" }
wasmtime-obj = { path = "../obj", version = "0.29.0" }
rayon = { version = "1.0", optional = true }
diff --git a/crates/jit/src/code_memory.rs b/crates/jit/src/code_memory.rs
index 89f790abc0..2b943d8f59 100644
--- a/crates/jit/src/code_memory.rs
+++ b/crates/jit/src/code_memory.rs
@@ -5,6 +5,7 @@ use crate::object::{
ObjectUnwindInfo,
};
use crate::unwind::UnwindRegistry;
+use crate::Compiler;
use anyhow::{Context, Result};
use object::read::{File as ObjectFile, Object, ObjectSection, ObjectSymbol};
use region;
@@ -12,7 +13,7 @@ use std::collections::BTreeMap;
use std::mem::ManuallyDrop;
use std::{cmp, mem};
use wasmtime_environ::{
- isa::{unwind::UnwindInfo, TargetIsa},
+ isa::unwind::UnwindInfo,
wasm::{FuncIndex, SignatureIndex},
CompiledFunction,
};
@@ -131,7 +132,7 @@ impl CodeMemory {
}
/// Make all allocated memory executable.
- pub fn publish(&mut self, isa: &dyn TargetIsa) {
+ pub fn publish(&mut self, compiler: &Compiler) {
self.push_current(0)
.expect("failed to push current memory map");
@@ -142,7 +143,7 @@ impl CodeMemory {
} in &mut self.entries[self.published..]
{
// Remove write access to the pages due to the relocation fixups.
- r.publish(isa)
+ r.publish(compiler)
.expect("failed to publish function unwind registry");
if !m.is_empty() {
diff --git a/crates/jit/src/compiler.rs b/crates/jit/src/compiler.rs
index 02c7842b08..a94dc8eae4 100644
--- a/crates/jit/src/compiler.rs
+++ b/crates/jit/src/compiler.rs
@@ -6,15 +6,14 @@ use object::write::Object;
#[cfg(feature = "parallel-compilation")]
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
+use std::collections::BTreeMap;
use std::hash::{Hash, Hasher};
use std::mem;
use wasmparser::WasmFeatures;
-use wasmtime_debug::{emit_dwarf, DwarfSection};
use wasmtime_environ::entity::EntityRef;
-use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa};
use wasmtime_environ::wasm::{DefinedMemoryIndex, MemoryIndex};
use wasmtime_environ::{
- CompiledFunctions, Compiler as EnvCompiler, DebugInfoData, Module, ModuleMemoryOffset,
+ CompiledFunctions, Compiler as EnvCompiler, CompilerBuilder, ModuleMemoryOffset,
ModuleTranslation, Tunables, TypeTables, VMOffsets,
};
@@ -33,41 +32,35 @@ pub enum CompilationStrategy {
}
/// A WebAssembly code JIT compiler.
-///
-/// A `Compiler` instance owns the executable memory that it allocates.
-///
-/// TODO: Evolve this to support streaming rather than requiring a `&[u8]`
-/// containing a whole wasm module at once.
-///
-/// TODO: Consider using cranelift-module.
pub struct Compiler {
- isa: Box,
compiler: Box,
- strategy: CompilationStrategy,
tunables: Tunables,
features: WasmFeatures,
parallel_compilation: bool,
}
impl Compiler {
- /// Construct a new `Compiler`.
+ /// Creates a new compiler builder for the provided compilation strategy.
+ pub fn builder(strategy: CompilationStrategy) -> Box {
+ match strategy {
+ CompilationStrategy::Auto | CompilationStrategy::Cranelift => {
+ wasmtime_cranelift::builder()
+ }
+ #[cfg(feature = "lightbeam")]
+ CompilationStrategy::Lightbeam => unimplemented!(),
+ }
+ }
+
+ /// Creates a new instance of a `Compiler` from the provided compiler
+ /// builder.
pub fn new(
- isa: Box,
- strategy: CompilationStrategy,
+ builder: &dyn CompilerBuilder,
tunables: Tunables,
features: WasmFeatures,
parallel_compilation: bool,
- ) -> Self {
- Self {
- isa,
- strategy,
- compiler: match strategy {
- CompilationStrategy::Auto | CompilationStrategy::Cranelift => {
- Box::new(wasmtime_cranelift::Cranelift::default())
- }
- #[cfg(feature = "lightbeam")]
- CompilationStrategy::Lightbeam => Box::new(wasmtime_lightbeam::Lightbeam),
- },
+ ) -> Compiler {
+ Compiler {
+ compiler: builder.build(),
tunables,
features,
parallel_compilation,
@@ -80,25 +73,6 @@ fn _assert_compiler_send_sync() {
_assert::();
}
-fn transform_dwarf_data(
- isa: &dyn TargetIsa,
- module: &Module,
- debug_data: &DebugInfoData,
- funcs: &CompiledFunctions,
-) -> Result, SetupError> {
- let target_config = isa.frontend_config();
- let ofs = VMOffsets::new(target_config.pointer_bytes(), &module);
-
- let memory_offset = if ofs.num_imported_memories > 0 {
- ModuleMemoryOffset::Imported(ofs.vmctx_vmmemory_import(MemoryIndex::new(0)))
- } else if ofs.num_defined_memories > 0 {
- ModuleMemoryOffset::Defined(ofs.vmctx_vmmemory_definition_base(DefinedMemoryIndex::new(0)))
- } else {
- ModuleMemoryOffset::None
- };
- emit_dwarf(isa, debug_data, funcs, &memory_offset).map_err(SetupError::DebugInfo)
-}
-
#[allow(missing_docs)]
pub struct Compilation {
pub obj: Object,
@@ -107,21 +81,6 @@ pub struct Compilation {
}
impl Compiler {
- /// Return the isa.
- pub fn isa(&self) -> &dyn TargetIsa {
- self.isa.as_ref()
- }
-
- /// Return the compiler's strategy.
- pub fn strategy(&self) -> CompilationStrategy {
- self.strategy
- }
-
- /// Return the target's frontend configuration settings.
- pub fn frontend_config(&self) -> TargetFrontendConfig {
- self.isa.frontend_config()
- }
-
/// Return the tunables in use by this engine.
pub fn tunables(&self) -> &Tunables {
&self.tunables
@@ -137,6 +96,11 @@ impl Compiler {
&*self.compiler
}
+ /// Returns the target this compiler is compiling for.
+ pub fn triple(&self) -> &target_lexicon::Triple {
+ self.compiler.triple()
+ }
+
/// Compile the given function bodies.
pub fn compile<'data>(
&self,
@@ -148,25 +112,35 @@ impl Compiler {
let funcs = self
.run_maybe_parallel(functions, |(index, func)| {
- self.compiler.compile_function(
- translation,
- index,
- func,
- &*self.isa,
- &self.tunables,
- types,
- )
+ self.compiler
+ .compile_function(translation, index, func, &self.tunables, types)
})?
.into_iter()
.collect::();
let dwarf_sections = if self.tunables.generate_native_debuginfo && !funcs.is_empty() {
- transform_dwarf_data(
- &*self.isa,
+ let ofs = VMOffsets::new(
+ self.compiler
+ .triple()
+ .architecture
+ .pointer_width()
+ .unwrap()
+ .bytes(),
&translation.module,
- &translation.debuginfo,
- &funcs,
- )?
+ );
+
+ let memory_offset = if ofs.num_imported_memories > 0 {
+ ModuleMemoryOffset::Imported(ofs.vmctx_vmmemory_import(MemoryIndex::new(0)))
+ } else if ofs.num_defined_memories > 0 {
+ ModuleMemoryOffset::Defined(
+ ofs.vmctx_vmmemory_definition_base(DefinedMemoryIndex::new(0)),
+ )
+ } else {
+ ModuleMemoryOffset::None
+ };
+ self.compiler
+ .emit_dwarf(&translation.debuginfo, &funcs, &memory_offset)
+ .map_err(SetupError::DebugInfo)?
} else {
vec![]
};
@@ -211,29 +185,27 @@ impl Compiler {
impl Hash for Compiler {
fn hash(&self, hasher: &mut H) {
let Compiler {
- strategy,
- compiler: _,
- isa,
+ compiler,
tunables,
features,
parallel_compilation: _,
} = self;
- // Hash compiler's flags: compilation strategy, isa, frontend config,
- // misc tunables.
- strategy.hash(hasher);
- isa.triple().hash(hasher);
- isa.hash_all_flags(hasher);
- isa.frontend_config().hash(hasher);
+ compiler.triple().hash(hasher);
+ compiler
+ .flags()
+ .into_iter()
+ .collect::>()
+ .hash(hasher);
+ compiler
+ .isa_flags()
+ .into_iter()
+ .collect::>()
+ .hash(hasher);
tunables.hash(hasher);
features.hash(hasher);
// Catch accidental bugs of reusing across crate versions.
env!("CARGO_PKG_VERSION").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.
}
}
diff --git a/crates/jit/src/debug.rs b/crates/jit/src/debug.rs
new file mode 100644
index 0000000000..f7bdd77b27
--- /dev/null
+++ b/crates/jit/src/debug.rs
@@ -0,0 +1,212 @@
+use anyhow::{bail, ensure, Error};
+use object::endian::{BigEndian, Endian, Endianness, LittleEndian};
+use object::{RelocationEncoding, RelocationKind};
+use std::collections::HashMap;
+
+pub fn create_gdbjit_image(
+ mut bytes: Vec,
+ code_region: (*const u8, usize),
+ defined_funcs_offset: usize,
+ funcs: &[*const u8],
+) -> Result, Error> {
+ let e = ensure_supported_elf_format(&bytes)?;
+
+ // patch relocs
+ relocate_dwarf_sections(&bytes, defined_funcs_offset, funcs)?;
+
+ // elf is still missing details...
+ match e {
+ Endianness::Little => {
+ convert_object_elf_to_loadable_file::(&mut bytes, code_region)
+ }
+ Endianness::Big => {
+ convert_object_elf_to_loadable_file::(&mut bytes, code_region)
+ }
+ }
+
+ // let mut file = ::std::fs::File::create(::std::path::Path::new("test.o")).expect("file");
+ // ::std::io::Write::write_all(&mut file, &bytes).expect("write");
+
+ Ok(bytes)
+}
+
+fn relocate_dwarf_sections(
+ bytes: &[u8],
+ defined_funcs_offset: usize,
+ funcs: &[*const u8],
+) -> Result<(), Error> {
+ use object::read::{File, Object, ObjectSection, ObjectSymbol, RelocationTarget};
+
+ let obj = File::parse(bytes)?;
+ let mut func_symbols = HashMap::new();
+ for sym in obj.symbols() {
+ match (sym.name(), sym.section_index()) {
+ (Ok(name), Some(_section_index)) if name.starts_with("_wasm_function_") => {
+ let index = name["_wasm_function_".len()..].parse::()?;
+ let data = funcs[index - defined_funcs_offset];
+ func_symbols.insert(sym.index(), data);
+ }
+ _ => (),
+ }
+ }
+
+ for section in obj.sections() {
+ for (off, r) in section.relocations() {
+ if r.kind() != RelocationKind::Absolute
+ || r.encoding() != RelocationEncoding::Generic
+ || r.size() != 64
+ {
+ continue;
+ }
+
+ let data = match r.target() {
+ RelocationTarget::Symbol(ref index) => func_symbols.get(index),
+ _ => None,
+ };
+ let data: *const u8 = match data {
+ Some(data) => *data,
+ None => {
+ continue;
+ }
+ };
+
+ let target = (data as u64).wrapping_add(r.addend() as u64);
+
+ let entry_ptr = section.data_range(off, 8).unwrap().unwrap().as_ptr();
+ unsafe {
+ std::ptr::write(entry_ptr as *mut u64, target);
+ }
+ }
+ }
+ Ok(())
+}
+
+fn ensure_supported_elf_format(bytes: &[u8]) -> Result {
+ use object::elf::*;
+ use object::read::elf::*;
+ use std::mem::size_of;
+
+ let kind = match object::FileKind::parse(bytes) {
+ Ok(file) => file,
+ Err(err) => {
+ bail!("Failed to parse file: {}", err);
+ }
+ };
+ let header = match kind {
+ object::FileKind::Elf64 => match object::elf::FileHeader64::::parse(bytes) {
+ Ok(header) => header,
+ Err(err) => {
+ bail!("Unsupported ELF file: {}", err);
+ }
+ },
+ _ => {
+ bail!("only 64-bit ELF files currently supported")
+ }
+ };
+ let e = header.endian().unwrap();
+
+ match header.e_machine.get(e) {
+ EM_AARCH64 => (),
+ EM_X86_64 => (),
+ EM_S390 => (),
+ machine => {
+ bail!("Unsupported ELF target machine: {:x}", machine);
+ }
+ }
+ ensure!(
+ header.e_phoff.get(e) == 0 && header.e_phnum.get(e) == 0,
+ "program header table is empty"
+ );
+ let e_shentsize = header.e_shentsize.get(e);
+ let req_shentsize = match e {
+ Endianness::Little => size_of::>(),
+ Endianness::Big => size_of::>(),
+ };
+ ensure!(e_shentsize as usize == req_shentsize, "size of sh");
+ Ok(e)
+}
+
+fn convert_object_elf_to_loadable_file(
+ bytes: &mut Vec,
+ code_region: (*const u8, usize),
+) {
+ use object::elf::*;
+ use std::ffi::CStr;
+ use std::mem::size_of;
+ use std::os::raw::c_char;
+
+ let e = E::default();
+ let header: &FileHeader64 = unsafe { &*(bytes.as_mut_ptr() as *const FileHeader64<_>) };
+
+ let e_shentsize = header.e_shentsize.get(e);
+ let e_shoff = header.e_shoff.get(e);
+ let e_shnum = header.e_shnum.get(e);
+ let mut shstrtab_off = 0;
+ for i in 0..e_shnum {
+ let off = e_shoff as isize + i as isize * e_shentsize as isize;
+ let section: &SectionHeader64 =
+ unsafe { &*(bytes.as_ptr().offset(off) as *const SectionHeader64<_>) };
+ if section.sh_type.get(e) != SHT_STRTAB {
+ continue;
+ }
+ shstrtab_off = section.sh_offset.get(e);
+ }
+ let mut segment: Option<_> = None;
+ for i in 0..e_shnum {
+ let off = e_shoff as isize + i as isize * e_shentsize as isize;
+ let section: &mut SectionHeader64 =
+ unsafe { &mut *(bytes.as_mut_ptr().offset(off) as *mut SectionHeader64<_>) };
+ if section.sh_type.get(e) != SHT_PROGBITS {
+ continue;
+ }
+ // It is a SHT_PROGBITS, but we need to check sh_name to ensure it is our function
+ let sh_name_off = section.sh_name.get(e);
+ let sh_name = unsafe {
+ CStr::from_ptr(
+ bytes
+ .as_ptr()
+ .offset((shstrtab_off + sh_name_off as u64) as isize)
+ as *const c_char,
+ )
+ .to_str()
+ .expect("name")
+ };
+ if sh_name != ".text" {
+ continue;
+ }
+
+ assert!(segment.is_none());
+ // Patch vaddr, and save file location and its size.
+ section.sh_addr.set(e, code_region.0 as u64);
+ let sh_offset = section.sh_offset.get(e);
+ let sh_size = section.sh_size.get(e);
+ segment = Some((sh_offset, sh_size));
+ }
+
+ // LLDB wants segment with virtual address set, placing them at the end of ELF.
+ let ph_off = bytes.len();
+ let e_phentsize = size_of::>();
+ let e_phnum = 1;
+ bytes.resize(ph_off + e_phentsize * e_phnum, 0);
+ if let Some((sh_offset, sh_size)) = segment {
+ let (v_offset, size) = code_region;
+ let program: &mut ProgramHeader64 =
+ unsafe { &mut *(bytes.as_ptr().add(ph_off) as *mut ProgramHeader64<_>) };
+ program.p_type.set(e, PT_LOAD);
+ program.p_offset.set(e, sh_offset);
+ program.p_vaddr.set(e, v_offset as u64);
+ program.p_paddr.set(e, v_offset as u64);
+ program.p_filesz.set(e, sh_size);
+ program.p_memsz.set(e, size as u64);
+ } else {
+ unreachable!();
+ }
+
+ // It is somewhat loadable ELF file at this moment.
+ let header: &mut FileHeader64 =
+ unsafe { &mut *(bytes.as_mut_ptr() as *mut FileHeader64<_>) };
+ header.e_type.set(e, ET_DYN);
+ header.e_phoff.set(e, ph_off as u64);
+ header.e_phentsize.set(e, e_phentsize as u16);
+ header.e_phnum.set(e, e_phnum as u16);
+}
diff --git a/crates/jit/src/instantiate.rs b/crates/jit/src/instantiate.rs
index bc88ca638a..f333a0b9e4 100644
--- a/crates/jit/src/instantiate.rs
+++ b/crates/jit/src/instantiate.rs
@@ -5,6 +5,7 @@
use crate::code_memory::CodeMemory;
use crate::compiler::{Compilation, Compiler};
+use crate::debug::create_gdbjit_image;
use crate::link::link_module;
use crate::object::ObjectUnwindInfo;
use anyhow::{Context, Result};
@@ -13,9 +14,7 @@ use serde::{Deserialize, Serialize};
use std::ops::Range;
use std::sync::Arc;
use thiserror::Error;
-use wasmtime_debug::create_gdbjit_image;
use wasmtime_environ::entity::PrimaryMap;
-use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::wasm::{
DefinedFuncIndex, InstanceTypeIndex, ModuleTypeIndex, SignatureIndex, WasmFuncType,
};
@@ -103,13 +102,10 @@ impl CompilationArtifacts {
data: &[u8],
use_paged_mem_init: bool,
) -> Result<(usize, Vec, TypeTables), SetupError> {
- let (main_module, translations, types) = ModuleEnvironment::new(
- compiler.frontend_config(),
- compiler.tunables(),
- compiler.features(),
- )
- .translate(data)
- .map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?;
+ let (main_module, translations, types) =
+ ModuleEnvironment::new(compiler.tunables(), compiler.features())
+ .translate(data)
+ .map_err(|error| SetupError::Compile(CompileError::Wasm(error)))?;
let list = compiler.run_maybe_parallel::<_, _, SetupError, _>(
translations,
@@ -225,25 +221,24 @@ impl CompiledModule {
/// artifacts.
pub fn from_artifacts_list(
artifacts: Vec,
- isa: &dyn TargetIsa,
- profiler: &dyn ProfilingAgent,
compiler: &Compiler,
+ profiler: &dyn ProfilingAgent,
) -> Result>, SetupError> {
compiler.run_maybe_parallel(artifacts, |a| {
- CompiledModule::from_artifacts(a, isa, profiler)
+ CompiledModule::from_artifacts(a, compiler, profiler)
})
}
/// Creates `CompiledModule` directly from `CompilationArtifacts`.
pub fn from_artifacts(
artifacts: CompilationArtifacts,
- isa: &dyn TargetIsa,
+ compiler: &Compiler,
profiler: &dyn ProfilingAgent,
) -> Result, SetupError> {
// Allocate all of the compiled functions into executable memory,
// copying over their contents.
let (code_memory, code_range, finished_functions, trampolines) = build_code_memory(
- isa,
+ compiler,
&artifacts.obj,
&artifacts.module,
&artifacts.unwind_info,
@@ -480,7 +475,7 @@ fn create_dbg_image(
}
fn build_code_memory(
- isa: &dyn TargetIsa,
+ compiler: &Compiler,
obj: &[u8],
module: &Module,
unwind_info: &[ObjectUnwindInfo],
@@ -531,7 +526,7 @@ fn build_code_memory(
let code_range = (code_range.as_ptr(), code_range.len());
// Make all code compiled thus far executable.
- code_memory.publish(isa);
+ code_memory.publish(compiler);
Ok((code_memory, code_range, finished_functions, trampolines))
}
diff --git a/crates/jit/src/lib.rs b/crates/jit/src/lib.rs
index aa75e68236..f35010d1b3 100644
--- a/crates/jit/src/lib.rs
+++ b/crates/jit/src/lib.rs
@@ -22,6 +22,7 @@
mod code_memory;
mod compiler;
+mod debug;
mod instantiate;
mod link;
mod object;
diff --git a/crates/jit/src/object.rs b/crates/jit/src/object.rs
index b20dc8c7ee..2953490324 100644
--- a/crates/jit/src/object.rs
+++ b/crates/jit/src/object.rs
@@ -4,10 +4,9 @@ use crate::Compiler;
use object::write::Object;
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
-use wasmtime_debug::DwarfSection;
use wasmtime_environ::isa::unwind::UnwindInfo;
use wasmtime_environ::wasm::{FuncIndex, SignatureIndex};
-use wasmtime_environ::{CompiledFunctions, ModuleTranslation, TypeTables};
+use wasmtime_environ::{CompiledFunctions, DwarfSection, ModuleTranslation, TypeTables};
use wasmtime_obj::{ObjectBuilder, ObjectBuilderTarget};
pub use wasmtime_obj::utils;
@@ -52,7 +51,7 @@ pub(crate) fn build_object(
for i in signatures {
let func = compiler
.compiler()
- .host_to_wasm_trampoline(compiler.isa(), &types.wasm_signatures[i])?;
+ .host_to_wasm_trampoline(&types.wasm_signatures[i])?;
// Preserve trampoline function unwind info.
if let Some(info) = &func.unwind_info {
unwind_info.push(ObjectUnwindInfo::Trampoline(i, info.clone()))
@@ -60,7 +59,7 @@ pub(crate) fn build_object(
trampolines.push((i, func));
}
- let target = ObjectBuilderTarget::new(compiler.isa().triple().architecture)?;
+ let target = ObjectBuilderTarget::new(compiler.compiler().triple().architecture)?;
let mut builder = ObjectBuilder::new(target, &translation.module, funcs);
builder
.set_code_alignment(CODE_SECTION_ALIGNMENT)
diff --git a/crates/jit/src/unwind/systemv.rs b/crates/jit/src/unwind/systemv.rs
index d29819d30f..c8a9142b06 100644
--- a/crates/jit/src/unwind/systemv.rs
+++ b/crates/jit/src/unwind/systemv.rs
@@ -1,11 +1,12 @@
//! Module for System V ABI unwind registry.
+use crate::Compiler;
use anyhow::{bail, Result};
use gimli::{
write::{Address, EhFrame, EndianVec, FrameTable, Writer},
RunTimeEndian,
};
-use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
+use wasmtime_environ::isa::unwind::UnwindInfo;
/// Represents a registry of function unwind information for System V ABI.
pub struct UnwindRegistry {
@@ -53,7 +54,7 @@ impl UnwindRegistry {
}
/// Publishes all registered functions.
- pub fn publish(&mut self, isa: &dyn TargetIsa) -> Result<()> {
+ pub fn publish(&mut self, compiler: &Compiler) -> Result<()> {
if self.published {
bail!("unwind registry has already been published");
}
@@ -63,7 +64,7 @@ impl UnwindRegistry {
return Ok(());
}
- self.set_frame_table(isa)?;
+ self.set_frame_table(compiler)?;
unsafe {
self.register_frames();
@@ -74,9 +75,9 @@ impl UnwindRegistry {
Ok(())
}
- fn set_frame_table(&mut self, isa: &dyn TargetIsa) -> Result<()> {
+ fn set_frame_table(&mut self, compiler: &Compiler) -> Result<()> {
let mut table = FrameTable::default();
- let cie_id = table.add_cie(match isa.create_systemv_cie() {
+ let cie_id = table.add_cie(match compiler.compiler().create_systemv_cie() {
Some(cie) => cie,
None => bail!("ISA does not support System V unwind information"),
});
diff --git a/crates/jit/src/unwind/winx64.rs b/crates/jit/src/unwind/winx64.rs
index f46d2edfd3..260a8cf629 100644
--- a/crates/jit/src/unwind/winx64.rs
+++ b/crates/jit/src/unwind/winx64.rs
@@ -1,7 +1,8 @@
//! Module for Windows x64 ABI unwind registry.
+use crate::Compiler;
use anyhow::{bail, Result};
-use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
+use wasmtime_environ::isa::unwind::UnwindInfo;
use winapi::um::winnt;
/// Represents a registry of function unwind information for Windows x64 ABI.
@@ -49,7 +50,7 @@ impl UnwindRegistry {
}
/// Publishes all registered functions.
- pub fn publish(&mut self, _isa: &dyn TargetIsa) -> Result<()> {
+ pub fn publish(&mut self, _compiler: &Compiler) -> Result<()> {
if self.published {
bail!("unwind registry has already been published");
}
diff --git a/crates/lightbeam/wasmtime/Cargo.toml b/crates/lightbeam/wasmtime/Cargo.toml
index a2c2e48906..c316bbbac2 100644
--- a/crates/lightbeam/wasmtime/Cargo.toml
+++ b/crates/lightbeam/wasmtime/Cargo.toml
@@ -12,6 +12,9 @@ readme = "README.md"
edition = "2018"
[dependencies]
+anyhow = "1.0"
+target-lexicon = "0.12"
+gimli = "0.25"
lightbeam = { path = "..", version = "0.29.0" }
wasmparser = "0.80"
cranelift-codegen = { path = "../../../cranelift/codegen", version = "0.76.0" }
diff --git a/crates/lightbeam/wasmtime/src/lib.rs b/crates/lightbeam/wasmtime/src/lib.rs
index 338ccab118..3b98f719ac 100644
--- a/crates/lightbeam/wasmtime/src/lib.rs
+++ b/crates/lightbeam/wasmtime/src/lib.rs
@@ -3,16 +3,19 @@
//! This crates provides an implementation of [`Compiler`] in the form of
//! [`Lightbeam`].
+#![allow(dead_code)]
+
+use anyhow::Result;
use cranelift_codegen::binemit;
use cranelift_codegen::ir::{self, ExternalName};
-use cranelift_codegen::isa;
-use lightbeam::{CodeGenSession, NullOffsetSink, Sinks};
+use std::collections::HashMap;
use wasmtime_environ::wasm::{
DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex,
GlobalIndex, MemoryIndex, TableIndex, TypeIndex, WasmFuncType,
};
use wasmtime_environ::{
- BuiltinFunctionIndex, CompileError, CompiledFunction, Compiler, FunctionBodyData, Module,
+ BuiltinFunctionIndex, CompileError, CompiledFunction, CompiledFunctions, Compiler,
+ DebugInfoData, DwarfSection, FlagValue, FunctionBodyData, Module, ModuleMemoryOffset,
ModuleTranslation, Relocation, RelocationTarget, TrapInformation, Tunables, TypeTables,
VMOffsets,
};
@@ -23,62 +26,61 @@ pub struct Lightbeam;
impl Compiler for Lightbeam {
fn compile_function(
&self,
- translation: &ModuleTranslation,
- i: DefinedFuncIndex,
- function_body: FunctionBodyData<'_>,
- isa: &dyn isa::TargetIsa,
- tunables: &Tunables,
+ _translation: &ModuleTranslation,
+ _i: DefinedFuncIndex,
+ _function_body: FunctionBodyData<'_>,
+ _tunables: &Tunables,
_types: &TypeTables,
) -> Result {
- if tunables.generate_native_debuginfo {
- return Err(CompileError::DebugInfoNotSupported);
- }
- let func_index = translation.module.func_index(i);
+ unimplemented!()
+ // if tunables.generate_native_debuginfo {
+ // return Err(CompileError::DebugInfoNotSupported);
+ // }
+ // let func_index = translation.module.func_index(i);
- let env = FuncEnvironment::new(isa.frontend_config().pointer_bytes(), translation);
- let mut codegen_session: CodeGenSession<_> = CodeGenSession::new(
- translation.function_body_inputs.len() as u32,
- &env,
- lightbeam::microwasm::I32,
- );
+ // let env = FuncEnvironment::new(isa.frontend_config().pointer_bytes(), translation);
+ // let mut codegen_session: CodeGenSession<_> = CodeGenSession::new(
+ // translation.function_body_inputs.len() as u32,
+ // &env,
+ // lightbeam::microwasm::I32,
+ // );
- let mut reloc_sink = RelocSink::new(func_index);
- let mut trap_sink = TrapSink::new();
- lightbeam::translate_function(
- &mut codegen_session,
- Sinks {
- relocs: &mut reloc_sink,
- traps: &mut trap_sink,
- offsets: &mut NullOffsetSink,
- },
- i.as_u32(),
- function_body.body,
- )
- .map_err(|e| CompileError::Codegen(format!("Failed to translate function: {}", e)))?;
+ // let mut reloc_sink = RelocSink::new(func_index);
+ // let mut trap_sink = TrapSink::new();
+ // lightbeam::translate_function(
+ // &mut codegen_session,
+ // Sinks {
+ // relocs: &mut reloc_sink,
+ // traps: &mut trap_sink,
+ // offsets: &mut NullOffsetSink,
+ // },
+ // i.as_u32(),
+ // function_body.body,
+ // )
+ // .map_err(|e| CompileError::Codegen(format!("Failed to translate function: {}", e)))?;
- let code_section = codegen_session
- .into_translated_code_section()
- .map_err(|e| CompileError::Codegen(format!("Failed to generate output code: {}", e)))?;
+ // let code_section = codegen_session
+ // .into_translated_code_section()
+ // .map_err(|e| CompileError::Codegen(format!("Failed to generate output code: {}", e)))?;
- Ok(CompiledFunction {
- // TODO: try to remove copy here (?)
- body: code_section.buffer().to_vec(),
- traps: trap_sink.traps,
- relocations: reloc_sink.func_relocs,
+ // Ok(CompiledFunction {
+ // // TODO: try to remove copy here (?)
+ // body: code_section.buffer().to_vec(),
+ // traps: trap_sink.traps,
+ // relocations: reloc_sink.func_relocs,
- // not implemented for lightbeam currently
- unwind_info: None,
- stack_maps: Default::default(),
- stack_slots: Default::default(),
- value_labels_ranges: Default::default(),
- address_map: Default::default(),
- jt_offsets: Default::default(),
- })
+ // // not implemented for lightbeam currently
+ // unwind_info: None,
+ // stack_maps: Default::default(),
+ // stack_slots: Default::default(),
+ // value_labels_ranges: Default::default(),
+ // address_map: Default::default(),
+ // jt_offsets: Default::default(),
+ // })
}
fn host_to_wasm_trampoline(
&self,
- _isa: &dyn isa::TargetIsa,
_ty: &WasmFuncType,
) -> Result {
unimplemented!()
@@ -86,12 +88,36 @@ impl Compiler for Lightbeam {
fn wasm_to_host_trampoline(
&self,
- _isa: &dyn isa::TargetIsa,
_ty: &WasmFuncType,
_host_fn: usize,
) -> Result {
unimplemented!()
}
+
+ fn emit_dwarf(
+ &self,
+ _debuginfo_data: &DebugInfoData,
+ _funcs: &CompiledFunctions,
+ _memory_offset: &crate::ModuleMemoryOffset,
+ ) -> Result> {
+ unimplemented!()
+ }
+
+ fn triple(&self) -> &target_lexicon::Triple {
+ unimplemented!()
+ }
+
+ fn create_systemv_cie(&self) -> Option {
+ unimplemented!()
+ }
+
+ fn flags(&self) -> HashMap {
+ unimplemented!()
+ }
+
+ fn isa_flags(&self) -> HashMap {
+ unimplemented!()
+ }
}
/// Implementation of a relocation sink that just saves all the information for later
@@ -229,8 +255,9 @@ impl lightbeam::ModuleContext for FuncEnvironment<'_> {
.map(DefinedGlobalIndex::as_u32)
}
- fn global_type(&self, global_index: u32) -> &Self::GlobalType {
- &self.module.globals[GlobalIndex::from_u32(global_index)].ty
+ fn global_type(&self, _global_index: u32) -> &Self::GlobalType {
+ unimplemented!()
+ // &self.module.globals[GlobalIndex::from_u32(global_index)].ty
}
fn func_type_index(&self, func_idx: u32) -> u32 {
diff --git a/crates/obj/Cargo.toml b/crates/obj/Cargo.toml
index 7d2bc21b96..5e7a4f4975 100644
--- a/crates/obj/Cargo.toml
+++ b/crates/obj/Cargo.toml
@@ -15,7 +15,6 @@ wasmtime-environ = { path = "../environ", version = "0.29.0" }
object = { version = "0.26.0", default-features = false, features = ["write"] }
more-asserts = "0.2.1"
target-lexicon = { version = "0.12.0", default-features = false }
-wasmtime-debug = { path = "../debug", version = "0.29.0" }
[badges]
maintenance = { status = "experimental" }
diff --git a/crates/obj/src/builder.rs b/crates/obj/src/builder.rs
index f606d72078..becc8264eb 100644
--- a/crates/obj/src/builder.rs
+++ b/crates/obj/src/builder.rs
@@ -26,12 +26,14 @@ use object::{
};
use std::collections::HashMap;
use target_lexicon::Triple;
-use wasmtime_debug::{DwarfSection, DwarfSectionRelocTarget};
use wasmtime_environ::entity::{EntityRef, PrimaryMap};
use wasmtime_environ::ir::{LibCall, Reloc};
use wasmtime_environ::isa::unwind::UnwindInfo;
use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex, SignatureIndex};
-use wasmtime_environ::{CompiledFunction, CompiledFunctions, Module, Relocation, RelocationTarget};
+use wasmtime_environ::{
+ CompiledFunction, CompiledFunctions, DwarfSection, DwarfSectionRelocTarget, Module, Relocation,
+ RelocationTarget,
+};
fn to_object_relocations<'a>(
it: impl Iterator
- + 'a,
diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs
index 07311abb65..1324b771fa 100644
--- a/crates/runtime/src/instance.rs
+++ b/crates/runtime/src/instance.rs
@@ -5,7 +5,7 @@
use crate::export::Export;
use crate::externref::VMExternRefActivationsTable;
use crate::memory::{Memory, RuntimeMemoryCreator};
-use crate::table::{Table, TableElement};
+use crate::table::{Table, TableElement, TableElementType};
use crate::traphandlers::Trap;
use crate::vmcontext::{
VMCallerCheckedAnyfunc, VMContext, VMFunctionImport, VMGlobalDefinition, VMGlobalImport,
@@ -25,7 +25,7 @@ use std::{mem, ptr, slice};
use wasmtime_environ::entity::{packed_option::ReservedValue, EntityRef, EntitySet, PrimaryMap};
use wasmtime_environ::wasm::{
DataIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, ElemIndex, EntityIndex,
- FuncIndex, GlobalIndex, MemoryIndex, TableElementType, TableIndex, WasmType,
+ FuncIndex, GlobalIndex, MemoryIndex, TableIndex, WasmType,
};
use wasmtime_environ::{ir, HostPtr, Module, VMOffsets};
@@ -590,7 +590,7 @@ impl Instance {
)?;
},
- TableElementType::Val(_) => {
+ TableElementType::Extern => {
debug_assert!(elements.iter().all(|e| *e == FuncIndex::reserved_value()));
table.fill(dst, TableElement::ExternRef(None), len)?;
}
diff --git a/crates/runtime/src/instance/allocator/pooling.rs b/crates/runtime/src/instance/allocator/pooling.rs
index a886ea1c06..9a792bfbad 100644
--- a/crates/runtime/src/instance/allocator/pooling.rs
+++ b/crates/runtime/src/instance/allocator/pooling.rs
@@ -1051,8 +1051,7 @@ mod test {
use crate::{Imports, VMSharedSignatureIndex};
use wasmtime_environ::{
entity::EntityRef,
- ir::Type,
- wasm::{Global, GlobalInit, Memory, SignatureIndex, Table, TableElementType, WasmType},
+ wasm::{Global, GlobalInit, Memory, SignatureIndex, Table, WasmType},
MemoryPlan, ModuleType, TablePlan, TableStyle,
};
@@ -1088,7 +1087,6 @@ mod test {
style: TableStyle::CallerChecksSignature,
table: Table {
wasm_ty: WasmType::FuncRef,
- ty: TableElementType::Func,
minimum: 0,
maximum: None,
},
@@ -1144,7 +1142,6 @@ mod test {
module.globals.push(Global {
wasm_ty: WasmType::I32,
- ty: Type::int(32).unwrap(),
mutability: false,
initializer: GlobalInit::I32Const(0),
});
@@ -1208,7 +1205,6 @@ mod test {
style: TableStyle::CallerChecksSignature,
table: Table {
wasm_ty: WasmType::FuncRef,
- ty: TableElementType::Func,
minimum: 0,
maximum: None,
},
@@ -1258,7 +1254,6 @@ mod test {
module.globals.push(Global {
wasm_ty: WasmType::I32,
- ty: Type::int(32).unwrap(),
mutability: false,
initializer: GlobalInit::I32Const(0),
});
@@ -1281,7 +1276,6 @@ mod test {
style: TableStyle::CallerChecksSignature,
table: Table {
wasm_ty: WasmType::FuncRef,
- ty: TableElementType::Func,
minimum: 11,
maximum: None,
},
diff --git a/crates/runtime/src/libcalls.rs b/crates/runtime/src/libcalls.rs
index b5cf8874f7..17ee935faf 100644
--- a/crates/runtime/src/libcalls.rs
+++ b/crates/runtime/src/libcalls.rs
@@ -58,16 +58,14 @@
use crate::externref::VMExternRef;
use crate::instance::Instance;
-use crate::table::Table;
+use crate::table::{Table, TableElementType};
use crate::traphandlers::{raise_lib_trap, Trap};
use crate::vmcontext::{VMCallerCheckedAnyfunc, VMContext};
use backtrace::Backtrace;
use std::mem;
use std::ptr::{self, NonNull};
use wasmtime_environ::ir::TrapCode;
-use wasmtime_environ::wasm::{
- DataIndex, ElemIndex, GlobalIndex, MemoryIndex, TableElementType, TableIndex,
-};
+use wasmtime_environ::wasm::{DataIndex, ElemIndex, GlobalIndex, MemoryIndex, TableIndex};
const TOINT_32: f32 = 1.0 / f32::EPSILON;
const TOINT_64: f64 = 1.0 / f64::EPSILON;
@@ -214,9 +212,7 @@ pub unsafe extern "C" fn wasmtime_table_grow(
let table_index = TableIndex::from_u32(table_index);
let element = match instance.table_element_type(table_index) {
TableElementType::Func => (init_value as *mut VMCallerCheckedAnyfunc).into(),
- TableElementType::Val(ty) => {
- debug_assert_eq!(ty, crate::ref_type());
-
+ TableElementType::Extern => {
let init_value = if init_value.is_null() {
None
} else {
@@ -249,8 +245,7 @@ pub unsafe extern "C" fn wasmtime_table_fill(
let val = val as *mut VMCallerCheckedAnyfunc;
table.fill(dst, val.into(), len)
}
- TableElementType::Val(ty) => {
- debug_assert_eq!(ty, crate::ref_type());
+ TableElementType::Extern => {
let val = if val.is_null() {
None
} else {
diff --git a/crates/runtime/src/table.rs b/crates/runtime/src/table.rs
index 6045af8301..d2dbcc5c56 100644
--- a/crates/runtime/src/table.rs
+++ b/crates/runtime/src/table.rs
@@ -8,7 +8,7 @@ use anyhow::{bail, Result};
use std::convert::{TryFrom, TryInto};
use std::ops::Range;
use std::ptr;
-use wasmtime_environ::wasm::TableElementType;
+use wasmtime_environ::wasm::WasmType;
use wasmtime_environ::{ir, TablePlan};
/// An element going into or coming out of a table.
@@ -22,6 +22,12 @@ pub enum TableElement {
ExternRef(Option),
}
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub enum TableElementType {
+ Func,
+ Extern,
+}
+
// The usage of `*mut VMCallerCheckedAnyfunc` is safe w.r.t. thread safety, this
// just relies on thread-safety of `VMExternRef` itself.
unsafe impl Send for TableElement where VMExternRef: Send {}
@@ -38,7 +44,7 @@ impl TableElement {
unsafe fn from_raw(ty: TableElementType, ptr: usize) -> Self {
match ty {
TableElementType::Func => Self::FuncRef(ptr as _),
- TableElementType::Val(_) => Self::ExternRef(if ptr == 0 {
+ TableElementType::Extern => Self::ExternRef(if ptr == 0 {
None
} else {
Some(VMExternRef::from_raw(ptr as *mut u8))
@@ -54,7 +60,7 @@ impl TableElement {
unsafe fn clone_from_raw(ty: TableElementType, ptr: usize) -> Self {
match ty {
TableElementType::Func => Self::FuncRef(ptr as _),
- TableElementType::Val(_) => Self::ExternRef(if ptr == 0 {
+ TableElementType::Extern => Self::ExternRef(if ptr == 0 {
None
} else {
Some(VMExternRef::clone_from_raw(ptr as *mut u8))
@@ -122,6 +128,14 @@ pub enum Table {
},
}
+fn wasm_to_table_type(ty: WasmType) -> Result {
+ match ty {
+ WasmType::FuncRef => Ok(TableElementType::Func),
+ WasmType::ExternRef => Ok(TableElementType::Extern),
+ ty => bail!("invalid table element type {:?}", ty),
+ }
+}
+
impl Table {
/// Create a new dynamic (movable) table instance for the specified table plan.
pub fn new_dynamic(
@@ -130,7 +144,7 @@ impl Table {
) -> Result {
Self::limit_new(plan, limiter)?;
let elements = vec![0; plan.table.minimum as usize];
- let ty = plan.table.ty.clone();
+ let ty = wasm_to_table_type(plan.table.wasm_ty)?;
let maximum = plan.table.maximum;
Ok(Table::Dynamic {
@@ -148,7 +162,7 @@ impl Table {
) -> Result {
Self::limit_new(plan, limiter)?;
let size = plan.table.minimum;
- let ty = plan.table.ty.clone();
+ let ty = wasm_to_table_type(plan.table.wasm_ty)?;
let data = match plan.table.maximum {
Some(max) if (max as usize) < data.len() => &mut data[..max as usize],
_ => data,
@@ -403,7 +417,7 @@ impl Table {
fn type_matches(&self, val: &TableElement) -> bool {
match (&val, self.element_type()) {
(TableElement::FuncRef(_), TableElementType::Func) => true,
- (TableElement::ExternRef(_), TableElementType::Val(_)) => true,
+ (TableElement::ExternRef(_), TableElementType::Extern) => true,
_ => false,
}
}
@@ -449,7 +463,7 @@ impl Table {
dst_table.elements_mut()[dst_range]
.copy_from_slice(&src_table.elements()[src_range]);
}
- TableElementType::Val(_) => {
+ TableElementType::Extern => {
// We need to clone each `externref`
let dst = dst_table.elements_mut();
let src = src_table.elements();
@@ -469,7 +483,7 @@ impl Table {
// `funcref` are `Copy`, so just do a memmove
dst.copy_within(src_range, dst_range.start);
}
- TableElementType::Val(_) => {
+ TableElementType::Extern => {
// We need to clone each `externref` while handling overlapping
// ranges
if dst_range.start <= src_range.start {
diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml
index 8bf29e6a2f..4c0ee28516 100644
--- a/crates/wasmtime/Cargo.toml
+++ b/crates/wasmtime/Cargo.toml
@@ -13,7 +13,6 @@ edition = "2018"
rustdoc-args = ["--cfg", "nightlydoc"]
[dependencies]
-cranelift-native = { path = '../../cranelift/native', version = '0.76.0' }
wasmtime-runtime = { path = "../runtime", version = "0.29.0" }
wasmtime-environ = { path = "../environ", version = "0.29.0" }
wasmtime-jit = { path = "../jit", version = "0.29.0" }
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index 5eb4b46c93..ca28bda5a9 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -10,8 +10,7 @@ use std::sync::Arc;
use wasmparser::WasmFeatures;
#[cfg(feature = "cache")]
use wasmtime_cache::CacheConfig;
-use wasmtime_environ::settings::{self, Configurable, SetError};
-use wasmtime_environ::{isa, isa::TargetIsa, Tunables};
+use wasmtime_environ::{CompilerBuilder, Tunables};
use wasmtime_jit::{CompilationStrategy, Compiler};
use wasmtime_profiling::{JitDumpAgent, NullProfilerAgent, ProfilingAgent, VTuneAgent};
use wasmtime_runtime::{
@@ -337,12 +336,9 @@ impl Default for InstanceAllocationStrategy {
///
/// This structure exposed a builder-like interface and is primarily consumed by
/// [`Engine::new()`](crate::Engine::new)
-#[derive(Clone)]
pub struct Config {
- pub(crate) flags: settings::Builder,
- pub(crate) isa_flags: isa::Builder,
+ pub(crate) compiler: Box,
pub(crate) tunables: Tunables,
- pub(crate) strategy: CompilationStrategy,
#[cfg(feature = "cache")]
pub(crate) cache_config: CacheConfig,
pub(crate) profiler: Arc,
@@ -362,24 +358,9 @@ impl Config {
/// Creates a new configuration object with the default configuration
/// specified.
pub fn new() -> Self {
- let mut flags = settings::builder();
-
- // There are two possible traps for division, and this way
- // we get the proper one if code traps.
- flags
- .enable("avoid_div_traps")
- .expect("should be valid flag");
-
- // We don't use probestack as a stack limit mechanism
- flags
- .set("enable_probestack", "false")
- .expect("should be valid flag");
-
let mut ret = Self {
tunables: Tunables::default(),
- flags,
- isa_flags: cranelift_native::builder().expect("host machine is not a supported target"),
- strategy: CompilationStrategy::Auto,
+ compiler: Compiler::builder(CompilationStrategy::Auto),
#[cfg(feature = "cache")]
cache_config: CacheConfig::new_cache_disabled(),
profiler: Arc::new(NullProfilerAgent),
@@ -417,9 +398,8 @@ impl Config {
/// This method will error if the given target triple is not supported.
pub fn target(&mut self, target: &str) -> Result<&mut Self> {
use std::str::FromStr;
- self.isa_flags = wasmtime_environ::isa::lookup(
- target_lexicon::Triple::from_str(target).map_err(|e| anyhow::anyhow!(e))?,
- )?;
+ self.compiler
+ .target(target_lexicon::Triple::from_str(target).map_err(|e| anyhow::anyhow!(e))?)?;
Ok(self)
}
@@ -675,7 +655,7 @@ impl Config {
pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self {
self.features.reference_types = enable;
- self.flags
+ self.compiler
.set("enable_safepoints", if enable { "true" } else { "false" })
.unwrap();
@@ -710,7 +690,7 @@ impl Config {
pub fn wasm_simd(&mut self, enable: bool) -> &mut Self {
self.features.simd = enable;
let val = if enable { "true" } else { "false" };
- self.flags
+ self.compiler
.set("enable_simd", val)
.expect("should be valid flag");
self
@@ -801,7 +781,7 @@ impl Config {
/// itself to be set, but if they're not set and the strategy is specified
/// here then an error will be returned.
pub fn strategy(&mut self, strategy: Strategy) -> Result<&mut Self> {
- self.strategy = match strategy {
+ let strategy = match strategy {
Strategy::Auto => CompilationStrategy::Auto,
Strategy::Cranelift => CompilationStrategy::Cranelift,
#[cfg(feature = "lightbeam")]
@@ -811,6 +791,7 @@ impl Config {
anyhow::bail!("lightbeam compilation strategy wasn't enabled at compile time");
}
};
+ self.compiler = Compiler::builder(strategy);
Ok(self)
}
@@ -837,7 +818,7 @@ impl Config {
/// The default value for this is `false`
pub fn cranelift_debug_verifier(&mut self, enable: bool) -> &mut Self {
let val = if enable { "true" } else { "false" };
- self.flags
+ self.compiler
.set("enable_verifier", val)
.expect("should be valid flag");
self
@@ -856,7 +837,7 @@ impl Config {
OptLevel::Speed => "speed",
OptLevel::SpeedAndSize => "speed_and_size",
};
- self.flags
+ self.compiler
.set("opt_level", val)
.expect("should be valid flag");
self
@@ -872,7 +853,7 @@ impl Config {
/// The default value for this is `false`
pub fn cranelift_nan_canonicalization(&mut self, enable: bool) -> &mut Self {
let val = if enable { "true" } else { "false" };
- self.flags
+ self.compiler
.set("enable_nan_canonicalization", val)
.expect("should be valid flag");
self
@@ -893,15 +874,7 @@ impl Config {
///
/// This method can fail if the flag's name does not exist.
pub unsafe fn cranelift_flag_enable(&mut self, flag: &str) -> Result<&mut Self> {
- if let Err(err) = self.flags.enable(flag) {
- match err {
- SetError::BadName(_) => {
- // Try the target-specific flags.
- self.isa_flags.enable(flag)?;
- }
- _ => bail!(err),
- }
- }
+ self.compiler.enable(flag)?;
Ok(self)
}
@@ -919,15 +892,7 @@ impl Config {
/// This method can fail if the flag's name does not exist, or the value is not appropriate for
/// the flag type.
pub unsafe fn cranelift_flag_set(&mut self, name: &str, value: &str) -> Result<&mut Self> {
- if let Err(err) = self.flags.set(name, value) {
- match err {
- SetError::BadName(_) => {
- // Try the target-specific flags.
- self.isa_flags.set(name, value)?;
- }
- _ => bail!(err),
- }
- }
+ self.compiler.set(name, value)?;
Ok(self)
}
@@ -1238,25 +1203,11 @@ impl Config {
self
}
- pub(crate) fn target_isa(&self) -> Box {
- self.isa_flags
- .clone()
- .finish(settings::Flags::new(self.flags.clone()))
- }
-
- pub(crate) fn target_isa_with_reference_types(&self) -> Box {
- let mut flags = self.flags.clone();
- flags.set("enable_safepoints", "true").unwrap();
- self.isa_flags.clone().finish(settings::Flags::new(flags))
- }
-
pub(crate) fn build_compiler(&self, allocator: &dyn InstanceAllocator) -> Compiler {
- let isa = self.target_isa();
let mut tunables = self.tunables.clone();
allocator.adjust_tunables(&mut tunables);
Compiler::new(
- isa,
- self.strategy,
+ &*self.compiler,
tunables,
self.features,
self.parallel_compilation,
@@ -1304,12 +1255,33 @@ impl Default for Config {
}
}
+impl Clone for Config {
+ fn clone(&self) -> Config {
+ Config {
+ compiler: self.compiler.clone(),
+ tunables: self.tunables.clone(),
+ #[cfg(feature = "cache")]
+ cache_config: self.cache_config.clone(),
+ profiler: self.profiler.clone(),
+ features: self.features.clone(),
+ mem_creator: self.mem_creator.clone(),
+ allocation_strategy: self.allocation_strategy.clone(),
+ max_wasm_stack: self.max_wasm_stack,
+ wasm_backtrace_details_env_used: self.wasm_backtrace_details_env_used,
+ async_support: self.async_support,
+ #[cfg(feature = "async")]
+ async_stack_size: self.async_stack_size,
+ deserialize_check_wasmtime_version: self.deserialize_check_wasmtime_version,
+ parallel_compilation: self.parallel_compilation,
+ }
+ }
+}
+
impl fmt::Debug for Config {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Config")
.field("debug_info", &self.tunables.generate_native_debuginfo)
.field("parse_wasm_debuginfo", &self.tunables.parse_wasm_debuginfo)
- .field("strategy", &self.strategy)
.field("wasm_threads", &self.features.threads)
.field("wasm_reference_types", &self.features.reference_types)
.field("wasm_bulk_memory", &self.features.bulk_memory)
@@ -1333,10 +1305,8 @@ impl fmt::Debug for Config {
"guard_before_linear_memory",
&self.tunables.guard_before_linear_memory,
)
- .field(
- "flags",
- &settings::Flags::new(self.flags.clone()).to_string(),
- )
+ .field("parallel_compilation", &self.parallel_compilation)
+ .field("compiler", &self.compiler)
.finish()
}
}
diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs
index d6b3b6a7ba..a862d84244 100644
--- a/crates/wasmtime/src/module.rs
+++ b/crates/wasmtime/src/module.rs
@@ -279,7 +279,7 @@ impl Module {
/// ```
pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result {
// Check to see that the config's target matches the host
- let target = engine.config().isa_flags.triple();
+ let target = engine.compiler().compiler().triple();
if *target != target_lexicon::Triple::host() {
bail!(
"target '{}' specified in the configuration does not match the host",
@@ -318,9 +318,8 @@ impl Module {
let modules = CompiledModule::from_artifacts_list(
artifacts,
- engine.compiler().isa(),
- &*engine.config().profiler,
engine.compiler(),
+ &*engine.config().profiler,
)?;
Self::from_parts(engine, modules, main_module, Arc::new(types), &[])
diff --git a/crates/wasmtime/src/module/serialization.rs b/crates/wasmtime/src/module/serialization.rs
index 2918d7ffc6..507af1fdba 100644
--- a/crates/wasmtime/src/module/serialization.rs
+++ b/crates/wasmtime/src/module/serialization.rs
@@ -1,18 +1,14 @@
//! Implements module serialization.
-use crate::{Engine, Module, OptLevel};
+use crate::{Engine, Module};
use anyhow::{anyhow, bail, Context, Result};
use bincode::Options;
use serde::{Deserialize, Serialize};
-use std::borrow::Cow;
-use std::fmt;
+use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
-use std::{collections::HashMap, fmt::Display};
-use wasmtime_environ::{isa::TargetIsa, settings, Tunables};
-use wasmtime_jit::{
- CompilationArtifacts, CompilationStrategy, CompiledModule, Compiler, TypeTables,
-};
+use wasmtime_environ::{FlagValue, Tunables};
+use wasmtime_jit::{CompilationArtifacts, CompiledModule, Compiler, TypeTables};
const HEADER: &[u8] = b"\0wasmtime-aot";
@@ -111,16 +107,6 @@ impl<'a, 'b, T: Deserialize<'a>> Deserialize<'a> for MyCow<'b, T> {
}
}
-impl From for OptLevel {
- fn from(level: settings::OptLevel) -> Self {
- match level {
- settings::OptLevel::Speed => OptLevel::Speed,
- settings::OptLevel::SpeedAndSize => OptLevel::SpeedAndSize,
- settings::OptLevel::None => OptLevel::None,
- }
- }
-}
-
/// A small helper struct for serialized module upvars.
#[derive(Serialize, Deserialize)]
pub struct SerializedModuleUpvar {
@@ -163,40 +149,11 @@ impl SerializedModuleUpvar {
}
}
-#[derive(Serialize, Deserialize, Eq, PartialEq)]
-enum FlagValue {
- Enum(Cow<'static, str>),
- Num(u8),
- Bool(bool),
-}
-
-impl From for FlagValue {
- fn from(v: settings::Value) -> Self {
- match v.kind() {
- settings::SettingKind::Enum => Self::Enum(v.as_enum().unwrap().into()),
- settings::SettingKind::Num => Self::Num(v.as_num().unwrap()),
- settings::SettingKind::Bool => Self::Bool(v.as_bool().unwrap()),
- settings::SettingKind::Preset => unreachable!(),
- }
- }
-}
-
-impl Display for FlagValue {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::Enum(v) => v.fmt(f),
- Self::Num(v) => v.fmt(f),
- Self::Bool(v) => v.fmt(f),
- }
- }
-}
-
#[derive(Serialize, Deserialize)]
pub struct SerializedModule<'a> {
target: String,
shared_flags: HashMap,
isa_flags: HashMap,
- strategy: CompilationStrategy,
tunables: Tunables,
features: WasmFeatures,
artifacts: Vec>,
@@ -250,21 +207,10 @@ impl<'a> SerializedModule<'a> {
module_upvars: Vec,
types: MyCow<'a, TypeTables>,
) -> Self {
- let isa = compiler.isa();
-
Self {
- target: isa.triple().to_string(),
- shared_flags: isa
- .flags()
- .iter()
- .map(|v| (v.name.to_owned(), v.into()))
- .collect(),
- isa_flags: isa
- .isa_flags()
- .into_iter()
- .map(|v| (v.name.to_owned(), v.into()))
- .collect(),
- strategy: compiler.strategy(),
+ target: compiler.triple().to_string(),
+ shared_flags: compiler.compiler().flags(),
+ isa_flags: compiler.compiler().isa_flags(),
tunables: compiler.tunables().clone(),
features: compiler.features().into(),
artifacts,
@@ -275,12 +221,10 @@ impl<'a> SerializedModule<'a> {
pub fn into_module(mut self, engine: &Engine) -> Result {
let compiler = engine.compiler();
- let isa = compiler.isa();
- self.check_triple(isa)?;
- self.check_shared_flags(isa)?;
- self.check_isa_flags(isa)?;
- self.check_strategy(compiler)?;
+ self.check_triple(compiler)?;
+ self.check_shared_flags(compiler)?;
+ self.check_isa_flags(compiler)?;
self.check_tunables(compiler)?;
self.check_features(compiler)?;
@@ -289,9 +233,8 @@ impl<'a> SerializedModule<'a> {
.into_iter()
.map(|i| i.unwrap_owned())
.collect(),
- engine.compiler().isa(),
- &*engine.config().profiler,
engine.compiler(),
+ &*engine.config().profiler,
)?;
assert!(!modules.is_empty());
@@ -361,17 +304,17 @@ impl<'a> SerializedModule<'a> {
.context("deserialize compilation artifacts")?)
}
- fn check_triple(&self, isa: &dyn TargetIsa) -> Result<()> {
+ fn check_triple(&self, compiler: &Compiler) -> Result<()> {
let triple = target_lexicon::Triple::from_str(&self.target).map_err(|e| anyhow!(e))?;
- if triple.architecture != isa.triple().architecture {
+ if triple.architecture != compiler.triple().architecture {
bail!(
"Module was compiled for architecture '{}'",
triple.architecture
);
}
- if triple.operating_system != isa.triple().operating_system {
+ if triple.operating_system != compiler.triple().operating_system {
bail!(
"Module was compiled for operating system '{}'",
triple.operating_system
@@ -381,13 +324,11 @@ impl<'a> SerializedModule<'a> {
Ok(())
}
- fn check_shared_flags(&mut self, isa: &dyn TargetIsa) -> Result<()> {
+ fn check_shared_flags(&mut self, compiler: &Compiler) -> Result<()> {
let mut shared_flags = std::mem::take(&mut self.shared_flags);
- for value in isa.flags().iter() {
- let name = value.name;
- match shared_flags.remove(name) {
+ for (name, host) in compiler.compiler().flags() {
+ match shared_flags.remove(&name) {
Some(v) => {
- let host: FlagValue = value.into();
if v != host {
bail!("Module was compiled with a different '{}' setting: expected '{}' but host has '{}'", name, v, host);
}
@@ -406,12 +347,10 @@ impl<'a> SerializedModule<'a> {
Ok(())
}
- fn check_isa_flags(&mut self, isa: &dyn TargetIsa) -> Result<()> {
+ fn check_isa_flags(&mut self, compiler: &Compiler) -> Result<()> {
let mut isa_flags = std::mem::take(&mut self.isa_flags);
- for value in isa.isa_flags().into_iter() {
- let name = value.name;
- let host: FlagValue = value.into();
- match isa_flags.remove(name) {
+ for (name, host) in compiler.compiler().isa_flags() {
+ match isa_flags.remove(&name) {
Some(v) => match (&v, &host) {
(FlagValue::Bool(v), FlagValue::Bool(host)) => {
// ISA flags represent CPU features; for boolean values, only
@@ -441,25 +380,6 @@ impl<'a> SerializedModule<'a> {
Ok(())
}
- fn check_strategy(&self, compiler: &Compiler) -> Result<()> {
- #[allow(unreachable_patterns)]
- let matches = match (self.strategy, compiler.strategy()) {
- (CompilationStrategy::Auto, CompilationStrategy::Auto)
- | (CompilationStrategy::Auto, CompilationStrategy::Cranelift)
- | (CompilationStrategy::Cranelift, CompilationStrategy::Auto)
- | (CompilationStrategy::Cranelift, CompilationStrategy::Cranelift) => true,
- #[cfg(feature = "lightbeam")]
- (CompilationStrategy::Lightbeam, CompilationStrategy::Lightbeam) => true,
- _ => false,
- };
-
- if !matches {
- bail!("Module was compiled with strategy '{:?}'", self.strategy);
- }
-
- Ok(())
- }
-
fn check_int(found: T, expected: T, feature: &str) -> Result<()> {
if found == expected {
return Ok(());
@@ -610,6 +530,7 @@ impl<'a> SerializedModule<'a> {
mod test {
use super::*;
use crate::Config;
+ use std::borrow::Cow;
#[test]
fn test_architecture_mismatch() -> Result<()> {
diff --git a/crates/wasmtime/src/trampoline/func.rs b/crates/wasmtime/src/trampoline/func.rs
index dbe3299b07..e47f83d04b 100644
--- a/crates/wasmtime/src/trampoline/func.rs
+++ b/crates/wasmtime/src/trampoline/func.rs
@@ -77,19 +77,14 @@ pub fn create_function(
func: Box Result<(), Trap> + Send + Sync>,
engine: &Engine,
) -> Result<(InstanceHandle, VMTrampoline)> {
- // Note that we specifically enable reference types here in our ISA because
- // `Func::new` is intended to be infallible, but our signature may use
- // reference types which requires safepoints.
- let isa = &*engine.config().target_isa_with_reference_types();
- let wasm_trampoline = engine.compiler().compiler().wasm_to_host_trampoline(
- isa,
- ft.as_wasm_func_type(),
- stub_fn as usize,
- )?;
+ let wasm_trampoline = engine
+ .compiler()
+ .compiler()
+ .wasm_to_host_trampoline(ft.as_wasm_func_type(), stub_fn as usize)?;
let host_trampoline = engine
.compiler()
.compiler()
- .host_to_wasm_trampoline(isa, ft.as_wasm_func_type())?;
+ .host_to_wasm_trampoline(ft.as_wasm_func_type())?;
let mut code_memory = CodeMemory::new();
let host_trampoline = code_memory
@@ -98,7 +93,7 @@ pub fn create_function(
let wasm_trampoline =
code_memory.allocate_for_function(&wasm_trampoline)? as *mut [VMFunctionBody];
- code_memory.publish(isa);
+ code_memory.publish(engine.compiler());
let sig = engine.signatures().register(ft.as_wasm_func_type());
diff --git a/crates/wasmtime/src/trampoline/global.rs b/crates/wasmtime/src/trampoline/global.rs
index 20529de888..1f6080e229 100644
--- a/crates/wasmtime/src/trampoline/global.rs
+++ b/crates/wasmtime/src/trampoline/global.rs
@@ -17,7 +17,6 @@ pub fn create_global(store: &mut StoreOpaque<'_>, gt: &GlobalType, val: Val) ->
let global = wasm::Global {
wasm_ty: gt.content().to_wasm_type(),
- ty: gt.content().get_wasmtime_type(),
mutability: match gt.mutability() {
Mutability::Const => false,
Mutability::Var => true,
diff --git a/crates/wasmtime/src/types.rs b/crates/wasmtime/src/types.rs
index 9c46930342..03d96109e2 100644
--- a/crates/wasmtime/src/types.rs
+++ b/crates/wasmtime/src/types.rs
@@ -1,6 +1,6 @@
use std::fmt;
+use wasmtime_environ::wasm;
use wasmtime_environ::wasm::{EntityType, WasmFuncType};
-use wasmtime_environ::{ir, wasm};
use wasmtime_jit::TypeTables;
pub(crate) mod matching;
@@ -71,18 +71,6 @@ impl ValType {
}
}
- pub(crate) fn get_wasmtime_type(&self) -> ir::Type {
- match self {
- ValType::I32 => ir::types::I32,
- ValType::I64 => ir::types::I64,
- ValType::F32 => ir::types::F32,
- ValType::F64 => ir::types::F64,
- ValType::V128 => ir::types::I8X16,
- ValType::ExternRef => wasmtime_runtime::ref_type(),
- ValType::FuncRef => wasmtime_runtime::pointer_type(),
- }
- }
-
pub(crate) fn to_wasm_type(&self) -> wasm::WasmType {
match self {
Self::I32 => wasm::WasmType::I32,
@@ -334,10 +322,6 @@ impl TableType {
pub fn new(element: ValType, min: u32, max: Option) -> TableType {
TableType {
ty: wasm::Table {
- ty: match element {
- ValType::FuncRef => wasm::TableElementType::Func,
- _ => wasm::TableElementType::Val(element.get_wasmtime_type()),
- },
wasm_ty: element.to_wasm_type(),
minimum: min,
maximum: max,
diff --git a/crates/wasmtime/src/types/matching.rs b/crates/wasmtime/src/types/matching.rs
index 851cff3fba..2fcc114f48 100644
--- a/crates/wasmtime/src/types/matching.rs
+++ b/crates/wasmtime/src/types/matching.rs
@@ -22,10 +22,7 @@ impl MatchCx<'_> {
}
fn global_ty(&self, expected: &Global, actual: &Global) -> Result<()> {
- if expected.ty == actual.ty
- && expected.wasm_ty == actual.wasm_ty
- && expected.mutability == actual.mutability
- {
+ if expected.wasm_ty == actual.wasm_ty && expected.mutability == actual.mutability {
Ok(())
} else {
bail!("global types incompatible")
@@ -38,7 +35,6 @@ impl MatchCx<'_> {
fn table_ty(&self, expected: &Table, actual: &Table) -> Result<()> {
if expected.wasm_ty == actual.wasm_ty
- && expected.ty == actual.ty
&& expected.minimum <= actual.minimum
&& match expected.maximum {
Some(expected) => match actual.maximum {
diff --git a/src/commands/settings.rs b/src/commands/settings.rs
index 28d53ac6f8..b063569029 100644
--- a/src/commands/settings.rs
+++ b/src/commands/settings.rs
@@ -1,9 +1,11 @@
//! The module that implements the `wasmtime settings` command.
use anyhow::{anyhow, Result};
+use std::collections::BTreeMap;
use std::str::FromStr;
use structopt::StructOpt;
-use wasmtime_environ::settings::{self, Setting, SettingKind};
+use wasmtime_environ::{FlagValue, Setting, SettingKind};
+use wasmtime_jit::Compiler;
/// Displays available Cranelift settings for a target.
#[derive(StructOpt)]
@@ -17,19 +19,18 @@ pub struct SettingsCommand {
impl SettingsCommand {
/// Executes the command.
pub fn execute(self) -> Result<()> {
- let settings = match &self.target {
- Some(target) => wasmtime_environ::isa::lookup(
- target_lexicon::Triple::from_str(target).map_err(|e| anyhow!(e))?,
- )?,
- None => cranelift_native::builder().unwrap(),
- };
+ let mut builder = Compiler::builder(wasmtime_jit::CompilationStrategy::Auto);
+ if let Some(target) = &self.target {
+ let target = target_lexicon::Triple::from_str(target).map_err(|e| anyhow!(e))?;
+ builder.target(target)?;
+ }
let mut enums = (Vec::new(), 0, "Enum settings:");
let mut nums = (Vec::new(), 0, "Numerical settings:");
let mut bools = (Vec::new(), 0, "Boolean settings:");
let mut presets = (Vec::new(), 0, "Presets:");
- for setting in settings.iter() {
+ for setting in builder.settings() {
let (collection, max, _) = match setting.kind {
SettingKind::Enum => &mut enums,
SettingKind::Num => &mut nums,
@@ -45,11 +46,11 @@ impl SettingsCommand {
}
if enums.0.is_empty() && nums.0.is_empty() && bools.0.is_empty() && presets.0.is_empty() {
- println!("Target '{}' has no settings.", settings.triple());
+ println!("Target '{}' has no settings.", builder.triple());
return Ok(());
}
- println!("Cranelift settings for target '{}':", settings.triple());
+ println!("Cranelift settings for target '{}':", builder.triple());
for (collection, max, header) in &mut [enums, nums, bools, presets] {
if collection.is_empty() {
@@ -62,16 +63,15 @@ impl SettingsCommand {
}
if self.target.is_none() {
- let isa = settings.finish(settings::Flags::new(settings::builder()));
+ let compiler = builder.build();
println!();
println!("Settings inferred for the current host:");
- let mut values = isa.isa_flags();
- values.sort_by_key(|k| k.name);
+ let values = compiler.isa_flags().into_iter().collect::>();
- for value in values {
- if value.as_bool().unwrap_or(false) {
- println!(" {}", value.name);
+ for (name, value) in values {
+ if let FlagValue::Bool(true) = value {
+ println!(" {}", name);
}
}
}
diff --git a/src/lib.rs b/src/lib.rs
index b8630d5a13..6403270ce2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -304,10 +304,10 @@ impl CommonOptions {
}
config
+ .strategy(pick_compilation_strategy(self.cranelift, self.lightbeam)?)?
.cranelift_debug_verifier(self.enable_cranelift_debug_verifier)
.debug_info(self.debug_info)
.cranelift_opt_level(self.opt_level())
- .strategy(pick_compilation_strategy(self.cranelift, self.lightbeam)?)?
.profiler(pick_profiling_strategy(self.jitdump, self.vtune)?)?
.cranelift_nan_canonicalization(self.enable_cranelift_nan_canonicalization);
diff --git a/src/obj.rs b/src/obj.rs
index 0166c6c580..3ef7498dc2 100644
--- a/src/obj.rs
+++ b/src/obj.rs
@@ -3,7 +3,7 @@ use object::write::Object;
use target_lexicon::Triple;
use wasmparser::WasmFeatures;
use wasmtime::Strategy;
-use wasmtime_environ::{settings, settings::Configurable, ModuleEnvironment, Tunables};
+use wasmtime_environ::{ModuleEnvironment, Tunables};
use wasmtime_jit::Compiler;
/// Creates object file from binary wasm data.
@@ -15,57 +15,44 @@ pub fn compile_to_obj(
opt_level: wasmtime::OptLevel,
debug_info: bool,
) -> Result