1 //! Codegen `extern "platform-intrinsic"` intrinsics.
6 pub(super) fn codegen_simd_intrinsic_call<'tcx>(
7 fx: &mut FunctionCx<'_, 'tcx, impl Module>,
8 instance: Instance<'tcx>,
9 args: &[mir::Operand<'tcx>],
13 let def_id = instance.def_id();
14 let substs = instance.substs;
16 let intrinsic = fx.tcx.item_name(def_id).as_str();
17 let intrinsic = &intrinsic[..];
20 fx, intrinsic, substs, args,
22 fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic));
26 validate_simd_type!(fx, intrinsic, span, a.layout().ty);
27 simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| {
28 let ret_lane_ty = fx.clif_type(ret_lane_layout.ty).unwrap();
30 let from_signed = type_sign(lane_layout.ty);
31 let to_signed = type_sign(ret_lane_layout.ty);
33 let ret_lane = clif_int_or_float_cast(fx, lane, from_signed, ret_lane_ty, to_signed);
34 CValue::by_val(ret_lane, ret_lane_layout)
38 // FIXME support float comparisons
40 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
41 simd_cmp!(fx, Equal(x, y) -> ret);
44 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
45 simd_cmp!(fx, NotEqual(x, y) -> ret);
48 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
49 simd_cmp!(fx, UnsignedLessThan|SignedLessThan(x, y) -> ret);
52 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
53 simd_cmp!(fx, UnsignedLessThanOrEqual|SignedLessThanOrEqual(x, y) -> ret);
56 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
57 simd_cmp!(fx, UnsignedGreaterThan|SignedGreaterThan(x, y) -> ret);
60 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
61 simd_cmp!(fx, UnsignedGreaterThanOrEqual|SignedGreaterThanOrEqual(x, y) -> ret);
64 // simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U
65 _ if intrinsic.starts_with("simd_shuffle"), (c x, c y, o idx) {
66 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
68 let n: u16 = intrinsic["simd_shuffle".len()..].parse().unwrap();
70 assert_eq!(x.layout(), y.layout());
71 let layout = x.layout();
73 let (lane_type, lane_count) = lane_type_and_count(fx.tcx, layout);
74 let (ret_lane_type, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout());
76 assert_eq!(lane_type, ret_lane_type);
77 assert_eq!(n, ret_lane_count);
79 let total_len = lane_count * 2;
82 use rustc_middle::mir::interpret::*;
83 let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const");
85 let idx_bytes = match idx_const.val {
86 ty::ConstKind::Value(ConstValue::ByRef { alloc, offset }) => {
87 let ptr = Pointer::new(AllocId(0 /* dummy */), offset);
88 let size = Size::from_bytes(4 * u64::from(ret_lane_count) /* size_of([u32; ret_lane_count]) */);
89 alloc.get_bytes(fx, ptr, size).unwrap()
91 _ => unreachable!("{:?}", idx_const),
94 (0..ret_lane_count).map(|i| {
95 let i = usize::try_from(i).unwrap();
96 let idx = rustc_middle::mir::interpret::read_target_uint(
97 fx.tcx.data_layout.endian,
98 &idx_bytes[4*i.. 4*i + 4],
99 ).expect("read_target_uint");
100 u16::try_from(idx).expect("try_from u32")
101 }).collect::<Vec<u16>>()
104 for &idx in &indexes {
105 assert!(idx < total_len, "idx {} out of range 0..{}", idx, total_len);
108 for (out_idx, in_idx) in indexes.into_iter().enumerate() {
109 let in_lane = if in_idx < lane_count {
110 x.value_field(fx, mir::Field::new(in_idx.try_into().unwrap()))
112 y.value_field(fx, mir::Field::new((in_idx - lane_count).try_into().unwrap()))
114 let out_lane = ret.place_field(fx, mir::Field::new(out_idx));
115 out_lane.write_cvalue(fx, in_lane);
119 simd_insert, (c base, o idx, c val) {
121 let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
124 fx.tcx.sess.span_fatal(
126 "Index argument for `simd_insert` is not a constant",
130 let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
131 let (_lane_type, lane_count) = lane_type_and_count(fx.tcx, base.layout());
132 if idx >= lane_count.into() {
133 fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count));
136 ret.write_cvalue(fx, base);
137 let ret_lane = ret.place_field(fx, mir::Field::new(idx.try_into().unwrap()));
138 ret_lane.write_cvalue(fx, val);
141 simd_extract, (c v, o idx) {
142 validate_simd_type!(fx, intrinsic, span, v.layout().ty);
143 let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
146 fx.tcx.sess.span_fatal(
148 "Index argument for `simd_extract` is not a constant",
152 let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
153 let (_lane_type, lane_count) = lane_type_and_count(fx.tcx, v.layout());
154 if idx >= lane_count.into() {
155 fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count));
158 let ret_lane = v.value_field(fx, mir::Field::new(idx.try_into().unwrap()));
159 ret.write_cvalue(fx, ret_lane);
162 simd_add, (c x, c y) {
163 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
164 simd_int_flt_binop!(fx, iadd|fadd(x, y) -> ret);
166 simd_sub, (c x, c y) {
167 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
168 simd_int_flt_binop!(fx, isub|fsub(x, y) -> ret);
170 simd_mul, (c x, c y) {
171 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
172 simd_int_flt_binop!(fx, imul|fmul(x, y) -> ret);
174 simd_div, (c x, c y) {
175 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
176 simd_int_flt_binop!(fx, udiv|sdiv|fdiv(x, y) -> ret);
178 simd_shl, (c x, c y) {
179 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
180 simd_int_binop!(fx, ishl(x, y) -> ret);
182 simd_shr, (c x, c y) {
183 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
184 simd_int_binop!(fx, ushr|sshr(x, y) -> ret);
186 simd_and, (c x, c y) {
187 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
188 simd_int_binop!(fx, band(x, y) -> ret);
190 simd_or, (c x, c y) {
191 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
192 simd_int_binop!(fx, bor(x, y) -> ret);
194 simd_xor, (c x, c y) {
195 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
196 simd_int_binop!(fx, bxor(x, y) -> ret);
199 simd_fma, (c a, c b, c c) {
200 validate_simd_type!(fx, intrinsic, span, a.layout().ty);
201 assert_eq!(a.layout(), b.layout());
202 assert_eq!(a.layout(), c.layout());
203 let layout = a.layout();
205 let (_lane_layout, lane_count) = lane_type_and_count(fx.tcx, layout);
206 let (ret_lane_layout, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout());
207 assert_eq!(lane_count, ret_lane_count);
209 for lane in 0..lane_count {
210 let lane = mir::Field::new(lane.try_into().unwrap());
211 let a_lane = a.value_field(fx, lane).load_scalar(fx);
212 let b_lane = b.value_field(fx, lane).load_scalar(fx);
213 let c_lane = c.value_field(fx, lane).load_scalar(fx);
215 let mul_lane = fx.bcx.ins().fmul(a_lane, b_lane);
216 let res_lane = CValue::by_val(fx.bcx.ins().fadd(mul_lane, c_lane), ret_lane_layout);
218 ret.place_field(fx, lane).write_cvalue(fx, res_lane);
222 simd_fmin, (c x, c y) {
223 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
224 simd_flt_binop!(fx, fmin(x, y) -> ret);
226 simd_fmax, (c x, c y) {
227 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
228 simd_flt_binop!(fx, fmax(x, y) -> ret);
232 // simd_saturating_add
235 // simd_reduce_add_{,un}ordered