Integrate Lightbeam (#51)

* Integrate lightbeam
This commit is contained in:
Jef
2019-04-05 21:42:54 +02:00
committed by Dan Gohman
parent a1c123c3dd
commit 9bf6d73210
14 changed files with 351 additions and 85 deletions

4
.gitmodules vendored
View File

@@ -1,3 +1,7 @@
[submodule "spec_testsuite"] [submodule "spec_testsuite"]
path = spec_testsuite path = spec_testsuite
url = https://github.com/WebAssembly/testsuite url = https://github.com/WebAssembly/testsuite
[submodule "lightbeam"]
path = lightbeam
url = https://github.com/CraneStation/lightbeam.git
branch = master

View File

@@ -44,3 +44,6 @@ libc = "0.2.50"
errno = "0.2.4" errno = "0.2.4"
[workspace] [workspace]
[features]
lightbeam = ["wasmtime-environ/lightbeam", "wasmtime-jit/lightbeam"]

1
lightbeam Submodule

Submodule lightbeam added at 762cd3fb32

View File

@@ -49,7 +49,7 @@ use std::str;
use std::str::FromStr; use std::str::FromStr;
use target_lexicon::Triple; use target_lexicon::Triple;
use wasmtime_debug::{emit_debugsections, read_debuginfo}; use wasmtime_debug::{emit_debugsections, read_debuginfo};
use wasmtime_environ::{cranelift, ModuleEnvironment, Tunables}; use wasmtime_environ::{Compiler, Cranelift, ModuleEnvironment, Tunables};
use wasmtime_obj::emit_module; use wasmtime_obj::emit_module;
const USAGE: &str = " const USAGE: &str = "
@@ -159,7 +159,7 @@ fn handle_module(
) )
}; };
let (compilation, relocations, address_transform) = cranelift::compile_module( let (compilation, relocations, address_transform) = Cranelift::compile_module(
&module, &module,
lazy_function_body_inputs, lazy_function_body_inputs,
&*isa, &*isa,

View File

@@ -15,6 +15,7 @@ edition = "2018"
cranelift-codegen = "0.30.0" cranelift-codegen = "0.30.0"
cranelift-entity = "0.30.0" cranelift-entity = "0.30.0"
cranelift-wasm = "0.30.0" cranelift-wasm = "0.30.0"
lightbeam = { path = "../lightbeam", optional = true }
cast = { version = "0.2.2", default-features = false } cast = { version = "0.2.2", default-features = false }
failure = { version = "0.1.3", default-features = false } failure = { version = "0.1.3", default-features = false }
failure_derive = { version = "0.1.3", default-features = false } failure_derive = { version = "0.1.3", default-features = false }

View File

@@ -1,25 +1,71 @@
//! A `Compilation` contains the compiled function bodies for a WebAssembly //! A `Compilation` contains the compiled function bodies for a WebAssembly
//! module. //! module.
use cranelift_codegen::binemit; use crate::module;
use cranelift_codegen::ir; use crate::module_environ::FunctionBodyData;
use cranelift_codegen::CodegenError; use cranelift_codegen::{binemit, ir, isa, CodegenError};
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmError}; use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmError};
use std::ops::Range;
use std::vec::Vec; use std::vec::Vec;
type Functions = PrimaryMap<DefinedFuncIndex, Vec<u8>>;
/// The result of compiling a WebAssembly module's functions. /// The result of compiling a WebAssembly module's functions.
#[derive(Debug)] #[derive(Debug)]
pub struct Compilation { pub struct Compilation {
/// Compiled machine code for the function bodies. /// Compiled machine code for the function bodies.
pub functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>, functions: Functions,
} }
impl Compilation { impl Compilation {
/// Allocates the compilation result with the given function bodies. /// Creates a compilation artifact from a contiguous function buffer and a set of ranges
pub fn new(functions: PrimaryMap<DefinedFuncIndex, Vec<u8>>) -> Self { pub fn new(functions: Functions) -> Self {
Self { functions } Self { functions }
} }
/// Allocates the compilation result with the given function bodies.
pub fn from_buffer(buffer: Vec<u8>, functions: impl IntoIterator<Item = Range<usize>>) -> Self {
Self::new(
functions
.into_iter()
.map(|range| buffer[range].to_vec())
.collect(),
)
}
/// Gets the bytes of a single function
pub fn get(&self, func: DefinedFuncIndex) -> &[u8] {
&self.functions[func]
}
/// Gets the number of functions defined.
pub fn len(&self) -> usize {
self.functions.len()
}
}
impl<'a> IntoIterator for &'a Compilation {
type IntoIter = Iter<'a>;
type Item = <Self::IntoIter as Iterator>::Item;
fn into_iter(self) -> Self::IntoIter {
Iter {
iterator: self.functions.iter(),
}
}
}
pub struct Iter<'a> {
iterator: <&'a Functions as IntoIterator>::IntoIter,
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
self.iterator.next().map(|(_, b)| &b[..])
}
} }
/// A record of a relocation to perform. /// A record of a relocation to perform.
@@ -95,3 +141,14 @@ pub struct FunctionAddressTransform {
/// Function AddressTransforms collection. /// Function AddressTransforms collection.
pub type AddressTransforms = PrimaryMap<DefinedFuncIndex, FunctionAddressTransform>; pub type AddressTransforms = PrimaryMap<DefinedFuncIndex, FunctionAddressTransform>;
/// An implementation of a compiler from parsed WebAssembly module to native code.
pub trait Compiler {
/// Compile a parsed module with the given `TargetIsa`.
fn compile_module<'data, 'module>(
module: &'module module::Module,
function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
isa: &dyn isa::TargetIsa,
generate_debug_info: bool,
) -> Result<(Compilation, Relocations, AddressTransforms), CompileError>;
}

View File

@@ -21,9 +21,9 @@ use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use std::vec::Vec; use std::vec::Vec;
/// Implementation of a relocation sink that just saves all the information for later /// Implementation of a relocation sink that just saves all the information for later
struct RelocSink { pub struct RelocSink {
/// Relocations recorded for the function. /// Relocations recorded for the function.
func_relocs: Vec<Relocation>, pub func_relocs: Vec<Relocation>,
} }
impl binemit::RelocSink for RelocSink { impl binemit::RelocSink for RelocSink {
@@ -109,14 +109,19 @@ fn get_address_transform(
result result
} }
/// Compile the module using Cranelift, producing a compilation result with /// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR,
/// associated relocations. /// optimizing it and then translating to assembly.
pub fn compile_module<'data, 'module>( pub struct Cranelift;
impl crate::compilation::Compiler for Cranelift {
/// Compile the module using Cranelift, producing a compilation result with
/// associated relocations.
fn compile_module<'data, 'module>(
module: &'module Module, module: &'module Module,
function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>, function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
isa: &dyn isa::TargetIsa, isa: &dyn isa::TargetIsa,
generate_debug_info: bool, generate_debug_info: bool,
) -> Result<(Compilation, Relocations, AddressTransforms), CompileError> { ) -> Result<(Compilation, Relocations, AddressTransforms), CompileError> {
let mut functions = PrimaryMap::with_capacity(function_body_inputs.len()); let mut functions = PrimaryMap::with_capacity(function_body_inputs.len());
let mut relocations = PrimaryMap::with_capacity(function_body_inputs.len()); let mut relocations = PrimaryMap::with_capacity(function_body_inputs.len());
let mut address_transforms = PrimaryMap::with_capacity(function_body_inputs.len()); let mut address_transforms = PrimaryMap::with_capacity(function_body_inputs.len());
@@ -174,4 +179,5 @@ pub fn compile_module<'data, 'module>(
// TODO: Reorganize where we create the Vec for the resolved imports. // TODO: Reorganize where we create the Vec for the resolved imports.
Ok((Compilation::new(functions), relocations, address_transforms)) Ok((Compilation::new(functions), relocations, address_transforms))
}
} }

View File

@@ -14,8 +14,8 @@ use cranelift_codegen::ir::{
use cranelift_codegen::isa::TargetFrontendConfig; use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
use cranelift_wasm::{ use cranelift_wasm::{
self, FuncIndex, GlobalIndex, GlobalVariable, MemoryIndex, SignatureIndex, TableIndex, self, DefinedFuncIndex, DefinedGlobalIndex, DefinedMemoryIndex, DefinedTableIndex, FuncIndex,
WasmResult, GlobalIndex, GlobalVariable, MemoryIndex, SignatureIndex, TableIndex, WasmResult,
}; };
use std::vec::Vec; use std::vec::Vec;
@@ -207,6 +207,138 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
} }
} }
#[cfg(feature = "lightbeam")]
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(|i| i.as_u32())
}
fn defined_global_index(&self, global_index: u32) -> Option<u32> {
self.module
.defined_global_index(GlobalIndex::from_u32(global_index))
.map(|i| i.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)]
}
fn defined_table_index(&self, table_index: u32) -> Option<u32> {
self.module
.defined_table_index(TableIndex::from_u32(table_index))
.map(|i| i.as_u32())
}
fn defined_memory_index(&self, memory_index: u32) -> Option<u32> {
self.module
.defined_memory_index(MemoryIndex::from_u32(memory_index))
.map(|i| i.as_u32())
}
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
}
impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> { impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> {
fn target_config(&self) -> TargetFrontendConfig { fn target_config(&self) -> TargetFrontendConfig {
self.target_config self.target_config

View File

@@ -45,11 +45,16 @@ mod tunables;
mod vmoffsets; mod vmoffsets;
pub mod cranelift; pub mod cranelift;
#[cfg(feature = "lightbeam")]
pub mod lightbeam;
pub use crate::compilation::{ pub use crate::compilation::{
AddressTransforms, Compilation, CompileError, InstructionAddressTransform, Relocation, AddressTransforms, Compilation, CompileError, Compiler, InstructionAddressTransform,
RelocationTarget, Relocations, Relocation, RelocationTarget, Relocations,
}; };
pub use crate::cranelift::Cranelift;
#[cfg(feature = "lightbeam")]
pub use crate::lightbeam::Lightbeam;
pub use crate::module::{ pub use crate::module::{
Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle, Export, MemoryPlan, MemoryStyle, Module, TableElements, TablePlan, TableStyle,
}; };

View File

@@ -0,0 +1,55 @@
//! Support for compiling with Lightbeam.
use crate::compilation::{AddressTransforms, Compilation, CompileError, Relocations};
use crate::func_environ::FuncEnvironment;
use crate::module::Module;
use crate::module_environ::FunctionBodyData;
// TODO: Put this in `compilation`
use crate::cranelift::RelocSink;
use cranelift_codegen::isa;
use cranelift_entity::PrimaryMap;
use cranelift_wasm::DefinedFuncIndex;
use lightbeam;
/// A compiler that compiles a WebAssembly module with Lightbeam, directly translating the Wasm file.
pub struct Lightbeam;
impl crate::compilation::Compiler for Lightbeam {
/// Compile the module using Lightbeam, producing a compilation result with
/// associated relocations.
fn compile_module<'data, 'module>(
module: &'module Module,
function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
isa: &dyn isa::TargetIsa,
// TODO
_generate_debug_info: bool,
) -> Result<(Compilation, Relocations, AddressTransforms), CompileError> {
let env = FuncEnvironment::new(isa.frontend_config(), module);
let mut relocations = PrimaryMap::new();
let mut codegen_session: lightbeam::CodeGenSession<_> =
lightbeam::CodeGenSession::new(function_body_inputs.len() as u32, &env);
for (i, function_body) in &function_body_inputs {
let mut reloc_sink = RelocSink::new();
lightbeam::translate_function(
&mut codegen_session,
&mut reloc_sink,
i.as_u32(),
&lightbeam::wasmparser::FunctionBody::new(0, function_body.data),
)
.expect("Failed to translate function. TODO: Stop this from panicking");
relocations.push(reloc_sink.func_relocs);
}
let code_section = codegen_session
.into_translated_code_section()
.expect("Failed to generate output code. TODO: Stop this from panicking");
Ok((
Compilation::from_buffer(code_section.buffer().to_vec(), code_section.funcs()),
relocations,
AddressTransforms::new(),
))
}
}

View File

@@ -30,6 +30,7 @@ wasmparser = "0.29.2"
default = ["std"] default = ["std"]
std = ["cranelift-codegen/std", "cranelift-wasm/std"] std = ["cranelift-codegen/std", "cranelift-wasm/std"]
core = ["hashbrown/nightly", "cranelift-codegen/core", "cranelift-wasm/core", "wasmtime-environ/core"] core = ["hashbrown/nightly", "cranelift-codegen/core", "cranelift-wasm/core", "wasmtime-environ/core"]
lightbeam = ["wasmtime-environ/lightbeam"]
[badges] [badges]
maintenance = { status = "experimental" } maintenance = { status = "experimental" }

View File

@@ -17,7 +17,7 @@ use std::vec::Vec;
use wasmtime_debug::{emit_debugsections_image, DebugInfoData}; use wasmtime_debug::{emit_debugsections_image, DebugInfoData};
use wasmtime_environ::cranelift; use wasmtime_environ::cranelift;
use wasmtime_environ::{ use wasmtime_environ::{
Compilation, CompileError, FunctionBodyData, Module, Relocations, Tunables, Compilation, CompileError, Compiler as _C, FunctionBodyData, Module, Relocations, Tunables,
}; };
use wasmtime_runtime::{InstantiationError, SignatureRegistry, VMFunctionBody}; use wasmtime_runtime::{InstantiationError, SignatureRegistry, VMFunctionBody};
@@ -53,6 +53,11 @@ impl Compiler {
} }
} }
#[cfg(feature = "lightbeam")]
type DefaultCompiler = wasmtime_environ::lightbeam::Lightbeam;
#[cfg(not(feature = "lightbeam"))]
type DefaultCompiler = wasmtime_environ::cranelift::Cranelift;
impl Compiler { impl Compiler {
/// Return the target's frontend configuration settings. /// Return the target's frontend configuration settings.
pub fn frontend_config(&self) -> TargetFrontendConfig { pub fn frontend_config(&self) -> TargetFrontendConfig {
@@ -78,7 +83,7 @@ impl Compiler {
), ),
SetupError, SetupError,
> { > {
let (compilation, relocations, address_transform) = cranelift::compile_module( let (compilation, relocations, address_transform) = DefaultCompiler::compile_module(
module, module,
function_body_inputs, function_body_inputs,
&*self.isa, &*self.isa,
@@ -100,7 +105,7 @@ impl Compiler {
let mut funcs = Vec::new(); let mut funcs = Vec::new();
for (i, allocated) in allocated_functions.into_iter() { for (i, allocated) in allocated_functions.into_iter() {
let ptr = (*allocated) as *const u8; let ptr = (*allocated) as *const u8;
let body_len = compilation.functions[i].len(); let body_len = compilation.get(i).len();
funcs.push((ptr, body_len)); funcs.push((ptr, body_len));
} }
let bytes = emit_debugsections_image( let bytes = emit_debugsections_image(
@@ -255,14 +260,10 @@ fn allocate_functions(
// Allocate code for all function in one continuous memory block. // Allocate code for all function in one continuous memory block.
// First, collect all function bodies into vector to pass to the // First, collect all function bodies into vector to pass to the
// allocate_copy_of_byte_slices. // allocate_copy_of_byte_slices.
let bodies = compilation let bodies = compilation.into_iter().collect::<Vec<&[u8]>>();
.functions
.values()
.map(|body| body.as_slice())
.collect::<Vec<&[u8]>>();
let fat_ptrs = code_memory.allocate_copy_of_byte_slices(&bodies)?; let fat_ptrs = code_memory.allocate_copy_of_byte_slices(&bodies)?;
// Second, create a PrimaryMap from result vector of pointers. // Second, create a PrimaryMap from result vector of pointers.
let mut result = PrimaryMap::with_capacity(compilation.functions.len()); let mut result = PrimaryMap::with_capacity(compilation.len());
for i in 0..fat_ptrs.len() { for i in 0..fat_ptrs.len() {
let fat_ptr: *mut [VMFunctionBody] = fat_ptrs[i]; let fat_ptr: *mut [VMFunctionBody] = fat_ptrs[i];
result.push(fat_ptr); result.push(fat_ptr);

View File

@@ -62,11 +62,11 @@ pub fn emit_functions(
.expect("Missing enable_verifier setting"); .expect("Missing enable_verifier setting");
for (i, _function_relocs) in relocations.iter() { for (i, _function_relocs) in relocations.iter() {
let body = &compilation.functions[i]; let body = compilation.get(i);
let func_index = module.func_index(i); let func_index = module.func_index(i);
let string_name = format!("_wasm_function_{}", func_index.index()); let string_name = format!("_wasm_function_{}", func_index.index());
obj.define(string_name, body.clone()) obj.define(string_name, body.to_vec())
.map_err(|err| format!("{}", err))?; .map_err(|err| format!("{}", err))?;
} }