1 //! Codegen `extern "platform-intrinsic"` intrinsics.
6 pub(super) fn codegen_simd_intrinsic_call<'tcx>(
7 fx: &mut FunctionCx<'_, '_, 'tcx>,
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);
19 fx, intrinsic, substs, args,
21 fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic));
25 validate_simd_type!(fx, intrinsic, span, a.layout().ty);
26 simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| {
27 let ret_lane_ty = fx.clif_type(ret_lane_layout.ty).unwrap();
29 let from_signed = type_sign(lane_layout.ty);
30 let to_signed = type_sign(ret_lane_layout.ty);
32 let ret_lane = clif_int_or_float_cast(fx, lane, from_signed, ret_lane_ty, to_signed);
33 CValue::by_val(ret_lane, ret_lane_layout)
38 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
39 simd_cmp!(fx, Equal|Equal(x, y) -> ret);
42 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
43 simd_cmp!(fx, NotEqual|NotEqual(x, y) -> ret);
46 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
47 simd_cmp!(fx, UnsignedLessThan|SignedLessThan|LessThan(x, y) -> ret);
50 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
51 simd_cmp!(fx, UnsignedLessThanOrEqual|SignedLessThanOrEqual|LessThanOrEqual(x, y) -> ret);
54 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
55 simd_cmp!(fx, UnsignedGreaterThan|SignedGreaterThan|GreaterThan(x, y) -> ret);
58 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
61 UnsignedGreaterThanOrEqual|SignedGreaterThanOrEqual|GreaterThanOrEqual
66 // simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U
67 _ if intrinsic.as_str().starts_with("simd_shuffle"), (c x, c y, o idx) {
68 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
70 let n: u16 = intrinsic.as_str()["simd_shuffle".len()..].parse().unwrap();
72 assert_eq!(x.layout(), y.layout());
73 let layout = x.layout();
75 let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
76 let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
78 assert_eq!(lane_ty, ret_lane_ty);
79 assert_eq!(u64::from(n), ret_lane_count);
81 let total_len = lane_count * 2;
84 use rustc_middle::mir::interpret::*;
85 let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const");
87 let idx_bytes = match idx_const {
88 ConstValue::ByRef { alloc, offset } => {
89 let size = Size::from_bytes(4 * ret_lane_count /* size_of([u32; ret_lane_count]) */);
90 alloc.get_bytes(fx, alloc_range(offset, size)).unwrap()
92 _ => unreachable!("{:?}", idx_const),
95 (0..ret_lane_count).map(|i| {
96 let i = usize::try_from(i).unwrap();
97 let idx = rustc_middle::mir::interpret::read_target_uint(
98 fx.tcx.data_layout.endian,
99 &idx_bytes[4*i.. 4*i + 4],
100 ).expect("read_target_uint");
101 u16::try_from(idx).expect("try_from u32")
102 }).collect::<Vec<u16>>()
105 for &idx in &indexes {
106 assert!(u64::from(idx) < total_len, "idx {} out of range 0..{}", idx, total_len);
109 for (out_idx, in_idx) in indexes.into_iter().enumerate() {
110 let in_lane = if u64::from(in_idx) < lane_count {
111 x.value_lane(fx, in_idx.into())
113 y.value_lane(fx, u64::from(in_idx) - lane_count)
115 let out_lane = ret.place_lane(fx, u64::try_from(out_idx).unwrap());
116 out_lane.write_cvalue(fx, in_lane);
120 simd_insert, (c base, o idx, c val) {
122 let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
125 fx.tcx.sess.span_fatal(
127 "Index argument for `simd_insert` is not a constant",
131 let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
132 let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx);
133 if idx >= lane_count.into() {
134 fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count));
137 ret.write_cvalue(fx, base);
138 let ret_lane = ret.place_field(fx, mir::Field::new(idx.try_into().unwrap()));
139 ret_lane.write_cvalue(fx, val);
142 simd_extract, (c v, o idx) {
143 validate_simd_type!(fx, intrinsic, span, v.layout().ty);
144 let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
147 fx.tcx.sess.span_warn(
149 "Index argument for `simd_extract` is not a constant",
151 let res = crate::trap::trap_unimplemented_ret_value(
154 "Index argument for `simd_extract` is not a constant",
156 ret.write_cvalue(fx, res);
160 let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
161 let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx);
162 if idx >= lane_count.into() {
163 fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count));
166 let ret_lane = v.value_lane(fx, idx.try_into().unwrap());
167 ret.write_cvalue(fx, ret_lane);
171 validate_simd_type!(fx, intrinsic, span, a.layout().ty);
172 simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| {
173 let ret_lane = match lane_layout.ty.kind() {
174 ty::Int(_) => fx.bcx.ins().ineg(lane),
175 ty::Float(_) => fx.bcx.ins().fneg(lane),
178 CValue::by_val(ret_lane, ret_lane_layout)
183 validate_simd_type!(fx, intrinsic, span, a.layout().ty);
184 simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| {
185 let ret_lane = fx.bcx.ins().fabs(lane);
186 CValue::by_val(ret_lane, ret_lane_layout)
191 validate_simd_type!(fx, intrinsic, span, a.layout().ty);
192 simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| {
193 let ret_lane = fx.bcx.ins().sqrt(lane);
194 CValue::by_val(ret_lane, ret_lane_layout)
198 simd_add, (c x, c y) {
199 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
200 simd_int_flt_binop!(fx, iadd|fadd(x, y) -> ret);
202 simd_sub, (c x, c y) {
203 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
204 simd_int_flt_binop!(fx, isub|fsub(x, y) -> ret);
206 simd_mul, (c x, c y) {
207 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
208 simd_int_flt_binop!(fx, imul|fmul(x, y) -> ret);
210 simd_div, (c x, c y) {
211 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
212 simd_int_flt_binop!(fx, udiv|sdiv|fdiv(x, y) -> ret);
214 simd_rem, (c x, c y) {
215 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
216 simd_pair_for_each_lane(fx, x, y, ret, |fx, lane_layout, ret_lane_layout, x_lane, y_lane| {
217 let res_lane = match lane_layout.ty.kind() {
218 ty::Uint(_) => fx.bcx.ins().urem(x_lane, y_lane),
219 ty::Int(_) => fx.bcx.ins().srem(x_lane, y_lane),
220 ty::Float(FloatTy::F32) => fx.lib_call(
222 vec![AbiParam::new(types::F32), AbiParam::new(types::F32)],
223 vec![AbiParam::new(types::F32)],
226 ty::Float(FloatTy::F64) => fx.lib_call(
228 vec![AbiParam::new(types::F64), AbiParam::new(types::F64)],
229 vec![AbiParam::new(types::F64)],
232 _ => unreachable!("{:?}", lane_layout.ty),
234 CValue::by_val(res_lane, ret_lane_layout)
237 simd_shl, (c x, c y) {
238 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
239 simd_int_binop!(fx, ishl(x, y) -> ret);
241 simd_shr, (c x, c y) {
242 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
243 simd_int_binop!(fx, ushr|sshr(x, y) -> ret);
245 simd_and, (c x, c y) {
246 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
247 simd_int_binop!(fx, band(x, y) -> ret);
249 simd_or, (c x, c y) {
250 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
251 simd_int_binop!(fx, bor(x, y) -> ret);
253 simd_xor, (c x, c y) {
254 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
255 simd_int_binop!(fx, bxor(x, y) -> ret);
258 simd_fma, (c a, c b, c c) {
259 validate_simd_type!(fx, intrinsic, span, a.layout().ty);
260 assert_eq!(a.layout(), b.layout());
261 assert_eq!(a.layout(), c.layout());
262 let layout = a.layout();
264 let (lane_count, _lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
265 let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
266 assert_eq!(lane_count, ret_lane_count);
267 let ret_lane_layout = fx.layout_of(ret_lane_ty);
269 for lane in 0..lane_count {
270 let a_lane = a.value_lane(fx, lane).load_scalar(fx);
271 let b_lane = b.value_lane(fx, lane).load_scalar(fx);
272 let c_lane = c.value_lane(fx, lane).load_scalar(fx);
274 let mul_lane = fx.bcx.ins().fmul(a_lane, b_lane);
275 let res_lane = CValue::by_val(fx.bcx.ins().fadd(mul_lane, c_lane), ret_lane_layout);
277 ret.place_lane(fx, lane).write_cvalue(fx, res_lane);
281 simd_fmin, (c x, c y) {
282 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
283 simd_flt_binop!(fx, fmin(x, y) -> ret);
285 simd_fmax, (c x, c y) {
286 validate_simd_type!(fx, intrinsic, span, x.layout().ty);
287 simd_flt_binop!(fx, fmax(x, y) -> ret);
291 validate_simd_type!(fx, intrinsic, span, a.layout().ty);
292 simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| {
293 let res_lane = match lane_layout.ty.kind() {
294 ty::Float(FloatTy::F32) => fx.lib_call(
296 vec![AbiParam::new(types::F32)],
297 vec![AbiParam::new(types::F32)],
300 ty::Float(FloatTy::F64) => fx.lib_call(
302 vec![AbiParam::new(types::F64)],
303 vec![AbiParam::new(types::F64)],
306 _ => unreachable!("{:?}", lane_layout.ty),
308 CValue::by_val(res_lane, ret_lane_layout)
312 validate_simd_type!(fx, intrinsic, span, a.layout().ty);
313 simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| {
314 let ret_lane = fx.bcx.ins().ceil(lane);
315 CValue::by_val(ret_lane, ret_lane_layout)
319 validate_simd_type!(fx, intrinsic, span, a.layout().ty);
320 simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| {
321 let ret_lane = fx.bcx.ins().floor(lane);
322 CValue::by_val(ret_lane, ret_lane_layout)
326 validate_simd_type!(fx, intrinsic, span, a.layout().ty);
327 simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| {
328 let ret_lane = fx.bcx.ins().trunc(lane);
329 CValue::by_val(ret_lane, ret_lane_layout)
333 simd_reduce_add_ordered | simd_reduce_add_unordered, (c v, v acc) {
334 validate_simd_type!(fx, intrinsic, span, v.layout().ty);
335 simd_reduce(fx, v, Some(acc), ret, |fx, lane_layout, a, b| {
336 if lane_layout.ty.is_floating_point() {
337 fx.bcx.ins().fadd(a, b)
339 fx.bcx.ins().iadd(a, b)
344 simd_reduce_mul_ordered | simd_reduce_mul_unordered, (c v, v acc) {
345 validate_simd_type!(fx, intrinsic, span, v.layout().ty);
346 simd_reduce(fx, v, Some(acc), ret, |fx, lane_layout, a, b| {
347 if lane_layout.ty.is_floating_point() {
348 fx.bcx.ins().fmul(a, b)
350 fx.bcx.ins().imul(a, b)
355 simd_reduce_all, (c v) {
356 validate_simd_type!(fx, intrinsic, span, v.layout().ty);
357 simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().band(a, b));
360 simd_reduce_any, (c v) {
361 validate_simd_type!(fx, intrinsic, span, v.layout().ty);
362 simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().bor(a, b));
365 simd_reduce_and, (c v) {
366 validate_simd_type!(fx, intrinsic, span, v.layout().ty);
367 simd_reduce(fx, v, None, ret, |fx, _layout, a, b| fx.bcx.ins().band(a, b));
370 simd_reduce_or, (c v) {
371 validate_simd_type!(fx, intrinsic, span, v.layout().ty);
372 simd_reduce(fx, v, None, ret, |fx, _layout, a, b| fx.bcx.ins().bor(a, b));
375 simd_reduce_xor, (c v) {
376 validate_simd_type!(fx, intrinsic, span, v.layout().ty);
377 simd_reduce(fx, v, None, ret, |fx, _layout, a, b| fx.bcx.ins().bxor(a, b));
380 simd_reduce_min, (c v) {
381 // FIXME support floats
382 validate_simd_type!(fx, intrinsic, span, v.layout().ty);
383 simd_reduce(fx, v, None, ret, |fx, layout, a, b| {
384 let lt = fx.bcx.ins().icmp(if layout.ty.is_signed() {
385 IntCC::SignedLessThan
387 IntCC::UnsignedLessThan
389 fx.bcx.ins().select(lt, a, b)
393 simd_reduce_max, (c v) {
394 // FIXME support floats
395 validate_simd_type!(fx, intrinsic, span, v.layout().ty);
396 simd_reduce(fx, v, None, ret, |fx, layout, a, b| {
397 let gt = fx.bcx.ins().icmp(if layout.ty.is_signed() {
398 IntCC::SignedGreaterThan
400 IntCC::UnsignedGreaterThan
402 fx.bcx.ins().select(gt, a, b)
406 simd_select, (c m, c a, c b) {
407 validate_simd_type!(fx, intrinsic, span, m.layout().ty);
408 validate_simd_type!(fx, intrinsic, span, a.layout().ty);
409 assert_eq!(a.layout(), b.layout());
411 let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx);
412 let lane_layout = fx.layout_of(lane_ty);
414 for lane in 0..lane_count {
415 let m_lane = m.value_lane(fx, lane).load_scalar(fx);
416 let a_lane = a.value_lane(fx, lane).load_scalar(fx);
417 let b_lane = b.value_lane(fx, lane).load_scalar(fx);
419 let m_lane = fx.bcx.ins().icmp_imm(IntCC::Equal, m_lane, 0);
420 let res_lane = CValue::by_val(fx.bcx.ins().select(m_lane, b_lane, a_lane), lane_layout);
422 ret.place_lane(fx, lane).write_cvalue(fx, res_lane);