Refactor where results of compilation are stored (#2086)

* Refactor where results of compilation are stored

This commit refactors the internals of compilation in Wasmtime to change
where results of individual function compilation are stored. Previously
compilation resulted in many maps being returned, and compilation
results generally held all these maps together. This commit instead
switches this to have all metadata stored in a `CompiledFunction`
instead of having a separate map for each item that can be stored.

The motivation for this is primarily to help out with future
module-linking-related PRs. What exactly "module level" is depends on
how we interpret modules and how many modules are in play, so it's a bit
easier for operations in wasmtime to work at the function level where
possible. This means that we don't have to pass around multiple
different maps and a function index, but instead just one map or just
one entry representing a compiled function.

Additionally this change updates where the parallelism of compilation
happens, pushing it into `wasmtime-jit` instead of `wasmtime-environ`.
This is another goal where `wasmtime-jit` will have more knowledge about
module-level pieces with module linking in play. User-facing-wise this
should be the same in terms of parallel compilation, though.

The ultimate goal of this refactoring is to make it easier for the
results of compilation to actually be a set of wasm modules. This means
we won't be able to have a map-per-metadata where the primary key is the
function index, because there will be many modules within one "object
file".

* Don't clear out fields, just don't store them

Persist a smaller set of fields in `CompilationArtifacts` instead of
trying to clear fields out and dynamically not accessing them.
This commit is contained in:
Alex Crichton
2020-08-03 12:20:51 -05:00
committed by GitHub
parent 026fb8d388
commit 65eaca35dd
31 changed files with 466 additions and 757 deletions

2
Cargo.lock generated
View File

@@ -2474,7 +2474,6 @@ dependencies = [
"log", "log",
"more-asserts", "more-asserts",
"pretty_env_logger", "pretty_env_logger",
"rayon",
"serde", "serde",
"sha2", "sha2",
"tempfile", "tempfile",
@@ -2531,6 +2530,7 @@ dependencies = [
"log", "log",
"more-asserts", "more-asserts",
"object 0.20.0", "object 0.20.0",
"rayon",
"region", "region",
"serde", "serde",
"target-lexicon", "target-lexicon",

View File

@@ -182,7 +182,7 @@ pub struct StackLayoutInfo {
/// Stack frame manager. /// Stack frame manager.
/// ///
/// Keep track of all the stack slots used by a function. /// Keep track of all the stack slots used by a function.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub struct StackSlots { pub struct StackSlots {
/// All allocated stack slots. /// All allocated stack slots.
@@ -202,12 +202,7 @@ pub struct StackSlots {
impl StackSlots { impl StackSlots {
/// Create an empty stack slot manager. /// Create an empty stack slot manager.
pub fn new() -> Self { pub fn new() -> Self {
Self { StackSlots::default()
slots: PrimaryMap::new(),
outgoing: Vec::new(),
emergency: Vec::new(),
layout_info: None,
}
} }
/// Clear out everything. /// Clear out everything.

View File

@@ -1,6 +1,7 @@
//! A double-ended iterator over entity references and entities. //! A double-ended iterator over entity references and entities.
use crate::EntityRef; use crate::EntityRef;
use alloc::vec;
use core::iter::Enumerate; use core::iter::Enumerate;
use core::marker::PhantomData; use core::marker::PhantomData;
use core::slice; use core::slice;
@@ -84,3 +85,40 @@ impl<'a, K: EntityRef, V> DoubleEndedIterator for IterMut<'a, K, V> {
} }
impl<'a, K: EntityRef, V> ExactSizeIterator for IterMut<'a, K, V> {} impl<'a, K: EntityRef, V> ExactSizeIterator for IterMut<'a, K, V> {}
/// Iterate over all keys in order.
pub struct IntoIter<K: EntityRef, V> {
enumerate: Enumerate<vec::IntoIter<V>>,
unused: PhantomData<K>,
}
impl<K: EntityRef, V> IntoIter<K, V> {
/// Create an `IntoIter` iterator that visits the `PrimaryMap` keys and values
/// of `iter`.
pub fn new(iter: vec::IntoIter<V>) -> Self {
Self {
enumerate: iter.enumerate(),
unused: PhantomData,
}
}
}
impl<K: EntityRef, V> Iterator for IntoIter<K, V> {
type Item = (K, V);
fn next(&mut self) -> Option<Self::Item> {
self.enumerate.next().map(|(i, v)| (K::new(i), v))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.enumerate.size_hint()
}
}
impl<K: EntityRef, V> DoubleEndedIterator for IntoIter<K, V> {
fn next_back(&mut self) -> Option<Self::Item> {
self.enumerate.next_back().map(|(i, v)| (K::new(i), v))
}
}
impl<K: EntityRef, V> ExactSizeIterator for IntoIter<K, V> {}

View File

@@ -131,6 +131,16 @@ where
} }
} }
impl<K, V> Default for SecondaryMap<K, V>
where
K: EntityRef,
V: Clone + Default,
{
fn default() -> SecondaryMap<K, V> {
SecondaryMap::new()
}
}
/// Immutable indexing into an `SecondaryMap`. /// Immutable indexing into an `SecondaryMap`.
/// ///
/// All keys are permitted. Untouched entries have the default value. /// All keys are permitted. Untouched entries have the default value.

View File

@@ -1,6 +1,6 @@
//! Densely numbered entity references as mapping keys. //! Densely numbered entity references as mapping keys.
use crate::boxed_slice::BoxedSlice; use crate::boxed_slice::BoxedSlice;
use crate::iter::{Iter, IterMut}; use crate::iter::{IntoIter, Iter, IterMut};
use crate::keys::Keys; use crate::keys::Keys;
use crate::EntityRef; use crate::EntityRef;
use alloc::boxed::Box; use alloc::boxed::Box;
@@ -150,6 +150,15 @@ where
} }
} }
impl<K, V> Default for PrimaryMap<K, V>
where
K: EntityRef,
{
fn default() -> PrimaryMap<K, V> {
PrimaryMap::new()
}
}
/// Immutable indexing into an `PrimaryMap`. /// Immutable indexing into an `PrimaryMap`.
/// The indexed value must be in the map. /// The indexed value must be in the map.
impl<K, V> Index<K> for PrimaryMap<K, V> impl<K, V> Index<K> for PrimaryMap<K, V>
@@ -173,6 +182,18 @@ where
} }
} }
impl<K, V> IntoIterator for PrimaryMap<K, V>
where
K: EntityRef,
{
type Item = (K, V);
type IntoIter = IntoIter<K, V>;
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self.elems.into_iter())
}
}
impl<'a, K, V> IntoIterator for &'a PrimaryMap<K, V> impl<'a, K, V> IntoIterator for &'a PrimaryMap<K, V>
where where
K: EntityRef, K: EntityRef,

View File

