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, FuncKey, Trap, TrapInformation, Tunables, WasmFuncType,
29 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;
46mod trap;
47
48use self::compiler::Compiler;
49
50const TRAP_INTERNAL_ASSERT: TrapCode = TrapCode::unwrap_user(1);
51const TRAP_OFFSET: u8 = 2;
52pub const TRAP_CANNOT_LEAVE_COMPONENT: TrapCode =
53 TrapCode::unwrap_user(Trap::CannotLeaveComponent as u8 + TRAP_OFFSET);
54pub const TRAP_INDIRECT_CALL_TO_NULL: TrapCode =
55 TrapCode::unwrap_user(Trap::IndirectCallToNull as u8 + TRAP_OFFSET);
56pub const TRAP_BAD_SIGNATURE: TrapCode =
57 TrapCode::unwrap_user(Trap::BadSignature as u8 + TRAP_OFFSET);
58pub const TRAP_NULL_REFERENCE: TrapCode =
59 TrapCode::unwrap_user(Trap::NullReference as u8 + TRAP_OFFSET);
60pub const TRAP_ALLOCATION_TOO_LARGE: TrapCode =
61 TrapCode::unwrap_user(Trap::AllocationTooLarge as u8 + TRAP_OFFSET);
62pub const TRAP_ARRAY_OUT_OF_BOUNDS: TrapCode =
63 TrapCode::unwrap_user(Trap::ArrayOutOfBounds as u8 + TRAP_OFFSET);
64pub const TRAP_UNREACHABLE: TrapCode =
65 TrapCode::unwrap_user(Trap::UnreachableCodeReached as u8 + TRAP_OFFSET);
66pub const TRAP_HEAP_MISALIGNED: TrapCode =
67 TrapCode::unwrap_user(Trap::HeapMisaligned as u8 + TRAP_OFFSET);
68pub const TRAP_TABLE_OUT_OF_BOUNDS: TrapCode =
69 TrapCode::unwrap_user(Trap::TableOutOfBounds as u8 + TRAP_OFFSET);
70pub const TRAP_UNHANDLED_TAG: TrapCode =
71 TrapCode::unwrap_user(Trap::UnhandledTag as u8 + TRAP_OFFSET);
72pub const TRAP_CONTINUATION_ALREADY_CONSUMED: TrapCode =
73 TrapCode::unwrap_user(Trap::ContinuationAlreadyConsumed as u8 + TRAP_OFFSET);
74pub const TRAP_CAST_FAILURE: TrapCode =
75 TrapCode::unwrap_user(Trap::CastFailure as u8 + TRAP_OFFSET);
76
77fn blank_sig(isa: &dyn TargetIsa, call_conv: CallConv) -> ir::Signature {
82 let pointer_type = isa.pointer_type();
83 let mut sig = ir::Signature::new(call_conv);
84 sig.params.push(ir::AbiParam::special(
86 pointer_type,
87 ir::ArgumentPurpose::VMContext,
88 ));
89 sig.params.push(ir::AbiParam::new(pointer_type));
90 return sig;
91}
92
93fn unbarriered_store_type_at_offset(
102 pos: &mut FuncCursor,
103 flags: ir::MemFlags,
104 base: ir::Value,
105 offset: i32,
106 value: ir::Value,
107) {
108 pos.ins().store(flags, value, base, offset);
109}
110
111fn unbarriered_load_type_at_offset(
121 isa: &dyn TargetIsa,
122 pos: &mut FuncCursor,
123 ty: WasmValType,
124 flags: ir::MemFlags,
125 base: ir::Value,
126 offset: i32,
127) -> ir::Value {
128 let ir_ty = value_type(isa, ty);
129 pos.ins().load(ir_ty, flags, base, offset)
130}
131
132fn value_type(isa: &dyn TargetIsa, ty: WasmValType) -> ir::types::Type {
134 match ty {
135 WasmValType::I32 => ir::types::I32,
136 WasmValType::I64 => ir::types::I64,
137 WasmValType::F32 => ir::types::F32,
138 WasmValType::F64 => ir::types::F64,
139 WasmValType::V128 => ir::types::I8X16,
140 WasmValType::Ref(rt) => reference_type(rt.heap_type, isa.pointer_type()),
141 }
142}
143
144fn array_call_signature(isa: &dyn TargetIsa) -> ir::Signature {
160 let mut sig = blank_sig(isa, CallConv::triple_default(isa.triple()));
161 sig.params.push(ir::AbiParam::new(isa.pointer_type()));
165 sig.params.push(ir::AbiParam::new(isa.pointer_type()));
166 sig.returns.push(ir::AbiParam::new(ir::types::I8));
168 sig
169}
170
171fn wasm_call_conv(isa: &dyn TargetIsa, tunables: &Tunables) -> CallConv {
173 if tunables.winch_callable {
183 assert!(
184 matches!(
185 isa.triple().architecture,
186 Architecture::X86_64 | Architecture::Aarch64(_)
187 ),
188 "The Winch calling convention is only implemented for x86_64 and aarch64"
189 );
190 CallConv::Winch
191 } else {
192 CallConv::Tail
193 }
194}
195
196fn wasm_call_signature(
198 isa: &dyn TargetIsa,
199 wasm_func_ty: &WasmFuncType,
200 tunables: &Tunables,
201) -> ir::Signature {
202 let call_conv = wasm_call_conv(isa, tunables);
203 let mut sig = blank_sig(isa, call_conv);
204 let cvt = |ty: &WasmValType| ir::AbiParam::new(value_type(isa, *ty));
205 sig.params.extend(wasm_func_ty.params().iter().map(&cvt));
206 sig.returns.extend(wasm_func_ty.results().iter().map(&cvt));
207 sig
208}
209
210fn reference_type(wasm_ht: WasmHeapType, pointer_type: ir::Type) -> ir::Type {
212 match wasm_ht.top() {
213 WasmHeapTopType::Func => pointer_type,
214 WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => ir::types::I32,
215 WasmHeapTopType::Cont => {
216 ir::Type::int((2 * pointer_type.bits()).try_into().unwrap()).unwrap()
218 }
219 }
220}
221
222#[derive(Debug, Clone, PartialEq, Eq)]
226pub struct Relocation {
227 pub reloc: binemit::Reloc,
229 pub reloc_target: FuncKey,
231 pub offset: binemit::CodeOffset,
233 pub addend: binemit::Addend,
235}
236
237pub fn clif_flags_to_wasmtime(
239 flags: impl IntoIterator<Item = settings::Value>,
240) -> Vec<(&'static str, FlagValue<'static>)> {
241 flags
242 .into_iter()
243 .map(|val| (val.name, to_flag_value(&val)))
244 .collect()
245}
246
247fn to_flag_value(v: &settings::Value) -> FlagValue<'static> {
248 match v.kind() {
249 settings::SettingKind::Enum => FlagValue::Enum(v.as_enum().unwrap()),
250 settings::SettingKind::Num => FlagValue::Num(v.as_num().unwrap()),
251 settings::SettingKind::Bool => FlagValue::Bool(v.as_bool().unwrap()),
252 settings::SettingKind::Preset => unreachable!(),
253 }
254}
255
256pub fn mach_trap_to_trap(trap: &MachTrap) -> Option<TrapInformation> {
258 let &MachTrap { offset, code } = trap;
259 Some(TrapInformation {
260 code_offset: offset,
261 trap_code: clif_trap_to_env_trap(code)?,
262 })
263}
264
265fn clif_trap_to_env_trap(trap: ir::TrapCode) -> Option<Trap> {
266 Some(match trap {
267 ir::TrapCode::STACK_OVERFLOW => Trap::StackOverflow,
268 ir::TrapCode::HEAP_OUT_OF_BOUNDS => Trap::MemoryOutOfBounds,
269 ir::TrapCode::INTEGER_OVERFLOW => Trap::IntegerOverflow,
270 ir::TrapCode::INTEGER_DIVISION_BY_ZERO => Trap::IntegerDivisionByZero,
271 ir::TrapCode::BAD_CONVERSION_TO_INTEGER => Trap::BadConversionToInteger,
272
273 TRAP_INTERNAL_ASSERT => return None,
277
278 other => Trap::from_u8(other.as_raw().get() - TRAP_OFFSET).unwrap(),
279 })
280}
281
282fn mach_reloc_to_reloc(
285 reloc: &FinalizedMachReloc,
286 name_map: &PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
287) -> Relocation {
288 let &FinalizedMachReloc {
289 offset,
290 kind,
291 ref target,
292 addend,
293 } = reloc;
294 let reloc_target = match *target {
295 FinalizedRelocTarget::ExternalName(ExternalName::User(user_func_ref)) => {
296 let name = &name_map[user_func_ref];
297 FuncKey::from_raw_parts(name.namespace, name.index)
298 }
299 FinalizedRelocTarget::ExternalName(ExternalName::LibCall(libcall)) => {
300 panic!("unexpected libcall {libcall:?}");
303 }
304 _ => panic!("unrecognized external name {target:?}"),
305 };
306 Relocation {
307 reloc: kind,
308 reloc_target,
309 offset,
310 addend,
311 }
312}
313
314struct BuiltinFunctionSignatures {
316 pointer_type: ir::Type,
317
318 host_call_conv: CallConv,
319 wasm_call_conv: CallConv,
320 argument_extension: ir::ArgumentExtension,
321}
322
323impl BuiltinFunctionSignatures {
324 fn new(compiler: &Compiler) -> Self {
325 Self {
326 pointer_type: compiler.isa().pointer_type(),
327 host_call_conv: CallConv::triple_default(compiler.isa().triple()),
328 wasm_call_conv: wasm_call_conv(compiler.isa(), compiler.tunables()),
329 argument_extension: compiler.isa().default_argument_extension(),
330 }
331 }
332
333 fn vmctx(&self) -> AbiParam {
334 AbiParam::special(self.pointer_type, ArgumentPurpose::VMContext)
335 }
336
337 fn pointer(&self) -> AbiParam {
338 AbiParam::new(self.pointer_type)
339 }
340
341 fn u32(&self) -> AbiParam {
342 AbiParam::new(ir::types::I32)
343 }
344
345 fn u64(&self) -> AbiParam {
346 AbiParam::new(ir::types::I64)
347 }
348
349 fn f32(&self) -> AbiParam {
350 AbiParam::new(ir::types::F32)
351 }
352
353 fn f64(&self) -> AbiParam {
354 AbiParam::new(ir::types::F64)
355 }
356
357 fn u8(&self) -> AbiParam {
358 AbiParam::new(ir::types::I8)
359 }
360
361 fn i8x16(&self) -> AbiParam {
362 AbiParam::new(ir::types::I8X16)
363 }
364
365 fn f32x4(&self) -> AbiParam {
366 AbiParam::new(ir::types::F32X4)
367 }
368
369 fn f64x2(&self) -> AbiParam {
370 AbiParam::new(ir::types::F64X2)
371 }
372
373 fn bool(&self) -> AbiParam {
374 AbiParam::new(ir::types::I8)
375 }
376
377 #[cfg(feature = "stack-switching")]
378 fn size(&self) -> AbiParam {
379 AbiParam::new(self.pointer_type)
380 }
381
382 fn wasm_signature(&self, builtin: BuiltinFunctionIndex) -> Signature {
383 let mut _cur = 0;
384 macro_rules! iter {
385 (
386 $(
387 $( #[$attr:meta] )*
388 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
389 )*
390 ) => {
391 $(
392 $( #[$attr] )*
393 if _cur == builtin.index() {
394 return Signature {
395 params: vec![ $( self.$param() ),* ],
396 returns: vec![ $( self.$result() )? ],
397 call_conv: self.wasm_call_conv,
398 };
399 }
400 _cur += 1;
401 )*
402 };
403 }
404
405 wasmtime_environ::foreach_builtin_function!(iter);
406
407 unreachable!();
408 }
409
410 fn host_signature(&self, builtin: BuiltinFunctionIndex) -> Signature {
411 let mut sig = self.wasm_signature(builtin);
412 sig.call_conv = self.host_call_conv;
413
414 for arg in sig.params.iter_mut().chain(sig.returns.iter_mut()) {
418 if arg.value_type.is_int() {
419 arg.extension = self.argument_extension;
420 }
421 }
422
423 sig
424 }
425}
426
427const I31_REF_DISCRIMINANT: u32 = 1;
433
434#[derive(PartialEq, Eq)]
440#[must_use]
441enum Reachability<T> {
442 Reachable(T),
444 Unreachable,
448}