#![warn(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
use cranelift_codegen::{
binemit,
cursor::FuncCursor,
ir::{self, AbiParam, ArgumentPurpose, ExternalName, InstBuilder, Signature, TrapCode},
isa::{CallConv, TargetIsa},
settings, FinalizedMachReloc, FinalizedRelocTarget, MachTrap,
};
use cranelift_entity::PrimaryMap;
use target_lexicon::Architecture;
use wasmtime_environ::{
BuiltinFunctionIndex, FlagValue, FuncIndex, RelocationTarget, Trap, TrapInformation, Tunables,
WasmFuncType, WasmHeapTopType, WasmHeapType, WasmValType,
};
pub use builder::builder;
pub mod isa_builder;
mod obj;
pub use obj::*;
mod compiled_function;
pub use compiled_function::*;
mod builder;
mod compiler;
mod debug;
mod func_environ;
mod gc;
mod translate;
const TRAP_INTERNAL_ASSERT: TrapCode = TrapCode::unwrap_user(1);
const TRAP_OFFSET: u8 = 2;
pub const TRAP_ALWAYS: TrapCode =
TrapCode::unwrap_user(Trap::AlwaysTrapAdapter as u8 + TRAP_OFFSET);
pub const TRAP_CANNOT_ENTER: TrapCode =
TrapCode::unwrap_user(Trap::CannotEnterComponent as u8 + TRAP_OFFSET);
pub const TRAP_INDIRECT_CALL_TO_NULL: TrapCode =
TrapCode::unwrap_user(Trap::IndirectCallToNull as u8 + TRAP_OFFSET);
pub const TRAP_BAD_SIGNATURE: TrapCode =
TrapCode::unwrap_user(Trap::BadSignature as u8 + TRAP_OFFSET);
pub const TRAP_NULL_REFERENCE: TrapCode =
TrapCode::unwrap_user(Trap::NullReference as u8 + TRAP_OFFSET);
pub const TRAP_ALLOCATION_TOO_LARGE: TrapCode =
TrapCode::unwrap_user(Trap::AllocationTooLarge as u8 + TRAP_OFFSET);
pub const TRAP_ARRAY_OUT_OF_BOUNDS: TrapCode =
TrapCode::unwrap_user(Trap::ArrayOutOfBounds as u8 + TRAP_OFFSET);
pub const TRAP_UNREACHABLE: TrapCode =
TrapCode::unwrap_user(Trap::UnreachableCodeReached as u8 + TRAP_OFFSET);
pub const TRAP_HEAP_MISALIGNED: TrapCode =
TrapCode::unwrap_user(Trap::HeapMisaligned as u8 + TRAP_OFFSET);
pub const TRAP_TABLE_OUT_OF_BOUNDS: TrapCode =
TrapCode::unwrap_user(Trap::TableOutOfBounds as u8 + TRAP_OFFSET);
pub const TRAP_CAST_FAILURE: TrapCode =
TrapCode::unwrap_user(Trap::CastFailure as u8 + TRAP_OFFSET);
fn blank_sig(isa: &dyn TargetIsa, call_conv: CallConv) -> ir::Signature {
let pointer_type = isa.pointer_type();
let mut sig = ir::Signature::new(call_conv);
sig.params.push(ir::AbiParam::special(
pointer_type,
ir::ArgumentPurpose::VMContext,
));
sig.params.push(ir::AbiParam::new(pointer_type));
return sig;
}
fn unbarriered_store_type_at_offset(
pos: &mut FuncCursor,
flags: ir::MemFlags,
base: ir::Value,
offset: i32,
value: ir::Value,
) {
pos.ins().store(flags, value, base, offset);
}
fn unbarriered_load_type_at_offset(
isa: &dyn TargetIsa,
pos: &mut FuncCursor,
ty: WasmValType,
flags: ir::MemFlags,
base: ir::Value,
offset: i32,
) -> ir::Value {
let ir_ty = value_type(isa, ty);
pos.ins().load(ir_ty, flags, base, offset)
}
fn value_type(isa: &dyn TargetIsa, ty: WasmValType) -> ir::types::Type {
match ty {
WasmValType::I32 => ir::types::I32,
WasmValType::I64 => ir::types::I64,
WasmValType::F32 => ir::types::F32,
WasmValType::F64 => ir::types::F64,
WasmValType::V128 => ir::types::I8X16,
WasmValType::Ref(rt) => reference_type(rt.heap_type, isa.pointer_type()),
}
}
fn array_call_signature(isa: &dyn TargetIsa) -> ir::Signature {
let mut sig = blank_sig(isa, CallConv::triple_default(isa.triple()));
sig.params.push(ir::AbiParam::new(isa.pointer_type()));
sig.params.push(ir::AbiParam::new(isa.pointer_type()));
sig
}
fn wasm_call_signature(
isa: &dyn TargetIsa,
wasm_func_ty: &WasmFuncType,
tunables: &Tunables,
) -> ir::Signature {
let call_conv = if tunables.winch_callable {
assert!(
matches!(
isa.triple().architecture,
Architecture::X86_64 | Architecture::Aarch64(_)
),
"The Winch calling convention is only implemented for x86_64 and aarch64"
);
CallConv::Winch
} else {
CallConv::Tail
};
let mut sig = blank_sig(isa, call_conv);
let cvt = |ty: &WasmValType| ir::AbiParam::new(value_type(isa, *ty));
sig.params.extend(wasm_func_ty.params().iter().map(&cvt));
sig.returns.extend(wasm_func_ty.returns().iter().map(&cvt));
sig
}
fn reference_type(wasm_ht: WasmHeapType, pointer_type: ir::Type) -> ir::Type {
match wasm_ht.top() {
WasmHeapTopType::Func => pointer_type,
WasmHeapTopType::Any | WasmHeapTopType::Extern => ir::types::I32,
}
}
pub const NS_WASM_FUNC: u32 = 0;
pub const NS_WASMTIME_BUILTIN: u32 = 1;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Relocation {
pub reloc: binemit::Reloc,
pub reloc_target: RelocationTarget,
pub offset: binemit::CodeOffset,
pub addend: binemit::Addend,
}
pub fn clif_flags_to_wasmtime(
flags: impl IntoIterator<Item = settings::Value>,
) -> Vec<(&'static str, FlagValue<'static>)> {
flags
.into_iter()
.map(|val| (val.name, to_flag_value(&val)))
.collect()
}
fn to_flag_value(v: &settings::Value) -> FlagValue<'static> {
match v.kind() {
settings::SettingKind::Enum => FlagValue::Enum(v.as_enum().unwrap()),
settings::SettingKind::Num => FlagValue::Num(v.as_num().unwrap()),
settings::SettingKind::Bool => FlagValue::Bool(v.as_bool().unwrap()),
settings::SettingKind::Preset => unreachable!(),
}
}
pub fn mach_trap_to_trap(trap: &MachTrap) -> Option<TrapInformation> {
let &MachTrap { offset, code } = trap;
Some(TrapInformation {
code_offset: offset,
trap_code: clif_trap_to_env_trap(code)?,
})
}
fn clif_trap_to_env_trap(trap: ir::TrapCode) -> Option<Trap> {
Some(match trap {
ir::TrapCode::STACK_OVERFLOW => Trap::StackOverflow,
ir::TrapCode::HEAP_OUT_OF_BOUNDS => Trap::MemoryOutOfBounds,
ir::TrapCode::INTEGER_OVERFLOW => Trap::IntegerOverflow,
ir::TrapCode::INTEGER_DIVISION_BY_ZERO => Trap::IntegerDivisionByZero,
ir::TrapCode::BAD_CONVERSION_TO_INTEGER => Trap::BadConversionToInteger,
TRAP_INTERNAL_ASSERT => return None,
other => Trap::from_u8(other.as_raw().get() - TRAP_OFFSET).unwrap(),
})
}
fn mach_reloc_to_reloc(
reloc: &FinalizedMachReloc,
name_map: &PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
) -> Relocation {
let &FinalizedMachReloc {
offset,
kind,
ref target,
addend,
} = reloc;
let reloc_target = match *target {
FinalizedRelocTarget::ExternalName(ExternalName::User(user_func_ref)) => {
let name = &name_map[user_func_ref];
match name.namespace {
NS_WASM_FUNC => RelocationTarget::Wasm(FuncIndex::from_u32(name.index)),
NS_WASMTIME_BUILTIN => {
RelocationTarget::Builtin(BuiltinFunctionIndex::from_u32(name.index))
}
_ => panic!("unknown namespace {}", name.namespace),
}
}
FinalizedRelocTarget::ExternalName(ExternalName::LibCall(libcall)) => {
let libcall = libcall_cranelift_to_wasmtime(libcall);
RelocationTarget::HostLibcall(libcall)
}
_ => panic!("unrecognized external name"),
};
Relocation {
reloc: kind,
reloc_target,
offset,
addend,
}
}
fn libcall_cranelift_to_wasmtime(call: ir::LibCall) -> wasmtime_environ::obj::LibCall {
use wasmtime_environ::obj::LibCall as LC;
match call {
ir::LibCall::FloorF32 => LC::FloorF32,
ir::LibCall::FloorF64 => LC::FloorF64,
ir::LibCall::NearestF32 => LC::NearestF32,
ir::LibCall::NearestF64 => LC::NearestF64,
ir::LibCall::CeilF32 => LC::CeilF32,
ir::LibCall::CeilF64 => LC::CeilF64,
ir::LibCall::TruncF32 => LC::TruncF32,
ir::LibCall::TruncF64 => LC::TruncF64,
ir::LibCall::FmaF32 => LC::FmaF32,
ir::LibCall::FmaF64 => LC::FmaF64,
ir::LibCall::X86Pshufb => LC::X86Pshufb,
_ => panic!("cranelift emitted a libcall wasmtime does not support: {call:?}"),
}
}
struct BuiltinFunctionSignatures {
pointer_type: ir::Type,
#[cfg(feature = "gc")]
reference_type: ir::Type,
call_conv: CallConv,
}
impl BuiltinFunctionSignatures {
fn new(isa: &dyn TargetIsa) -> Self {
Self {
pointer_type: isa.pointer_type(),
call_conv: CallConv::triple_default(isa.triple()),
#[cfg(feature = "gc")]
reference_type: ir::types::I32,
}
}
fn vmctx(&self) -> AbiParam {
AbiParam::special(self.pointer_type, ArgumentPurpose::VMContext)
}
#[cfg(feature = "gc")]
fn reference(&self) -> AbiParam {
AbiParam::new(self.reference_type)
}
fn pointer(&self) -> AbiParam {
AbiParam::new(self.pointer_type)
}
fn i32(&self) -> AbiParam {
AbiParam::new(ir::types::I32).uext()
}
fn i64(&self) -> AbiParam {
AbiParam::new(ir::types::I64)
}
fn f64(&self) -> AbiParam {
AbiParam::new(ir::types::F64)
}
fn u8(&self) -> AbiParam {
AbiParam::new(ir::types::I8)
}
fn signature(&self, builtin: BuiltinFunctionIndex) -> Signature {
let mut _cur = 0;
macro_rules! iter {
(
$(
$( #[$attr:meta] )*
$name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
)*
) => {
$(
$( #[$attr] )*
if _cur == builtin.index() {
return Signature {
params: vec![ $( self.$param() ),* ],
returns: vec![ $( self.$result() )? ],
call_conv: self.call_conv,
};
}
_cur += 1;
)*
};
}
wasmtime_environ::foreach_builtin_function!(iter);
unreachable!();
}
}
const I31_REF_DISCRIMINANT: u32 = 1;