@@ -5,8 +5,7 @@ use std::iter::FromIterator;
use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::entity::{EntityRef, PrimaryMap};
use wasmtime_environ::ir::SourceLoc; use wasmtime_environ::ir::SourceLoc;
use wasmtime_environ::wasm::DefinedFuncIndex; use wasmtime_environ::wasm::DefinedFuncIndex;
use wasmtime_environ::WasmFileInfo; use wasmtime_environ::{CompiledFunctions, FunctionAddressMap, WasmFileInfo};
use wasmtime_environ::{FunctionAddressMap, ModuleAddressMap};
pub type GeneratedAddress = usize; pub type GeneratedAddress = usize;
pub type WasmAddress = u64; pub type WasmAddress = u64;
@@ -187,11 +186,12 @@ fn build_function_lookup(
} }
fn build_function_addr_map( fn build_function_addr_map(
at: &ModuleAddressMap, funcs: &CompiledFunctions,
code_section_offset: u64, code_section_offset: u64,
) -> PrimaryMap<DefinedFuncIndex, FunctionMap> { ) -> PrimaryMap<DefinedFuncIndex, FunctionMap> {
let mut map = PrimaryMap::new(); let mut map = PrimaryMap::new();
for (_, ft) in at { for (_, f) in funcs {
let ft = &f.address_map;
let mut fn_map = Vec::new(); let mut fn_map = Vec::new();
for t in &ft.instructions { for t in &ft.instructions {
if t.srcloc.is_default() { if t.srcloc.is_default() {
@@ -447,11 +447,12 @@ impl<'a> Iterator for TransformRangeIter<'a> {
} }
impl AddressTransform { impl AddressTransform {
pub fn new(at: &ModuleAddressMap, wasm_file: &WasmFileInfo) -> Self { pub fn new(funcs: &CompiledFunctions, wasm_file: &WasmFileInfo) -> Self {
let code_section_offset = wasm_file.code_section_offset; let code_section_offset = wasm_file.code_section_offset;
let mut func = BTreeMap::new(); let mut func = BTreeMap::new();
for (i, ft) in at { for (i, f) in funcs {
let ft = &f.address_map;
let (fn_start, fn_end, lookup) = build_function_lookup(ft, code_section_offset); let (fn_start, fn_end, lookup) = build_function_lookup(ft, code_section_offset);
func.insert( func.insert(
@@ -465,7 +466,7 @@ impl AddressTransform {
); );
} }
let map = build_function_addr_map(at, code_section_offset); let map = build_function_addr_map(funcs, code_section_offset);
let func = Vec::from_iter(func.into_iter()); let func = Vec::from_iter(func.into_iter());
AddressTransform { map, func } AddressTransform { map, func }
} }
@@ -606,8 +607,8 @@ mod tests {
use std::iter::FromIterator; use std::iter::FromIterator;
use wasmtime_environ::entity::PrimaryMap; use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::ir::SourceLoc; use wasmtime_environ::ir::SourceLoc;
use wasmtime_environ::WasmFileInfo; use wasmtime_environ::{CompiledFunction, WasmFileInfo};
use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap, ModuleAddressMap}; use wasmtime_environ::{CompiledFunctions, FunctionAddressMap, InstructionAddressMap};
#[test] #[test]
fn test_get_wasm_code_offset() { fn test_get_wasm_code_offset() {
@@ -640,8 +641,11 @@ mod tests {
} }
} }
fn create_simple_module(func: FunctionAddressMap) -> ModuleAddressMap { fn create_simple_module(address_map: FunctionAddressMap) -> CompiledFunctions {
PrimaryMap::from_iter(vec![func]) PrimaryMap::from_iter(vec![CompiledFunction {
address_map,
..Default::default()
}])
} }
#[test] #[test]

View File

@@ -601,6 +601,7 @@ mod tests {
use super::compile_expression; use super::compile_expression;
use super::{AddressTransform, FunctionFrameInfo, ValueLabel, ValueLabelsRanges}; use super::{AddressTransform, FunctionFrameInfo, ValueLabel, ValueLabelsRanges};
use gimli::{self, Encoding, EndianSlice, Expression, RunTimeEndian}; use gimli::{self, Encoding, EndianSlice, Expression, RunTimeEndian};
use wasmtime_environ::CompiledFunction;
macro_rules! expression { macro_rules! expression {
($($i:literal),*) => { ($($i:literal),*) => {
@@ -689,7 +690,8 @@ mod tests {
use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap}; use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap};
let mut module_map = PrimaryMap::new(); let mut module_map = PrimaryMap::new();
let code_section_offset: u32 = 100; let code_section_offset: u32 = 100;
module_map.push(FunctionAddressMap { module_map.push(CompiledFunction {
address_map: FunctionAddressMap {
instructions: vec![ instructions: vec![
InstructionAddressMap { InstructionAddressMap {
srcloc: SourceLoc::new(code_section_offset + 12), srcloc: SourceLoc::new(code_section_offset + 12),
@@ -706,6 +708,8 @@ mod tests {
end_srcloc: SourceLoc::new(code_section_offset + 20), end_srcloc: SourceLoc::new(code_section_offset + 20),
body_offset: 0, body_offset: 0,
body_len: 30, body_len: 30,
},
..Default::default()
}); });
let fi = WasmFileInfo { let fi = WasmFileInfo {
code_section_offset: code_section_offset.into(), code_section_offset: code_section_offset.into(),

View File

@@ -10,8 +10,7 @@ use gimli::{
use std::collections::HashSet; use std::collections::HashSet;
use thiserror::Error; use thiserror::Error;
use wasmtime_environ::isa::TargetIsa; use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::DebugInfoData; use wasmtime_environ::{CompiledFunctions, DebugInfoData, ModuleMemoryOffset};
use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
pub use address_transform::AddressTransform; pub use address_transform::AddressTransform;
@@ -50,11 +49,10 @@ where
pub fn transform_dwarf( pub fn transform_dwarf(
isa: &dyn TargetIsa, isa: &dyn TargetIsa,
di: &DebugInfoData, di: &DebugInfoData,
at: &ModuleAddressMap, funcs: &CompiledFunctions,
vmctx_info: &ModuleVmctxInfo, memory_offset: &ModuleMemoryOffset,
ranges: &ValueLabelsRanges,
) -> Result<write::Dwarf, Error> { ) -> Result<write::Dwarf, Error> {
let addr_tr = AddressTransform::new(at, &di.wasm_file); let addr_tr = AddressTransform::new(funcs, &di.wasm_file);
let reachable = build_dependencies(&di.dwarf, &addr_tr)?.get_reachable(); let reachable = build_dependencies(&di.dwarf, &addr_tr)?.get_reachable();
let context = DebugInputContext { let context = DebugInputContext {
@@ -90,9 +88,9 @@ pub fn transform_dwarf(
unit, unit,
&context, &context,
&addr_tr, &addr_tr,
&ranges, funcs,
memory_offset,
out_encoding, out_encoding,
&vmctx_info,
&mut out_units, &mut out_units,
&mut out_strings, &mut out_strings,
&mut translated, &mut translated,
@@ -107,8 +105,8 @@ pub fn transform_dwarf(
generate_simulated_dwarf( generate_simulated_dwarf(
&addr_tr, &addr_tr,
di, di,
&vmctx_info, memory_offset,
&ranges, funcs,
&translated, &translated,
out_encoding, out_encoding,
&mut out_units, &mut out_units,

View File

@@ -11,9 +11,9 @@ use wasmparser::Type as WasmType;
use wasmtime_environ::entity::EntityRef; use wasmtime_environ::entity::EntityRef;
use wasmtime_environ::isa::TargetIsa; use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex}; use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex};
use wasmtime_environ::WasmFileInfo; use wasmtime_environ::{
use wasmtime_environ::{DebugInfoData, FunctionMetadata}; CompiledFunctions, DebugInfoData, FunctionMetadata, ModuleMemoryOffset, WasmFileInfo,
use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges}; };
const PRODUCER_NAME: &str = "wasmtime"; const PRODUCER_NAME: &str = "wasmtime";
@@ -120,9 +120,9 @@ fn add_wasm_types(
unit: &mut write::Unit, unit: &mut write::Unit,
root_id: write::UnitEntryId, root_id: write::UnitEntryId,
out_strings: &mut write::StringTable, out_strings: &mut write::StringTable,
vmctx_info: &ModuleVmctxInfo, memory_offset: &ModuleMemoryOffset,
) -> WasmTypesDieRefs { ) -> WasmTypesDieRefs {
let (_wp_die_id, vmctx_die_id) = add_internal_types(unit, root_id, out_strings, vmctx_info); let (_wp_die_id, vmctx_die_id) = add_internal_types(unit, root_id, out_strings, memory_offset);
macro_rules! def_type { macro_rules! def_type {
($id:literal, $size:literal, $enc:path) => {{ ($id:literal, $size:literal, $enc:path) => {{
@@ -280,8 +280,8 @@ fn check_invalid_chars_in_path(path: PathBuf) -> Option<PathBuf> {
pub fn generate_simulated_dwarf( pub fn generate_simulated_dwarf(
addr_tr: &AddressTransform, addr_tr: &AddressTransform,
di: &DebugInfoData, di: &DebugInfoData,
vmctx_info: &ModuleVmctxInfo, memory_offset: &ModuleMemoryOffset,
ranges: &ValueLabelsRanges, funcs: &CompiledFunctions,
translated: &HashSet<DefinedFuncIndex>, translated: &HashSet<DefinedFuncIndex>,
out_encoding: gimli::Encoding, out_encoding: gimli::Encoding,
out_units: &mut write::UnitTable, out_units: &mut write::UnitTable,
@@ -342,7 +342,7 @@ pub fn generate_simulated_dwarf(
(unit, root_id, name_id) (unit, root_id, name_id)
}; };
let wasm_types = add_wasm_types(unit, root_id, out_strings, vmctx_info); let wasm_types = add_wasm_types(unit, root_id, out_strings, memory_offset);
for (i, map) in addr_tr.map().iter() { for (i, map) in addr_tr.map().iter() {
let index = i.index(); let index = i.index();
@@ -389,7 +389,7 @@ pub fn generate_simulated_dwarf(
write::AttributeValue::Udata(wasm_offset), write::AttributeValue::Udata(wasm_offset),
); );
if let Some(frame_info) = get_function_frame_info(vmctx_info, i, ranges) { if let Some(frame_info) = get_function_frame_info(memory_offset, funcs, i) {
let source_range = addr_tr.func_source_range(i); let source_range = addr_tr.func_source_range(i);
generate_vars( generate_vars(
unit, unit,

View File

@@ -12,7 +12,7 @@ use gimli::{AttributeValue, DebuggingInformationEntry, Unit};
use std::collections::HashSet; use std::collections::HashSet;
use wasmtime_environ::isa::TargetIsa; use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::wasm::DefinedFuncIndex; use wasmtime_environ::wasm::DefinedFuncIndex;
use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges}; use wasmtime_environ::{CompiledFunctions, ModuleMemoryOffset};
struct InheritedAttr<T> { struct InheritedAttr<T> {
stack: Vec<(usize, T)>, stack: Vec<(usize, T)>,
@@ -246,9 +246,9 @@ pub(crate) fn clone_unit<'a, R>(
unit: Unit<R, R::Offset>, unit: Unit<R, R::Offset>,
context: &DebugInputContext<R>, context: &DebugInputContext<R>,
addr_tr: &'a AddressTransform, addr_tr: &'a AddressTransform,
value_ranges: &'a ValueLabelsRanges, funcs: &'a CompiledFunctions,
memory_offset: &ModuleMemoryOffset,
out_encoding: gimli::Encoding, out_encoding: gimli::Encoding,
module_info: &ModuleVmctxInfo,
out_units: &mut write::UnitTable, out_units: &mut write::UnitTable,
out_strings: &mut write::StringTable, out_strings: &mut write::StringTable,
translated: &mut HashSet<DefinedFuncIndex>, translated: &mut HashSet<DefinedFuncIndex>,
@@ -319,7 +319,7 @@ where
)?; )?;
let (wp_die_id, vmctx_die_id) = let (wp_die_id, vmctx_die_id) =
add_internal_types(comp_unit, root_id, out_strings, module_info); add_internal_types(comp_unit, root_id, out_strings, memory_offset);
stack.push(root_id); stack.push(root_id);
( (
@@ -371,8 +371,7 @@ where
let range_builder = let range_builder =
RangeInfoBuilder::from_subprogram_die(&unit, entry, context, addr_tr, cu_low_pc)?; RangeInfoBuilder::from_subprogram_die(&unit, entry, context, addr_tr, cu_low_pc)?;
if let RangeInfoBuilder::Function(func_index) = range_builder { if let RangeInfoBuilder::Function(func_index) = range_builder {
if let Some(frame_info) = if let Some(frame_info) = get_function_frame_info(memory_offset, funcs, func_index)
get_function_frame_info(module_info, func_index, value_ranges)
{ {
current_value_range.push(new_stack_len, frame_info); current_value_range.push(new_stack_len, frame_info);
} }

View File

@@ -4,7 +4,7 @@ use anyhow::Error;
use gimli::write; use gimli::write;
use wasmtime_environ::isa::TargetIsa; use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::wasm::DefinedFuncIndex; use wasmtime_environ::wasm::DefinedFuncIndex;
use wasmtime_environ::{ModuleMemoryOffset, ModuleVmctxInfo, ValueLabelsRanges}; use wasmtime_environ::{CompiledFunctions, ModuleMemoryOffset};
/// Adds internal Wasm utility types DIEs such as WebAssemblyPtr and /// Adds internal Wasm utility types DIEs such as WebAssemblyPtr and
/// WasmtimeVMContext. /// WasmtimeVMContext.
@@ -17,7 +17,7 @@ pub(crate) fn add_internal_types(
comp_unit: &mut write::Unit, comp_unit: &mut write::Unit,
root_id: write::UnitEntryId, root_id: write::UnitEntryId,
out_strings: &mut write::StringTable, out_strings: &mut write::StringTable,
module_info: &ModuleVmctxInfo, memory_offset: &ModuleMemoryOffset,
) -> (write::UnitEntryId, write::UnitEntryId) { ) -> (write::UnitEntryId, write::UnitEntryId) {
const WASM_PTR_LEN: u8 = 4; const WASM_PTR_LEN: u8 = 4;
@@ -70,7 +70,7 @@ pub(crate) fn add_internal_types(
); );
// TODO multiple memories // TODO multiple memories
match module_info.memory_offset { match *memory_offset {
ModuleMemoryOffset::Defined(memory_offset) => { ModuleMemoryOffset::Defined(memory_offset) => {
// The context has defined memory: extend the WasmtimeVMContext size // The context has defined memory: extend the WasmtimeVMContext size
// past the "memory" field. // past the "memory" field.
@@ -165,19 +165,19 @@ pub(crate) fn append_vmctx_info(
} }
pub(crate) fn get_function_frame_info<'a, 'b, 'c>( pub(crate) fn get_function_frame_info<'a, 'b, 'c>(
module_info: &'b ModuleVmctxInfo, memory_offset: &ModuleMemoryOffset,
funcs: &'b CompiledFunctions,
func_index: DefinedFuncIndex, func_index: DefinedFuncIndex,
value_ranges: &'c ValueLabelsRanges,
) -> Option<FunctionFrameInfo<'a>> ) -> Option<FunctionFrameInfo<'a>>
where where
'b: 'a, 'b: 'a,
'c: 'a, 'c: 'a,
{ {
if let Some(value_ranges) = value_ranges.get(func_index) { if let Some(func) = funcs.get(func_index) {
let frame_info = FunctionFrameInfo { let frame_info = FunctionFrameInfo {
value_ranges, value_ranges: &func.value_labels_ranges,
memory_offset: module_info.memory_offset.clone(), memory_offset: memory_offset.clone(),
stack_slots: &module_info.stack_slots[func_index], stack_slots: &func.stack_slots,
}; };
Some(frame_info) Some(frame_info)
} else { } else {

View File

@@ -1,11 +1,9 @@
pub use crate::transform::transform_dwarf; pub use crate::transform::transform_dwarf;
use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer}; use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
use gimli::{RunTimeEndian, SectionId}; use gimli::{RunTimeEndian, SectionId};
use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::entity::EntityRef;
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa}; use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
use wasmtime_environ::wasm::DefinedFuncIndex; use wasmtime_environ::{CompiledFunctions, DebugInfoData, ModuleMemoryOffset};
use wasmtime_environ::DebugInfoData;
use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges};
#[derive(Clone)] #[derive(Clone)]
pub enum DwarfSectionRelocTarget { pub enum DwarfSectionRelocTarget {
@@ -130,16 +128,13 @@ impl Writer for WriterRelocate {
} }
} }
fn create_frame_table<'a>( fn create_frame_table<'a>(isa: &dyn TargetIsa, funcs: &CompiledFunctions) -> Option<FrameTable> {
isa: &dyn TargetIsa,
infos: &PrimaryMap<DefinedFuncIndex, &Option<UnwindInfo>>,
) -> Option<FrameTable> {
let mut table = FrameTable::default(); let mut table = FrameTable::default();
let cie_id = table.add_cie(isa.create_systemv_cie()?); let cie_id = table.add_cie(isa.create_systemv_cie()?);
for (i, info) in infos { for (i, f) in funcs {
if let Some(UnwindInfo::SystemV(info)) = info { if let Some(UnwindInfo::SystemV(info)) = &f.unwind_info {
table.add_fde( table.add_fde(
cie_id, cie_id,
info.to_fde(Address::Symbol { info.to_fde(Address::Symbol {
@@ -156,13 +151,11 @@ fn create_frame_table<'a>(
pub fn emit_dwarf<'a>( pub fn emit_dwarf<'a>(
isa: &dyn TargetIsa, isa: &dyn TargetIsa,
debuginfo_data: &DebugInfoData, debuginfo_data: &DebugInfoData,
at: &ModuleAddressMap, funcs: &CompiledFunctions,
vmctx_info: &ModuleVmctxInfo, memory_offset: &ModuleMemoryOffset,
ranges: &ValueLabelsRanges,
unwind_info: &PrimaryMap<DefinedFuncIndex, &Option<UnwindInfo>>,
) -> anyhow::Result<Vec<DwarfSection>> { ) -> anyhow::Result<Vec<DwarfSection>> {
let dwarf = transform_dwarf(isa, debuginfo_data, at, vmctx_info, ranges)?; let dwarf = transform_dwarf(isa, debuginfo_data, funcs, memory_offset)?;
let frame_table = create_frame_table(isa, unwind_info); let frame_table = create_frame_table(isa, funcs);
let sections = emit_dwarf_sections(dwarf, frame_table)?; let sections = emit_dwarf_sections(dwarf, frame_table)?;
Ok(sections) Ok(sections)
} }

View File

@@ -20,7 +20,6 @@ cranelift-wasm = { path = "../../cranelift/wasm", version = "0.66.0", features =
wasmparser = "0.59.0" wasmparser = "0.59.0"
lightbeam = { path = "../lightbeam", optional = true, version = "0.19.0" } lightbeam = { path = "../lightbeam", optional = true, version = "0.19.0" }
indexmap = { version = "1.0.2", features = ["serde-1"] } indexmap = { version = "1.0.2", features = ["serde-1"] }
rayon = { version = "1.2.1", optional = true }
thiserror = "1.0.4" thiserror = "1.0.4"
directories = "2.0.1" directories = "2.0.1"
sha2 = "0.8.0" sha2 = "0.8.0"
@@ -48,8 +47,5 @@ pretty_env_logger = "0.4.0"
filetime = "0.2.7" filetime = "0.2.7"
lazy_static = "1.3.0" lazy_static = "1.3.0"
[features]
parallel-compilation = ["rayon"]
[badges] [badges]
maintenance = { status = "actively-developed" } maintenance = { status = "actively-developed" }

View File

@@ -2,8 +2,6 @@
// addresses of a WebAssembly module into the native code. // addresses of a WebAssembly module into the native code.
use cranelift_codegen::ir; use cranelift_codegen::ir;
use cranelift_entity::PrimaryMap;
use cranelift_wasm::DefinedFuncIndex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Single source location to generated address mapping. /// Single source location to generated address mapping.
@@ -20,7 +18,7 @@ pub struct InstructionAddressMap {
} }
/// Function and its instructions addresses mappings. /// Function and its instructions addresses mappings.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub struct FunctionAddressMap { pub struct FunctionAddressMap {
/// Instructions maps. /// Instructions maps.
/// The array is sorted by the InstructionAddressMap::code_offset field. /// The array is sorted by the InstructionAddressMap::code_offset field.
@@ -39,15 +37,6 @@ pub struct FunctionAddressMap {
pub body_len: usize, pub body_len: usize,
} }
/// Module functions addresses mappings.
pub type ModuleAddressMap = PrimaryMap<DefinedFuncIndex, FunctionAddressMap>;
/// Value ranges for functions.
pub type ValueLabelsRanges = PrimaryMap<DefinedFuncIndex, cranelift_codegen::ValueLabelsRanges>;
/// Stack slots for functions.
pub type StackSlots = PrimaryMap<DefinedFuncIndex, ir::StackSlots>;
/// Memory definition offset in the VMContext structure. /// Memory definition offset in the VMContext structure.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ModuleMemoryOffset { pub enum ModuleMemoryOffset {
@@ -58,13 +47,3 @@ pub enum ModuleMemoryOffset {
/// Offset to the imported memory. /// Offset to the imported memory.
Imported(u32), Imported(u32),
} }
/// Module `vmctx` related info.
#[derive(Debug, Clone)]
pub struct ModuleVmctxInfo {
/// The memory definition offset in the VMContext structure.
pub memory_offset: ModuleMemoryOffset,
/// The functions stack slots.
pub stack_slots: StackSlots,
}

View File

@@ -1,19 +1,21 @@
//! A `Compilation` contains the compiled function bodies for a WebAssembly //! A `Compilation` contains the compiled function bodies for a WebAssembly
//! module. //! module.
use crate::address_map::{ModuleAddressMap, ValueLabelsRanges}; use crate::{FunctionAddressMap, FunctionBodyData, ModuleTranslation};
use crate::ModuleTranslation;
use cranelift_codegen::{binemit, ir, isa, isa::unwind::UnwindInfo}; use cranelift_codegen::{binemit, ir, isa, isa::unwind::UnwindInfo};
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmError}; use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::ops::Range;
use thiserror::Error; use thiserror::Error;
#[allow(missing_docs)]
pub type CompiledFunctions = PrimaryMap<DefinedFuncIndex, CompiledFunction>;
/// Compiled function: machine code body, jump table offsets, and unwind information. /// Compiled function: machine code body, jump table offsets, and unwind information.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
#[allow(missing_docs)]
pub struct CompiledFunction { pub struct CompiledFunction {
/// The function body. /// The machine code for this function.
pub body: Vec<u8>, pub body: Vec<u8>,
/// The jump tables offsets (in the body). /// The jump tables offsets (in the body).
@@ -21,93 +23,13 @@ pub struct CompiledFunction {
/// The unwind information. /// The unwind information.
pub unwind_info: Option<UnwindInfo>, pub unwind_info: Option<UnwindInfo>,
}
type Functions = PrimaryMap<DefinedFuncIndex, CompiledFunction>; pub relocations: Vec<Relocation>,
pub address_map: FunctionAddressMap,
/// The result of compiling a WebAssembly module's functions. pub value_labels_ranges: cranelift_codegen::ValueLabelsRanges,
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] pub stack_slots: ir::StackSlots,
pub struct Compilation { pub traps: Vec<TrapInformation>,
/// Compiled machine code for the function bodies. pub stack_maps: Vec<StackMapInformation>,
functions: Functions,
}
impl Compilation {
/// Creates a compilation artifact from a contiguous function buffer and a set of ranges
pub fn new(functions: Functions) -> Self {
Self { functions }
}
/// Allocates the compilation result with the given function bodies.
pub fn from_buffer(
buffer: Vec<u8>,
functions: impl IntoIterator<Item = (Range<usize>, ir::JumpTableOffsets)>,
) -> Self {
Self::new(
functions
.into_iter()
.map(|(body_range, jt_offsets)| CompiledFunction {
body: buffer[body_range].to_vec(),
jt_offsets,
unwind_info: None, // not implemented for lightbeam currently
})
.collect(),
)
}
/// Gets the bytes of a single function
pub fn get(&self, func: DefinedFuncIndex) -> &CompiledFunction {
&self.functions[func]
}
/// Gets the number of functions defined.
pub fn len(&self) -> usize {
self.functions.len()
}
/// Returns whether there are no functions defined.
pub fn is_empty(&self) -> bool {
self.functions.is_empty()
}
/// Returns unwind info for all defined functions.
pub fn unwind_info(&self) -> PrimaryMap<DefinedFuncIndex, &Option<UnwindInfo>> {
self.functions
.iter()
.map(|(_, func)| &func.unwind_info)
.collect::<PrimaryMap<DefinedFuncIndex, _>>()
}
/// Gets functions jump table offsets.
pub fn get_jt_offsets(&self) -> PrimaryMap<DefinedFuncIndex, ir::JumpTableOffsets> {
self.functions
.iter()
.map(|(_, func)| func.jt_offsets.clone())
.collect::<PrimaryMap<DefinedFuncIndex, _>>()
}
}
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 CompiledFunction;
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.
@@ -134,9 +56,6 @@ pub enum RelocationTarget {
JumpTable(FuncIndex, ir::JumpTable), JumpTable(FuncIndex, ir::JumpTable),
} }
/// Relocations to apply to function bodies.
pub type Relocations = PrimaryMap<DefinedFuncIndex, Vec<Relocation>>;
/// Information about trap. /// Information about trap.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct TrapInformation { pub struct TrapInformation {
@@ -148,9 +67,6 @@ pub struct TrapInformation {
pub trap_code: ir::TrapCode, pub trap_code: ir::TrapCode,
} }
/// Information about traps associated with the functions where the traps are placed.
pub type Traps = PrimaryMap<DefinedFuncIndex, Vec<TrapInformation>>;
/// The offset within a function of a GC safepoint, and its associated stack /// The offset within a function of a GC safepoint, and its associated stack
/// map. /// map.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
@@ -163,10 +79,6 @@ pub struct StackMapInformation {
pub stack_map: binemit::Stackmap, pub stack_map: binemit::Stackmap,
} }
/// Information about GC safepoints and their associated stack maps within each
/// function.
pub type StackMaps = PrimaryMap<DefinedFuncIndex, Vec<StackMapInformation>>;
/// An error while compiling WebAssembly to machine code. /// An error while compiling WebAssembly to machine code.
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum CompileError { pub enum CompileError {
@@ -183,22 +95,15 @@ pub enum CompileError {
DebugInfoNotSupported, DebugInfoNotSupported,
} }
/// A type alias for the result of `Compiler::compile_module` /// An implementation of a compiler from parsed WebAssembly module to native
pub type CompileResult = ( /// code.
Compilation, pub trait Compiler: Send + Sync {
Relocations, /// Compile a function with the given `TargetIsa`.
ModuleAddressMap, fn compile_function(
ValueLabelsRanges, &self,
PrimaryMap<DefinedFuncIndex, ir::StackSlots>, translation: &ModuleTranslation<'_>,
Traps, index: DefinedFuncIndex,
StackMaps, data: &FunctionBodyData<'_>,
);
/// 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(
translation: &ModuleTranslation,
isa: &dyn isa::TargetIsa, isa: &dyn isa::TargetIsa,
) -> Result<CompileResult, CompileError>; ) -> Result<CompiledFunction, CompileError>;
} }

View File

@@ -85,23 +85,21 @@
// also need to actually catch stack overflow, so for now 32k is chosen and it's // 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`. // assume no valid stack pointer will ever be `usize::max_value() - 32k`.
use crate::address_map::{FunctionAddressMap, InstructionAddressMap}; use crate::func_environ::{get_func_name, FuncEnvironment};
use crate::compilation::{ use crate::Compiler;
Compilation, CompileError, CompiledFunction, Relocation, RelocationTarget, StackMapInformation, use crate::{
CompileError, CompiledFunction, Relocation, RelocationTarget, StackMapInformation,
TrapInformation, TrapInformation,
}; };
use crate::compilation::{CompileResult, Compiler}; use crate::{FunctionAddressMap, InstructionAddressMap};
use crate::func_environ::{get_func_name, FuncEnvironment}; use crate::{FunctionBodyData, ModuleTranslation};
use crate::{FunctionBodyData, ModuleLocal, ModuleTranslation, Tunables};
use cranelift_codegen::ir::{self, ExternalName}; use cranelift_codegen::ir::{self, ExternalName};
use cranelift_codegen::machinst::buffer::MachSrcLoc; use cranelift_codegen::machinst::buffer::MachSrcLoc;
use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::print_errors::pretty_error;
use cranelift_codegen::{binemit, isa, Context}; use cranelift_codegen::{binemit, isa, Context};
use cranelift_entity::PrimaryMap; use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator};
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator, ModuleTranslationState};
#[cfg(feature = "parallel-compilation")]
use rayon::prelude::{IntoParallelRefIterator, ParallelIterator};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::sync::Mutex;
/// Implementation of a relocation sink that just saves all the information for later /// Implementation of a relocation sink that just saves all the information for later
pub struct RelocSink { pub struct RelocSink {
@@ -280,45 +278,33 @@ fn get_function_address_map<'data>(
/// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR, /// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR,
/// optimizing it and then translating to assembly. /// optimizing it and then translating to assembly.
pub struct Cranelift; #[derive(Default)]
pub struct Cranelift {
translators: Mutex<Vec<FuncTranslator>>,
}
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 { impl Compiler for Cranelift {
/// Compile the module using Cranelift, producing a compilation result with fn compile_function(
/// associated relocations. &self,
fn compile_module( translation: &ModuleTranslation<'_>,
translation: &ModuleTranslation, func_index: DefinedFuncIndex,
input: &FunctionBodyData<'_>,
isa: &dyn isa::TargetIsa, isa: &dyn isa::TargetIsa,
) -> Result<CompileResult, CompileError> { ) -> Result<CompiledFunction, CompileError> {
compile( let local = &translation.module.local;
isa, let tunables = &translation.tunables;
&translation.module.local, let func_index = local.func_index(func_index);
translation.module_translation.as_ref().unwrap(),
&translation.function_body_inputs,
&translation.tunables,
)
}
}
fn compile(
isa: &dyn isa::TargetIsa,
local: &ModuleLocal,
module_translation: &ModuleTranslationState,
function_body_inputs: &PrimaryMap<DefinedFuncIndex, FunctionBodyData<'_>>,
tunables: &Tunables,
) -> Result<CompileResult, CompileError> {
let mut functions = 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 value_ranges = PrimaryMap::with_capacity(function_body_inputs.len());
let mut stack_slots = PrimaryMap::with_capacity(function_body_inputs.len());
let mut traps = PrimaryMap::with_capacity(function_body_inputs.len());
let mut stack_maps = PrimaryMap::with_capacity(function_body_inputs.len());
type FunctionBodyInput<'a> = (DefinedFuncIndex, &'a FunctionBodyData<'a>);
let compile_function = |func_translator: &mut FuncTranslator,
(i, input): &FunctionBodyInput| {
let func_index = local.func_index(*i);
let mut context = Context::new(); let mut context = Context::new();
context.func.name = get_func_name(func_index); context.func.name = get_func_name(func_index);
context.func.signature = local.native_func_signature(func_index).clone(); context.func.signature = local.native_func_signature(func_index).clone();
@@ -361,13 +347,16 @@ fn compile(
readonly: false, readonly: false,
}); });
context.func.stack_limit = Some(stack_limit); context.func.stack_limit = Some(stack_limit);
func_translator.translate( let mut func_translator = self.take_translator();
module_translation, let result = func_translator.translate(
translation.module_translation.as_ref().unwrap(),
input.data, input.data,
input.module_offset, input.module_offset,
&mut context.func, &mut context.func,
&mut func_env, &mut func_env,
)?; );
self.save_translator(func_translator);
result?;
let mut code_buf: Vec<u8> = Vec::new(); let mut code_buf: Vec<u8> = Vec::new();
let mut reloc_sink = RelocSink::new(func_index); let mut reloc_sink = RelocSink::new(func_index);
@@ -400,73 +389,16 @@ fn compile(
None None
}; };
Ok(( Ok(CompiledFunction {
code_buf, body: code_buf,
context.func.jt_offsets, jt_offsets: context.func.jt_offsets,
reloc_sink.func_relocs, relocations: reloc_sink.func_relocs,
address_transform, address_map: address_transform,
ranges, value_labels_ranges: ranges.unwrap_or(Default::default()),
context.func.stack_slots, stack_slots: context.func.stack_slots,
trap_sink.traps, traps: trap_sink.traps,
unwind_info, unwind_info,
stack_map_sink.finish(), stack_maps: stack_map_sink.finish(),
)) })
};
let inputs: Vec<FunctionBodyInput> = function_body_inputs.into_iter().collect();
let results: Result<Vec<_>, CompileError> = {
cfg_if::cfg_if! {
if #[cfg(feature = "parallel-compilation")] {
inputs
.par_iter()
.map_init(FuncTranslator::new, compile_function)
.collect()
} else {
let mut func_translator = FuncTranslator::new();
inputs
.iter()
.map(|input| compile_function(&mut func_translator, input))
.collect()
} }
} }
};
results?.into_iter().for_each(
|(
function,
func_jt_offsets,
relocs,
address_transform,
ranges,
sss,
function_traps,
unwind_info,
stack_map,
)| {
functions.push(CompiledFunction {
body: function,
jt_offsets: func_jt_offsets,
unwind_info,
});
relocations.push(relocs);
address_transforms.push(address_transform);
value_ranges.push(ranges.unwrap_or_default());
stack_slots.push(sss);
traps.push(function_traps);
stack_maps.push(stack_map);
},
);
// TODO: Reorganize where we create the Vec for the resolved imports.
Ok((
Compilation::new(functions),
relocations,
address_transforms,
value_ranges,
stack_slots,
traps,
stack_maps,
))
}

View File

@@ -39,16 +39,10 @@ pub mod cranelift;
#[cfg(feature = "lightbeam")] #[cfg(feature = "lightbeam")]
pub mod lightbeam; pub mod lightbeam;
pub use crate::address_map::{ pub use crate::address_map::*;
FunctionAddressMap, InstructionAddressMap, ModuleAddressMap, ModuleMemoryOffset,
ModuleVmctxInfo, ValueLabelsRanges,
};
pub use crate::cache::create_new_config as cache_create_new_config; pub use crate::cache::create_new_config as cache_create_new_config;
pub use crate::cache::{CacheConfig, ModuleCacheEntry}; pub use crate::cache::{CacheConfig, ModuleCacheEntry};
pub use crate::compilation::{ pub use crate::compilation::*;
Compilation, CompileError, CompiledFunction, Compiler, Relocation, RelocationTarget,
Relocations, StackMapInformation, StackMaps, TrapInformation, Traps,
};
pub use crate::cranelift::Cranelift; pub use crate::cranelift::Cranelift;
pub use crate::data_structures::*; pub use crate::data_structures::*;
pub use crate::func_environ::BuiltinFunctionIndex; pub use crate::func_environ::BuiltinFunctionIndex;

View File

@@ -1,48 +1,40 @@
//! Support for compiling with Lightbeam. //! Support for compiling with Lightbeam.
use crate::compilation::{Compilation, CompileError, CompileResult, Compiler}; use crate::compilation::{CompileError, CompiledFunction, Compiler};
use crate::func_environ::FuncEnvironment;
use crate::CacheConfig;
use crate::ModuleTranslation;
// TODO: Put this in `compilation`
use crate::address_map::{ModuleAddressMap, ValueLabelsRanges};
use crate::cranelift::{RelocSink, TrapSink}; use crate::cranelift::{RelocSink, TrapSink};
use crate::func_environ::FuncEnvironment;
use crate::{FunctionBodyData, ModuleTranslation};
use cranelift_codegen::isa; use cranelift_codegen::isa;
use cranelift_entity::{PrimaryMap, SecondaryMap}; use cranelift_wasm::DefinedFuncIndex;
use lightbeam::{CodeGenSession, NullOffsetSink, Sinks}; use lightbeam::{CodeGenSession, NullOffsetSink, Sinks};
/// A compiler that compiles a WebAssembly module with Lightbeam, directly translating the Wasm file. /// A compiler that compiles a WebAssembly module with Lightbeam, directly translating the Wasm file.
pub struct Lightbeam; pub struct Lightbeam;
impl Compiler for Lightbeam { impl Compiler for Lightbeam {
/// Compile the module using Lightbeam, producing a compilation result with fn compile_function(
/// associated relocations. &self,
fn compile_module(
translation: &ModuleTranslation, translation: &ModuleTranslation,
i: DefinedFuncIndex,
function_body: &FunctionBodyData<'_>,
isa: &dyn isa::TargetIsa, isa: &dyn isa::TargetIsa,
) -> Result<CompileResult, CompileError> { ) -> Result<CompiledFunction, CompileError> {
if translation.tunables.debug_info { if translation.tunables.debug_info {
return Err(CompileError::DebugInfoNotSupported); return Err(CompileError::DebugInfoNotSupported);
} }
let func_index = translation.module.local.func_index(i);
let env = FuncEnvironment::new( let env = FuncEnvironment::new(
isa.frontend_config(), isa.frontend_config(),
&translation.module.local, &translation.module.local,
&translation.tunables, &translation.tunables,
); );
let mut relocations = PrimaryMap::with_capacity(translation.function_body_inputs.len());
let mut traps = PrimaryMap::with_capacity(translation.function_body_inputs.len());
let stack_maps = PrimaryMap::with_capacity(translation.function_body_inputs.len());
let mut codegen_session: CodeGenSession<_> = CodeGenSession::new( let mut codegen_session: CodeGenSession<_> = CodeGenSession::new(
translation.function_body_inputs.len() as u32, translation.function_body_inputs.len() as u32,
&env, &env,
lightbeam::microwasm::I32, lightbeam::microwasm::I32,
); );
for (i, function_body) in &translation.function_body_inputs {
let func_index = translation.module.local.func_index(i);
let mut reloc_sink = RelocSink::new(func_index); let mut reloc_sink = RelocSink::new(func_index);
let mut trap_sink = TrapSink::new(); let mut trap_sink = TrapSink::new();
lightbeam::translate_function( lightbeam::translate_function(
@@ -57,30 +49,23 @@ impl Compiler for Lightbeam {
) )
.map_err(|e| CompileError::Codegen(format!("Failed to translate function: {}", e)))?; .map_err(|e| CompileError::Codegen(format!("Failed to translate function: {}", e)))?;
relocations.push(reloc_sink.func_relocs);
traps.push(trap_sink.traps);
}
let code_section = codegen_session let code_section = codegen_session
.into_translated_code_section() .into_translated_code_section()
.map_err(|e| CompileError::Codegen(format!("Failed to generate output code: {}", e)))?; .map_err(|e| CompileError::Codegen(format!("Failed to generate output code: {}", e)))?;
// TODO pass jump table offsets to Compilation::from_buffer() when they Ok(CompiledFunction {
// are implemented in lightbeam -- using empty set of offsets for now. // TODO: try to remove copy here (?)
// TODO: pass an empty range for the unwind information until lightbeam emits it body: code_section.buffer().to_vec(),
let code_section_ranges_and_jt = code_section traps: trap_sink.traps,
.funcs() relocations: reloc_sink.func_relocs,
.into_iter()
.map(|r| (r, SecondaryMap::new()));
Ok(( // not implemented for lightbeam currently
Compilation::from_buffer(code_section.buffer().to_vec(), code_section_ranges_and_jt), unwind_info: None,
relocations, stack_maps: Default::default(),
ModuleAddressMap::new(), stack_slots: Default::default(),
ValueLabelsRanges::new(), value_labels_ranges: Default::default(),
PrimaryMap::new(), address_map: Default::default(),
traps, jt_offsets: Default::default(),
stack_maps, })
))
} }
} }

View File

@@ -22,6 +22,7 @@ wasmtime-runtime = { path = "../runtime", version = "0.19.0" }
wasmtime-debug = { path = "../debug", version = "0.19.0" } wasmtime-debug = { path = "../debug", version = "0.19.0" }
wasmtime-profiling = { path = "../profiling", version = "0.19.0" } wasmtime-profiling = { path = "../profiling", version = "0.19.0" }
wasmtime-obj = { path = "../obj", version = "0.19.0" } wasmtime-obj = { path = "../obj", version = "0.19.0" }
rayon = { version = "1.0", optional = true }
region = "2.1.0" region = "2.1.0"
thiserror = "1.0.4" thiserror = "1.0.4"
target-lexicon = { version = "0.10.0", default-features = false } target-lexicon = { version = "0.10.0", default-features = false }
@@ -41,6 +42,7 @@ winapi = { version = "0.3.8", features = ["winnt", "impl-default"] }
lightbeam = ["wasmtime-environ/lightbeam"] lightbeam = ["wasmtime-environ/lightbeam"]
jitdump = ["wasmtime-profiling/jitdump"] jitdump = ["wasmtime-profiling/jitdump"]
vtune = ["wasmtime-profiling/vtune"] vtune = ["wasmtime-profiling/vtune"]
parallel-compilation = ["rayon"]
# Try the experimental, work-in-progress new x86_64 backend. This is not stable # Try the experimental, work-in-progress new x86_64 backend. This is not stable
# as of June 2020. # as of June 2020.

View File

@@ -13,7 +13,7 @@ use std::{cmp, mem};
use wasmtime_environ::{ use wasmtime_environ::{
isa::{unwind::UnwindInfo, TargetIsa}, isa::{unwind::UnwindInfo, TargetIsa},
wasm::{FuncIndex, SignatureIndex}, wasm::{FuncIndex, SignatureIndex},
Compilation, CompiledFunction, CompiledFunction,
}; };
use wasmtime_runtime::{Mmap, VMFunctionBody}; use wasmtime_runtime::{Mmap, VMFunctionBody};
@@ -119,31 +119,6 @@ impl CodeMemory {
Ok(vmfunc) Ok(vmfunc)
} }
/// Allocate a continuous memory block for a compilation.
pub fn allocate_for_compilation(
&mut self,
compilation: &Compilation,
) -> Result<Box<[&mut [VMFunctionBody]]>, String> {
let total_len = compilation
.into_iter()
.fold(0, |acc, func| acc + Self::function_allocation_size(func));
let (mut buf, registry, start) = self.allocate(total_len)?;
let mut result = Vec::with_capacity(compilation.len());
let mut start = start as u32;
for func in compilation.into_iter() {
let (next_start, next_buf, vmfunc) = Self::copy_function(func, start, buf, registry);
result.push(vmfunc);
start = next_start;
buf = next_buf;
}
Ok(result.into_boxed_slice())
}
/// Make all allocated memory executable. /// Make all allocated memory executable.
pub fn publish(&mut self, isa: &dyn TargetIsa) { pub fn publish(&mut self, isa: &dyn TargetIsa) {
self.push_current(0) self.push_current(0)

View File

@@ -2,16 +2,15 @@
use crate::instantiate::SetupError; use crate::instantiate::SetupError;
use crate::object::{build_object, ObjectUnwindInfo}; use crate::object::{build_object, ObjectUnwindInfo};
use cranelift_codegen::ir;
use object::write::Object; use object::write::Object;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use wasmtime_debug::{emit_dwarf, DwarfSection}; use wasmtime_debug::{emit_dwarf, DwarfSection};
use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::entity::EntityRef;
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetFrontendConfig, TargetIsa}; use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa};
use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex, MemoryIndex}; use wasmtime_environ::wasm::{DefinedMemoryIndex, MemoryIndex};
use wasmtime_environ::{ use wasmtime_environ::{
Compiler as _C, DebugInfoData, Module, ModuleAddressMap, ModuleMemoryOffset, ModuleTranslation, CompiledFunctions, Compiler as EnvCompiler, DebugInfoData, Module, ModuleMemoryOffset,
ModuleVmctxInfo, StackMaps, Traps, Tunables, VMOffsets, ValueLabelsRanges, ModuleTranslation, Tunables, VMOffsets,
}; };
/// Select which kind of compilation to use. /// Select which kind of compilation to use.
@@ -38,6 +37,7 @@ pub enum CompilationStrategy {
/// TODO: Consider using cranelift-module. /// TODO: Consider using cranelift-module.
pub struct Compiler { pub struct Compiler {
isa: Box<dyn TargetIsa>, isa: Box<dyn TargetIsa>,
compiler: Box<dyn EnvCompiler>,
strategy: CompilationStrategy, strategy: CompilationStrategy,
tunables: Tunables, tunables: Tunables,
} }
@@ -48,6 +48,13 @@ impl Compiler {
Self { Self {
isa, isa,
strategy, strategy,
compiler: match strategy {
CompilationStrategy::Auto | CompilationStrategy::Cranelift => {
Box::new(wasmtime_environ::cranelift::Cranelift::default())
}
#[cfg(feature = "lightbeam")]
CompilationStrategy::Lightbeam => Box::new(wasmtime_environ::lightbeam::Lightbeam),
},
tunables, tunables,
} }
} }
@@ -62,46 +69,26 @@ fn transform_dwarf_data(
isa: &dyn TargetIsa, isa: &dyn TargetIsa,
module: &Module, module: &Module,
debug_data: &DebugInfoData, debug_data: &DebugInfoData,
address_transform: &ModuleAddressMap, funcs: &CompiledFunctions,
value_ranges: &ValueLabelsRanges,
stack_slots: PrimaryMap<DefinedFuncIndex, ir::StackSlots>,
unwind_info: PrimaryMap<DefinedFuncIndex, &Option<UnwindInfo>>,
) -> Result<Vec<DwarfSection>, SetupError> { ) -> Result<Vec<DwarfSection>, SetupError> {
let target_config = isa.frontend_config(); let target_config = isa.frontend_config();
let ofs = VMOffsets::new(target_config.pointer_bytes(), &module.local); let ofs = VMOffsets::new(target_config.pointer_bytes(), &module.local);
let module_vmctx_info = { let memory_offset = if ofs.num_imported_memories > 0 {
ModuleVmctxInfo {
memory_offset: if ofs.num_imported_memories > 0 {
ModuleMemoryOffset::Imported(ofs.vmctx_vmmemory_import(MemoryIndex::new(0))) ModuleMemoryOffset::Imported(ofs.vmctx_vmmemory_import(MemoryIndex::new(0)))
} else if ofs.num_defined_memories > 0 { } else if ofs.num_defined_memories > 0 {
ModuleMemoryOffset::Defined( ModuleMemoryOffset::Defined(ofs.vmctx_vmmemory_definition_base(DefinedMemoryIndex::new(0)))
ofs.vmctx_vmmemory_definition_base(DefinedMemoryIndex::new(0)),
)
} else { } else {
ModuleMemoryOffset::None ModuleMemoryOffset::None
},
stack_slots,
}
}; };
emit_dwarf( emit_dwarf(isa, debug_data, funcs, &memory_offset).map_err(SetupError::DebugInfo)
isa,
debug_data,
&address_transform,
&module_vmctx_info,
&value_ranges,
&unwind_info,
)
.map_err(SetupError::DebugInfo)
} }
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct Compilation { pub struct Compilation {
pub obj: Object, pub obj: Object,
pub unwind_info: Vec<ObjectUnwindInfo>, pub unwind_info: Vec<ObjectUnwindInfo>,
pub traps: Traps, pub funcs: CompiledFunctions,
pub stack_maps: StackMaps,
pub address_transform: ModuleAddressMap,
} }
impl Compiler { impl Compiler {
@@ -120,66 +107,49 @@ impl Compiler {
&self.tunables &self.tunables
} }
/// Return the compilation strategy.
pub fn strategy(&self) -> CompilationStrategy {
self.strategy
}
/// Compile the given function bodies. /// Compile the given function bodies.
pub(crate) fn compile<'data>( pub fn compile<'data>(
&self, &self,
translation: &ModuleTranslation, translation: &ModuleTranslation,
) -> Result<Compilation, SetupError> { ) -> Result<Compilation, SetupError> {
let ( cfg_if::cfg_if! {
compilation, if #[cfg(feature = "parallel-compilation")] {
relocations, use rayon::prelude::*;
address_transform, let iter = translation.function_body_inputs
value_ranges, .iter()
stack_slots, .collect::<Vec<_>>()
traps, .into_par_iter();
stack_maps, } else {
) = match self.strategy { let iter = translation.function_body_inputs.iter();
// For now, interpret `Auto` as `Cranelift` since that's the most stable
// implementation.
CompilationStrategy::Auto | CompilationStrategy::Cranelift => {
wasmtime_environ::cranelift::Cranelift::compile_module(translation, &*self.isa)
}
#[cfg(feature = "lightbeam")]
CompilationStrategy::Lightbeam => {
wasmtime_environ::lightbeam::Lightbeam::compile_module(translation, &*self.isa)
} }
} }
.map_err(SetupError::Compile)?; let funcs = iter
.map(|(index, func)| {
self.compiler
.compile_function(translation, index, func, &*self.isa)
})
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.collect::<CompiledFunctions>();
let dwarf_sections = if translation.debuginfo.is_some() && !compilation.is_empty() { let dwarf_sections = if translation.debuginfo.is_some() && !funcs.is_empty() {
let unwind_info = compilation.unwind_info();
transform_dwarf_data( transform_dwarf_data(
&*self.isa, &*self.isa,
&translation.module, &translation.module,
translation.debuginfo.as_ref().unwrap(), translation.debuginfo.as_ref().unwrap(),
&address_transform, &funcs,
&value_ranges,
stack_slots,
unwind_info,
)? )?
} else { } else {
vec![] vec![]
}; };
let (obj, unwind_info) = build_object( let (obj, unwind_info) =
&*self.isa, build_object(&*self.isa, &translation.module, &funcs, dwarf_sections)?;
&translation.module,
compilation,
relocations,
dwarf_sections,
)?;
Ok(Compilation { Ok(Compilation {
obj, obj,
unwind_info, unwind_info,
traps, funcs,
stack_maps,
address_transform,
}) })
} }
} }
@@ -188,6 +158,7 @@ impl Hash for Compiler {
fn hash<H: Hasher>(&self, hasher: &mut H) { fn hash<H: Hasher>(&self, hasher: &mut H) {
let Compiler { let Compiler {
strategy, strategy,
compiler: _,
isa, isa,
tunables, tunables,
} = self; } = self;

View File

@@ -20,8 +20,8 @@ use wasmtime_environ::entity::{BoxedSlice, PrimaryMap};
use wasmtime_environ::isa::TargetIsa; use wasmtime_environ::isa::TargetIsa;
use wasmtime_environ::wasm::{DefinedFuncIndex, SignatureIndex}; use wasmtime_environ::wasm::{DefinedFuncIndex, SignatureIndex};
use wasmtime_environ::{ use wasmtime_environ::{
CompileError, DataInitializer, DataInitializerLocation, Module, ModuleAddressMap, CompileError, DataInitializer, DataInitializerLocation, FunctionAddressMap, Module,
ModuleEnvironment, ModuleTranslation, StackMaps, Traps, ModuleEnvironment, ModuleTranslation, StackMapInformation, TrapInformation,
}; };
use wasmtime_profiling::ProfilingAgent; use wasmtime_profiling::ProfilingAgent;
use wasmtime_runtime::VMInterrupts; use wasmtime_runtime::VMInterrupts;
@@ -67,19 +67,20 @@ pub struct CompilationArtifacts {
/// Data initiailizers. /// Data initiailizers.
data_initializers: Box<[OwnedDataInitializer]>, data_initializers: Box<[OwnedDataInitializer]>,
/// Traps descriptors. /// Descriptions of compiled functions
traps: Traps, funcs: PrimaryMap<DefinedFuncIndex, FunctionInfo>,
/// Stack map descriptors.
stack_maps: StackMaps,
/// Wasm to function code address map.
address_transform: ModuleAddressMap,
/// Debug info presence flags. /// Debug info presence flags.
debug_info: bool, debug_info: bool,
} }
#[derive(Serialize, Deserialize, Clone)]
struct FunctionInfo {
traps: Vec<TrapInformation>,
address_map: FunctionAddressMap,
stack_maps: Vec<StackMapInformation>,
}
impl CompilationArtifacts { impl CompilationArtifacts {
/// Builds compilation artifacts. /// Builds compilation artifacts.
pub fn build(compiler: &Compiler, data: &[u8]) -> Result<Self, SetupError> { pub fn build(compiler: &Compiler, data: &[u8]) -> Result<Self, SetupError> {
@@ -92,9 +93,7 @@ impl CompilationArtifacts {
let Compilation { let Compilation {
obj, obj,
unwind_info, unwind_info,
traps, funcs,
stack_maps,
address_transform,
} = compiler.compile(&translation)?; } = compiler.compile(&translation)?;
let ModuleTranslation { let ModuleTranslation {
@@ -120,9 +119,14 @@ impl CompilationArtifacts {
obj: obj.into_boxed_slice(), obj: obj.into_boxed_slice(),
unwind_info: unwind_info.into_boxed_slice(), unwind_info: unwind_info.into_boxed_slice(),
data_initializers, data_initializers,
traps, funcs: funcs
stack_maps, .into_iter()
address_transform, .map(|(_, func)| FunctionInfo {
stack_maps: func.stack_maps,
traps: func.traps,
address_map: func.address_map,
})
.collect(),
debug_info: compiler.tunables().debug_info, debug_info: compiler.tunables().debug_info,
}) })
} }
@@ -147,9 +151,7 @@ pub struct CompiledModule {
finished_functions: FinishedFunctions, finished_functions: FinishedFunctions,
trampolines: PrimaryMap<SignatureIndex, VMTrampoline>, trampolines: PrimaryMap<SignatureIndex, VMTrampoline>,
data_initializers: Box<[OwnedDataInitializer]>, data_initializers: Box<[OwnedDataInitializer]>,
traps: Traps, funcs: PrimaryMap<DefinedFuncIndex, FunctionInfo>,
stack_maps: StackMaps,
address_transform: ModuleAddressMap,
obj: Box<[u8]>, obj: Box<[u8]>,
unwind_info: Box<[ObjectUnwindInfo]>, unwind_info: Box<[ObjectUnwindInfo]>,
} }
@@ -176,9 +178,7 @@ impl CompiledModule {
obj, obj,
unwind_info, unwind_info,
data_initializers, data_initializers,
traps, funcs,
stack_maps,
address_transform,
debug_info, debug_info,
} = artifacts; } = artifacts;
@@ -216,9 +216,7 @@ impl CompiledModule {
finished_functions, finished_functions,
trampolines, trampolines,
data_initializers, data_initializers,
traps, funcs,
stack_maps,
address_transform,
obj, obj,
unwind_info, unwind_info,
}) })
@@ -231,9 +229,7 @@ impl CompiledModule {
obj: self.obj.clone(), obj: self.obj.clone(),
unwind_info: self.unwind_info.clone(), unwind_info: self.unwind_info.clone(),
data_initializers: self.data_initializers.clone(), data_initializers: self.data_initializers.clone(),
traps: self.traps.clone(), funcs: self.funcs.clone(),
stack_maps: self.stack_maps.clone(),
address_transform: self.address_transform.clone(),
debug_info: self.code.dbg_jit_registration.is_some(), debug_info: self.code.dbg_jit_registration.is_some(),
} }
} }
@@ -318,19 +314,36 @@ impl CompiledModule {
&self.finished_functions.0 &self.finished_functions.0
} }
/// Returns the map for all traps in this module. /// Returns the stack map information for all functions defined in this
pub fn traps(&self) -> &Traps { /// module.
&self.traps ///
/// The iterator returned iterates over the span of the compiled function in
/// memory with the stack maps associated with those bytes.
pub fn stack_maps(
&self,
) -> impl Iterator<Item = (*mut [VMFunctionBody], &[StackMapInformation])> {
self.finished_functions()
.values()
.copied()
.zip(self.funcs.values().map(|f| f.stack_maps.as_slice()))
} }
/// Returns the map for each of this module's stack maps. /// Iterates over all functions in this module, returning information about
pub fn stack_maps(&self) -> &StackMaps { /// how to decode traps which happen in the function.
&self.stack_maps pub fn trap_information(
} &self,
) -> impl Iterator<
/// Returns a map of compiled addresses back to original bytecode offsets. Item = (
pub fn address_transform(&self) -> &ModuleAddressMap { DefinedFuncIndex,
&self.address_transform *mut [VMFunctionBody],
&[TrapInformation],
&FunctionAddressMap,
),
> {
self.finished_functions()
.iter()
.zip(self.funcs.values())
.map(|((i, alloc), func)| (i, *alloc, func.traps.as_slice(), &func.address_map))
} }
/// Returns all ranges convered by JIT code. /// Returns all ranges convered by JIT code.

View File

@@ -5,10 +5,10 @@ use cranelift_frontend::FunctionBuilderContext;
use object::write::Object; use object::write::Object;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use wasmtime_debug::DwarfSection; use wasmtime_debug::DwarfSection;
use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::entity::PrimaryMap;
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa}; use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
use wasmtime_environ::wasm::{FuncIndex, SignatureIndex}; use wasmtime_environ::wasm::{FuncIndex, SignatureIndex};
use wasmtime_environ::{Compilation, Module, Relocations}; use wasmtime_environ::{CompiledFunctions, Module};
use wasmtime_obj::{ObjectBuilder, ObjectBuilderTarget}; use wasmtime_obj::{ObjectBuilder, ObjectBuilderTarget};
pub use wasmtime_obj::utils; pub use wasmtime_obj::utils;
@@ -24,8 +24,7 @@ pub enum ObjectUnwindInfo {
pub(crate) fn build_object( pub(crate) fn build_object(
isa: &dyn TargetIsa, isa: &dyn TargetIsa,
module: &Module, module: &Module,
compilation: Compilation, funcs: &CompiledFunctions,
relocations: Relocations,
dwarf_sections: Vec<DwarfSection>, dwarf_sections: Vec<DwarfSection>,
) -> Result<(Object, Vec<ObjectUnwindInfo>), anyhow::Error> { ) -> Result<(Object, Vec<ObjectUnwindInfo>), anyhow::Error> {
const CODE_SECTION_ALIGNMENT: u64 = 0x1000; const CODE_SECTION_ALIGNMENT: u64 = 0x1000;
@@ -37,38 +36,28 @@ pub(crate) fn build_object(
let mut unwind_info = Vec::new(); let mut unwind_info = Vec::new();
// Preserve function unwind info. // Preserve function unwind info.
unwind_info.extend( unwind_info.extend(funcs.iter().filter_map(|(index, func)| {
compilation func.unwind_info
.into_iter() .as_ref()
.enumerate() .map(|info| ObjectUnwindInfo::Func(module.local.func_index(index), info.clone()))
.filter_map(|(index, func)| { }));
func.unwind_info.as_ref().map(|info| {
ObjectUnwindInfo::Func(
FuncIndex::new(module.local.num_imported_funcs + index),
info.clone(),
)
})
}),
);
let mut trampolines = PrimaryMap::with_capacity(module.local.signatures.len()); let mut trampolines = PrimaryMap::with_capacity(module.local.signatures.len());
let mut cx = FunctionBuilderContext::new(); let mut cx = FunctionBuilderContext::new();
// Build trampolines for every signature. // Build trampolines for every signature.
for (i, (_, native_sig)) in module.local.signatures.iter() { for (i, (_, native_sig)) in module.local.signatures.iter() {
let (func, relocs) = let func = build_trampoline(isa, &mut cx, native_sig, std::mem::size_of::<u128>())?;
build_trampoline(isa, &mut cx, native_sig, std::mem::size_of::<u128>())?;
// Preserve trampoline function unwind info. // Preserve trampoline function unwind info.
if let Some(info) = &func.unwind_info { if let Some(info) = &func.unwind_info {
unwind_info.push(ObjectUnwindInfo::Trampoline(i, info.clone())) unwind_info.push(ObjectUnwindInfo::Trampoline(i, info.clone()))
} }
trampolines.push((func, relocs)); trampolines.push(func);
} }
let target = ObjectBuilderTarget::new(isa.triple().architecture)?; let target = ObjectBuilderTarget::new(isa.triple().architecture)?;
let mut builder = ObjectBuilder::new(target, module); let mut builder = ObjectBuilder::new(target, module, funcs);
builder builder
.set_code_alignment(CODE_SECTION_ALIGNMENT) .set_code_alignment(CODE_SECTION_ALIGNMENT)
.set_compilation(compilation, relocations)
.set_trampolines(trampolines) .set_trampolines(trampolines)
.set_dwarf_sections(dwarf_sections); .set_dwarf_sections(dwarf_sections);
let obj = builder.build()?; let obj = builder.build()?;

View File

@@ -33,9 +33,9 @@ pub fn make_trampoline(
signature: &ir::Signature, signature: &ir::Signature,
value_size: usize, value_size: usize,
) -> Result<VMTrampoline, SetupError> { ) -> Result<VMTrampoline, SetupError> {
let (compiled_function, relocs) = build_trampoline(isa, fn_builder_ctx, signature, value_size)?; let compiled_function = build_trampoline(isa, fn_builder_ctx, signature, value_size)?;
assert!(relocs.is_empty()); assert!(compiled_function.relocations.is_empty());
let ptr = code_memory let ptr = code_memory
.allocate_for_function(&compiled_function) .allocate_for_function(&compiled_function)
.map_err(|message| SetupError::Instantiate(InstantiationError::Resource(message)))? .map_err(|message| SetupError::Instantiate(InstantiationError::Resource(message)))?
@@ -48,7 +48,7 @@ pub(crate) fn build_trampoline(
fn_builder_ctx: &mut FunctionBuilderContext, fn_builder_ctx: &mut FunctionBuilderContext,
signature: &ir::Signature, signature: &ir::Signature,
value_size: usize, value_size: usize,
) -> Result<(CompiledFunction, Vec<Relocation>), SetupError> { ) -> Result<CompiledFunction, SetupError> {
let pointer_type = isa.pointer_type(); let pointer_type = isa.pointer_type();
let mut wrapper_sig = ir::Signature::new(isa.frontend_config().default_call_conv); let mut wrapper_sig = ir::Signature::new(isa.frontend_config().default_call_conv);
@@ -155,14 +155,17 @@ pub(crate) fn build_trampoline(
))) )))
})?; })?;
Ok(( Ok(CompiledFunction {
CompiledFunction {
body: code_buf, body: code_buf,
jt_offsets: context.func.jt_offsets, jt_offsets: context.func.jt_offsets,
unwind_info, unwind_info,
}, relocations: reloc_sink.relocs,
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 /// We don't expect trampoline compilation to produce many relocations, so

View File

@@ -28,12 +28,10 @@ use std::collections::HashMap;
use target_lexicon::Triple; use target_lexicon::Triple;
use wasmtime_debug::{DwarfSection, DwarfSectionRelocTarget}; use wasmtime_debug::{DwarfSection, DwarfSectionRelocTarget};
use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::entity::{EntityRef, PrimaryMap};
use wasmtime_environ::ir::{JumpTableOffsets, LibCall, Reloc}; use wasmtime_environ::ir::{LibCall, Reloc};
use wasmtime_environ::isa::unwind::UnwindInfo; use wasmtime_environ::isa::unwind::UnwindInfo;
use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex, SignatureIndex}; use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex, SignatureIndex};
use wasmtime_environ::{ use wasmtime_environ::{CompiledFunction, CompiledFunctions, Module, Relocation, RelocationTarget};
Compilation, CompiledFunction, Module, Relocation, RelocationTarget, Relocations,
};
fn to_object_relocations<'a>( fn to_object_relocations<'a>(
it: impl Iterator<Item = &'a Relocation> + 'a, it: impl Iterator<Item = &'a Relocation> + 'a,
@@ -41,7 +39,7 @@ fn to_object_relocations<'a>(
module: &'a Module, module: &'a Module,
funcs: &'a PrimaryMap<FuncIndex, SymbolId>, funcs: &'a PrimaryMap<FuncIndex, SymbolId>,
libcalls: &'a HashMap<LibCall, SymbolId>, libcalls: &'a HashMap<LibCall, SymbolId>,
jt_offsets: &'a PrimaryMap<DefinedFuncIndex, JumpTableOffsets>, compiled_funcs: &'a CompiledFunctions,
) -> impl Iterator<Item = ObjectRelocation> + 'a { ) -> impl Iterator<Item = ObjectRelocation> + 'a {
it.filter_map(move |r| { it.filter_map(move |r| {
let (symbol, symbol_offset) = match r.reloc_target { let (symbol, symbol_offset) = match r.reloc_target {
@@ -49,9 +47,9 @@ fn to_object_relocations<'a>(
RelocationTarget::LibCall(call) => (libcalls[&call], 0), RelocationTarget::LibCall(call) => (libcalls[&call], 0),
RelocationTarget::JumpTable(f, jt) => { RelocationTarget::JumpTable(f, jt) => {
let df = module.local.defined_func_index(f).unwrap(); let df = module.local.defined_func_index(f).unwrap();
let offset = *jt_offsets let offset = *compiled_funcs
.get(df) .get(df)
.and_then(|ofs| ofs.get(jt)) .and_then(|f| f.jt_offsets.get(jt))
.expect("func jump table"); .expect("func jump table");
(funcs[f], offset) (funcs[f], offset)
} }
@@ -258,20 +256,24 @@ pub struct ObjectBuilder<'a> {
target: ObjectBuilderTarget, target: ObjectBuilderTarget,
module: &'a Module, module: &'a Module,
code_alignment: u64, code_alignment: u64,
compilation: Option<(Compilation, Relocations)>, compilation: &'a CompiledFunctions,
trampolines: PrimaryMap<SignatureIndex, (CompiledFunction, Vec<Relocation>)>, trampolines: PrimaryMap<SignatureIndex, CompiledFunction>,
dwarf_sections: Vec<DwarfSection>, dwarf_sections: Vec<DwarfSection>,
} }
impl<'a> ObjectBuilder<'a> { impl<'a> ObjectBuilder<'a> {
pub fn new(target: ObjectBuilderTarget, module: &'a Module) -> Self { pub fn new(
target: ObjectBuilderTarget,
module: &'a Module,
compilation: &'a CompiledFunctions,
) -> Self {
Self { Self {
target, target,
module, module,
code_alignment: 1, code_alignment: 1,
compilation: None,
trampolines: PrimaryMap::new(), trampolines: PrimaryMap::new(),
dwarf_sections: vec![], dwarf_sections: vec![],
compilation,
} }
} }
@@ -280,18 +282,9 @@ impl<'a> ObjectBuilder<'a> {
self self
} }
pub fn set_compilation(
&mut self,
compilation: Compilation,
relocations: Relocations,
) -> &mut Self {
self.compilation = Some((compilation, relocations));
self
}
pub fn set_trampolines( pub fn set_trampolines(
&mut self, &mut self,
trampolines: PrimaryMap<SignatureIndex, (CompiledFunction, Vec<Relocation>)>, trampolines: PrimaryMap<SignatureIndex, CompiledFunction>,
) -> &mut Self { ) -> &mut Self {
self.trampolines = trampolines; self.trampolines = trampolines;
self self
@@ -319,17 +312,8 @@ impl<'a> ObjectBuilder<'a> {
SectionKind::Text, SectionKind::Text,
); );
let (compilation, jt_offsets, relocations) = self.compilation.map_or_else(
|| (None, PrimaryMap::new(), PrimaryMap::new()),
|(c, relocations)| {
let jt_offsets = c.get_jt_offsets();
(Some(c), jt_offsets, relocations)
},
);
// Create symbols for imports -- needed during linking. // Create symbols for imports -- needed during linking.
let mut func_symbols = let mut func_symbols = PrimaryMap::with_capacity(self.compilation.len());
PrimaryMap::with_capacity(compilation.as_ref().map_or_else(|| 0, |c| c.len()));
for index in 0..module.local.num_imported_funcs { for index in 0..module.local.num_imported_funcs {
let symbol_id = obj.add_symbol(Symbol { let symbol_id = obj.add_symbol(Symbol {
name: utils::func_symbol_name(FuncIndex::new(index)) name: utils::func_symbol_name(FuncIndex::new(index))
@@ -346,16 +330,10 @@ impl<'a> ObjectBuilder<'a> {
func_symbols.push(symbol_id); func_symbols.push(symbol_id);
} }
if let Some(compilation) = compilation { let mut append_func = |name: Vec<u8>, func: &CompiledFunction| {
// Create symbols and section data for the compiled functions.
for (index, func) in compilation.into_iter().enumerate() {
let off = obj.append_section_data(section_id, &func.body, 1); let off = obj.append_section_data(section_id, &func.body, 1);
let symbol_id = obj.add_symbol(Symbol { let symbol_id = obj.add_symbol(Symbol {
name: utils::func_symbol_name( name,
module.local.func_index(DefinedFuncIndex::new(index)),
)
.as_bytes()
.to_vec(),
value: off, value: off,
size: func.body.len() as u64, size: func.body.len() as u64,
kind: SymbolKind::Text, kind: SymbolKind::Text,
@@ -364,33 +342,25 @@ impl<'a> ObjectBuilder<'a> {
section: SymbolSection::Section(section_id), section: SymbolSection::Section(section_id),
flags: SymbolFlags::None, flags: SymbolFlags::None,
}); });
func_symbols.push(symbol_id);
// Preserve function unwind info. // Preserve function unwind info.
if let Some(info) = &func.unwind_info { if let Some(info) = &func.unwind_info {
process_unwind_info(info, &mut obj, section_id); process_unwind_info(info, &mut obj, section_id);
} }
} symbol_id
} };
// Create trampoline symbols for every signature. // Create symbols and section data for the compiled functions.
let mut trampoline_relocs = HashMap::new(); for (index, func) in self.compilation.iter() {
for (i, (func, relocs)) in self.trampolines.into_iter() { let name = utils::func_symbol_name(module.local.func_index(index))
let off = obj.append_section_data(section_id, &func.body, 1); .as_bytes()
let symbol_id = obj.add_symbol(Symbol { .to_vec();
name: utils::trampoline_symbol_name(i).as_bytes().to_vec(), let symbol_id = append_func(name, func);
value: off, func_symbols.push(symbol_id);
size: func.body.len() as u64,
kind: SymbolKind::Text,
scope: SymbolScope::Compilation,
weak: false,
section: SymbolSection::Section(section_id),
flags: SymbolFlags::None,
});
trampoline_relocs.insert(symbol_id, relocs);
// Preserve trampoline function unwind info.
if let Some(info) = &func.unwind_info {
process_unwind_info(info, &mut obj, section_id);
} }
let mut trampolines = Vec::new();
for (i, func) in self.trampolines.iter() {
let name = utils::trampoline_symbol_name(i).as_bytes().to_vec();
trampolines.push(append_func(name, func));
} }
obj.append_section_data(section_id, &[], self.code_alignment); obj.append_section_data(section_id, &[], self.code_alignment);
@@ -412,32 +382,32 @@ impl<'a> ObjectBuilder<'a> {
let libcalls = write_libcall_symbols(&mut obj); let libcalls = write_libcall_symbols(&mut obj);
// Write all functions relocations. // Write all functions relocations.
for (index, relocs) in relocations.into_iter() { for (index, func) in self.compilation.into_iter() {
let func_index = module.local.func_index(index); let func_index = module.local.func_index(index);
let (_, off) = obj let (_, off) = obj
.symbol_section_and_offset(func_symbols[func_index]) .symbol_section_and_offset(func_symbols[func_index])
.unwrap(); .unwrap();
for r in to_object_relocations( for r in to_object_relocations(
relocs.iter(), func.relocations.iter(),
off, off,
module, module,
&func_symbols, &func_symbols,
&libcalls, &libcalls,
&jt_offsets, &self.compilation,
) { ) {
obj.add_relocation(section_id, r)?; obj.add_relocation(section_id, r)?;
} }
} }
for (symbol, relocs) in trampoline_relocs { for (func, symbol) in self.trampolines.values().zip(trampolines) {
let (_, off) = obj.symbol_section_and_offset(symbol).unwrap(); let (_, off) = obj.symbol_section_and_offset(symbol).unwrap();
for r in to_object_relocations( for r in to_object_relocations(
relocs.iter(), func.relocations.iter(),
off, off,
module, module,
&func_symbols, &func_symbols,
&libcalls, &libcalls,
&jt_offsets, &self.compilation,
) { ) {
obj.add_relocation(section_id, r)?; obj.add_relocation(section_id, r)?;
} }

View File

@@ -7,7 +7,7 @@ use object::write::{Object, Relocation, StandardSection, Symbol, SymbolSection};
use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope}; use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope};
use wasmtime_debug::DwarfSection; use wasmtime_debug::DwarfSection;
use wasmtime_environ::isa::TargetFrontendConfig; use wasmtime_environ::isa::TargetFrontendConfig;
use wasmtime_environ::{Compilation, DataInitializer, Module, Relocations}; use wasmtime_environ::{CompiledFunctions, DataInitializer, Module};
fn emit_vmcontext_init( fn emit_vmcontext_init(
obj: &mut Object, obj: &mut Object,
@@ -52,13 +52,11 @@ pub fn emit_module(
target: ObjectBuilderTarget, target: ObjectBuilderTarget,
module: &Module, module: &Module,
target_config: &TargetFrontendConfig, target_config: &TargetFrontendConfig,
compilation: Compilation, compilation: CompiledFunctions,
relocations: Relocations,
dwarf_sections: Vec<DwarfSection>, dwarf_sections: Vec<DwarfSection>,
data_initializers: &[DataInitializer], data_initializers: &[DataInitializer],
) -> Result<Object> { ) -> Result<Object> {
let mut builder = ObjectBuilder::new(target, module); let mut builder = ObjectBuilder::new(target, module, &compilation);
builder.set_compilation(compilation, relocations);
builder.set_dwarf_sections(dwarf_sections); builder.set_dwarf_sections(dwarf_sections);
let mut obj = builder.build()?; let mut obj = builder.build()?;

View File

@@ -54,4 +54,4 @@ jitdump = ["wasmtime-jit/jitdump"]
vtune = ["wasmtime-jit/vtune"] vtune = ["wasmtime-jit/vtune"]
# Enables parallel compilation of WebAssembly code # Enables parallel compilation of WebAssembly code
parallel-compilation = ["wasmtime-environ/parallel-compilation"] parallel-compilation = ["wasmtime-jit/parallel-compilation"]

View File

@@ -152,15 +152,10 @@ pub fn register(module: &CompiledModule) -> Option<GlobalFrameInfoRegistration>
let mut min = usize::max_value(); let mut min = usize::max_value();
let mut max = 0; let mut max = 0;
let mut functions = BTreeMap::new(); let mut functions = BTreeMap::new();
for (((i, allocated), traps), instrs) in module for (i, allocated, traps, address_map) in module.trap_information() {
.finished_functions()
.iter()
.zip(module.traps().values())
.zip(module.address_transform().values())
{
let (start, end) = unsafe { let (start, end) = unsafe {
let ptr = (**allocated).as_ptr(); let ptr = (*allocated).as_ptr();
let len = (**allocated).len(); let len = (*allocated).len();
(ptr as usize, ptr as usize + len) (ptr as usize, ptr as usize + len)
}; };
min = cmp::min(min, start); min = cmp::min(min, start);
@@ -169,7 +164,7 @@ pub fn register(module: &CompiledModule) -> Option<GlobalFrameInfoRegistration>
start, start,
index: module.module().local.func_index(i), index: module.module().local.func_index(i),
traps: traps.to_vec(), traps: traps.to_vec(),
instr_map: (*instrs).clone(), instr_map: address_map.clone(),
}; };
assert!(functions.insert(end, func).is_none()); assert!(functions.insert(end, func).is_none());
} }

View File

@@ -982,20 +982,15 @@ impl Store {
pub(crate) fn register_stack_maps(&self, module: &Module) { pub(crate) fn register_stack_maps(&self, module: &Module) {
let module = &module.compiled_module(); let module = &module.compiled_module();
self.stack_map_registry().register_stack_maps( self.stack_map_registry()
module .register_stack_maps(module.stack_maps().map(|(func, stack_maps)| unsafe {
.finished_functions() let ptr = (*func).as_ptr();
.values() let len = (*func).len();
.zip(module.stack_maps().values())
.map(|(func, stack_maps)| unsafe {
let ptr = (**func).as_ptr();
let len = (**func).len();
let start = ptr as usize; let start = ptr as usize;
let end = ptr as usize + len; let end = ptr as usize + len;
let range = start..end; let range = start..end;
(range, &stack_maps[..]) (range, stack_maps)
}), }));
);
} }
pub(crate) unsafe fn add_instance(&self, handle: InstanceHandle) -> StoreInstanceHandle { pub(crate) unsafe fn add_instance(&self, handle: InstanceHandle) -> StoreInstanceHandle {

View File

@@ -197,6 +197,12 @@ fn make_trampoline(
body: code_buf, body: code_buf,
jt_offsets: context.func.jt_offsets, jt_offsets: context.func.jt_offsets,
unwind_info, unwind_info,
relocations: Default::default(),
address_map: Default::default(),
stack_maps: Default::default(),
stack_slots: Default::default(),
traps: Default::default(),
value_labels_ranges: Default::default(),
}) })
.expect("allocate_for_function") .expect("allocate_for_function")
} }

View File

@@ -1,17 +1,11 @@
use anyhow::{anyhow, bail, Context as _, Result}; use anyhow::{bail, Context as _, Result};
use object::write::Object; use object::write::Object;
use target_lexicon::Triple; use target_lexicon::Triple;
use wasmtime::Strategy; use wasmtime::Strategy;
use wasmtime_debug::emit_dwarf;
#[cfg(feature = "lightbeam")] #[cfg(feature = "lightbeam")]
use wasmtime_environ::Lightbeam; use wasmtime_environ::Lightbeam;
use wasmtime_environ::{ use wasmtime_environ::{settings, settings::Configurable, ModuleEnvironment, Tunables};
entity::EntityRef, settings, settings::Configurable, wasm::DefinedMemoryIndex, use wasmtime_jit::{native, Compiler};
wasm::MemoryIndex, Compiler, Cranelift, ModuleEnvironment, ModuleMemoryOffset, ModuleVmctxInfo,
Tunables, VMOffsets,
};
use wasmtime_jit::native;
use wasmtime_obj::{emit_module, ObjectBuilderTarget};
/// Creates object file from binary wasm data. /// Creates object file from binary wasm data.
pub fn compile_to_obj( pub fn compile_to_obj(
@@ -49,83 +43,28 @@ pub fn compile_to_obj(
let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let isa = isa_builder.finish(settings::Flags::new(flag_builder));
let target = ObjectBuilderTarget::from_triple(isa.triple())?;
// TODO: Expose the tunables as command-line flags. // TODO: Expose the tunables as command-line flags.
let mut tunables = Tunables::default(); let mut tunables = Tunables::default();
tunables.debug_info = debug_info; tunables.debug_info = debug_info;
let environ = ModuleEnvironment::new(isa.frontend_config(), &tunables); let compiler = Compiler::new(
isa,
match strategy {
Strategy::Auto => wasmtime_jit::CompilationStrategy::Auto,
Strategy::Cranelift => wasmtime_jit::CompilationStrategy::Cranelift,
#[cfg(feature = "lightbeam")]
Strategy::Lightbeam => wasmtime_jit::CompilationStrategy::Lightbeam,
#[cfg(not(feature = "lightbeam"))]
Strategy::Lightbeam => bail!("lightbeam support not enabled"),
s => bail!("unknown compilation strategy {:?}", s),
},
tunables.clone(),
);
let environ = ModuleEnvironment::new(compiler.isa().frontend_config(), &tunables);
let translation = environ let translation = environ
.translate(wasm) .translate(wasm)
.context("failed to translate module")?; .context("failed to translate module")?;
let compilation = compiler.compile(&translation)?;
// TODO: use the traps and stack maps information. Ok(compilation.obj)
let (
compilation,
relocations,
address_transform,
value_ranges,
stack_slots,
_traps,
_stack_maps,
) = match strategy {
Strategy::Auto | Strategy::Cranelift => Cranelift::compile_module(&translation, &*isa),
#[cfg(feature = "lightbeam")]
Strategy::Lightbeam => Lightbeam::compile_module(&translation, &*isa),
#[cfg(not(feature = "lightbeam"))]
Strategy::Lightbeam => bail!("lightbeam support not enabled"),
other => bail!("unsupported compilation strategy {:?}", other),
}
.context("failed to compile module")?;
if compilation.is_empty() {
bail!("no functions were found/compiled");
}
let module_vmctx_info = {
let ofs = VMOffsets::new(
translation.target_config.pointer_bytes(),
&translation.module.local,
);
ModuleVmctxInfo {
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
},
stack_slots,
}
};
let dwarf_sections = if let Some(debug_data) = &translation.debuginfo {
emit_dwarf(
&*isa,
&debug_data,
&address_transform,
&module_vmctx_info,
&value_ranges,
&compilation.unwind_info(),
)
.context("failed to emit debug sections")?
} else {
vec![]
};
Ok(emit_module(
target,
&translation.module,
&translation.target_config,
compilation,
relocations,
dwarf_sections,
&translation.data_initializers,
)
.map_err(|e| anyhow!(e))
.context("failed to emit module")?)
} }