]> git.lizzy.rs Git - rust.git/blob - src/intrinsics/mod.rs
Turn validate_simd_type into a function
[rust.git] / src / intrinsics / mod.rs
1 //! Codegen of intrinsics. This includes `extern "rust-intrinsic"`, `extern "platform-intrinsic"`
2 //! and LLVM intrinsics that have symbol names starting with `llvm.`.
3
4 mod cpuid;
5 mod llvm;
6 mod simd;
7
8 pub(crate) use cpuid::codegen_cpuid_call;
9 pub(crate) use llvm::codegen_llvm_intrinsic_call;
10
11 use rustc_middle::ty::print::with_no_trimmed_paths;
12 use rustc_middle::ty::subst::SubstsRef;
13 use rustc_span::symbol::{kw, sym, Symbol};
14
15 use crate::prelude::*;
16 use cranelift_codegen::ir::AtomicRmwOp;
17
18 macro intrinsic_pat {
19     (_) => {
20         _
21     },
22     ($name:ident) => {
23         sym::$name
24     },
25     (kw.$name:ident) => {
26         kw::$name
27     },
28     ($name:literal) => {
29         $name
30     },
31 }
32
33 macro intrinsic_arg {
34     (o $fx:expr, $arg:ident) => {
35         $arg
36     },
37     (c $fx:expr, $arg:ident) => {
38         codegen_operand($fx, $arg)
39     },
40     (v $fx:expr, $arg:ident) => {
41         codegen_operand($fx, $arg).load_scalar($fx)
42     }
43 }
44
45 macro intrinsic_match {
46     ($fx:expr, $intrinsic:expr, $substs:expr, $args:expr,
47     _ => $unknown:block;
48     $(
49         $($($name:tt).*)|+ $(if $cond:expr)?, ($($a:ident $arg:ident),*) $content:block;
50     )*) => {
51         let _ = $substs; // Silence warning when substs is unused.
52         match $intrinsic {
53             $(
54                 $(intrinsic_pat!($($name).*))|* $(if $cond)? => {
55                     #[allow(unused_parens, non_snake_case)]
56                     {
57                         if let [$($arg),*] = $args {
58                             let ($($arg,)*) = (
59                                 $(intrinsic_arg!($a $fx, $arg),)*
60                             );
61                             #[warn(unused_parens, non_snake_case)]
62                             {
63                                 $content
64                             }
65                         } else {
66                             bug!("wrong number of args for intrinsic {:?}", $intrinsic);
67                         }
68                     }
69                 }
70             )*
71             _ => $unknown,
72         }
73     }
74 }
75
76 macro validate_atomic_type($fx:ident, $intrinsic:ident, $span:ident, $ty:expr) {
77     match $ty.kind() {
78         ty::Uint(_) | ty::Int(_) | ty::RawPtr(..) => {}
79         _ => {
80             $fx.tcx.sess.span_err(
81                 $span,
82                 &format!(
83                     "`{}` intrinsic: expected basic integer or raw pointer type, found `{:?}`",
84                     $intrinsic, $ty
85                 ),
86             );
87             // Prevent verifier error
88             crate::trap::trap_unreachable($fx, "compilation should not have succeeded");
89             return;
90         }
91     }
92 }
93
94 fn validate_simd_type(fx: &mut FunctionCx<'_, '_, '_>, intrinsic: Symbol, span: Span, ty: Ty<'_>) {
95     if !ty.is_simd() {
96         fx.tcx.sess.span_err(span, &format!("invalid monomorphization of `{}` intrinsic: expected SIMD input type, found non-SIMD `{}`", intrinsic, ty));
97         // Prevent verifier error
98         crate::trap::trap_unreachable(fx, "compilation should not have succeeded");
99         return;
100     }
101 }
102
103 pub(crate) fn clif_vector_type<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx>) -> Option<Type> {
104     let (element, count) = match layout.abi {
105         Abi::Vector { element, count } => (element, count),
106         _ => unreachable!(),
107     };
108
109     match scalar_to_clif_type(tcx, element).by(u16::try_from(count).unwrap()) {
110         // Cranelift currently only implements icmp for 128bit vectors.
111         Some(vector_ty) if vector_ty.bits() == 128 => Some(vector_ty),
112         _ => None,
113     }
114 }
115
116 fn simd_for_each_lane<'tcx>(
117     fx: &mut FunctionCx<'_, '_, 'tcx>,
118     val: CValue<'tcx>,
119     ret: CPlace<'tcx>,
120     f: impl Fn(
121         &mut FunctionCx<'_, '_, 'tcx>,
122         TyAndLayout<'tcx>,
123         TyAndLayout<'tcx>,
124         Value,
125     ) -> CValue<'tcx>,
126 ) {
127     let layout = val.layout();
128
129     let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
130     let lane_layout = fx.layout_of(lane_ty);
131     let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
132     let ret_lane_layout = fx.layout_of(ret_lane_ty);
133     assert_eq!(lane_count, ret_lane_count);
134
135     for lane_idx in 0..lane_count {
136         let lane = val.value_lane(fx, lane_idx).load_scalar(fx);
137
138         let res_lane = f(fx, lane_layout, ret_lane_layout, lane);
139
140         ret.place_lane(fx, lane_idx).write_cvalue(fx, res_lane);
141     }
142 }
143
144 fn simd_pair_for_each_lane<'tcx>(
145     fx: &mut FunctionCx<'_, '_, 'tcx>,
146     x: CValue<'tcx>,
147     y: CValue<'tcx>,
148     ret: CPlace<'tcx>,
149     f: impl Fn(
150         &mut FunctionCx<'_, '_, 'tcx>,
151         TyAndLayout<'tcx>,
152         TyAndLayout<'tcx>,
153         Value,
154         Value,
155     ) -> CValue<'tcx>,
156 ) {
157     assert_eq!(x.layout(), y.layout());
158     let layout = x.layout();
159
160     let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
161     let lane_layout = fx.layout_of(lane_ty);
162     let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
163     let ret_lane_layout = fx.layout_of(ret_lane_ty);
164     assert_eq!(lane_count, ret_lane_count);
165
166     for lane_idx in 0..lane_count {
167         let x_lane = x.value_lane(fx, lane_idx).load_scalar(fx);
168         let y_lane = y.value_lane(fx, lane_idx).load_scalar(fx);
169
170         let res_lane = f(fx, lane_layout, ret_lane_layout, x_lane, y_lane);
171
172         ret.place_lane(fx, lane_idx).write_cvalue(fx, res_lane);
173     }
174 }
175
176 fn simd_reduce<'tcx>(
177     fx: &mut FunctionCx<'_, '_, 'tcx>,
178     val: CValue<'tcx>,
179     acc: Option<Value>,
180     ret: CPlace<'tcx>,
181     f: impl Fn(&mut FunctionCx<'_, '_, 'tcx>, TyAndLayout<'tcx>, Value, Value) -> Value,
182 ) {
183     let (lane_count, lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx);
184     let lane_layout = fx.layout_of(lane_ty);
185     assert_eq!(lane_layout, ret.layout());
186
187     let (mut res_val, start_lane) =
188         if let Some(acc) = acc { (acc, 0) } else { (val.value_lane(fx, 0).load_scalar(fx), 1) };
189     for lane_idx in start_lane..lane_count {
190         let lane = val.value_lane(fx, lane_idx).load_scalar(fx);
191         res_val = f(fx, lane_layout, res_val, lane);
192     }
193     let res = CValue::by_val(res_val, lane_layout);
194     ret.write_cvalue(fx, res);
195 }
196
197 // FIXME move all uses to `simd_reduce`
198 fn simd_reduce_bool<'tcx>(
199     fx: &mut FunctionCx<'_, '_, 'tcx>,
200     val: CValue<'tcx>,
201     ret: CPlace<'tcx>,
202     f: impl Fn(&mut FunctionCx<'_, '_, 'tcx>, Value, Value) -> Value,
203 ) {
204     let (lane_count, _lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx);
205     assert!(ret.layout().ty.is_bool());
206
207     let res_val = val.value_lane(fx, 0).load_scalar(fx);
208     let mut res_val = fx.bcx.ins().band_imm(res_val, 1); // mask to boolean
209     for lane_idx in 1..lane_count {
210         let lane = val.value_lane(fx, lane_idx).load_scalar(fx);
211         let lane = fx.bcx.ins().band_imm(lane, 1); // mask to boolean
212         res_val = f(fx, res_val, lane);
213     }
214     let res_val = if fx.bcx.func.dfg.value_type(res_val) != types::I8 {
215         fx.bcx.ins().ireduce(types::I8, res_val)
216     } else {
217         res_val
218     };
219     let res = CValue::by_val(res_val, ret.layout());
220     ret.write_cvalue(fx, res);
221 }
222
223 fn bool_to_zero_or_max_uint<'tcx>(
224     fx: &mut FunctionCx<'_, '_, 'tcx>,
225     layout: TyAndLayout<'tcx>,
226     val: Value,
227 ) -> CValue<'tcx> {
228     let ty = fx.clif_type(layout.ty).unwrap();
229
230     let int_ty = match ty {
231         types::F32 => types::I32,
232         types::F64 => types::I64,
233         ty => ty,
234     };
235
236     let val = fx.bcx.ins().bint(int_ty, val);
237     let mut res = fx.bcx.ins().ineg(val);
238
239     if ty.is_float() {
240         res = fx.bcx.ins().bitcast(ty, res);
241     }
242
243     CValue::by_val(res, layout)
244 }
245
246 macro simd_cmp {
247     ($fx:expr, $cc:ident|$cc_f:ident($x:ident, $y:ident) -> $ret:ident) => {
248         let vector_ty = clif_vector_type($fx.tcx, $x.layout());
249
250         if let Some(vector_ty) = vector_ty {
251             let x = $x.load_scalar($fx);
252             let y = $y.load_scalar($fx);
253             let val = if vector_ty.lane_type().is_float() {
254                 $fx.bcx.ins().fcmp(FloatCC::$cc_f, x, y)
255             } else {
256                 $fx.bcx.ins().icmp(IntCC::$cc, x, y)
257             };
258
259             // HACK This depends on the fact that icmp for vectors represents bools as 0 and !0, not 0 and 1.
260             let val = $fx.bcx.ins().raw_bitcast(vector_ty, val);
261
262             $ret.write_cvalue($fx, CValue::by_val(val, $ret.layout()));
263         } else {
264             simd_pair_for_each_lane(
265                 $fx,
266                 $x,
267                 $y,
268                 $ret,
269                 |fx, lane_layout, res_lane_layout, x_lane, y_lane| {
270                     let res_lane = match lane_layout.ty.kind() {
271                         ty::Uint(_) | ty::Int(_) => fx.bcx.ins().icmp(IntCC::$cc, x_lane, y_lane),
272                         ty::Float(_) => fx.bcx.ins().fcmp(FloatCC::$cc_f, x_lane, y_lane),
273                         _ => unreachable!("{:?}", lane_layout.ty),
274                     };
275                     bool_to_zero_or_max_uint(fx, res_lane_layout, res_lane)
276                 },
277             );
278         }
279     },
280     ($fx:expr, $cc_u:ident|$cc_s:ident|$cc_f:ident($x:ident, $y:ident) -> $ret:ident) => {
281         // FIXME use vector icmp when possible
282         simd_pair_for_each_lane(
283             $fx,
284             $x,
285             $y,
286             $ret,
287             |fx, lane_layout, res_lane_layout, x_lane, y_lane| {
288                 let res_lane = match lane_layout.ty.kind() {
289                     ty::Uint(_) => fx.bcx.ins().icmp(IntCC::$cc_u, x_lane, y_lane),
290                     ty::Int(_) => fx.bcx.ins().icmp(IntCC::$cc_s, x_lane, y_lane),
291                     ty::Float(_) => fx.bcx.ins().fcmp(FloatCC::$cc_f, x_lane, y_lane),
292                     _ => unreachable!("{:?}", lane_layout.ty),
293                 };
294                 bool_to_zero_or_max_uint(fx, res_lane_layout, res_lane)
295             },
296         );
297     },
298 }
299
300 macro simd_int_binop {
301     ($fx:expr, $op:ident($x:ident, $y:ident) -> $ret:ident) => {
302         simd_int_binop!($fx, $op|$op($x, $y) -> $ret);
303     },
304     ($fx:expr, $op_u:ident|$op_s:ident($x:ident, $y:ident) -> $ret:ident) => {
305         simd_pair_for_each_lane(
306             $fx,
307             $x,
308             $y,
309             $ret,
310             |fx, lane_layout, ret_lane_layout, x_lane, y_lane| {
311                 let res_lane = match lane_layout.ty.kind() {
312                     ty::Uint(_) => fx.bcx.ins().$op_u(x_lane, y_lane),
313                     ty::Int(_) => fx.bcx.ins().$op_s(x_lane, y_lane),
314                     _ => unreachable!("{:?}", lane_layout.ty),
315                 };
316                 CValue::by_val(res_lane, ret_lane_layout)
317             },
318         );
319     },
320 }
321
322 macro simd_int_flt_binop {
323     ($fx:expr, $op:ident|$op_f:ident($x:ident, $y:ident) -> $ret:ident) => {
324         simd_int_flt_binop!($fx, $op|$op|$op_f($x, $y) -> $ret);
325     },
326     ($fx:expr, $op_u:ident|$op_s:ident|$op_f:ident($x:ident, $y:ident) -> $ret:ident) => {
327         simd_pair_for_each_lane(
328             $fx,
329             $x,
330             $y,
331             $ret,
332             |fx, lane_layout, ret_lane_layout, x_lane, y_lane| {
333                 let res_lane = match lane_layout.ty.kind() {
334                     ty::Uint(_) => fx.bcx.ins().$op_u(x_lane, y_lane),
335                     ty::Int(_) => fx.bcx.ins().$op_s(x_lane, y_lane),
336                     ty::Float(_) => fx.bcx.ins().$op_f(x_lane, y_lane),
337                     _ => unreachable!("{:?}", lane_layout.ty),
338                 };
339                 CValue::by_val(res_lane, ret_lane_layout)
340             },
341         );
342     },
343 }
344
345 macro simd_flt_binop($fx:expr, $op:ident($x:ident, $y:ident) -> $ret:ident) {
346     simd_pair_for_each_lane(
347         $fx,
348         $x,
349         $y,
350         $ret,
351         |fx, lane_layout, ret_lane_layout, x_lane, y_lane| {
352             let res_lane = match lane_layout.ty.kind() {
353                 ty::Float(_) => fx.bcx.ins().$op(x_lane, y_lane),
354                 _ => unreachable!("{:?}", lane_layout.ty),
355             };
356             CValue::by_val(res_lane, ret_lane_layout)
357         },
358     );
359 }
360
361 pub(crate) fn codegen_intrinsic_call<'tcx>(
362     fx: &mut FunctionCx<'_, '_, 'tcx>,
363     instance: Instance<'tcx>,
364     args: &[mir::Operand<'tcx>],
365     destination: Option<(CPlace<'tcx>, BasicBlock)>,
366     span: Span,
367 ) {
368     let intrinsic = fx.tcx.item_name(instance.def_id());
369     let substs = instance.substs;
370
371     let ret = match destination {
372         Some((place, _)) => place,
373         None => {
374             // Insert non returning intrinsics here
375             match intrinsic {
376                 sym::abort => {
377                     trap_abort(fx, "Called intrinsic::abort.");
378                 }
379                 sym::transmute => {
380                     crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", span);
381                 }
382                 _ => unimplemented!("unsupported instrinsic {}", intrinsic),
383             }
384             return;
385         }
386     };
387
388     if intrinsic.as_str().starts_with("simd_") {
389         self::simd::codegen_simd_intrinsic_call(fx, intrinsic, substs, args, ret, span);
390         let ret_block = fx.get_block(destination.expect("SIMD intrinsics don't diverge").1);
391         fx.bcx.ins().jump(ret_block, &[]);
392     } else if codegen_float_intrinsic_call(fx, intrinsic, args, ret) {
393         let ret_block = fx.get_block(destination.expect("Float intrinsics don't diverge").1);
394         fx.bcx.ins().jump(ret_block, &[]);
395     } else {
396         codegen_regular_intrinsic_call(
397             fx,
398             instance,
399             intrinsic,
400             substs,
401             args,
402             ret,
403             span,
404             destination,
405         );
406     }
407 }
408
409 fn codegen_float_intrinsic_call<'tcx>(
410     fx: &mut FunctionCx<'_, '_, 'tcx>,
411     intrinsic: Symbol,
412     args: &[mir::Operand<'tcx>],
413     ret: CPlace<'tcx>,
414 ) -> bool {
415     let (name, arg_count, ty) = match intrinsic {
416         sym::expf32 => ("expf", 1, fx.tcx.types.f32),
417         sym::expf64 => ("exp", 1, fx.tcx.types.f64),
418         sym::exp2f32 => ("exp2f", 1, fx.tcx.types.f32),
419         sym::exp2f64 => ("exp2", 1, fx.tcx.types.f64),
420         sym::sqrtf32 => ("sqrtf", 1, fx.tcx.types.f32),
421         sym::sqrtf64 => ("sqrt", 1, fx.tcx.types.f64),
422         sym::powif32 => ("__powisf2", 2, fx.tcx.types.f32), // compiler-builtins
423         sym::powif64 => ("__powidf2", 2, fx.tcx.types.f64), // compiler-builtins
424         sym::powf32 => ("powf", 2, fx.tcx.types.f32),
425         sym::powf64 => ("pow", 2, fx.tcx.types.f64),
426         sym::logf32 => ("logf", 1, fx.tcx.types.f32),
427         sym::logf64 => ("log", 1, fx.tcx.types.f64),
428         sym::log2f32 => ("log2f", 1, fx.tcx.types.f32),
429         sym::log2f64 => ("log2", 1, fx.tcx.types.f64),
430         sym::log10f32 => ("log10f", 1, fx.tcx.types.f32),
431         sym::log10f64 => ("log10", 1, fx.tcx.types.f64),
432         sym::fabsf32 => ("fabsf", 1, fx.tcx.types.f32),
433         sym::fabsf64 => ("fabs", 1, fx.tcx.types.f64),
434         sym::fmaf32 => ("fmaf", 3, fx.tcx.types.f32),
435         sym::fmaf64 => ("fma", 3, fx.tcx.types.f64),
436         sym::copysignf32 => ("copysignf", 2, fx.tcx.types.f32),
437         sym::copysignf64 => ("copysign", 2, fx.tcx.types.f64),
438         sym::floorf32 => ("floorf", 1, fx.tcx.types.f32),
439         sym::floorf64 => ("floor", 1, fx.tcx.types.f64),
440         sym::ceilf32 => ("ceilf", 1, fx.tcx.types.f32),
441         sym::ceilf64 => ("ceil", 1, fx.tcx.types.f64),
442         sym::truncf32 => ("truncf", 1, fx.tcx.types.f32),
443         sym::truncf64 => ("trunc", 1, fx.tcx.types.f64),
444         sym::roundf32 => ("roundf", 1, fx.tcx.types.f32),
445         sym::roundf64 => ("round", 1, fx.tcx.types.f64),
446         sym::sinf32 => ("sinf", 1, fx.tcx.types.f32),
447         sym::sinf64 => ("sin", 1, fx.tcx.types.f64),
448         sym::cosf32 => ("cosf", 1, fx.tcx.types.f32),
449         sym::cosf64 => ("cos", 1, fx.tcx.types.f64),
450         _ => return false,
451     };
452
453     if args.len() != arg_count {
454         bug!("wrong number of args for intrinsic {:?}", intrinsic);
455     }
456
457     let (a, b, c);
458     let args = match args {
459         [x] => {
460             a = [codegen_operand(fx, x)];
461             &a as &[_]
462         }
463         [x, y] => {
464             b = [codegen_operand(fx, x), codegen_operand(fx, y)];
465             &b
466         }
467         [x, y, z] => {
468             c = [codegen_operand(fx, x), codegen_operand(fx, y), codegen_operand(fx, z)];
469             &c
470         }
471         _ => unreachable!(),
472     };
473
474     let res = fx.easy_call(name, &args, ty);
475     ret.write_cvalue(fx, res);
476
477     true
478 }
479
480 fn codegen_regular_intrinsic_call<'tcx>(
481     fx: &mut FunctionCx<'_, '_, 'tcx>,
482     instance: Instance<'tcx>,
483     intrinsic: Symbol,
484     substs: SubstsRef<'tcx>,
485     args: &[mir::Operand<'tcx>],
486     ret: CPlace<'tcx>,
487     span: Span,
488     destination: Option<(CPlace<'tcx>, BasicBlock)>,
489 ) {
490     let usize_layout = fx.layout_of(fx.tcx.types.usize);
491
492     intrinsic_match! {
493         fx, intrinsic, substs, args,
494         _ => {
495             fx.tcx.sess.span_fatal(span, &format!("unsupported intrinsic {}", intrinsic));
496         };
497
498         assume, (c _a) {};
499         likely | unlikely, (c a) {
500             ret.write_cvalue(fx, a);
501         };
502         breakpoint, () {
503             fx.bcx.ins().debugtrap();
504         };
505         copy | copy_nonoverlapping, (v src, v dst, v count) {
506             let elem_ty = substs.type_at(0);
507             let elem_size: u64 = fx.layout_of(elem_ty).size.bytes();
508             assert_eq!(args.len(), 3);
509             let byte_amount = if elem_size != 1 {
510                 fx.bcx.ins().imul_imm(count, elem_size as i64)
511             } else {
512                 count
513             };
514
515             if intrinsic == sym::copy_nonoverlapping {
516                 // FIXME emit_small_memcpy
517                 fx.bcx.call_memcpy(fx.target_config, dst, src, byte_amount);
518             } else {
519                 // FIXME emit_small_memmove
520                 fx.bcx.call_memmove(fx.target_config, dst, src, byte_amount);
521             }
522         };
523         // NOTE: the volatile variants have src and dst swapped
524         volatile_copy_memory | volatile_copy_nonoverlapping_memory, (v dst, v src, v count) {
525             let elem_ty = substs.type_at(0);
526             let elem_size: u64 = fx.layout_of(elem_ty).size.bytes();
527             assert_eq!(args.len(), 3);
528             let byte_amount = if elem_size != 1 {
529                 fx.bcx.ins().imul_imm(count, elem_size as i64)
530             } else {
531                 count
532             };
533
534             // FIXME make the copy actually volatile when using emit_small_mem{cpy,move}
535             if intrinsic == sym::volatile_copy_nonoverlapping_memory {
536                 // FIXME emit_small_memcpy
537                 fx.bcx.call_memcpy(fx.target_config, dst, src, byte_amount);
538             } else {
539                 // FIXME emit_small_memmove
540                 fx.bcx.call_memmove(fx.target_config, dst, src, byte_amount);
541             }
542         };
543         size_of_val, (c ptr) {
544             let layout = fx.layout_of(substs.type_at(0));
545             let size = if layout.is_unsized() {
546                 let (_ptr, info) = ptr.load_scalar_pair(fx);
547                 let (size, _align) = crate::unsize::size_and_align_of_dst(fx, layout, info);
548                 size
549             } else {
550                 fx
551                     .bcx
552                     .ins()
553                     .iconst(fx.pointer_type, layout.size.bytes() as i64)
554             };
555             ret.write_cvalue(fx, CValue::by_val(size, usize_layout));
556         };
557         min_align_of_val, (c ptr) {
558             let layout = fx.layout_of(substs.type_at(0));
559             let align = if layout.is_unsized() {
560                 let (_ptr, info) = ptr.load_scalar_pair(fx);
561                 let (_size, align) = crate::unsize::size_and_align_of_dst(fx, layout, info);
562                 align
563             } else {
564                 fx
565                     .bcx
566                     .ins()
567                     .iconst(fx.pointer_type, layout.align.abi.bytes() as i64)
568             };
569             ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
570         };
571
572         unchecked_add | unchecked_sub | unchecked_div | exact_div | unchecked_rem
573         | unchecked_shl | unchecked_shr, (c x, c y) {
574             // FIXME trap on overflow
575             let bin_op = match intrinsic {
576                 sym::unchecked_add => BinOp::Add,
577                 sym::unchecked_sub => BinOp::Sub,
578                 sym::unchecked_div | sym::exact_div => BinOp::Div,
579                 sym::unchecked_rem => BinOp::Rem,
580                 sym::unchecked_shl => BinOp::Shl,
581                 sym::unchecked_shr => BinOp::Shr,
582                 _ => unreachable!(),
583             };
584             let res = crate::num::codegen_int_binop(fx, bin_op, x, y);
585             ret.write_cvalue(fx, res);
586         };
587         add_with_overflow | sub_with_overflow | mul_with_overflow, (c x, c y) {
588             assert_eq!(x.layout().ty, y.layout().ty);
589             let bin_op = match intrinsic {
590                 sym::add_with_overflow => BinOp::Add,
591                 sym::sub_with_overflow => BinOp::Sub,
592                 sym::mul_with_overflow => BinOp::Mul,
593                 _ => unreachable!(),
594             };
595
596             let res = crate::num::codegen_checked_int_binop(
597                 fx,
598                 bin_op,
599                 x,
600                 y,
601             );
602             ret.write_cvalue(fx, res);
603         };
604         saturating_add | saturating_sub, (c lhs, c rhs) {
605             assert_eq!(lhs.layout().ty, rhs.layout().ty);
606             let bin_op = match intrinsic {
607                 sym::saturating_add => BinOp::Add,
608                 sym::saturating_sub => BinOp::Sub,
609                 _ => unreachable!(),
610             };
611
612             let signed = type_sign(lhs.layout().ty);
613
614             let checked_res = crate::num::codegen_checked_int_binop(
615                 fx,
616                 bin_op,
617                 lhs,
618                 rhs,
619             );
620
621             let (val, has_overflow) = checked_res.load_scalar_pair(fx);
622             let clif_ty = fx.clif_type(lhs.layout().ty).unwrap();
623
624             let (min, max) = type_min_max_value(&mut fx.bcx, clif_ty, signed);
625
626             let val = match (intrinsic, signed) {
627                 (sym::saturating_add, false) => fx.bcx.ins().select(has_overflow, max, val),
628                 (sym::saturating_sub, false) => fx.bcx.ins().select(has_overflow, min, val),
629                 (sym::saturating_add, true) => {
630                     let rhs = rhs.load_scalar(fx);
631                     let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0);
632                     let sat_val = fx.bcx.ins().select(rhs_ge_zero, max, min);
633                     fx.bcx.ins().select(has_overflow, sat_val, val)
634                 }
635                 (sym::saturating_sub, true) => {
636                     let rhs = rhs.load_scalar(fx);
637                     let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0);
638                     let sat_val = fx.bcx.ins().select(rhs_ge_zero, min, max);
639                     fx.bcx.ins().select(has_overflow, sat_val, val)
640                 }
641                 _ => unreachable!(),
642             };
643
644             let res = CValue::by_val(val, lhs.layout());
645
646             ret.write_cvalue(fx, res);
647         };
648         rotate_left, (c x, v y) {
649             let layout = x.layout();
650             let x = x.load_scalar(fx);
651             let res = fx.bcx.ins().rotl(x, y);
652             ret.write_cvalue(fx, CValue::by_val(res, layout));
653         };
654         rotate_right, (c x, v y) {
655             let layout = x.layout();
656             let x = x.load_scalar(fx);
657             let res = fx.bcx.ins().rotr(x, y);
658             ret.write_cvalue(fx, CValue::by_val(res, layout));
659         };
660
661         // The only difference between offset and arith_offset is regarding UB. Because Cranelift
662         // doesn't have UB both are codegen'ed the same way
663         offset | arith_offset, (c base, v offset) {
664             let pointee_ty = base.layout().ty.builtin_deref(true).unwrap().ty;
665             let pointee_size = fx.layout_of(pointee_ty).size.bytes();
666             let ptr_diff = if pointee_size != 1 {
667                 fx.bcx.ins().imul_imm(offset, pointee_size as i64)
668             } else {
669                 offset
670             };
671             let base_val = base.load_scalar(fx);
672             let res = fx.bcx.ins().iadd(base_val, ptr_diff);
673             ret.write_cvalue(fx, CValue::by_val(res, base.layout()));
674         };
675
676         transmute, (c from) {
677             ret.write_cvalue_transmute(fx, from);
678         };
679         write_bytes | volatile_set_memory, (c dst, v val, v count) {
680             let pointee_ty = dst.layout().ty.builtin_deref(true).unwrap().ty;
681             let pointee_size = fx.layout_of(pointee_ty).size.bytes();
682             let count = if pointee_size != 1 {
683                 fx.bcx.ins().imul_imm(count, pointee_size as i64)
684             } else {
685                 count
686             };
687             let dst_ptr = dst.load_scalar(fx);
688             // FIXME make the memset actually volatile when switching to emit_small_memset
689             // FIXME use emit_small_memset
690             fx.bcx.call_memset(fx.target_config, dst_ptr, val, count);
691         };
692         ctlz | ctlz_nonzero, (c arg) {
693             let val = arg.load_scalar(fx);
694             // FIXME trap on `ctlz_nonzero` with zero arg.
695             let res = fx.bcx.ins().clz(val);
696             let res = CValue::by_val(res, arg.layout());
697             ret.write_cvalue(fx, res);
698         };
699         cttz | cttz_nonzero, (c arg) {
700             let val = arg.load_scalar(fx);
701             // FIXME trap on `cttz_nonzero` with zero arg.
702             let res = fx.bcx.ins().ctz(val);
703             let res = CValue::by_val(res, arg.layout());
704             ret.write_cvalue(fx, res);
705         };
706         ctpop, (c arg) {
707             let val = arg.load_scalar(fx);
708             let res = fx.bcx.ins().popcnt(val);
709             let res = CValue::by_val(res, arg.layout());
710             ret.write_cvalue(fx, res);
711         };
712         bitreverse, (c arg) {
713             let val = arg.load_scalar(fx);
714             let res = fx.bcx.ins().bitrev(val);
715             let res = CValue::by_val(res, arg.layout());
716             ret.write_cvalue(fx, res);
717         };
718         bswap, (c arg) {
719             // FIXME(CraneStation/cranelift#794) add bswap instruction to cranelift
720             fn swap(bcx: &mut FunctionBuilder<'_>, v: Value) -> Value {
721                 match bcx.func.dfg.value_type(v) {
722                     types::I8 => v,
723
724                     // https://code.woboq.org/gcc/include/bits/byteswap.h.html
725                     types::I16 => {
726                         let tmp1 = bcx.ins().ishl_imm(v, 8);
727                         let n1 = bcx.ins().band_imm(tmp1, 0xFF00);
728
729                         let tmp2 = bcx.ins().ushr_imm(v, 8);
730                         let n2 = bcx.ins().band_imm(tmp2, 0x00FF);
731
732                         bcx.ins().bor(n1, n2)
733                     }
734                     types::I32 => {
735                         let tmp1 = bcx.ins().ishl_imm(v, 24);
736                         let n1 = bcx.ins().band_imm(tmp1, 0xFF00_0000);
737
738                         let tmp2 = bcx.ins().ishl_imm(v, 8);
739                         let n2 = bcx.ins().band_imm(tmp2, 0x00FF_0000);
740
741                         let tmp3 = bcx.ins().ushr_imm(v, 8);
742                         let n3 = bcx.ins().band_imm(tmp3, 0x0000_FF00);
743
744                         let tmp4 = bcx.ins().ushr_imm(v, 24);
745                         let n4 = bcx.ins().band_imm(tmp4, 0x0000_00FF);
746
747                         let or_tmp1 = bcx.ins().bor(n1, n2);
748                         let or_tmp2 = bcx.ins().bor(n3, n4);
749                         bcx.ins().bor(or_tmp1, or_tmp2)
750                     }
751                     types::I64 => {
752                         let tmp1 = bcx.ins().ishl_imm(v, 56);
753                         let n1 = bcx.ins().band_imm(tmp1, 0xFF00_0000_0000_0000u64 as i64);
754
755                         let tmp2 = bcx.ins().ishl_imm(v, 40);
756                         let n2 = bcx.ins().band_imm(tmp2, 0x00FF_0000_0000_0000u64 as i64);
757
758                         let tmp3 = bcx.ins().ishl_imm(v, 24);
759                         let n3 = bcx.ins().band_imm(tmp3, 0x0000_FF00_0000_0000u64 as i64);
760
761                         let tmp4 = bcx.ins().ishl_imm(v, 8);
762                         let n4 = bcx.ins().band_imm(tmp4, 0x0000_00FF_0000_0000u64 as i64);
763
764                         let tmp5 = bcx.ins().ushr_imm(v, 8);
765                         let n5 = bcx.ins().band_imm(tmp5, 0x0000_0000_FF00_0000u64 as i64);
766
767                         let tmp6 = bcx.ins().ushr_imm(v, 24);
768                         let n6 = bcx.ins().band_imm(tmp6, 0x0000_0000_00FF_0000u64 as i64);
769
770                         let tmp7 = bcx.ins().ushr_imm(v, 40);
771                         let n7 = bcx.ins().band_imm(tmp7, 0x0000_0000_0000_FF00u64 as i64);
772
773                         let tmp8 = bcx.ins().ushr_imm(v, 56);
774                         let n8 = bcx.ins().band_imm(tmp8, 0x0000_0000_0000_00FFu64 as i64);
775
776                         let or_tmp1 = bcx.ins().bor(n1, n2);
777                         let or_tmp2 = bcx.ins().bor(n3, n4);
778                         let or_tmp3 = bcx.ins().bor(n5, n6);
779                         let or_tmp4 = bcx.ins().bor(n7, n8);
780
781                         let or_tmp5 = bcx.ins().bor(or_tmp1, or_tmp2);
782                         let or_tmp6 = bcx.ins().bor(or_tmp3, or_tmp4);
783                         bcx.ins().bor(or_tmp5, or_tmp6)
784                     }
785                     types::I128 => {
786                         let (lo, hi) = bcx.ins().isplit(v);
787                         let lo = swap(bcx, lo);
788                         let hi = swap(bcx, hi);
789                         bcx.ins().iconcat(hi, lo)
790                     }
791                     ty => unreachable!("bswap {}", ty),
792                 }
793             }
794             let val = arg.load_scalar(fx);
795             let res = CValue::by_val(swap(&mut fx.bcx, val), arg.layout());
796             ret.write_cvalue(fx, res);
797         };
798         assert_inhabited | assert_zero_valid | assert_uninit_valid, () {
799             let layout = fx.layout_of(substs.type_at(0));
800             if layout.abi.is_uninhabited() {
801                 with_no_trimmed_paths(|| crate::base::codegen_panic(
802                     fx,
803                     &format!("attempted to instantiate uninhabited type `{}`", layout.ty),
804                     span,
805                 ));
806                 return;
807             }
808
809             if intrinsic == sym::assert_zero_valid && !layout.might_permit_raw_init(fx, /*zero:*/ true) {
810                 with_no_trimmed_paths(|| crate::base::codegen_panic(
811                     fx,
812                     &format!("attempted to zero-initialize type `{}`, which is invalid", layout.ty),
813                     span,
814                 ));
815                 return;
816             }
817
818             if intrinsic == sym::assert_uninit_valid && !layout.might_permit_raw_init(fx, /*zero:*/ false) {
819                 with_no_trimmed_paths(|| crate::base::codegen_panic(
820                     fx,
821                     &format!("attempted to leave type `{}` uninitialized, which is invalid", layout.ty),
822                     span,
823                 ));
824                 return;
825             }
826         };
827
828         volatile_load | unaligned_volatile_load, (c ptr) {
829             // Cranelift treats loads as volatile by default
830             // FIXME correctly handle unaligned_volatile_load
831             let inner_layout =
832                 fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty);
833             let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), inner_layout);
834             ret.write_cvalue(fx, val);
835         };
836         volatile_store | unaligned_volatile_store, (v ptr, c val) {
837             // Cranelift treats stores as volatile by default
838             // FIXME correctly handle unaligned_volatile_store
839             let dest = CPlace::for_ptr(Pointer::new(ptr), val.layout());
840             dest.write_cvalue(fx, val);
841         };
842
843         pref_align_of | needs_drop | type_id | type_name | variant_count, () {
844             let const_val =
845                 fx.tcx.const_eval_instance(ParamEnv::reveal_all(), instance, None).unwrap();
846             let val = crate::constant::codegen_const_value(
847                 fx,
848                 const_val,
849                 ret.layout().ty,
850             );
851             ret.write_cvalue(fx, val);
852         };
853
854         ptr_offset_from, (v ptr, v base) {
855             let ty = substs.type_at(0);
856             let isize_layout = fx.layout_of(fx.tcx.types.isize);
857
858             let pointee_size: u64 = fx.layout_of(ty).size.bytes();
859             let diff = fx.bcx.ins().isub(ptr, base);
860             // FIXME this can be an exact division.
861             let val = CValue::by_val(fx.bcx.ins().sdiv_imm(diff, pointee_size as i64), isize_layout);
862             ret.write_cvalue(fx, val);
863         };
864
865         ptr_guaranteed_eq, (c a, c b) {
866             let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b);
867             ret.write_cvalue(fx, val);
868         };
869
870         ptr_guaranteed_ne, (c a, c b) {
871             let val = crate::num::codegen_ptr_binop(fx, BinOp::Ne, a, b);
872             ret.write_cvalue(fx, val);
873         };
874
875         caller_location, () {
876             let caller_location = fx.get_caller_location(span);
877             ret.write_cvalue(fx, caller_location);
878         };
879
880         _ if intrinsic.as_str().starts_with("atomic_fence"), () {
881             fx.bcx.ins().fence();
882         };
883         _ if intrinsic.as_str().starts_with("atomic_singlethreadfence"), () {
884             // FIXME use a compiler fence once Cranelift supports it
885             fx.bcx.ins().fence();
886         };
887         _ if intrinsic.as_str().starts_with("atomic_load"), (v ptr) {
888             let ty = substs.type_at(0);
889             validate_atomic_type!(fx, intrinsic, span, ty);
890             let clif_ty = fx.clif_type(ty).unwrap();
891
892             let val = fx.bcx.ins().atomic_load(clif_ty, MemFlags::trusted(), ptr);
893
894             let val = CValue::by_val(val, fx.layout_of(ty));
895             ret.write_cvalue(fx, val);
896         };
897         _ if intrinsic.as_str().starts_with("atomic_store"), (v ptr, c val) {
898             validate_atomic_type!(fx, intrinsic, span, val.layout().ty);
899
900             let val = val.load_scalar(fx);
901
902             fx.bcx.ins().atomic_store(MemFlags::trusted(), val, ptr);
903         };
904         _ if intrinsic.as_str().starts_with("atomic_xchg"), (v ptr, c new) {
905             let layout = new.layout();
906             validate_atomic_type!(fx, intrinsic, span, layout.ty);
907             let ty = fx.clif_type(layout.ty).unwrap();
908
909             let new = new.load_scalar(fx);
910
911             let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Xchg, ptr, new);
912
913             let old = CValue::by_val(old, layout);
914             ret.write_cvalue(fx, old);
915         };
916         _ if intrinsic.as_str().starts_with("atomic_cxchg"), (v ptr, c test_old, c new) { // both atomic_cxchg_* and atomic_cxchgweak_*
917             let layout = new.layout();
918             validate_atomic_type!(fx, intrinsic, span, layout.ty);
919
920             let test_old = test_old.load_scalar(fx);
921             let new = new.load_scalar(fx);
922
923             let old = fx.bcx.ins().atomic_cas(MemFlags::trusted(), ptr, test_old, new);
924             let is_eq = fx.bcx.ins().icmp(IntCC::Equal, old, test_old);
925
926             let ret_val = CValue::by_val_pair(old, fx.bcx.ins().bint(types::I8, is_eq), ret.layout());
927             ret.write_cvalue(fx, ret_val)
928         };
929
930         _ if intrinsic.as_str().starts_with("atomic_xadd"), (v ptr, c amount) {
931             let layout = amount.layout();
932             validate_atomic_type!(fx, intrinsic, span, layout.ty);
933             let ty = fx.clif_type(layout.ty).unwrap();
934
935             let amount = amount.load_scalar(fx);
936
937             let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Add, ptr, amount);
938
939             let old = CValue::by_val(old, layout);
940             ret.write_cvalue(fx, old);
941         };
942         _ if intrinsic.as_str().starts_with("atomic_xsub"), (v ptr, c amount) {
943             let layout = amount.layout();
944             validate_atomic_type!(fx, intrinsic, span, layout.ty);
945             let ty = fx.clif_type(layout.ty).unwrap();
946
947             let amount = amount.load_scalar(fx);
948
949             let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Sub, ptr, amount);
950
951             let old = CValue::by_val(old, layout);
952             ret.write_cvalue(fx, old);
953         };
954         _ if intrinsic.as_str().starts_with("atomic_and"), (v ptr, c src) {
955             let layout = src.layout();
956             validate_atomic_type!(fx, intrinsic, span, layout.ty);
957             let ty = fx.clif_type(layout.ty).unwrap();
958
959             let src = src.load_scalar(fx);
960
961             let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::And, ptr, src);
962
963             let old = CValue::by_val(old, layout);
964             ret.write_cvalue(fx, old);
965         };
966         _ if intrinsic.as_str().starts_with("atomic_or"), (v ptr, c src) {
967             let layout = src.layout();
968             validate_atomic_type!(fx, intrinsic, span, layout.ty);
969             let ty = fx.clif_type(layout.ty).unwrap();
970
971             let src = src.load_scalar(fx);
972
973             let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Or, ptr, src);
974
975             let old = CValue::by_val(old, layout);
976             ret.write_cvalue(fx, old);
977         };
978         _ if intrinsic.as_str().starts_with("atomic_xor"), (v ptr, c src) {
979             let layout = src.layout();
980             validate_atomic_type!(fx, intrinsic, span, layout.ty);
981             let ty = fx.clif_type(layout.ty).unwrap();
982
983             let src = src.load_scalar(fx);
984
985             let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Xor, ptr, src);
986
987             let old = CValue::by_val(old, layout);
988             ret.write_cvalue(fx, old);
989         };
990         _ if intrinsic.as_str().starts_with("atomic_nand"), (v ptr, c src) {
991             let layout = src.layout();
992             validate_atomic_type!(fx, intrinsic, span, layout.ty);
993             let ty = fx.clif_type(layout.ty).unwrap();
994
995             let src = src.load_scalar(fx);
996
997             let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Nand, ptr, src);
998
999             let old = CValue::by_val(old, layout);
1000             ret.write_cvalue(fx, old);
1001         };
1002         _ if intrinsic.as_str().starts_with("atomic_max"), (v ptr, c src) {
1003             let layout = src.layout();
1004             validate_atomic_type!(fx, intrinsic, span, layout.ty);
1005             let ty = fx.clif_type(layout.ty).unwrap();
1006
1007             let src = src.load_scalar(fx);
1008
1009             let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Smax, ptr, src);
1010
1011             let old = CValue::by_val(old, layout);
1012             ret.write_cvalue(fx, old);
1013         };
1014         _ if intrinsic.as_str().starts_with("atomic_umax"), (v ptr, c src) {
1015             let layout = src.layout();
1016             validate_atomic_type!(fx, intrinsic, span, layout.ty);
1017             let ty = fx.clif_type(layout.ty).unwrap();
1018
1019             let src = src.load_scalar(fx);
1020
1021             let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Umax, ptr, src);
1022
1023             let old = CValue::by_val(old, layout);
1024             ret.write_cvalue(fx, old);
1025         };
1026         _ if intrinsic.as_str().starts_with("atomic_min"), (v ptr, c src) {
1027             let layout = src.layout();
1028             validate_atomic_type!(fx, intrinsic, span, layout.ty);
1029             let ty = fx.clif_type(layout.ty).unwrap();
1030
1031             let src = src.load_scalar(fx);
1032
1033             let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Smin, ptr, src);
1034
1035             let old = CValue::by_val(old, layout);
1036             ret.write_cvalue(fx, old);
1037         };
1038         _ if intrinsic.as_str().starts_with("atomic_umin"), (v ptr, c src) {
1039             let layout = src.layout();
1040             validate_atomic_type!(fx, intrinsic, span, layout.ty);
1041             let ty = fx.clif_type(layout.ty).unwrap();
1042
1043             let src = src.load_scalar(fx);
1044
1045             let old = fx.bcx.ins().atomic_rmw(ty, MemFlags::trusted(), AtomicRmwOp::Umin, ptr, src);
1046
1047             let old = CValue::by_val(old, layout);
1048             ret.write_cvalue(fx, old);
1049         };
1050
1051         // In Rust floating point min and max don't propagate NaN. In Cranelift they do however.
1052         // For this reason it is necessary to use `a.is_nan() ? b : (a >= b ? b : a)` for `minnumf*`
1053         // and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by comparing
1054         // a float against itself. Only in case of NaN is it not equal to itself.
1055         minnumf32, (v a, v b) {
1056             let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
1057             let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b);
1058             let temp = fx.bcx.ins().select(a_ge_b, b, a);
1059             let val = fx.bcx.ins().select(a_is_nan, b, temp);
1060             let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32));
1061             ret.write_cvalue(fx, val);
1062         };
1063         minnumf64, (v a, v b) {
1064             let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
1065             let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b);
1066             let temp = fx.bcx.ins().select(a_ge_b, b, a);
1067             let val = fx.bcx.ins().select(a_is_nan, b, temp);
1068             let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
1069             ret.write_cvalue(fx, val);
1070         };
1071         maxnumf32, (v a, v b) {
1072             let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
1073             let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b);
1074             let temp = fx.bcx.ins().select(a_le_b, b, a);
1075             let val = fx.bcx.ins().select(a_is_nan, b, temp);
1076             let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32));
1077             ret.write_cvalue(fx, val);
1078         };
1079         maxnumf64, (v a, v b) {
1080             let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
1081             let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b);
1082             let temp = fx.bcx.ins().select(a_le_b, b, a);
1083             let val = fx.bcx.ins().select(a_is_nan, b, temp);
1084             let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
1085             ret.write_cvalue(fx, val);
1086         };
1087
1088         kw.Try, (v f, v data, v _catch_fn) {
1089             // FIXME once unwinding is supported, change this to actually catch panics
1090             let f_sig = fx.bcx.func.import_signature(Signature {
1091                 call_conv: fx.target_config.default_call_conv,
1092                 params: vec![AbiParam::new(fx.bcx.func.dfg.value_type(data))],
1093                 returns: vec![],
1094             });
1095
1096             fx.bcx.ins().call_indirect(f_sig, f, &[data]);
1097
1098             let layout = ret.layout();
1099             let ret_val = CValue::const_val(fx, layout, ty::ScalarInt::null(layout.size));
1100             ret.write_cvalue(fx, ret_val);
1101         };
1102
1103         fadd_fast | fsub_fast | fmul_fast | fdiv_fast | frem_fast, (c x, c y) {
1104             let res = crate::num::codegen_float_binop(fx, match intrinsic {
1105                 sym::fadd_fast => BinOp::Add,
1106                 sym::fsub_fast => BinOp::Sub,
1107                 sym::fmul_fast => BinOp::Mul,
1108                 sym::fdiv_fast => BinOp::Div,
1109                 sym::frem_fast => BinOp::Rem,
1110                 _ => unreachable!(),
1111             }, x, y);
1112             ret.write_cvalue(fx, res);
1113         };
1114         float_to_int_unchecked, (v f) {
1115             let res = crate::cast::clif_int_or_float_cast(
1116                 fx,
1117                 f,
1118                 false,
1119                 fx.clif_type(ret.layout().ty).unwrap(),
1120                 type_sign(ret.layout().ty),
1121             );
1122             ret.write_cvalue(fx, CValue::by_val(res, ret.layout()));
1123         };
1124
1125         raw_eq, (v lhs_ref, v rhs_ref) {
1126             let size = fx.layout_of(substs.type_at(0)).layout.size;
1127             // FIXME add and use emit_small_memcmp
1128             let is_eq_value =
1129                 if size == Size::ZERO {
1130                     // No bytes means they're trivially equal
1131                     fx.bcx.ins().iconst(types::I8, 1)
1132                 } else if let Some(clty) = size.bits().try_into().ok().and_then(Type::int) {
1133                     // Can't use `trusted` for these loads; they could be unaligned.
1134                     let mut flags = MemFlags::new();
1135                     flags.set_notrap();
1136                     let lhs_val = fx.bcx.ins().load(clty, flags, lhs_ref, 0);
1137                     let rhs_val = fx.bcx.ins().load(clty, flags, rhs_ref, 0);
1138                     let eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_val, rhs_val);
1139                     fx.bcx.ins().bint(types::I8, eq)
1140                 } else {
1141                     // Just call `memcmp` (like slices do in core) when the
1142                     // size is too large or it's not a power-of-two.
1143                     let signed_bytes = i64::try_from(size.bytes()).unwrap();
1144                     let bytes_val = fx.bcx.ins().iconst(fx.pointer_type, signed_bytes);
1145                     let params = vec![AbiParam::new(fx.pointer_type); 3];
1146                     let returns = vec![AbiParam::new(types::I32)];
1147                     let args = &[lhs_ref, rhs_ref, bytes_val];
1148                     let cmp = fx.lib_call("memcmp", params, returns, args)[0];
1149                     let eq = fx.bcx.ins().icmp_imm(IntCC::Equal, cmp, 0);
1150                     fx.bcx.ins().bint(types::I8, eq)
1151                 };
1152             ret.write_cvalue(fx, CValue::by_val(is_eq_value, ret.layout()));
1153         };
1154
1155         black_box, (c a) {
1156             // FIXME implement black_box semantics
1157             ret.write_cvalue(fx, a);
1158         };
1159     }
1160
1161     if let Some((_, dest)) = destination {
1162         let ret_block = fx.get_block(dest);
1163         fx.bcx.ins().jump(ret_block, &[]);
1164     } else {
1165         trap_unreachable(fx, "[corruption] Diverging intrinsic returned.");
1166     }
1167 }