]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_codegen_llvm/src/intrinsic.rs
New upstream version 1.59.0+dfsg1
[rustc.git] / compiler / rustc_codegen_llvm / src / intrinsic.rs
CommitLineData
94222f64 1use crate::abi::{Abi, FnAbi, FnAbiLlvmExt, LlvmType, PassMode};
dfeec247 2use crate::builder::Builder;
9fa01778 3use crate::context::CodegenCx;
dfeec247 4use crate::llvm;
9fa01778
XL
5use crate::type_::Type;
6use crate::type_of::LayoutLlvmExt;
9fa01778 7use crate::va_arg::emit_va_arg;
dfeec247 8use crate::value::Value;
ba9703b0 9
94222f64 10use rustc_ast as ast;
1b1a35ee 11use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh};
ba9703b0 12use rustc_codegen_ssa::common::span_invalid_monomorphization_error;
f035d41b 13use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
1b1a35ee 14use rustc_codegen_ssa::mir::operand::OperandRef;
dfeec247 15use rustc_codegen_ssa::mir::place::PlaceRef;
ba9703b0 16use rustc_codegen_ssa::traits::*;
dfeec247 17use rustc_hir as hir;
c295e0f8 18use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, LayoutOf};
ba9703b0
XL
19use rustc_middle::ty::{self, Ty};
20use rustc_middle::{bug, span_bug};
3dfed10e 21use rustc_span::{sym, symbol::kw, Span, Symbol};
3c0e092e 22use rustc_target::abi::{self, Align, HasDataLayout, Primitive};
c295e0f8 23use rustc_target::spec::{HasTargetSpec, PanicStrategy};
32a655c1 24
e9174d1e 25use std::cmp::Ordering;
ba9703b0 26use std::iter;
e9174d1e 27
a2a8927a
XL
28fn get_simple_intrinsic<'ll>(
29 cx: &CodegenCx<'ll, '_>,
30 name: Symbol,
31) -> Option<(&'ll Type, &'ll Value)> {
54a0048b 32 let llvm_name = match name {
3dfed10e
XL
33 sym::sqrtf32 => "llvm.sqrt.f32",
34 sym::sqrtf64 => "llvm.sqrt.f64",
35 sym::powif32 => "llvm.powi.f32",
36 sym::powif64 => "llvm.powi.f64",
37 sym::sinf32 => "llvm.sin.f32",
38 sym::sinf64 => "llvm.sin.f64",
39 sym::cosf32 => "llvm.cos.f32",
40 sym::cosf64 => "llvm.cos.f64",
41 sym::powf32 => "llvm.pow.f32",
42 sym::powf64 => "llvm.pow.f64",
43 sym::expf32 => "llvm.exp.f32",
44 sym::expf64 => "llvm.exp.f64",
45 sym::exp2f32 => "llvm.exp2.f32",
46 sym::exp2f64 => "llvm.exp2.f64",
47 sym::logf32 => "llvm.log.f32",
48 sym::logf64 => "llvm.log.f64",
49 sym::log10f32 => "llvm.log10.f32",
50 sym::log10f64 => "llvm.log10.f64",
51 sym::log2f32 => "llvm.log2.f32",
52 sym::log2f64 => "llvm.log2.f64",
53 sym::fmaf32 => "llvm.fma.f32",
54 sym::fmaf64 => "llvm.fma.f64",
55 sym::fabsf32 => "llvm.fabs.f32",
56 sym::fabsf64 => "llvm.fabs.f64",
57 sym::minnumf32 => "llvm.minnum.f32",
58 sym::minnumf64 => "llvm.minnum.f64",
59 sym::maxnumf32 => "llvm.maxnum.f32",
60 sym::maxnumf64 => "llvm.maxnum.f64",
61 sym::copysignf32 => "llvm.copysign.f32",
62 sym::copysignf64 => "llvm.copysign.f64",
63 sym::floorf32 => "llvm.floor.f32",
64 sym::floorf64 => "llvm.floor.f64",
65 sym::ceilf32 => "llvm.ceil.f32",
66 sym::ceilf64 => "llvm.ceil.f64",
67 sym::truncf32 => "llvm.trunc.f32",
68 sym::truncf64 => "llvm.trunc.f64",
69 sym::rintf32 => "llvm.rint.f32",
70 sym::rintf64 => "llvm.rint.f64",
71 sym::nearbyintf32 => "llvm.nearbyint.f32",
72 sym::nearbyintf64 => "llvm.nearbyint.f64",
73 sym::roundf32 => "llvm.round.f32",
74 sym::roundf64 => "llvm.round.f64",
dfeec247 75 _ => return None,
1a4d82fc 76 };
c295e0f8 77 Some(cx.get_intrinsic(llvm_name))
1a4d82fc
JJ
78}
79
a2a8927a 80impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> {
a1dfa0c6
XL
81 fn codegen_intrinsic_call(
82 &mut self,
e1599b0c 83 instance: ty::Instance<'tcx>,
60c5eb7d 84 fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
a1dfa0c6
XL
85 args: &[OperandRef<'tcx, &'ll Value>],
86 llresult: &'ll Value,
87 span: Span,
88 ) {
89 let tcx = self.tcx;
3dfed10e 90 let callee_ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
c34b1796 91
1b1a35ee 92 let (def_id, substs) = match *callee_ty.kind() {
a1dfa0c6 93 ty::FnDef(def_id, substs) => (def_id, substs),
dfeec247 94 _ => bug!("expected fn item type, found {}", callee_ty),
a1dfa0c6 95 };
9e0c209e 96
a1dfa0c6 97 let sig = callee_ty.fn_sig(tcx);
fc512014 98 let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), sig);
a1dfa0c6
XL
99 let arg_tys = sig.inputs();
100 let ret_ty = sig.output();
3dfed10e 101 let name = tcx.item_name(def_id);
a1dfa0c6
XL
102
103 let llret_ty = self.layout_of(ret_ty).llvm_type(self);
60c5eb7d 104 let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout);
a1dfa0c6
XL
105
106 let simple = get_simple_intrinsic(self, name);
107 let llval = match name {
94222f64
XL
108 _ if simple.is_some() => {
109 let (simple_ty, simple_fn) = simple.unwrap();
110 self.call(
111 simple_ty,
112 simple_fn,
113 &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(),
114 None,
115 )
d9579d0f 116 }
94222f64
XL
117 sym::likely => {
118 self.call_intrinsic("llvm.expect.i1", &[args[0].immediate(), self.const_bool(true)])
1a4d82fc 119 }
94222f64
XL
120 sym::unlikely => self
121 .call_intrinsic("llvm.expect.i1", &[args[0].immediate(), self.const_bool(false)]),
3dfed10e 122 kw::Try => {
dfeec247
XL
123 try_intrinsic(
124 self,
125 args[0].immediate(),
126 args[1].immediate(),
127 args[2].immediate(),
128 llresult,
129 );
a1dfa0c6 130 return;
54a0048b 131 }
94222f64 132 sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[]),
3dfed10e 133 sym::va_copy => {
94222f64 134 self.call_intrinsic("llvm.va_copy", &[args[0].immediate(), args[1].immediate()])
a1dfa0c6 135 }
3dfed10e 136 sym::va_arg => {
60c5eb7d 137 match fn_abi.ret.layout.abi {
c295e0f8 138 abi::Abi::Scalar(scalar) => {
a1dfa0c6
XL
139 match scalar.value {
140 Primitive::Int(..) => {
141 if self.cx().size_of(ret_ty).bytes() < 4 {
94222f64 142 // `va_arg` should not be called on an integer type
a1dfa0c6 143 // less than 4 bytes in length. If it is, promote
94222f64 144 // the integer to an `i32` and truncate the result
a1dfa0c6 145 // back to the smaller type.
dfeec247 146 let promoted_result = emit_va_arg(self, args[0], tcx.types.i32);
a1dfa0c6
XL
147 self.trunc(promoted_result, llret_ty)
148 } else {
149 emit_va_arg(self, args[0], ret_ty)
150 }
151 }
dfeec247 152 Primitive::F64 | Primitive::Pointer => {
a1dfa0c6
XL
153 emit_va_arg(self, args[0], ret_ty)
154 }
155 // `va_arg` should never be used with the return type f32.
dfeec247 156 Primitive::F32 => bug!("the va_arg intrinsic does not work with `f32`"),
92a42be0 157 }
a1dfa0c6 158 }
dfeec247 159 _ => bug!("the va_arg intrinsic does not work with non-scalar types"),
54a0048b
SL
160 }
161 }
32a655c1 162
3dfed10e 163 sym::volatile_load | sym::unaligned_volatile_load => {
a1dfa0c6 164 let tp_ty = substs.type_at(0);
136023e0
XL
165 let ptr = args[0].immediate();
166 let load = if let PassMode::Cast(ty) = fn_abi.ret.mode {
167 let llty = ty.llvm_type(self);
168 let ptr = self.pointercast(ptr, self.type_ptr_to(llty));
169 self.volatile_load(llty, ptr)
170 } else {
171 self.volatile_load(self.layout_of(tp_ty).llvm_type(self), ptr)
172 };
3dfed10e 173 let align = if name == sym::unaligned_volatile_load {
a1dfa0c6
XL
174 1
175 } else {
176 self.align_of(tp_ty).bytes() as u32
177 };
178 unsafe {
179 llvm::LLVMSetAlignment(load, align);
180 }
1b1a35ee 181 self.to_immediate(load, self.layout_of(tp_ty))
dfeec247 182 }
3dfed10e 183 sym::volatile_store => {
a1dfa0c6
XL
184 let dst = args[0].deref(self.cx());
185 args[1].val.volatile_store(self, dst);
186 return;
dfeec247 187 }
3dfed10e 188 sym::unaligned_volatile_store => {
a1dfa0c6
XL
189 let dst = args[0].deref(self.cx());
190 args[1].val.unaligned_volatile_store(self, dst);
191 return;
dfeec247 192 }
3dfed10e
XL
193 sym::prefetch_read_data
194 | sym::prefetch_write_data
195 | sym::prefetch_read_instruction
196 | sym::prefetch_write_instruction => {
a1dfa0c6 197 let (rw, cache_type) = match name {
3dfed10e
XL
198 sym::prefetch_read_data => (0, 1),
199 sym::prefetch_write_data => (1, 1),
200 sym::prefetch_read_instruction => (0, 0),
201 sym::prefetch_write_instruction => (1, 0),
dfeec247 202 _ => bug!(),
a1dfa0c6 203 };
94222f64
XL
204 self.call_intrinsic(
205 "llvm.prefetch",
dfeec247
XL
206 &[
207 args[0].immediate(),
208 self.const_i32(rw),
209 args[1].immediate(),
210 self.const_i32(cache_type),
211 ],
dfeec247
XL
212 )
213 }
3dfed10e
XL
214 sym::ctlz
215 | sym::ctlz_nonzero
216 | sym::cttz
217 | sym::cttz_nonzero
218 | sym::ctpop
219 | sym::bswap
220 | sym::bitreverse
3dfed10e
XL
221 | sym::rotate_left
222 | sym::rotate_right
223 | sym::saturating_add
224 | sym::saturating_sub => {
a1dfa0c6
XL
225 let ty = arg_tys[0];
226 match int_type_width_signed(ty, self) {
dfeec247 227 Some((width, signed)) => match name {
3dfed10e 228 sym::ctlz | sym::cttz => {
dfeec247 229 let y = self.const_bool(false);
94222f64
XL
230 self.call_intrinsic(
231 &format!("llvm.{}.i{}", name, width),
232 &[args[0].immediate(), y],
233 )
dfeec247 234 }
c295e0f8 235 sym::ctlz_nonzero => {
dfeec247 236 let y = self.const_bool(true);
c295e0f8
XL
237 let llvm_name = &format!("llvm.ctlz.i{}", width);
238 self.call_intrinsic(llvm_name, &[args[0].immediate(), y])
239 }
240 sym::cttz_nonzero => {
241 let y = self.const_bool(true);
242 let llvm_name = &format!("llvm.cttz.i{}", width);
94222f64 243 self.call_intrinsic(llvm_name, &[args[0].immediate(), y])
dfeec247 244 }
94222f64
XL
245 sym::ctpop => self.call_intrinsic(
246 &format!("llvm.ctpop.i{}", width),
dfeec247 247 &[args[0].immediate()],
dfeec247 248 ),
3dfed10e 249 sym::bswap => {
dfeec247
XL
250 if width == 8 {
251 args[0].immediate() // byte swap a u8/i8 is just a no-op
252 } else {
94222f64
XL
253 self.call_intrinsic(
254 &format!("llvm.bswap.i{}", width),
a1dfa0c6 255 &[args[0].immediate()],
a1dfa0c6
XL
256 )
257 }
dfeec247 258 }
94222f64
XL
259 sym::bitreverse => self.call_intrinsic(
260 &format!("llvm.bitreverse.i{}", width),
dfeec247 261 &[args[0].immediate()],
dfeec247 262 ),
3dfed10e
XL
263 sym::rotate_left | sym::rotate_right => {
264 let is_left = name == sym::rotate_left;
dfeec247
XL
265 let val = args[0].immediate();
266 let raw_shift = args[1].immediate();
267 // rotate = funnel shift with first two args the same
268 let llvm_name =
269 &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width);
94222f64 270 self.call_intrinsic(llvm_name, &[val, val, raw_shift])
dfeec247 271 }
3dfed10e
XL
272 sym::saturating_add | sym::saturating_sub => {
273 let is_add = name == sym::saturating_add;
dfeec247
XL
274 let lhs = args[0].immediate();
275 let rhs = args[1].immediate();
ba9703b0
XL
276 let llvm_name = &format!(
277 "llvm.{}{}.sat.i{}",
278 if signed { 's' } else { 'u' },
279 if is_add { "add" } else { "sub" },
280 width
281 );
94222f64 282 self.call_intrinsic(llvm_name, &[lhs, rhs])
dfeec247
XL
283 }
284 _ => bug!(),
285 },
a1dfa0c6
XL
286 None => {
287 span_invalid_monomorphization_error(
dfeec247
XL
288 tcx.sess,
289 span,
290 &format!(
291 "invalid monomorphization of `{}` intrinsic: \
292 expected basic integer type, found `{}`",
293 name, ty
294 ),
295 );
a1dfa0c6 296 return;
54a0048b 297 }
1a4d82fc 298 }
dfeec247 299 }
1a4d82fc 300
136023e0
XL
301 sym::raw_eq => {
302 use abi::Abi::*;
303 let tp_ty = substs.type_at(0);
304 let layout = self.layout_of(tp_ty).layout;
305 let use_integer_compare = match layout.abi {
306 Scalar(_) | ScalarPair(_, _) => true,
307 Uninhabited | Vector { .. } => false,
308 Aggregate { .. } => {
309 // For rusty ABIs, small aggregates are actually passed
310 // as `RegKind::Integer` (see `FnAbi::adjust_for_abi`),
311 // so we re-use that same threshold here.
312 layout.size <= self.data_layout().pointer_size * 2
313 }
314 };
315
316 let a = args[0].immediate();
317 let b = args[1].immediate();
318 if layout.size.bytes() == 0 {
319 self.const_bool(true)
320 } else if use_integer_compare {
321 let integer_ty = self.type_ix(layout.size.bits());
322 let ptr_ty = self.type_ptr_to(integer_ty);
323 let a_ptr = self.bitcast(a, ptr_ty);
324 let a_val = self.load(integer_ty, a_ptr, layout.align.abi);
325 let b_ptr = self.bitcast(b, ptr_ty);
326 let b_val = self.load(integer_ty, b_ptr, layout.align.abi);
327 self.icmp(IntPredicate::IntEQ, a_val, b_val)
328 } else {
329 let i8p_ty = self.type_i8p();
330 let a_ptr = self.bitcast(a, i8p_ty);
331 let b_ptr = self.bitcast(b, i8p_ty);
332 let n = self.const_usize(layout.size.bytes());
94222f64 333 let cmp = self.call_intrinsic("memcmp", &[a_ptr, b_ptr, n]);
136023e0
XL
334 self.icmp(IntPredicate::IntEQ, cmp, self.const_i32(0))
335 }
336 }
337
94222f64
XL
338 sym::black_box => {
339 args[0].val.store(self, result);
340
341 // We need to "use" the argument in some way LLVM can't introspect, and on
342 // targets that support it we can typically leverage inline assembly to do
343 // this. LLVM's interpretation of inline assembly is that it's, well, a black
344 // box. This isn't the greatest implementation since it probably deoptimizes
345 // more than we want, but it's so far good enough.
346 crate::asm::inline_asm_call(
347 self,
348 "",
349 "r,~{memory}",
350 &[result.llval],
351 self.type_void(),
352 true,
353 false,
354 ast::LlvmAsmDialect::Att,
355 &[span],
a2a8927a
XL
356 false,
357 None,
94222f64
XL
358 )
359 .unwrap_or_else(|| bug!("failed to generate inline asm call for `black_box`"));
360
361 // We have copied the value to `result` already.
362 return;
363 }
364
c295e0f8 365 _ if name.as_str().starts_with("simd_") => {
dfeec247 366 match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) {
a1dfa0c6 367 Ok(llval) => llval,
dfeec247 368 Err(()) => return,
1a4d82fc
JJ
369 }
370 }
e74abb32 371
0731742a 372 _ => bug!("unknown intrinsic '{}'", name),
a1dfa0c6 373 };
1a4d82fc 374
60c5eb7d
XL
375 if !fn_abi.ret.is_ignore() {
376 if let PassMode::Cast(ty) = fn_abi.ret.mode {
a1dfa0c6
XL
377 let ptr_llty = self.type_ptr_to(ty.llvm_type(self));
378 let ptr = self.pointercast(result.llval, ptr_llty);
379 self.store(llval, ptr, result.align);
380 } else {
381 OperandRef::from_immediate_or_packed_pair(self, llval, result.layout)
dfeec247
XL
382 .val
383 .store(self, result);
a1dfa0c6 384 }
54a0048b 385 }
1a4d82fc 386 }
a1dfa0c6
XL
387
388 fn abort(&mut self) {
94222f64 389 self.call_intrinsic("llvm.trap", &[]);
a1dfa0c6
XL
390 }
391
392 fn assume(&mut self, val: Self::Value) {
94222f64 393 self.call_intrinsic("llvm.assume", &[val]);
a1dfa0c6
XL
394 }
395
396 fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value {
94222f64 397 self.call_intrinsic("llvm.expect.i1", &[cond, self.const_bool(expected)])
a1dfa0c6 398 }
532ac7d7 399
3c0e092e
XL
400 fn type_test(&mut self, pointer: Self::Value, typeid: Self::Value) -> Self::Value {
401 // Test the called operand using llvm.type.test intrinsic. The LowerTypeTests link-time
402 // optimization pass replaces calls to this intrinsic with code to test type membership.
403 let i8p_ty = self.type_i8p();
404 let bitcast = self.bitcast(pointer, i8p_ty);
405 self.call_intrinsic("llvm.type.test", &[bitcast, typeid])
e74abb32
XL
406 }
407
dc9dc135 408 fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value {
94222f64 409 self.call_intrinsic("llvm.va_start", &[va_list])
532ac7d7
XL
410 }
411
dc9dc135 412 fn va_end(&mut self, va_list: &'ll Value) -> &'ll Value {
94222f64 413 self.call_intrinsic("llvm.va_end", &[va_list])
532ac7d7 414 }
1a4d82fc
JJ
415}
416
a2a8927a
XL
417fn try_intrinsic<'ll>(
418 bx: &mut Builder<'_, 'll, '_>,
ba9703b0 419 try_func: &'ll Value,
b7449926 420 data: &'ll Value,
ba9703b0 421 catch_func: &'ll Value,
b7449926 422 dest: &'ll Value,
32a655c1 423) {
f9f354fc 424 if bx.sess().panic_strategy() == PanicStrategy::Abort {
94222f64
XL
425 let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void());
426 bx.call(try_func_ty, try_func, &[data], None);
ba9703b0
XL
427 // Return 0 unconditionally from the intrinsic call;
428 // we can never unwind.
429 let ret_align = bx.tcx().data_layout.i32_align.abi;
430 bx.store(bx.const_i32(0), dest, ret_align);
2c00a5a8 431 } else if wants_msvc_seh(bx.sess()) {
ba9703b0 432 codegen_msvc_try(bx, try_func, data, catch_func, dest);
29967ef6 433 } else if bx.sess().target.is_like_emscripten {
1b1a35ee 434 codegen_emcc_try(bx, try_func, data, catch_func, dest);
c1a9b12d 435 } else {
ba9703b0 436 codegen_gnu_try(bx, try_func, data, catch_func, dest);
c1a9b12d
SL
437 }
438}
439
7453a54e 440// MSVC's definition of the `rust_try` function.
c1a9b12d 441//
7453a54e
SL
442// This implementation uses the new exception handling instructions in LLVM
443// which have support in LLVM for SEH on MSVC targets. Although these
444// instructions are meant to work for all targets, as of the time of this
445// writing, however, LLVM does not recommend the usage of these new instructions
446// as the old ones are still more optimized.
a2a8927a
XL
447fn codegen_msvc_try<'ll>(
448 bx: &mut Builder<'_, 'll, '_>,
ba9703b0 449 try_func: &'ll Value,
b7449926 450 data: &'ll Value,
ba9703b0 451 catch_func: &'ll Value,
b7449926
XL
452 dest: &'ll Value,
453) {
94222f64 454 let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
a1dfa0c6 455 bx.set_personality_fn(bx.eh_personality());
32a655c1 456
a1dfa0c6
XL
457 let mut normal = bx.build_sibling_block("normal");
458 let mut catchswitch = bx.build_sibling_block("catchswitch");
1b1a35ee
XL
459 let mut catchpad_rust = bx.build_sibling_block("catchpad_rust");
460 let mut catchpad_foreign = bx.build_sibling_block("catchpad_foreign");
a1dfa0c6 461 let mut caught = bx.build_sibling_block("caught");
32a655c1 462
ba9703b0 463 let try_func = llvm::get_param(bx.llfn(), 0);
2c00a5a8 464 let data = llvm::get_param(bx.llfn(), 1);
ba9703b0 465 let catch_func = llvm::get_param(bx.llfn(), 2);
c1a9b12d 466
7453a54e
SL
467 // We're generating an IR snippet that looks like:
468 //
ba9703b0 469 // declare i32 @rust_try(%try_func, %data, %catch_func) {
1b1a35ee 470 // %slot = alloca i8*
ba9703b0 471 // invoke %try_func(%data) to label %normal unwind label %catchswitch
7453a54e
SL
472 //
473 // normal:
474 // ret i32 0
c1a9b12d 475 //
7453a54e 476 // catchswitch:
1b1a35ee 477 // %cs = catchswitch within none [%catchpad_rust, %catchpad_foreign] unwind to caller
c1a9b12d 478 //
1b1a35ee
XL
479 // catchpad_rust:
480 // %tok = catchpad within %cs [%type_descriptor, 8, %slot]
ba9703b0
XL
481 // %ptr = load %slot
482 // call %catch_func(%data, %ptr)
7453a54e 483 // catchret from %tok to label %caught
c1a9b12d 484 //
1b1a35ee
XL
485 // catchpad_foreign:
486 // %tok = catchpad within %cs [null, 64, null]
487 // call %catch_func(%data, null)
488 // catchret from %tok to label %caught
489 //
7453a54e
SL
490 // caught:
491 // ret i32 1
492 // }
c1a9b12d 493 //
a7813a04
XL
494 // This structure follows the basic usage of throw/try/catch in LLVM.
495 // For example, compile this C++ snippet to see what LLVM generates:
496 //
e74abb32 497 // struct rust_panic {
dfeec247
XL
498 // rust_panic(const rust_panic&);
499 // ~rust_panic();
500 //
1b1a35ee 501 // void* x[2];
ba9703b0 502 // };
e74abb32 503 //
ba9703b0
XL
504 // int __rust_try(
505 // void (*try_func)(void*),
506 // void *data,
507 // void (*catch_func)(void*, void*) noexcept
508 // ) {
a7813a04 509 // try {
ba9703b0 510 // try_func(data);
a7813a04 511 // return 0;
dfeec247 512 // } catch(rust_panic& a) {
ba9703b0 513 // catch_func(data, &a);
a7813a04 514 // return 1;
1b1a35ee
XL
515 // } catch(...) {
516 // catch_func(data, NULL);
517 // return 1;
a7813a04
XL
518 // }
519 // }
7453a54e
SL
520 //
521 // More information can be found in libstd's seh.rs implementation.
dfeec247 522 let ptr_align = bx.tcx().data_layout.pointer_align.abi;
ba9703b0 523 let slot = bx.alloca(bx.type_i8p(), ptr_align);
94222f64
XL
524 let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void());
525 bx.invoke(try_func_ty, try_func, &[data], normal.llbb(), catchswitch.llbb(), None);
7453a54e 526
a1dfa0c6 527 normal.ret(bx.const_i32(0));
7453a54e 528
1b1a35ee
XL
529 let cs = catchswitch.catch_switch(None, None, 2);
530 catchswitch.add_handler(cs, catchpad_rust.llbb());
531 catchswitch.add_handler(cs, catchpad_foreign.llbb());
7453a54e 532
ba9703b0
XL
533 // We can't use the TypeDescriptor defined in libpanic_unwind because it
534 // might be in another DLL and the SEH encoding only supports specifying
535 // a TypeDescriptor from the current module.
536 //
537 // However this isn't an issue since the MSVC runtime uses string
538 // comparison on the type name to match TypeDescriptors rather than
539 // pointer equality.
540 //
541 // So instead we generate a new TypeDescriptor in each module that uses
542 // `try` and let the linker merge duplicate definitions in the same
543 // module.
544 //
545 // When modifying, make sure that the type_name string exactly matches
546 // the one used in src/libpanic_unwind/seh.rs.
547 let type_info_vtable = bx.declare_global("??_7type_info@@6B@", bx.type_i8p());
548 let type_name = bx.const_bytes(b"rust_panic\0");
549 let type_info =
550 bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_i8p()), type_name], false);
551 let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info));
552 unsafe {
553 llvm::LLVMRustSetLinkage(tydesc, llvm::Linkage::LinkOnceODRLinkage);
554 llvm::SetUniqueComdat(bx.llmod, tydesc);
555 llvm::LLVMSetInitializer(tydesc, type_info);
556 }
557
dfeec247
XL
558 // The flag value of 8 indicates that we are catching the exception by
559 // reference instead of by value. We can't use catch by value because
560 // that requires copying the exception object, which we don't support
561 // since our exception object effectively contains a Box.
562 //
563 // Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang
564 let flags = bx.const_i32(8);
1b1a35ee 565 let funclet = catchpad_rust.catch_pad(cs, &[tydesc, flags, slot]);
136023e0 566 let ptr = catchpad_rust.load(bx.type_i8p(), slot, ptr_align);
94222f64
XL
567 let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
568 catchpad_rust.call(catch_ty, catch_func, &[data, ptr], Some(&funclet));
1b1a35ee
XL
569 catchpad_rust.catch_ret(&funclet, caught.llbb());
570
571 // The flag value of 64 indicates a "catch-all".
572 let flags = bx.const_i32(64);
573 let null = bx.const_null(bx.type_i8p());
574 let funclet = catchpad_foreign.catch_pad(cs, &[null, flags, null]);
94222f64 575 catchpad_foreign.call(catch_ty, catch_func, &[data, null], Some(&funclet));
1b1a35ee 576 catchpad_foreign.catch_ret(&funclet, caught.llbb());
7453a54e 577
a1dfa0c6 578 caught.ret(bx.const_i32(1));
c1a9b12d
SL
579 });
580
581 // Note that no invoke is used here because by definition this function
582 // can't panic (that's what it's catching).
94222f64 583 let ret = bx.call(llty, llfn, &[try_func, data, catch_func], None);
a1dfa0c6 584 let i32_align = bx.tcx().data_layout.i32_align.abi;
2c00a5a8 585 bx.store(ret, dest, i32_align);
c1a9b12d
SL
586}
587
dc9dc135
XL
588// Definition of the standard `try` function for Rust using the GNU-like model
589// of exceptions (e.g., the normal semantics of LLVM's `landingpad` and `invoke`
c1a9b12d
SL
590// instructions).
591//
94b46f34 592// This codegen is a little surprising because we always call a shim
7453a54e
SL
593// function instead of inlining the call to `invoke` manually here. This is done
594// because in LLVM we're only allowed to have one personality per function
595// definition. The call to the `try` intrinsic is being inlined into the
596// function calling it, and that function may already have other personality
597// functions in play. By calling a shim we're guaranteed that our shim will have
598// the right personality function.
a2a8927a
XL
599fn codegen_gnu_try<'ll>(
600 bx: &mut Builder<'_, 'll, '_>,
ba9703b0 601 try_func: &'ll Value,
b7449926 602 data: &'ll Value,
ba9703b0 603 catch_func: &'ll Value,
b7449926
XL
604 dest: &'ll Value,
605) {
94222f64 606 let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
94b46f34 607 // Codegens the shims described above:
c1a9b12d 608 //
2c00a5a8 609 // bx:
ba9703b0 610 // invoke %try_func(%data) normal %normal unwind %catch
c1a9b12d
SL
611 //
612 // normal:
7453a54e 613 // ret 0
c1a9b12d
SL
614 //
615 // catch:
ba9703b0
XL
616 // (%ptr, _) = landingpad
617 // call %catch_func(%data, %ptr)
7453a54e 618 // ret 1
a1dfa0c6
XL
619 let mut then = bx.build_sibling_block("then");
620 let mut catch = bx.build_sibling_block("catch");
c1a9b12d 621
ba9703b0 622 let try_func = llvm::get_param(bx.llfn(), 0);
2c00a5a8 623 let data = llvm::get_param(bx.llfn(), 1);
ba9703b0 624 let catch_func = llvm::get_param(bx.llfn(), 2);
94222f64
XL
625 let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void());
626 bx.invoke(try_func_ty, try_func, &[data], then.llbb(), catch.llbb(), None);
a1dfa0c6 627 then.ret(bx.const_i32(0));
c1a9b12d
SL
628
629 // Type indicator for the exception being thrown.
7453a54e
SL
630 //
631 // The first value in this tuple is a pointer to the exception object
632 // being thrown. The second value is a "selector" indicating which of
633 // the landing pad clauses the exception's type had been matched to.
634 // rust_try ignores the selector.
a1dfa0c6
XL
635 let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false);
636 let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 1);
1b1a35ee 637 let tydesc = bx.const_null(bx.type_i8p());
e74abb32 638 catch.add_clause(vals, tydesc);
32a655c1 639 let ptr = catch.extract_value(vals, 0);
94222f64
XL
640 let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
641 catch.call(catch_ty, catch_func, &[data, ptr], None);
a1dfa0c6 642 catch.ret(bx.const_i32(1));
c1a9b12d
SL
643 });
644
645 // Note that no invoke is used here because by definition this function
646 // can't panic (that's what it's catching).
94222f64 647 let ret = bx.call(llty, llfn, &[try_func, data, catch_func], None);
a1dfa0c6 648 let i32_align = bx.tcx().data_layout.i32_align.abi;
2c00a5a8 649 bx.store(ret, dest, i32_align);
c1a9b12d
SL
650}
651
1b1a35ee
XL
652// Variant of codegen_gnu_try used for emscripten where Rust panics are
653// implemented using C++ exceptions. Here we use exceptions of a specific type
654// (`struct rust_panic`) to represent Rust panics.
a2a8927a
XL
655fn codegen_emcc_try<'ll>(
656 bx: &mut Builder<'_, 'll, '_>,
1b1a35ee
XL
657 try_func: &'ll Value,
658 data: &'ll Value,
659 catch_func: &'ll Value,
660 dest: &'ll Value,
661) {
94222f64 662 let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
1b1a35ee
XL
663 // Codegens the shims described above:
664 //
665 // bx:
666 // invoke %try_func(%data) normal %normal unwind %catch
667 //
668 // normal:
669 // ret 0
670 //
671 // catch:
672 // (%ptr, %selector) = landingpad
673 // %rust_typeid = @llvm.eh.typeid.for(@_ZTI10rust_panic)
674 // %is_rust_panic = %selector == %rust_typeid
675 // %catch_data = alloca { i8*, i8 }
676 // %catch_data[0] = %ptr
677 // %catch_data[1] = %is_rust_panic
678 // call %catch_func(%data, %catch_data)
679 // ret 1
1b1a35ee
XL
680 let mut then = bx.build_sibling_block("then");
681 let mut catch = bx.build_sibling_block("catch");
682
683 let try_func = llvm::get_param(bx.llfn(), 0);
684 let data = llvm::get_param(bx.llfn(), 1);
685 let catch_func = llvm::get_param(bx.llfn(), 2);
94222f64
XL
686 let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void());
687 bx.invoke(try_func_ty, try_func, &[data], then.llbb(), catch.llbb(), None);
1b1a35ee
XL
688 then.ret(bx.const_i32(0));
689
690 // Type indicator for the exception being thrown.
691 //
692 // The first value in this tuple is a pointer to the exception object
693 // being thrown. The second value is a "selector" indicating which of
694 // the landing pad clauses the exception's type had been matched to.
695 let tydesc = bx.eh_catch_typeinfo();
696 let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false);
697 let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 2);
698 catch.add_clause(vals, tydesc);
699 catch.add_clause(vals, bx.const_null(bx.type_i8p()));
700 let ptr = catch.extract_value(vals, 0);
701 let selector = catch.extract_value(vals, 1);
702
703 // Check if the typeid we got is the one for a Rust panic.
94222f64 704 let rust_typeid = catch.call_intrinsic("llvm.eh.typeid.for", &[tydesc]);
1b1a35ee
XL
705 let is_rust_panic = catch.icmp(IntPredicate::IntEQ, selector, rust_typeid);
706 let is_rust_panic = catch.zext(is_rust_panic, bx.type_bool());
707
708 // We need to pass two values to catch_func (ptr and is_rust_panic), so
709 // create an alloca and pass a pointer to that.
710 let ptr_align = bx.tcx().data_layout.pointer_align.abi;
711 let i8_align = bx.tcx().data_layout.i8_align.abi;
94222f64
XL
712 let catch_data_type = bx.type_struct(&[bx.type_i8p(), bx.type_bool()], false);
713 let catch_data = catch.alloca(catch_data_type, ptr_align);
714 let catch_data_0 = catch.inbounds_gep(
715 catch_data_type,
716 catch_data,
717 &[bx.const_usize(0), bx.const_usize(0)],
718 );
1b1a35ee 719 catch.store(ptr, catch_data_0, ptr_align);
94222f64
XL
720 let catch_data_1 = catch.inbounds_gep(
721 catch_data_type,
722 catch_data,
723 &[bx.const_usize(0), bx.const_usize(1)],
724 );
1b1a35ee
XL
725 catch.store(is_rust_panic, catch_data_1, i8_align);
726 let catch_data = catch.bitcast(catch_data, bx.type_i8p());
727
94222f64
XL
728 let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
729 catch.call(catch_ty, catch_func, &[data, catch_data], None);
1b1a35ee
XL
730 catch.ret(bx.const_i32(1));
731 });
732
733 // Note that no invoke is used here because by definition this function
734 // can't panic (that's what it's catching).
94222f64 735 let ret = bx.call(llty, llfn, &[try_func, data, catch_func], None);
1b1a35ee
XL
736 let i32_align = bx.tcx().data_layout.i32_align.abi;
737 bx.store(ret, dest, i32_align);
738}
739
94b46f34 740// Helper function to give a Block to a closure to codegen a shim function.
7453a54e 741// This is currently primarily used for the `try` intrinsic functions above.
b7449926
XL
742fn gen_fn<'ll, 'tcx>(
743 cx: &CodegenCx<'ll, 'tcx>,
744 name: &str,
29967ef6 745 rust_fn_sig: ty::PolyFnSig<'tcx>,
b7449926 746 codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>),
94222f64 747) -> (&'ll Type, &'ll Value) {
c295e0f8 748 let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty());
94222f64 749 let llty = fn_abi.llvm_type(cx);
c295e0f8 750 let llfn = cx.declare_fn(name, fn_abi);
136023e0 751 cx.set_frame_pointer_type(llfn);
ba9703b0 752 cx.apply_target_cpu_attr(llfn);
60c5eb7d
XL
753 // FIXME(eddyb) find a nicer way to do this.
754 unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) };
17df50a5
XL
755 let llbb = Builder::append_block(cx, llfn, "entry-block");
756 let bx = Builder::build(cx, llbb);
94b46f34 757 codegen(bx);
94222f64 758 (llty, llfn)
7453a54e
SL
759}
760
761// Helper function used to get a handle to the `__rust_try` function used to
762// catch exceptions.
763//
764// This function is only generated once and is then cached.
b7449926
XL
765fn get_rust_try_fn<'ll, 'tcx>(
766 cx: &CodegenCx<'ll, 'tcx>,
767 codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>),
94222f64 768) -> (&'ll Type, &'ll Value) {
2c00a5a8 769 if let Some(llfn) = cx.rust_try_fn.get() {
54a0048b 770 return llfn;
c1a9b12d
SL
771 }
772
773 // Define the type up front for the signature of the rust_try function.
2c00a5a8 774 let tcx = cx.tcx;
c1a9b12d 775 let i8p = tcx.mk_mut_ptr(tcx.types.i8);
29967ef6
XL
776 // `unsafe fn(*mut i8) -> ()`
777 let try_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
8bb4bdeb 778 iter::once(i8p),
b7449926 779 tcx.mk_unit(),
8bb4bdeb
XL
780 false,
781 hir::Unsafety::Unsafe,
dfeec247 782 Abi::Rust,
8bb4bdeb 783 )));
29967ef6
XL
784 // `unsafe fn(*mut i8, *mut i8) -> ()`
785 let catch_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
ba9703b0
XL
786 [i8p, i8p].iter().cloned(),
787 tcx.mk_unit(),
788 false,
789 hir::Unsafety::Unsafe,
790 Abi::Rust,
791 )));
29967ef6
XL
792 // `unsafe fn(unsafe fn(*mut i8) -> (), *mut i8, unsafe fn(*mut i8, *mut i8) -> ()) -> i32`
793 let rust_fn_sig = ty::Binder::dummy(cx.tcx.mk_fn_sig(
794 vec![try_fn_ty, i8p, catch_fn_ty].into_iter(),
795 tcx.types.i32,
796 false,
797 hir::Unsafety::Unsafe,
798 Abi::Rust,
799 ));
800 let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen);
2c00a5a8 801 cx.rust_try_fn.set(Some(rust_try));
0bf4aa26 802 rust_try
c1a9b12d 803}
e9174d1e 804
a2a8927a
XL
805fn generic_simd_intrinsic<'ll, 'tcx>(
806 bx: &mut Builder<'_, 'll, 'tcx>,
3dfed10e 807 name: Symbol,
32a655c1 808 callee_ty: Ty<'tcx>,
a1dfa0c6 809 args: &[OperandRef<'tcx, &'ll Value>],
32a655c1 810 ret_ty: Ty<'tcx>,
b7449926 811 llret_ty: &'ll Type,
dfeec247 812 span: Span,
b7449926 813) -> Result<&'ll Value, ()> {
e9174d1e
SL
814 // macros for error handling:
815 macro_rules! emit_error {
816 ($msg: tt) => {
817 emit_error!($msg, )
818 };
819 ($msg: tt, $($fmt: tt)*) => {
b039eaaf 820 span_invalid_monomorphization_error(
2c00a5a8 821 bx.sess(), span,
0bf4aa26 822 &format!(concat!("invalid monomorphization of `{}` intrinsic: ", $msg),
b039eaaf 823 name, $($fmt)*));
e9174d1e
SL
824 }
825 }
0bf4aa26 826
0531ce1d
XL
827 macro_rules! return_error {
828 ($($fmt: tt)*) => {
829 {
e9174d1e 830 emit_error!($($fmt)*);
ff7c6d11 831 return Err(());
e9174d1e
SL
832 }
833 }
834 }
0531ce1d
XL
835
836 macro_rules! require {
837 ($cond: expr, $($fmt: tt)*) => {
838 if !$cond {
839 return_error!($($fmt)*);
840 }
841 };
842 }
0bf4aa26 843
e9174d1e
SL
844 macro_rules! require_simd {
845 ($ty: expr, $position: expr) => {
846 require!($ty.is_simd(), "expected SIMD {} type, found non-SIMD `{}`", $position, $ty)
dfeec247 847 };
e9174d1e
SL
848 }
849
2c00a5a8 850 let tcx = bx.tcx();
fc512014
XL
851 let sig =
852 tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), callee_ty.fn_sig(tcx));
476ff2be 853 let arg_tys = sig.inputs();
e9174d1e 854
3dfed10e 855 if name == sym::simd_select_bitmask {
0731742a 856 require_simd!(arg_tys[1], "argument");
3c0e092e
XL
857 let (len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
858
859 let expected_int_bits = (len.max(8) - 1).next_power_of_two();
860 let expected_bytes = len / 8 + ((len % 8 > 0) as u64);
861
862 let mask_ty = arg_tys[0];
863 let mask = match mask_ty.kind() {
864 ty::Int(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
865 ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
866 ty::Array(elem, len)
867 if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
868 && len.try_eval_usize(bx.tcx, ty::ParamEnv::reveal_all())
869 == Some(expected_bytes) =>
870 {
871 let place = PlaceRef::alloca(bx, args[0].layout);
872 args[0].val.store(bx, place);
873 let int_ty = bx.type_ix(expected_bytes * 8);
874 let ptr = bx.pointercast(place.llval, bx.cx.type_ptr_to(int_ty));
875 bx.load(int_ty, ptr, Align::ONE)
876 }
877 _ => return_error!(
878 "invalid bitmask `{}`, expected `u{}` or `[u8; {}]`",
879 mask_ty,
880 expected_int_bits,
881 expected_bytes
882 ),
883 };
884
0731742a 885 let i1 = bx.type_i1();
3c0e092e
XL
886 let im = bx.type_ix(len);
887 let i1xn = bx.type_vector(i1, len);
888 let m_im = bx.trunc(mask, im);
29967ef6 889 let m_i1s = bx.bitcast(m_im, i1xn);
0731742a
XL
890 return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
891 }
892
893 // every intrinsic below takes a SIMD vector as its first argument
e9174d1e
SL
894 require_simd!(arg_tys[0], "input");
895 let in_ty = arg_tys[0];
e9174d1e
SL
896
897 let comparison = match name {
3dfed10e
XL
898 sym::simd_eq => Some(hir::BinOpKind::Eq),
899 sym::simd_ne => Some(hir::BinOpKind::Ne),
900 sym::simd_lt => Some(hir::BinOpKind::Lt),
901 sym::simd_le => Some(hir::BinOpKind::Le),
902 sym::simd_gt => Some(hir::BinOpKind::Gt),
903 sym::simd_ge => Some(hir::BinOpKind::Ge),
dfeec247 904 _ => None,
e9174d1e
SL
905 };
906
fc512014 907 let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx());
e9174d1e
SL
908 if let Some(cmp_op) = comparison {
909 require_simd!(ret_ty, "return");
910
fc512014 911 let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
dfeec247
XL
912 require!(
913 in_len == out_len,
914 "expected return type with length {} (same as input type `{}`), \
fc512014 915 found `{}` with length {}",
dfeec247
XL
916 in_len,
917 in_ty,
918 ret_ty,
919 out_len
920 );
921 require!(
922 bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer,
923 "expected return type with integer elements, found `{}` with non-integer `{}`",
924 ret_ty,
fc512014 925 out_ty
dfeec247
XL
926 );
927
928 return Ok(compare_simd_types(
929 bx,
930 args[0].immediate(),
931 args[1].immediate(),
932 in_elem,
933 llret_ty,
934 cmp_op,
935 ));
e9174d1e
SL
936 }
937
c295e0f8
XL
938 if let Some(stripped) = name.as_str().strip_prefix("simd_shuffle") {
939 // If this intrinsic is the older "simd_shuffleN" form, simply parse the integer.
940 // If there is no suffix, use the index array length.
941 let n: u64 = if stripped.is_empty() {
942 // Make sure this is actually an array, since typeck only checks the length-suffixed
943 // version of this intrinsic.
944 match args[2].layout.ty.kind() {
945 ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => {
946 len.try_eval_usize(bx.cx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else(|| {
947 span_bug!(span, "could not evaluate shuffle index array length")
948 })
949 }
950 _ => return_error!(
951 "simd_shuffle index must be an array of `u32`, got `{}`",
952 args[2].layout.ty
953 ),
954 }
955 } else {
956 stripped.parse().unwrap_or_else(|_| {
957 span_bug!(span, "bad `simd_shuffle` instruction only caught in codegen?")
958 })
959 };
e9174d1e
SL
960
961 require_simd!(ret_ty, "return");
fc512014 962 let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx());
dfeec247
XL
963 require!(
964 out_len == n,
965 "expected return type of length {}, found `{}` with length {}",
966 n,
967 ret_ty,
968 out_len
969 );
970 require!(
fc512014 971 in_elem == out_ty,
dfeec247 972 "expected return element type `{}` (element of input `{}`), \
fc512014 973 found `{}` with element type `{}`",
dfeec247
XL
974 in_elem,
975 in_ty,
976 ret_ty,
fc512014 977 out_ty
dfeec247 978 );
e9174d1e 979
60c5eb7d 980 let total_len = u128::from(in_len) * 2;
e9174d1e 981
ff7c6d11 982 let vector = args[2].immediate();
e9174d1e
SL
983
984 let indices: Option<Vec<_>> = (0..n)
985 .map(|i| {
986 let arg_idx = i;
a1dfa0c6
XL
987 let val = bx.const_get_elt(vector, i as u64);
988 match bx.const_to_opt_u128(val, true) {
e9174d1e
SL
989 None => {
990 emit_error!("shuffle index #{} is not a constant", arg_idx);
991 None
992 }
993 Some(idx) if idx >= total_len => {
dfeec247
XL
994 emit_error!(
995 "shuffle index #{} is out of bounds (limit {})",
996 arg_idx,
997 total_len
998 );
e9174d1e
SL
999 None
1000 }
a1dfa0c6 1001 Some(idx) => Some(bx.const_i32(idx as i32)),
e9174d1e
SL
1002 }
1003 })
1004 .collect();
1005 let indices = match indices {
1006 Some(i) => i,
dfeec247 1007 None => return Ok(bx.const_null(llret_ty)),
e9174d1e
SL
1008 };
1009
dfeec247
XL
1010 return Ok(bx.shuffle_vector(
1011 args[0].immediate(),
1012 args[1].immediate(),
1013 bx.const_vector(&indices),
1014 ));
e9174d1e
SL
1015 }
1016
3dfed10e 1017 if name == sym::simd_insert {
dfeec247
XL
1018 require!(
1019 in_elem == arg_tys[2],
1020 "expected inserted type `{}` (element of input `{}`), found `{}`",
1021 in_elem,
1022 in_ty,
1023 arg_tys[2]
1024 );
1025 return Ok(bx.insert_element(
1026 args[0].immediate(),
1027 args[2].immediate(),
1028 args[1].immediate(),
1029 ));
e9174d1e 1030 }
3dfed10e 1031 if name == sym::simd_extract {
dfeec247
XL
1032 require!(
1033 ret_ty == in_elem,
1034 "expected return type `{}` (element of input `{}`), found `{}`",
1035 in_elem,
1036 in_ty,
1037 ret_ty
1038 );
1039 return Ok(bx.extract_element(args[0].immediate(), args[1].immediate()));
e9174d1e
SL
1040 }
1041
3dfed10e 1042 if name == sym::simd_select {
0531ce1d
XL
1043 let m_elem_ty = in_elem;
1044 let m_len = in_len;
0731742a 1045 require_simd!(arg_tys[1], "argument");
fc512014 1046 let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
dfeec247
XL
1047 require!(
1048 m_len == v_len,
1049 "mismatched lengths: mask length `{}` != other vector length `{}`",
1050 m_len,
1051 v_len
0531ce1d 1052 );
1b1a35ee 1053 match m_elem_ty.kind() {
dfeec247
XL
1054 ty::Int(_) => {}
1055 _ => return_error!("mask element type is `{}`, expected `i_`", m_elem_ty),
0531ce1d
XL
1056 }
1057 // truncate the mask to a vector of i1s
a1dfa0c6
XL
1058 let i1 = bx.type_i1();
1059 let i1xn = bx.type_vector(i1, m_len as u64);
0531ce1d
XL
1060 let m_i1s = bx.trunc(args[0].immediate(), i1xn);
1061 return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
1062 }
1063
3dfed10e 1064 if name == sym::simd_bitmask {
9fa01778 1065 // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a
3c0e092e
XL
1066 // vector mask and returns the most significant bit (MSB) of each lane in the form
1067 // of either:
1068 // * an unsigned integer
1069 // * an array of `u8`
1070 // If the vector has less than 8 lanes, a u8 is returned with zeroed trailing bits.
1071 //
1072 // The bit order of the result depends on the byte endianness, LSB-first for little
1073 // endian and MSB-first for big endian.
9fa01778 1074 let expected_int_bits = in_len.max(8);
3c0e092e 1075 let expected_bytes = expected_int_bits / 8 + ((expected_int_bits % 8 > 0) as u64);
9fa01778
XL
1076
1077 // Integer vector <i{in_bitwidth} x in_len>:
1b1a35ee 1078 let (i_xn, in_elem_bitwidth) = match in_elem.kind() {
29967ef6
XL
1079 ty::Int(i) => (
1080 args[0].immediate(),
1081 i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()),
1082 ),
1083 ty::Uint(i) => (
1084 args[0].immediate(),
1085 i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()),
1086 ),
9fa01778
XL
1087 _ => return_error!(
1088 "vector argument `{}`'s element type `{}`, expected integer element type",
dfeec247
XL
1089 in_ty,
1090 in_elem
9fa01778
XL
1091 ),
1092 };
1093
1094 // Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position.
dfeec247
XL
1095 let shift_indices =
1096 vec![
ba9703b0 1097 bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _);
dfeec247
XL
1098 in_len as _
1099 ];
9fa01778
XL
1100 let i_xn_msb = bx.lshr(i_xn, bx.const_vector(shift_indices.as_slice()));
1101 // Truncate vector to an <i1 x N>
ba9703b0 1102 let i1xn = bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len));
9fa01778 1103 // Bitcast <i1 x N> to iN:
ba9703b0 1104 let i_ = bx.bitcast(i1xn, bx.type_ix(in_len));
3c0e092e
XL
1105
1106 match ret_ty.kind() {
1107 ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => {
1108 // Zero-extend iN to the bitmask type:
1109 return Ok(bx.zext(i_, bx.type_ix(expected_int_bits)));
1110 }
1111 ty::Array(elem, len)
1112 if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))
1113 && len.try_eval_usize(bx.tcx, ty::ParamEnv::reveal_all())
1114 == Some(expected_bytes) =>
1115 {
1116 // Zero-extend iN to the array lengh:
1117 let ze = bx.zext(i_, bx.type_ix(expected_bytes * 8));
1118
1119 // Convert the integer to a byte array
1120 let ptr = bx.alloca(bx.type_ix(expected_bytes * 8), Align::ONE);
1121 bx.store(ze, ptr, Align::ONE);
1122 let array_ty = bx.type_array(bx.type_i8(), expected_bytes);
1123 let ptr = bx.pointercast(ptr, bx.cx.type_ptr_to(array_ty));
1124 return Ok(bx.load(array_ty, ptr, Align::ONE));
1125 }
1126 _ => return_error!(
1127 "cannot return `{}`, expected `u{}` or `[u8; {}]`",
1128 ret_ty,
1129 expected_int_bits,
1130 expected_bytes
1131 ),
1132 }
9fa01778
XL
1133 }
1134
a2a8927a 1135 fn simd_simple_float_intrinsic<'ll, 'tcx>(
6a06907d 1136 name: Symbol,
ba9703b0
XL
1137 in_elem: &::rustc_middle::ty::TyS<'_>,
1138 in_ty: &::rustc_middle::ty::TyS<'_>,
60c5eb7d 1139 in_len: u64,
a2a8927a 1140 bx: &mut Builder<'_, 'll, 'tcx>,
b7449926 1141 span: Span,
a1dfa0c6 1142 args: &[OperandRef<'tcx, &'ll Value>],
b7449926 1143 ) -> Result<&'ll Value, ()> {
94b46f34
XL
1144 macro_rules! emit_error {
1145 ($msg: tt) => {
1146 emit_error!($msg, )
1147 };
1148 ($msg: tt, $($fmt: tt)*) => {
1149 span_invalid_monomorphization_error(
1150 bx.sess(), span,
0bf4aa26 1151 &format!(concat!("invalid monomorphization of `{}` intrinsic: ", $msg),
94b46f34
XL
1152 name, $($fmt)*));
1153 }
1154 }
1155 macro_rules! return_error {
1156 ($($fmt: tt)*) => {
1157 {
1158 emit_error!($($fmt)*);
1159 return Err(());
1160 }
1161 }
1162 }
6a06907d
XL
1163
1164 let (elem_ty_str, elem_ty) = if let ty::Float(f) = in_elem.kind() {
1165 let elem_ty = bx.cx.type_float_from_ty(*f);
1166 match f.bit_width() {
1167 32 => ("f32", elem_ty),
1168 64 => ("f64", elem_ty),
1169 _ => {
dfeec247 1170 return_error!(
6a06907d
XL
1171 "unsupported element type `{}` of floating-point vector `{}`",
1172 f.name_str(),
1173 in_ty
dfeec247 1174 );
94b46f34 1175 }
94b46f34 1176 }
6a06907d
XL
1177 } else {
1178 return_error!("`{}` is not a floating-point type", in_ty);
94b46f34
XL
1179 };
1180
6a06907d
XL
1181 let vec_ty = bx.type_vector(elem_ty, in_len);
1182
1183 let (intr_name, fn_ty) = match name {
6a06907d 1184 sym::simd_ceil => ("ceil", bx.type_func(&[vec_ty], vec_ty)),
cdc7bbd5
XL
1185 sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)),
1186 sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)),
6a06907d 1187 sym::simd_fexp2 => ("exp2", bx.type_func(&[vec_ty], vec_ty)),
cdc7bbd5 1188 sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)),
6a06907d
XL
1189 sym::simd_flog10 => ("log10", bx.type_func(&[vec_ty], vec_ty)),
1190 sym::simd_flog2 => ("log2", bx.type_func(&[vec_ty], vec_ty)),
1191 sym::simd_flog => ("log", bx.type_func(&[vec_ty], vec_ty)),
cdc7bbd5
XL
1192 sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)),
1193 sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)),
6a06907d
XL
1194 sym::simd_fpowi => ("powi", bx.type_func(&[vec_ty, bx.type_i32()], vec_ty)),
1195 sym::simd_fpow => ("pow", bx.type_func(&[vec_ty, vec_ty], vec_ty)),
cdc7bbd5
XL
1196 sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)),
1197 sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)),
1198 sym::simd_round => ("round", bx.type_func(&[vec_ty], vec_ty)),
1199 sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)),
6a06907d
XL
1200 _ => return_error!("unrecognized intrinsic `{}`", name),
1201 };
6a06907d 1202 let llvm_name = &format!("llvm.{0}.v{1}{2}", intr_name, in_len, elem_ty_str);
c295e0f8 1203 let f = bx.declare_cfn(llvm_name, llvm::UnnamedAddr::No, fn_ty);
94222f64
XL
1204 let c =
1205 bx.call(fn_ty, f, &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None);
0bf4aa26 1206 Ok(c)
94b46f34
XL
1207 }
1208
6a06907d
XL
1209 if std::matches!(
1210 name,
cdc7bbd5 1211 sym::simd_ceil
6a06907d 1212 | sym::simd_fabs
cdc7bbd5 1213 | sym::simd_fcos
6a06907d 1214 | sym::simd_fexp2
cdc7bbd5 1215 | sym::simd_fexp
6a06907d
XL
1216 | sym::simd_flog10
1217 | sym::simd_flog2
1218 | sym::simd_flog
cdc7bbd5 1219 | sym::simd_floor
6a06907d 1220 | sym::simd_fma
cdc7bbd5
XL
1221 | sym::simd_fpow
1222 | sym::simd_fpowi
1223 | sym::simd_fsin
1224 | sym::simd_fsqrt
1225 | sym::simd_round
1226 | sym::simd_trunc
6a06907d
XL
1227 ) {
1228 return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args);
94b46f34
XL
1229 }
1230
1231 // FIXME: use:
1232 // https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Function.h#L182
1233 // https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Intrinsics.h#L81
c295e0f8
XL
1234 fn llvm_vector_str(
1235 elem_ty: Ty<'_>,
1236 vec_len: u64,
1237 no_pointers: usize,
a2a8927a 1238 bx: &Builder<'_, '_, '_>,
c295e0f8 1239 ) -> String {
94b46f34 1240 let p0s: String = "p0".repeat(no_pointers);
1b1a35ee 1241 match *elem_ty.kind() {
c295e0f8
XL
1242 ty::Int(v) => format!(
1243 "v{}{}i{}",
1244 vec_len,
1245 p0s,
1246 // Normalize to prevent crash if v: IntTy::Isize
1247 v.normalize(bx.target_spec().pointer_width).bit_width().unwrap()
1248 ),
1249 ty::Uint(v) => format!(
1250 "v{}{}i{}",
1251 vec_len,
1252 p0s,
1253 // Normalize to prevent crash if v: UIntTy::Usize
1254 v.normalize(bx.target_spec().pointer_width).bit_width().unwrap()
1255 ),
b7449926 1256 ty::Float(v) => format!("v{}{}f{}", vec_len, p0s, v.bit_width()),
94b46f34
XL
1257 _ => unreachable!(),
1258 }
1259 }
1260
a2a8927a 1261 fn llvm_vector_ty<'ll>(
dfeec247
XL
1262 cx: &CodegenCx<'ll, '_>,
1263 elem_ty: Ty<'_>,
1264 vec_len: u64,
1265 mut no_pointers: usize,
1266 ) -> &'ll Type {
94b46f34 1267 // FIXME: use cx.layout_of(ty).llvm_type() ?
1b1a35ee 1268 let mut elem_ty = match *elem_ty.kind() {
dfeec247
XL
1269 ty::Int(v) => cx.type_int_from_ty(v),
1270 ty::Uint(v) => cx.type_uint_from_ty(v),
1271 ty::Float(v) => cx.type_float_from_ty(v),
94b46f34
XL
1272 _ => unreachable!(),
1273 };
1274 while no_pointers > 0 {
a1dfa0c6 1275 elem_ty = cx.type_ptr_to(elem_ty);
94b46f34
XL
1276 no_pointers -= 1;
1277 }
60c5eb7d 1278 cx.type_vector(elem_ty, vec_len)
94b46f34
XL
1279 }
1280
3dfed10e 1281 if name == sym::simd_gather {
94b46f34
XL
1282 // simd_gather(values: <N x T>, pointers: <N x *_ T>,
1283 // mask: <N x i{M}>) -> <N x T>
1284 // * N: number of elements in the input vectors
1285 // * T: type of the element to load
1286 // * M: any integer width is supported, will be truncated to i1
1287
1288 // All types must be simd vector types
1289 require_simd!(in_ty, "first");
1290 require_simd!(arg_tys[1], "second");
1291 require_simd!(arg_tys[2], "third");
1292 require_simd!(ret_ty, "return");
1293
1294 // Of the same length:
fc512014
XL
1295 let (out_len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
1296 let (out_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx());
dfeec247 1297 require!(
fc512014 1298 in_len == out_len,
dfeec247 1299 "expected {} argument with length {} (same as input type `{}`), \
fc512014 1300 found `{}` with length {}",
dfeec247
XL
1301 "second",
1302 in_len,
1303 in_ty,
1304 arg_tys[1],
fc512014 1305 out_len
dfeec247
XL
1306 );
1307 require!(
fc512014 1308 in_len == out_len2,
dfeec247 1309 "expected {} argument with length {} (same as input type `{}`), \
fc512014 1310 found `{}` with length {}",
dfeec247
XL
1311 "third",
1312 in_len,
1313 in_ty,
1314 arg_tys[2],
fc512014 1315 out_len2
dfeec247 1316 );
94b46f34
XL
1317
1318 // The return type must match the first argument type
dfeec247 1319 require!(ret_ty == in_ty, "expected return type `{}`, found `{}`", in_ty, ret_ty);
94b46f34
XL
1320
1321 // This counts how many pointers
dc9dc135 1322 fn ptr_count(t: Ty<'_>) -> usize {
1b1a35ee 1323 match t.kind() {
b7449926 1324 ty::RawPtr(p) => 1 + ptr_count(p.ty),
94b46f34
XL
1325 _ => 0,
1326 }
1327 }
1328
1329 // Non-ptr type
dc9dc135 1330 fn non_ptr(t: Ty<'_>) -> Ty<'_> {
1b1a35ee 1331 match t.kind() {
b7449926 1332 ty::RawPtr(p) => non_ptr(p.ty),
94b46f34
XL
1333 _ => t,
1334 }
1335 }
1336
1337 // The second argument must be a simd vector with an element type that's a pointer
1338 // to the element type of the first argument
fc512014
XL
1339 let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx());
1340 let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx());
1341 let (pointer_count, underlying_ty) = match element_ty1.kind() {
1342 ty::RawPtr(p) if p.ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)),
94b46f34 1343 _ => {
dfeec247
XL
1344 require!(
1345 false,
1346 "expected element type `{}` of second argument `{}` \
fc512014
XL
1347 to be a pointer to the element type `{}` of the first \
1348 argument `{}`, found `{}` != `*_ {}`",
1349 element_ty1,
dfeec247
XL
1350 arg_tys[1],
1351 in_elem,
1352 in_ty,
fc512014 1353 element_ty1,
dfeec247
XL
1354 in_elem
1355 );
94b46f34
XL
1356 unreachable!();
1357 }
1358 };
1359 assert!(pointer_count > 0);
fc512014
XL
1360 assert_eq!(pointer_count - 1, ptr_count(element_ty0));
1361 assert_eq!(underlying_ty, non_ptr(element_ty0));
94b46f34
XL
1362
1363 // The element type of the third argument must be a signed integer type of any width:
fc512014
XL
1364 let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx());
1365 match element_ty2.kind() {
b7449926 1366 ty::Int(_) => (),
94b46f34 1367 _ => {
dfeec247
XL
1368 require!(
1369 false,
1370 "expected element type `{}` of third argument `{}` \
94b46f34 1371 to be a signed integer type",
fc512014 1372 element_ty2,
dfeec247
XL
1373 arg_tys[2]
1374 );
94b46f34
XL
1375 }
1376 }
1377
1378 // Alignment of T, must be a constant integer value:
a1dfa0c6
XL
1379 let alignment_ty = bx.type_i32();
1380 let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32);
94b46f34
XL
1381
1382 // Truncate the mask vector to a vector of i1s:
1383 let (mask, mask_ty) = {
a1dfa0c6 1384 let i1 = bx.type_i1();
60c5eb7d 1385 let i1xn = bx.type_vector(i1, in_len);
94b46f34
XL
1386 (bx.trunc(args[2].immediate(), i1xn), i1xn)
1387 };
1388
1389 // Type of the vector of pointers:
a1dfa0c6 1390 let llvm_pointer_vec_ty = llvm_vector_ty(bx, underlying_ty, in_len, pointer_count);
c295e0f8 1391 let llvm_pointer_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count, bx);
94b46f34
XL
1392
1393 // Type of the vector of elements:
a1dfa0c6 1394 let llvm_elem_vec_ty = llvm_vector_ty(bx, underlying_ty, in_len, pointer_count - 1);
c295e0f8 1395 let llvm_elem_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count - 1, bx);
94b46f34 1396
dfeec247
XL
1397 let llvm_intrinsic =
1398 format!("llvm.masked.gather.{}.{}", llvm_elem_vec_str, llvm_pointer_vec_str);
94222f64
XL
1399 let fn_ty = bx.type_func(
1400 &[llvm_pointer_vec_ty, alignment_ty, mask_ty, llvm_elem_vec_ty],
1401 llvm_elem_vec_ty,
dfeec247 1402 );
94222f64
XL
1403 let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
1404 let v =
1405 bx.call(fn_ty, f, &[args[1].immediate(), alignment, mask, args[0].immediate()], None);
94b46f34
XL
1406 return Ok(v);
1407 }
1408
3dfed10e 1409 if name == sym::simd_scatter {
94b46f34
XL
1410 // simd_scatter(values: <N x T>, pointers: <N x *mut T>,
1411 // mask: <N x i{M}>) -> ()
1412 // * N: number of elements in the input vectors
1413 // * T: type of the element to load
1414 // * M: any integer width is supported, will be truncated to i1
1415
1416 // All types must be simd vector types
1417 require_simd!(in_ty, "first");
1418 require_simd!(arg_tys[1], "second");
1419 require_simd!(arg_tys[2], "third");
1420
1421 // Of the same length:
fc512014
XL
1422 let (element_len1, _) = arg_tys[1].simd_size_and_type(bx.tcx());
1423 let (element_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx());
dfeec247 1424 require!(
fc512014 1425 in_len == element_len1,
dfeec247 1426 "expected {} argument with length {} (same as input type `{}`), \
fc512014 1427 found `{}` with length {}",
dfeec247
XL
1428 "second",
1429 in_len,
1430 in_ty,
1431 arg_tys[1],
fc512014 1432 element_len1
dfeec247
XL
1433 );
1434 require!(
fc512014 1435 in_len == element_len2,
dfeec247 1436 "expected {} argument with length {} (same as input type `{}`), \
fc512014 1437 found `{}` with length {}",
dfeec247
XL
1438 "third",
1439 in_len,
1440 in_ty,
1441 arg_tys[2],
fc512014 1442 element_len2
dfeec247 1443 );
94b46f34
XL
1444
1445 // This counts how many pointers
dc9dc135 1446 fn ptr_count(t: Ty<'_>) -> usize {
1b1a35ee 1447 match t.kind() {
b7449926 1448 ty::RawPtr(p) => 1 + ptr_count(p.ty),
94b46f34
XL
1449 _ => 0,
1450 }
1451 }
1452
1453 // Non-ptr type
dc9dc135 1454 fn non_ptr(t: Ty<'_>) -> Ty<'_> {
1b1a35ee 1455 match t.kind() {
b7449926 1456 ty::RawPtr(p) => non_ptr(p.ty),
94b46f34
XL
1457 _ => t,
1458 }
1459 }
1460
1461 // The second argument must be a simd vector with an element type that's a pointer
1462 // to the element type of the first argument
fc512014
XL
1463 let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx());
1464 let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx());
1465 let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx());
1466 let (pointer_count, underlying_ty) = match element_ty1.kind() {
dfeec247 1467 ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::Mutability::Mut => {
fc512014 1468 (ptr_count(element_ty1), non_ptr(element_ty1))
dfeec247 1469 }
94b46f34 1470 _ => {
dfeec247
XL
1471 require!(
1472 false,
1473 "expected element type `{}` of second argument `{}` \
fc512014
XL
1474 to be a pointer to the element type `{}` of the first \
1475 argument `{}`, found `{}` != `*mut {}`",
1476 element_ty1,
dfeec247
XL
1477 arg_tys[1],
1478 in_elem,
1479 in_ty,
fc512014 1480 element_ty1,
dfeec247
XL
1481 in_elem
1482 );
94b46f34
XL
1483 unreachable!();
1484 }
1485 };
1486 assert!(pointer_count > 0);
fc512014
XL
1487 assert_eq!(pointer_count - 1, ptr_count(element_ty0));
1488 assert_eq!(underlying_ty, non_ptr(element_ty0));
94b46f34
XL
1489
1490 // The element type of the third argument must be a signed integer type of any width:
fc512014 1491 match element_ty2.kind() {
b7449926 1492 ty::Int(_) => (),
94b46f34 1493 _ => {
dfeec247
XL
1494 require!(
1495 false,
1496 "expected element type `{}` of third argument `{}` \
fc512014
XL
1497 be a signed integer type",
1498 element_ty2,
dfeec247
XL
1499 arg_tys[2]
1500 );
94b46f34
XL
1501 }
1502 }
1503
1504 // Alignment of T, must be a constant integer value:
a1dfa0c6
XL
1505 let alignment_ty = bx.type_i32();
1506 let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32);
94b46f34
XL
1507
1508 // Truncate the mask vector to a vector of i1s:
1509 let (mask, mask_ty) = {
a1dfa0c6 1510 let i1 = bx.type_i1();
60c5eb7d 1511 let i1xn = bx.type_vector(i1, in_len);
94b46f34
XL
1512 (bx.trunc(args[2].immediate(), i1xn), i1xn)
1513 };
1514
a1dfa0c6 1515 let ret_t = bx.type_void();
94b46f34
XL
1516
1517 // Type of the vector of pointers:
a1dfa0c6 1518 let llvm_pointer_vec_ty = llvm_vector_ty(bx, underlying_ty, in_len, pointer_count);
c295e0f8 1519 let llvm_pointer_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count, bx);
94b46f34
XL
1520
1521 // Type of the vector of elements:
a1dfa0c6 1522 let llvm_elem_vec_ty = llvm_vector_ty(bx, underlying_ty, in_len, pointer_count - 1);
c295e0f8 1523 let llvm_elem_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count - 1, bx);
94b46f34 1524
dfeec247
XL
1525 let llvm_intrinsic =
1526 format!("llvm.masked.scatter.{}.{}", llvm_elem_vec_str, llvm_pointer_vec_str);
94222f64
XL
1527 let fn_ty =
1528 bx.type_func(&[llvm_elem_vec_ty, llvm_pointer_vec_ty, alignment_ty, mask_ty], ret_t);
1529 let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
1530 let v =
1531 bx.call(fn_ty, f, &[args[0].immediate(), args[1].immediate(), alignment, mask], None);
94b46f34
XL
1532 return Ok(v);
1533 }
1534
0531ce1d 1535 macro_rules! arith_red {
3dfed10e
XL
1536 ($name:ident : $integer_reduce:ident, $float_reduce:ident, $ordered:expr, $op:ident,
1537 $identity:expr) => {
1538 if name == sym::$name {
dfeec247
XL
1539 require!(
1540 ret_ty == in_elem,
1541 "expected return type `{}` (element of input `{}`), found `{}`",
1542 in_elem,
1543 in_ty,
1544 ret_ty
1545 );
1b1a35ee 1546 return match in_elem.kind() {
b7449926 1547 ty::Int(_) | ty::Uint(_) => {
0531ce1d
XL
1548 let r = bx.$integer_reduce(args[0].immediate());
1549 if $ordered {
1550 // if overflow occurs, the result is the
1551 // mathematical result modulo 2^n:
3dfed10e 1552 Ok(bx.$op(args[1].immediate(), r))
0531ce1d
XL
1553 } else {
1554 Ok(bx.$integer_reduce(args[0].immediate()))
1555 }
dfeec247 1556 }
b7449926 1557 ty::Float(f) => {
0531ce1d 1558 let acc = if $ordered {
416331ca
XL
1559 // ordered arithmetic reductions take an accumulator
1560 args[1].immediate()
0531ce1d 1561 } else {
416331ca 1562 // unordered arithmetic reductions use the identity accumulator
0531ce1d 1563 match f.bit_width() {
3dfed10e
XL
1564 32 => bx.const_real(bx.type_f32(), $identity),
1565 64 => bx.const_real(bx.type_f64(), $identity),
dfeec247
XL
1566 v => return_error!(
1567 r#"
0531ce1d 1568unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#,
3dfed10e 1569 sym::$name,
dfeec247
XL
1570 in_ty,
1571 in_elem,
1572 v,
1573 ret_ty
1574 ),
0531ce1d 1575 }
0531ce1d
XL
1576 };
1577 Ok(bx.$float_reduce(acc, args[0].immediate()))
1578 }
dfeec247
XL
1579 _ => return_error!(
1580 "unsupported {} from `{}` with element `{}` to `{}`",
3dfed10e 1581 sym::$name,
dfeec247
XL
1582 in_ty,
1583 in_elem,
1584 ret_ty
1585 ),
1586 };
0531ce1d 1587 }
dfeec247 1588 };
0531ce1d
XL
1589 }
1590
3dfed10e
XL
1591 arith_red!(simd_reduce_add_ordered: vector_reduce_add, vector_reduce_fadd, true, add, 0.0);
1592 arith_red!(simd_reduce_mul_ordered: vector_reduce_mul, vector_reduce_fmul, true, mul, 1.0);
1593 arith_red!(
1594 simd_reduce_add_unordered: vector_reduce_add,
1595 vector_reduce_fadd_fast,
1596 false,
1597 add,
1598 0.0
1599 );
1600 arith_red!(
1601 simd_reduce_mul_unordered: vector_reduce_mul,
1602 vector_reduce_fmul_fast,
1603 false,
1604 mul,
1605 1.0
1606 );
0531ce1d
XL
1607
1608 macro_rules! minmax_red {
3dfed10e
XL
1609 ($name:ident: $int_red:ident, $float_red:ident) => {
1610 if name == sym::$name {
dfeec247
XL
1611 require!(
1612 ret_ty == in_elem,
1613 "expected return type `{}` (element of input `{}`), found `{}`",
1614 in_elem,
1615 in_ty,
1616 ret_ty
1617 );
1b1a35ee 1618 return match in_elem.kind() {
dfeec247
XL
1619 ty::Int(_i) => Ok(bx.$int_red(args[0].immediate(), true)),
1620 ty::Uint(_u) => Ok(bx.$int_red(args[0].immediate(), false)),
1621 ty::Float(_f) => Ok(bx.$float_red(args[0].immediate())),
1622 _ => return_error!(
1623 "unsupported {} from `{}` with element `{}` to `{}`",
3dfed10e 1624 sym::$name,
dfeec247
XL
1625 in_ty,
1626 in_elem,
1627 ret_ty
1628 ),
1629 };
0531ce1d 1630 }
dfeec247 1631 };
0531ce1d
XL
1632 }
1633
3dfed10e
XL
1634 minmax_red!(simd_reduce_min: vector_reduce_min, vector_reduce_fmin);
1635 minmax_red!(simd_reduce_max: vector_reduce_max, vector_reduce_fmax);
0531ce1d 1636
3dfed10e
XL
1637 minmax_red!(simd_reduce_min_nanless: vector_reduce_min, vector_reduce_fmin_fast);
1638 minmax_red!(simd_reduce_max_nanless: vector_reduce_max, vector_reduce_fmax_fast);
0531ce1d
XL
1639
1640 macro_rules! bitwise_red {
3dfed10e
XL
1641 ($name:ident : $red:ident, $boolean:expr) => {
1642 if name == sym::$name {
0531ce1d 1643 let input = if !$boolean {
dfeec247
XL
1644 require!(
1645 ret_ty == in_elem,
1646 "expected return type `{}` (element of input `{}`), found `{}`",
1647 in_elem,
1648 in_ty,
1649 ret_ty
1650 );
0531ce1d
XL
1651 args[0].immediate()
1652 } else {
1b1a35ee 1653 match in_elem.kind() {
dfeec247
XL
1654 ty::Int(_) | ty::Uint(_) => {}
1655 _ => return_error!(
1656 "unsupported {} from `{}` with element `{}` to `{}`",
3dfed10e 1657 sym::$name,
dfeec247
XL
1658 in_ty,
1659 in_elem,
1660 ret_ty
1661 ),
0531ce1d
XL
1662 }
1663
1664 // boolean reductions operate on vectors of i1s:
a1dfa0c6
XL
1665 let i1 = bx.type_i1();
1666 let i1xn = bx.type_vector(i1, in_len as u64);
0531ce1d
XL
1667 bx.trunc(args[0].immediate(), i1xn)
1668 };
1b1a35ee 1669 return match in_elem.kind() {
b7449926 1670 ty::Int(_) | ty::Uint(_) => {
0531ce1d 1671 let r = bx.$red(input);
dfeec247
XL
1672 Ok(if !$boolean { r } else { bx.zext(r, bx.type_bool()) })
1673 }
1674 _ => return_error!(
1675 "unsupported {} from `{}` with element `{}` to `{}`",
3dfed10e 1676 sym::$name,
dfeec247
XL
1677 in_ty,
1678 in_elem,
1679 ret_ty
1680 ),
1681 };
0531ce1d 1682 }
dfeec247 1683 };
0531ce1d
XL
1684 }
1685
3dfed10e
XL
1686 bitwise_red!(simd_reduce_and: vector_reduce_and, false);
1687 bitwise_red!(simd_reduce_or: vector_reduce_or, false);
1688 bitwise_red!(simd_reduce_xor: vector_reduce_xor, false);
1689 bitwise_red!(simd_reduce_all: vector_reduce_and, true);
1690 bitwise_red!(simd_reduce_any: vector_reduce_or, true);
0531ce1d 1691
3dfed10e 1692 if name == sym::simd_cast {
e9174d1e 1693 require_simd!(ret_ty, "return");
fc512014 1694 let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx());
dfeec247
XL
1695 require!(
1696 in_len == out_len,
1697 "expected return type with length {} (same as input type `{}`), \
e9174d1e 1698 found `{}` with length {}",
dfeec247
XL
1699 in_len,
1700 in_ty,
1701 ret_ty,
1702 out_len
1703 );
e9174d1e 1704 // casting cares about nominal type, not just structural type
dfeec247
XL
1705 if in_elem == out_elem {
1706 return Ok(args[0].immediate());
1707 }
e9174d1e 1708
dfeec247
XL
1709 enum Style {
1710 Float,
1711 Int(/* is signed? */ bool),
1712 Unsupported,
1713 }
e9174d1e 1714
1b1a35ee 1715 let (in_style, in_width) = match in_elem.kind() {
e9174d1e
SL
1716 // vectors of pointer-sized integers should've been
1717 // disallowed before here, so this unwrap is safe.
b7449926
XL
1718 ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()),
1719 ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()),
1720 ty::Float(f) => (Style::Float, f.bit_width()),
dfeec247 1721 _ => (Style::Unsupported, 0),
e9174d1e 1722 };
1b1a35ee 1723 let (out_style, out_width) = match out_elem.kind() {
b7449926
XL
1724 ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()),
1725 ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()),
1726 ty::Float(f) => (Style::Float, f.bit_width()),
dfeec247 1727 _ => (Style::Unsupported, 0),
e9174d1e
SL
1728 };
1729
1730 match (in_style, out_style) {
1731 (Style::Int(in_is_signed), Style::Int(_)) => {
ff7c6d11 1732 return Ok(match in_width.cmp(&out_width) {
2c00a5a8 1733 Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty),
ff7c6d11 1734 Ordering::Equal => args[0].immediate(),
dfeec247
XL
1735 Ordering::Less => {
1736 if in_is_signed {
1737 bx.sext(args[0].immediate(), llret_ty)
1738 } else {
1739 bx.zext(args[0].immediate(), llret_ty)
1740 }
e9174d1e 1741 }
dfeec247 1742 });
e9174d1e
SL
1743 }
1744 (Style::Int(in_is_signed), Style::Float) => {
ff7c6d11 1745 return Ok(if in_is_signed {
2c00a5a8 1746 bx.sitofp(args[0].immediate(), llret_ty)
e9174d1e 1747 } else {
2c00a5a8 1748 bx.uitofp(args[0].immediate(), llret_ty)
dfeec247 1749 });
e9174d1e
SL
1750 }
1751 (Style::Float, Style::Int(out_is_signed)) => {
ff7c6d11 1752 return Ok(if out_is_signed {
2c00a5a8 1753 bx.fptosi(args[0].immediate(), llret_ty)
e9174d1e 1754 } else {
2c00a5a8 1755 bx.fptoui(args[0].immediate(), llret_ty)
dfeec247 1756 });
e9174d1e
SL
1757 }
1758 (Style::Float, Style::Float) => {
ff7c6d11 1759 return Ok(match in_width.cmp(&out_width) {
2c00a5a8 1760 Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty),
ff7c6d11 1761 Ordering::Equal => args[0].immediate(),
dfeec247
XL
1762 Ordering::Less => bx.fpext(args[0].immediate(), llret_ty),
1763 });
e9174d1e 1764 }
dfeec247 1765 _ => { /* Unsupported. Fallthrough. */ }
e9174d1e 1766 }
dfeec247
XL
1767 require!(
1768 false,
1769 "unsupported cast from `{}` with element `{}` to `{}` with element `{}`",
1770 in_ty,
1771 in_elem,
1772 ret_ty,
1773 out_elem
1774 );
e9174d1e 1775 }
6a06907d 1776 macro_rules! arith_binary {
32a655c1 1777 ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
3dfed10e 1778 $(if name == sym::$name {
1b1a35ee 1779 match in_elem.kind() {
ff7c6d11 1780 $($(ty::$p(_))|* => {
2c00a5a8 1781 return Ok(bx.$call(args[0].immediate(), args[1].immediate()))
ff7c6d11
XL
1782 })*
1783 _ => {},
1784 }
1785 require!(false,
0bf4aa26
XL
1786 "unsupported operation on `{}` with element `{}`",
1787 in_ty,
1788 in_elem)
ff7c6d11 1789 })*
e9174d1e
SL
1790 }
1791 }
6a06907d 1792 arith_binary! {
b7449926
XL
1793 simd_add: Uint, Int => add, Float => fadd;
1794 simd_sub: Uint, Int => sub, Float => fsub;
1795 simd_mul: Uint, Int => mul, Float => fmul;
1796 simd_div: Uint => udiv, Int => sdiv, Float => fdiv;
1797 simd_rem: Uint => urem, Int => srem, Float => frem;
1798 simd_shl: Uint, Int => shl;
1799 simd_shr: Uint => lshr, Int => ashr;
1800 simd_and: Uint, Int => and;
1801 simd_or: Uint, Int => or;
1802 simd_xor: Uint, Int => xor;
1803 simd_fmax: Float => maxnum;
1804 simd_fmin: Float => minnum;
9fa01778 1805
e9174d1e 1806 }
6a06907d
XL
1807 macro_rules! arith_unary {
1808 ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => {
1809 $(if name == sym::$name {
1810 match in_elem.kind() {
1811 $($(ty::$p(_))|* => {
1812 return Ok(bx.$call(args[0].immediate()))
1813 })*
1814 _ => {},
1815 }
1816 require!(false,
1817 "unsupported operation on `{}` with element `{}`",
1818 in_ty,
1819 in_elem)
1820 })*
1821 }
1822 }
1823 arith_unary! {
1824 simd_neg: Int => neg, Float => fneg;
1825 }
9fa01778 1826
3dfed10e 1827 if name == sym::simd_saturating_add || name == sym::simd_saturating_sub {
9fa01778
XL
1828 let lhs = args[0].immediate();
1829 let rhs = args[1].immediate();
3dfed10e 1830 let is_add = name == sym::simd_saturating_add;
9fa01778 1831 let ptr_bits = bx.tcx().data_layout.pointer_size.bits() as _;
1b1a35ee 1832 let (signed, elem_width, elem_ty) = match *in_elem.kind() {
dfeec247
XL
1833 ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_int_from_ty(i)),
1834 ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_uint_from_ty(i)),
9fa01778
XL
1835 _ => {
1836 return_error!(
1837 "expected element type `{}` of vector type `{}` \
1838 to be a signed or unsigned integer type",
fc512014 1839 arg_tys[0].simd_size_and_type(bx.tcx()).1,
dfeec247 1840 arg_tys[0]
9fa01778
XL
1841 );
1842 }
1843 };
1844 let llvm_intrinsic = &format!(
1845 "llvm.{}{}.sat.v{}i{}",
1846 if signed { 's' } else { 'u' },
1847 if is_add { "add" } else { "sub" },
dfeec247
XL
1848 in_len,
1849 elem_width
9fa01778
XL
1850 );
1851 let vec_ty = bx.cx.type_vector(elem_ty, in_len as u64);
1852
94222f64 1853 let fn_ty = bx.type_func(&[vec_ty, vec_ty], vec_ty);
c295e0f8 1854 let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty);
94222f64 1855 let v = bx.call(fn_ty, f, &[lhs, rhs], None);
9fa01778
XL
1856 return Ok(v);
1857 }
1858
54a0048b 1859 span_bug!(span, "unknown SIMD intrinsic");
e9174d1e 1860}
92a42be0 1861
ff7c6d11 1862// Returns the width of an int Ty, and if it's signed or not
92a42be0 1863// Returns None if the type is not an integer
32a655c1
SL
1864// FIXME: there’s multiple of this functions, investigate using some of the already existing
1865// stuffs.
9fa01778 1866fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, bool)> {
1b1a35ee
XL
1867 match ty.kind() {
1868 ty::Int(t) => {
29967ef6 1869 Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), true))
1b1a35ee
XL
1870 }
1871 ty::Uint(t) => {
29967ef6 1872 Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), false))
1b1a35ee 1873 }
54a0048b
SL
1874 _ => None,
1875 }
1876}