Move address maps to a section of the compiled image (#3240)
This commit moves the `address_map` field of `FunctionInfo` into a custom-encoded section of the executable. The goal of this commit is, as previous commits, to push less data through `bincode`. The `address_map` field is actually extremely large and has huge benefits of not being decoded when we load a module. This data is only used for traps and such as well, so it's not overly important that it's massaged in to precise data the runtime can extremely speedily use. The `FunctionInfo` type does retain a tiny bit of information about the function itself (it's start source location), but other than that the `FunctionAddressMap` structure is moved from `wasmtime-environ` to `wasmtime-cranelift` since it's now no longer needed outside of that context.
This commit is contained in:
@@ -3,7 +3,7 @@ use crate::func_environ::{get_func_name, FuncEnvironment};
|
||||
use crate::obj::ObjectBuilder;
|
||||
use crate::{
|
||||
blank_sig, func_signature, indirect_signature, value_type, wasmtime_call_conv,
|
||||
CompiledFunction, Relocation, RelocationTarget,
|
||||
CompiledFunction, FunctionAddressMap, Relocation, RelocationTarget,
|
||||
};
|
||||
use anyhow::{Context as _, Result};
|
||||
use cranelift_codegen::ir::{self, ExternalName, InstBuilder, MemFlags};
|
||||
@@ -26,7 +26,7 @@ use std::convert::TryFrom;
|
||||
use std::mem;
|
||||
use std::sync::Mutex;
|
||||
use wasmtime_environ::{
|
||||
CompileError, FilePos, FlagValue, FunctionAddressMap, FunctionBodyData, FunctionInfo,
|
||||
AddressMapSection, CompileError, FilePos, FlagValue, FunctionBodyData, FunctionInfo,
|
||||
InstructionAddressMap, Module, ModuleTranslation, StackMapInformation, TrapCode,
|
||||
TrapInformation, Tunables, TypeTables, VMOffsets,
|
||||
};
|
||||
@@ -209,10 +209,11 @@ impl wasmtime_environ::Compiler for Compiler {
|
||||
stack_slots: context.func.stack_slots,
|
||||
unwind_info,
|
||||
info: FunctionInfo {
|
||||
address_map: address_transform,
|
||||
traps: trap_sink.traps,
|
||||
start_srcloc: address_transform.start_srcloc,
|
||||
stack_maps: stack_map_sink.finish(),
|
||||
},
|
||||
address_map: address_transform,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -247,9 +248,11 @@ impl wasmtime_environ::Compiler for Compiler {
|
||||
}
|
||||
|
||||
let mut builder = ObjectBuilder::new(obj, &translation.module);
|
||||
let mut addrs = AddressMapSection::default();
|
||||
|
||||
for (i, func) in funcs.iter() {
|
||||
builder.func(i, func);
|
||||
let range = builder.func(i, func);
|
||||
addrs.push(range, &func.address_map.instructions);
|
||||
}
|
||||
for (i, func) in trampolines.iter() {
|
||||
builder.trampoline(*i, func);
|
||||
@@ -287,6 +290,8 @@ impl wasmtime_environ::Compiler for Compiler {
|
||||
}
|
||||
|
||||
builder.finish(&*self.isa)?;
|
||||
addrs.append_to(obj);
|
||||
|
||||
Ok(funcs.into_iter().map(|(_, f)| f.info).collect())
|
||||
}
|
||||
|
||||
@@ -527,6 +532,7 @@ impl Compiler {
|
||||
stack_slots: Default::default(),
|
||||
value_labels_ranges: Default::default(),
|
||||
info: Default::default(),
|
||||
address_map: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use crate::CompiledFunctions;
|
||||
use crate::{CompiledFunctions, FunctionAddressMap};
|
||||
use gimli::write;
|
||||
use more_asserts::assert_le;
|
||||
use std::collections::BTreeMap;
|
||||
use std::iter::FromIterator;
|
||||
use wasmtime_environ::{
|
||||
DefinedFuncIndex, EntityRef, FilePos, FunctionAddressMap, PrimaryMap, WasmFileInfo,
|
||||
};
|
||||
use wasmtime_environ::{DefinedFuncIndex, EntityRef, FilePos, PrimaryMap, WasmFileInfo};
|
||||
|
||||
pub type GeneratedAddress = usize;
|
||||
pub type WasmAddress = u64;
|
||||
@@ -199,7 +197,7 @@ fn build_function_addr_map(
|
||||
) -> PrimaryMap<DefinedFuncIndex, FunctionMap> {
|
||||
let mut map = PrimaryMap::new();
|
||||
for (_, f) in funcs {
|
||||
let ft = &f.info.address_map;
|
||||
let ft = &f.address_map;
|
||||
let mut fn_map = Vec::new();
|
||||
for t in ft.instructions.iter() {
|
||||
if t.srcloc.file_offset().is_none() {
|
||||
@@ -460,7 +458,7 @@ impl AddressTransform {
|
||||
|
||||
let mut func = BTreeMap::new();
|
||||
for (i, f) in funcs {
|
||||
let ft = &f.info.address_map;
|
||||
let ft = &f.address_map;
|
||||
let (fn_start, fn_end, lookup) = build_function_lookup(ft, code_section_offset);
|
||||
|
||||
func.insert(
|
||||
@@ -611,14 +609,12 @@ impl AddressTransform {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{build_function_lookup, get_wasm_code_offset, AddressTransform};
|
||||
use crate::{CompiledFunction, CompiledFunctions};
|
||||
use crate::{CompiledFunction, CompiledFunctions, FunctionAddressMap};
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use gimli::write::Address;
|
||||
use std::iter::FromIterator;
|
||||
use std::mem;
|
||||
use wasmtime_environ::{
|
||||
FilePos, FunctionAddressMap, FunctionInfo, InstructionAddressMap, WasmFileInfo,
|
||||
};
|
||||
use wasmtime_environ::{FilePos, InstructionAddressMap, WasmFileInfo};
|
||||
|
||||
#[test]
|
||||
fn test_get_wasm_code_offset() {
|
||||
@@ -660,10 +656,7 @@ mod tests {
|
||||
|
||||
fn create_simple_module(address_map: FunctionAddressMap) -> CompiledFunctions {
|
||||
PrimaryMap::from_iter(vec![CompiledFunction {
|
||||
info: FunctionInfo {
|
||||
address_map,
|
||||
..Default::default()
|
||||
},
|
||||
address_map,
|
||||
..Default::default()
|
||||
}])
|
||||
}
|
||||
|
||||
@@ -849,7 +849,7 @@ mod tests {
|
||||
};
|
||||
use crate::CompiledFunction;
|
||||
use gimli::{self, constants, Encoding, EndianSlice, Expression, RunTimeEndian};
|
||||
use wasmtime_environ::{FilePos, FunctionInfo};
|
||||
use wasmtime_environ::FilePos;
|
||||
|
||||
macro_rules! dw_op {
|
||||
(DW_OP_WASM_location) => {
|
||||
@@ -1177,39 +1177,37 @@ mod tests {
|
||||
}
|
||||
|
||||
fn create_mock_address_transform() -> AddressTransform {
|
||||
use crate::FunctionAddressMap;
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use wasmtime_environ::InstructionAddressMap;
|
||||
use wasmtime_environ::WasmFileInfo;
|
||||
use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap};
|
||||
let mut module_map = PrimaryMap::new();
|
||||
let code_section_offset: u32 = 100;
|
||||
module_map.push(CompiledFunction {
|
||||
info: FunctionInfo {
|
||||
address_map: FunctionAddressMap {
|
||||
instructions: vec![
|
||||
InstructionAddressMap {
|
||||
srcloc: FilePos::new(code_section_offset + 12),
|
||||
code_offset: 5,
|
||||
},
|
||||
InstructionAddressMap {
|
||||
srcloc: FilePos::default(),
|
||||
code_offset: 8,
|
||||
},
|
||||
InstructionAddressMap {
|
||||
srcloc: FilePos::new(code_section_offset + 17),
|
||||
code_offset: 15,
|
||||
},
|
||||
InstructionAddressMap {
|
||||
srcloc: FilePos::default(),
|
||||
code_offset: 23,
|
||||
},
|
||||
]
|
||||
.into(),
|
||||
start_srcloc: FilePos::new(code_section_offset + 10),
|
||||
end_srcloc: FilePos::new(code_section_offset + 20),
|
||||
body_offset: 0,
|
||||
body_len: 30,
|
||||
},
|
||||
..Default::default()
|
||||
address_map: FunctionAddressMap {
|
||||
instructions: vec![
|
||||
InstructionAddressMap {
|
||||
srcloc: FilePos::new(code_section_offset + 12),
|
||||
code_offset: 5,
|
||||
},
|
||||
InstructionAddressMap {
|
||||
srcloc: FilePos::default(),
|
||||
code_offset: 8,
|
||||
},
|
||||
InstructionAddressMap {
|
||||
srcloc: FilePos::new(code_section_offset + 17),
|
||||
code_offset: 15,
|
||||
},
|
||||
InstructionAddressMap {
|
||||
srcloc: FilePos::default(),
|
||||
code_offset: 23,
|
||||
},
|
||||
]
|
||||
.into(),
|
||||
start_srcloc: FilePos::new(code_section_offset + 10),
|
||||
end_srcloc: FilePos::new(code_section_offset + 20),
|
||||
body_offset: 0,
|
||||
body_len: 30,
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
@@ -94,7 +94,7 @@ use cranelift_codegen::isa::{unwind::UnwindInfo, CallConv, TargetIsa};
|
||||
use cranelift_entity::PrimaryMap;
|
||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmFuncType, WasmType};
|
||||
use target_lexicon::CallingConvention;
|
||||
use wasmtime_environ::{FunctionInfo, Module, TypeTables};
|
||||
use wasmtime_environ::{FilePos, FunctionInfo, InstructionAddressMap, Module, TypeTables};
|
||||
|
||||
pub use builder::builder;
|
||||
|
||||
@@ -118,6 +118,10 @@ pub struct CompiledFunction {
|
||||
/// The unwind information.
|
||||
unwind_info: Option<UnwindInfo>,
|
||||
|
||||
/// Information used to translate from binary offsets back to the original
|
||||
/// location found in the wasm input.
|
||||
address_map: FunctionAddressMap,
|
||||
|
||||
relocations: Vec<Relocation>,
|
||||
value_labels_ranges: cranelift_codegen::ValueLabelsRanges,
|
||||
stack_slots: ir::StackSlots,
|
||||
@@ -125,6 +129,32 @@ pub struct CompiledFunction {
|
||||
info: FunctionInfo,
|
||||
}
|
||||
|
||||
/// Function and its instructions addresses mappings.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
struct FunctionAddressMap {
|
||||
/// An array of data for the instructions in this function, indicating where
|
||||
/// each instruction maps back to in the original function.
|
||||
///
|
||||
/// This array is sorted least-to-greatest by the `code_offset` field.
|
||||
/// Additionally the span of each `InstructionAddressMap` is implicitly the
|
||||
/// gap between it and the next item in the array.
|
||||
instructions: Box<[InstructionAddressMap]>,
|
||||
|
||||
/// Function's initial offset in the source file, specified in bytes from
|
||||
/// the front of the file.
|
||||
start_srcloc: FilePos,
|
||||
|
||||
/// Function's end offset in the source file, specified in bytes from
|
||||
/// the front of the file.
|
||||
end_srcloc: FilePos,
|
||||
|
||||
/// Generated function body offset if applicable, otherwise 0.
|
||||
body_offset: usize,
|
||||
|
||||
/// Generated function body length.
|
||||
body_len: u32,
|
||||
}
|
||||
|
||||
/// A record of a relocation to perform.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct Relocation {
|
||||
|
||||
@@ -34,6 +34,7 @@ use object::{
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::ops::Range;
|
||||
use wasmtime_environ::obj;
|
||||
use wasmtime_environ::{
|
||||
DefinedFuncIndex, EntityRef, FuncIndex, Module, PrimaryMap, SignatureIndex,
|
||||
@@ -157,7 +158,11 @@ impl<'a> ObjectBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn append_func(&mut self, name: Vec<u8>, func: &'a CompiledFunction) -> SymbolId {
|
||||
/// Appends the `func` specified named `name` to this object.
|
||||
///
|
||||
/// Returns the symbol associated with the function as well as the range
|
||||
/// that the function resides within the text section.
|
||||
fn append_func(&mut self, name: Vec<u8>, func: &'a CompiledFunction) -> (SymbolId, Range<u64>) {
|
||||
let off = self
|
||||
.obj
|
||||
.append_section_data(self.text_section, &func.body, 1);
|
||||
@@ -207,15 +212,22 @@ impl<'a> ObjectBuilder<'a> {
|
||||
if !func.relocations.is_empty() {
|
||||
self.pending_relocations.push((off, &func.relocations));
|
||||
}
|
||||
symbol_id
|
||||
(symbol_id, off..off + func.body.len() as u64)
|
||||
}
|
||||
|
||||
pub fn func(&mut self, index: DefinedFuncIndex, func: &'a CompiledFunction) {
|
||||
/// Pushes a new defined function from the a wasm module into this object,
|
||||
/// returning the range that the compiled code will live at relative in the
|
||||
/// text section of the final executable.
|
||||
///
|
||||
/// Note that functions must be pushed in the order of their
|
||||
/// `DefinedFuncIndex`.
|
||||
pub fn func(&mut self, index: DefinedFuncIndex, func: &'a CompiledFunction) -> Range<u64> {
|
||||
assert_eq!(self.jump_tables.push(&func.jt_offsets), index);
|
||||
let index = self.module.func_index(index);
|
||||
let name = obj::func_symbol_name(index);
|
||||
let symbol_id = self.append_func(name.into_bytes(), func);
|
||||
let (symbol_id, range) = self.append_func(name.into_bytes(), func);
|
||||
assert_eq!(self.func_symbols.push(symbol_id), index);
|
||||
range
|
||||
}
|
||||
|
||||
pub fn trampoline(&mut self, sig: SignatureIndex, func: &'a CompiledFunction) {
|
||||
|
||||
Reference in New Issue
Block a user