use crate::cache::ModuleCacheDataTupleType;
use crate::module;
use crate::module_environ::FunctionBodyData;
use crate::CacheConfig;
use cranelift_codegen::{binemit, ir, isa, Context};
use cranelift_entity::PrimaryMap;
use cranelift_wasm::{DefinedFuncIndex, FuncIndex, ModuleTranslationState, WasmError};
use serde::{Deserialize, Serialize};
use std::ops::Range;
use thiserror::Error;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct FDERelocEntry(pub i64, pub usize, pub u8);
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct CompiledFunctionUnwindInfoReloc {
pub offset: u32,
pub addend: u32,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum CompiledFunctionUnwindInfo {
None,
Windows(Vec<u8>),
FrameLayout(Vec<u8>, usize, Vec<FDERelocEntry>),
}
impl CompiledFunctionUnwindInfo {
pub fn new(isa: &dyn isa::TargetIsa, context: &Context) -> Self {
use cranelift_codegen::binemit::{
FrameUnwindKind, FrameUnwindOffset, FrameUnwindSink, Reloc,
};
use cranelift_codegen::isa::CallConv;
struct Sink(Vec<u8>, usize, Vec<FDERelocEntry>);
impl FrameUnwindSink for Sink {
fn len(&self) -> FrameUnwindOffset {
self.0.len()
}
fn bytes(&mut self, b: &[u8]) {
self.0.extend_from_slice(b);
}
fn reserve(&mut self, len: usize) {
self.0.reserve(len)
}
fn reloc(&mut self, r: Reloc, off: FrameUnwindOffset) {
self.2.push(FDERelocEntry(
0,
off,
match r {
Reloc::Abs4 => 4,
Reloc::Abs8 => 8,
_ => {
panic!("unexpected reloc type");
}
},
))
}
fn set_entry_offset(&mut self, off: FrameUnwindOffset) {
self.1 = off;
}
}
let kind = match context.func.signature.call_conv {
CallConv::SystemV | CallConv::Fast | CallConv::Cold => FrameUnwindKind::Libunwind,
CallConv::WindowsFastcall => FrameUnwindKind::Fastcall,
_ => {
return CompiledFunctionUnwindInfo::None;
}
};
let mut sink = Sink(Vec::new(), 0, Vec::new());
context.emit_unwind_info(isa, kind, &mut sink);
let Sink(data, offset, relocs) = sink;
if data.is_empty() {
return CompiledFunctionUnwindInfo::None;
}
match kind {
FrameUnwindKind::Fastcall => CompiledFunctionUnwindInfo::Windows(data),
FrameUnwindKind::Libunwind => {
CompiledFunctionUnwindInfo::FrameLayout(data, offset, relocs)
}
}
}
pub fn is_empty(&self) -> bool {
match self {
CompiledFunctionUnwindInfo::None => true,
CompiledFunctionUnwindInfo::Windows(d) => d.is_empty(),
CompiledFunctionUnwindInfo::FrameLayout(c, _, _) => c.is_empty(),
}
}
pub fn len(&self) -> usize {
match self {
CompiledFunctionUnwindInfo::None => 0,
CompiledFunctionUnwindInfo::Windows(d) => d.len(),
CompiledFunctionUnwindInfo::FrameLayout(c, _, _) => c.len(),
}
}
pub fn serialize(&self, dest: &mut [u8], relocs: &mut Vec<CompiledFunctionUnwindInfoReloc>) {
match self {
CompiledFunctionUnwindInfo::None => (),
CompiledFunctionUnwindInfo::Windows(d) => {
dest.copy_from_slice(d);
}
CompiledFunctionUnwindInfo::FrameLayout(code, _fde_offset, r) => {
dest.copy_from_slice(code);
r.iter().for_each(move |r| {
assert_eq!(r.2, 8);
relocs.push(CompiledFunctionUnwindInfoReloc {
offset: r.1 as u32,
addend: r.0 as u32,
})
});
}
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct CompiledFunction {
pub body: Vec<u8>,
pub jt_offsets: ir::JumpTableOffsets,
pub unwind_info: CompiledFunctionUnwindInfo,
}
type Functions = PrimaryMap<DefinedFuncIndex, CompiledFunction>;
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
pub struct Compilation {
functions: Functions,
}
impl Compilation {
pub fn new(functions: Functions) -> Self {
Self { functions }
}
pub fn from_buffer(
buffer: Vec<u8>,
functions: impl IntoIterator<Item = (Range<usize>, ir::JumpTableOffsets, Range<usize>)>,
) -> Self {
Self::new(
functions
.into_iter()
.map(|(body_range, jt_offsets, unwind_range)| CompiledFunction {
body: buffer[body_range].to_vec(),
jt_offsets,
unwind_info: CompiledFunctionUnwindInfo::Windows(buffer[unwind_range].to_vec()),
})
.collect(),
)
}
pub fn get(&self, func: DefinedFuncIndex) -> &CompiledFunction {
&self.functions[func]
}
pub fn len(&self) -> usize {
self.functions.len()
}
pub fn is_empty(&self) -> bool {
self.functions.is_empty()
}
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)
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Relocation {
pub reloc: binemit::Reloc,
pub reloc_target: RelocationTarget,
pub offset: binemit::CodeOffset,
pub addend: binemit::Addend,
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub enum RelocationTarget {
UserFunc(FuncIndex),
LibCall(ir::LibCall),
Memory32Grow,
ImportedMemory32Grow,
Memory32Size,
ImportedMemory32Size,
JumpTable(FuncIndex, ir::JumpTable),
}
pub type Relocations = PrimaryMap<DefinedFuncIndex, Vec<Relocation>>;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct TrapInformation {
pub code_offset: binemit::CodeOffset,
pub source_loc: ir::SourceLoc,
pub trap_code: ir::TrapCode,
}
pub type Traps = PrimaryMap<DefinedFuncIndex, Vec<TrapInformation>>;
#[derive(Error, Debug)]
pub enum CompileError {
#[error("WebAssembly translation error")]
Wasm(#[from] WasmError),
#[error("Compilation error: {0}")]
Codegen(String),
#[error("Debug info is not supported with this configuration")]
DebugInfoNotSupported,
}
pub trait Compiler {
fn compile_module<'data, 'module>(
module: &'module module::Module,
module_translation: &ModuleTranslationState,
function_body_inputs: PrimaryMap<DefinedFuncIndex, FunctionBodyData<'data>>,
isa: &dyn isa::TargetIsa,
generate_debug_info: bool,
cache_config: &CacheConfig,
) -> Result<ModuleCacheDataTupleType, CompileError>;
}