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::{DefinedFuncIndex, FuncIndex, WasmFuncType, 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
35type CompiledFunctionsMetadata<'a> = PrimaryMap<DefinedFuncIndex, &'a CompiledFunctionMetadata>;
36
37const DEBUG_ASSERT_TRAP_CODE: u16 = u16::MAX;
39
40fn blank_sig(isa: &dyn TargetIsa, call_conv: CallConv) -> ir::Signature {
45 let pointer_type = isa.pointer_type();
46 let mut sig = ir::Signature::new(call_conv);
47 sig.params.push(ir::AbiParam::special(
49 pointer_type,
50 ir::ArgumentPurpose::VMContext,
51 ));
52 sig.params.push(ir::AbiParam::new(pointer_type));
53 return sig;
54}
55
56fn unbarriered_store_type_at_offset(
65 isa: &dyn TargetIsa,
66 pos: &mut FuncCursor,
67 ty: WasmValType,
68 flags: ir::MemFlags,
69 base: ir::Value,
70 offset: i32,
71 value: ir::Value,
72) {
73 let ir_ty = value_type(isa, ty);
74 if ir_ty.is_ref() {
75 let value = pos
76 .ins()
77 .bitcast(ir_ty.as_int(), ir::MemFlags::new(), value);
78 let truncated = match isa.pointer_bytes() {
79 4 => value,
80 8 => pos.ins().ireduce(ir::types::I32, value),
81 _ => unreachable!(),
82 };
83 pos.ins().store(flags, truncated, base, offset);
84 } else {
85 pos.ins().store(flags, value, base, offset);
86 }
87}
88
89fn unbarriered_load_type_at_offset(
99 isa: &dyn TargetIsa,
100 pos: &mut FuncCursor,
101 ty: WasmValType,
102 flags: ir::MemFlags,
103 base: ir::Value,
104 offset: i32,
105) -> ir::Value {
106 let ir_ty = value_type(isa, ty);
107 if ir_ty.is_ref() {
108 let gc_ref = pos.ins().load(ir::types::I32, flags, base, offset);
109 let extended = match isa.pointer_bytes() {
110 4 => gc_ref,
111 8 => pos.ins().uextend(ir::types::I64, gc_ref),
112 _ => unreachable!(),
113 };
114 pos.ins().bitcast(ir_ty, ir::MemFlags::new(), extended)
115 } else {
116 pos.ins().load(ir_ty, flags, base, offset)
117 }
118}
119
120fn value_type(isa: &dyn TargetIsa, ty: WasmValType) -> ir::types::Type {
122 match ty {
123 WasmValType::I32 => ir::types::I32,
124 WasmValType::I64 => ir::types::I64,
125 WasmValType::F32 => ir::types::F32,
126 WasmValType::F64 => ir::types::F64,
127 WasmValType::V128 => ir::types::I8X16,
128 WasmValType::Ref(rt) => reference_type(rt.heap_type, isa.pointer_type()),
129 }
130}
131
132fn native_call_signature(isa: &dyn TargetIsa, wasm: &WasmFuncType) -> ir::Signature {
169 let mut sig = blank_sig(isa, CallConv::triple_default(isa.triple()));
170 let cvt = |ty: &WasmValType| ir::AbiParam::new(value_type(isa, *ty));
171 sig.params.extend(wasm.params().iter().map(&cvt));
172 if let Some(first_ret) = wasm.returns().get(0) {
173 sig.returns.push(cvt(first_ret));
174 }
175 if wasm.returns().len() > 1 {
176 sig.params.push(ir::AbiParam::new(isa.pointer_type()));
177 }
178 sig
179}
180
181fn array_call_signature(isa: &dyn TargetIsa) -> ir::Signature {
197 let mut sig = blank_sig(isa, CallConv::triple_default(isa.triple()));
198 sig.params.push(ir::AbiParam::new(isa.pointer_type()));
202 sig.params.push(ir::AbiParam::new(isa.pointer_type()));
203 sig
204}
205
206fn wasm_call_signature(
208 isa: &dyn TargetIsa,
209 wasm_func_ty: &WasmFuncType,
210 tunables: &Tunables,
211) -> ir::Signature {
212 let call_conv = match isa.triple().architecture {
222 arch if tunables.tail_callable => {
226 assert_ne!(
227 arch,
228 Architecture::S390x,
229 "https://github.com/bytecodealliance/wasmtime/issues/6530"
230 );
231 CallConv::Tail
232 }
233
234 arch if tunables.winch_callable => {
236 assert!(
237 matches!(arch, Architecture::X86_64 | Architecture::Aarch64(_)),
238 "The Winch calling convention is only implemented for x86_64 and aarch64"
239 );
240 CallConv::Winch
241 }
242
243 Architecture::S390x => CallConv::WasmtimeSystemV,
249
250 _ => CallConv::Fast,
253 };
254 let mut sig = blank_sig(isa, call_conv);
255 let cvt = |ty: &WasmValType| ir::AbiParam::new(value_type(isa, *ty));
256 sig.params.extend(wasm_func_ty.params().iter().map(&cvt));
257 sig.returns.extend(wasm_func_ty.returns().iter().map(&cvt));
258 sig
259}
260
261fn reference_type(wasm_ht: WasmHeapType, pointer_type: ir::Type) -> ir::Type {
263 match wasm_ht {
264 WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => pointer_type,
265 WasmHeapType::Extern | WasmHeapType::Any | WasmHeapType::I31 | WasmHeapType::None => {
266 match pointer_type {
267 ir::types::I32 => ir::types::R32,
268 ir::types::I64 => ir::types::R64,
269 _ => panic!("unsupported pointer type"),
270 }
271 }
272 }
273}
274
275pub const NS_WASM_FUNC: u32 = 0;
280
281pub const NS_WASMTIME_BUILTIN: u32 = 1;
285
286#[derive(Debug, Clone, PartialEq, Eq)]
288pub struct Relocation {
289 pub reloc: binemit::Reloc,
291 pub reloc_target: RelocationTarget,
293 pub offset: binemit::CodeOffset,
295 pub addend: binemit::Addend,
297}
298
299pub fn clif_flags_to_wasmtime(
301 flags: impl IntoIterator<Item = settings::Value>,
302) -> Vec<(&'static str, FlagValue<'static>)> {
303 flags
304 .into_iter()
305 .map(|val| (val.name, to_flag_value(&val)))
306 .collect()
307}
308
309fn to_flag_value(v: &settings::Value) -> FlagValue<'static> {
310 match v.kind() {
311 settings::SettingKind::Enum => FlagValue::Enum(v.as_enum().unwrap()),
312 settings::SettingKind::Num => FlagValue::Num(v.as_num().unwrap()),
313 settings::SettingKind::Bool => FlagValue::Bool(v.as_bool().unwrap()),
314 settings::SettingKind::Preset => unreachable!(),
315 }
316}
317
318pub const ALWAYS_TRAP_CODE: u16 = 100;
323
324pub const CANNOT_ENTER_CODE: u16 = 101;
328
329pub fn mach_trap_to_trap(trap: &MachTrap) -> Option<TrapInformation> {
331 let &MachTrap { offset, code } = trap;
332 Some(TrapInformation {
333 code_offset: offset,
334 trap_code: match code {
335 ir::TrapCode::StackOverflow => Trap::StackOverflow,
336 ir::TrapCode::HeapOutOfBounds => Trap::MemoryOutOfBounds,
337 ir::TrapCode::HeapMisaligned => Trap::HeapMisaligned,
338 ir::TrapCode::TableOutOfBounds => Trap::TableOutOfBounds,
339 ir::TrapCode::IndirectCallToNull => Trap::IndirectCallToNull,
340 ir::TrapCode::BadSignature => Trap::BadSignature,
341 ir::TrapCode::IntegerOverflow => Trap::IntegerOverflow,
342 ir::TrapCode::IntegerDivisionByZero => Trap::IntegerDivisionByZero,
343 ir::TrapCode::BadConversionToInteger => Trap::BadConversionToInteger,
344 ir::TrapCode::UnreachableCodeReached => Trap::UnreachableCodeReached,
345 ir::TrapCode::Interrupt => Trap::Interrupt,
346 ir::TrapCode::User(ALWAYS_TRAP_CODE) => Trap::AlwaysTrapAdapter,
347 ir::TrapCode::User(CANNOT_ENTER_CODE) => Trap::CannotEnterComponent,
348 ir::TrapCode::NullReference => Trap::NullReference,
349 ir::TrapCode::NullI31Ref => Trap::NullI31Ref,
350
351 ir::TrapCode::User(DEBUG_ASSERT_TRAP_CODE) => return None,
355
356 ir::TrapCode::User(_) => unreachable!(),
358 },
359 })
360}
361
362fn mach_reloc_to_reloc(
365 reloc: &FinalizedMachReloc,
366 name_map: &PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
367) -> Relocation {
368 let &FinalizedMachReloc {
369 offset,
370 kind,
371 ref target,
372 addend,
373 } = reloc;
374 let reloc_target = match *target {
375 FinalizedRelocTarget::ExternalName(ExternalName::User(user_func_ref)) => {
376 let name = &name_map[user_func_ref];
377 match name.namespace {
378 NS_WASM_FUNC => RelocationTarget::Wasm(FuncIndex::from_u32(name.index)),
379 NS_WASMTIME_BUILTIN => {
380 RelocationTarget::Builtin(BuiltinFunctionIndex::from_u32(name.index))
381 }
382 _ => panic!("unknown namespace {}", name.namespace),
383 }
384 }
385 FinalizedRelocTarget::ExternalName(ExternalName::LibCall(libcall)) => {
386 let libcall = libcall_cranelift_to_wasmtime(libcall);
387 RelocationTarget::HostLibcall(libcall)
388 }
389 _ => panic!("unrecognized external name"),
390 };
391 Relocation {
392 reloc: kind,
393 reloc_target,
394 offset,
395 addend,
396 }
397}
398
399fn libcall_cranelift_to_wasmtime(call: ir::LibCall) -> wasmtime_environ::obj::LibCall {
400 use wasmtime_environ::obj::LibCall as LC;
401 match call {
402 ir::LibCall::FloorF32 => LC::FloorF32,
403 ir::LibCall::FloorF64 => LC::FloorF64,
404 ir::LibCall::NearestF32 => LC::NearestF32,
405 ir::LibCall::NearestF64 => LC::NearestF64,
406 ir::LibCall::CeilF32 => LC::CeilF32,
407 ir::LibCall::CeilF64 => LC::CeilF64,
408 ir::LibCall::TruncF32 => LC::TruncF32,
409 ir::LibCall::TruncF64 => LC::TruncF64,
410 ir::LibCall::FmaF32 => LC::FmaF32,
411 ir::LibCall::FmaF64 => LC::FmaF64,
412 ir::LibCall::X86Pshufb => LC::X86Pshufb,
413 _ => panic!("cranelift emitted a libcall wasmtime does not support: {call:?}"),
414 }
415}
416
417struct BuiltinFunctionSignatures {
419 pointer_type: ir::Type,
420
421 #[cfg(feature = "gc")]
422 reference_type: ir::Type,
423
424 call_conv: CallConv,
425}
426
427impl BuiltinFunctionSignatures {
428 fn new(isa: &dyn TargetIsa) -> Self {
429 Self {
430 pointer_type: isa.pointer_type(),
431 #[cfg(feature = "gc")]
432 reference_type: match isa.pointer_type() {
433 ir::types::I32 => ir::types::R32,
434 ir::types::I64 => ir::types::R64,
435 _ => panic!(),
436 },
437 call_conv: CallConv::triple_default(isa.triple()),
438 }
439 }
440
441 fn vmctx(&self) -> AbiParam {
442 AbiParam::special(self.pointer_type, ArgumentPurpose::VMContext)
443 }
444
445 #[cfg(feature = "gc")]
446 fn reference(&self) -> AbiParam {
447 AbiParam::new(self.reference_type)
448 }
449
450 fn pointer(&self) -> AbiParam {
451 AbiParam::new(self.pointer_type)
452 }
453
454 fn i32(&self) -> AbiParam {
455 AbiParam::new(ir::types::I32).uext()
465 }
466
467 fn i64(&self) -> AbiParam {
468 AbiParam::new(ir::types::I64)
469 }
470
471 fn signature(&self, builtin: BuiltinFunctionIndex) -> Signature {
472 let mut _cur = 0;
473 macro_rules! iter {
474 (
475 $(
476 $( #[$attr:meta] )*
477 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
478 )*
479 ) => {
480 $(
481 $( #[$attr] )*
482 if _cur == builtin.index() {
483 return Signature {
484 params: vec![ $( self.$param() ),* ],
485 returns: vec![ $( self.$result() )? ],
486 call_conv: self.call_conv,
487 };
488 }
489 _cur += 1;
490 )*
491 };
492 }
493
494 wasmtime_environ::foreach_builtin_function!(iter);
495
496 unreachable!();
497 }
498}
499
500const I31_REF_DISCRIMINANT: u32 = 1;