Move all trampoline compilation to wasmtime-cranelift (#3176)
* Move all trampoline compilation to `wasmtime-cranelift` This commit moves compilation of all the trampolines used in wasmtime behind the `Compiler` trait object to live in `wasmtime-cranelift`. The long-term goal of this is to enable depending on cranelift *only* from the `wasmtime-cranelift` crate, so by moving these dependencies we should make that a little more flexible. * Fix windows build
This commit is contained in:
@@ -11,11 +11,6 @@ repository = "https://github.com/bytecodealliance/wasmtime"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
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"] }
|
||||
cranelift-native = { path = "../../cranelift/native", version = "0.76.0" }
|
||||
cranelift-frontend = { path = "../../cranelift/frontend", version = "0.76.0" }
|
||||
wasmtime-environ = { path = "../environ", version = "0.29.0" }
|
||||
wasmtime-runtime = { path = "../runtime", version = "0.29.0" }
|
||||
wasmtime-cranelift = { path = "../cranelift", version = "0.29.0" }
|
||||
@@ -45,10 +40,10 @@ lightbeam = ["wasmtime-lightbeam"]
|
||||
jitdump = ["wasmtime-profiling/jitdump"]
|
||||
vtune = ["wasmtime-profiling/vtune"]
|
||||
parallel-compilation = ["rayon"]
|
||||
all-arch = ["cranelift-codegen/all-arch"]
|
||||
all-arch = ["wasmtime-cranelift/all-arch"]
|
||||
|
||||
# Use the old x86 backend.
|
||||
old-x86-backend = ["cranelift-codegen/old-x86-backend"]
|
||||
old-x86-backend = ["wasmtime-cranelift/old-x86-backend"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::object::{
|
||||
ObjectUnwindInfo,
|
||||
};
|
||||
use crate::unwind::UnwindRegistry;
|
||||
use anyhow::{Context, Result};
|
||||
use object::read::{File as ObjectFile, Object, ObjectSection, ObjectSymbol};
|
||||
use region;
|
||||
use std::collections::BTreeMap;
|
||||
@@ -24,8 +25,8 @@ struct CodeMemoryEntry {
|
||||
}
|
||||
|
||||
impl CodeMemoryEntry {
|
||||
fn with_capacity(cap: usize) -> Result<Self, String> {
|
||||
let mmap = ManuallyDrop::new(Mmap::with_at_least(cap).map_err(|e| e.to_string())?);
|
||||
fn with_capacity(cap: usize) -> Result<Self> {
|
||||
let mmap = ManuallyDrop::new(Mmap::with_at_least(cap)?);
|
||||
let registry = ManuallyDrop::new(UnwindRegistry::new(mmap.as_ptr() as usize));
|
||||
Ok(Self {
|
||||
mmap,
|
||||
@@ -119,7 +120,7 @@ impl CodeMemory {
|
||||
pub fn allocate_for_function<'a>(
|
||||
&mut self,
|
||||
func: &'a CompiledFunction,
|
||||
) -> Result<&mut [VMFunctionBody], String> {
|
||||
) -> Result<&mut [VMFunctionBody]> {
|
||||
let size = Self::function_allocation_size(func);
|
||||
|
||||
let (buf, registry, start) = self.allocate(size)?;
|
||||
@@ -167,7 +168,7 @@ impl CodeMemory {
|
||||
/// * The offset within the current mmap that the slice starts at
|
||||
///
|
||||
/// TODO: Add an alignment flag.
|
||||
fn allocate(&mut self, size: usize) -> Result<(&mut [u8], &mut UnwindRegistry, usize), String> {
|
||||
fn allocate(&mut self, size: usize) -> Result<(&mut [u8], &mut UnwindRegistry, usize)> {
|
||||
assert!(size > 0);
|
||||
|
||||
if match &self.current {
|
||||
@@ -249,7 +250,7 @@ impl CodeMemory {
|
||||
}
|
||||
|
||||
/// Pushes the current entry and allocates a new one with the given size.
|
||||
fn push_current(&mut self, new_size: usize) -> Result<(), String> {
|
||||
fn push_current(&mut self, new_size: usize) -> Result<()> {
|
||||
let previous = mem::replace(
|
||||
&mut self.current,
|
||||
if new_size == 0 {
|
||||
@@ -279,7 +280,7 @@ impl CodeMemory {
|
||||
&'a mut self,
|
||||
obj: &ObjectFile,
|
||||
unwind_info: &[ObjectUnwindInfo],
|
||||
) -> Result<CodeMemoryObjectAllocation<'a>, String> {
|
||||
) -> Result<CodeMemoryObjectAllocation<'a>> {
|
||||
let text_section = obj.section_by_name(".text").unwrap();
|
||||
|
||||
if text_section.size() == 0 {
|
||||
@@ -296,7 +297,7 @@ impl CodeMemory {
|
||||
buf.copy_from_slice(
|
||||
text_section
|
||||
.data()
|
||||
.map_err(|_| "cannot read section data".to_string())?,
|
||||
.with_context(|| "cannot read section data")?,
|
||||
);
|
||||
|
||||
// Track locations of all defined functions and trampolines.
|
||||
|
||||
@@ -132,6 +132,11 @@ impl Compiler {
|
||||
&self.features
|
||||
}
|
||||
|
||||
/// Return the underlying compiler in use
|
||||
pub fn compiler(&self) -> &dyn EnvCompiler {
|
||||
&*self.compiler
|
||||
}
|
||||
|
||||
/// Compile the given function bodies.
|
||||
pub fn compile<'data>(
|
||||
&self,
|
||||
@@ -166,8 +171,7 @@ impl Compiler {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let (obj, unwind_info) =
|
||||
build_object(&*self.isa, &translation, types, &funcs, dwarf_sections)?;
|
||||
let (obj, unwind_info) = build_object(self, &translation, types, &funcs, dwarf_sections)?;
|
||||
|
||||
Ok(Compilation {
|
||||
obj,
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::code_memory::CodeMemory;
|
||||
use crate::compiler::{Compilation, Compiler};
|
||||
use crate::link::link_module;
|
||||
use crate::object::ObjectUnwindInfo;
|
||||
use anyhow::{Context, Result};
|
||||
use object::File as ObjectFile;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::Range;
|
||||
@@ -483,16 +484,13 @@ fn build_code_memory(
|
||||
obj: &[u8],
|
||||
module: &Module,
|
||||
unwind_info: &[ObjectUnwindInfo],
|
||||
) -> Result<
|
||||
(
|
||||
CodeMemory,
|
||||
(*const u8, usize),
|
||||
PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
Vec<(SignatureIndex, VMTrampoline)>,
|
||||
),
|
||||
String,
|
||||
> {
|
||||
let obj = ObjectFile::parse(obj).map_err(|_| "Unable to read obj".to_string())?;
|
||||
) -> Result<(
|
||||
CodeMemory,
|
||||
(*const u8, usize),
|
||||
PrimaryMap<DefinedFuncIndex, *mut [VMFunctionBody]>,
|
||||
Vec<(SignatureIndex, VMTrampoline)>,
|
||||
)> {
|
||||
let obj = ObjectFile::parse(obj).with_context(|| "Unable to read obj")?;
|
||||
|
||||
let mut code_memory = CodeMemory::new();
|
||||
|
||||
|
||||
@@ -27,16 +27,12 @@ mod link;
|
||||
mod object;
|
||||
mod unwind;
|
||||
|
||||
pub mod native;
|
||||
pub mod trampoline;
|
||||
|
||||
pub use crate::code_memory::CodeMemory;
|
||||
pub use crate::compiler::{Compilation, CompilationStrategy, Compiler};
|
||||
pub use crate::instantiate::{
|
||||
CompilationArtifacts, CompiledModule, ModuleCode, SetupError, SymbolizeContext, TypeTables,
|
||||
};
|
||||
pub use crate::link::link_module;
|
||||
pub use wasmtime_cranelift::{blank_sig, wasmtime_call_conv};
|
||||
|
||||
/// Version number of this crate.
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use cranelift_codegen;
|
||||
|
||||
pub fn builder() -> cranelift_codegen::isa::Builder {
|
||||
cranelift_native::builder().expect("host machine is not a supported target")
|
||||
}
|
||||
|
||||
pub fn builder_without_flags() -> cranelift_codegen::isa::Builder {
|
||||
cranelift_native::builder_with_options(cranelift_codegen::isa::BackendVariant::Any, false)
|
||||
.expect("host machine is not a supported target")
|
||||
}
|
||||
|
||||
pub use cranelift_codegen::isa::lookup;
|
||||
@@ -1,12 +1,11 @@
|
||||
//! Object file generation.
|
||||
|
||||
use super::trampoline::build_trampoline;
|
||||
use cranelift_frontend::FunctionBuilderContext;
|
||||
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, TargetIsa};
|
||||
use wasmtime_environ::isa::unwind::UnwindInfo;
|
||||
use wasmtime_environ::wasm::{FuncIndex, SignatureIndex};
|
||||
use wasmtime_environ::{CompiledFunctions, ModuleTranslation, TypeTables};
|
||||
use wasmtime_obj::{ObjectBuilder, ObjectBuilderTarget};
|
||||
@@ -22,7 +21,7 @@ pub enum ObjectUnwindInfo {
|
||||
|
||||
// Builds ELF image from the module `Compilation`.
|
||||
pub(crate) fn build_object(
|
||||
isa: &dyn TargetIsa,
|
||||
compiler: &Compiler,
|
||||
translation: &ModuleTranslation,
|
||||
types: &TypeTables,
|
||||
funcs: &CompiledFunctions,
|
||||
@@ -50,10 +49,10 @@ pub(crate) fn build_object(
|
||||
})
|
||||
.collect::<BTreeSet<_>>();
|
||||
let mut trampolines = Vec::with_capacity(signatures.len());
|
||||
let mut cx = FunctionBuilderContext::new();
|
||||
for i in signatures {
|
||||
let native_sig = wasmtime_cranelift::indirect_signature(isa, &types, i);
|
||||
let func = build_trampoline(isa, &mut cx, &native_sig, std::mem::size_of::<u128>())?;
|
||||
let func = compiler
|
||||
.compiler()
|
||||
.host_to_wasm_trampoline(compiler.isa(), &types.wasm_signatures[i])?;
|
||||
// Preserve trampoline function unwind info.
|
||||
if let Some(info) = &func.unwind_info {
|
||||
unwind_info.push(ObjectUnwindInfo::Trampoline(i, info.clone()))
|
||||
@@ -61,7 +60,7 @@ pub(crate) fn build_object(
|
||||
trampolines.push((i, func));
|
||||
}
|
||||
|
||||
let target = ObjectBuilderTarget::new(isa.triple().architecture)?;
|
||||
let target = ObjectBuilderTarget::new(compiler.isa().triple().architecture)?;
|
||||
let mut builder = ObjectBuilder::new(target, &translation.module, funcs);
|
||||
builder
|
||||
.set_code_alignment(CODE_SECTION_ALIGNMENT)
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use crate::code_memory::CodeMemory;
|
||||
use crate::instantiate::SetupError;
|
||||
use cranelift_codegen::ir::InstBuilder;
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use wasmtime_environ::{CompileError, CompiledFunction, Relocation, RelocationTarget};
|
||||
use wasmtime_runtime::{InstantiationError, VMFunctionBody, VMTrampoline};
|
||||
|
||||
pub mod ir {
|
||||
pub(super) use cranelift_codegen::ir::{
|
||||
AbiParam, ConstantOffset, JumpTable, Signature, SourceLoc,
|
||||
};
|
||||
pub use cranelift_codegen::ir::{
|
||||
ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind,
|
||||
};
|
||||
}
|
||||
pub use cranelift_codegen::print_errors::pretty_error;
|
||||
pub use cranelift_codegen::Context;
|
||||
pub use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
|
||||
|
||||
pub mod binemit {
|
||||
pub use cranelift_codegen::binemit::NullTrapSink;
|
||||
pub(super) use cranelift_codegen::binemit::{Addend, Reloc, RelocSink};
|
||||
pub use cranelift_codegen::binemit::{CodeOffset, NullStackMapSink, TrapSink};
|
||||
}
|
||||
|
||||
/// Create a trampoline for invoking a function.
|
||||
pub fn make_trampoline(
|
||||
isa: &dyn TargetIsa,
|
||||
code_memory: &mut CodeMemory,
|
||||
fn_builder_ctx: &mut FunctionBuilderContext,
|
||||
signature: &ir::Signature,
|
||||
value_size: usize,
|
||||
) -> Result<VMTrampoline, SetupError> {
|
||||
let compiled_function = build_trampoline(isa, fn_builder_ctx, signature, value_size)?;
|
||||
|
||||
assert!(compiled_function.relocations.is_empty());
|
||||
let ptr = code_memory
|
||||
.allocate_for_function(&compiled_function)
|
||||
.map_err(|message| {
|
||||
SetupError::Instantiate(InstantiationError::Resource(anyhow::anyhow!(message)))
|
||||
})?
|
||||
.as_ptr();
|
||||
Ok(unsafe { std::mem::transmute::<*const VMFunctionBody, VMTrampoline>(ptr) })
|
||||
}
|
||||
|
||||
pub(crate) fn build_trampoline(
|
||||
isa: &dyn TargetIsa,
|
||||
fn_builder_ctx: &mut FunctionBuilderContext,
|
||||
signature: &ir::Signature,
|
||||
value_size: usize,
|
||||
) -> Result<CompiledFunction, SetupError> {
|
||||
let pointer_type = isa.pointer_type();
|
||||
let mut wrapper_sig =
|
||||
wasmtime_cranelift::blank_sig(isa, wasmtime_cranelift::wasmtime_call_conv(isa));
|
||||
|
||||
// Add the `callee_address` parameter.
|
||||
wrapper_sig.params.push(ir::AbiParam::new(pointer_type));
|
||||
|
||||
// Add the `values_vec` parameter.
|
||||
wrapper_sig.params.push(ir::AbiParam::new(pointer_type));
|
||||
|
||||
let mut context = Context::new();
|
||||
context.func = ir::Function::with_name_signature(ir::ExternalName::user(0, 0), wrapper_sig);
|
||||
|
||||
{
|
||||
let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx);
|
||||
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 = 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::<Vec<_>>();
|
||||
|
||||
let new_sig = builder.import_signature(signature.clone());
|
||||
|
||||
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 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| {
|
||||
SetupError::Compile(CompileError::Codegen(pretty_error(
|
||||
&context.func,
|
||||
Some(isa),
|
||||
error,
|
||||
)))
|
||||
})?;
|
||||
|
||||
let unwind_info = context.create_unwind_info(isa).map_err(|error| {
|
||||
SetupError::Compile(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(),
|
||||
})
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
pub struct TrampolineRelocSink {
|
||||
relocs: Vec<Relocation>,
|
||||
}
|
||||
|
||||
impl TrampolineRelocSink {
|
||||
/// Returns collected relocations.
|
||||
pub fn relocs(&self) -> &[Relocation] {
|
||||
&self.relocs
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
//! Module for System V ABI unwind registry.
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use cranelift_codegen::isa::{unwind::UnwindInfo, TargetIsa};
|
||||
use gimli::{
|
||||
write::{Address, EhFrame, EndianVec, FrameTable, Writer},
|
||||
RunTimeEndian,
|
||||
};
|
||||
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
|
||||
|
||||
/// Represents a registry of function unwind information for System V ABI.
|
||||
pub struct UnwindRegistry {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Module for Windows x64 ABI unwind registry.
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use cranelift_codegen::isa::{unwind::UnwindInfo, TargetIsa};
|
||||
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
|
||||
use winapi::um::winnt;
|
||||
|
||||
/// Represents a registry of function unwind information for Windows x64 ABI.
|
||||
|
||||
Reference in New Issue
Block a user