Remove dependency on TargetIsa from Wasmtime crates (#3178)
This commit started off by deleting the `cranelift_codegen::settings` reexport in the `wasmtime-environ` crate and then basically played whack-a-mole until everything compiled again. The main result of this is that the `wasmtime-*` family of crates have generally less of a dependency on the `TargetIsa` trait and type from Cranelift. While the dependency isn't entirely severed yet this is at least a significant start. This commit is intended to be largely refactorings, no functional changes are intended here. The refactorings are: * A `CompilerBuilder` trait has been added to `wasmtime_environ` which server as an abstraction used to create compilers and configure them in a uniform fashion. The `wasmtime::Config` type now uses this instead of cranelift-specific settings. The `wasmtime-jit` crate exports the ability to create a compiler builder from a `CompilationStrategy`, which only works for Cranelift right now. In a cranelift-less build of Wasmtime this is expected to return a trait object that fails all requests to compile. * The `Compiler` trait in the `wasmtime_environ` crate has been souped up with a number of methods that Wasmtime and other crates needed. * The `wasmtime-debug` crate is now moved entirely behind the `wasmtime-cranelift` crate. * The `wasmtime-cranelift` crate is now only depended on by the `wasmtime-jit` crate. * Wasm types in `cranelift-wasm` no longer contain their IR type, instead they only contain the `WasmType`. This is required to get everything to align correctly but will also be required in a future refactoring where the types used by `cranelift-wasm` will be extracted to a separate crate. * I moved around a fair bit of code in `wasmtime-cranelift`. * Some gdb-specific jit-specific code has moved from `wasmtime-debug` to `wasmtime-jit`.
This commit is contained in:
660
crates/cranelift/src/compiler.rs
Normal file
660
crates/cranelift/src/compiler.rs
Normal file
@@ -0,0 +1,660 @@
|
||||
use crate::func_environ::{get_func_name, FuncEnvironment};
|
||||
use crate::{blank_sig, func_signature, indirect_signature, value_type, wasmtime_call_conv};
|
||||
use anyhow::Result;
|
||||
use cranelift_codegen::ir::{self, ExternalName, InstBuilder, MemFlags};
|
||||
use cranelift_codegen::isa::TargetIsa;
|
||||
use cranelift_codegen::print_errors::pretty_error;
|
||||
use cranelift_codegen::settings;
|
||||
use cranelift_codegen::MachSrcLoc;
|
||||
use cranelift_codegen::{binemit, Context};
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, FuncTranslator, WasmFuncType};
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::mem;
|
||||
use std::sync::Mutex;
|
||||
use wasmtime_environ::{
|
||||
CompileError, CompiledFunction, CompiledFunctions, DebugInfoData, DwarfSection, FlagValue,
|
||||
FunctionAddressMap, FunctionBodyData, InstructionAddressMap, ModuleMemoryOffset,
|
||||
ModuleTranslation, Relocation, RelocationTarget, StackMapInformation, TrapInformation,
|
||||
Tunables, TypeTables,
|
||||
};
|
||||
|
||||
/// A compiler that compiles a WebAssembly module with Compiler, translating
|
||||
/// the Wasm to Compiler IR, optimizing it and then translating to assembly.
|
||||
pub(crate) struct Compiler {
|
||||
translators: Mutex<Vec<FuncTranslator>>,
|
||||
isa: Box<dyn TargetIsa>,
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
pub(crate) fn new(isa: Box<dyn TargetIsa>) -> Compiler {
|
||||
Compiler {
|
||||
translators: Default::default(),
|
||||
isa,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
fn get_function_address_map(
|
||||
&self,
|
||||
context: &Context,
|
||||
data: &FunctionBodyData<'_>,
|
||||
body_len: u32,
|
||||
) -> FunctionAddressMap {
|
||||
// Generate artificial srcloc for function start/end to identify boundary
|
||||
// within module.
|
||||
let data = 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 = ir::SourceLoc::new(offset as u32);
|
||||
let end_srcloc = ir::SourceLoc::new((offset + len) as u32);
|
||||
|
||||
let instructions = if let Some(ref mcr) = &context.mach_compile_result {
|
||||
// New-style backend: we have a `MachCompileResult` that will give us `MachSrcLoc` mapping
|
||||
// tuples.
|
||||
collect_address_maps(
|
||||
body_len,
|
||||
mcr.buffer
|
||||
.get_srclocs_sorted()
|
||||
.into_iter()
|
||||
.map(|&MachSrcLoc { start, end, loc }| (loc, start, (end - start))),
|
||||
)
|
||||
} else {
|
||||
// Old-style backend: we need to traverse the instruction/encoding info in the function.
|
||||
let func = &context.func;
|
||||
let mut blocks = func.layout.blocks().collect::<Vec<_>>();
|
||||
blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase
|
||||
|
||||
let encinfo = self.isa.encoding_info();
|
||||
collect_address_maps(
|
||||
body_len,
|
||||
blocks
|
||||
.into_iter()
|
||||
.flat_map(|block| func.inst_offsets(block, &encinfo))
|
||||
.map(|(offset, inst, size)| (func.srclocs[inst], offset, size)),
|
||||
)
|
||||
};
|
||||
|
||||
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,
|
||||
mut input: FunctionBodyData<'_>,
|
||||
tunables: &Tunables,
|
||||
types: &TypeTables,
|
||||
) -> Result<CompiledFunction, CompileError> {
|
||||
let isa = &*self.isa;
|
||||
let module = &translation.module;
|
||||
let func_index = module.func_index(func_index);
|
||||
let mut context = Context::new();
|
||||
context.func.name = get_func_name(func_index);
|
||||
context.func.signature = func_signature(isa, module, types, func_index);
|
||||
if tunables.generate_native_debuginfo {
|
||||
context.func.collect_debug_info();
|
||||
}
|
||||
|
||||
let mut func_env = FuncEnvironment::new(isa, module, types, tunables);
|
||||
|
||||
// We use these as constant offsets below in
|
||||
// `stack_limit_from_arguments`, so assert their values here. This
|
||||
// allows the closure below to get coerced to a function pointer, as
|
||||
// needed by `ir::Function`.
|
||||
//
|
||||
// Otherwise our stack limit is specially calculated from the vmctx
|
||||
// argument, where we need to load the `*const VMInterrupts`
|
||||
// pointer, and then from that pointer we need to load the stack
|
||||
// limit itself. Note that manual register allocation is needed here
|
||||
// too due to how late in the process this codegen happens.
|
||||
//
|
||||
// For more information about interrupts and stack checks, see the
|
||||
// top of this file.
|
||||
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_interrupts())
|
||||
.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.vminterrupts_stack_limit())
|
||||
.unwrap()
|
||||
.into(),
|
||||
global_type: isa.pointer_type(),
|
||||
readonly: false,
|
||||
});
|
||||
context.func.stack_limit = Some(stack_limit);
|
||||
let mut func_translator = self.take_translator();
|
||||
func_translator.translate_body(
|
||||
&mut input.validator,
|
||||
input.body.clone(),
|
||||
&mut context.func,
|
||||
&mut func_env,
|
||||
)?;
|
||||
self.save_translator(func_translator);
|
||||
|
||||
let mut code_buf: Vec<u8> = Vec::new();
|
||||
let mut reloc_sink = RelocSink::new(func_index);
|
||||
let mut trap_sink = TrapSink::new();
|
||||
let mut stack_map_sink = StackMapSink::default();
|
||||
context
|
||||
.compile_and_emit(
|
||||
isa,
|
||||
&mut code_buf,
|
||||
&mut reloc_sink,
|
||||
&mut trap_sink,
|
||||
&mut stack_map_sink,
|
||||
)
|
||||
.map_err(|error| {
|
||||
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
||||
})?;
|
||||
|
||||
let unwind_info = context.create_unwind_info(isa).map_err(|error| {
|
||||
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
||||
})?;
|
||||
|
||||
let address_transform =
|
||||
self.get_function_address_map(&context, &input, code_buf.len() as u32);
|
||||
|
||||
let ranges = if tunables.generate_native_debuginfo {
|
||||
let ranges = context.build_value_labels_ranges(isa).map_err(|error| {
|
||||
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
||||
})?;
|
||||
Some(ranges)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(CompiledFunction {
|
||||
body: code_buf,
|
||||
jt_offsets: context.func.jt_offsets,
|
||||
relocations: reloc_sink.func_relocs,
|
||||
address_map: address_transform,
|
||||
value_labels_ranges: ranges.unwrap_or(Default::default()),
|
||||
stack_slots: context.func.stack_slots,
|
||||
traps: trap_sink.traps,
|
||||
unwind_info,
|
||||
stack_maps: stack_map_sink.finish(),
|
||||
})
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// The wasm signature we're calling in this trampoline has the actual
|
||||
// ABI of the function signature described by `ty`
|
||||
let wasm_signature = indirect_signature(isa, ty);
|
||||
|
||||
// The host signature has the `VMTrampoline` signature where the ABI is
|
||||
// fixed.
|
||||
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 mut func_translator = self.take_translator();
|
||||
let mut context = Context::new();
|
||||
context.func = ir::Function::with_name_signature(ExternalName::user(0, 0), host_signature);
|
||||
|
||||
// This trampoline will load all the parameters from the `values_vec`
|
||||
// that is passed in and then call the real function (also passed
|
||||
// indirectly) with the specified ABI.
|
||||
//
|
||||
// All the results are then stored into the same `values_vec`.
|
||||
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])
|
||||
};
|
||||
|
||||
// Load the argument values out of `values_vec`.
|
||||
let mflags = ir::MemFlags::trusted();
|
||||
let callee_args = wasm_signature
|
||||
.params
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, r)| {
|
||||
match i {
|
||||
0 => vmctx_ptr_val,
|
||||
1 => caller_vmctx_ptr_val,
|
||||
_ =>
|
||||
// i - 2 because vmctx and caller vmctx aren't passed through `values_vec`.
|
||||
{
|
||||
builder.ins().load(
|
||||
r.value_type,
|
||||
mflags,
|
||||
values_vec_ptr_val,
|
||||
((i - 2) * value_size) as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Call the indirect function pointer we were given
|
||||
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();
|
||||
|
||||
// Store the return values into `values_vec`.
|
||||
let mflags = ir::MemFlags::trusted();
|
||||
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(context, isa)?;
|
||||
self.save_translator(func_translator);
|
||||
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);
|
||||
// The host signature has an added parameter for the `values_vec` input
|
||||
// and output.
|
||||
let mut host_signature = blank_sig(isa, wasmtime_call_conv(isa));
|
||||
host_signature.params.push(ir::AbiParam::new(pointer_type));
|
||||
|
||||
// Compute the size of the values vector. The vmctx and caller vmctx are passed separately.
|
||||
let value_size = mem::size_of::<u128>();
|
||||
let values_vec_len = (value_size * cmp::max(ty.params.len(), ty.returns.len())) as u32;
|
||||
|
||||
let mut context = Context::new();
|
||||
context.func =
|
||||
ir::Function::with_name_signature(ir::ExternalName::user(0, 0), wasm_signature);
|
||||
|
||||
let ss = context.func.create_stack_slot(ir::StackSlotData::new(
|
||||
ir::StackSlotKind::ExplicitSlot,
|
||||
values_vec_len,
|
||||
));
|
||||
|
||||
let mut func_translator = self.take_translator();
|
||||
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 values_vec_ptr_val = builder.ins().stack_addr(pointer_type, ss, 0);
|
||||
let mflags = MemFlags::trusted();
|
||||
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);
|
||||
}
|
||||
|
||||
let block_params = builder.func.dfg.block_params(block0);
|
||||
let vmctx_ptr_val = block_params[0];
|
||||
let caller_vmctx_ptr_val = block_params[1];
|
||||
|
||||
let callee_args = vec![vmctx_ptr_val, caller_vmctx_ptr_val, values_vec_ptr_val];
|
||||
|
||||
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);
|
||||
|
||||
let mflags = MemFlags::trusted();
|
||||
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();
|
||||
|
||||
let func = self.finish_trampoline(context, isa)?;
|
||||
self.save_translator(func_translator);
|
||||
Ok(func)
|
||||
}
|
||||
|
||||
fn emit_dwarf(
|
||||
&self,
|
||||
debuginfo_data: &DebugInfoData,
|
||||
funcs: &CompiledFunctions,
|
||||
memory_offset: &ModuleMemoryOffset,
|
||||
) -> Result<Vec<DwarfSection>> {
|
||||
wasmtime_debug::emit_dwarf(&*self.isa, debuginfo_data, funcs, memory_offset)
|
||||
}
|
||||
|
||||
fn triple(&self) -> &target_lexicon::Triple {
|
||||
self.isa.triple()
|
||||
}
|
||||
|
||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||
self.isa.create_systemv_cie()
|
||||
}
|
||||
|
||||
fn flags(&self) -> HashMap<String, FlagValue> {
|
||||
self.isa
|
||||
.flags()
|
||||
.iter()
|
||||
.map(|val| (val.name.to_string(), to_flag_value(&val)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn isa_flags(&self) -> HashMap<String, FlagValue> {
|
||||
self.isa
|
||||
.isa_flags()
|
||||
.iter()
|
||||
.map(|val| (val.name.to_string(), to_flag_value(val)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn to_flag_value(v: &settings::Value) -> FlagValue {
|
||||
match v.kind() {
|
||||
settings::SettingKind::Enum => FlagValue::Enum(v.as_enum().unwrap().into()),
|
||||
settings::SettingKind::Num => FlagValue::Num(v.as_num().unwrap()),
|
||||
settings::SettingKind::Bool => FlagValue::Bool(v.as_bool().unwrap()),
|
||||
settings::SettingKind::Preset => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
fn finish_trampoline(
|
||||
&self,
|
||||
mut context: Context,
|
||||
isa: &dyn TargetIsa,
|
||||
) -> Result<CompiledFunction, CompileError> {
|
||||
let mut code_buf = Vec::new();
|
||||
let mut reloc_sink = TrampolineRelocSink::default();
|
||||
let mut trap_sink = binemit::NullTrapSink {};
|
||||
let mut stack_map_sink = binemit::NullStackMapSink {};
|
||||
context
|
||||
.compile_and_emit(
|
||||
isa,
|
||||
&mut code_buf,
|
||||
&mut reloc_sink,
|
||||
&mut trap_sink,
|
||||
&mut stack_map_sink,
|
||||
)
|
||||
.map_err(|error| {
|
||||
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
||||
})?;
|
||||
|
||||
let unwind_info = context.create_unwind_info(isa).map_err(|error| {
|
||||
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
|
||||
})?;
|
||||
|
||||
Ok(CompiledFunction {
|
||||
body: code_buf,
|
||||
jt_offsets: context.func.jt_offsets,
|
||||
unwind_info,
|
||||
relocations: reloc_sink.relocs,
|
||||
stack_maps: Default::default(),
|
||||
stack_slots: Default::default(),
|
||||
traps: Default::default(),
|
||||
value_labels_ranges: Default::default(),
|
||||
address_map: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Collects an iterator of `InstructionAddressMap` into a `Vec` for insertion
|
||||
// into a `FunctionAddressMap`. This will automatically coalesce adjacent
|
||||
// instructions which map to the same original source position.
|
||||
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 this instruction is adjacent to the previous and has the same
|
||||
// source location then we can "coalesce" it with the current
|
||||
// instruction.
|
||||
if cur_offset + cur_len == offset && loc == cur_loc {
|
||||
cur_len += len;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Push an entry for the previous source item.
|
||||
ret.push(InstructionAddressMap {
|
||||
srcloc: cur_loc,
|
||||
code_offset: cur_offset,
|
||||
});
|
||||
// And push a "dummy" entry if necessary to cover the span of ranges,
|
||||
// if any, between the previous source offset and this one.
|
||||
if cur_offset + cur_len != offset {
|
||||
ret.push(InstructionAddressMap {
|
||||
srcloc: ir::SourceLoc::default(),
|
||||
code_offset: cur_offset + cur_len,
|
||||
});
|
||||
}
|
||||
// Update our current location to get extended later or pushed on at
|
||||
// the end.
|
||||
cur_loc = loc;
|
||||
cur_offset = offset;
|
||||
cur_len = len;
|
||||
}
|
||||
ret.push(InstructionAddressMap {
|
||||
srcloc: cur_loc,
|
||||
code_offset: cur_offset,
|
||||
});
|
||||
if cur_offset + cur_len != code_size {
|
||||
ret.push(InstructionAddressMap {
|
||||
srcloc: ir::SourceLoc::default(),
|
||||
code_offset: cur_offset + cur_len,
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Implementation of a relocation sink that just saves all the information for later
|
||||
struct RelocSink {
|
||||
/// Current function index.
|
||||
func_index: FuncIndex,
|
||||
|
||||
/// Relocations recorded for the function.
|
||||
func_relocs: Vec<Relocation>,
|
||||
}
|
||||
|
||||
impl binemit::RelocSink for RelocSink {
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
offset: binemit::CodeOffset,
|
||||
_srcloc: ir::SourceLoc,
|
||||
reloc: binemit::Reloc,
|
||||
name: &ExternalName,
|
||||
addend: binemit::Addend,
|
||||
) {
|
||||
let reloc_target = if let ExternalName::User { namespace, index } = *name {
|
||||
debug_assert_eq!(namespace, 0);
|
||||
RelocationTarget::UserFunc(FuncIndex::from_u32(index))
|
||||
} else if let ExternalName::LibCall(libcall) = *name {
|
||||
RelocationTarget::LibCall(libcall)
|
||||
} else {
|
||||
panic!("unrecognized external name")
|
||||
};
|
||||
self.func_relocs.push(Relocation {
|
||||
reloc,
|
||||
reloc_target,
|
||||
offset,
|
||||
addend,
|
||||
});
|
||||
}
|
||||
|
||||
fn reloc_constant(
|
||||
&mut self,
|
||||
_code_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_constant_offset: ir::ConstantOffset,
|
||||
) {
|
||||
// Do nothing for now: cranelift emits constant data after the function code and also emits
|
||||
// function code with correct relative offsets to the constant data.
|
||||
}
|
||||
|
||||
fn reloc_jt(&mut self, offset: binemit::CodeOffset, reloc: binemit::Reloc, jt: ir::JumpTable) {
|
||||
self.func_relocs.push(Relocation {
|
||||
reloc,
|
||||
reloc_target: RelocationTarget::JumpTable(self.func_index, jt),
|
||||
offset,
|
||||
addend: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl RelocSink {
|
||||
/// Return a new `RelocSink` instance.
|
||||
fn new(func_index: FuncIndex) -> Self {
|
||||
Self {
|
||||
func_index,
|
||||
func_relocs: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of a trap sink that simply stores all trap info in-memory
|
||||
#[derive(Default)]
|
||||
struct TrapSink {
|
||||
/// The in-memory vector of trap info
|
||||
traps: Vec<TrapInformation>,
|
||||
}
|
||||
|
||||
impl TrapSink {
|
||||
/// Create a new `TrapSink`
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl binemit::TrapSink for TrapSink {
|
||||
fn trap(
|
||||
&mut self,
|
||||
code_offset: binemit::CodeOffset,
|
||||
_source_loc: ir::SourceLoc,
|
||||
trap_code: ir::TrapCode,
|
||||
) {
|
||||
self.traps.push(TrapInformation {
|
||||
code_offset,
|
||||
trap_code,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct StackMapSink {
|
||||
infos: Vec<StackMapInformation>,
|
||||
}
|
||||
|
||||
impl binemit::StackMapSink for StackMapSink {
|
||||
fn add_stack_map(&mut self, code_offset: binemit::CodeOffset, stack_map: binemit::StackMap) {
|
||||
self.infos.push(StackMapInformation {
|
||||
code_offset,
|
||||
stack_map,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl StackMapSink {
|
||||
fn finish(mut self) -> Vec<StackMapInformation> {
|
||||
self.infos.sort_unstable_by_key(|info| info.code_offset);
|
||||
self.infos
|
||||
}
|
||||
}
|
||||
|
||||
/// We don't expect trampoline compilation to produce many relocations, so
|
||||
/// this `RelocSink` just asserts that it doesn't recieve most of them, but
|
||||
/// handles libcall ones.
|
||||
#[derive(Default)]
|
||||
struct TrampolineRelocSink {
|
||||
relocs: Vec<Relocation>,
|
||||
}
|
||||
|
||||
impl binemit::RelocSink for TrampolineRelocSink {
|
||||
fn reloc_external(
|
||||
&mut self,
|
||||
offset: binemit::CodeOffset,
|
||||
_srcloc: ir::SourceLoc,
|
||||
reloc: binemit::Reloc,
|
||||
name: &ir::ExternalName,
|
||||
addend: binemit::Addend,
|
||||
) {
|
||||
let reloc_target = if let ir::ExternalName::LibCall(libcall) = *name {
|
||||
RelocationTarget::LibCall(libcall)
|
||||
} else {
|
||||
panic!("unrecognized external name")
|
||||
};
|
||||
self.relocs.push(Relocation {
|
||||
reloc,
|
||||
reloc_target,
|
||||
offset,
|
||||
addend,
|
||||
});
|
||||
}
|
||||
fn reloc_constant(
|
||||
&mut self,
|
||||
_code_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_constant_offset: ir::ConstantOffset,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce constant relocs");
|
||||
}
|
||||
fn reloc_jt(
|
||||
&mut self,
|
||||
_offset: binemit::CodeOffset,
|
||||
_reloc: binemit::Reloc,
|
||||
_jt: ir::JumpTable,
|
||||
) {
|
||||
panic!("trampoline compilation should not produce jump table relocs");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user