1#![deny(missing_docs)]
5
6use crate::error::{err, err_nt};
7use crate::translator::intrinsics::{Intrinsics, type_to_llvm};
8use inkwell::{
9 AddressSpace,
10 attributes::{Attribute, AttributeLoc},
11 builder::Builder,
12 context::Context,
13 targets::TargetMachine,
14 types::{AnyType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, StructType},
15 values::{
16 BasicValue, BasicValueEnum, CallSiteValue, FloatValue, FunctionValue, IntValue,
17 PointerValue, VectorValue,
18 },
19};
20use itertools::Itertools;
21use wasmer_compiler::abi::{
22 PairSlot, ReturnAbi, ReturnSlot, classify_return_type_aarch64,
23 classify_return_type_loongarch64, classify_return_type_riscv64, classify_return_type_x86_64,
24};
25use wasmer_types::{CompileError, FunctionType as FuncSig, Type};
26use wasmer_vm::VMOffsets;
27
28pub(crate) trait Architecture {
30 fn classify_return_type(&self, types: &[Type]) -> ReturnAbi;
32
33 fn sign_extend_i32_params(&self) -> bool {
35 false
36 }
37}
38
39pub(crate) enum TargetArchitecture {
41 X86_64,
42 Aarch64,
43 LoongArch64,
44 Riscv64,
45}
46
47impl Architecture for TargetArchitecture {
48 fn classify_return_type(&self, types: &[Type]) -> ReturnAbi {
49 match self {
50 Self::X86_64 => classify_return_type_x86_64(types),
51 Self::Aarch64 => classify_return_type_aarch64(types),
52 Self::LoongArch64 => classify_return_type_loongarch64(types),
53 Self::Riscv64 => classify_return_type_riscv64(types),
54 }
55 }
56
57 fn sign_extend_i32_params(&self) -> bool {
58 matches!(self, Self::Riscv64)
59 }
60}
61
62pub(crate) struct LLVMAbi<A: Architecture = TargetArchitecture> {
64 pub(crate) architecture: A,
65}
66
67pub(crate) fn get_abi(
69 target_machine: &TargetMachine,
70) -> Result<LLVMAbi<TargetArchitecture>, CompileError> {
71 let target_name = target_machine.get_triple();
72 let target_name = target_name.as_str().to_string_lossy();
73 let architecture = if target_name.starts_with("aarch64") {
74 TargetArchitecture::Aarch64
75 } else if target_name.starts_with("loongarch64") {
76 TargetArchitecture::LoongArch64
77 } else if target_name.starts_with("riscv64") {
78 TargetArchitecture::Riscv64
79 } else if target_name.starts_with("x86_64") {
80 TargetArchitecture::X86_64
81 } else {
82 return Err(CompileError::UnsupportedTarget(target_name.to_string()));
83 };
84 Ok(LLVMAbi { architecture })
85}
86
87impl<A: Architecture> LLVMAbi<A> {
92 pub(crate) fn get_vmctx_ptr_param<'ctx>(
94 &self,
95 func_value: &FunctionValue<'ctx>,
96 ) -> PointerValue<'ctx> {
97 let param = func_value
98 .get_nth_param(u32::from(
99 func_value
100 .get_enum_attribute(
101 AttributeLoc::Param(0),
102 Attribute::get_named_enum_kind_id("sret"),
103 )
104 .is_some(),
105 ))
106 .unwrap();
107 param.set_name("vmctx");
108
109 param.into_pointer_value()
110 }
111
112 pub(crate) fn get_m0_ptr_param<'ctx>(
115 &self,
116 func_value: &FunctionValue<'ctx>,
117 ) -> PointerValue<'ctx> {
118 let vmctx_idx = u32::from(
119 func_value
120 .get_enum_attribute(
121 AttributeLoc::Param(0),
122 Attribute::get_named_enum_kind_id("sret"),
123 )
124 .is_some(),
125 );
126
127 let param = func_value.get_nth_param(vmctx_idx + 1).unwrap();
128 param.set_name("m0_base_ptr");
129
130 param.into_pointer_value()
131 }
132
133 #[allow(clippy::too_many_arguments)]
135 pub(crate) fn args_to_call<'ctx>(
136 &self,
137 alloca_builder: &Builder<'ctx>,
138 func_sig: &FuncSig,
139 llvm_fn_ty: &FunctionType<'ctx>,
140 ctx_ptr: PointerValue<'ctx>,
141 values: &[BasicValueEnum<'ctx>],
142 intrinsics: &Intrinsics<'ctx>,
143 m0: Option<PointerValue<'ctx>>,
144 sret_ptr: Option<PointerValue<'ctx>>,
145 ) -> Result<Vec<BasicValueEnum<'ctx>>, CompileError> {
146 let sret = if self.llvm_fn_uses_sret(llvm_fn_ty, func_sig) {
148 let llvm_params: Vec<_> = func_sig
149 .results()
150 .iter()
151 .map(|x| type_to_llvm(intrinsics, *x).unwrap())
152 .collect();
153 let llvm_params = llvm_fn_ty
154 .get_context()
155 .struct_type(llvm_params.as_slice(), false);
156 Some(match sret_ptr {
158 Some(sret_ptr) => sret_ptr,
159 None => err!(alloca_builder.build_alloca(llvm_params, "sret")),
160 })
161 } else {
162 None
163 };
164
165 let mut args = vec![ctx_ptr.as_basic_value_enum()];
166
167 if let Some(m0) = m0 {
168 args.push(m0.into());
169 }
170
171 let args = args.into_iter().chain(values.iter().copied());
172
173 let ret = if let Some(sret) = sret {
174 std::iter::once(sret.as_basic_value_enum())
175 .chain(args)
176 .collect()
177 } else {
178 args.collect()
179 };
180
181 Ok(ret)
182 }
183
184 pub(crate) fn llvm_fn_uses_sret<'ctx>(
186 &self,
187 llvm_fn_ty: &FunctionType<'ctx>,
188 func_sig: &FuncSig,
189 ) -> bool {
190 llvm_fn_ty.get_return_type().is_none() && func_sig.results().len() > 1
191 }
192
193 pub(crate) fn is_sret(&self, func_sig: &FuncSig) -> Result<bool, CompileError> {
195 Ok(matches!(
196 self.architecture.classify_return_type(func_sig.results()),
197 ReturnAbi::Sret(_)
198 ))
199 }
200}
201
202impl<A: Architecture> LLVMAbi<A> {
203 pub(crate) fn func_type_to_llvm<'ctx>(
205 &self,
206 context: &'ctx Context,
207 intrinsics: &Intrinsics<'ctx>,
208 offsets: Option<&VMOffsets>,
209 sig: &FuncSig,
210 include_m0_param: bool,
211 ) -> Result<(FunctionType<'ctx>, Vec<(Attribute, AttributeLoc)>), CompileError> {
212 let slot_llvm_type = |slot| match slot {
214 ReturnSlot::Natural(t) => type_to_llvm(intrinsics, t),
215 ReturnSlot::Raw(Type::F32) => Ok(intrinsics.i32_ty.as_basic_type_enum()),
216 ReturnSlot::Raw(Type::F64) => Ok(intrinsics.i64_ty.as_basic_type_enum()),
217 ReturnSlot::Raw(t) => type_to_llvm(intrinsics, t),
218 };
219
220 let pair_llvm_type = |pair| match pair {
222 PairSlot::F32Vector(_, _) => intrinsics.f32_ty.vec_type(2).as_basic_type_enum(),
223 PairSlot::Raw(_, _) => intrinsics.i64_ty.as_basic_type_enum(),
224 };
225
226 let return_abi = self.architecture.classify_return_type(sig.results());
227 let return_llvm_type: Option<BasicTypeEnum<'ctx>> = match &return_abi {
228 ReturnAbi::Void | ReturnAbi::Sret(_) => None,
229 ReturnAbi::Single(single_value) => Some(type_to_llvm(intrinsics, *single_value)?),
230 ReturnAbi::Pair(s0, s1) => Some(
231 context
232 .struct_type(&[slot_llvm_type(*s0)?, slot_llvm_type(*s1)?], false)
233 .as_basic_type_enum(),
234 ),
235 ReturnAbi::Unpacked(types) => Some(
236 context
237 .struct_type(
238 &types
239 .iter()
240 .map(|ty| type_to_llvm(intrinsics, *ty))
241 .collect::<Result<Vec<_>, _>>()?,
242 false,
243 )
244 .as_basic_type_enum(),
245 ),
246 ReturnAbi::PackedPair(pair) => Some(pair_llvm_type(*pair)),
247 ReturnAbi::PackedFirst(pair, slot) => Some(
248 context
249 .struct_type(&[pair_llvm_type(*pair), slot_llvm_type(*slot)?], false)
250 .as_basic_type_enum(),
251 ),
252 ReturnAbi::PackedLast(slot, pair) => Some(
253 context
254 .struct_type(&[slot_llvm_type(*slot)?, pair_llvm_type(*pair)], false)
255 .as_basic_type_enum(),
256 ),
257 ReturnAbi::PackedQuads(p0, p1) => Some(
258 context
259 .struct_type(&[pair_llvm_type(*p0), pair_llvm_type(*p1)], false)
260 .as_basic_type_enum(),
261 ),
262 };
263
264 let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty));
265 let mut param_types = vec![Ok(intrinsics.ptr_ty.as_basic_type_enum())];
266 if include_m0_param {
267 param_types.push(Ok(intrinsics.ptr_ty.as_basic_type_enum()));
268 }
269 let param_llvm_types = param_types
270 .into_iter()
271 .chain(user_param_types)
272 .map(|v| v.map(Into::into))
273 .collect::<Result<Vec<BasicMetadataTypeEnum>, _>>()?;
274
275 let vmctx_attributes = |i: u32| {
277 vec![
278 (
279 context.create_enum_attribute(Attribute::get_named_enum_kind_id("nofree"), 0),
280 AttributeLoc::Param(i),
281 ),
282 (
283 if let Some(offsets) = offsets {
284 context.create_enum_attribute(
285 Attribute::get_named_enum_kind_id("dereferenceable"),
286 offsets.size_of_vmctx().into(),
287 )
288 } else {
289 context
290 .create_enum_attribute(Attribute::get_named_enum_kind_id("nonnull"), 0)
291 },
292 AttributeLoc::Param(i),
293 ),
294 (
295 context.create_enum_attribute(
296 Attribute::get_named_enum_kind_id("align"),
297 std::mem::align_of::<wasmer_vm::VMContext>()
298 .try_into()
299 .unwrap(),
300 ),
301 AttributeLoc::Param(i),
302 ),
303 ]
304 };
305
306 let (function_type, mut attributes, sret_param) =
307 if let ReturnAbi::Sret(types) = &return_abi {
308 let basic_types = types
309 .iter()
310 .map(|&ty| type_to_llvm(intrinsics, ty))
311 .collect::<Result<Vec<_>, _>>()?;
312 let sret = context.struct_type(&basic_types, false);
313 let sret_ptr = context.ptr_type(AddressSpace::default());
314 let sret_param_llvm_types =
315 std::iter::once(BasicMetadataTypeEnum::from(sret_ptr.as_basic_type_enum()))
316 .chain(param_llvm_types.iter().copied())
317 .collect_vec();
318
319 let mut attributes = vec![(
320 context.create_type_attribute(
321 Attribute::get_named_enum_kind_id("sret"),
322 sret.as_any_type_enum(),
323 ),
324 AttributeLoc::Param(0),
325 )];
326 attributes.append(&mut vmctx_attributes(1));
327 (
328 intrinsics
329 .void_ty
330 .fn_type(sret_param_llvm_types.as_slice(), false),
331 attributes,
332 1,
333 )
334 } else {
335 let function_type = match return_llvm_type {
336 Some(return_type) => return_type.fn_type(param_llvm_types.as_slice(), false),
337 None => intrinsics
338 .void_ty
339 .fn_type(param_llvm_types.as_slice(), false),
340 };
341 (function_type, vmctx_attributes(0), 0)
342 };
343
344 if self.architecture.sign_extend_i32_params() {
345 let extra_params = 1 + usize::from(include_m0_param) + sret_param;
346 for (index, ty) in sig.params().iter().enumerate() {
347 if *ty == Type::I32 {
348 for name in ["signext", "noundef"] {
349 attributes.push((
350 context
351 .create_enum_attribute(Attribute::get_named_enum_kind_id(name), 0),
352 AttributeLoc::Param((index + extra_params) as u32),
353 ));
354 }
355 }
356 }
357 }
358
359 Ok((function_type, attributes))
360 }
361
362 pub(crate) fn rets_from_call<'ctx>(
364 &self,
365 builder: &Builder<'ctx>,
366 intrinsics: &Intrinsics<'ctx>,
367 call_site: CallSiteValue<'ctx>,
368 func_sig: &FuncSig,
369 ) -> Result<Vec<BasicValueEnum<'ctx>>, CompileError> {
370 let split_i64 =
371 |value: IntValue<'ctx>| -> Result<(IntValue<'ctx>, IntValue<'ctx>), CompileError> {
372 assert!(value.get_type() == intrinsics.i64_ty);
373 let low = err!(builder.build_int_truncate(value, intrinsics.i32_ty, ""));
374 let lshr = err!(builder.build_right_shift(
375 value,
376 intrinsics.i64_ty.const_int(32, false),
377 false,
378 ""
379 ));
380 let high = err!(builder.build_int_truncate(lshr, intrinsics.i32_ty, ""));
381 Ok((low, high))
382 };
383
384 let f32x2_ty = intrinsics.f32_ty.vec_type(2).as_basic_type_enum();
385 let extract_f32x2 = |value: VectorValue<'ctx>| -> Result<(FloatValue<'ctx>, FloatValue<'ctx>), CompileError> {
386 assert!(value.get_type() == f32x2_ty.into_vector_type());
387 let ret0 = err!(builder
388 .build_extract_element(value, intrinsics.i32_ty.const_int(0, false), ""))
389 .into_float_value();
390 let ret1 = err!(builder
391 .build_extract_element(value, intrinsics.i32_ty.const_int(1, false), ""))
392 .into_float_value();
393 Ok((ret0, ret1))
394 };
395
396 let casted =
397 |value: BasicValueEnum<'ctx>, ty: Type| -> Result<BasicValueEnum<'ctx>, CompileError> {
398 match ty {
399 Type::I32 | Type::ExceptionRef => {
400 assert!(
401 value.get_type() == intrinsics.i32_ty.as_basic_type_enum()
402 || value.get_type() == intrinsics.f32_ty.as_basic_type_enum()
403 );
404 err_nt!(builder.build_bit_cast(value, intrinsics.i32_ty, ""))
405 }
406 Type::F32 => {
407 assert!(
408 value.get_type() == intrinsics.i32_ty.as_basic_type_enum()
409 || value.get_type() == intrinsics.f32_ty.as_basic_type_enum()
410 );
411 err_nt!(builder.build_bit_cast(value, intrinsics.f32_ty, ""))
412 }
413 _ => panic!("should only be called to repack 32-bit values"),
414 }
415 };
416
417 let restore_slot = |value, slot| match slot {
419 ReturnSlot::Natural(_) => Ok(value),
420 ReturnSlot::Raw(Type::F32) => {
421 err_nt!(builder.build_bit_cast(value, intrinsics.f32_ty, ""))
422 }
423 ReturnSlot::Raw(Type::F64) => {
424 err_nt!(builder.build_bit_cast(value, intrinsics.f64_ty, ""))
425 }
426 ReturnSlot::Raw(_) => Ok(value),
427 };
428
429 let unpack_pair =
431 |value: BasicValueEnum<'ctx>,
432 pair: PairSlot|
433 -> Result<(BasicValueEnum<'ctx>, BasicValueEnum<'ctx>), CompileError> {
434 match pair {
435 PairSlot::F32Vector(_, _) => {
436 let (v0, v1) = extract_f32x2(value.into_vector_value())?;
437 Ok((v0.into(), v1.into()))
438 }
439 PairSlot::Raw(t0, t1) => {
440 let (low, high) = split_i64(value.into_int_value())?;
441 Ok((casted(low.into(), t0)?, casted(high.into(), t1)?))
442 }
443 }
444 };
445
446 if let Some(basic_value) = call_site.try_as_basic_value().basic() {
447 if func_sig.results().len() > 1 {
448 if basic_value.get_type() == intrinsics.i64_ty.as_basic_type_enum() {
449 assert!(func_sig.results().len() == 2);
450 let value = basic_value.into_int_value();
451 let (low, high) = split_i64(value)?;
452 let low = casted(low.into(), func_sig.results()[0])?;
453 let high = casted(high.into(), func_sig.results()[1])?;
454 return Ok(vec![low, high]);
455 }
456 if basic_value.get_type() == f32x2_ty {
457 assert!(func_sig.results().len() == 2);
458 let (ret0, ret1) = extract_f32x2(basic_value.into_vector_value())?;
459 return Ok(vec![ret0.into(), ret1.into()]);
460 }
461 let struct_value = basic_value.into_struct_value();
462 let rets = (0..struct_value.get_type().count_fields())
463 .map(|i| builder.build_extract_value(struct_value, i, "").unwrap())
464 .collect_vec();
465 let ret = match self.architecture.classify_return_type(func_sig.results()) {
466 ReturnAbi::Unpacked(_) => rets,
467 ReturnAbi::Pair(s0, s1) => {
468 vec![restore_slot(rets[0], s0)?, restore_slot(rets[1], s1)?]
469 }
470 ReturnAbi::PackedFirst(pair, slot) => {
471 assert!(func_sig.results().len() == 3);
472 let (low, high) = unpack_pair(rets[0], pair)?;
473 vec![low, high, restore_slot(rets[1], slot)?]
474 }
475 ReturnAbi::PackedLast(slot, pair) => {
476 assert!(func_sig.results().len() == 3);
477 let (low, high) = unpack_pair(rets[1], pair)?;
478 vec![restore_slot(rets[0], slot)?, low, high]
479 }
480 ReturnAbi::PackedQuads(p0, p1) => {
481 assert!(func_sig.results().len() == 4);
482 let (low0, high0) = unpack_pair(rets[0], p0)?;
483 let (low1, high1) = unpack_pair(rets[1], p1)?;
484 vec![low0, high0, low1, high1]
485 }
486 ReturnAbi::Void
487 | ReturnAbi::Single(_)
488 | ReturnAbi::PackedPair(_)
489 | ReturnAbi::Sret(_) => {
490 unreachable!("expected an sret for this type")
491 }
492 };
493
494 Ok(ret)
495 } else {
496 assert!(func_sig.results().len() == 1);
497 Ok(vec![basic_value])
498 }
499 } else {
500 assert!(call_site.count_arguments() > 0); if call_site
502 .get_enum_attribute(
503 AttributeLoc::Param(0),
504 Attribute::get_named_enum_kind_id("sret"),
505 )
506 .is_some()
507 {
508 let sret_ty = call_site
509 .try_as_basic_value()
510 .unwrap_instruction()
511 .get_operand(0)
512 .unwrap()
513 .unwrap_value();
514 let sret = sret_ty.into_pointer_value();
515 let llvm_results: Vec<_> = func_sig
517 .results()
518 .iter()
519 .map(|x| type_to_llvm(intrinsics, *x).unwrap())
520 .collect();
521 let struct_type = intrinsics
522 .i32_ty
523 .get_context()
524 .struct_type(llvm_results.as_slice(), false);
525
526 let struct_value =
527 err!(builder.build_load(struct_type, sret, "")).into_struct_value();
528 let mut rets: Vec<_> = Vec::new();
529 for i in 0..struct_value.get_type().count_fields() {
530 let value = builder.build_extract_value(struct_value, i, "").unwrap();
531 rets.push(value);
532 }
533 assert!(func_sig.results().len() == rets.len());
534 Ok(rets)
535 } else {
536 assert!(func_sig.results().is_empty());
537 Ok(vec![])
538 }
539 }
540 }
541
542 pub(crate) fn pack_values_for_register_return<'ctx>(
543 &self,
544 intrinsics: &Intrinsics<'ctx>,
545 builder: &Builder<'ctx>,
546 values: &[BasicValueEnum<'ctx>],
547 func_sig: &FuncSig,
548 func_type: &FunctionType<'ctx>,
549 ) -> Result<BasicValueEnum<'ctx>, CompileError> {
550 let pack_i32s = |low: BasicValueEnum<'ctx>, high: BasicValueEnum<'ctx>| {
551 assert!(low.get_type() == intrinsics.i32_ty.as_basic_type_enum());
552 assert!(high.get_type() == intrinsics.i32_ty.as_basic_type_enum());
553 let (low, high) = (low.into_int_value(), high.into_int_value());
554 let low = err!(builder.build_int_z_extend(low, intrinsics.i64_ty, ""));
555 let high = err!(builder.build_int_z_extend(high, intrinsics.i64_ty, ""));
556 let high =
557 err!(builder.build_left_shift(high, intrinsics.i64_ty.const_int(32, false), ""));
558 err_nt!(
559 builder
560 .build_or(low, high, "")
561 .map(|v| v.as_basic_value_enum())
562 )
563 };
564
565 let pack_f32s = |first: BasicValueEnum<'ctx>,
566 second: BasicValueEnum<'ctx>|
567 -> Result<BasicValueEnum<'ctx>, CompileError> {
568 assert!(first.get_type() == intrinsics.f32_ty.as_basic_type_enum());
569 assert!(second.get_type() == intrinsics.f32_ty.as_basic_type_enum());
570 let (first, second) = (first.into_float_value(), second.into_float_value());
571 let vec_ty = intrinsics.f32_ty.vec_type(2);
572 let vec = err!(builder.build_insert_element(
573 vec_ty.get_undef(),
574 first,
575 intrinsics.i32_zero,
576 ""
577 ));
578 err_nt!(
579 builder
580 .build_insert_element(vec, second, intrinsics.i32_ty.const_int(1, false), "")
581 .map(|v| v.as_basic_value_enum())
582 )
583 };
584
585 let build_struct = |ty: StructType<'ctx>, values: &[BasicValueEnum<'ctx>]| {
586 let mut struct_value = ty.get_undef();
587 for (i, v) in values.iter().enumerate() {
588 struct_value = builder
589 .build_insert_value(struct_value, *v, i as u32, "")
590 .unwrap()
591 .into_struct_value();
592 }
593 struct_value.as_basic_value_enum()
594 };
595
596 let pack_slot = |value, slot| match slot {
597 ReturnSlot::Natural(_) => Ok(value),
598 ReturnSlot::Raw(Type::F32) => {
599 err_nt!(builder.build_bit_cast(value, intrinsics.i32_ty, ""))
600 }
601 ReturnSlot::Raw(Type::F64) => {
602 err_nt!(builder.build_bit_cast(value, intrinsics.i64_ty, ""))
603 }
604 ReturnSlot::Raw(_) => Ok(value),
605 };
606
607 let pack_pair = |first, second, pair| match pair {
609 PairSlot::F32Vector(_, _) => pack_f32s(first, second),
610 PairSlot::Raw(_, _) => {
611 let v1 = err!(builder.build_bit_cast(first, intrinsics.i32_ty, ""));
612 let v2 = err!(builder.build_bit_cast(second, intrinsics.i32_ty, ""));
613 pack_i32s(v1, v2)
614 }
615 };
616
617 let return_abi = self.architecture.classify_return_type(func_sig.results());
618 let struct_ty = || func_type.get_return_type().unwrap().into_struct_type();
619
620 Ok(match return_abi {
621 ReturnAbi::Single(_) => values[0],
622 ReturnAbi::PackedPair(pair) => pack_pair(values[0], values[1], pair)?,
623 ReturnAbi::Unpacked(_) => build_struct(struct_ty(), values),
624 ReturnAbi::Pair(s0, s1) => build_struct(
625 struct_ty(),
626 &[pack_slot(values[0], s0)?, pack_slot(values[1], s1)?],
627 ),
628 ReturnAbi::PackedFirst(pair, slot) => build_struct(
629 struct_ty(),
630 &[
631 pack_pair(values[0], values[1], pair)?,
632 pack_slot(values[2], slot)?,
633 ],
634 ),
635 ReturnAbi::PackedLast(slot, pair) => build_struct(
636 struct_ty(),
637 &[
638 pack_slot(values[0], slot)?,
639 pack_pair(values[1], values[2], pair)?,
640 ],
641 ),
642 ReturnAbi::PackedQuads(p0, p1) => build_struct(
643 struct_ty(),
644 &[
645 pack_pair(values[0], values[1], p0)?,
646 pack_pair(values[2], values[3], p1)?,
647 ],
648 ),
649 ReturnAbi::Void | ReturnAbi::Sret(_) => {
650 unreachable!("called to perform register return on struct return or void function")
651 }
652 })
653 }
654}