2024-08-11 16:10:36 +00:00
|
|
|
use std::assert_matches::assert_matches;
|
2019-02-17 18:58:58 +00:00
|
|
|
use std::cmp::Ordering;
|
2020-03-31 16:16:47 +00:00
|
|
|
|
2025-02-18 08:12:26 +00:00
|
|
|
use rustc_abi::{Align, BackendRepr, ExternAbi, Float, HasDataLayout, Primitive, Size};
|
2023-05-01 19:24:21 +00:00
|
|
|
use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh};
|
2020-06-16 08:37:34 +00:00
|
|
|
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
|
2022-11-27 01:28:01 +00:00
|
|
|
use rustc_codegen_ssa::errors::{ExpectedPointerMutability, InvalidMonomorphization};
|
2023-10-24 06:01:47 +00:00
|
|
|
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
|
2024-07-26 18:36:21 +00:00
|
|
|
use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue};
|
2020-03-31 16:16:47 +00:00
|
|
|
use rustc_codegen_ssa::traits::*;
|
2020-01-05 01:37:57 +00:00
|
|
|
use rustc_hir as hir;
|
2024-05-22 07:56:57 +00:00
|
|
|
use rustc_middle::mir::BinOp;
|
2024-11-15 12:53:31 +00:00
|
|
|
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf};
|
2023-09-18 14:37:19 +00:00
|
|
|
use rustc_middle::ty::{self, GenericArgsRef, Ty};
|
2020-03-29 14:41:09 +00:00
|
|
|
use rustc_middle::{bug, span_bug};
|
2024-02-25 17:51:22 +00:00
|
|
|
use rustc_span::{Span, Symbol, sym};
|
2025-02-18 08:12:26 +00:00
|
|
|
use rustc_target::callconv::{FnAbi, PassMode};
|
2021-09-27 12:18:22 +00:00
|
|
|
use rustc_target::spec::{HasTargetSpec, PanicStrategy};
|
2024-05-22 04:50:24 +00:00
|
|
|
use tracing::debug;
|
2015-09-18 22:42:57 +00:00
|
|
|
|
2025-02-18 08:12:26 +00:00
|
|
|
use crate::abi::{FnAbiLlvmExt, LlvmType};
|
2015-08-14 22:46:51 +00:00
|
|
|
use crate::builder::Builder;
|
|
|
|
use crate::context::CodegenCx;
|
2024-09-19 01:39:28 +00:00
|
|
|
use crate::llvm::{self, Metadata};
|
2019-02-17 18:58:58 +00:00
|
|
|
use crate::type_::Type;
|
|
|
|
use crate::type_of::LayoutLlvmExt;
|
2015-08-14 22:46:51 +00:00
|
|
|
use crate::va_arg::emit_va_arg;
|
2018-10-03 14:56:24 +00:00
|
|
|
use crate::value::Value;
|
2015-08-14 22:46:51 +00:00
|
|
|
|
2021-12-14 18:49:49 +00:00
|
|
|
fn get_simple_intrinsic<'ll>(
|
|
|
|
cx: &CodegenCx<'ll, '_>,
|
|
|
|
name: Symbol,
|
|
|
|
) -> Option<(&'ll Type, &'ll Value)> {
|
2016-02-23 20:00:59 +00:00
|
|
|
let llvm_name = match name {
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::sqrtf16 => "llvm.sqrt.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::sqrtf32 => "llvm.sqrt.f32",
|
|
|
|
sym::sqrtf64 => "llvm.sqrt.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::sqrtf128 => "llvm.sqrt.f128",
|
|
|
|
|
2024-07-12 07:54:54 +00:00
|
|
|
sym::powif16 => "llvm.powi.f16.i32",
|
|
|
|
sym::powif32 => "llvm.powi.f32.i32",
|
|
|
|
sym::powif64 => "llvm.powi.f64.i32",
|
|
|
|
sym::powif128 => "llvm.powi.f128.i32",
|
2024-03-01 08:53:26 +00:00
|
|
|
|
|
|
|
sym::sinf16 => "llvm.sin.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::sinf32 => "llvm.sin.f32",
|
|
|
|
sym::sinf64 => "llvm.sin.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::sinf128 => "llvm.sin.f128",
|
|
|
|
|
|
|
|
sym::cosf16 => "llvm.cos.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::cosf32 => "llvm.cos.f32",
|
|
|
|
sym::cosf64 => "llvm.cos.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::cosf128 => "llvm.cos.f128",
|
|
|
|
|
|
|
|
sym::powf16 => "llvm.pow.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::powf32 => "llvm.pow.f32",
|
|
|
|
sym::powf64 => "llvm.pow.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::powf128 => "llvm.pow.f128",
|
|
|
|
|
|
|
|
sym::expf16 => "llvm.exp.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::expf32 => "llvm.exp.f32",
|
|
|
|
sym::expf64 => "llvm.exp.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::expf128 => "llvm.exp.f128",
|
|
|
|
|
|
|
|
sym::exp2f16 => "llvm.exp2.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::exp2f32 => "llvm.exp2.f32",
|
|
|
|
sym::exp2f64 => "llvm.exp2.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::exp2f128 => "llvm.exp2.f128",
|
|
|
|
|
|
|
|
sym::logf16 => "llvm.log.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::logf32 => "llvm.log.f32",
|
|
|
|
sym::logf64 => "llvm.log.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::logf128 => "llvm.log.f128",
|
|
|
|
|
|
|
|
sym::log10f16 => "llvm.log10.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::log10f32 => "llvm.log10.f32",
|
|
|
|
sym::log10f64 => "llvm.log10.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::log10f128 => "llvm.log10.f128",
|
|
|
|
|
|
|
|
sym::log2f16 => "llvm.log2.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::log2f32 => "llvm.log2.f32",
|
|
|
|
sym::log2f64 => "llvm.log2.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::log2f128 => "llvm.log2.f128",
|
|
|
|
|
|
|
|
sym::fmaf16 => "llvm.fma.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::fmaf32 => "llvm.fma.f32",
|
|
|
|
sym::fmaf64 => "llvm.fma.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::fmaf128 => "llvm.fma.f128",
|
|
|
|
|
intrinsics.fmuladdf{16,32,64,128}: expose llvm.fmuladd.* semantics
Add intrinsics `fmuladd{f16,f32,f64,f128}`. This computes `(a * b) +
c`, to be fused if the code generator determines that (i) the target
instruction set has support for a fused operation, and (ii) that the
fused operation is more efficient than the equivalent, separate pair
of `mul` and `add` instructions.
https://llvm.org/docs/LangRef.html#llvm-fmuladd-intrinsic
MIRI support is included for f32 and f64.
The codegen_cranelift uses the `fma` function from libc, which is a
correct implementation, but without the desired performance semantic. I
think this requires an update to cranelift to expose a suitable
instruction in its IR.
I have not tested with codegen_gcc, but it should behave the same
way (using `fma` from libc).
2024-01-06 04:04:41 +00:00
|
|
|
sym::fmuladdf16 => "llvm.fmuladd.f16",
|
|
|
|
sym::fmuladdf32 => "llvm.fmuladd.f32",
|
|
|
|
sym::fmuladdf64 => "llvm.fmuladd.f64",
|
|
|
|
sym::fmuladdf128 => "llvm.fmuladd.f128",
|
|
|
|
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::fabsf16 => "llvm.fabs.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::fabsf32 => "llvm.fabs.f32",
|
|
|
|
sym::fabsf64 => "llvm.fabs.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::fabsf128 => "llvm.fabs.f128",
|
|
|
|
|
|
|
|
sym::minnumf16 => "llvm.minnum.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::minnumf32 => "llvm.minnum.f32",
|
|
|
|
sym::minnumf64 => "llvm.minnum.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::minnumf128 => "llvm.minnum.f128",
|
|
|
|
|
|
|
|
sym::maxnumf16 => "llvm.maxnum.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::maxnumf32 => "llvm.maxnum.f32",
|
|
|
|
sym::maxnumf64 => "llvm.maxnum.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::maxnumf128 => "llvm.maxnum.f128",
|
|
|
|
|
|
|
|
sym::copysignf16 => "llvm.copysign.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::copysignf32 => "llvm.copysign.f32",
|
|
|
|
sym::copysignf64 => "llvm.copysign.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::copysignf128 => "llvm.copysign.f128",
|
|
|
|
|
|
|
|
sym::floorf16 => "llvm.floor.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::floorf32 => "llvm.floor.f32",
|
|
|
|
sym::floorf64 => "llvm.floor.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::floorf128 => "llvm.floor.f128",
|
|
|
|
|
|
|
|
sym::ceilf16 => "llvm.ceil.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::ceilf32 => "llvm.ceil.f32",
|
|
|
|
sym::ceilf64 => "llvm.ceil.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::ceilf128 => "llvm.ceil.f128",
|
|
|
|
|
|
|
|
sym::truncf16 => "llvm.trunc.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::truncf32 => "llvm.trunc.f32",
|
|
|
|
sym::truncf64 => "llvm.trunc.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::truncf128 => "llvm.trunc.f128",
|
|
|
|
|
2025-02-04 14:15:28 +00:00
|
|
|
// We could use any of `rint`, `nearbyint`, or `roundeven`
|
|
|
|
// for this -- they are all identical in semantics when
|
|
|
|
// assuming the default FP environment.
|
|
|
|
// `rint` is what we used for $forever.
|
|
|
|
sym::round_ties_even_f16 => "llvm.rint.f16",
|
|
|
|
sym::round_ties_even_f32 => "llvm.rint.f32",
|
|
|
|
sym::round_ties_even_f64 => "llvm.rint.f64",
|
|
|
|
sym::round_ties_even_f128 => "llvm.rint.f128",
|
2024-03-01 08:53:26 +00:00
|
|
|
|
|
|
|
sym::roundf16 => "llvm.round.f16",
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::roundf32 => "llvm.round.f32",
|
|
|
|
sym::roundf64 => "llvm.round.f64",
|
2024-03-01 08:53:26 +00:00
|
|
|
sym::roundf128 => "llvm.round.f128",
|
|
|
|
|
2022-05-11 13:52:00 +00:00
|
|
|
sym::ptr_mask => "llvm.ptrmask",
|
2024-03-01 08:53:26 +00:00
|
|
|
|
2014-02-14 05:07:09 +00:00
|
|
|
_ => return None,
|
|
|
|
};
|
2021-09-30 17:38:50 +00:00
|
|
|
Some(cx.get_intrinsic(llvm_name))
|
2014-01-29 23:44:14 +00:00
|
|
|
}
|
|
|
|
|
2024-09-16 20:39:34 +00:00
|
|
|
impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
2018-09-11 09:46:03 +00:00
|
|
|
fn codegen_intrinsic_call(
|
2018-10-05 13:08:49 +00:00
|
|
|
&mut self,
|
2019-06-07 17:22:42 +00:00
|
|
|
instance: ty::Instance<'tcx>,
|
2019-10-29 14:35:26 +00:00
|
|
|
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
|
2018-09-11 09:46:03 +00:00
|
|
|
args: &[OperandRef<'tcx, &'ll Value>],
|
|
|
|
llresult: &'ll Value,
|
|
|
|
span: Span,
|
2024-01-31 20:39:59 +00:00
|
|
|
) -> Result<(), ty::Instance<'tcx>> {
|
2018-11-27 18:00:25 +00:00
|
|
|
let tcx = self.tcx;
|
2024-11-15 12:53:31 +00:00
|
|
|
let callee_ty = instance.ty(tcx, self.typing_env());
|
2018-09-11 09:46:03 +00:00
|
|
|
|
2023-07-11 21:35:29 +00:00
|
|
|
let ty::FnDef(def_id, fn_args) = *callee_ty.kind() else {
|
2022-02-18 23:48:49 +00:00
|
|
|
bug!("expected fn item type, found {}", callee_ty);
|
2018-09-11 09:46:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let sig = callee_ty.fn_sig(tcx);
|
2024-11-15 12:53:31 +00:00
|
|
|
let sig = tcx.normalize_erasing_late_bound_regions(self.typing_env(), sig);
|
2018-09-11 09:46:03 +00:00
|
|
|
let arg_tys = sig.inputs();
|
|
|
|
let ret_ty = sig.output();
|
2020-07-08 01:04:10 +00:00
|
|
|
let name = tcx.item_name(def_id);
|
2018-09-11 09:46:03 +00:00
|
|
|
|
2018-11-27 18:00:25 +00:00
|
|
|
let llret_ty = self.layout_of(ret_ty).llvm_type(self);
|
2019-10-29 14:35:26 +00:00
|
|
|
let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout);
|
2018-09-11 09:46:03 +00:00
|
|
|
|
2018-11-27 18:00:25 +00:00
|
|
|
let simple = get_simple_intrinsic(self, name);
|
2018-09-11 09:46:03 +00:00
|
|
|
let llval = match name {
|
2021-08-03 22:09:57 +00:00
|
|
|
_ if simple.is_some() => {
|
|
|
|
let (simple_ty, simple_fn) = simple.unwrap();
|
|
|
|
self.call(
|
|
|
|
simple_ty,
|
2022-10-01 17:01:31 +00:00
|
|
|
None,
|
2022-12-13 06:42:44 +00:00
|
|
|
None,
|
2021-08-03 22:09:57 +00:00
|
|
|
simple_fn,
|
|
|
|
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
|
|
|
|
None,
|
2024-03-15 19:45:46 +00:00
|
|
|
Some(instance),
|
2021-08-03 22:09:57 +00:00
|
|
|
)
|
2018-09-11 09:46:03 +00:00
|
|
|
}
|
2024-01-30 02:01:15 +00:00
|
|
|
sym::is_val_statically_known => {
|
|
|
|
let intrinsic_type = args[0].layout.immediate_llvm_type(self.cx);
|
2024-08-16 22:42:10 +00:00
|
|
|
let kind = self.type_kind(intrinsic_type);
|
|
|
|
let intrinsic_name = match kind {
|
|
|
|
TypeKind::Pointer | TypeKind::Integer => {
|
|
|
|
Some(format!("llvm.is.constant.{intrinsic_type:?}"))
|
2024-01-30 02:01:15 +00:00
|
|
|
}
|
2024-08-16 22:42:10 +00:00
|
|
|
// LLVM float types' intrinsic names differ from their type names.
|
|
|
|
TypeKind::Half => Some(format!("llvm.is.constant.f16")),
|
|
|
|
TypeKind::Float => Some(format!("llvm.is.constant.f32")),
|
|
|
|
TypeKind::Double => Some(format!("llvm.is.constant.f64")),
|
|
|
|
TypeKind::FP128 => Some(format!("llvm.is.constant.f128")),
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
if let Some(intrinsic_name) = intrinsic_name {
|
|
|
|
self.call_intrinsic(&intrinsic_name, &[args[0].immediate()])
|
|
|
|
} else {
|
|
|
|
self.const_bool(false)
|
2024-01-30 02:01:15 +00:00
|
|
|
}
|
|
|
|
}
|
2024-07-26 18:36:21 +00:00
|
|
|
sym::select_unpredictable => {
|
|
|
|
let cond = args[0].immediate();
|
|
|
|
assert_eq!(args[1].layout, args[2].layout);
|
|
|
|
let select = |bx: &mut Self, true_val, false_val| {
|
|
|
|
let result = bx.select(cond, true_val, false_val);
|
|
|
|
bx.set_unpredictable(&result);
|
|
|
|
result
|
|
|
|
};
|
|
|
|
match (args[1].val, args[2].val) {
|
|
|
|
(OperandValue::Ref(true_val), OperandValue::Ref(false_val)) => {
|
|
|
|
assert!(true_val.llextra.is_none());
|
|
|
|
assert!(false_val.llextra.is_none());
|
|
|
|
assert_eq!(true_val.align, false_val.align);
|
|
|
|
let ptr = select(self, true_val.llval, false_val.llval);
|
|
|
|
let selected =
|
|
|
|
OperandValue::Ref(PlaceValue::new_sized(ptr, true_val.align));
|
|
|
|
selected.store(self, result);
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
(OperandValue::Immediate(_), OperandValue::Immediate(_))
|
|
|
|
| (OperandValue::Pair(_, _), OperandValue::Pair(_, _)) => {
|
|
|
|
let true_val = args[1].immediate_or_packed_pair(self);
|
|
|
|
let false_val = args[2].immediate_or_packed_pair(self);
|
|
|
|
select(self, true_val, false_val)
|
|
|
|
}
|
|
|
|
(OperandValue::ZeroSized, OperandValue::ZeroSized) => return Ok(()),
|
|
|
|
_ => span_bug!(span, "Incompatible OperandValue for select_unpredictable"),
|
|
|
|
}
|
|
|
|
}
|
2024-02-25 17:51:22 +00:00
|
|
|
sym::catch_unwind => {
|
|
|
|
catch_unwind_intrinsic(
|
2018-10-05 13:08:49 +00:00
|
|
|
self,
|
2018-09-11 09:46:03 +00:00
|
|
|
args[0].immediate(),
|
|
|
|
args[1].immediate(),
|
|
|
|
args[2].immediate(),
|
|
|
|
llresult,
|
|
|
|
);
|
2024-01-31 20:39:59 +00:00
|
|
|
return Ok(());
|
2018-09-11 09:46:03 +00:00
|
|
|
}
|
2021-08-03 22:09:57 +00:00
|
|
|
sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[]),
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::va_copy => {
|
2021-08-03 22:09:57 +00:00
|
|
|
self.call_intrinsic("llvm.va_copy", &[args[0].immediate(), args[1].immediate()])
|
2018-10-23 23:13:33 +00:00
|
|
|
}
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::va_arg => {
|
2024-10-29 20:37:26 +00:00
|
|
|
match fn_abi.ret.layout.backend_repr {
|
2025-02-18 08:12:26 +00:00
|
|
|
BackendRepr::Scalar(scalar) => {
|
2022-03-03 12:02:12 +00:00
|
|
|
match scalar.primitive() {
|
2018-10-23 23:13:33 +00:00
|
|
|
Primitive::Int(..) => {
|
|
|
|
if self.cx().size_of(ret_ty).bytes() < 4 {
|
2021-08-22 12:46:15 +00:00
|
|
|
// `va_arg` should not be called on an integer type
|
2018-10-23 23:13:33 +00:00
|
|
|
// less than 4 bytes in length. If it is, promote
|
2021-08-22 14:20:58 +00:00
|
|
|
// the integer to an `i32` and truncate the result
|
2018-10-23 23:13:33 +00:00
|
|
|
// back to the smaller type.
|
|
|
|
let promoted_result = emit_va_arg(self, args[0], tcx.types.i32);
|
|
|
|
self.trunc(promoted_result, llret_ty)
|
|
|
|
} else {
|
|
|
|
emit_va_arg(self, args[0], ret_ty)
|
|
|
|
}
|
|
|
|
}
|
2024-05-06 12:27:40 +00:00
|
|
|
Primitive::Float(Float::F16) => {
|
|
|
|
bug!("the va_arg intrinsic does not work with `f16`")
|
|
|
|
}
|
|
|
|
Primitive::Float(Float::F64) | Primitive::Pointer(_) => {
|
2018-10-23 23:13:33 +00:00
|
|
|
emit_va_arg(self, args[0], ret_ty)
|
|
|
|
}
|
|
|
|
// `va_arg` should never be used with the return type f32.
|
2024-05-06 12:27:40 +00:00
|
|
|
Primitive::Float(Float::F32) => {
|
|
|
|
bug!("the va_arg intrinsic does not work with `f32`")
|
|
|
|
}
|
|
|
|
Primitive::Float(Float::F128) => {
|
2024-02-28 08:44:23 +00:00
|
|
|
bug!("the va_arg intrinsic does not work with `f128`")
|
|
|
|
}
|
2018-10-23 23:13:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => bug!("the va_arg intrinsic does not work with non-scalar types"),
|
|
|
|
}
|
|
|
|
}
|
2017-08-16 13:41:09 +00:00
|
|
|
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::volatile_load | sym::unaligned_volatile_load => {
|
2023-07-11 21:35:29 +00:00
|
|
|
let tp_ty = fn_args.type_at(0);
|
2021-07-04 16:53:04 +00:00
|
|
|
let ptr = args[0].immediate();
|
2023-09-07 20:06:37 +00:00
|
|
|
let load = if let PassMode::Cast { cast: ty, pad_i32: _ } = &fn_abi.ret.mode {
|
2021-07-04 16:53:04 +00:00
|
|
|
let llty = ty.llvm_type(self);
|
|
|
|
self.volatile_load(llty, ptr)
|
|
|
|
} else {
|
|
|
|
self.volatile_load(self.layout_of(tp_ty).llvm_type(self), ptr)
|
|
|
|
};
|
2020-07-08 01:04:10 +00:00
|
|
|
let align = if name == sym::unaligned_volatile_load {
|
2018-09-11 09:46:03 +00:00
|
|
|
1
|
|
|
|
} else {
|
2018-11-27 18:00:25 +00:00
|
|
|
self.align_of(tp_ty).bytes() as u32
|
2018-09-11 09:46:03 +00:00
|
|
|
};
|
|
|
|
unsafe {
|
|
|
|
llvm::LLVMSetAlignment(load, align);
|
|
|
|
}
|
Separate immediate and in-memory ScalarPair representation
Currently, we assume that ScalarPair is always represented using
a two-element struct, both as an immediate value and when stored
in memory.
This currently works fairly well, but runs into problems with
https://github.com/rust-lang/rust/pull/116672, where a ScalarPair
involving an i128 type can no longer be represented as a two-element
struct in memory. For example, the tuple `(i32, i128)` needs to be
represented in-memory as `{ i32, [3 x i32], i128 }` to satisfy
alignment requirement. Using `{ i32, i128 }` instead will result in
the second element being stored at the wrong offset (prior to
LLVM 18).
Resolve this issue by no longer requiring that the immediate and
in-memory type for ScalarPair are the same. The in-memory type
will now look the same as for normal struct types (and will include
padding filler and similar), while the immediate type stays a
simple two-element struct type. This also means that booleans in
immediate ScalarPair are now represented as i1 rather than i8,
just like we do everywhere else.
The core change here is to llvm_type (which now treats ScalarPair
as a normal struct) and immediate_llvm_type (which returns the
two-element struct that llvm_type used to produce). The rest is
fixing things up to no longer assume these are the same. In
particular, this switches places that try to get pointers to the
ScalarPair elements to use byte-geps instead of struct-geps.
2023-12-15 11:14:39 +00:00
|
|
|
if !result.layout.is_zst() {
|
2024-04-11 06:20:39 +00:00
|
|
|
self.store_to_place(load, result.val);
|
Separate immediate and in-memory ScalarPair representation
Currently, we assume that ScalarPair is always represented using
a two-element struct, both as an immediate value and when stored
in memory.
This currently works fairly well, but runs into problems with
https://github.com/rust-lang/rust/pull/116672, where a ScalarPair
involving an i128 type can no longer be represented as a two-element
struct in memory. For example, the tuple `(i32, i128)` needs to be
represented in-memory as `{ i32, [3 x i32], i128 }` to satisfy
alignment requirement. Using `{ i32, i128 }` instead will result in
the second element being stored at the wrong offset (prior to
LLVM 18).
Resolve this issue by no longer requiring that the immediate and
in-memory type for ScalarPair are the same. The in-memory type
will now look the same as for normal struct types (and will include
padding filler and similar), while the immediate type stays a
simple two-element struct type. This also means that booleans in
immediate ScalarPair are now represented as i1 rather than i8,
just like we do everywhere else.
The core change here is to llvm_type (which now treats ScalarPair
as a normal struct) and immediate_llvm_type (which returns the
two-element struct that llvm_type used to produce). The rest is
fixing things up to no longer assume these are the same. In
particular, this switches places that try to get pointers to the
ScalarPair elements to use byte-geps instead of struct-geps.
2023-12-15 11:14:39 +00:00
|
|
|
}
|
2024-01-31 20:39:59 +00:00
|
|
|
return Ok(());
|
2018-09-11 09:46:03 +00:00
|
|
|
}
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::volatile_store => {
|
2018-10-05 13:08:49 +00:00
|
|
|
let dst = args[0].deref(self.cx());
|
2018-09-20 13:47:22 +00:00
|
|
|
args[1].val.volatile_store(self, dst);
|
2024-01-31 20:39:59 +00:00
|
|
|
return Ok(());
|
2018-09-11 09:46:03 +00:00
|
|
|
}
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::unaligned_volatile_store => {
|
2018-10-05 13:08:49 +00:00
|
|
|
let dst = args[0].deref(self.cx());
|
2018-09-20 13:47:22 +00:00
|
|
|
args[1].val.unaligned_volatile_store(self, dst);
|
2024-01-31 20:39:59 +00:00
|
|
|
return Ok(());
|
2018-09-11 09:46:03 +00:00
|
|
|
}
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::prefetch_read_data
|
|
|
|
| sym::prefetch_write_data
|
|
|
|
| sym::prefetch_read_instruction
|
|
|
|
| sym::prefetch_write_instruction => {
|
2018-09-11 09:46:03 +00:00
|
|
|
let (rw, cache_type) = match name {
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::prefetch_read_data => (0, 1),
|
|
|
|
sym::prefetch_write_data => (1, 1),
|
|
|
|
sym::prefetch_read_instruction => (0, 0),
|
|
|
|
sym::prefetch_write_instruction => (1, 0),
|
2018-09-11 09:46:03 +00:00
|
|
|
_ => bug!(),
|
|
|
|
};
|
2021-08-03 22:09:57 +00:00
|
|
|
self.call_intrinsic(
|
|
|
|
"llvm.prefetch",
|
|
|
|
&[
|
2018-09-11 09:46:03 +00:00
|
|
|
args[0].immediate(),
|
2018-11-27 18:00:25 +00:00
|
|
|
self.const_i32(rw),
|
2018-09-11 09:46:03 +00:00
|
|
|
args[1].immediate(),
|
2018-11-27 18:00:25 +00:00
|
|
|
self.const_i32(cache_type),
|
2018-09-11 09:46:03 +00:00
|
|
|
],
|
|
|
|
)
|
|
|
|
}
|
2024-11-30 06:51:46 +00:00
|
|
|
sym::carrying_mul_add => {
|
|
|
|
let (size, signed) = fn_args.type_at(0).int_size_and_signed(self.tcx);
|
|
|
|
|
|
|
|
let wide_llty = self.type_ix(size.bits() * 2);
|
|
|
|
let args = args.as_array().unwrap();
|
|
|
|
let [a, b, c, d] = args.map(|a| self.intcast(a.immediate(), wide_llty, signed));
|
|
|
|
|
|
|
|
let wide = if signed {
|
|
|
|
let prod = self.unchecked_smul(a, b);
|
|
|
|
let acc = self.unchecked_sadd(prod, c);
|
|
|
|
self.unchecked_sadd(acc, d)
|
|
|
|
} else {
|
|
|
|
let prod = self.unchecked_umul(a, b);
|
|
|
|
let acc = self.unchecked_uadd(prod, c);
|
|
|
|
self.unchecked_uadd(acc, d)
|
|
|
|
};
|
|
|
|
|
|
|
|
let narrow_llty = self.type_ix(size.bits());
|
|
|
|
let low = self.trunc(wide, narrow_llty);
|
|
|
|
let bits_const = self.const_uint(wide_llty, size.bits());
|
|
|
|
// No need for ashr when signed; LLVM changes it to lshr anyway.
|
|
|
|
let high = self.lshr(wide, bits_const);
|
|
|
|
// FIXME: could be `trunc nuw`, even for signed.
|
|
|
|
let high = self.trunc(high, narrow_llty);
|
|
|
|
|
|
|
|
let pair_llty = self.type_struct(&[narrow_llty, narrow_llty], false);
|
|
|
|
let pair = self.const_poison(pair_llty);
|
|
|
|
let pair = self.insert_value(pair, low, 0);
|
|
|
|
let pair = self.insert_value(pair, high, 1);
|
|
|
|
pair
|
|
|
|
}
|
2020-07-08 01:04:10 +00:00
|
|
|
sym::ctlz
|
|
|
|
| sym::ctlz_nonzero
|
|
|
|
| sym::cttz
|
|
|
|
| sym::cttz_nonzero
|
|
|
|
| sym::ctpop
|
|
|
|
| sym::bswap
|
|
|
|
| sym::bitreverse
|
|
|
|
| sym::rotate_left
|
|
|
|
| sym::rotate_right
|
|
|
|
| sym::saturating_add
|
|
|
|
| sym::saturating_sub => {
|
2018-09-11 09:46:03 +00:00
|
|
|
let ty = arg_tys[0];
|
2024-12-08 02:53:51 +00:00
|
|
|
if !ty.is_integral() {
|
|
|
|
tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
ty,
|
|
|
|
});
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
let (size, signed) = ty.int_size_and_signed(self.tcx);
|
|
|
|
let width = size.bits();
|
|
|
|
match name {
|
|
|
|
sym::ctlz | sym::cttz => {
|
|
|
|
let y = self.const_bool(false);
|
|
|
|
let ret = self.call_intrinsic(
|
|
|
|
&format!("llvm.{name}.i{width}"),
|
|
|
|
&[args[0].immediate(), y],
|
|
|
|
);
|
|
|
|
|
|
|
|
self.intcast(ret, llret_ty, false)
|
|
|
|
}
|
|
|
|
sym::ctlz_nonzero => {
|
|
|
|
let y = self.const_bool(true);
|
|
|
|
let llvm_name = &format!("llvm.ctlz.i{width}");
|
|
|
|
let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
|
|
|
|
self.intcast(ret, llret_ty, false)
|
|
|
|
}
|
|
|
|
sym::cttz_nonzero => {
|
|
|
|
let y = self.const_bool(true);
|
|
|
|
let llvm_name = &format!("llvm.cttz.i{width}");
|
|
|
|
let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
|
|
|
|
self.intcast(ret, llret_ty, false)
|
|
|
|
}
|
|
|
|
sym::ctpop => {
|
|
|
|
let ret = self.call_intrinsic(
|
|
|
|
&format!("llvm.ctpop.i{width}"),
|
|
|
|
&[args[0].immediate()],
|
|
|
|
);
|
|
|
|
self.intcast(ret, llret_ty, false)
|
|
|
|
}
|
|
|
|
sym::bswap => {
|
|
|
|
if width == 8 {
|
|
|
|
args[0].immediate() // byte swap a u8/i8 is just a no-op
|
|
|
|
} else {
|
|
|
|
self.call_intrinsic(
|
|
|
|
&format!("llvm.bswap.i{width}"),
|
2018-10-05 13:08:49 +00:00
|
|
|
&[args[0].immediate()],
|
2024-12-08 02:53:51 +00:00
|
|
|
)
|
2019-12-22 22:42:04 +00:00
|
|
|
}
|
2016-03-06 14:30:21 +00:00
|
|
|
}
|
2024-12-08 02:53:51 +00:00
|
|
|
sym::bitreverse => self.call_intrinsic(
|
|
|
|
&format!("llvm.bitreverse.i{width}"),
|
|
|
|
&[args[0].immediate()],
|
|
|
|
),
|
|
|
|
sym::rotate_left | sym::rotate_right => {
|
|
|
|
let is_left = name == sym::rotate_left;
|
|
|
|
let val = args[0].immediate();
|
|
|
|
let raw_shift = args[1].immediate();
|
|
|
|
// rotate = funnel shift with first two args the same
|
|
|
|
let llvm_name =
|
|
|
|
&format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width);
|
|
|
|
|
|
|
|
// llvm expects shift to be the same type as the values, but rust
|
|
|
|
// always uses `u32`.
|
|
|
|
let raw_shift = self.intcast(raw_shift, self.val_ty(val), false);
|
|
|
|
|
|
|
|
self.call_intrinsic(llvm_name, &[val, val, raw_shift])
|
|
|
|
}
|
|
|
|
sym::saturating_add | sym::saturating_sub => {
|
|
|
|
let is_add = name == sym::saturating_add;
|
|
|
|
let lhs = args[0].immediate();
|
|
|
|
let rhs = args[1].immediate();
|
|
|
|
let llvm_name = &format!(
|
|
|
|
"llvm.{}{}.sat.i{}",
|
|
|
|
if signed { 's' } else { 'u' },
|
|
|
|
if is_add { "add" } else { "sub" },
|
|
|
|
width
|
|
|
|
);
|
|
|
|
self.call_intrinsic(llvm_name, &[lhs, rhs])
|
|
|
|
}
|
|
|
|
_ => bug!(),
|
2014-07-09 22:31:45 +00:00
|
|
|
}
|
2021-05-30 17:25:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sym::raw_eq => {
|
2025-02-18 08:12:26 +00:00
|
|
|
use BackendRepr::*;
|
2023-07-11 21:35:29 +00:00
|
|
|
let tp_ty = fn_args.type_at(0);
|
2021-06-03 06:35:30 +00:00
|
|
|
let layout = self.layout_of(tp_ty).layout;
|
2024-10-29 20:37:26 +00:00
|
|
|
let use_integer_compare = match layout.backend_repr() {
|
2021-06-03 06:35:30 +00:00
|
|
|
Scalar(_) | ScalarPair(_, _) => true,
|
2025-01-26 02:15:24 +00:00
|
|
|
Vector { .. } => false,
|
2024-10-29 20:37:26 +00:00
|
|
|
Memory { .. } => {
|
2021-06-03 06:35:30 +00:00
|
|
|
// For rusty ABIs, small aggregates are actually passed
|
|
|
|
// as `RegKind::Integer` (see `FnAbi::adjust_for_abi`),
|
|
|
|
// so we re-use that same threshold here.
|
2022-03-04 02:46:56 +00:00
|
|
|
layout.size() <= self.data_layout().pointer_size * 2
|
2021-06-03 06:35:30 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-05-30 17:25:41 +00:00
|
|
|
let a = args[0].immediate();
|
|
|
|
let b = args[1].immediate();
|
2022-03-04 02:46:56 +00:00
|
|
|
if layout.size().bytes() == 0 {
|
2021-05-30 17:25:41 +00:00
|
|
|
self.const_bool(true)
|
2021-06-03 06:35:30 +00:00
|
|
|
} else if use_integer_compare {
|
2022-03-04 02:46:56 +00:00
|
|
|
let integer_ty = self.type_ix(layout.size().bits());
|
2022-12-06 05:07:28 +00:00
|
|
|
let a_val = self.load(integer_ty, a, layout.align().abi);
|
|
|
|
let b_val = self.load(integer_ty, b, layout.align().abi);
|
2021-06-03 06:35:30 +00:00
|
|
|
self.icmp(IntPredicate::IntEQ, a_val, b_val)
|
|
|
|
} else {
|
2022-03-04 02:46:56 +00:00
|
|
|
let n = self.const_usize(layout.size().bytes());
|
2022-12-06 05:07:28 +00:00
|
|
|
let cmp = self.call_intrinsic("memcmp", &[a, b, n]);
|
2022-03-22 10:43:05 +00:00
|
|
|
match self.cx.sess().target.arch.as_ref() {
|
2021-11-11 04:14:23 +00:00
|
|
|
"avr" | "msp430" => self.icmp(IntPredicate::IntEQ, cmp, self.const_i16(0)),
|
|
|
|
_ => self.icmp(IntPredicate::IntEQ, cmp, self.const_i32(0)),
|
|
|
|
}
|
2021-05-30 17:25:41 +00:00
|
|
|
}
|
2018-09-11 09:46:03 +00:00
|
|
|
}
|
2014-07-09 22:31:45 +00:00
|
|
|
|
2023-08-02 19:45:52 +00:00
|
|
|
sym::compare_bytes => {
|
2023-08-04 06:22:50 +00:00
|
|
|
// Here we assume that the `memcmp` provided by the target is a NOP for size 0.
|
2023-08-02 19:45:52 +00:00
|
|
|
let cmp = self.call_intrinsic(
|
|
|
|
"memcmp",
|
|
|
|
&[args[0].immediate(), args[1].immediate(), args[2].immediate()],
|
|
|
|
);
|
|
|
|
// Some targets have `memcmp` returning `i16`, but the intrinsic is always `i32`.
|
|
|
|
self.sext(cmp, self.type_ix(32))
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:50:33 +00:00
|
|
|
sym::black_box => {
|
|
|
|
args[0].val.store(self, result);
|
2024-04-11 05:07:21 +00:00
|
|
|
let result_val_span = [result.val.llval];
|
2021-08-10 10:50:33 +00:00
|
|
|
// We need to "use" the argument in some way LLVM can't introspect, and on
|
|
|
|
// targets that support it we can typically leverage inline assembly to do
|
|
|
|
// this. LLVM's interpretation of inline assembly is that it's, well, a black
|
|
|
|
// box. This isn't the greatest implementation since it probably deoptimizes
|
|
|
|
// more than we want, but it's so far good enough.
|
2022-11-04 16:27:28 +00:00
|
|
|
//
|
|
|
|
// For zero-sized types, the location pointed to by the result may be
|
|
|
|
// uninitialized. Do not "use" the result in this case; instead just clobber
|
|
|
|
// the memory.
|
|
|
|
let (constraint, inputs): (&str, &[_]) = if result.layout.is_zst() {
|
|
|
|
("~{memory}", &[])
|
|
|
|
} else {
|
|
|
|
("r,~{memory}", &result_val_span)
|
|
|
|
};
|
2021-08-10 10:50:33 +00:00
|
|
|
crate::asm::inline_asm_call(
|
|
|
|
self,
|
|
|
|
"",
|
2022-11-04 16:27:28 +00:00
|
|
|
constraint,
|
|
|
|
inputs,
|
2021-08-10 10:50:33 +00:00
|
|
|
self.type_void(),
|
2023-12-27 04:08:04 +00:00
|
|
|
&[],
|
2021-08-10 10:50:33 +00:00
|
|
|
true,
|
|
|
|
false,
|
2022-01-12 00:00:00 +00:00
|
|
|
llvm::AsmDialect::Att,
|
2021-08-10 10:50:33 +00:00
|
|
|
&[span],
|
2021-09-04 17:25:09 +00:00
|
|
|
false,
|
|
|
|
None,
|
2023-12-27 04:08:04 +00:00
|
|
|
None,
|
2021-08-10 10:50:33 +00:00
|
|
|
)
|
|
|
|
.unwrap_or_else(|| bug!("failed to generate inline asm call for `black_box`"));
|
|
|
|
|
|
|
|
// We have copied the value to `result` already.
|
2024-01-31 20:39:59 +00:00
|
|
|
return Ok(());
|
2021-08-10 10:50:33 +00:00
|
|
|
}
|
|
|
|
|
2021-09-18 16:03:58 +00:00
|
|
|
_ if name.as_str().starts_with("simd_") => {
|
2024-06-01 18:17:16 +00:00
|
|
|
// Unpack non-power-of-2 #[repr(packed, simd)] arguments.
|
|
|
|
// This gives them the expected layout of a regular #[repr(simd)] vector.
|
2024-05-20 05:09:29 +00:00
|
|
|
let mut loaded_args = Vec::new();
|
|
|
|
for (ty, arg) in arg_tys.iter().zip(args) {
|
|
|
|
loaded_args.push(
|
2024-06-01 18:17:16 +00:00
|
|
|
// #[repr(packed, simd)] vectors are passed like arrays (as references,
|
|
|
|
// with reduced alignment and no padding) rather than as immediates.
|
|
|
|
// We can use a vector load to fix the layout and turn the argument
|
|
|
|
// into an immediate.
|
2024-05-20 05:09:29 +00:00
|
|
|
if ty.is_simd()
|
|
|
|
&& let OperandValue::Ref(place) = arg.val
|
|
|
|
{
|
|
|
|
let (size, elem_ty) = ty.simd_size_and_type(self.tcx());
|
|
|
|
let elem_ll_ty = match elem_ty.kind() {
|
|
|
|
ty::Float(f) => self.type_float_from_ty(*f),
|
|
|
|
ty::Int(i) => self.type_int_from_ty(*i),
|
|
|
|
ty::Uint(u) => self.type_uint_from_ty(*u),
|
|
|
|
ty::RawPtr(_, _) => self.type_ptr(),
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
let loaded =
|
|
|
|
self.load_from_place(self.type_vector(elem_ll_ty, size), place);
|
|
|
|
OperandRef::from_immediate_or_packed_pair(self, loaded, arg.layout)
|
|
|
|
} else {
|
|
|
|
*arg
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let llret_ty = if ret_ty.is_simd()
|
2025-02-18 08:12:26 +00:00
|
|
|
&& let BackendRepr::Memory { .. } = self.layout_of(ret_ty).layout.backend_repr
|
2024-05-20 05:09:29 +00:00
|
|
|
{
|
|
|
|
let (size, elem_ty) = ret_ty.simd_size_and_type(self.tcx());
|
|
|
|
let elem_ll_ty = match elem_ty.kind() {
|
|
|
|
ty::Float(f) => self.type_float_from_ty(*f),
|
|
|
|
ty::Int(i) => self.type_int_from_ty(*i),
|
|
|
|
ty::Uint(u) => self.type_uint_from_ty(*u),
|
|
|
|
ty::RawPtr(_, _) => self.type_ptr(),
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
self.type_vector(elem_ll_ty, size)
|
|
|
|
} else {
|
|
|
|
llret_ty
|
|
|
|
};
|
|
|
|
|
2023-09-18 14:37:19 +00:00
|
|
|
match generic_simd_intrinsic(
|
2024-05-20 05:09:29 +00:00
|
|
|
self,
|
|
|
|
name,
|
|
|
|
callee_ty,
|
|
|
|
fn_args,
|
|
|
|
&loaded_args,
|
|
|
|
ret_ty,
|
|
|
|
llret_ty,
|
|
|
|
span,
|
2023-09-18 14:37:19 +00:00
|
|
|
) {
|
2018-09-11 09:46:03 +00:00
|
|
|
Ok(llval) => llval,
|
2024-09-18 03:34:49 +00:00
|
|
|
// If there was an error, just skip this invocation... we'll abort compilation
|
|
|
|
// anyway, but we can keep codegen'ing to find more errors.
|
2024-01-31 20:39:59 +00:00
|
|
|
Err(()) => return Ok(()),
|
2014-07-09 22:31:45 +00:00
|
|
|
}
|
|
|
|
}
|
2019-08-20 15:51:54 +00:00
|
|
|
|
2024-01-31 20:39:59 +00:00
|
|
|
_ => {
|
|
|
|
debug!("unknown intrinsic '{}' -- falling back to default body", name);
|
|
|
|
// Call the fallback body instead of generating the intrinsic code
|
|
|
|
return Err(ty::Instance::new(instance.def_id(), instance.args));
|
|
|
|
}
|
2018-09-11 09:46:03 +00:00
|
|
|
};
|
2014-07-09 22:31:45 +00:00
|
|
|
|
2019-10-29 14:35:26 +00:00
|
|
|
if !fn_abi.ret.is_ignore() {
|
2023-09-07 20:06:37 +00:00
|
|
|
if let PassMode::Cast { .. } = &fn_abi.ret.mode {
|
2024-04-11 05:07:21 +00:00
|
|
|
self.store(llval, result.val.llval, result.val.align);
|
2018-09-11 09:46:03 +00:00
|
|
|
} else {
|
2018-09-20 13:47:22 +00:00
|
|
|
OperandRef::from_immediate_or_packed_pair(self, llval, result.layout)
|
2018-09-14 15:48:57 +00:00
|
|
|
.val
|
|
|
|
.store(self, result);
|
2018-09-11 09:46:03 +00:00
|
|
|
}
|
2016-03-06 11:23:20 +00:00
|
|
|
}
|
2024-01-31 20:39:59 +00:00
|
|
|
Ok(())
|
2014-07-09 22:31:45 +00:00
|
|
|
}
|
2018-11-24 15:01:47 +00:00
|
|
|
|
|
|
|
fn abort(&mut self) {
|
2021-08-03 22:09:57 +00:00
|
|
|
self.call_intrinsic("llvm.trap", &[]);
|
2018-11-24 15:01:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn assume(&mut self, val: Self::Value) {
|
2024-02-25 22:56:08 +00:00
|
|
|
if self.cx.sess().opts.optimize != rustc_session::config::OptLevel::No {
|
|
|
|
self.call_intrinsic("llvm.assume", &[val]);
|
|
|
|
}
|
2018-11-24 15:01:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value {
|
2024-02-25 22:56:08 +00:00
|
|
|
if self.cx.sess().opts.optimize != rustc_session::config::OptLevel::No {
|
|
|
|
self.call_intrinsic("llvm.expect.i1", &[cond, self.const_bool(expected)])
|
|
|
|
} else {
|
|
|
|
cond
|
|
|
|
}
|
2018-11-24 15:01:47 +00:00
|
|
|
}
|
2018-11-30 15:53:44 +00:00
|
|
|
|
2024-09-19 01:39:28 +00:00
|
|
|
fn type_test(&mut self, pointer: Self::Value, typeid: Self::Metadata) -> Self::Value {
|
2021-10-07 22:33:13 +00:00
|
|
|
// Test the called operand using llvm.type.test intrinsic. The LowerTypeTests link-time
|
|
|
|
// optimization pass replaces calls to this intrinsic with code to test type membership.
|
2024-09-19 01:39:28 +00:00
|
|
|
let typeid = unsafe { llvm::LLVMMetadataAsValue(&self.llcx, typeid) };
|
2022-12-06 05:07:28 +00:00
|
|
|
self.call_intrinsic("llvm.type.test", &[pointer, typeid])
|
2021-10-07 22:33:13 +00:00
|
|
|
}
|
|
|
|
|
2022-04-21 12:58:25 +00:00
|
|
|
fn type_checked_load(
|
|
|
|
&mut self,
|
|
|
|
llvtable: &'ll Value,
|
|
|
|
vtable_byte_offset: u64,
|
2024-09-19 01:39:28 +00:00
|
|
|
typeid: &'ll Metadata,
|
2022-04-21 12:58:25 +00:00
|
|
|
) -> Self::Value {
|
2024-09-19 01:39:28 +00:00
|
|
|
let typeid = unsafe { llvm::LLVMMetadataAsValue(&self.llcx, typeid) };
|
2022-04-21 12:58:25 +00:00
|
|
|
let vtable_byte_offset = self.const_i32(vtable_byte_offset as i32);
|
2022-12-03 18:27:43 +00:00
|
|
|
let type_checked_load =
|
|
|
|
self.call_intrinsic("llvm.type.checked.load", &[llvtable, vtable_byte_offset, typeid]);
|
|
|
|
self.extract_value(type_checked_load, 0)
|
2022-04-21 12:58:25 +00:00
|
|
|
}
|
|
|
|
|
2019-03-25 21:28:03 +00:00
|
|
|
fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value {
|
2021-08-03 22:09:57 +00:00
|
|
|
self.call_intrinsic("llvm.va_start", &[va_list])
|
2018-11-30 15:53:44 +00:00
|
|
|
}
|
|
|
|
|
2019-03-25 21:28:03 +00:00
|
|
|
fn va_end(&mut self, va_list: &'ll Value) -> &'ll Value {
|
2021-08-03 22:09:57 +00:00
|
|
|
self.call_intrinsic("llvm.va_end", &[va_list])
|
2018-11-30 15:53:44 +00:00
|
|
|
}
|
2014-07-09 22:31:45 +00:00
|
|
|
}
|
|
|
|
|
2024-02-25 17:51:22 +00:00
|
|
|
fn catch_unwind_intrinsic<'ll>(
|
2021-12-14 18:49:49 +00:00
|
|
|
bx: &mut Builder<'_, 'll, '_>,
|
2020-03-02 13:59:20 +00:00
|
|
|
try_func: &'ll Value,
|
2018-07-10 10:28:39 +00:00
|
|
|
data: &'ll Value,
|
2020-03-02 13:59:20 +00:00
|
|
|
catch_func: &'ll Value,
|
2018-07-10 10:28:39 +00:00
|
|
|
dest: &'ll Value,
|
2016-12-11 03:32:44 +00:00
|
|
|
) {
|
2020-03-31 22:15:39 +00:00
|
|
|
if bx.sess().panic_strategy() == PanicStrategy::Abort {
|
2022-12-06 05:07:28 +00:00
|
|
|
let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
|
2024-03-15 19:45:46 +00:00
|
|
|
bx.call(try_func_ty, None, None, try_func, &[data], None, None);
|
2019-12-26 15:03:41 +00:00
|
|
|
// Return 0 unconditionally from the intrinsic call;
|
|
|
|
// we can never unwind.
|
|
|
|
let ret_align = bx.tcx().data_layout.i32_align.abi;
|
|
|
|
bx.store(bx.const_i32(0), dest, ret_align);
|
2018-11-27 18:00:25 +00:00
|
|
|
} else if wants_msvc_seh(bx.sess()) {
|
2020-03-02 13:59:20 +00:00
|
|
|
codegen_msvc_try(bx, try_func, data, catch_func, dest);
|
2023-05-01 19:24:21 +00:00
|
|
|
} else if wants_wasm_eh(bx.sess()) {
|
|
|
|
codegen_wasm_try(bx, try_func, data, catch_func, dest);
|
2022-06-17 22:01:19 +00:00
|
|
|
} else if bx.sess().target.os == "emscripten" {
|
2020-03-21 07:50:38 +00:00
|
|
|
codegen_emcc_try(bx, try_func, data, catch_func, dest);
|
2015-07-20 20:27:38 +00:00
|
|
|
} else {
|
2020-03-02 13:59:20 +00:00
|
|
|
codegen_gnu_try(bx, try_func, data, catch_func, dest);
|
2015-07-20 20:27:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-24 01:18:44 +00:00
|
|
|
// MSVC's definition of the `rust_try` function.
|
2015-07-20 20:27:38 +00:00
|
|
|
//
|
2015-10-24 01:18:44 +00:00
|
|
|
// This implementation uses the new exception handling instructions in LLVM
|
|
|
|
// which have support in LLVM for SEH on MSVC targets. Although these
|
|
|
|
// instructions are meant to work for all targets, as of the time of this
|
|
|
|
// writing, however, LLVM does not recommend the usage of these new instructions
|
|
|
|
// as the old ones are still more optimized.
|
2021-12-14 18:49:49 +00:00
|
|
|
fn codegen_msvc_try<'ll>(
|
|
|
|
bx: &mut Builder<'_, 'll, '_>,
|
2020-03-02 13:59:20 +00:00
|
|
|
try_func: &'ll Value,
|
2018-07-10 10:28:39 +00:00
|
|
|
data: &'ll Value,
|
2020-03-02 13:59:20 +00:00
|
|
|
catch_func: &'ll Value,
|
2018-07-10 10:28:39 +00:00
|
|
|
dest: &'ll Value,
|
2018-07-02 14:52:53 +00:00
|
|
|
) {
|
2021-08-03 22:09:57 +00:00
|
|
|
let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
|
2018-11-27 18:00:25 +00:00
|
|
|
bx.set_personality_fn(bx.eh_personality());
|
2015-07-20 20:27:38 +00:00
|
|
|
|
2022-02-18 14:10:56 +00:00
|
|
|
let normal = bx.append_sibling_block("normal");
|
|
|
|
let catchswitch = bx.append_sibling_block("catchswitch");
|
|
|
|
let catchpad_rust = bx.append_sibling_block("catchpad_rust");
|
|
|
|
let catchpad_foreign = bx.append_sibling_block("catchpad_foreign");
|
|
|
|
let caught = bx.append_sibling_block("caught");
|
2015-07-20 20:27:38 +00:00
|
|
|
|
2020-03-02 13:59:20 +00:00
|
|
|
let try_func = llvm::get_param(bx.llfn(), 0);
|
2018-01-05 05:12:32 +00:00
|
|
|
let data = llvm::get_param(bx.llfn(), 1);
|
2020-03-02 13:59:20 +00:00
|
|
|
let catch_func = llvm::get_param(bx.llfn(), 2);
|
2015-07-20 20:27:38 +00:00
|
|
|
|
2015-10-24 01:18:44 +00:00
|
|
|
// We're generating an IR snippet that looks like:
|
|
|
|
//
|
2020-03-02 13:59:20 +00:00
|
|
|
// declare i32 @rust_try(%try_func, %data, %catch_func) {
|
2020-03-21 07:50:38 +00:00
|
|
|
// %slot = alloca i8*
|
2020-03-02 13:59:20 +00:00
|
|
|
// invoke %try_func(%data) to label %normal unwind label %catchswitch
|
2015-10-24 01:18:44 +00:00
|
|
|
//
|
|
|
|
// normal:
|
|
|
|
// ret i32 0
|
2015-07-20 20:27:38 +00:00
|
|
|
//
|
2015-10-24 01:18:44 +00:00
|
|
|
// catchswitch:
|
2020-03-21 07:50:38 +00:00
|
|
|
// %cs = catchswitch within none [%catchpad_rust, %catchpad_foreign] unwind to caller
|
2015-07-20 20:27:38 +00:00
|
|
|
//
|
2020-03-21 07:50:38 +00:00
|
|
|
// catchpad_rust:
|
|
|
|
// %tok = catchpad within %cs [%type_descriptor, 8, %slot]
|
2020-03-02 13:59:20 +00:00
|
|
|
// %ptr = load %slot
|
|
|
|
// call %catch_func(%data, %ptr)
|
2015-10-24 01:18:44 +00:00
|
|
|
// catchret from %tok to label %caught
|
2015-07-20 20:27:38 +00:00
|
|
|
//
|
2020-03-21 07:50:38 +00:00
|
|
|
// catchpad_foreign:
|
|
|
|
// %tok = catchpad within %cs [null, 64, null]
|
|
|
|
// call %catch_func(%data, null)
|
|
|
|
// catchret from %tok to label %caught
|
|
|
|
//
|
2015-10-24 01:18:44 +00:00
|
|
|
// caught:
|
|
|
|
// ret i32 1
|
|
|
|
// }
|
2015-07-20 20:27:38 +00:00
|
|
|
//
|
rustc: Use C++ personalities on MSVC
Currently the compiler has two relatively critical bugs in the implementation of
MSVC unwinding:
* #33112 - faults like segfaults and illegal instructions will run destructors
in Rust, meaning we keep running code after a super-fatal exception
has happened.
* #33116 - When compiling with LTO plus `-Z no-landing-pads` (or `-C
panic=abort` with the previous commit) LLVM won't remove all `invoke`
instructions, meaning that some landing pads stick around and
cleanups may be run due to the previous bug.
These both stem from the flavor of "personality function" that Rust uses for
unwinding on MSVC. On 32-bit this is `_except_handler3` and on 64-bit this is
`__C_specific_handler`, but they both essentially are the "most generic"
personality functions for catching exceptions and running cleanups. That is,
thse two personalities will run cleanups for all exceptions unconditionally, so
when we use them we run cleanups for **all SEH exceptions** (include things like
segfaults).
Note that this also explains why LLVM won't optimize away `invoke` instructions.
These functions can legitimately still unwind (the `nounwind` attribute only
seems to apply to "C++ exception-like unwining"). Also note that the standard
library only *catches* Rust exceptions, not others like segfaults and illegal
instructions.
LLVM has support for another personality, `__CxxFrameHandler3`, which does not
run cleanups for general exceptions, only C++ exceptions thrown by
`_CxxThrowException`. This essentially ideally matches our use case, so this
commit moves us over to using this well-known personality function as well as
exception-throwing function.
This doesn't *seem* to pull in any extra runtime dependencies just yet, but if
it does we can perhaps try to work out how to implement more of it in Rust
rather than relying on MSVCRT runtime bits.
More details about how this is actually implemented can be found in the changes
itself, but this...
Closes #33112
Closes #33116
2016-04-26 21:30:01 +00:00
|
|
|
// This structure follows the basic usage of throw/try/catch in LLVM.
|
|
|
|
// For example, compile this C++ snippet to see what LLVM generates:
|
|
|
|
//
|
2019-10-27 22:33:25 +00:00
|
|
|
// struct rust_panic {
|
2019-12-29 20:16:20 +00:00
|
|
|
// rust_panic(const rust_panic&);
|
|
|
|
// ~rust_panic();
|
|
|
|
//
|
2020-03-21 07:50:38 +00:00
|
|
|
// void* x[2];
|
2020-03-02 13:59:20 +00:00
|
|
|
// };
|
2019-10-27 22:33:25 +00:00
|
|
|
//
|
2020-03-02 13:59:20 +00:00
|
|
|
// int __rust_try(
|
|
|
|
// void (*try_func)(void*),
|
|
|
|
// void *data,
|
|
|
|
// void (*catch_func)(void*, void*) noexcept
|
|
|
|
// ) {
|
rustc: Use C++ personalities on MSVC
Currently the compiler has two relatively critical bugs in the implementation of
MSVC unwinding:
* #33112 - faults like segfaults and illegal instructions will run destructors
in Rust, meaning we keep running code after a super-fatal exception
has happened.
* #33116 - When compiling with LTO plus `-Z no-landing-pads` (or `-C
panic=abort` with the previous commit) LLVM won't remove all `invoke`
instructions, meaning that some landing pads stick around and
cleanups may be run due to the previous bug.
These both stem from the flavor of "personality function" that Rust uses for
unwinding on MSVC. On 32-bit this is `_except_handler3` and on 64-bit this is
`__C_specific_handler`, but they both essentially are the "most generic"
personality functions for catching exceptions and running cleanups. That is,
thse two personalities will run cleanups for all exceptions unconditionally, so
when we use them we run cleanups for **all SEH exceptions** (include things like
segfaults).
Note that this also explains why LLVM won't optimize away `invoke` instructions.
These functions can legitimately still unwind (the `nounwind` attribute only
seems to apply to "C++ exception-like unwining"). Also note that the standard
library only *catches* Rust exceptions, not others like segfaults and illegal
instructions.
LLVM has support for another personality, `__CxxFrameHandler3`, which does not
run cleanups for general exceptions, only C++ exceptions thrown by
`_CxxThrowException`. This essentially ideally matches our use case, so this
commit moves us over to using this well-known personality function as well as
exception-throwing function.
This doesn't *seem* to pull in any extra runtime dependencies just yet, but if
it does we can perhaps try to work out how to implement more of it in Rust
rather than relying on MSVCRT runtime bits.
More details about how this is actually implemented can be found in the changes
itself, but this...
Closes #33112
Closes #33116
2016-04-26 21:30:01 +00:00
|
|
|
// try {
|
2020-03-02 13:59:20 +00:00
|
|
|
// try_func(data);
|
rustc: Use C++ personalities on MSVC
Currently the compiler has two relatively critical bugs in the implementation of
MSVC unwinding:
* #33112 - faults like segfaults and illegal instructions will run destructors
in Rust, meaning we keep running code after a super-fatal exception
has happened.
* #33116 - When compiling with LTO plus `-Z no-landing-pads` (or `-C
panic=abort` with the previous commit) LLVM won't remove all `invoke`
instructions, meaning that some landing pads stick around and
cleanups may be run due to the previous bug.
These both stem from the flavor of "personality function" that Rust uses for
unwinding on MSVC. On 32-bit this is `_except_handler3` and on 64-bit this is
`__C_specific_handler`, but they both essentially are the "most generic"
personality functions for catching exceptions and running cleanups. That is,
thse two personalities will run cleanups for all exceptions unconditionally, so
when we use them we run cleanups for **all SEH exceptions** (include things like
segfaults).
Note that this also explains why LLVM won't optimize away `invoke` instructions.
These functions can legitimately still unwind (the `nounwind` attribute only
seems to apply to "C++ exception-like unwining"). Also note that the standard
library only *catches* Rust exceptions, not others like segfaults and illegal
instructions.
LLVM has support for another personality, `__CxxFrameHandler3`, which does not
run cleanups for general exceptions, only C++ exceptions thrown by
`_CxxThrowException`. This essentially ideally matches our use case, so this
commit moves us over to using this well-known personality function as well as
exception-throwing function.
This doesn't *seem* to pull in any extra runtime dependencies just yet, but if
it does we can perhaps try to work out how to implement more of it in Rust
rather than relying on MSVCRT runtime bits.
More details about how this is actually implemented can be found in the changes
itself, but this...
Closes #33112
Closes #33116
2016-04-26 21:30:01 +00:00
|
|
|
// return 0;
|
2019-12-29 20:16:20 +00:00
|
|
|
// } catch(rust_panic& a) {
|
2020-03-02 13:59:20 +00:00
|
|
|
// catch_func(data, &a);
|
rustc: Use C++ personalities on MSVC
Currently the compiler has two relatively critical bugs in the implementation of
MSVC unwinding:
* #33112 - faults like segfaults and illegal instructions will run destructors
in Rust, meaning we keep running code after a super-fatal exception
has happened.
* #33116 - When compiling with LTO plus `-Z no-landing-pads` (or `-C
panic=abort` with the previous commit) LLVM won't remove all `invoke`
instructions, meaning that some landing pads stick around and
cleanups may be run due to the previous bug.
These both stem from the flavor of "personality function" that Rust uses for
unwinding on MSVC. On 32-bit this is `_except_handler3` and on 64-bit this is
`__C_specific_handler`, but they both essentially are the "most generic"
personality functions for catching exceptions and running cleanups. That is,
thse two personalities will run cleanups for all exceptions unconditionally, so
when we use them we run cleanups for **all SEH exceptions** (include things like
segfaults).
Note that this also explains why LLVM won't optimize away `invoke` instructions.
These functions can legitimately still unwind (the `nounwind` attribute only
seems to apply to "C++ exception-like unwining"). Also note that the standard
library only *catches* Rust exceptions, not others like segfaults and illegal
instructions.
LLVM has support for another personality, `__CxxFrameHandler3`, which does not
run cleanups for general exceptions, only C++ exceptions thrown by
`_CxxThrowException`. This essentially ideally matches our use case, so this
commit moves us over to using this well-known personality function as well as
exception-throwing function.
This doesn't *seem* to pull in any extra runtime dependencies just yet, but if
it does we can perhaps try to work out how to implement more of it in Rust
rather than relying on MSVCRT runtime bits.
More details about how this is actually implemented can be found in the changes
itself, but this...
Closes #33112
Closes #33116
2016-04-26 21:30:01 +00:00
|
|
|
// return 1;
|
2020-03-21 07:50:38 +00:00
|
|
|
// } catch(...) {
|
|
|
|
// catch_func(data, NULL);
|
|
|
|
// return 1;
|
rustc: Use C++ personalities on MSVC
Currently the compiler has two relatively critical bugs in the implementation of
MSVC unwinding:
* #33112 - faults like segfaults and illegal instructions will run destructors
in Rust, meaning we keep running code after a super-fatal exception
has happened.
* #33116 - When compiling with LTO plus `-Z no-landing-pads` (or `-C
panic=abort` with the previous commit) LLVM won't remove all `invoke`
instructions, meaning that some landing pads stick around and
cleanups may be run due to the previous bug.
These both stem from the flavor of "personality function" that Rust uses for
unwinding on MSVC. On 32-bit this is `_except_handler3` and on 64-bit this is
`__C_specific_handler`, but they both essentially are the "most generic"
personality functions for catching exceptions and running cleanups. That is,
thse two personalities will run cleanups for all exceptions unconditionally, so
when we use them we run cleanups for **all SEH exceptions** (include things like
segfaults).
Note that this also explains why LLVM won't optimize away `invoke` instructions.
These functions can legitimately still unwind (the `nounwind` attribute only
seems to apply to "C++ exception-like unwining"). Also note that the standard
library only *catches* Rust exceptions, not others like segfaults and illegal
instructions.
LLVM has support for another personality, `__CxxFrameHandler3`, which does not
run cleanups for general exceptions, only C++ exceptions thrown by
`_CxxThrowException`. This essentially ideally matches our use case, so this
commit moves us over to using this well-known personality function as well as
exception-throwing function.
This doesn't *seem* to pull in any extra runtime dependencies just yet, but if
it does we can perhaps try to work out how to implement more of it in Rust
rather than relying on MSVCRT runtime bits.
More details about how this is actually implemented can be found in the changes
itself, but this...
Closes #33112
Closes #33116
2016-04-26 21:30:01 +00:00
|
|
|
// }
|
|
|
|
// }
|
2015-10-24 01:18:44 +00:00
|
|
|
//
|
|
|
|
// More information can be found in libstd's seh.rs implementation.
|
2024-02-24 05:48:20 +00:00
|
|
|
let ptr_size = bx.tcx().data_layout.pointer_size;
|
2019-12-29 20:16:20 +00:00
|
|
|
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
|
2024-02-24 05:48:20 +00:00
|
|
|
let slot = bx.alloca(ptr_size, ptr_align);
|
2022-12-06 05:07:28 +00:00
|
|
|
let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
|
2024-03-15 19:45:46 +00:00
|
|
|
bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None, None);
|
2015-10-24 01:18:44 +00:00
|
|
|
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.switch_to_block(normal);
|
|
|
|
bx.ret(bx.const_i32(0));
|
2015-10-24 01:18:44 +00:00
|
|
|
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.switch_to_block(catchswitch);
|
|
|
|
let cs = bx.catch_switch(None, None, &[catchpad_rust, catchpad_foreign]);
|
2015-10-24 01:18:44 +00:00
|
|
|
|
2020-01-13 00:55:36 +00:00
|
|
|
// We can't use the TypeDescriptor defined in libpanic_unwind because it
|
|
|
|
// might be in another DLL and the SEH encoding only supports specifying
|
|
|
|
// a TypeDescriptor from the current module.
|
|
|
|
//
|
|
|
|
// However this isn't an issue since the MSVC runtime uses string
|
|
|
|
// comparison on the type name to match TypeDescriptors rather than
|
|
|
|
// pointer equality.
|
|
|
|
//
|
|
|
|
// So instead we generate a new TypeDescriptor in each module that uses
|
|
|
|
// `try` and let the linker merge duplicate definitions in the same
|
|
|
|
// module.
|
|
|
|
//
|
|
|
|
// When modifying, make sure that the type_name string exactly matches
|
2022-12-22 16:34:27 +00:00
|
|
|
// the one used in library/panic_unwind/src/seh.rs.
|
2022-12-06 05:07:28 +00:00
|
|
|
let type_info_vtable = bx.declare_global("??_7type_info@@6B@", bx.type_ptr());
|
2020-01-13 00:55:36 +00:00
|
|
|
let type_name = bx.const_bytes(b"rust_panic\0");
|
|
|
|
let type_info =
|
2022-12-06 05:07:28 +00:00
|
|
|
bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_ptr()), type_name], false);
|
2020-01-13 00:55:36 +00:00
|
|
|
let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info));
|
2024-10-25 10:40:38 +00:00
|
|
|
|
|
|
|
llvm::set_linkage(tydesc, llvm::Linkage::LinkOnceODRLinkage);
|
|
|
|
if bx.cx.tcx.sess.target.supports_comdat() {
|
|
|
|
llvm::SetUniqueComdat(bx.llmod, tydesc);
|
2020-01-13 00:55:36 +00:00
|
|
|
}
|
2025-01-14 12:25:16 +00:00
|
|
|
llvm::set_initializer(tydesc, type_info);
|
2020-01-13 00:55:36 +00:00
|
|
|
|
2020-01-01 21:24:39 +00:00
|
|
|
// The flag value of 8 indicates that we are catching the exception by
|
2020-01-07 10:36:57 +00:00
|
|
|
// reference instead of by value. We can't use catch by value because
|
|
|
|
// that requires copying the exception object, which we don't support
|
|
|
|
// since our exception object effectively contains a Box.
|
2020-01-01 21:24:39 +00:00
|
|
|
//
|
|
|
|
// Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.switch_to_block(catchpad_rust);
|
2020-01-01 21:24:39 +00:00
|
|
|
let flags = bx.const_i32(8);
|
2022-02-18 14:37:31 +00:00
|
|
|
let funclet = bx.catch_pad(cs, &[tydesc, flags, slot]);
|
2022-12-06 05:07:28 +00:00
|
|
|
let ptr = bx.load(bx.type_ptr(), slot, ptr_align);
|
|
|
|
let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void());
|
2024-03-15 19:45:46 +00:00
|
|
|
bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), None);
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.catch_ret(&funclet, caught);
|
2019-12-29 20:16:20 +00:00
|
|
|
|
2020-03-21 07:50:38 +00:00
|
|
|
// The flag value of 64 indicates a "catch-all".
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.switch_to_block(catchpad_foreign);
|
2020-03-21 07:50:38 +00:00
|
|
|
let flags = bx.const_i32(64);
|
2022-12-06 05:07:28 +00:00
|
|
|
let null = bx.const_null(bx.type_ptr());
|
2022-02-18 14:37:31 +00:00
|
|
|
let funclet = bx.catch_pad(cs, &[null, flags, null]);
|
2024-03-15 19:45:46 +00:00
|
|
|
bx.call(catch_ty, None, None, catch_func, &[data, null], Some(&funclet), None);
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.catch_ret(&funclet, caught);
|
2015-10-24 01:18:44 +00:00
|
|
|
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.switch_to_block(caught);
|
|
|
|
bx.ret(bx.const_i32(1));
|
2015-07-20 20:27:38 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Note that no invoke is used here because by definition this function
|
|
|
|
// can't panic (that's what it's catching).
|
2024-03-15 19:45:46 +00:00
|
|
|
let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None);
|
2018-09-08 22:16:45 +00:00
|
|
|
let i32_align = bx.tcx().data_layout.i32_align.abi;
|
2018-01-05 05:12:32 +00:00
|
|
|
bx.store(ret, dest, i32_align);
|
2015-07-20 20:27:38 +00:00
|
|
|
}
|
|
|
|
|
2023-05-01 19:24:21 +00:00
|
|
|
// WASM's definition of the `rust_try` function.
|
|
|
|
fn codegen_wasm_try<'ll>(
|
|
|
|
bx: &mut Builder<'_, 'll, '_>,
|
|
|
|
try_func: &'ll Value,
|
|
|
|
data: &'ll Value,
|
|
|
|
catch_func: &'ll Value,
|
|
|
|
dest: &'ll Value,
|
|
|
|
) {
|
|
|
|
let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
|
|
|
|
bx.set_personality_fn(bx.eh_personality());
|
|
|
|
|
|
|
|
let normal = bx.append_sibling_block("normal");
|
|
|
|
let catchswitch = bx.append_sibling_block("catchswitch");
|
|
|
|
let catchpad = bx.append_sibling_block("catchpad");
|
|
|
|
let caught = bx.append_sibling_block("caught");
|
|
|
|
|
|
|
|
let try_func = llvm::get_param(bx.llfn(), 0);
|
|
|
|
let data = llvm::get_param(bx.llfn(), 1);
|
|
|
|
let catch_func = llvm::get_param(bx.llfn(), 2);
|
|
|
|
|
|
|
|
// We're generating an IR snippet that looks like:
|
|
|
|
//
|
|
|
|
// declare i32 @rust_try(%try_func, %data, %catch_func) {
|
|
|
|
// %slot = alloca i8*
|
|
|
|
// invoke %try_func(%data) to label %normal unwind label %catchswitch
|
|
|
|
//
|
|
|
|
// normal:
|
|
|
|
// ret i32 0
|
|
|
|
//
|
|
|
|
// catchswitch:
|
2023-05-22 09:19:28 +00:00
|
|
|
// %cs = catchswitch within none [%catchpad] unwind to caller
|
2023-05-01 19:24:21 +00:00
|
|
|
//
|
|
|
|
// catchpad:
|
|
|
|
// %tok = catchpad within %cs [null]
|
|
|
|
// %ptr = call @llvm.wasm.get.exception(token %tok)
|
|
|
|
// %sel = call @llvm.wasm.get.ehselector(token %tok)
|
|
|
|
// call %catch_func(%data, %ptr)
|
|
|
|
// catchret from %tok to label %caught
|
|
|
|
//
|
|
|
|
// caught:
|
|
|
|
// ret i32 1
|
|
|
|
// }
|
|
|
|
//
|
2022-12-06 05:07:28 +00:00
|
|
|
let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
|
2024-03-15 19:45:46 +00:00
|
|
|
bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None, None);
|
2023-05-01 19:24:21 +00:00
|
|
|
|
|
|
|
bx.switch_to_block(normal);
|
|
|
|
bx.ret(bx.const_i32(0));
|
|
|
|
|
|
|
|
bx.switch_to_block(catchswitch);
|
|
|
|
let cs = bx.catch_switch(None, None, &[catchpad]);
|
|
|
|
|
|
|
|
bx.switch_to_block(catchpad);
|
2022-12-06 05:07:28 +00:00
|
|
|
let null = bx.const_null(bx.type_ptr());
|
2023-05-01 19:24:21 +00:00
|
|
|
let funclet = bx.catch_pad(cs, &[null]);
|
|
|
|
|
|
|
|
let ptr = bx.call_intrinsic("llvm.wasm.get.exception", &[funclet.cleanuppad()]);
|
|
|
|
let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[funclet.cleanuppad()]);
|
|
|
|
|
2022-12-06 05:07:28 +00:00
|
|
|
let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void());
|
2024-03-15 19:45:46 +00:00
|
|
|
bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), None);
|
2023-05-01 19:24:21 +00:00
|
|
|
bx.catch_ret(&funclet, caught);
|
|
|
|
|
|
|
|
bx.switch_to_block(caught);
|
|
|
|
bx.ret(bx.const_i32(1));
|
|
|
|
});
|
|
|
|
|
|
|
|
// Note that no invoke is used here because by definition this function
|
|
|
|
// can't panic (that's what it's catching).
|
2024-03-15 19:45:46 +00:00
|
|
|
let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None);
|
2023-05-01 19:24:21 +00:00
|
|
|
let i32_align = bx.tcx().data_layout.i32_align.abi;
|
|
|
|
bx.store(ret, dest, i32_align);
|
|
|
|
}
|
|
|
|
|
2019-02-28 22:43:53 +00:00
|
|
|
// Definition of the standard `try` function for Rust using the GNU-like model
|
|
|
|
// of exceptions (e.g., the normal semantics of LLVM's `landingpad` and `invoke`
|
2015-07-20 20:27:38 +00:00
|
|
|
// instructions).
|
|
|
|
//
|
2018-05-08 13:10:16 +00:00
|
|
|
// This codegen is a little surprising because we always call a shim
|
2015-10-24 01:18:44 +00:00
|
|
|
// function instead of inlining the call to `invoke` manually here. This is done
|
|
|
|
// because in LLVM we're only allowed to have one personality per function
|
|
|
|
// definition. The call to the `try` intrinsic is being inlined into the
|
|
|
|
// function calling it, and that function may already have other personality
|
|
|
|
// functions in play. By calling a shim we're guaranteed that our shim will have
|
|
|
|
// the right personality function.
|
2021-12-14 18:49:49 +00:00
|
|
|
fn codegen_gnu_try<'ll>(
|
|
|
|
bx: &mut Builder<'_, 'll, '_>,
|
2020-03-02 13:59:20 +00:00
|
|
|
try_func: &'ll Value,
|
2018-07-10 10:28:39 +00:00
|
|
|
data: &'ll Value,
|
2020-03-02 13:59:20 +00:00
|
|
|
catch_func: &'ll Value,
|
2018-07-10 10:28:39 +00:00
|
|
|
dest: &'ll Value,
|
2018-07-02 14:52:53 +00:00
|
|
|
) {
|
2021-08-03 22:09:57 +00:00
|
|
|
let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
|
2018-05-08 13:10:16 +00:00
|
|
|
// Codegens the shims described above:
|
2015-07-14 01:11:44 +00:00
|
|
|
//
|
2018-01-05 05:12:32 +00:00
|
|
|
// bx:
|
2020-03-07 16:31:30 +00:00
|
|
|
// invoke %try_func(%data) normal %normal unwind %catch
|
2015-07-14 01:11:44 +00:00
|
|
|
//
|
|
|
|
// normal:
|
2015-10-24 01:18:44 +00:00
|
|
|
// ret 0
|
2015-07-14 01:11:44 +00:00
|
|
|
//
|
|
|
|
// catch:
|
2020-03-02 13:59:20 +00:00
|
|
|
// (%ptr, _) = landingpad
|
|
|
|
// call %catch_func(%data, %ptr)
|
2015-10-24 01:18:44 +00:00
|
|
|
// ret 1
|
2022-02-18 14:10:56 +00:00
|
|
|
let then = bx.append_sibling_block("then");
|
|
|
|
let catch = bx.append_sibling_block("catch");
|
2015-07-20 20:27:38 +00:00
|
|
|
|
2020-03-02 13:59:20 +00:00
|
|
|
let try_func = llvm::get_param(bx.llfn(), 0);
|
2018-01-05 05:12:32 +00:00
|
|
|
let data = llvm::get_param(bx.llfn(), 1);
|
2020-03-02 13:59:20 +00:00
|
|
|
let catch_func = llvm::get_param(bx.llfn(), 2);
|
2022-12-06 05:07:28 +00:00
|
|
|
let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
|
2024-03-15 19:45:46 +00:00
|
|
|
bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, None);
|
2022-02-18 14:10:56 +00:00
|
|
|
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.switch_to_block(then);
|
|
|
|
bx.ret(bx.const_i32(0));
|
2015-07-14 01:11:44 +00:00
|
|
|
|
|
|
|
// Type indicator for the exception being thrown.
|
2015-10-24 01:18:44 +00:00
|
|
|
//
|
|
|
|
// The first value in this tuple is a pointer to the exception object
|
2022-11-16 20:34:16 +00:00
|
|
|
// being thrown. The second value is a "selector" indicating which of
|
2015-10-24 01:18:44 +00:00
|
|
|
// the landing pad clauses the exception's type had been matched to.
|
|
|
|
// rust_try ignores the selector.
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.switch_to_block(catch);
|
2022-12-06 05:07:28 +00:00
|
|
|
let lpad_ty = bx.type_struct(&[bx.type_ptr(), bx.type_i32()], false);
|
2022-02-18 14:37:31 +00:00
|
|
|
let vals = bx.landing_pad(lpad_ty, bx.eh_personality(), 1);
|
2022-12-06 05:07:28 +00:00
|
|
|
let tydesc = bx.const_null(bx.type_ptr());
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.add_clause(vals, tydesc);
|
|
|
|
let ptr = bx.extract_value(vals, 0);
|
2022-12-06 05:07:28 +00:00
|
|
|
let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void());
|
2024-03-15 19:45:46 +00:00
|
|
|
bx.call(catch_ty, None, None, catch_func, &[data, ptr], None, None);
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.ret(bx.const_i32(1));
|
2015-07-14 01:11:44 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Note that no invoke is used here because by definition this function
|
|
|
|
// can't panic (that's what it's catching).
|
2024-03-15 19:45:46 +00:00
|
|
|
let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None);
|
2020-03-21 07:50:38 +00:00
|
|
|
let i32_align = bx.tcx().data_layout.i32_align.abi;
|
|
|
|
bx.store(ret, dest, i32_align);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Variant of codegen_gnu_try used for emscripten where Rust panics are
|
|
|
|
// implemented using C++ exceptions. Here we use exceptions of a specific type
|
|
|
|
// (`struct rust_panic`) to represent Rust panics.
|
2021-12-14 18:49:49 +00:00
|
|
|
fn codegen_emcc_try<'ll>(
|
|
|
|
bx: &mut Builder<'_, 'll, '_>,
|
2020-03-21 07:50:38 +00:00
|
|
|
try_func: &'ll Value,
|
|
|
|
data: &'ll Value,
|
|
|
|
catch_func: &'ll Value,
|
|
|
|
dest: &'ll Value,
|
|
|
|
) {
|
2021-08-03 22:09:57 +00:00
|
|
|
let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
|
2020-03-21 07:50:38 +00:00
|
|
|
// Codegens the shims described above:
|
|
|
|
//
|
|
|
|
// bx:
|
|
|
|
// invoke %try_func(%data) normal %normal unwind %catch
|
|
|
|
//
|
|
|
|
// normal:
|
|
|
|
// ret 0
|
|
|
|
//
|
|
|
|
// catch:
|
|
|
|
// (%ptr, %selector) = landingpad
|
|
|
|
// %rust_typeid = @llvm.eh.typeid.for(@_ZTI10rust_panic)
|
|
|
|
// %is_rust_panic = %selector == %rust_typeid
|
|
|
|
// %catch_data = alloca { i8*, i8 }
|
|
|
|
// %catch_data[0] = %ptr
|
|
|
|
// %catch_data[1] = %is_rust_panic
|
|
|
|
// call %catch_func(%data, %catch_data)
|
|
|
|
// ret 1
|
2022-02-18 14:10:56 +00:00
|
|
|
let then = bx.append_sibling_block("then");
|
|
|
|
let catch = bx.append_sibling_block("catch");
|
2020-03-21 07:50:38 +00:00
|
|
|
|
|
|
|
let try_func = llvm::get_param(bx.llfn(), 0);
|
|
|
|
let data = llvm::get_param(bx.llfn(), 1);
|
|
|
|
let catch_func = llvm::get_param(bx.llfn(), 2);
|
2022-12-06 05:07:28 +00:00
|
|
|
let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void());
|
2024-03-15 19:45:46 +00:00
|
|
|
bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, None);
|
2022-02-18 14:10:56 +00:00
|
|
|
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.switch_to_block(then);
|
|
|
|
bx.ret(bx.const_i32(0));
|
2020-03-21 07:50:38 +00:00
|
|
|
|
|
|
|
// Type indicator for the exception being thrown.
|
|
|
|
//
|
|
|
|
// The first value in this tuple is a pointer to the exception object
|
2022-11-16 20:34:16 +00:00
|
|
|
// being thrown. The second value is a "selector" indicating which of
|
2020-03-21 07:50:38 +00:00
|
|
|
// the landing pad clauses the exception's type had been matched to.
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.switch_to_block(catch);
|
2020-03-21 07:50:38 +00:00
|
|
|
let tydesc = bx.eh_catch_typeinfo();
|
2022-12-06 05:07:28 +00:00
|
|
|
let lpad_ty = bx.type_struct(&[bx.type_ptr(), bx.type_i32()], false);
|
2022-02-18 14:37:31 +00:00
|
|
|
let vals = bx.landing_pad(lpad_ty, bx.eh_personality(), 2);
|
|
|
|
bx.add_clause(vals, tydesc);
|
2022-12-06 05:07:28 +00:00
|
|
|
bx.add_clause(vals, bx.const_null(bx.type_ptr()));
|
2022-02-18 14:37:31 +00:00
|
|
|
let ptr = bx.extract_value(vals, 0);
|
|
|
|
let selector = bx.extract_value(vals, 1);
|
2020-03-21 07:50:38 +00:00
|
|
|
|
|
|
|
// Check if the typeid we got is the one for a Rust panic.
|
2022-02-18 14:37:31 +00:00
|
|
|
let rust_typeid = bx.call_intrinsic("llvm.eh.typeid.for", &[tydesc]);
|
|
|
|
let is_rust_panic = bx.icmp(IntPredicate::IntEQ, selector, rust_typeid);
|
|
|
|
let is_rust_panic = bx.zext(is_rust_panic, bx.type_bool());
|
2020-03-21 07:50:38 +00:00
|
|
|
|
|
|
|
// We need to pass two values to catch_func (ptr and is_rust_panic), so
|
|
|
|
// create an alloca and pass a pointer to that.
|
2024-02-24 05:48:20 +00:00
|
|
|
let ptr_size = bx.tcx().data_layout.pointer_size;
|
2020-03-21 07:50:38 +00:00
|
|
|
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
|
|
|
|
let i8_align = bx.tcx().data_layout.i8_align.abi;
|
2024-02-24 05:48:20 +00:00
|
|
|
// Required in order for there to be no padding between the fields.
|
|
|
|
assert!(i8_align <= ptr_align);
|
|
|
|
let catch_data = bx.alloca(2 * ptr_size, ptr_align);
|
|
|
|
bx.store(ptr, catch_data, ptr_align);
|
|
|
|
let catch_data_1 = bx.inbounds_ptradd(catch_data, bx.const_usize(ptr_size.bytes()));
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.store(is_rust_panic, catch_data_1, i8_align);
|
2020-03-21 07:50:38 +00:00
|
|
|
|
2022-12-06 05:07:28 +00:00
|
|
|
let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void());
|
2024-03-15 19:45:46 +00:00
|
|
|
bx.call(catch_ty, None, None, catch_func, &[data, catch_data], None, None);
|
2022-02-18 14:37:31 +00:00
|
|
|
bx.ret(bx.const_i32(1));
|
2020-03-21 07:50:38 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Note that no invoke is used here because by definition this function
|
|
|
|
// can't panic (that's what it's catching).
|
2024-03-15 19:45:46 +00:00
|
|
|
let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None);
|
2018-09-08 22:16:45 +00:00
|
|
|
let i32_align = bx.tcx().data_layout.i32_align.abi;
|
2018-01-05 05:12:32 +00:00
|
|
|
bx.store(ret, dest, i32_align);
|
2015-07-20 20:27:38 +00:00
|
|
|
}
|
|
|
|
|
2018-05-08 13:10:16 +00:00
|
|
|
// Helper function to give a Block to a closure to codegen a shim function.
|
2015-10-24 01:18:44 +00:00
|
|
|
// This is currently primarily used for the `try` intrinsic functions above.
|
2025-01-24 21:05:26 +00:00
|
|
|
fn gen_fn<'a, 'll, 'tcx>(
|
|
|
|
cx: &'a CodegenCx<'ll, 'tcx>,
|
2018-07-02 14:52:53 +00:00
|
|
|
name: &str,
|
2020-10-08 00:02:06 +00:00
|
|
|
rust_fn_sig: ty::PolyFnSig<'tcx>,
|
2025-01-24 21:05:26 +00:00
|
|
|
codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>),
|
2021-08-03 22:09:57 +00:00
|
|
|
) -> (&'ll Type, &'ll Value) {
|
2021-09-01 21:29:15 +00:00
|
|
|
let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty());
|
2021-08-04 18:20:31 +00:00
|
|
|
let llty = fn_abi.llvm_type(cx);
|
2023-05-03 22:22:24 +00:00
|
|
|
let llfn = cx.declare_fn(name, fn_abi, None);
|
2021-06-26 20:53:35 +00:00
|
|
|
cx.set_frame_pointer_type(llfn);
|
2020-01-14 17:42:47 +00:00
|
|
|
cx.apply_target_cpu_attr(llfn);
|
2019-10-29 16:39:54 +00:00
|
|
|
// FIXME(eddyb) find a nicer way to do this.
|
2024-10-25 10:40:38 +00:00
|
|
|
llvm::set_linkage(llfn, llvm::Linkage::InternalLinkage);
|
2021-05-06 15:57:04 +00:00
|
|
|
let llbb = Builder::append_block(cx, llfn, "entry-block");
|
|
|
|
let bx = Builder::build(cx, llbb);
|
2018-05-08 13:10:16 +00:00
|
|
|
codegen(bx);
|
2021-08-03 22:09:57 +00:00
|
|
|
(llty, llfn)
|
2015-10-24 01:18:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Helper function used to get a handle to the `__rust_try` function used to
|
|
|
|
// catch exceptions.
|
|
|
|
//
|
|
|
|
// This function is only generated once and is then cached.
|
2025-01-24 21:05:26 +00:00
|
|
|
fn get_rust_try_fn<'a, 'll, 'tcx>(
|
|
|
|
cx: &'a CodegenCx<'ll, 'tcx>,
|
|
|
|
codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>),
|
2021-08-03 22:09:57 +00:00
|
|
|
) -> (&'ll Type, &'ll Value) {
|
2018-01-05 05:04:08 +00:00
|
|
|
if let Some(llfn) = cx.rust_try_fn.get() {
|
2016-02-23 19:59:23 +00:00
|
|
|
return llfn;
|
2015-07-20 20:27:38 +00:00
|
|
|
}
|
|
|
|
|
2015-07-14 01:11:44 +00:00
|
|
|
// Define the type up front for the signature of the rust_try function.
|
2018-01-05 05:04:08 +00:00
|
|
|
let tcx = cx.tcx;
|
2023-07-05 19:13:26 +00:00
|
|
|
let i8p = Ty::new_mut_ptr(tcx, tcx.types.i8);
|
2020-10-08 00:02:06 +00:00
|
|
|
// `unsafe fn(*mut i8) -> ()`
|
2023-07-05 19:13:26 +00:00
|
|
|
let try_fn_ty = Ty::new_fn_ptr(
|
|
|
|
tcx,
|
|
|
|
ty::Binder::dummy(tcx.mk_fn_sig(
|
|
|
|
[i8p],
|
2024-05-02 15:49:23 +00:00
|
|
|
tcx.types.unit,
|
2023-07-05 19:13:26 +00:00
|
|
|
false,
|
2024-05-17 17:17:48 +00:00
|
|
|
hir::Safety::Unsafe,
|
2024-11-03 02:32:52 +00:00
|
|
|
ExternAbi::Rust,
|
2023-07-05 19:13:26 +00:00
|
|
|
)),
|
|
|
|
);
|
2020-10-08 00:02:06 +00:00
|
|
|
// `unsafe fn(*mut i8, *mut i8) -> ()`
|
2023-07-05 19:13:26 +00:00
|
|
|
let catch_fn_ty = Ty::new_fn_ptr(
|
|
|
|
tcx,
|
|
|
|
ty::Binder::dummy(tcx.mk_fn_sig(
|
|
|
|
[i8p, i8p],
|
2024-05-02 15:49:23 +00:00
|
|
|
tcx.types.unit,
|
2023-07-05 19:13:26 +00:00
|
|
|
false,
|
2024-05-17 17:17:48 +00:00
|
|
|
hir::Safety::Unsafe,
|
2024-11-03 02:32:52 +00:00
|
|
|
ExternAbi::Rust,
|
2023-07-05 19:13:26 +00:00
|
|
|
)),
|
|
|
|
);
|
2020-10-11 21:14:07 +00:00
|
|
|
// `unsafe fn(unsafe fn(*mut i8) -> (), *mut i8, unsafe fn(*mut i8, *mut i8) -> ()) -> i32`
|
2020-10-08 00:02:06 +00:00
|
|
|
let rust_fn_sig = ty::Binder::dummy(cx.tcx.mk_fn_sig(
|
2023-02-16 05:05:08 +00:00
|
|
|
[try_fn_ty, i8p, catch_fn_ty],
|
2020-10-11 21:14:07 +00:00
|
|
|
tcx.types.i32,
|
2020-10-08 00:02:06 +00:00
|
|
|
false,
|
2024-05-17 17:17:48 +00:00
|
|
|
hir::Safety::Unsafe,
|
2024-11-03 02:32:52 +00:00
|
|
|
ExternAbi::Rust,
|
2020-10-08 00:02:06 +00:00
|
|
|
));
|
|
|
|
let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen);
|
2018-01-05 05:04:08 +00:00
|
|
|
cx.rust_try_fn.set(Some(rust_try));
|
2018-10-08 15:00:30 +00:00
|
|
|
rust_try
|
2015-07-20 20:27:38 +00:00
|
|
|
}
|
2015-07-16 18:59:23 +00:00
|
|
|
|
2021-12-14 18:49:49 +00:00
|
|
|
fn generic_simd_intrinsic<'ll, 'tcx>(
|
|
|
|
bx: &mut Builder<'_, 'll, 'tcx>,
|
2020-07-08 01:04:10 +00:00
|
|
|
name: Symbol,
|
2016-12-11 03:32:44 +00:00
|
|
|
callee_ty: Ty<'tcx>,
|
2023-09-18 14:37:19 +00:00
|
|
|
fn_args: GenericArgsRef<'tcx>,
|
2018-08-02 14:48:44 +00:00
|
|
|
args: &[OperandRef<'tcx, &'ll Value>],
|
2016-12-11 03:32:44 +00:00
|
|
|
ret_ty: Ty<'tcx>,
|
2018-07-02 14:52:53 +00:00
|
|
|
llret_ty: &'ll Type,
|
2016-12-11 03:32:44 +00:00
|
|
|
span: Span,
|
2018-07-10 10:28:39 +00:00
|
|
|
) -> Result<&'ll Value, ()> {
|
2018-03-13 15:46:55 +00:00
|
|
|
macro_rules! return_error {
|
2022-11-27 01:28:01 +00:00
|
|
|
($diag: expr) => {{
|
2023-12-18 11:21:37 +00:00
|
|
|
bx.sess().dcx().emit_err($diag);
|
2022-11-27 01:28:01 +00:00
|
|
|
return Err(());
|
|
|
|
}};
|
2015-08-13 23:00:44 +00:00
|
|
|
}
|
2018-03-13 15:46:55 +00:00
|
|
|
|
|
|
|
macro_rules! require {
|
2022-11-27 01:28:01 +00:00
|
|
|
($cond: expr, $diag: expr) => {
|
2018-03-13 15:46:55 +00:00
|
|
|
if !$cond {
|
2022-11-27 01:28:01 +00:00
|
|
|
return_error!($diag);
|
2018-03-13 15:46:55 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2018-10-08 14:52:34 +00:00
|
|
|
|
2015-08-14 22:20:22 +00:00
|
|
|
macro_rules! require_simd {
|
2023-09-29 15:14:53 +00:00
|
|
|
($ty: expr, $variant:ident) => {{
|
|
|
|
require!($ty.is_simd(), InvalidMonomorphization::$variant { span, name, ty: $ty });
|
|
|
|
$ty.simd_size_and_type(bx.tcx())
|
|
|
|
}};
|
2015-08-14 22:20:22 +00:00
|
|
|
}
|
|
|
|
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
/// Returns the bitwidth of the `$ty` argument if it is an `Int` type.
|
|
|
|
macro_rules! require_int_ty {
|
|
|
|
($ty: expr, $diag: expr) => {
|
|
|
|
match $ty {
|
|
|
|
ty::Int(i) => i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()),
|
|
|
|
_ => {
|
|
|
|
return_error!($diag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the bitwidth of the `$ty` argument if it is an `Int` or `Uint` type.
|
|
|
|
macro_rules! require_int_or_uint_ty {
|
|
|
|
($ty: expr, $diag: expr) => {
|
|
|
|
match $ty {
|
|
|
|
ty::Int(i) => i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()),
|
|
|
|
ty::Uint(i) => {
|
|
|
|
i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits())
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return_error!($diag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Converts a vector mask, where each element has a bit width equal to the data elements it is used with,
|
|
|
|
/// down to an i1 based mask that can be used by llvm intrinsics.
|
|
|
|
///
|
|
|
|
/// The rust simd semantics are that each element should either consist of all ones or all zeroes,
|
|
|
|
/// but this information is not available to llvm. Truncating the vector effectively uses the lowest bit,
|
|
|
|
/// but codegen for several targets is better if we consider the highest bit by shifting.
|
|
|
|
///
|
|
|
|
/// For x86 SSE/AVX targets this is beneficial since most instructions with mask parameters only consider the highest bit.
|
|
|
|
/// So even though on llvm level we have an additional shift, in the final assembly there is no shift or truncate and
|
|
|
|
/// instead the mask can be used as is.
|
|
|
|
///
|
|
|
|
/// For aarch64 and other targets there is a benefit because a mask from the sign bit can be more
|
|
|
|
/// efficiently converted to an all ones / all zeroes mask by comparing whether each element is negative.
|
|
|
|
fn vector_mask_to_bitmask<'a, 'll, 'tcx>(
|
|
|
|
bx: &mut Builder<'a, 'll, 'tcx>,
|
|
|
|
i_xn: &'ll Value,
|
|
|
|
in_elem_bitwidth: u64,
|
|
|
|
in_len: u64,
|
|
|
|
) -> &'ll Value {
|
|
|
|
// Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position.
|
|
|
|
let shift_idx = bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _);
|
|
|
|
let shift_indices = vec![shift_idx; in_len as _];
|
|
|
|
let i_xn_msb = bx.lshr(i_xn, bx.const_vector(shift_indices.as_slice()));
|
|
|
|
// Truncate vector to an <i1 x N>
|
|
|
|
bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len))
|
|
|
|
}
|
|
|
|
|
2018-01-05 05:12:32 +00:00
|
|
|
let tcx = bx.tcx();
|
2024-11-15 12:53:31 +00:00
|
|
|
let sig = tcx.normalize_erasing_late_bound_regions(bx.typing_env(), callee_ty.fn_sig(tcx));
|
2016-11-29 02:35:38 +00:00
|
|
|
let arg_tys = sig.inputs();
|
2015-07-16 18:59:23 +00:00
|
|
|
|
2024-06-08 16:05:44 +00:00
|
|
|
// Sanity-check: all vector arguments must be immediates.
|
|
|
|
if cfg!(debug_assertions) {
|
|
|
|
for (ty, arg) in arg_tys.iter().zip(args) {
|
|
|
|
if ty.is_simd() {
|
2024-08-11 16:10:36 +00:00
|
|
|
assert_matches!(arg.val, OperandValue::Immediate(_));
|
2024-06-08 16:05:44 +00:00
|
|
|
}
|
2023-10-24 06:01:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-08 01:04:10 +00:00
|
|
|
if name == sym::simd_select_bitmask {
|
2023-09-29 15:14:53 +00:00
|
|
|
let (len, _) = require_simd!(arg_tys[1], SimdArgument);
|
2021-09-12 02:37:23 +00:00
|
|
|
|
2024-06-09 08:29:11 +00:00
|
|
|
let expected_int_bits = len.max(8).next_power_of_two();
|
|
|
|
let expected_bytes = len.div_ceil(8);
|
2021-09-12 02:37:23 +00:00
|
|
|
|
|
|
|
let mask_ty = arg_tys[0];
|
|
|
|
let mask = match mask_ty.kind() {
|
|
|
|
ty::Int(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
|
|
|
|
ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
|
|
|
|
ty::Array(elem, len)
|
|
|
|
if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
|
2024-09-27 16:56:51 +00:00
|
|
|
&& len
|
|
|
|
.try_to_target_usize(bx.tcx)
|
|
|
|
.expect("expected monomorphic const in codegen")
|
|
|
|
== expected_bytes =>
|
2021-09-12 02:37:23 +00:00
|
|
|
{
|
|
|
|
let place = PlaceRef::alloca(bx, args[0].layout);
|
|
|
|
args[0].val.store(bx, place);
|
|
|
|
let int_ty = bx.type_ix(expected_bytes * 8);
|
2024-04-11 05:07:21 +00:00
|
|
|
bx.load(int_ty, place.val.llval, Align::ONE)
|
2021-09-12 02:37:23 +00:00
|
|
|
}
|
2022-11-27 01:28:01 +00:00
|
|
|
_ => return_error!(InvalidMonomorphization::InvalidBitmask {
|
|
|
|
span,
|
|
|
|
name,
|
2021-09-12 02:37:23 +00:00
|
|
|
mask_ty,
|
|
|
|
expected_int_bits,
|
|
|
|
expected_bytes
|
2022-11-27 01:28:01 +00:00
|
|
|
}),
|
2021-09-12 02:37:23 +00:00
|
|
|
};
|
|
|
|
|
2018-12-13 20:50:42 +00:00
|
|
|
let i1 = bx.type_i1();
|
2021-09-12 02:37:23 +00:00
|
|
|
let im = bx.type_ix(len);
|
|
|
|
let i1xn = bx.type_vector(i1, len);
|
|
|
|
let m_im = bx.trunc(mask, im);
|
2020-10-03 15:13:06 +00:00
|
|
|
let m_i1s = bx.bitcast(m_im, i1xn);
|
2018-12-13 20:50:42 +00:00
|
|
|
return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
|
|
|
|
}
|
|
|
|
|
2018-12-13 20:50:42 +00:00
|
|
|
// every intrinsic below takes a SIMD vector as its first argument
|
2023-09-29 15:14:53 +00:00
|
|
|
let (in_len, in_elem) = require_simd!(arg_tys[0], SimdInput);
|
2015-08-14 22:20:22 +00:00
|
|
|
let in_ty = arg_tys[0];
|
|
|
|
|
2015-07-16 18:59:23 +00:00
|
|
|
let comparison = match name {
|
2024-05-22 07:56:57 +00:00
|
|
|
sym::simd_eq => Some(BinOp::Eq),
|
|
|
|
sym::simd_ne => Some(BinOp::Ne),
|
|
|
|
sym::simd_lt => Some(BinOp::Lt),
|
|
|
|
sym::simd_le => Some(BinOp::Le),
|
|
|
|
sym::simd_gt => Some(BinOp::Gt),
|
|
|
|
sym::simd_ge => Some(BinOp::Ge),
|
2015-07-16 18:59:23 +00:00
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(cmp_op) = comparison {
|
2023-09-29 15:14:53 +00:00
|
|
|
let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
|
2022-11-27 01:28:01 +00:00
|
|
|
|
|
|
|
require!(
|
|
|
|
in_len == out_len,
|
|
|
|
InvalidMonomorphization::ReturnLengthInputType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_len,
|
|
|
|
in_ty,
|
|
|
|
ret_ty,
|
|
|
|
out_len
|
2015-08-14 22:20:22 +00:00
|
|
|
}
|
|
|
|
);
|
2018-11-27 18:00:25 +00:00
|
|
|
require!(
|
|
|
|
bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer,
|
2022-11-27 01:28:01 +00:00
|
|
|
InvalidMonomorphization::ReturnIntegerType { span, name, ret_ty, out_ty }
|
2015-08-14 22:20:22 +00:00
|
|
|
);
|
2019-12-22 22:42:04 +00:00
|
|
|
|
2018-01-05 05:12:32 +00:00
|
|
|
return Ok(compare_simd_types(
|
|
|
|
bx,
|
2017-09-19 23:32:22 +00:00
|
|
|
args[0].immediate(),
|
|
|
|
args[1].immediate(),
|
2017-06-01 18:50:53 +00:00
|
|
|
in_elem,
|
|
|
|
llret_ty,
|
|
|
|
cmp_op,
|
|
|
|
));
|
2015-07-16 18:59:23 +00:00
|
|
|
}
|
|
|
|
|
2023-09-18 14:37:19 +00:00
|
|
|
if name == sym::simd_shuffle_generic {
|
2025-01-27 03:30:00 +00:00
|
|
|
let idx = fn_args[2].expect_const().to_value().valtree.unwrap_branch();
|
2023-09-18 14:37:19 +00:00
|
|
|
let n = idx.len() as u64;
|
|
|
|
|
2023-09-29 15:14:53 +00:00
|
|
|
let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
|
2023-09-18 14:37:19 +00:00
|
|
|
require!(
|
|
|
|
out_len == n,
|
|
|
|
InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len }
|
|
|
|
);
|
|
|
|
require!(
|
|
|
|
in_elem == out_ty,
|
|
|
|
InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty }
|
|
|
|
);
|
|
|
|
|
|
|
|
let total_len = in_len * 2;
|
|
|
|
|
|
|
|
let indices: Option<Vec<_>> = idx
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(arg_idx, val)| {
|
2024-06-08 14:13:45 +00:00
|
|
|
let idx = val.unwrap_leaf().to_i32();
|
2023-09-18 14:37:19 +00:00
|
|
|
if idx >= i32::try_from(total_len).unwrap() {
|
2024-02-23 18:22:27 +00:00
|
|
|
bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
|
2023-09-18 14:37:19 +00:00
|
|
|
span,
|
|
|
|
name,
|
|
|
|
arg_idx: arg_idx as u64,
|
|
|
|
total_len: total_len.into(),
|
|
|
|
});
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(bx.const_i32(idx))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
let Some(indices) = indices else {
|
|
|
|
return Ok(bx.const_null(llret_ty));
|
|
|
|
};
|
|
|
|
|
|
|
|
return Ok(bx.shuffle_vector(
|
|
|
|
args[0].immediate(),
|
|
|
|
args[1].immediate(),
|
|
|
|
bx.const_vector(&indices),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2023-07-10 13:03:48 +00:00
|
|
|
if name == sym::simd_shuffle {
|
2024-09-12 09:33:13 +00:00
|
|
|
// Make sure this is actually a SIMD vector.
|
2024-08-06 10:25:37 +00:00
|
|
|
let idx_ty = args[2].layout.ty;
|
2024-09-12 09:33:13 +00:00
|
|
|
let n: u64 = if idx_ty.is_simd()
|
|
|
|
&& matches!(idx_ty.simd_size_and_type(bx.cx.tcx).1.kind(), ty::Uint(ty::UintTy::U32))
|
|
|
|
{
|
|
|
|
idx_ty.simd_size_and_type(bx.cx.tcx).0
|
|
|
|
} else {
|
|
|
|
return_error!(InvalidMonomorphization::SimdShuffle { span, name, ty: idx_ty })
|
2021-09-11 14:47:28 +00:00
|
|
|
};
|
2015-07-16 18:59:23 +00:00
|
|
|
|
2023-09-29 15:14:53 +00:00
|
|
|
let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
out_len == n,
|
|
|
|
InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len }
|
|
|
|
);
|
|
|
|
require!(
|
|
|
|
in_elem == out_ty,
|
|
|
|
InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty }
|
2015-08-14 22:20:22 +00:00
|
|
|
);
|
2015-07-16 18:59:23 +00:00
|
|
|
|
2019-11-16 15:09:45 +00:00
|
|
|
let total_len = u128::from(in_len) * 2;
|
2015-07-16 18:59:23 +00:00
|
|
|
|
2024-09-12 09:33:13 +00:00
|
|
|
// Check that the indices are in-bounds.
|
|
|
|
let indices = args[2].immediate();
|
|
|
|
for i in 0..n {
|
|
|
|
let val = bx.const_get_elt(indices, i as u64);
|
|
|
|
let idx = bx
|
|
|
|
.const_to_opt_u128(val, true)
|
|
|
|
.unwrap_or_else(|| bug!("typeck should have already ensured that these are const"));
|
|
|
|
if idx >= total_len {
|
|
|
|
return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
arg_idx: i,
|
|
|
|
total_len,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2015-07-16 18:59:23 +00:00
|
|
|
|
2024-09-12 09:33:13 +00:00
|
|
|
return Ok(bx.shuffle_vector(args[0].immediate(), args[1].immediate(), indices));
|
2015-07-16 18:59:23 +00:00
|
|
|
}
|
2015-07-20 18:55:47 +00:00
|
|
|
|
2020-07-08 01:04:10 +00:00
|
|
|
if name == sym::simd_insert {
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
in_elem == arg_tys[2],
|
|
|
|
InvalidMonomorphization::InsertedType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_elem,
|
|
|
|
in_ty,
|
|
|
|
out_ty: arg_tys[2]
|
2015-08-14 22:20:22 +00:00
|
|
|
}
|
|
|
|
);
|
2024-02-23 18:22:27 +00:00
|
|
|
let idx = bx
|
|
|
|
.const_to_opt_u128(args[1].immediate(), false)
|
|
|
|
.expect("typeck should have ensure that this is a const");
|
|
|
|
if idx >= in_len.into() {
|
2024-09-12 09:33:13 +00:00
|
|
|
return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
|
2024-02-23 18:22:27 +00:00
|
|
|
span,
|
|
|
|
name,
|
|
|
|
arg_idx: 1,
|
|
|
|
total_len: in_len.into(),
|
|
|
|
});
|
|
|
|
}
|
2018-01-05 05:12:32 +00:00
|
|
|
return Ok(bx.insert_element(
|
|
|
|
args[0].immediate(),
|
2018-10-08 14:52:34 +00:00
|
|
|
args[2].immediate(),
|
2024-02-23 18:22:27 +00:00
|
|
|
bx.const_i32(idx as i32),
|
2018-10-08 14:52:34 +00:00
|
|
|
));
|
2015-07-20 18:55:47 +00:00
|
|
|
}
|
2020-07-08 01:04:10 +00:00
|
|
|
if name == sym::simd_extract {
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
ret_ty == in_elem,
|
|
|
|
InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
|
2015-08-14 22:20:22 +00:00
|
|
|
);
|
2024-02-23 18:22:27 +00:00
|
|
|
let idx = bx
|
|
|
|
.const_to_opt_u128(args[1].immediate(), false)
|
|
|
|
.expect("typeck should have ensure that this is a const");
|
|
|
|
if idx >= in_len.into() {
|
2024-09-12 09:33:13 +00:00
|
|
|
return_error!(InvalidMonomorphization::SimdIndexOutOfBounds {
|
2024-02-23 18:22:27 +00:00
|
|
|
span,
|
|
|
|
name,
|
|
|
|
arg_idx: 1,
|
|
|
|
total_len: in_len.into(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return Ok(bx.extract_element(args[0].immediate(), bx.const_i32(idx as i32)));
|
2015-07-20 18:55:47 +00:00
|
|
|
}
|
2015-07-29 23:40:22 +00:00
|
|
|
|
2020-07-08 01:04:10 +00:00
|
|
|
if name == sym::simd_select {
|
2018-03-18 17:33:36 +00:00
|
|
|
let m_elem_ty = in_elem;
|
|
|
|
let m_len = in_len;
|
2023-09-29 15:14:53 +00:00
|
|
|
let (v_len, _) = require_simd!(arg_tys[1], SimdArgument);
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
m_len == v_len,
|
|
|
|
InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len }
|
2018-03-18 17:33:36 +00:00
|
|
|
);
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
let in_elem_bitwidth = require_int_ty!(
|
|
|
|
m_elem_ty.kind(),
|
|
|
|
InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }
|
|
|
|
);
|
|
|
|
let m_i1s = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, m_len);
|
2018-03-18 17:33:36 +00:00
|
|
|
return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
|
|
|
|
}
|
|
|
|
|
2020-07-08 01:04:10 +00:00
|
|
|
if name == sym::simd_bitmask {
|
2024-06-09 08:29:11 +00:00
|
|
|
// The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a vector mask and
|
|
|
|
// returns one bit for each lane (which must all be `0` or `!0`) in the form of either:
|
2021-09-11 18:00:48 +00:00
|
|
|
// * an unsigned integer
|
|
|
|
// * an array of `u8`
|
|
|
|
// If the vector has less than 8 lanes, a u8 is returned with zeroed trailing bits.
|
2021-11-10 01:54:28 +00:00
|
|
|
//
|
|
|
|
// The bit order of the result depends on the byte endianness, LSB-first for little
|
|
|
|
// endian and MSB-first for big endian.
|
2024-06-09 08:29:11 +00:00
|
|
|
let expected_int_bits = in_len.max(8).next_power_of_two();
|
|
|
|
let expected_bytes = in_len.div_ceil(8);
|
2019-01-02 15:49:30 +00:00
|
|
|
|
|
|
|
// Integer vector <i{in_bitwidth} x in_len>:
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
let in_elem_bitwidth = require_int_or_uint_ty!(
|
|
|
|
in_elem.kind(),
|
|
|
|
InvalidMonomorphization::VectorArgument { span, name, in_ty, in_elem }
|
|
|
|
);
|
2019-01-02 15:49:30 +00:00
|
|
|
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
let i1xn = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, in_len);
|
2019-01-02 15:49:30 +00:00
|
|
|
// Bitcast <i1 x N> to iN:
|
2020-03-21 12:58:06 +00:00
|
|
|
let i_ = bx.bitcast(i1xn, bx.type_ix(in_len));
|
2021-09-11 18:00:48 +00:00
|
|
|
|
|
|
|
match ret_ty.kind() {
|
|
|
|
ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => {
|
|
|
|
// Zero-extend iN to the bitmask type:
|
|
|
|
return Ok(bx.zext(i_, bx.type_ix(expected_int_bits)));
|
|
|
|
}
|
|
|
|
ty::Array(elem, len)
|
|
|
|
if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
|
2024-09-27 16:56:51 +00:00
|
|
|
&& len
|
|
|
|
.try_to_target_usize(bx.tcx)
|
|
|
|
.expect("expected monomorphic const in codegen")
|
|
|
|
== expected_bytes =>
|
2021-09-11 18:00:48 +00:00
|
|
|
{
|
2022-03-30 05:39:38 +00:00
|
|
|
// Zero-extend iN to the array length:
|
2021-09-11 18:00:48 +00:00
|
|
|
let ze = bx.zext(i_, bx.type_ix(expected_bytes * 8));
|
|
|
|
|
|
|
|
// Convert the integer to a byte array
|
2024-02-24 05:48:20 +00:00
|
|
|
let ptr = bx.alloca(Size::from_bytes(expected_bytes), Align::ONE);
|
2021-09-11 18:00:48 +00:00
|
|
|
bx.store(ze, ptr, Align::ONE);
|
|
|
|
let array_ty = bx.type_array(bx.type_i8(), expected_bytes);
|
|
|
|
return Ok(bx.load(array_ty, ptr, Align::ONE));
|
|
|
|
}
|
2022-11-27 01:28:01 +00:00
|
|
|
_ => return_error!(InvalidMonomorphization::CannotReturn {
|
|
|
|
span,
|
|
|
|
name,
|
2021-09-11 18:00:48 +00:00
|
|
|
ret_ty,
|
|
|
|
expected_int_bits,
|
|
|
|
expected_bytes
|
2022-11-27 01:28:01 +00:00
|
|
|
}),
|
2021-09-11 18:00:48 +00:00
|
|
|
}
|
2019-01-02 15:49:30 +00:00
|
|
|
}
|
|
|
|
|
2021-12-14 18:49:49 +00:00
|
|
|
fn simd_simple_float_intrinsic<'ll, 'tcx>(
|
2021-01-03 04:49:28 +00:00
|
|
|
name: Symbol,
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 03:13:38 +00:00
|
|
|
in_elem: Ty<'_>,
|
|
|
|
in_ty: Ty<'_>,
|
2019-11-16 12:31:09 +00:00
|
|
|
in_len: u64,
|
2021-12-14 18:49:49 +00:00
|
|
|
bx: &mut Builder<'_, 'll, 'tcx>,
|
2018-07-02 14:52:53 +00:00
|
|
|
span: Span,
|
2018-08-02 14:48:44 +00:00
|
|
|
args: &[OperandRef<'tcx, &'ll Value>],
|
2018-07-10 10:28:39 +00:00
|
|
|
) -> Result<&'ll Value, ()> {
|
2022-11-27 01:28:01 +00:00
|
|
|
macro_rules! return_error {
|
|
|
|
($diag: expr) => {{
|
2023-12-18 11:21:37 +00:00
|
|
|
bx.sess().dcx().emit_err($diag);
|
2022-11-27 01:28:01 +00:00
|
|
|
return Err(());
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
2021-01-03 04:49:28 +00:00
|
|
|
let (elem_ty_str, elem_ty) = if let ty::Float(f) = in_elem.kind() {
|
|
|
|
let elem_ty = bx.cx.type_float_from_ty(*f);
|
|
|
|
match f.bit_width() {
|
2024-07-08 16:27:26 +00:00
|
|
|
16 => ("f16", elem_ty),
|
2021-01-03 04:49:28 +00:00
|
|
|
32 => ("f32", elem_ty),
|
|
|
|
64 => ("f64", elem_ty),
|
2024-07-08 16:27:26 +00:00
|
|
|
128 => ("f128", elem_ty),
|
2022-11-27 01:28:01 +00:00
|
|
|
_ => return_error!(InvalidMonomorphization::FloatingPointVector {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
f_ty: *f,
|
|
|
|
in_ty,
|
|
|
|
}),
|
2018-05-04 18:07:35 +00:00
|
|
|
}
|
2021-01-03 04:49:28 +00:00
|
|
|
} else {
|
2022-11-27 01:28:01 +00:00
|
|
|
return_error!(InvalidMonomorphization::FloatingPointType { span, name, in_ty });
|
2021-01-03 04:49:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let vec_ty = bx.type_vector(elem_ty, in_len);
|
|
|
|
|
|
|
|
let (intr_name, fn_ty) = match name {
|
|
|
|
sym::simd_ceil => ("ceil", bx.type_func(&[vec_ty], vec_ty)),
|
2021-04-17 17:40:59 +00:00
|
|
|
sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)),
|
|
|
|
sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)),
|
2021-01-03 04:49:28 +00:00
|
|
|
sym::simd_fexp2 => ("exp2", bx.type_func(&[vec_ty], vec_ty)),
|
2021-04-17 17:40:59 +00:00
|
|
|
sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)),
|
2021-01-03 04:49:28 +00:00
|
|
|
sym::simd_flog10 => ("log10", bx.type_func(&[vec_ty], vec_ty)),
|
|
|
|
sym::simd_flog2 => ("log2", bx.type_func(&[vec_ty], vec_ty)),
|
|
|
|
sym::simd_flog => ("log", bx.type_func(&[vec_ty], vec_ty)),
|
2021-04-17 17:40:59 +00:00
|
|
|
sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)),
|
|
|
|
sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
|
2024-11-23 19:31:20 +00:00
|
|
|
sym::simd_relaxed_fma => ("fmuladd", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
|
2021-01-03 04:49:28 +00:00
|
|
|
sym::simd_fpowi => ("powi", bx.type_func(&[vec_ty, bx.type_i32()], vec_ty)),
|
|
|
|
sym::simd_fpow => ("pow", bx.type_func(&[vec_ty, vec_ty], vec_ty)),
|
2021-04-17 17:40:59 +00:00
|
|
|
sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)),
|
|
|
|
sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)),
|
|
|
|
sym::simd_round => ("round", bx.type_func(&[vec_ty], vec_ty)),
|
|
|
|
sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)),
|
2022-11-27 01:28:01 +00:00
|
|
|
_ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name }),
|
2018-05-04 18:07:35 +00:00
|
|
|
};
|
2023-07-25 21:04:01 +00:00
|
|
|
let llvm_name = &format!("llvm.{intr_name}.v{in_len}{elem_ty_str}");
|
2021-09-30 17:38:50 +00:00
|
|
|
let f = bx.declare_cfn(llvm_name, llvm::UnnamedAddr::No, fn_ty);
|
2022-10-01 17:01:31 +00:00
|
|
|
let c = bx.call(
|
|
|
|
fn_ty,
|
|
|
|
None,
|
2022-12-13 06:42:44 +00:00
|
|
|
None,
|
2022-10-01 17:01:31 +00:00
|
|
|
f,
|
|
|
|
&args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
|
|
|
|
None,
|
2024-03-15 19:45:46 +00:00
|
|
|
None,
|
2022-10-01 17:01:31 +00:00
|
|
|
);
|
2018-10-08 14:58:26 +00:00
|
|
|
Ok(c)
|
2018-05-04 18:07:35 +00:00
|
|
|
}
|
|
|
|
|
2021-01-03 04:49:28 +00:00
|
|
|
if std::matches!(
|
|
|
|
name,
|
2021-04-17 17:40:59 +00:00
|
|
|
sym::simd_ceil
|
2021-01-03 04:49:28 +00:00
|
|
|
| sym::simd_fabs
|
2021-04-17 17:40:59 +00:00
|
|
|
| sym::simd_fcos
|
2021-01-03 04:49:28 +00:00
|
|
|
| sym::simd_fexp2
|
2021-04-17 17:40:59 +00:00
|
|
|
| sym::simd_fexp
|
2021-01-03 04:49:28 +00:00
|
|
|
| sym::simd_flog10
|
|
|
|
| sym::simd_flog2
|
|
|
|
| sym::simd_flog
|
2021-04-17 17:40:59 +00:00
|
|
|
| sym::simd_floor
|
2021-01-03 04:49:28 +00:00
|
|
|
| sym::simd_fma
|
2021-04-17 17:40:59 +00:00
|
|
|
| sym::simd_fpow
|
|
|
|
| sym::simd_fpowi
|
|
|
|
| sym::simd_fsin
|
|
|
|
| sym::simd_fsqrt
|
2024-11-23 19:31:20 +00:00
|
|
|
| sym::simd_relaxed_fma
|
2021-04-17 17:40:59 +00:00
|
|
|
| sym::simd_round
|
|
|
|
| sym::simd_trunc
|
2021-01-03 04:49:28 +00:00
|
|
|
) {
|
|
|
|
return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args);
|
2018-05-04 18:07:35 +00:00
|
|
|
}
|
|
|
|
|
2018-05-16 18:47:28 +00:00
|
|
|
// FIXME: use:
|
|
|
|
// https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Function.h#L182
|
|
|
|
// https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Intrinsics.h#L81
|
2023-07-29 20:56:27 +00:00
|
|
|
fn llvm_vector_str(bx: &Builder<'_, '_, '_>, elem_ty: Ty<'_>, vec_len: u64) -> String {
|
2020-08-02 22:49:11 +00:00
|
|
|
match *elem_ty.kind() {
|
2021-09-27 12:18:22 +00:00
|
|
|
ty::Int(v) => format!(
|
2023-07-29 20:56:27 +00:00
|
|
|
"v{}i{}",
|
2021-09-27 12:18:22 +00:00
|
|
|
vec_len,
|
|
|
|
// Normalize to prevent crash if v: IntTy::Isize
|
|
|
|
v.normalize(bx.target_spec().pointer_width).bit_width().unwrap()
|
|
|
|
),
|
|
|
|
ty::Uint(v) => format!(
|
2023-07-29 20:56:27 +00:00
|
|
|
"v{}i{}",
|
2021-09-27 12:18:22 +00:00
|
|
|
vec_len,
|
|
|
|
// Normalize to prevent crash if v: UIntTy::Usize
|
|
|
|
v.normalize(bx.target_spec().pointer_width).bit_width().unwrap()
|
|
|
|
),
|
2023-07-29 20:56:27 +00:00
|
|
|
ty::Float(v) => format!("v{}f{}", vec_len, v.bit_width()),
|
2024-03-21 21:11:06 +00:00
|
|
|
ty::RawPtr(_, _) => format!("v{}p0", vec_len),
|
2018-05-16 18:47:28 +00:00
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-29 20:56:27 +00:00
|
|
|
fn llvm_vector_ty<'ll>(cx: &CodegenCx<'ll, '_>, elem_ty: Ty<'_>, vec_len: u64) -> &'ll Type {
|
|
|
|
let elem_ty = match *elem_ty.kind() {
|
2018-09-06 20:52:15 +00:00
|
|
|
ty::Int(v) => cx.type_int_from_ty(v),
|
|
|
|
ty::Uint(v) => cx.type_uint_from_ty(v),
|
|
|
|
ty::Float(v) => cx.type_float_from_ty(v),
|
2024-03-21 21:11:06 +00:00
|
|
|
ty::RawPtr(_, _) => cx.type_ptr(),
|
2018-05-16 18:47:28 +00:00
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
2019-11-16 15:09:45 +00:00
|
|
|
cx.type_vector(elem_ty, vec_len)
|
2018-05-16 18:47:28 +00:00
|
|
|
}
|
|
|
|
|
2020-07-08 01:04:10 +00:00
|
|
|
if name == sym::simd_gather {
|
2018-05-04 18:07:35 +00:00
|
|
|
// simd_gather(values: <N x T>, pointers: <N x *_ T>,
|
|
|
|
// mask: <N x i{M}>) -> <N x T>
|
|
|
|
// * N: number of elements in the input vectors
|
|
|
|
// * T: type of the element to load
|
|
|
|
// * M: any integer width is supported, will be truncated to i1
|
|
|
|
|
|
|
|
// All types must be simd vector types
|
2023-09-29 15:14:53 +00:00
|
|
|
|
|
|
|
// The second argument must be a simd vector with an element type that's a pointer
|
|
|
|
// to the element type of the first argument
|
|
|
|
let (_, element_ty0) = require_simd!(in_ty, SimdFirst);
|
|
|
|
let (out_len, element_ty1) = require_simd!(arg_tys[1], SimdSecond);
|
|
|
|
// The element type of the third argument must be a signed integer type of any width:
|
|
|
|
let (out_len2, element_ty2) = require_simd!(arg_tys[2], SimdThird);
|
2023-09-29 14:54:38 +00:00
|
|
|
require_simd!(ret_ty, SimdReturn);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
// Of the same length:
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
in_len == out_len,
|
|
|
|
InvalidMonomorphization::SecondArgumentLength {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_len,
|
|
|
|
in_ty,
|
|
|
|
arg_ty: arg_tys[1],
|
|
|
|
out_len
|
2018-05-04 18:07:35 +00:00
|
|
|
}
|
|
|
|
);
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
in_len == out_len2,
|
|
|
|
InvalidMonomorphization::ThirdArgumentLength {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_len,
|
|
|
|
in_ty,
|
|
|
|
arg_ty: arg_tys[2],
|
|
|
|
out_len: out_len2
|
2018-05-04 18:07:35 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// The return type must match the first argument type
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
ret_ty == in_ty,
|
|
|
|
InvalidMonomorphization::ExpectedReturnType { span, name, in_ty, ret_ty }
|
|
|
|
);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
2023-07-29 20:56:27 +00:00
|
|
|
require!(
|
|
|
|
matches!(
|
2024-03-21 21:33:10 +00:00
|
|
|
*element_ty1.kind(),
|
|
|
|
ty::RawPtr(p_ty, _) if p_ty == in_elem && p_ty.kind() == element_ty0.kind()
|
2023-07-29 20:56:27 +00:00
|
|
|
),
|
|
|
|
InvalidMonomorphization::ExpectedElementType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
expected_element: element_ty1,
|
|
|
|
second_arg: arg_tys[1],
|
|
|
|
in_elem,
|
|
|
|
in_ty,
|
|
|
|
mutability: ExpectedPointerMutability::Not,
|
2018-05-04 18:07:35 +00:00
|
|
|
}
|
2023-07-29 20:56:27 +00:00
|
|
|
);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
let mask_elem_bitwidth = require_int_ty!(
|
|
|
|
element_ty2.kind(),
|
|
|
|
InvalidMonomorphization::ThirdArgElementType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
expected_element: element_ty2,
|
|
|
|
third_arg: arg_tys[2]
|
|
|
|
}
|
|
|
|
);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
// Alignment of T, must be a constant integer value:
|
2018-11-27 18:00:25 +00:00
|
|
|
let alignment_ty = bx.type_i32();
|
|
|
|
let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
// Truncate the mask vector to a vector of i1s:
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len);
|
|
|
|
let mask_ty = bx.type_vector(bx.type_i1(), in_len);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
// Type of the vector of pointers:
|
2023-07-29 20:56:27 +00:00
|
|
|
let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len);
|
|
|
|
let llvm_pointer_vec_str = llvm_vector_str(bx, element_ty1, in_len);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
// Type of the vector of elements:
|
2023-07-29 20:56:27 +00:00
|
|
|
let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len);
|
|
|
|
let llvm_elem_vec_str = llvm_vector_str(bx, element_ty0, in_len);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
let llvm_intrinsic =
|
2023-07-25 21:04:01 +00:00
|
|
|
format!("llvm.masked.gather.{llvm_elem_vec_str}.{llvm_pointer_vec_str}");
|
2021-08-03 22:09:57 +00:00
|
|
|
let fn_ty = bx.type_func(
|
|
|
|
&[llvm_pointer_vec_ty, alignment_ty, mask_ty, llvm_elem_vec_ty],
|
|
|
|
llvm_elem_vec_ty,
|
2018-08-07 15:14:40 +00:00
|
|
|
);
|
2021-08-03 22:09:57 +00:00
|
|
|
let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
|
2022-10-01 17:01:31 +00:00
|
|
|
let v = bx.call(
|
|
|
|
fn_ty,
|
|
|
|
None,
|
2022-12-13 06:42:44 +00:00
|
|
|
None,
|
2022-10-01 17:01:31 +00:00
|
|
|
f,
|
|
|
|
&[args[1].immediate(), alignment, mask, args[0].immediate()],
|
|
|
|
None,
|
2024-03-15 19:45:46 +00:00
|
|
|
None,
|
2022-10-01 17:01:31 +00:00
|
|
|
);
|
2018-05-04 18:07:35 +00:00
|
|
|
return Ok(v);
|
|
|
|
}
|
|
|
|
|
2023-11-15 22:08:02 +00:00
|
|
|
if name == sym::simd_masked_load {
|
|
|
|
// simd_masked_load(mask: <N x i{M}>, pointer: *_ T, values: <N x T>) -> <N x T>
|
|
|
|
// * N: number of elements in the input vectors
|
|
|
|
// * T: type of the element to load
|
|
|
|
// * M: any integer width is supported, will be truncated to i1
|
|
|
|
// Loads contiguous elements from memory behind `pointer`, but only for
|
|
|
|
// those lanes whose `mask` bit is enabled.
|
|
|
|
// The memory addresses corresponding to the “off” lanes are not accessed.
|
|
|
|
|
|
|
|
// The element type of the "mask" argument must be a signed integer type of any width
|
|
|
|
let mask_ty = in_ty;
|
|
|
|
let (mask_len, mask_elem) = (in_len, in_elem);
|
|
|
|
|
|
|
|
// The second argument must be a pointer matching the element type
|
|
|
|
let pointer_ty = arg_tys[1];
|
|
|
|
|
|
|
|
// The last argument is a passthrough vector providing values for disabled lanes
|
|
|
|
let values_ty = arg_tys[2];
|
|
|
|
let (values_len, values_elem) = require_simd!(values_ty, SimdThird);
|
|
|
|
|
|
|
|
require_simd!(ret_ty, SimdReturn);
|
|
|
|
|
|
|
|
// Of the same length:
|
|
|
|
require!(
|
|
|
|
values_len == mask_len,
|
|
|
|
InvalidMonomorphization::ThirdArgumentLength {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_len: mask_len,
|
|
|
|
in_ty: mask_ty,
|
|
|
|
arg_ty: values_ty,
|
|
|
|
out_len: values_len
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// The return type must match the last argument type
|
|
|
|
require!(
|
|
|
|
ret_ty == values_ty,
|
|
|
|
InvalidMonomorphization::ExpectedReturnType { span, name, in_ty: values_ty, ret_ty }
|
|
|
|
);
|
|
|
|
|
|
|
|
require!(
|
|
|
|
matches!(
|
2024-03-21 21:33:10 +00:00
|
|
|
*pointer_ty.kind(),
|
|
|
|
ty::RawPtr(p_ty, _) if p_ty == values_elem && p_ty.kind() == values_elem.kind()
|
2023-11-15 22:08:02 +00:00
|
|
|
),
|
|
|
|
InvalidMonomorphization::ExpectedElementType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
expected_element: values_elem,
|
|
|
|
second_arg: pointer_ty,
|
|
|
|
in_elem: values_elem,
|
|
|
|
in_ty: values_ty,
|
|
|
|
mutability: ExpectedPointerMutability::Not,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
let m_elem_bitwidth = require_int_ty!(
|
|
|
|
mask_elem.kind(),
|
|
|
|
InvalidMonomorphization::ThirdArgElementType {
|
2023-11-15 22:08:02 +00:00
|
|
|
span,
|
|
|
|
name,
|
|
|
|
expected_element: values_elem,
|
|
|
|
third_arg: mask_ty,
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
|
|
|
|
let mask_ty = bx.type_vector(bx.type_i1(), mask_len);
|
2023-11-15 22:08:02 +00:00
|
|
|
|
|
|
|
// Alignment of T, must be a constant integer value:
|
|
|
|
let alignment_ty = bx.type_i32();
|
2023-12-12 12:11:59 +00:00
|
|
|
let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32);
|
2023-11-15 22:08:02 +00:00
|
|
|
|
|
|
|
let llvm_pointer = bx.type_ptr();
|
|
|
|
|
|
|
|
// Type of the vector of elements:
|
|
|
|
let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
|
|
|
|
let llvm_elem_vec_str = llvm_vector_str(bx, values_elem, values_len);
|
|
|
|
|
|
|
|
let llvm_intrinsic = format!("llvm.masked.load.{llvm_elem_vec_str}.p0");
|
|
|
|
let fn_ty = bx
|
|
|
|
.type_func(&[llvm_pointer, alignment_ty, mask_ty, llvm_elem_vec_ty], llvm_elem_vec_ty);
|
|
|
|
let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
|
|
|
|
let v = bx.call(
|
|
|
|
fn_ty,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
f,
|
|
|
|
&[args[1].immediate(), alignment, mask, args[2].immediate()],
|
|
|
|
None,
|
2024-03-15 19:45:46 +00:00
|
|
|
None,
|
2023-11-15 22:08:02 +00:00
|
|
|
);
|
|
|
|
return Ok(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
if name == sym::simd_masked_store {
|
|
|
|
// simd_masked_store(mask: <N x i{M}>, pointer: *mut T, values: <N x T>) -> ()
|
|
|
|
// * N: number of elements in the input vectors
|
|
|
|
// * T: type of the element to load
|
|
|
|
// * M: any integer width is supported, will be truncated to i1
|
|
|
|
// Stores contiguous elements to memory behind `pointer`, but only for
|
|
|
|
// those lanes whose `mask` bit is enabled.
|
|
|
|
// The memory addresses corresponding to the “off” lanes are not accessed.
|
|
|
|
|
|
|
|
// The element type of the "mask" argument must be a signed integer type of any width
|
|
|
|
let mask_ty = in_ty;
|
|
|
|
let (mask_len, mask_elem) = (in_len, in_elem);
|
|
|
|
|
|
|
|
// The second argument must be a pointer matching the element type
|
|
|
|
let pointer_ty = arg_tys[1];
|
|
|
|
|
|
|
|
// The last argument specifies the values to store to memory
|
|
|
|
let values_ty = arg_tys[2];
|
|
|
|
let (values_len, values_elem) = require_simd!(values_ty, SimdThird);
|
|
|
|
|
|
|
|
// Of the same length:
|
|
|
|
require!(
|
|
|
|
values_len == mask_len,
|
|
|
|
InvalidMonomorphization::ThirdArgumentLength {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_len: mask_len,
|
|
|
|
in_ty: mask_ty,
|
|
|
|
arg_ty: values_ty,
|
|
|
|
out_len: values_len
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// The second argument must be a mutable pointer type matching the element type
|
|
|
|
require!(
|
|
|
|
matches!(
|
2024-03-21 21:33:10 +00:00
|
|
|
*pointer_ty.kind(),
|
2024-09-18 03:34:49 +00:00
|
|
|
ty::RawPtr(p_ty, p_mutbl)
|
|
|
|
if p_ty == values_elem && p_ty.kind() == values_elem.kind() && p_mutbl.is_mut()
|
2023-11-15 22:08:02 +00:00
|
|
|
),
|
|
|
|
InvalidMonomorphization::ExpectedElementType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
expected_element: values_elem,
|
|
|
|
second_arg: pointer_ty,
|
|
|
|
in_elem: values_elem,
|
|
|
|
in_ty: values_ty,
|
|
|
|
mutability: ExpectedPointerMutability::Mut,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
let m_elem_bitwidth = require_int_ty!(
|
|
|
|
mask_elem.kind(),
|
|
|
|
InvalidMonomorphization::ThirdArgElementType {
|
2023-11-15 22:08:02 +00:00
|
|
|
span,
|
|
|
|
name,
|
|
|
|
expected_element: values_elem,
|
|
|
|
third_arg: mask_ty,
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
|
|
|
|
let mask_ty = bx.type_vector(bx.type_i1(), mask_len);
|
2023-11-15 22:08:02 +00:00
|
|
|
|
|
|
|
// Alignment of T, must be a constant integer value:
|
|
|
|
let alignment_ty = bx.type_i32();
|
|
|
|
let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32);
|
|
|
|
|
|
|
|
let ret_t = bx.type_void();
|
|
|
|
|
|
|
|
let llvm_pointer = bx.type_ptr();
|
|
|
|
|
|
|
|
// Type of the vector of elements:
|
|
|
|
let llvm_elem_vec_ty = llvm_vector_ty(bx, values_elem, values_len);
|
|
|
|
let llvm_elem_vec_str = llvm_vector_str(bx, values_elem, values_len);
|
|
|
|
|
|
|
|
let llvm_intrinsic = format!("llvm.masked.store.{llvm_elem_vec_str}.p0");
|
|
|
|
let fn_ty = bx.type_func(&[llvm_elem_vec_ty, llvm_pointer, alignment_ty, mask_ty], ret_t);
|
|
|
|
let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
|
|
|
|
let v = bx.call(
|
|
|
|
fn_ty,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
f,
|
|
|
|
&[args[2].immediate(), args[1].immediate(), alignment, mask],
|
|
|
|
None,
|
2024-03-15 19:45:46 +00:00
|
|
|
None,
|
2023-11-15 22:08:02 +00:00
|
|
|
);
|
|
|
|
return Ok(v);
|
|
|
|
}
|
|
|
|
|
2020-07-08 01:04:10 +00:00
|
|
|
if name == sym::simd_scatter {
|
2018-05-04 18:07:35 +00:00
|
|
|
// simd_scatter(values: <N x T>, pointers: <N x *mut T>,
|
|
|
|
// mask: <N x i{M}>) -> ()
|
|
|
|
// * N: number of elements in the input vectors
|
|
|
|
// * T: type of the element to load
|
|
|
|
// * M: any integer width is supported, will be truncated to i1
|
|
|
|
|
|
|
|
// All types must be simd vector types
|
2023-09-29 15:14:53 +00:00
|
|
|
// The second argument must be a simd vector with an element type that's a pointer
|
|
|
|
// to the element type of the first argument
|
|
|
|
let (_, element_ty0) = require_simd!(in_ty, SimdFirst);
|
|
|
|
let (element_len1, element_ty1) = require_simd!(arg_tys[1], SimdSecond);
|
|
|
|
let (element_len2, element_ty2) = require_simd!(arg_tys[2], SimdThird);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
// Of the same length:
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
in_len == element_len1,
|
|
|
|
InvalidMonomorphization::SecondArgumentLength {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_len,
|
|
|
|
in_ty,
|
|
|
|
arg_ty: arg_tys[1],
|
|
|
|
out_len: element_len1
|
2018-05-04 18:07:35 +00:00
|
|
|
}
|
|
|
|
);
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
in_len == element_len2,
|
|
|
|
InvalidMonomorphization::ThirdArgumentLength {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_len,
|
|
|
|
in_ty,
|
|
|
|
arg_ty: arg_tys[2],
|
|
|
|
out_len: element_len2
|
2018-05-04 18:07:35 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2023-07-29 20:56:27 +00:00
|
|
|
require!(
|
|
|
|
matches!(
|
2024-03-21 21:33:10 +00:00
|
|
|
*element_ty1.kind(),
|
|
|
|
ty::RawPtr(p_ty, p_mutbl)
|
|
|
|
if p_ty == in_elem && p_mutbl.is_mut() && p_ty.kind() == element_ty0.kind()
|
2023-07-29 20:56:27 +00:00
|
|
|
),
|
|
|
|
InvalidMonomorphization::ExpectedElementType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
expected_element: element_ty1,
|
|
|
|
second_arg: arg_tys[1],
|
|
|
|
in_elem,
|
|
|
|
in_ty,
|
|
|
|
mutability: ExpectedPointerMutability::Mut,
|
2018-05-04 18:07:35 +00:00
|
|
|
}
|
2023-07-29 20:56:27 +00:00
|
|
|
);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
// The element type of the third argument must be a signed integer type of any width:
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
let mask_elem_bitwidth = require_int_ty!(
|
|
|
|
element_ty2.kind(),
|
|
|
|
InvalidMonomorphization::ThirdArgElementType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
expected_element: element_ty2,
|
|
|
|
third_arg: arg_tys[2]
|
|
|
|
}
|
|
|
|
);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
// Alignment of T, must be a constant integer value:
|
2018-11-27 18:00:25 +00:00
|
|
|
let alignment_ty = bx.type_i32();
|
|
|
|
let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
// Truncate the mask vector to a vector of i1s:
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len);
|
|
|
|
let mask_ty = bx.type_vector(bx.type_i1(), in_len);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
2018-11-27 18:00:25 +00:00
|
|
|
let ret_t = bx.type_void();
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
// Type of the vector of pointers:
|
2023-07-29 20:56:27 +00:00
|
|
|
let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len);
|
|
|
|
let llvm_pointer_vec_str = llvm_vector_str(bx, element_ty1, in_len);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
// Type of the vector of elements:
|
2023-07-29 20:56:27 +00:00
|
|
|
let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len);
|
|
|
|
let llvm_elem_vec_str = llvm_vector_str(bx, element_ty0, in_len);
|
2018-05-04 18:07:35 +00:00
|
|
|
|
|
|
|
let llvm_intrinsic =
|
2023-07-25 21:04:01 +00:00
|
|
|
format!("llvm.masked.scatter.{llvm_elem_vec_str}.{llvm_pointer_vec_str}");
|
2021-08-03 22:09:57 +00:00
|
|
|
let fn_ty =
|
|
|
|
bx.type_func(&[llvm_elem_vec_ty, llvm_pointer_vec_ty, alignment_ty, mask_ty], ret_t);
|
|
|
|
let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
|
2022-10-01 17:01:31 +00:00
|
|
|
let v = bx.call(
|
|
|
|
fn_ty,
|
|
|
|
None,
|
2022-12-13 06:42:44 +00:00
|
|
|
None,
|
2022-10-01 17:01:31 +00:00
|
|
|
f,
|
|
|
|
&[args[0].immediate(), args[1].immediate(), alignment, mask],
|
|
|
|
None,
|
2024-03-15 19:45:46 +00:00
|
|
|
None,
|
2022-10-01 17:01:31 +00:00
|
|
|
);
|
2018-05-04 18:07:35 +00:00
|
|
|
return Ok(v);
|
|
|
|
}
|
|
|
|
|
2018-03-14 16:22:40 +00:00
|
|
|
macro_rules! arith_red {
|
2020-07-08 01:04:10 +00:00
|
|
|
($name:ident : $integer_reduce:ident, $float_reduce:ident, $ordered:expr, $op:ident,
|
|
|
|
$identity:expr) => {
|
|
|
|
if name == sym::$name {
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
ret_ty == in_elem,
|
|
|
|
InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
|
2018-03-14 16:22:40 +00:00
|
|
|
);
|
2020-08-02 22:49:11 +00:00
|
|
|
return match in_elem.kind() {
|
2018-08-22 00:35:55 +00:00
|
|
|
ty::Int(_) | ty::Uint(_) => {
|
2018-03-14 16:22:40 +00:00
|
|
|
let r = bx.$integer_reduce(args[0].immediate());
|
|
|
|
if $ordered {
|
|
|
|
// if overflow occurs, the result is the
|
|
|
|
// mathematical result modulo 2^n:
|
2020-07-08 01:04:10 +00:00
|
|
|
Ok(bx.$op(args[1].immediate(), r))
|
2018-03-14 16:22:40 +00:00
|
|
|
} else {
|
|
|
|
Ok(bx.$integer_reduce(args[0].immediate()))
|
|
|
|
}
|
|
|
|
}
|
2018-08-22 00:35:55 +00:00
|
|
|
ty::Float(f) => {
|
2018-03-14 16:22:40 +00:00
|
|
|
let acc = if $ordered {
|
2019-07-20 11:05:37 +00:00
|
|
|
// ordered arithmetic reductions take an accumulator
|
|
|
|
args[1].immediate()
|
2018-03-14 16:22:40 +00:00
|
|
|
} else {
|
2019-07-20 11:05:37 +00:00
|
|
|
// unordered arithmetic reductions use the identity accumulator
|
2018-03-14 16:22:40 +00:00
|
|
|
match f.bit_width() {
|
2020-07-08 01:04:10 +00:00
|
|
|
32 => bx.const_real(bx.type_f32(), $identity),
|
|
|
|
64 => bx.const_real(bx.type_f64(), $identity),
|
2018-03-14 19:14:47 +00:00
|
|
|
v => return_error!(
|
2022-11-27 01:28:01 +00:00
|
|
|
InvalidMonomorphization::UnsupportedSymbolOfSize {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
symbol: sym::$name,
|
|
|
|
in_ty,
|
|
|
|
in_elem,
|
|
|
|
size: v,
|
|
|
|
ret_ty
|
|
|
|
}
|
2018-03-14 16:22:40 +00:00
|
|
|
),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(bx.$float_reduce(acc, args[0].immediate()))
|
2018-03-13 15:46:55 +00:00
|
|
|
}
|
2022-11-27 01:28:01 +00:00
|
|
|
_ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
symbol: sym::$name,
|
2018-03-14 16:22:40 +00:00
|
|
|
in_ty,
|
|
|
|
in_elem,
|
|
|
|
ret_ty
|
2022-11-27 01:28:01 +00:00
|
|
|
}),
|
2018-03-14 16:22:40 +00:00
|
|
|
};
|
2018-03-13 15:46:55 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-09-13 20:36:32 +00:00
|
|
|
arith_red!(simd_reduce_add_ordered: vector_reduce_add, vector_reduce_fadd, true, add, -0.0);
|
2020-07-08 01:04:10 +00:00
|
|
|
arith_red!(simd_reduce_mul_ordered: vector_reduce_mul, vector_reduce_fmul, true, mul, 1.0);
|
|
|
|
arith_red!(
|
|
|
|
simd_reduce_add_unordered: vector_reduce_add,
|
2024-02-18 22:10:34 +00:00
|
|
|
vector_reduce_fadd_reassoc,
|
2020-07-08 01:04:10 +00:00
|
|
|
false,
|
|
|
|
add,
|
2024-09-13 20:36:32 +00:00
|
|
|
-0.0
|
2020-07-08 01:04:10 +00:00
|
|
|
);
|
|
|
|
arith_red!(
|
|
|
|
simd_reduce_mul_unordered: vector_reduce_mul,
|
2024-02-18 22:10:34 +00:00
|
|
|
vector_reduce_fmul_reassoc,
|
2020-07-08 01:04:10 +00:00
|
|
|
false,
|
|
|
|
mul,
|
|
|
|
1.0
|
|
|
|
);
|
2018-03-14 16:22:40 +00:00
|
|
|
|
|
|
|
macro_rules! minmax_red {
|
2020-07-08 01:04:10 +00:00
|
|
|
($name:ident: $int_red:ident, $float_red:ident) => {
|
|
|
|
if name == sym::$name {
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
ret_ty == in_elem,
|
|
|
|
InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
|
2018-03-14 16:22:40 +00:00
|
|
|
);
|
2020-08-02 22:49:11 +00:00
|
|
|
return match in_elem.kind() {
|
2018-03-14 16:22:40 +00:00
|
|
|
ty::Int(_i) => Ok(bx.$int_red(args[0].immediate(), true)),
|
|
|
|
ty::Uint(_u) => Ok(bx.$int_red(args[0].immediate(), false)),
|
|
|
|
ty::Float(_f) => Ok(bx.$float_red(args[0].immediate())),
|
2022-11-27 01:28:01 +00:00
|
|
|
_ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
symbol: sym::$name,
|
2018-03-14 16:22:40 +00:00
|
|
|
in_ty,
|
|
|
|
in_elem,
|
|
|
|
ret_ty
|
2022-11-27 01:28:01 +00:00
|
|
|
}),
|
2018-03-14 16:22:40 +00:00
|
|
|
};
|
2018-03-13 15:46:55 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-07-08 01:04:10 +00:00
|
|
|
minmax_red!(simd_reduce_min: vector_reduce_min, vector_reduce_fmin);
|
|
|
|
minmax_red!(simd_reduce_max: vector_reduce_max, vector_reduce_fmax);
|
2018-03-13 15:46:55 +00:00
|
|
|
|
2018-03-14 16:22:40 +00:00
|
|
|
macro_rules! bitwise_red {
|
2020-07-08 01:04:10 +00:00
|
|
|
($name:ident : $red:ident, $boolean:expr) => {
|
|
|
|
if name == sym::$name {
|
2018-03-14 16:22:40 +00:00
|
|
|
let input = if !$boolean {
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
ret_ty == in_elem,
|
|
|
|
InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
|
2018-03-14 16:22:40 +00:00
|
|
|
);
|
|
|
|
args[0].immediate()
|
|
|
|
} else {
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
let bitwidth = match in_elem.kind() {
|
|
|
|
ty::Int(i) => {
|
|
|
|
i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits())
|
|
|
|
}
|
|
|
|
ty::Uint(i) => {
|
|
|
|
i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits())
|
|
|
|
}
|
2022-11-27 01:28:01 +00:00
|
|
|
_ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
symbol: sym::$name,
|
2018-03-15 15:36:02 +00:00
|
|
|
in_ty,
|
|
|
|
in_elem,
|
|
|
|
ret_ty
|
2022-11-27 01:28:01 +00:00
|
|
|
}),
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
};
|
2018-03-15 15:36:02 +00:00
|
|
|
|
Consistently use the most significant bit of vector masks
This improves the codegen for vector `select`, `gather`, `scatter` and
boolean reduction intrinsics and fixes rust-lang/portable-simd#316.
The current behavior of most mask operations during llvm codegen is to
truncate the mask vector to <N x i1>, telling llvm to use the least
significat bit. The exception is the `simd_bitmask` intrinsics, which
already used the most signifiant bit.
Since sse/avx instructions are defined to use the most significant bit,
truncating means that llvm has to insert a left shift to move the bit
into the most significant position, before the mask can actually be
used.
Similarly on aarch64, mask operations like blend work bit by bit,
repeating the least significant bit across the whole lane involves
shifting it into the sign position and then comparing against zero.
By shifting before truncating to <N x i1>, we tell llvm that we only
consider the most significant bit, removing the need for additional
shift instructions in the assembly.
2023-01-04 22:55:40 +00:00
|
|
|
vector_mask_to_bitmask(bx, args[0].immediate(), bitwidth, in_len as _)
|
2018-03-14 16:22:40 +00:00
|
|
|
};
|
2020-08-02 22:49:11 +00:00
|
|
|
return match in_elem.kind() {
|
2018-08-22 00:35:55 +00:00
|
|
|
ty::Int(_) | ty::Uint(_) => {
|
2018-03-14 16:22:40 +00:00
|
|
|
let r = bx.$red(input);
|
2018-11-27 18:00:25 +00:00
|
|
|
Ok(if !$boolean { r } else { bx.zext(r, bx.type_bool()) })
|
2018-03-14 16:22:40 +00:00
|
|
|
}
|
2022-11-27 01:28:01 +00:00
|
|
|
_ => return_error!(InvalidMonomorphization::UnsupportedSymbol {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
symbol: sym::$name,
|
2018-03-14 16:22:40 +00:00
|
|
|
in_ty,
|
|
|
|
in_elem,
|
|
|
|
ret_ty
|
2022-11-27 01:28:01 +00:00
|
|
|
}),
|
2018-03-14 16:22:40 +00:00
|
|
|
};
|
|
|
|
}
|
2018-03-13 15:46:55 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-07-08 01:04:10 +00:00
|
|
|
bitwise_red!(simd_reduce_and: vector_reduce_and, false);
|
|
|
|
bitwise_red!(simd_reduce_or: vector_reduce_or, false);
|
|
|
|
bitwise_red!(simd_reduce_xor: vector_reduce_xor, false);
|
|
|
|
bitwise_red!(simd_reduce_all: vector_reduce_and, true);
|
|
|
|
bitwise_red!(simd_reduce_any: vector_reduce_or, true);
|
2018-03-13 15:46:55 +00:00
|
|
|
|
2022-07-22 01:48:30 +00:00
|
|
|
if name == sym::simd_cast_ptr {
|
2023-09-29 15:14:53 +00:00
|
|
|
let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn);
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
in_len == out_len,
|
|
|
|
InvalidMonomorphization::ReturnLengthInputType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_len,
|
|
|
|
in_ty,
|
|
|
|
ret_ty,
|
|
|
|
out_len
|
2022-07-22 01:48:30 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
match in_elem.kind() {
|
2024-03-21 21:33:10 +00:00
|
|
|
ty::RawPtr(p_ty, _) => {
|
|
|
|
let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| {
|
2024-11-15 12:53:31 +00:00
|
|
|
bx.tcx.normalize_erasing_regions(bx.typing_env(), ty)
|
2022-08-27 13:35:18 +00:00
|
|
|
});
|
2024-10-03 13:05:23 +00:00
|
|
|
require!(
|
|
|
|
metadata.is_unit(),
|
|
|
|
InvalidMonomorphization::CastWidePointer { span, name, ty: in_elem }
|
2022-11-27 01:28:01 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem })
|
2022-08-27 13:35:18 +00:00
|
|
|
}
|
2022-07-22 01:48:30 +00:00
|
|
|
}
|
|
|
|
match out_elem.kind() {
|
2024-03-21 21:33:10 +00:00
|
|
|
ty::RawPtr(p_ty, _) => {
|
|
|
|
let metadata = p_ty.ptr_metadata_ty(bx.tcx, |ty| {
|
2024-11-15 12:53:31 +00:00
|
|
|
bx.tcx.normalize_erasing_regions(bx.typing_env(), ty)
|
2022-08-27 13:35:18 +00:00
|
|
|
});
|
2024-10-03 13:05:23 +00:00
|
|
|
require!(
|
|
|
|
metadata.is_unit(),
|
|
|
|
InvalidMonomorphization::CastWidePointer { span, name, ty: out_elem }
|
2022-11-27 01:28:01 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem })
|
2022-08-27 13:35:18 +00:00
|
|
|
}
|
2022-07-22 01:48:30 +00:00
|
|
|
}
|
|
|
|
|
2022-12-06 05:07:28 +00:00
|
|
|
return Ok(args[0].immediate());
|
2022-07-22 01:48:30 +00:00
|
|
|
}
|
|
|
|
|
2024-04-03 13:17:00 +00:00
|
|
|
if name == sym::simd_expose_provenance {
|
2023-09-29 15:14:53 +00:00
|
|
|
let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn);
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
in_len == out_len,
|
|
|
|
InvalidMonomorphization::ReturnLengthInputType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_len,
|
|
|
|
in_ty,
|
|
|
|
ret_ty,
|
|
|
|
out_len
|
2022-07-22 01:48:30 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
match in_elem.kind() {
|
2024-03-21 21:11:06 +00:00
|
|
|
ty::RawPtr(_, _) => {}
|
2022-11-27 01:28:01 +00:00
|
|
|
_ => {
|
|
|
|
return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem })
|
|
|
|
}
|
2022-07-22 01:48:30 +00:00
|
|
|
}
|
|
|
|
match out_elem.kind() {
|
|
|
|
ty::Uint(ty::UintTy::Usize) => {}
|
2022-11-27 01:28:01 +00:00
|
|
|
_ => return_error!(InvalidMonomorphization::ExpectedUsize { span, name, ty: out_elem }),
|
2022-07-22 01:48:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Ok(bx.ptrtoint(args[0].immediate(), llret_ty));
|
|
|
|
}
|
|
|
|
|
2024-03-23 22:00:53 +00:00
|
|
|
if name == sym::simd_with_exposed_provenance {
|
2023-09-29 15:14:53 +00:00
|
|
|
let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn);
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
in_len == out_len,
|
|
|
|
InvalidMonomorphization::ReturnLengthInputType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_len,
|
|
|
|
in_ty,
|
|
|
|
ret_ty,
|
|
|
|
out_len
|
2022-07-22 01:48:30 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
match in_elem.kind() {
|
|
|
|
ty::Uint(ty::UintTy::Usize) => {}
|
2022-11-27 01:28:01 +00:00
|
|
|
_ => return_error!(InvalidMonomorphization::ExpectedUsize { span, name, ty: in_elem }),
|
2022-07-22 01:48:30 +00:00
|
|
|
}
|
|
|
|
match out_elem.kind() {
|
2024-03-21 21:11:06 +00:00
|
|
|
ty::RawPtr(_, _) => {}
|
2022-11-27 01:28:01 +00:00
|
|
|
_ => {
|
|
|
|
return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem })
|
|
|
|
}
|
2022-07-22 01:48:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return Ok(bx.inttoptr(args[0].immediate(), llret_ty));
|
|
|
|
}
|
|
|
|
|
2021-12-30 01:18:44 +00:00
|
|
|
if name == sym::simd_cast || name == sym::simd_as {
|
2023-09-29 15:14:53 +00:00
|
|
|
let (out_len, out_elem) = require_simd!(ret_ty, SimdReturn);
|
2022-11-27 01:28:01 +00:00
|
|
|
require!(
|
|
|
|
in_len == out_len,
|
|
|
|
InvalidMonomorphization::ReturnLengthInputType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_len,
|
|
|
|
in_ty,
|
|
|
|
ret_ty,
|
|
|
|
out_len
|
2015-08-14 22:20:22 +00:00
|
|
|
}
|
|
|
|
);
|
2015-07-29 23:40:22 +00:00
|
|
|
// casting cares about nominal type, not just structural type
|
2017-09-19 23:32:22 +00:00
|
|
|
if in_elem == out_elem {
|
|
|
|
return Ok(args[0].immediate());
|
|
|
|
}
|
2015-07-29 23:40:22 +00:00
|
|
|
|
2024-02-14 00:01:08 +00:00
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
enum Sign {
|
|
|
|
Unsigned,
|
|
|
|
Signed,
|
|
|
|
}
|
|
|
|
use Sign::*;
|
|
|
|
|
2015-08-14 22:46:51 +00:00
|
|
|
enum Style {
|
|
|
|
Float,
|
2024-02-14 00:01:08 +00:00
|
|
|
Int(Sign),
|
2015-08-14 22:46:51 +00:00
|
|
|
Unsupported,
|
|
|
|
}
|
|
|
|
|
2020-08-02 22:49:11 +00:00
|
|
|
let (in_style, in_width) = match in_elem.kind() {
|
2015-08-14 22:46:51 +00:00
|
|
|
// vectors of pointer-sized integers should've been
|
|
|
|
// disallowed before here, so this unwrap is safe.
|
2021-12-28 04:06:01 +00:00
|
|
|
ty::Int(i) => (
|
2024-02-14 00:01:08 +00:00
|
|
|
Style::Int(Signed),
|
2021-12-28 04:06:01 +00:00
|
|
|
i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(),
|
|
|
|
),
|
|
|
|
ty::Uint(u) => (
|
2024-02-14 00:01:08 +00:00
|
|
|
Style::Int(Unsigned),
|
2021-12-28 04:06:01 +00:00
|
|
|
u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(),
|
|
|
|
),
|
2018-08-22 00:35:55 +00:00
|
|
|
ty::Float(f) => (Style::Float, f.bit_width()),
|
2015-08-14 22:46:51 +00:00
|
|
|
_ => (Style::Unsupported, 0),
|
|
|
|
};
|
2020-08-02 22:49:11 +00:00
|
|
|
let (out_style, out_width) = match out_elem.kind() {
|
2021-12-28 04:06:01 +00:00
|
|
|
ty::Int(i) => (
|
2024-02-14 00:01:08 +00:00
|
|
|
Style::Int(Signed),
|
2021-12-28 04:06:01 +00:00
|
|
|
i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(),
|
|
|
|
),
|
|
|
|
ty::Uint(u) => (
|
2024-02-14 00:01:08 +00:00
|
|
|
Style::Int(Unsigned),
|
2021-12-28 04:06:01 +00:00
|
|
|
u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(),
|
|
|
|
),
|
2018-08-22 00:35:55 +00:00
|
|
|
ty::Float(f) => (Style::Float, f.bit_width()),
|
2015-08-14 22:46:51 +00:00
|
|
|
_ => (Style::Unsupported, 0),
|
|
|
|
};
|
|
|
|
|
|
|
|
match (in_style, out_style) {
|
2024-02-14 00:01:08 +00:00
|
|
|
(Style::Int(sign), Style::Int(_)) => {
|
2017-06-01 18:50:53 +00:00
|
|
|
return Ok(match in_width.cmp(&out_width) {
|
2018-01-05 05:12:32 +00:00
|
|
|
Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty),
|
2017-09-19 23:32:22 +00:00
|
|
|
Ordering::Equal => args[0].immediate(),
|
2024-02-14 00:01:08 +00:00
|
|
|
Ordering::Less => match sign {
|
|
|
|
Sign::Signed => bx.sext(args[0].immediate(), llret_ty),
|
|
|
|
Sign::Unsigned => bx.zext(args[0].immediate(), llret_ty),
|
|
|
|
},
|
2017-06-01 18:50:53 +00:00
|
|
|
});
|
2015-08-10 23:34:25 +00:00
|
|
|
}
|
2024-02-14 00:01:08 +00:00
|
|
|
(Style::Int(Sign::Signed), Style::Float) => {
|
|
|
|
return Ok(bx.sitofp(args[0].immediate(), llret_ty));
|
2015-08-10 23:34:25 +00:00
|
|
|
}
|
2024-02-14 00:01:08 +00:00
|
|
|
(Style::Int(Sign::Unsigned), Style::Float) => {
|
|
|
|
return Ok(bx.uitofp(args[0].immediate(), llret_ty));
|
|
|
|
}
|
|
|
|
(Style::Float, Style::Int(sign)) => {
|
|
|
|
return Ok(match (sign, name == sym::simd_as) {
|
|
|
|
(Sign::Unsigned, false) => bx.fptoui(args[0].immediate(), llret_ty),
|
|
|
|
(Sign::Signed, false) => bx.fptosi(args[0].immediate(), llret_ty),
|
|
|
|
(_, true) => bx.cast_float_to_int(
|
|
|
|
matches!(sign, Sign::Signed),
|
|
|
|
args[0].immediate(),
|
|
|
|
llret_ty,
|
|
|
|
),
|
2017-06-01 18:50:53 +00:00
|
|
|
});
|
2015-07-29 23:40:22 +00:00
|
|
|
}
|
2015-08-14 22:46:51 +00:00
|
|
|
(Style::Float, Style::Float) => {
|
2017-06-01 18:50:53 +00:00
|
|
|
return Ok(match in_width.cmp(&out_width) {
|
2018-01-05 05:12:32 +00:00
|
|
|
Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty),
|
2017-09-19 23:32:22 +00:00
|
|
|
Ordering::Equal => args[0].immediate(),
|
2018-01-05 05:12:32 +00:00
|
|
|
Ordering::Less => bx.fpext(args[0].immediate(), llret_ty),
|
2017-06-01 18:50:53 +00:00
|
|
|
});
|
2015-07-29 23:40:22 +00:00
|
|
|
}
|
2015-08-14 22:46:51 +00:00
|
|
|
_ => { /* Unsupported. Fallthrough. */ }
|
2015-07-29 23:40:22 +00:00
|
|
|
}
|
2023-09-29 14:39:37 +00:00
|
|
|
return_error!(InvalidMonomorphization::UnsupportedCast {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_ty,
|
|
|
|
in_elem,
|
|
|
|
ret_ty,
|
|
|
|
out_elem
|
|
|
|
});
|
2015-07-29 23:40:22 +00:00
|
|
|
}
|
2021-03-18 19:47:13 +00:00
|
|
|
macro_rules! arith_binary {
|
2016-12-11 15:59:20 +00:00
|
|
|
($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
|
2020-07-08 01:04:10 +00:00
|
|
|
$(if name == sym::$name {
|
2020-08-02 22:49:11 +00:00
|
|
|
match in_elem.kind() {
|
2017-09-19 23:32:22 +00:00
|
|
|
$($(ty::$p(_))|* => {
|
2018-01-05 05:12:32 +00:00
|
|
|
return Ok(bx.$call(args[0].immediate(), args[1].immediate()))
|
2017-09-19 23:32:22 +00:00
|
|
|
})*
|
|
|
|
_ => {},
|
|
|
|
}
|
2023-09-29 14:39:37 +00:00
|
|
|
return_error!(
|
2022-11-27 01:28:01 +00:00
|
|
|
InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem }
|
|
|
|
);
|
2017-09-19 23:32:22 +00:00
|
|
|
})*
|
2015-07-31 18:23:12 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-18 19:47:13 +00:00
|
|
|
arith_binary! {
|
2018-08-22 00:35:55 +00:00
|
|
|
simd_add: Uint, Int => add, Float => fadd;
|
|
|
|
simd_sub: Uint, Int => sub, Float => fsub;
|
|
|
|
simd_mul: Uint, Int => mul, Float => fmul;
|
|
|
|
simd_div: Uint => udiv, Int => sdiv, Float => fdiv;
|
|
|
|
simd_rem: Uint => urem, Int => srem, Float => frem;
|
|
|
|
simd_shl: Uint, Int => shl;
|
|
|
|
simd_shr: Uint => lshr, Int => ashr;
|
|
|
|
simd_and: Uint, Int => and;
|
|
|
|
simd_or: Uint, Int => or;
|
|
|
|
simd_xor: Uint, Int => xor;
|
|
|
|
simd_fmax: Float => maxnum;
|
|
|
|
simd_fmin: Float => minnum;
|
2019-02-08 14:23:48 +00:00
|
|
|
|
|
|
|
}
|
2021-03-18 19:47:13 +00:00
|
|
|
macro_rules! arith_unary {
|
2021-03-18 18:16:21 +00:00
|
|
|
($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
|
|
|
|
$(if name == sym::$name {
|
|
|
|
match in_elem.kind() {
|
|
|
|
$($(ty::$p(_))|* => {
|
|
|
|
return Ok(bx.$call(args[0].immediate()))
|
|
|
|
})*
|
|
|
|
_ => {},
|
|
|
|
}
|
2023-09-29 14:39:37 +00:00
|
|
|
return_error!(
|
2022-11-27 01:28:01 +00:00
|
|
|
InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem }
|
|
|
|
);
|
2021-03-18 18:16:21 +00:00
|
|
|
})*
|
|
|
|
}
|
|
|
|
}
|
2021-03-18 19:47:13 +00:00
|
|
|
arith_unary! {
|
2021-03-18 18:16:21 +00:00
|
|
|
simd_neg: Int => neg, Float => fneg;
|
|
|
|
}
|
2019-02-08 14:23:48 +00:00
|
|
|
|
2023-07-28 03:53:45 +00:00
|
|
|
// Unary integer intrinsics
|
2024-05-19 00:56:49 +00:00
|
|
|
if matches!(
|
|
|
|
name,
|
|
|
|
sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctlz | sym::simd_ctpop | sym::simd_cttz
|
|
|
|
) {
|
2023-07-28 03:04:14 +00:00
|
|
|
let vec_ty = bx.cx.type_vector(
|
|
|
|
match *in_elem.kind() {
|
|
|
|
ty::Int(i) => bx.cx.type_int_from_ty(i),
|
|
|
|
ty::Uint(i) => bx.cx.type_uint_from_ty(i),
|
|
|
|
_ => return_error!(InvalidMonomorphization::UnsupportedOperation {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
in_ty,
|
|
|
|
in_elem
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
in_len as u64,
|
|
|
|
);
|
2023-07-28 03:53:45 +00:00
|
|
|
let intrinsic_name = match name {
|
|
|
|
sym::simd_bswap => "bswap",
|
|
|
|
sym::simd_bitreverse => "bitreverse",
|
|
|
|
sym::simd_ctlz => "ctlz",
|
2024-05-19 00:56:49 +00:00
|
|
|
sym::simd_ctpop => "ctpop",
|
2023-07-28 03:53:45 +00:00
|
|
|
sym::simd_cttz => "cttz",
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
2023-07-30 19:40:32 +00:00
|
|
|
let int_size = in_elem.int_size_and_signed(bx.tcx()).0.bits();
|
|
|
|
let llvm_intrinsic = &format!("llvm.{}.v{}i{}", intrinsic_name, in_len, int_size,);
|
2023-07-28 03:53:45 +00:00
|
|
|
|
2024-05-19 00:56:49 +00:00
|
|
|
return match name {
|
2023-07-30 19:40:32 +00:00
|
|
|
// byte swap is no-op for i8/u8
|
2024-05-19 00:56:49 +00:00
|
|
|
sym::simd_bswap if int_size == 8 => Ok(args[0].immediate()),
|
|
|
|
sym::simd_ctlz | sym::simd_cttz => {
|
2024-05-20 02:04:03 +00:00
|
|
|
// for the (int, i1 immediate) pair, the second arg adds `(0, true) => poison`
|
2024-05-19 00:56:49 +00:00
|
|
|
let fn_ty = bx.type_func(&[vec_ty, bx.type_i1()], vec_ty);
|
2024-05-20 02:04:03 +00:00
|
|
|
let dont_poison_on_zero = bx.const_int(bx.type_i1(), 0);
|
2024-05-19 00:56:49 +00:00
|
|
|
let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
|
|
|
|
Ok(bx.call(
|
|
|
|
fn_ty,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
f,
|
2024-05-20 02:04:03 +00:00
|
|
|
&[args[0].immediate(), dont_poison_on_zero],
|
2024-05-19 00:56:49 +00:00
|
|
|
None,
|
|
|
|
None,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctpop => {
|
|
|
|
// simple unary argument cases
|
|
|
|
let fn_ty = bx.type_func(&[vec_ty], vec_ty);
|
|
|
|
let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
|
|
|
|
Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None, None))
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
2023-07-30 19:40:32 +00:00
|
|
|
};
|
2023-07-28 03:04:14 +00:00
|
|
|
}
|
|
|
|
|
2022-04-12 15:00:55 +00:00
|
|
|
if name == sym::simd_arith_offset {
|
|
|
|
// This also checks that the first operand is a ptr type.
|
|
|
|
let pointee = in_elem.builtin_deref(true).unwrap_or_else(|| {
|
|
|
|
span_bug!(span, "must be called with a vector of pointer types as first argument")
|
|
|
|
});
|
2024-05-10 02:45:14 +00:00
|
|
|
let layout = bx.layout_of(pointee);
|
2022-04-12 15:00:55 +00:00
|
|
|
let ptrs = args[0].immediate();
|
|
|
|
// The second argument must be a ptr-sized integer.
|
|
|
|
// (We don't care about the signedness, this is wrapping anyway.)
|
|
|
|
let (_offsets_len, offsets_elem) = arg_tys[1].simd_size_and_type(bx.tcx());
|
|
|
|
if !matches!(offsets_elem.kind(), ty::Int(ty::IntTy::Isize) | ty::Uint(ty::UintTy::Usize)) {
|
|
|
|
span_bug!(
|
|
|
|
span,
|
|
|
|
"must be called with a vector of pointer-sized integers as second argument"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let offsets = args[1].immediate();
|
|
|
|
|
|
|
|
return Ok(bx.gep(bx.backend_type(layout), ptrs, &[offsets]));
|
|
|
|
}
|
|
|
|
|
2020-07-08 01:04:10 +00:00
|
|
|
if name == sym::simd_saturating_add || name == sym::simd_saturating_sub {
|
2019-02-08 14:23:48 +00:00
|
|
|
let lhs = args[0].immediate();
|
|
|
|
let rhs = args[1].immediate();
|
2020-07-08 01:04:10 +00:00
|
|
|
let is_add = name == sym::simd_saturating_add;
|
2019-02-08 14:23:48 +00:00
|
|
|
let ptr_bits = bx.tcx().data_layout.pointer_size.bits() as _;
|
2020-08-02 22:49:11 +00:00
|
|
|
let (signed, elem_width, elem_ty) = match *in_elem.kind() {
|
2019-02-08 14:23:48 +00:00
|
|
|
ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_int_from_ty(i)),
|
|
|
|
ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_uint_from_ty(i)),
|
|
|
|
_ => {
|
2022-11-27 01:28:01 +00:00
|
|
|
return_error!(InvalidMonomorphization::ExpectedVectorElementType {
|
|
|
|
span,
|
|
|
|
name,
|
|
|
|
expected_element: arg_tys[0].simd_size_and_type(bx.tcx()).1,
|
|
|
|
vector_type: arg_tys[0]
|
|
|
|
});
|
2019-02-08 14:23:48 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
let llvm_intrinsic = &format!(
|
|
|
|
"llvm.{}{}.sat.v{}i{}",
|
|
|
|
if signed { 's' } else { 'u' },
|
|
|
|
if is_add { "add" } else { "sub" },
|
|
|
|
in_len,
|
|
|
|
elem_width
|
|
|
|
);
|
|
|
|
let vec_ty = bx.cx.type_vector(elem_ty, in_len as u64);
|
|
|
|
|
2021-08-03 22:09:57 +00:00
|
|
|
let fn_ty = bx.type_func(&[vec_ty, vec_ty], vec_ty);
|
2021-09-30 17:38:50 +00:00
|
|
|
let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
|
2024-03-15 19:45:46 +00:00
|
|
|
let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None, None);
|
2019-02-08 14:23:48 +00:00
|
|
|
return Ok(v);
|
2015-07-31 18:23:12 +00:00
|
|
|
}
|
2019-02-08 14:23:48 +00:00
|
|
|
|
2016-03-28 23:46:02 +00:00
|
|
|
span_bug!(span, "unknown SIMD intrinsic");
|
2015-07-16 18:59:23 +00:00
|
|
|
}
|