use crate::debug::{DwarfSectionRelocTarget, ModuleMemoryOffset};
use crate::func_environ::FuncEnvironment;
use crate::{
blank_sig, builder::LinkOptions, func_signature, indirect_signature, value_type,
wasmtime_call_conv, CompiledFunction, FunctionAddressMap,
};
use anyhow::{Context as _, Result};
use cranelift_codegen::ir::{
self, ExternalName, Function, InstBuilder, MemFlags, UserExternalName, UserFuncName, Value,
};
use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa};
use cranelift_codegen::print_errors::pretty_error;
use cranelift_codegen::Context;
use cranelift_codegen::{CompiledCode, MachSrcLoc, MachStackMap};
use cranelift_codegen::{MachReloc, MachTrap};
use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_frontend::FunctionBuilder;
use cranelift_wasm::{
DefinedFuncIndex, FuncIndex, FuncTranslator, MemoryIndex, OwnedMemoryIndex, WasmFuncType,
};
use object::write::{Object, StandardSegment, SymbolId};
use object::{RelocationEncoding, RelocationKind, SectionKind};
use std::any::Any;
use std::cmp;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::mem;
use std::sync::{Arc, Mutex};
use wasmparser::{FuncValidatorAllocations, FunctionBody};
use wasmtime_cranelift_shared::obj::ModuleTextBuilder;
use wasmtime_cranelift_shared::{Relocation, RelocationTarget};
use wasmtime_environ::{
AddressMapSection, CacheStore, CompileError, FilePos, FlagValue, FunctionBodyData, FunctionLoc,
InstructionAddressMap, ModuleTranslation, ModuleTypes, PtrSize, StackMapInformation, Trap,
TrapEncodingBuilder, TrapInformation, Tunables, VMOffsets, WasmFunctionInfo,
};
#[cfg(feature = "component-model")]
mod component;
struct IncrementalCacheContext {
#[cfg(feature = "incremental-cache")]
cache_store: Arc<dyn CacheStore>,
num_hits: usize,
num_cached: usize,
}
struct CompilerContext {
func_translator: FuncTranslator,
codegen_context: Context,
incremental_cache_ctx: Option<IncrementalCacheContext>,
validator_allocations: FuncValidatorAllocations,
}
impl Default for CompilerContext {
fn default() -> Self {
Self {
func_translator: FuncTranslator::new(),
codegen_context: Context::new(),
incremental_cache_ctx: None,
validator_allocations: Default::default(),
}
}
}
pub(crate) struct Compiler {
contexts: Mutex<Vec<CompilerContext>>,
isa: OwnedTargetIsa,
linkopts: LinkOptions,
cache_store: Option<Arc<dyn CacheStore>>,
}
impl Drop for Compiler {
fn drop(&mut self) {
if self.cache_store.is_none() {
return;
}
let mut num_hits = 0;
let mut num_cached = 0;
for ctx in self.contexts.lock().unwrap().iter() {
if let Some(ref cache_ctx) = ctx.incremental_cache_ctx {
num_hits += cache_ctx.num_hits;
num_cached += cache_ctx.num_cached;
}
}
let total = num_hits + num_cached;
if num_hits + num_cached > 0 {
log::trace!(
"Incremental compilation cache stats: {}/{} = {}% (hits/lookup)\ncached: {}",
num_hits,
total,
(num_hits as f32) / (total as f32) * 100.0,
num_cached
);
}
}
}
impl Compiler {
pub(crate) fn new(
isa: OwnedTargetIsa,
cache_store: Option<Arc<dyn CacheStore>>,
linkopts: LinkOptions,
) -> Compiler {
Compiler {
contexts: Default::default(),
isa,
linkopts,
cache_store,
}
}
fn take_context(&self) -> CompilerContext {
let candidate = self.contexts.lock().unwrap().pop();
candidate
.map(|mut ctx| {
ctx.codegen_context.clear();
ctx
})
.unwrap_or_else(|| CompilerContext {
#[cfg(feature = "incremental-cache")]
incremental_cache_ctx: self.cache_store.as_ref().map(|cache_store| {
IncrementalCacheContext {
cache_store: cache_store.clone(),
num_hits: 0,
num_cached: 0,
}
}),
..Default::default()
})
}
fn save_context(&self, ctx: CompilerContext) {
self.contexts.lock().unwrap().push(ctx);
}
fn get_function_address_map(
compiled_code: &CompiledCode,
body: &FunctionBody<'_>,
body_len: u32,
tunables: &Tunables,
) -> FunctionAddressMap {
let data = body.get_binary_reader();
let offset = data.original_position();
let len = data.bytes_remaining();
assert!((offset + len) <= u32::max_value() as usize);
let start_srcloc = FilePos::new(offset as u32);
let end_srcloc = FilePos::new((offset + len) as u32);
let instructions = if tunables.generate_address_map {
collect_address_maps(
body_len,
compiled_code
.buffer
.get_srclocs_sorted()
.into_iter()
.map(|&MachSrcLoc { start, end, loc }| (loc, start, (end - start))),
)
} else {
Vec::new()
};
FunctionAddressMap {
instructions: instructions.into(),
start_srcloc,
end_srcloc,
body_offset: 0,
body_len,
}
}
}
impl wasmtime_environ::Compiler for Compiler {
fn compile_function(
&self,
translation: &ModuleTranslation<'_>,
func_index: DefinedFuncIndex,
input: FunctionBodyData<'_>,
tunables: &Tunables,
types: &ModuleTypes,
) -> Result<(WasmFunctionInfo, Box<dyn Any + Send>), CompileError> {
let isa = &*self.isa;
let module = &translation.module;
let func_index = module.func_index(func_index);
let CompilerContext {
mut func_translator,
codegen_context: mut context,
incremental_cache_ctx: mut cache_ctx,
validator_allocations,
} = self.take_context();
context.func.signature = func_signature(isa, translation, types, func_index);
context.func.name = UserFuncName::User(UserExternalName {
namespace: 0,
index: func_index.as_u32(),
});
if tunables.generate_native_debuginfo {
context.func.collect_debug_info();
}
let mut func_env = FuncEnvironment::new(isa, translation, types, tunables);
let vmctx = context
.func
.create_global_value(ir::GlobalValueData::VMContext);
let interrupts_ptr = context.func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: i32::try_from(func_env.offsets.vmctx_runtime_limits())
.unwrap()
.into(),
global_type: isa.pointer_type(),
readonly: true,
});
let stack_limit = context.func.create_global_value(ir::GlobalValueData::Load {
base: interrupts_ptr,
offset: i32::try_from(func_env.offsets.ptr.vmruntime_limits_stack_limit())
.unwrap()
.into(),
global_type: isa.pointer_type(),
readonly: false,
});
context.func.stack_limit = Some(stack_limit);
let FunctionBodyData { validator, body } = input;
let mut validator = validator.into_validator(validator_allocations);
func_translator.translate_body(
&mut validator,
body.clone(),
&mut context.func,
&mut func_env,
)?;
let (_, code_buf) = compile_maybe_cached(&mut context, isa, cache_ctx.as_mut())?;
let compiled_code = context.compiled_code().unwrap();
let alignment = compiled_code.alignment;
let func_relocs = compiled_code
.buffer
.relocs()
.into_iter()
.map(|item| mach_reloc_to_reloc(&context.func, item))
.collect();
let traps = compiled_code
.buffer
.traps()
.into_iter()
.map(mach_trap_to_trap)
.collect();
let stack_maps = mach_stack_maps_to_stack_maps(compiled_code.buffer.stack_maps());
let unwind_info = if isa.flags().unwind_info() {
compiled_code
.create_unwind_info(isa)
.map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?
} else {
None
};
let length = u32::try_from(code_buf.len()).unwrap();
let address_transform =
Self::get_function_address_map(compiled_code, &body, length, tunables);
let ranges = if tunables.generate_native_debuginfo {
Some(compiled_code.value_labels_ranges.clone())
} else {
None
};
let timing = cranelift_codegen::timing::take_current();
log::debug!("{:?} translated in {:?}", func_index, timing.total());
log::trace!("{:?} timing info\n{}", func_index, timing);
let sized_stack_slots = std::mem::take(&mut context.func.sized_stack_slots);
self.save_context(CompilerContext {
func_translator,
codegen_context: context,
incremental_cache_ctx: cache_ctx,
validator_allocations: validator.into_allocations(),
});
Ok((
WasmFunctionInfo {
start_srcloc: address_transform.start_srcloc,
stack_maps: stack_maps.into(),
},
Box::new(CompiledFunction {
body: code_buf,
relocations: func_relocs,
value_labels_ranges: ranges.unwrap_or(Default::default()),
sized_stack_slots,
unwind_info,
traps,
alignment,
address_map: address_transform,
}),
))
}
fn compile_host_to_wasm_trampoline(
&self,
ty: &WasmFuncType,
) -> Result<Box<dyn Any + Send>, CompileError> {
self.host_to_wasm_trampoline(ty)
.map(|x| Box::new(x) as Box<_>)
}
fn append_code(
&self,
obj: &mut Object<'static>,
funcs: &[(String, Box<dyn Any + Send>)],
tunables: &Tunables,
resolve_reloc: &dyn Fn(usize, FuncIndex) -> usize,
) -> Result<Vec<(SymbolId, FunctionLoc)>> {
let mut builder =
ModuleTextBuilder::new(obj, self, self.isa.text_section_builder(funcs.len()));
if self.linkopts.force_jump_veneers {
builder.force_veneers();
}
let mut addrs = AddressMapSection::default();
let mut traps = TrapEncodingBuilder::default();
let mut ret = Vec::with_capacity(funcs.len());
for (i, (sym, func)) in funcs.iter().enumerate() {
let func = func.downcast_ref::<CompiledFunction>().unwrap();
let (sym, range) = builder.append_func(
&sym,
&func.body,
func.alignment,
func.unwind_info.as_ref(),
&func.relocations,
|idx| resolve_reloc(i, idx),
);
if tunables.generate_address_map {
addrs.push(range.clone(), &func.address_map.instructions);
}
traps.push(range.clone(), &func.traps);
builder.append_padding(self.linkopts.padding_between_functions);
let info = FunctionLoc {
start: u32::try_from(range.start).unwrap(),
length: u32::try_from(range.end - range.start).unwrap(),
};
ret.push((sym, info));
}
builder.finish();
if tunables.generate_address_map {
addrs.append_to(obj);
}
traps.append_to(obj);
Ok(ret)
}
fn emit_trampoline_obj(
&self,
ty: &WasmFuncType,
host_fn: usize,
obj: &mut Object<'static>,
) -> Result<(FunctionLoc, FunctionLoc)> {
let host_to_wasm = self.host_to_wasm_trampoline(ty)?;
let wasm_to_host = self.wasm_to_host_trampoline(ty, host_fn)?;
let mut builder = ModuleTextBuilder::new(obj, self, self.isa.text_section_builder(2));
let (_, a) = builder.append_func(
"host_to_wasm",
&host_to_wasm.body,
host_to_wasm.alignment,
host_to_wasm.unwind_info.as_ref(),
&host_to_wasm.relocations,
|_| unreachable!(),
);
let (_, b) = builder.append_func(
"wasm_to_host",
&wasm_to_host.body,
wasm_to_host.alignment,
wasm_to_host.unwind_info.as_ref(),
&wasm_to_host.relocations,
|_| unreachable!(),
);
let a = FunctionLoc {
start: u32::try_from(a.start).unwrap(),
length: u32::try_from(a.end - a.start).unwrap(),
};
let b = FunctionLoc {
start: u32::try_from(b.start).unwrap(),
length: u32::try_from(b.end - b.start).unwrap(),
};
builder.finish();
Ok((a, b))
}
fn triple(&self) -> &target_lexicon::Triple {
self.isa.triple()
}
fn flags(&self) -> BTreeMap<String, FlagValue> {
wasmtime_cranelift_shared::clif_flags_to_wasmtime(self.isa.flags().iter())
}
fn isa_flags(&self) -> BTreeMap<String, FlagValue> {
wasmtime_cranelift_shared::clif_flags_to_wasmtime(self.isa.isa_flags())
}
fn is_branch_protection_enabled(&self) -> bool {
self.isa.is_branch_protection_enabled()
}
#[cfg(feature = "component-model")]
fn component_compiler(&self) -> &dyn wasmtime_environ::component::ComponentCompiler {
self
}
fn append_dwarf(
&self,
obj: &mut Object<'_>,
translation: &ModuleTranslation<'_>,
funcs: &PrimaryMap<DefinedFuncIndex, (SymbolId, &(dyn Any + Send))>,
) -> Result<()> {
let ofs = VMOffsets::new(
self.isa
.triple()
.architecture
.pointer_width()
.unwrap()
.bytes(),
&translation.module,
);
let memory_offset = if ofs.num_imported_memories > 0 {
ModuleMemoryOffset::Imported(ofs.vmctx_vmmemory_import(MemoryIndex::new(0)))
} else if ofs.num_defined_memories > 0 {
assert_eq!(
ofs.num_defined_memories, ofs.num_owned_memories,
"the memory base pointer may be incorrect due to sharing memory"
);
ModuleMemoryOffset::Defined(
ofs.vmctx_vmmemory_definition_base(OwnedMemoryIndex::new(0)),
)
} else {
ModuleMemoryOffset::None
};
let compiled_funcs = funcs
.iter()
.map(|(_, (_, func))| func.downcast_ref().unwrap())
.collect();
let dwarf_sections = crate::debug::emit_dwarf(
&*self.isa,
&translation.debuginfo,
&compiled_funcs,
&memory_offset,
)
.with_context(|| "failed to emit DWARF debug information")?;
let (debug_bodies, debug_relocs): (Vec<_>, Vec<_>) = dwarf_sections
.iter()
.map(|s| ((s.name, &s.body), (s.name, &s.relocs)))
.unzip();
let mut dwarf_sections_ids = HashMap::new();
for (name, body) in debug_bodies {
let segment = obj.segment_name(StandardSegment::Debug).to_vec();
let section_id = obj.add_section(segment, name.as_bytes().to_vec(), SectionKind::Debug);
dwarf_sections_ids.insert(name, section_id);
obj.append_section_data(section_id, &body, 1);
}
for (name, relocs) in debug_relocs {
let section_id = *dwarf_sections_ids.get(name).unwrap();
for reloc in relocs {
let target_symbol = match reloc.target {
DwarfSectionRelocTarget::Func(index) => funcs[DefinedFuncIndex::new(index)].0,
DwarfSectionRelocTarget::Section(name) => {
obj.section_symbol(dwarf_sections_ids[name])
}
};
obj.add_relocation(
section_id,
object::write::Relocation {
offset: u64::from(reloc.offset),
size: reloc.size << 3,
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
symbol: target_symbol,
addend: i64::from(reloc.addend),
},
)?;
}
}
Ok(())
}
fn function_alignment(&self) -> u32 {
self.isa.function_alignment()
}
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
self.isa.create_systemv_cie()
}
}
#[cfg(feature = "incremental-cache")]
mod incremental_cache {
use super::*;
struct CraneliftCacheStore(Arc<dyn CacheStore>);
impl cranelift_codegen::incremental_cache::CacheKvStore for CraneliftCacheStore {
fn get(&self, key: &[u8]) -> Option<std::borrow::Cow<[u8]>> {
self.0.get(key)
}
fn insert(&mut self, key: &[u8], val: Vec<u8>) {
self.0.insert(key, val);
}
}
pub(super) fn compile_maybe_cached<'a>(
context: &'a mut Context,
isa: &dyn TargetIsa,
cache_ctx: Option<&mut IncrementalCacheContext>,
) -> Result<(&'a CompiledCode, Vec<u8>), CompileError> {
let cache_ctx = match cache_ctx {
Some(ctx) => ctx,
None => return compile_uncached(context, isa),
};
let mut cache_store = CraneliftCacheStore(cache_ctx.cache_store.clone());
let (compiled_code, from_cache) = context
.compile_with_cache(isa, &mut cache_store)
.map_err(|error| CompileError::Codegen(pretty_error(&error.func, error.inner)))?;
if from_cache {
cache_ctx.num_hits += 1;
} else {
cache_ctx.num_cached += 1;
}
Ok((compiled_code, compiled_code.code_buffer().to_vec()))
}
}
#[cfg(feature = "incremental-cache")]
use incremental_cache::*;
#[cfg(not(feature = "incremental-cache"))]
fn compile_maybe_cached<'a>(
context: &'a mut Context,
isa: &dyn TargetIsa,
_cache_ctx: Option<&mut IncrementalCacheContext>,
) -> Result<(&'a CompiledCode, Vec<u8>), CompileError> {
compile_uncached(context, isa)
}
fn compile_uncached<'a>(
context: &'a mut Context,
isa: &dyn TargetIsa,
) -> Result<(&'a CompiledCode, Vec<u8>), CompileError> {
let mut code_buf = Vec::new();
let compiled_code = context
.compile_and_emit(isa, &mut code_buf)
.map_err(|error| CompileError::Codegen(pretty_error(&error.func, error.inner)))?;
Ok((compiled_code, code_buf))
}
impl Compiler {
fn host_to_wasm_trampoline(&self, ty: &WasmFuncType) -> Result<CompiledFunction, CompileError> {
let isa = &*self.isa;
let value_size = mem::size_of::<u128>();
let pointer_type = isa.pointer_type();
let wasm_signature = indirect_signature(isa, ty);
let mut host_signature = blank_sig(isa, wasmtime_call_conv(isa));
host_signature.params.push(ir::AbiParam::new(pointer_type));
host_signature.params.push(ir::AbiParam::new(pointer_type));
let CompilerContext {
mut func_translator,
codegen_context: mut context,
incremental_cache_ctx: mut cache_ctx,
validator_allocations,
} = self.take_context();
context.func = ir::Function::with_name_signature(UserFuncName::default(), host_signature);
let mut builder = FunctionBuilder::new(&mut context.func, func_translator.context());
let block0 = builder.create_block();
builder.append_block_params_for_function_params(block0);
builder.switch_to_block(block0);
builder.seal_block(block0);
let (vmctx_ptr_val, caller_vmctx_ptr_val, callee_value, values_vec_ptr_val) = {
let params = builder.func.dfg.block_params(block0);
(params[0], params[1], params[2], params[3])
};
let mut mflags = ir::MemFlags::trusted();
mflags.set_endianness(ir::Endianness::Little);
let callee_args = wasm_signature
.params
.iter()
.enumerate()
.map(|(i, r)| {
match i {
0 => vmctx_ptr_val,
1 => caller_vmctx_ptr_val,
_ =>
{
builder.ins().load(
r.value_type,
mflags,
values_vec_ptr_val,
((i - 2) * value_size) as i32,
)
}
}
})
.collect::<Vec<_>>();
let new_sig = builder.import_signature(wasm_signature);
let call = builder
.ins()
.call_indirect(new_sig, callee_value, &callee_args);
let results = builder.func.dfg.inst_results(call).to_vec();
for (i, r) in results.iter().enumerate() {
builder
.ins()
.store(mflags, *r, values_vec_ptr_val, (i * value_size) as i32);
}
builder.ins().return_(&[]);
builder.finalize();
let func = self.finish_trampoline(&mut context, cache_ctx.as_mut(), isa)?;
self.save_context(CompilerContext {
func_translator,
codegen_context: context,
incremental_cache_ctx: cache_ctx,
validator_allocations,
});
Ok(func)
}
fn wasm_to_host_trampoline(
&self,
ty: &WasmFuncType,
host_fn: usize,
) -> Result<CompiledFunction, CompileError> {
let isa = &*self.isa;
let pointer_type = isa.pointer_type();
let wasm_signature = indirect_signature(isa, ty);
let mut host_signature = blank_sig(isa, wasmtime_call_conv(isa));
host_signature.params.push(ir::AbiParam::new(pointer_type));
host_signature.params.push(ir::AbiParam::new(pointer_type));
let CompilerContext {
mut func_translator,
codegen_context: mut context,
incremental_cache_ctx: mut cache_ctx,
validator_allocations,
} = self.take_context();
context.func = ir::Function::with_name_signature(Default::default(), wasm_signature);
let mut builder = FunctionBuilder::new(&mut context.func, func_translator.context());
let block0 = builder.create_block();
let (values_vec_ptr_val, values_vec_len) =
self.wasm_to_host_spill_args(ty, &mut builder, block0);
let block_params = builder.func.dfg.block_params(block0);
let callee_args = [
block_params[0],
block_params[1],
values_vec_ptr_val,
builder
.ins()
.iconst(pointer_type, i64::from(values_vec_len)),
];
let new_sig = builder.import_signature(host_signature);
let callee_value = builder.ins().iconst(pointer_type, host_fn as i64);
builder
.ins()
.call_indirect(new_sig, callee_value, &callee_args);
self.wasm_to_host_load_results(ty, builder, values_vec_ptr_val);
let func = self.finish_trampoline(&mut context, cache_ctx.as_mut(), isa)?;
self.save_context(CompilerContext {
func_translator,
codegen_context: context,
incremental_cache_ctx: cache_ctx,
validator_allocations,
});
Ok(func)
}
fn wasm_to_host_spill_args(
&self,
ty: &WasmFuncType,
builder: &mut FunctionBuilder,
block0: ir::Block,
) -> (Value, u32) {
let isa = &*self.isa;
let pointer_type = isa.pointer_type();
let value_size = mem::size_of::<u128>();
let values_vec_len = cmp::max(ty.params().len(), ty.returns().len());
let values_vec_byte_size = u32::try_from(value_size * values_vec_len).unwrap();
let values_vec_len = u32::try_from(values_vec_len).unwrap();
let ss = builder.func.create_sized_stack_slot(ir::StackSlotData::new(
ir::StackSlotKind::ExplicitSlot,
values_vec_byte_size,
));
builder.append_block_params_for_function_params(block0);
builder.switch_to_block(block0);
builder.seal_block(block0);
let mut mflags = MemFlags::trusted();
mflags.set_endianness(ir::Endianness::Little);
let values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0);
for i in 0..ty.params().len() {
let val = builder.func.dfg.block_params(block0)[i + 2];
builder
.ins()
.store(mflags, val, values_vec_ptr_val, (i * value_size) as i32);
}
(values_vec_ptr_val, values_vec_len)
}
fn wasm_to_host_load_results(
&self,
ty: &WasmFuncType,
mut builder: FunctionBuilder,
values_vec_ptr_val: Value,
) {
let isa = &*self.isa;
let value_size = mem::size_of::<u128>();
let mut mflags = MemFlags::trusted();
mflags.set_endianness(ir::Endianness::Little);
let mut results = Vec::new();
for (i, r) in ty.returns().iter().enumerate() {
let load = builder.ins().load(
value_type(isa, *r),
mflags,
values_vec_ptr_val,
(i * value_size) as i32,
);
results.push(load);
}
builder.ins().return_(&results);
builder.finalize();
}
fn finish_trampoline(
&self,
context: &mut Context,
cache_ctx: Option<&mut IncrementalCacheContext>,
isa: &dyn TargetIsa,
) -> Result<CompiledFunction, CompileError> {
let (compiled_code, code_buf) = compile_maybe_cached(context, isa, cache_ctx)?;
assert!(compiled_code.buffer.relocs().is_empty());
let traps = compiled_code
.buffer
.traps()
.into_iter()
.map(mach_trap_to_trap)
.collect();
let alignment = compiled_code.alignment;
let unwind_info = if isa.flags().unwind_info() {
compiled_code
.create_unwind_info(isa)
.map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?
} else {
None
};
Ok(CompiledFunction {
body: code_buf,
unwind_info,
relocations: Default::default(),
sized_stack_slots: Default::default(),
value_labels_ranges: Default::default(),
address_map: Default::default(),
traps,
alignment,
})
}
}
fn collect_address_maps(
code_size: u32,
iter: impl IntoIterator<Item = (ir::SourceLoc, u32, u32)>,
) -> Vec<InstructionAddressMap> {
let mut iter = iter.into_iter();
let (mut cur_loc, mut cur_offset, mut cur_len) = match iter.next() {
Some(i) => i,
None => return Vec::new(),
};
let mut ret = Vec::new();
for (loc, offset, len) in iter {
if cur_offset + cur_len == offset && loc == cur_loc {
cur_len += len;
continue;
}
ret.push(InstructionAddressMap {
srcloc: cvt(cur_loc),
code_offset: cur_offset,
});
if cur_offset + cur_len != offset {
ret.push(InstructionAddressMap {
srcloc: FilePos::default(),
code_offset: cur_offset + cur_len,
});
}
cur_loc = loc;
cur_offset = offset;
cur_len = len;
}
ret.push(InstructionAddressMap {
srcloc: cvt(cur_loc),
code_offset: cur_offset,
});
if cur_offset + cur_len != code_size {
ret.push(InstructionAddressMap {
srcloc: FilePos::default(),
code_offset: cur_offset + cur_len,
});
}
return ret;
fn cvt(loc: ir::SourceLoc) -> FilePos {
if loc.is_default() {
FilePos::default()
} else {
FilePos::new(loc.bits())
}
}
}
fn mach_reloc_to_reloc(func: &Function, reloc: &MachReloc) -> Relocation {
let &MachReloc {
offset,
kind,
ref name,
addend,
} = reloc;
let reloc_target = if let ExternalName::User(user_func_ref) = *name {
let UserExternalName { namespace, index } = func.params.user_named_funcs()[user_func_ref];
debug_assert_eq!(namespace, 0);
RelocationTarget::UserFunc(FuncIndex::from_u32(index))
} else if let ExternalName::LibCall(libcall) = *name {
RelocationTarget::LibCall(libcall)
} else {
panic!("unrecognized external name")
};
Relocation {
reloc: kind,
reloc_target,
offset,
addend,
}
}
const ALWAYS_TRAP_CODE: u16 = 100;
fn mach_trap_to_trap(trap: &MachTrap) -> TrapInformation {
let &MachTrap { offset, code } = trap;
TrapInformation {
code_offset: offset,
trap_code: match code {
ir::TrapCode::StackOverflow => Trap::StackOverflow,
ir::TrapCode::HeapOutOfBounds => Trap::MemoryOutOfBounds,
ir::TrapCode::HeapMisaligned => Trap::HeapMisaligned,
ir::TrapCode::TableOutOfBounds => Trap::TableOutOfBounds,
ir::TrapCode::IndirectCallToNull => Trap::IndirectCallToNull,
ir::TrapCode::BadSignature => Trap::BadSignature,
ir::TrapCode::IntegerOverflow => Trap::IntegerOverflow,
ir::TrapCode::IntegerDivisionByZero => Trap::IntegerDivisionByZero,
ir::TrapCode::BadConversionToInteger => Trap::BadConversionToInteger,
ir::TrapCode::UnreachableCodeReached => Trap::UnreachableCodeReached,
ir::TrapCode::Interrupt => Trap::Interrupt,
ir::TrapCode::User(ALWAYS_TRAP_CODE) => Trap::AlwaysTrapAdapter,
ir::TrapCode::User(_) => unreachable!(),
},
}
}
fn mach_stack_maps_to_stack_maps(mach_stack_maps: &[MachStackMap]) -> Vec<StackMapInformation> {
let mut stack_maps = Vec::new();
for &MachStackMap {
offset_end,
ref stack_map,
..
} in mach_stack_maps
{
let stack_map = wasmtime_environ::StackMap::new(
stack_map.mapped_words(),
stack_map.as_slice().iter().map(|a| a.0),
);
stack_maps.push(StackMapInformation {
code_offset: offset_end,
stack_map,
});
}
stack_maps.sort_unstable_by_key(|info| info.code_offset);
stack_maps
}