wasmtime: Extract cranelift/lightbeam compilers to separate crates (#2117)

This commit extracts the two implementations of `Compiler` into two
separate crates, `wasmtime-cranelfit` and `wasmtime-lightbeam`. The
`wasmtime-jit` crate then depends on these two and instantiates them
appropriately. The goal here is to start reducing the weight of the
`wasmtime-environ` crate, which currently serves as a common set of
types between all `wasmtime-*` crates. Long-term I'd like to remove the
dependency on Cranelift from `wasmtime-environ`, but that's going to
take a lot more work.

In the meantime I figure it's a good way to get started by separating
out the lightbeam/cranelift function compilers from the
`wasmtime-environ` crate. We can continue to iterate on moving things
out in the future, too.
This commit is contained in:
Alex Crichton
2020-08-20 04:34:31 -05:00
committed by GitHub
parent b5e24c8c67
commit 693c6ea771
19 changed files with 564 additions and 369 deletions

View File

@@ -0,0 +1,18 @@
[package]
name = "wasmtime-lightbeam"
version = "0.19.0"
authors = ["The Wasmtime Project Developers"]
description = "Integration between Lightbeam and Wasmtime"
license = "Apache-2.0 WITH LLVM-exception"
repository = "https://github.com/bytecodealliance/wasmtime"
documentation = "https://docs.rs/wasmtime-lightbeam/"
categories = ["wasm"]
keywords = ["webassembly", "wasm"]
readme = "README.md"
edition = "2018"
[dependencies]
lightbeam = { path = "..", version = "0.19.0" }
wasmparser = "0.59"
cranelift-codegen = { path = "../../../cranelift/codegen", version = "0.66.0" }
wasmtime-environ = { path = "../../environ", version = "0.19.0" }

View File

@@ -0,0 +1,4 @@
# `wasmtime-lightbeam`
This crate provides an implementation of the `Compiler` trait which is
connected to Lightbeam.

View File

@@ -0,0 +1,334 @@
//! Support for compiling with Lightbeam.
//!
//! This crates provides an implementation of [`Compiler`] in the form of
//! [`Lightbeam`].
use cranelift_codegen::binemit;
use cranelift_codegen::ir::{self, ExternalName};
use cranelift_codegen::isa;
use lightbeam::{CodeGenSession, NullOffsetSink, Sinks};
use wasmtime_environ::wasm::{
DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex,
GlobalIndex, MemoryIndex, SignatureIndex, TableIndex,
};
use wasmtime_environ::{
BuiltinFunctionIndex, CompileError, CompiledFunction, Compiler, FunctionBodyData, Module,
ModuleTranslation, Relocation, RelocationTarget, TrapInformation, VMOffsets,
};
/// A compiler that compiles a WebAssembly module with Lightbeam, directly translating the Wasm file.
pub struct Lightbeam;
impl Compiler for Lightbeam {
fn compile_function(
&self,
translation: &ModuleTranslation,
i: DefinedFuncIndex,
function_body: &FunctionBodyData<'_>,
isa: &dyn isa::TargetIsa,
) -> Result<CompiledFunction, CompileError> {
if translation.tunables.debug_info {
return Err(CompileError::DebugInfoNotSupported);
}
let func_index = translation.module.func_index(i);
let env = FuncEnvironment::new(isa.frontend_config().pointer_bytes(), &translation.module);
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(),
wasmparser::FunctionBody::new(0, function_body.data),
)
.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)))?;
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(),
})
}
}
/// 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<Relocation>,
}
impl binemit::RelocSink for RelocSink {
fn reloc_block(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_block_offset: binemit::CodeOffset,
) {
// This should use the `offsets` field of `ir::Function`.
panic!("block headers not yet implemented");
}
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<TrapInformation>,
}
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,
source_loc,
trap_code,
});
}
}
/// The `FuncEnvironment` implementation for use by the `ModuleEnvironment`.
struct FuncEnvironment<'module_environment> {
/// The module-level environment which this function-level environment belongs to.
module: &'module_environment Module,
/// Offsets to struct fields accessed by JIT code.
offsets: VMOffsets,
}
impl<'module_environment> FuncEnvironment<'module_environment> {
fn new(pointer_bytes: u8, module: &'module_environment Module) -> Self {
Self {
module,
offsets: VMOffsets::new(pointer_bytes, module),
}
}
}
// TODO: This is necessary as if Lightbeam used `FuncEnvironment` directly it would cause
// a circular dependency graph. We should extract common types out into a separate
// crate that Lightbeam can use but until then we need this trait.
impl lightbeam::ModuleContext for FuncEnvironment<'_> {
type Signature = ir::Signature;
type GlobalType = ir::Type;
fn func_index(&self, defined_func_index: u32) -> u32 {
self.module
.func_index(DefinedFuncIndex::from_u32(defined_func_index))
.as_u32()
}
fn defined_func_index(&self, func_index: u32) -> Option<u32> {
self.module
.defined_func_index(FuncIndex::from_u32(func_index))
.map(DefinedFuncIndex::as_u32)
}
fn defined_global_index(&self, global_index: u32) -> Option<u32> {
self.module
.defined_global_index(GlobalIndex::from_u32(global_index))
.map(DefinedGlobalIndex::as_u32)
}
fn global_type(&self, global_index: u32) -> &Self::GlobalType {
&self.module.globals[GlobalIndex::from_u32(global_index)].ty
}
fn func_type_index(&self, func_idx: u32) -> u32 {
self.module.functions[FuncIndex::from_u32(func_idx)].as_u32()
}
fn signature(&self, index: u32) -> &Self::Signature {
&self.module.signatures[SignatureIndex::from_u32(index)].1
}
fn defined_table_index(&self, table_index: u32) -> Option<u32> {
self.module
.defined_table_index(TableIndex::from_u32(table_index))
.map(DefinedTableIndex::as_u32)
}
fn defined_memory_index(&self, memory_index: u32) -> Option<u32> {
self.module
.defined_memory_index(MemoryIndex::from_u32(memory_index))
.map(DefinedMemoryIndex::as_u32)
}
fn vmctx_builtin_function(&self, func_index: u32) -> u32 {
self.offsets
.vmctx_builtin_function(BuiltinFunctionIndex::from_u32(func_index))
}
fn vmctx_vmfunction_import_body(&self, func_index: u32) -> u32 {
self.offsets
.vmctx_vmfunction_import_body(FuncIndex::from_u32(func_index))
}
fn vmctx_vmfunction_import_vmctx(&self, func_index: u32) -> u32 {
self.offsets
.vmctx_vmfunction_import_vmctx(FuncIndex::from_u32(func_index))
}
fn vmctx_vmglobal_import_from(&self, global_index: u32) -> u32 {
self.offsets
.vmctx_vmglobal_import_from(GlobalIndex::from_u32(global_index))
}
fn vmctx_vmglobal_definition(&self, defined_global_index: u32) -> u32 {
self.offsets
.vmctx_vmglobal_definition(DefinedGlobalIndex::from_u32(defined_global_index))
}
fn vmctx_vmmemory_import_from(&self, memory_index: u32) -> u32 {
self.offsets
.vmctx_vmmemory_import_from(MemoryIndex::from_u32(memory_index))
}
fn vmctx_vmmemory_definition(&self, defined_memory_index: u32) -> u32 {
self.offsets
.vmctx_vmmemory_definition(DefinedMemoryIndex::from_u32(defined_memory_index))
}
fn vmctx_vmmemory_definition_base(&self, defined_memory_index: u32) -> u32 {
self.offsets
.vmctx_vmmemory_definition_base(DefinedMemoryIndex::from_u32(defined_memory_index))
}
fn vmctx_vmmemory_definition_current_length(&self, defined_memory_index: u32) -> u32 {
self.offsets
.vmctx_vmmemory_definition_current_length(DefinedMemoryIndex::from_u32(
defined_memory_index,
))
}
fn vmmemory_definition_base(&self) -> u8 {
self.offsets.vmmemory_definition_base()
}
fn vmmemory_definition_current_length(&self) -> u8 {
self.offsets.vmmemory_definition_current_length()
}
fn vmctx_vmtable_import_from(&self, table_index: u32) -> u32 {
self.offsets
.vmctx_vmtable_import_from(TableIndex::from_u32(table_index))
}
fn vmctx_vmtable_definition(&self, defined_table_index: u32) -> u32 {
self.offsets
.vmctx_vmtable_definition(DefinedTableIndex::from_u32(defined_table_index))
}
fn vmctx_vmtable_definition_base(&self, defined_table_index: u32) -> u32 {
self.offsets
.vmctx_vmtable_definition_base(DefinedTableIndex::from_u32(defined_table_index))
}
fn vmctx_vmtable_definition_current_elements(&self, defined_table_index: u32) -> u32 {
self.offsets
.vmctx_vmtable_definition_current_elements(DefinedTableIndex::from_u32(
defined_table_index,
))
}
fn vmtable_definition_base(&self) -> u8 {
self.offsets.vmtable_definition_base()
}
fn vmtable_definition_current_elements(&self) -> u8 {
self.offsets.vmtable_definition_current_elements()
}
fn vmcaller_checked_anyfunc_type_index(&self) -> u8 {
self.offsets.vmcaller_checked_anyfunc_type_index()
}
fn vmcaller_checked_anyfunc_func_ptr(&self) -> u8 {
self.offsets.vmcaller_checked_anyfunc_func_ptr()
}
fn vmcaller_checked_anyfunc_vmctx(&self) -> u8 {
self.offsets.vmcaller_checked_anyfunc_vmctx()
}
fn size_of_vmcaller_checked_anyfunc(&self) -> u8 {
self.offsets.size_of_vmcaller_checked_anyfunc()
}
fn vmctx_vmshared_signature_id(&self, signature_idx: u32) -> u32 {
self.offsets
.vmctx_vmshared_signature_id(SignatureIndex::from_u32(signature_idx))
}
// TODO: type of a global
}