1use cranelift_codegen::{
7 binemit,
8 cursor::FuncCursor,
9 ir::{self, AbiParam, ArgumentPurpose, ExternalName, InstBuilder, Signature},
10 isa::{CallConv, TargetIsa},
11 settings, FinalizedMachReloc, FinalizedRelocTarget, MachTrap,
12};
13use cranelift_entity::PrimaryMap;
14use cranelift_wasm::{FuncIndex, WasmFuncType, WasmHeapTopType, WasmHeapType, WasmValType};
15
16use target_lexicon::Architecture;
17use wasmtime_environ::{
18 BuiltinFunctionIndex, FlagValue, RelocationTarget, Trap, TrapInformation, Tunables,
19};
20
21pub use builder::builder;
22
23pub mod isa_builder;
24mod obj;
25pub use obj::*;
26mod compiled_function;
27pub use compiled_function::*;
28
29mod builder;
30mod compiler;
31mod debug;
32mod func_environ;
33mod gc;
34
35const DEBUG_ASSERT_TRAP_CODE: u16 = u16::MAX;
37
38fn blank_sig(isa: &dyn TargetIsa, call_conv: CallConv) -> ir::Signature {
43 let pointer_type = isa.pointer_type();
44 let mut sig = ir::Signature::new(call_conv);
45 sig.params.push(ir::AbiParam::special(
47 pointer_type,
48 ir::ArgumentPurpose::VMContext,
49 ));
50 sig.params.push(ir::AbiParam::new(pointer_type));
51 return sig;
52}
53
54fn unbarriered_store_type_at_offset(
63 isa: &dyn TargetIsa,
64 pos: &mut FuncCursor,
65 ty: WasmValType,
66 flags: ir::MemFlags,
67 base: ir::Value,
68 offset: i32,
69 value: ir::Value,
70) {
71 let ir_ty = value_type(isa, ty);
72 if ir_ty.is_ref() {
73 let value = pos
74 .ins()
75 .bitcast(ir_ty.as_int(), ir::MemFlags::new(), value);
76 let truncated = match isa.pointer_bytes() {
77 4 => value,
78 8 => pos.ins().ireduce(ir::types::I32, value),
79 _ => unreachable!(),
80 };
81 pos.ins().store(flags, truncated, base, offset);
82 } else {
83 pos.ins().store(flags, value, base, offset);
84 }
85}
86
87fn unbarriered_load_type_at_offset(
97 isa: &dyn TargetIsa,
98 pos: &mut FuncCursor,
99 ty: WasmValType,
100 flags: ir::MemFlags,
101 base: ir::Value,
102 offset: i32,
103) -> ir::Value {
104 let ir_ty = value_type(isa, ty);
105 if ir_ty.is_ref() {
106 let gc_ref = pos.ins().load(ir::types::I32, flags, base, offset);
107 let extended = match isa.pointer_bytes() {
108 4 => gc_ref,
109 8 => pos.ins().uextend(ir::types::I64, gc_ref),
110 _ => unreachable!(),
111 };
112 pos.ins().bitcast(ir_ty, ir::MemFlags::new(), extended)
113 } else {
114 pos.ins().load(ir_ty, flags, base, offset)
115 }
116}
117
118fn value_type(isa: &dyn TargetIsa, ty: WasmValType) -> ir::types::Type {
120 match ty {
121 WasmValType::I32 => ir::types::I32,
122 WasmValType::I64 => ir::types::I64,
123 WasmValType::F32 => ir::types::F32,
124 WasmValType::F64 => ir::types::F64,
125 WasmValType::V128 => ir::types::I8X16,
126 WasmValType::Ref(rt) => reference_type(rt.heap_type, isa.pointer_type()),
127 }
128}
129
130fn array_call_signature(isa: &dyn TargetIsa) -> ir::Signature {
146 let mut sig = blank_sig(isa, CallConv::triple_default(isa.triple()));
147 sig.params.push(ir::AbiParam::new(isa.pointer_type()));
151 sig.params.push(ir::AbiParam::new(isa.pointer_type()));
152 sig
153}
154
155fn wasm_call_signature(
157 isa: &dyn TargetIsa,
158 wasm_func_ty: &WasmFuncType,
159 tunables: &Tunables,
160) -> ir::Signature {
161 let call_conv = if tunables.winch_callable {
171 assert!(
172 matches!(
173 isa.triple().architecture,
174 Architecture::X86_64 | Architecture::Aarch64(_)
175 ),
176 "The Winch calling convention is only implemented for x86_64 and aarch64"
177 );
178 CallConv::Winch
179 } else {
180 CallConv::Tail
181 };
182 let mut sig = blank_sig(isa, call_conv);
183 let cvt = |ty: &WasmValType| ir::AbiParam::new(value_type(isa, *ty));
184 sig.params.extend(wasm_func_ty.params().iter().map(&cvt));
185 sig.returns.extend(wasm_func_ty.returns().iter().map(&cvt));
186 sig
187}
188
189fn reference_type(wasm_ht: WasmHeapType, pointer_type: ir::Type) -> ir::Type {
191 match wasm_ht.top() {
192 WasmHeapTopType::Func => pointer_type,
193 WasmHeapTopType::Any | WasmHeapTopType::Extern => match pointer_type {
194 ir::types::I32 => ir::types::R32,
195 ir::types::I64 => ir::types::R64,
196 _ => panic!("unsupported pointer type"),
197 },
198 }
199}
200
201pub const NS_WASM_FUNC: u32 = 0;
206
207pub const NS_WASMTIME_BUILTIN: u32 = 1;
211
212#[derive(Debug, Clone, PartialEq, Eq)]
214pub struct Relocation {
215 pub reloc: binemit::Reloc,
217 pub reloc_target: RelocationTarget,
219 pub offset: binemit::CodeOffset,
221 pub addend: binemit::Addend,
223}
224
225pub fn clif_flags_to_wasmtime(
227 flags: impl IntoIterator<Item = settings::Value>,
228) -> Vec<(&'static str, FlagValue<'static>)> {
229 flags
230 .into_iter()
231 .map(|val| (val.name, to_flag_value(&val)))
232 .collect()
233}
234
235fn to_flag_value(v: &settings::Value) -> FlagValue<'static> {
236 match v.kind() {
237 settings::SettingKind::Enum => FlagValue::Enum(v.as_enum().unwrap()),
238 settings::SettingKind::Num => FlagValue::Num(v.as_num().unwrap()),
239 settings::SettingKind::Bool => FlagValue::Bool(v.as_bool().unwrap()),
240 settings::SettingKind::Preset => unreachable!(),
241 }
242}
243
244pub const ALWAYS_TRAP_CODE: u16 = 100;
249
250pub const CANNOT_ENTER_CODE: u16 = 101;
254
255pub fn mach_trap_to_trap(trap: &MachTrap) -> Option<TrapInformation> {
257 let &MachTrap { offset, code } = trap;
258 Some(TrapInformation {
259 code_offset: offset,
260 trap_code: match code {
261 ir::TrapCode::StackOverflow => Trap::StackOverflow,
262 ir::TrapCode::HeapOutOfBounds => Trap::MemoryOutOfBounds,
263 ir::TrapCode::HeapMisaligned => Trap::HeapMisaligned,
264 ir::TrapCode::TableOutOfBounds => Trap::TableOutOfBounds,
265 ir::TrapCode::IndirectCallToNull => Trap::IndirectCallToNull,
266 ir::TrapCode::BadSignature => Trap::BadSignature,
267 ir::TrapCode::IntegerOverflow => Trap::IntegerOverflow,
268 ir::TrapCode::IntegerDivisionByZero => Trap::IntegerDivisionByZero,
269 ir::TrapCode::BadConversionToInteger => Trap::BadConversionToInteger,
270 ir::TrapCode::UnreachableCodeReached => Trap::UnreachableCodeReached,
271 ir::TrapCode::Interrupt => Trap::Interrupt,
272 ir::TrapCode::User(ALWAYS_TRAP_CODE) => Trap::AlwaysTrapAdapter,
273 ir::TrapCode::User(CANNOT_ENTER_CODE) => Trap::CannotEnterComponent,
274 ir::TrapCode::NullReference => Trap::NullReference,
275 ir::TrapCode::NullI31Ref => Trap::NullI31Ref,
276
277 ir::TrapCode::User(DEBUG_ASSERT_TRAP_CODE) => return None,
281
282 ir::TrapCode::User(_) => unreachable!(),
284 },
285 })
286}
287
288fn mach_reloc_to_reloc(
291 reloc: &FinalizedMachReloc,
292 name_map: &PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
293) -> Relocation {
294 let &FinalizedMachReloc {
295 offset,
296 kind,
297 ref target,
298 addend,
299 } = reloc;
300 let reloc_target = match *target {
301 FinalizedRelocTarget::ExternalName(ExternalName::User(user_func_ref)) => {
302 let name = &name_map[user_func_ref];
303 match name.namespace {
304 NS_WASM_FUNC => RelocationTarget::Wasm(FuncIndex::from_u32(name.index)),
305 NS_WASMTIME_BUILTIN => {
306 RelocationTarget::Builtin(BuiltinFunctionIndex::from_u32(name.index))
307 }
308 _ => panic!("unknown namespace {}", name.namespace),
309 }
310 }
311 FinalizedRelocTarget::ExternalName(ExternalName::LibCall(libcall)) => {
312 let libcall = libcall_cranelift_to_wasmtime(libcall);
313 RelocationTarget::HostLibcall(libcall)
314 }
315 _ => panic!("unrecognized external name"),
316 };
317 Relocation {
318 reloc: kind,
319 reloc_target,
320 offset,
321 addend,
322 }
323}
324
325fn libcall_cranelift_to_wasmtime(call: ir::LibCall) -> wasmtime_environ::obj::LibCall {
326 use wasmtime_environ::obj::LibCall as LC;
327 match call {
328 ir::LibCall::FloorF32 => LC::FloorF32,
329 ir::LibCall::FloorF64 => LC::FloorF64,
330 ir::LibCall::NearestF32 => LC::NearestF32,
331 ir::LibCall::NearestF64 => LC::NearestF64,
332 ir::LibCall::CeilF32 => LC::CeilF32,
333 ir::LibCall::CeilF64 => LC::CeilF64,
334 ir::LibCall::TruncF32 => LC::TruncF32,
335 ir::LibCall::TruncF64 => LC::TruncF64,
336 ir::LibCall::FmaF32 => LC::FmaF32,
337 ir::LibCall::FmaF64 => LC::FmaF64,
338 ir::LibCall::X86Pshufb => LC::X86Pshufb,
339 _ => panic!("cranelift emitted a libcall wasmtime does not support: {call:?}"),
340 }
341}
342
343struct BuiltinFunctionSignatures {
345 pointer_type: ir::Type,
346
347 #[cfg(feature = "gc")]
348 reference_type: ir::Type,
349
350 call_conv: CallConv,
351}
352
353impl BuiltinFunctionSignatures {
354 fn new(isa: &dyn TargetIsa) -> Self {
355 Self {
356 pointer_type: isa.pointer_type(),
357 #[cfg(feature = "gc")]
358 reference_type: match isa.pointer_type() {
359 ir::types::I32 => ir::types::R32,
360 ir::types::I64 => ir::types::R64,
361 _ => panic!(),
362 },
363 call_conv: CallConv::triple_default(isa.triple()),
364 }
365 }
366
367 fn vmctx(&self) -> AbiParam {
368 AbiParam::special(self.pointer_type, ArgumentPurpose::VMContext)
369 }
370
371 #[cfg(feature = "gc")]
372 fn reference(&self) -> AbiParam {
373 AbiParam::new(self.reference_type)
374 }
375
376 fn pointer(&self) -> AbiParam {
377 AbiParam::new(self.pointer_type)
378 }
379
380 fn i32(&self) -> AbiParam {
381 AbiParam::new(ir::types::I32).uext()
391 }
392
393 fn i64(&self) -> AbiParam {
394 AbiParam::new(ir::types::I64)
395 }
396
397 fn signature(&self, builtin: BuiltinFunctionIndex) -> Signature {
398 let mut _cur = 0;
399 macro_rules! iter {
400 (
401 $(
402 $( #[$attr:meta] )*
403 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
404 )*
405 ) => {
406 $(
407 $( #[$attr] )*
408 if _cur == builtin.index() {
409 return Signature {
410 params: vec![ $( self.$param() ),* ],
411 returns: vec![ $( self.$result() )? ],
412 call_conv: self.call_conv,
413 };
414 }
415 _cur += 1;
416 )*
417 };
418 }
419
420 wasmtime_environ::foreach_builtin_function!(iter);
421
422 unreachable!();
423 }
424}
425
426const I31_REF_DISCRIMINANT: u32 = 1;