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