wasmtime_internal_cranelift/
lib.rs1#![warn(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
16
17use cranelift_codegen::{
18 FinalizedMachReloc, FinalizedRelocTarget, MachTrap, binemit,
19 cursor::FuncCursor,
20 ir::{self, AbiParam, ArgumentPurpose, ExternalName, InstBuilder, Signature, TrapCode},
21 isa::{CallConv, TargetIsa},
22 settings,
23};
24use cranelift_entity::PrimaryMap;
25
26use target_lexicon::Architecture;
27use wasmtime_environ::{
28 BuiltinFunctionIndex, FlagValue, FuncIndex, RelocationTarget, Trap, TrapInformation, Tunables,
29 WasmFuncType, WasmHeapTopType, WasmHeapType, WasmValType,
30};
31
32pub use builder::builder;
33
34pub mod isa_builder;
35mod obj;
36pub use obj::*;
37mod compiled_function;
38pub use compiled_function::*;
39
40mod bounds_checks;
41mod builder;
42mod compiler;
43mod debug;
44mod func_environ;
45mod translate;
46
47use self::compiler::Compiler;
48
49const TRAP_INTERNAL_ASSERT: TrapCode = TrapCode::unwrap_user(1);
50const TRAP_OFFSET: u8 = 2;
51pub const TRAP_ALWAYS: TrapCode =
52 TrapCode::unwrap_user(Trap::AlwaysTrapAdapter as u8 + TRAP_OFFSET);
53pub const TRAP_CANNOT_ENTER: TrapCode =
54 TrapCode::unwrap_user(Trap::CannotEnterComponent as u8 + TRAP_OFFSET);
55pub const TRAP_INDIRECT_CALL_TO_NULL: TrapCode =
56 TrapCode::unwrap_user(Trap::IndirectCallToNull as u8 + TRAP_OFFSET);
57pub const TRAP_BAD_SIGNATURE: TrapCode =
58 TrapCode::unwrap_user(Trap::BadSignature as u8 + TRAP_OFFSET);
59pub const TRAP_NULL_REFERENCE: TrapCode =
60 TrapCode::unwrap_user(Trap::NullReference as u8 + TRAP_OFFSET);
61pub const TRAP_ALLOCATION_TOO_LARGE: TrapCode =
62 TrapCode::unwrap_user(Trap::AllocationTooLarge as u8 + TRAP_OFFSET);
63pub const TRAP_ARRAY_OUT_OF_BOUNDS: TrapCode =
64 TrapCode::unwrap_user(Trap::ArrayOutOfBounds as u8 + TRAP_OFFSET);
65pub const TRAP_UNREACHABLE: TrapCode =
66 TrapCode::unwrap_user(Trap::UnreachableCodeReached as u8 + TRAP_OFFSET);
67pub const TRAP_HEAP_MISALIGNED: TrapCode =
68 TrapCode::unwrap_user(Trap::HeapMisaligned as u8 + TRAP_OFFSET);
69pub const TRAP_TABLE_OUT_OF_BOUNDS: TrapCode =
70 TrapCode::unwrap_user(Trap::TableOutOfBounds as u8 + TRAP_OFFSET);
71pub const TRAP_UNHANDLED_TAG: TrapCode =
72 TrapCode::unwrap_user(Trap::UnhandledTag as u8 + TRAP_OFFSET);
73pub const TRAP_CONTINUATION_ALREADY_CONSUMED: TrapCode =
74 TrapCode::unwrap_user(Trap::ContinuationAlreadyConsumed as u8 + TRAP_OFFSET);
75pub const TRAP_CAST_FAILURE: TrapCode =
76 TrapCode::unwrap_user(Trap::CastFailure as u8 + TRAP_OFFSET);
77
78fn blank_sig(isa: &dyn TargetIsa, call_conv: CallConv) -> ir::Signature {
83 let pointer_type = isa.pointer_type();
84 let mut sig = ir::Signature::new(call_conv);
85 sig.params.push(ir::AbiParam::special(
87 pointer_type,
88 ir::ArgumentPurpose::VMContext,
89 ));
90 sig.params.push(ir::AbiParam::new(pointer_type));
91 return sig;
92}
93
94fn unbarriered_store_type_at_offset(
103 pos: &mut FuncCursor,
104 flags: ir::MemFlags,
105 base: ir::Value,
106 offset: i32,
107 value: ir::Value,
108) {
109 pos.ins().store(flags, value, base, offset);
110}
111
112fn unbarriered_load_type_at_offset(
122 isa: &dyn TargetIsa,
123 pos: &mut FuncCursor,
124 ty: WasmValType,
125 flags: ir::MemFlags,
126 base: ir::Value,
127 offset: i32,
128) -> ir::Value {
129 let ir_ty = value_type(isa, ty);
130 pos.ins().load(ir_ty, flags, base, offset)
131}
132
133fn value_type(isa: &dyn TargetIsa, ty: WasmValType) -> ir::types::Type {
135 match ty {
136 WasmValType::I32 => ir::types::I32,
137 WasmValType::I64 => ir::types::I64,
138 WasmValType::F32 => ir::types::F32,
139 WasmValType::F64 => ir::types::F64,
140 WasmValType::V128 => ir::types::I8X16,
141 WasmValType::Ref(rt) => reference_type(rt.heap_type, isa.pointer_type()),
142 }
143}
144
145fn array_call_signature(isa: &dyn TargetIsa) -> ir::Signature {
161 let mut sig = blank_sig(isa, CallConv::triple_default(isa.triple()));
162 sig.params.push(ir::AbiParam::new(isa.pointer_type()));
166 sig.params.push(ir::AbiParam::new(isa.pointer_type()));
167 sig.returns.push(ir::AbiParam::new(ir::types::I8));
169 sig
170}
171
172fn wasm_call_conv(isa: &dyn TargetIsa, tunables: &Tunables) -> CallConv {
174 if tunables.winch_callable {
184 assert!(
185 matches!(
186 isa.triple().architecture,
187 Architecture::X86_64 | Architecture::Aarch64(_)
188 ),
189 "The Winch calling convention is only implemented for x86_64 and aarch64"
190 );
191 CallConv::Winch
192 } else {
193 CallConv::Tail
194 }
195}
196
197fn wasm_call_signature(
199 isa: &dyn TargetIsa,
200 wasm_func_ty: &WasmFuncType,
201 tunables: &Tunables,
202) -> ir::Signature {
203 let call_conv = wasm_call_conv(isa, tunables);
204 let mut sig = blank_sig(isa, call_conv);
205 let cvt = |ty: &WasmValType| ir::AbiParam::new(value_type(isa, *ty));
206 sig.params.extend(wasm_func_ty.params().iter().map(&cvt));
207 sig.returns.extend(wasm_func_ty.returns().iter().map(&cvt));
208 sig
209}
210
211fn reference_type(wasm_ht: WasmHeapType, pointer_type: ir::Type) -> ir::Type {
213 match wasm_ht.top() {
214 WasmHeapTopType::Func => pointer_type,
215 WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => ir::types::I32,
216 WasmHeapTopType::Cont =>
217 {
219 unimplemented!("codegen for stack switching types not implemented, yet")
220 }
221 }
222}
223
224pub const NS_WASM_FUNC: u32 = 0;
229
230pub const NS_WASMTIME_BUILTIN: u32 = 1;
234
235pub const NS_PULLEY_HOSTCALL: u32 = 2;
241
242#[derive(Debug, Clone, PartialEq, Eq)]
244pub struct Relocation {
245 pub reloc: binemit::Reloc,
247 pub reloc_target: RelocationTarget,
249 pub offset: binemit::CodeOffset,
251 pub addend: binemit::Addend,
253}
254
255pub fn clif_flags_to_wasmtime(
257 flags: impl IntoIterator<Item = settings::Value>,
258) -> Vec<(&'static str, FlagValue<'static>)> {
259 flags
260 .into_iter()
261 .map(|val| (val.name, to_flag_value(&val)))
262 .collect()
263}
264
265fn to_flag_value(v: &settings::Value) -> FlagValue<'static> {
266 match v.kind() {
267 settings::SettingKind::Enum => FlagValue::Enum(v.as_enum().unwrap()),
268 settings::SettingKind::Num => FlagValue::Num(v.as_num().unwrap()),
269 settings::SettingKind::Bool => FlagValue::Bool(v.as_bool().unwrap()),
270 settings::SettingKind::Preset => unreachable!(),
271 }
272}
273
274pub fn mach_trap_to_trap(trap: &MachTrap) -> Option<TrapInformation> {
276 let &MachTrap { offset, code } = trap;
277 Some(TrapInformation {
278 code_offset: offset,
279 trap_code: clif_trap_to_env_trap(code)?,
280 })
281}
282
283fn clif_trap_to_env_trap(trap: ir::TrapCode) -> Option<Trap> {
284 Some(match trap {
285 ir::TrapCode::STACK_OVERFLOW => Trap::StackOverflow,
286 ir::TrapCode::HEAP_OUT_OF_BOUNDS => Trap::MemoryOutOfBounds,
287 ir::TrapCode::INTEGER_OVERFLOW => Trap::IntegerOverflow,
288 ir::TrapCode::INTEGER_DIVISION_BY_ZERO => Trap::IntegerDivisionByZero,
289 ir::TrapCode::BAD_CONVERSION_TO_INTEGER => Trap::BadConversionToInteger,
290
291 TRAP_INTERNAL_ASSERT => return None,
295
296 other => Trap::from_u8(other.as_raw().get() - TRAP_OFFSET).unwrap(),
297 })
298}
299
300fn mach_reloc_to_reloc(
303 reloc: &FinalizedMachReloc,
304 name_map: &PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
305) -> Relocation {
306 let &FinalizedMachReloc {
307 offset,
308 kind,
309 ref target,
310 addend,
311 } = reloc;
312 let reloc_target = match *target {
313 FinalizedRelocTarget::ExternalName(ExternalName::User(user_func_ref)) => {
314 let name = &name_map[user_func_ref];
315 match name.namespace {
316 NS_WASM_FUNC => RelocationTarget::Wasm(FuncIndex::from_u32(name.index)),
317 NS_WASMTIME_BUILTIN => {
318 RelocationTarget::Builtin(BuiltinFunctionIndex::from_u32(name.index))
319 }
320 NS_PULLEY_HOSTCALL => RelocationTarget::PulleyHostcall(name.index),
321 _ => panic!("unknown namespace {}", name.namespace),
322 }
323 }
324 FinalizedRelocTarget::ExternalName(ExternalName::LibCall(libcall)) => {
325 panic!("unexpected libcall {libcall:?}");
328 }
329 _ => panic!("unrecognized external name"),
330 };
331 Relocation {
332 reloc: kind,
333 reloc_target,
334 offset,
335 addend,
336 }
337}
338
339struct BuiltinFunctionSignatures {
341 pointer_type: ir::Type,
342
343 host_call_conv: CallConv,
344 wasm_call_conv: CallConv,
345 argument_extension: ir::ArgumentExtension,
346}
347
348impl BuiltinFunctionSignatures {
349 fn new(compiler: &Compiler) -> Self {
350 Self {
351 pointer_type: compiler.isa().pointer_type(),
352 host_call_conv: CallConv::triple_default(compiler.isa().triple()),
353 wasm_call_conv: wasm_call_conv(compiler.isa(), compiler.tunables()),
354 argument_extension: compiler.isa().default_argument_extension(),
355 }
356 }
357
358 fn vmctx(&self) -> AbiParam {
359 AbiParam::special(self.pointer_type, ArgumentPurpose::VMContext)
360 }
361
362 fn pointer(&self) -> AbiParam {
363 AbiParam::new(self.pointer_type)
364 }
365
366 fn u32(&self) -> AbiParam {
367 AbiParam::new(ir::types::I32)
368 }
369
370 fn u64(&self) -> AbiParam {
371 AbiParam::new(ir::types::I64)
372 }
373
374 fn f32(&self) -> AbiParam {
375 AbiParam::new(ir::types::F32)
376 }
377
378 fn f64(&self) -> AbiParam {
379 AbiParam::new(ir::types::F64)
380 }
381
382 fn u8(&self) -> AbiParam {
383 AbiParam::new(ir::types::I8)
384 }
385
386 fn i8x16(&self) -> AbiParam {
387 AbiParam::new(ir::types::I8X16)
388 }
389
390 fn f32x4(&self) -> AbiParam {
391 AbiParam::new(ir::types::F32X4)
392 }
393
394 fn f64x2(&self) -> AbiParam {
395 AbiParam::new(ir::types::F64X2)
396 }
397
398 fn bool(&self) -> AbiParam {
399 AbiParam::new(ir::types::I8)
400 }
401
402 fn wasm_signature(&self, builtin: BuiltinFunctionIndex) -> Signature {
403 let mut _cur = 0;
404 macro_rules! iter {
405 (
406 $(
407 $( #[$attr:meta] )*
408 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
409 )*
410 ) => {
411 $(
412 $( #[$attr] )*
413 if _cur == builtin.index() {
414 return Signature {
415 params: vec![ $( self.$param() ),* ],
416 returns: vec![ $( self.$result() )? ],
417 call_conv: self.wasm_call_conv,
418 };
419 }
420 _cur += 1;
421 )*
422 };
423 }
424
425 wasmtime_environ::foreach_builtin_function!(iter);
426
427 unreachable!();
428 }
429
430 fn host_signature(&self, builtin: BuiltinFunctionIndex) -> Signature {
431 let mut sig = self.wasm_signature(builtin);
432 sig.call_conv = self.host_call_conv;
433
434 for arg in sig.params.iter_mut().chain(sig.returns.iter_mut()) {
438 if arg.value_type.is_int() {
439 arg.extension = self.argument_extension;
440 }
441 }
442
443 sig
444 }
445}
446
447const I31_REF_DISCRIMINANT: u32 = 1;
453
454#[derive(PartialEq, Eq)]
460#[must_use]
461enum Reachability<T> {
462 Reachable(T),
464 Unreachable,
468}