]>
Commit | Line | Data |
---|---|---|
b7449926 | 1 | //! Intrinsics and other functions that the miri engine executes without |
9fa01778 | 2 | //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE |
b7449926 XL |
3 | //! and miri. |
4 | ||
5 | use syntax::symbol::Symbol; | |
e74abb32 | 6 | use syntax_pos::Span; |
b7449926 | 7 | use rustc::ty; |
9fa01778 | 8 | use rustc::ty::layout::{LayoutOf, Primitive, Size}; |
e1599b0c XL |
9 | use rustc::ty::subst::SubstsRef; |
10 | use rustc::hir::def_id::DefId; | |
11 | use rustc::ty::TyCtxt; | |
b7449926 | 12 | use rustc::mir::BinOp; |
e1599b0c | 13 | use rustc::mir::interpret::{InterpResult, Scalar, GlobalId, ConstValue}; |
b7449926 XL |
14 | |
15 | use super::{ | |
e74abb32 | 16 | Machine, PlaceTy, OpTy, InterpCx, ImmTy, |
b7449926 XL |
17 | }; |
18 | ||
e74abb32 | 19 | mod caller_location; |
dc9dc135 XL |
20 | mod type_name; |
21 | ||
0bf4aa26 | 22 | fn numeric_intrinsic<'tcx, Tag>( |
b7449926 XL |
23 | name: &str, |
24 | bits: u128, | |
25 | kind: Primitive, | |
dc9dc135 | 26 | ) -> InterpResult<'tcx, Scalar<Tag>> { |
b7449926 XL |
27 | let size = match kind { |
28 | Primitive::Int(integer, _) => integer.size(), | |
29 | _ => bug!("invalid `{}` argument: {:?}", name, bits), | |
30 | }; | |
31 | let extra = 128 - size.bits() as u128; | |
32 | let bits_out = match name { | |
33 | "ctpop" => bits.count_ones() as u128, | |
34 | "ctlz" => bits.leading_zeros() as u128 - extra, | |
35 | "cttz" => (bits << extra).trailing_zeros() as u128 - extra, | |
36 | "bswap" => (bits << extra).swap_bytes(), | |
37 | "bitreverse" => (bits << extra).reverse_bits(), | |
38 | _ => bug!("not a numeric intrinsic: {}", name), | |
39 | }; | |
40 | Ok(Scalar::from_uint(bits_out, size)) | |
41 | } | |
42 | ||
e1599b0c XL |
43 | /// The logic for all nullary intrinsics is implemented here. These intrinsics don't get evaluated |
44 | /// inside an `InterpCx` and instead have their value computed directly from rustc internal info. | |
45 | crate fn eval_nullary_intrinsic<'tcx>( | |
46 | tcx: TyCtxt<'tcx>, | |
47 | param_env: ty::ParamEnv<'tcx>, | |
48 | def_id: DefId, | |
49 | substs: SubstsRef<'tcx>, | |
50 | ) -> InterpResult<'tcx, &'tcx ty::Const<'tcx>> { | |
51 | let tp_ty = substs.type_at(0); | |
52 | let name = &*tcx.item_name(def_id).as_str(); | |
53 | Ok(match name { | |
54 | "type_name" => { | |
55 | let alloc = type_name::alloc_type_name(tcx, tp_ty); | |
56 | tcx.mk_const(ty::Const { | |
57 | val: ConstValue::Slice { | |
58 | data: alloc, | |
59 | start: 0, | |
60 | end: alloc.len(), | |
61 | }, | |
62 | ty: tcx.mk_static_str(), | |
63 | }) | |
64 | }, | |
65 | "needs_drop" => ty::Const::from_bool(tcx, tp_ty.needs_drop(tcx, param_env)), | |
66 | "size_of" | | |
67 | "min_align_of" | | |
68 | "pref_align_of" => { | |
69 | let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(e)))?; | |
70 | let n = match name { | |
71 | "pref_align_of" => layout.align.pref.bytes(), | |
72 | "min_align_of" => layout.align.abi.bytes(), | |
73 | "size_of" => layout.size.bytes(), | |
74 | _ => bug!(), | |
75 | }; | |
76 | ty::Const::from_usize(tcx, n) | |
77 | }, | |
78 | "type_id" => ty::Const::from_bits( | |
79 | tcx, | |
80 | tcx.type_id_hash(tp_ty).into(), | |
81 | param_env.and(tcx.types.u64), | |
82 | ), | |
83 | other => bug!("`{}` is not a zero arg intrinsic", other), | |
84 | }) | |
85 | } | |
86 | ||
416331ca | 87 | impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { |
9fa01778 | 88 | /// Returns `true` if emulation happened. |
b7449926 XL |
89 | pub fn emulate_intrinsic( |
90 | &mut self, | |
e74abb32 | 91 | span: Span, |
b7449926 | 92 | instance: ty::Instance<'tcx>, |
0bf4aa26 XL |
93 | args: &[OpTy<'tcx, M::PointerTag>], |
94 | dest: PlaceTy<'tcx, M::PointerTag>, | |
dc9dc135 | 95 | ) -> InterpResult<'tcx, bool> { |
b7449926 XL |
96 | let substs = instance.substs; |
97 | ||
98 | let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..]; | |
99 | match intrinsic_name { | |
e74abb32 XL |
100 | "caller_location" => { |
101 | let caller = self.tcx.sess.source_map().lookup_char_pos(span.lo()); | |
102 | let location = self.alloc_caller_location( | |
103 | Symbol::intern(&caller.file.name.to_string()), | |
104 | caller.line as u32, | |
105 | caller.col_display as u32 + 1, | |
106 | )?; | |
107 | self.write_scalar(location.ptr, dest)?; | |
108 | } | |
109 | ||
e1599b0c XL |
110 | "min_align_of" | |
111 | "pref_align_of" | | |
112 | "needs_drop" | | |
113 | "size_of" | | |
114 | "type_id" | | |
dc9dc135 | 115 | "type_name" => { |
e1599b0c XL |
116 | let gid = GlobalId { |
117 | instance, | |
118 | promoted: None, | |
119 | }; | |
120 | let val = self.tcx.const_eval(self.param_env.and(gid))?; | |
121 | let val = self.eval_const_to_op(val, None)?; | |
122 | self.copy_op(val, dest)?; | |
dc9dc135 XL |
123 | } |
124 | ||
b7449926 XL |
125 | | "ctpop" |
126 | | "cttz" | |
127 | | "cttz_nonzero" | |
128 | | "ctlz" | |
129 | | "ctlz_nonzero" | |
130 | | "bswap" | |
131 | | "bitreverse" => { | |
132 | let ty = substs.type_at(0); | |
133 | let layout_of = self.layout_of(ty)?; | |
e1599b0c XL |
134 | let val = self.read_scalar(args[0])?.not_undef()?; |
135 | let bits = self.force_bits(val, layout_of.size)?; | |
b7449926 XL |
136 | let kind = match layout_of.abi { |
137 | ty::layout::Abi::Scalar(ref scalar) => scalar.value, | |
416331ca | 138 | _ => throw_unsup!(TypeNotPrimitive(ty)), |
b7449926 XL |
139 | }; |
140 | let out_val = if intrinsic_name.ends_with("_nonzero") { | |
141 | if bits == 0 { | |
416331ca | 142 | throw_ub_format!("`{}` called on 0", intrinsic_name); |
b7449926 | 143 | } |
0731742a | 144 | numeric_intrinsic(intrinsic_name.trim_end_matches("_nonzero"), bits, kind)? |
b7449926 XL |
145 | } else { |
146 | numeric_intrinsic(intrinsic_name, bits, kind)? | |
147 | }; | |
148 | self.write_scalar(out_val, dest)?; | |
149 | } | |
e1599b0c XL |
150 | | "wrapping_add" |
151 | | "wrapping_sub" | |
152 | | "wrapping_mul" | |
b7449926 XL |
153 | | "add_with_overflow" |
154 | | "sub_with_overflow" | |
155 | | "mul_with_overflow" => { | |
a1dfa0c6 XL |
156 | let lhs = self.read_immediate(args[0])?; |
157 | let rhs = self.read_immediate(args[1])?; | |
b7449926 | 158 | let (bin_op, ignore_overflow) = match intrinsic_name { |
e1599b0c XL |
159 | "wrapping_add" => (BinOp::Add, true), |
160 | "wrapping_sub" => (BinOp::Sub, true), | |
161 | "wrapping_mul" => (BinOp::Mul, true), | |
b7449926 XL |
162 | "add_with_overflow" => (BinOp::Add, false), |
163 | "sub_with_overflow" => (BinOp::Sub, false), | |
164 | "mul_with_overflow" => (BinOp::Mul, false), | |
165 | _ => bug!("Already checked for int ops") | |
166 | }; | |
167 | if ignore_overflow { | |
168 | self.binop_ignore_overflow(bin_op, lhs, rhs, dest)?; | |
169 | } else { | |
170 | self.binop_with_overflow(bin_op, lhs, rhs, dest)?; | |
171 | } | |
172 | } | |
9fa01778 XL |
173 | "saturating_add" | "saturating_sub" => { |
174 | let l = self.read_immediate(args[0])?; | |
175 | let r = self.read_immediate(args[1])?; | |
176 | let is_add = intrinsic_name == "saturating_add"; | |
e1599b0c | 177 | let (val, overflowed, _ty) = self.overflowing_binary_op(if is_add { |
9fa01778 XL |
178 | BinOp::Add |
179 | } else { | |
180 | BinOp::Sub | |
181 | }, l, r)?; | |
182 | let val = if overflowed { | |
183 | let num_bits = l.layout.size.bits(); | |
184 | if l.layout.abi.is_signed() { | |
185 | // For signed ints the saturated value depends on the sign of the first | |
186 | // term since the sign of the second term can be inferred from this and | |
187 | // the fact that the operation has overflowed (if either is 0 no | |
188 | // overflow can occur) | |
e1599b0c | 189 | let first_term: u128 = self.force_bits(l.to_scalar()?, l.layout.size)?; |
9fa01778 XL |
190 | let first_term_positive = first_term & (1 << (num_bits-1)) == 0; |
191 | if first_term_positive { | |
192 | // Negative overflow not possible since the positive first term | |
193 | // can only increase an (in range) negative term for addition | |
194 | // or corresponding negated positive term for subtraction | |
195 | Scalar::from_uint((1u128 << (num_bits - 1)) - 1, // max positive | |
196 | Size::from_bits(num_bits)) | |
197 | } else { | |
198 | // Positive overflow not possible for similar reason | |
199 | // max negative | |
200 | Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) | |
201 | } | |
202 | } else { // unsigned | |
203 | if is_add { | |
204 | // max unsigned | |
205 | Scalar::from_uint(u128::max_value() >> (128 - num_bits), | |
206 | Size::from_bits(num_bits)) | |
207 | } else { // underflow to 0 | |
208 | Scalar::from_uint(0u128, Size::from_bits(num_bits)) | |
209 | } | |
210 | } | |
211 | } else { | |
212 | val | |
213 | }; | |
214 | self.write_scalar(val, dest)?; | |
215 | } | |
b7449926 | 216 | "unchecked_shl" | "unchecked_shr" => { |
a1dfa0c6 XL |
217 | let l = self.read_immediate(args[0])?; |
218 | let r = self.read_immediate(args[1])?; | |
b7449926 XL |
219 | let bin_op = match intrinsic_name { |
220 | "unchecked_shl" => BinOp::Shl, | |
221 | "unchecked_shr" => BinOp::Shr, | |
222 | _ => bug!("Already checked for int ops") | |
223 | }; | |
e1599b0c | 224 | let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, l, r)?; |
b7449926 XL |
225 | if overflowed { |
226 | let layout = self.layout_of(substs.type_at(0))?; | |
e1599b0c | 227 | let r_val = self.force_bits(r.to_scalar()?, layout.size)?; |
416331ca | 228 | throw_ub_format!("Overflowing shift by {} in `{}`", r_val, intrinsic_name); |
b7449926 XL |
229 | } |
230 | self.write_scalar(val, dest)?; | |
231 | } | |
a1dfa0c6 XL |
232 | "rotate_left" | "rotate_right" => { |
233 | // rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW)) | |
234 | // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW)) | |
235 | let layout = self.layout_of(substs.type_at(0))?; | |
e1599b0c XL |
236 | let val = self.read_scalar(args[0])?.not_undef()?; |
237 | let val_bits = self.force_bits(val, layout.size)?; | |
238 | let raw_shift = self.read_scalar(args[1])?.not_undef()?; | |
239 | let raw_shift_bits = self.force_bits(raw_shift, layout.size)?; | |
a1dfa0c6 XL |
240 | let width_bits = layout.size.bits() as u128; |
241 | let shift_bits = raw_shift_bits % width_bits; | |
dc9dc135 | 242 | let inv_shift_bits = (width_bits - shift_bits) % width_bits; |
a1dfa0c6 XL |
243 | let result_bits = if intrinsic_name == "rotate_left" { |
244 | (val_bits << shift_bits) | (val_bits >> inv_shift_bits) | |
245 | } else { | |
246 | (val_bits >> shift_bits) | (val_bits << inv_shift_bits) | |
247 | }; | |
248 | let truncated_bits = self.truncate(result_bits, layout); | |
249 | let result = Scalar::from_uint(truncated_bits, layout.size); | |
250 | self.write_scalar(result, dest)?; | |
251 | } | |
e74abb32 XL |
252 | |
253 | "ptr_offset_from" => { | |
254 | let a = self.read_immediate(args[0])?.to_scalar()?.to_ptr()?; | |
255 | let b = self.read_immediate(args[1])?.to_scalar()?.to_ptr()?; | |
256 | if a.alloc_id != b.alloc_id { | |
257 | throw_ub_format!( | |
258 | "ptr_offset_from cannot compute offset of pointers into different \ | |
259 | allocations.", | |
260 | ); | |
261 | } | |
262 | let usize_layout = self.layout_of(self.tcx.types.usize)?; | |
263 | let a_offset = ImmTy::from_uint(a.offset.bytes(), usize_layout); | |
264 | let b_offset = ImmTy::from_uint(b.offset.bytes(), usize_layout); | |
265 | let (val, _overflowed, _ty) = self.overflowing_binary_op( | |
266 | BinOp::Sub, a_offset, b_offset, | |
267 | )?; | |
268 | let pointee_layout = self.layout_of(substs.type_at(0))?; | |
269 | let isize_layout = self.layout_of(self.tcx.types.isize)?; | |
270 | let val = ImmTy::from_scalar(val, isize_layout); | |
271 | let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout); | |
272 | self.exact_div(val, size, dest)?; | |
273 | } | |
274 | ||
b7449926 | 275 | "transmute" => { |
0bf4aa26 | 276 | self.copy_op_transmute(args[0], dest)?; |
b7449926 | 277 | } |
e74abb32 XL |
278 | "simd_insert" => { |
279 | let index = self.read_scalar(args[1])?.to_u32()? as u64; | |
280 | let scalar = args[2]; | |
281 | let input = args[0]; | |
282 | let (len, e_ty) = self.read_vector_ty(input); | |
283 | assert!( | |
284 | index < len, | |
285 | "Index `{}` must be in bounds of vector type `{}`: `[0, {})`", | |
286 | index, e_ty, len | |
287 | ); | |
288 | assert_eq!( | |
289 | input.layout, dest.layout, | |
290 | "Return type `{}` must match vector type `{}`", | |
291 | dest.layout.ty, input.layout.ty | |
292 | ); | |
293 | assert_eq!( | |
294 | scalar.layout.ty, e_ty, | |
295 | "Scalar type `{}` must match vector element type `{}`", | |
296 | scalar.layout.ty, e_ty | |
297 | ); | |
b7449926 | 298 | |
e74abb32 XL |
299 | for i in 0..len { |
300 | let place = self.place_field(dest, i)?; | |
301 | let value = if i == index { | |
302 | scalar | |
303 | } else { | |
304 | self.operand_field(input, i)? | |
305 | }; | |
306 | self.copy_op(value, place)?; | |
307 | } | |
308 | } | |
309 | "simd_extract" => { | |
310 | let index = self.read_scalar(args[1])?.to_u32()? as _; | |
311 | let (len, e_ty) = self.read_vector_ty(args[0]); | |
312 | assert!( | |
313 | index < len, | |
314 | "index `{}` is out-of-bounds of vector type `{}` with length `{}`", | |
315 | index, e_ty, len | |
316 | ); | |
317 | assert_eq!( | |
318 | e_ty, dest.layout.ty, | |
319 | "Return type `{}` must match vector element type `{}`", | |
320 | dest.layout.ty, e_ty | |
321 | ); | |
322 | self.copy_op(self.operand_field(args[0], index)?, dest)?; | |
323 | } | |
b7449926 XL |
324 | _ => return Ok(false), |
325 | } | |
326 | ||
327 | Ok(true) | |
328 | } | |
329 | ||
330 | /// "Intercept" a function call because we have something special to do for it. | |
9fa01778 | 331 | /// Returns `true` if an intercept happened. |
b7449926 XL |
332 | pub fn hook_fn( |
333 | &mut self, | |
334 | instance: ty::Instance<'tcx>, | |
0bf4aa26 | 335 | args: &[OpTy<'tcx, M::PointerTag>], |
416331ca | 336 | _dest: Option<PlaceTy<'tcx, M::PointerTag>>, |
dc9dc135 | 337 | ) -> InterpResult<'tcx, bool> { |
b7449926 | 338 | let def_id = instance.def_id(); |
416331ca | 339 | if Some(def_id) == self.tcx.lang_items().panic_fn() { |
e74abb32 XL |
340 | // &'static str, &core::panic::Location { &'static str, u32, u32 } |
341 | assert!(args.len() == 2); | |
b7449926 | 342 | |
e74abb32 | 343 | let msg_place = self.deref_operand(args[0])?; |
b7449926 | 344 | let msg = Symbol::intern(self.read_str(msg_place)?); |
e74abb32 XL |
345 | |
346 | let location = self.deref_operand(args[1])?; | |
347 | let (file, line, col) = ( | |
348 | self.mplace_field(location, 0)?, | |
349 | self.mplace_field(location, 1)?, | |
350 | self.mplace_field(location, 2)?, | |
351 | ); | |
352 | ||
a1dfa0c6 | 353 | let file_place = self.deref_operand(file.into())?; |
b7449926 XL |
354 | let file = Symbol::intern(self.read_str(file_place)?); |
355 | let line = self.read_scalar(line.into())?.to_u32()?; | |
356 | let col = self.read_scalar(col.into())?.to_u32()?; | |
416331ca | 357 | throw_panic!(Panic { msg, file, line, col }) |
b7449926 XL |
358 | } else if Some(def_id) == self.tcx.lang_items().begin_panic_fn() { |
359 | assert!(args.len() == 2); | |
360 | // &'static str, &(&'static str, u32, u32) | |
361 | let msg = args[0]; | |
a1dfa0c6 | 362 | let place = self.deref_operand(args[1])?; |
b7449926 XL |
363 | let (file, line, col) = ( |
364 | self.mplace_field(place, 0)?, | |
365 | self.mplace_field(place, 1)?, | |
366 | self.mplace_field(place, 2)?, | |
367 | ); | |
368 | ||
a1dfa0c6 | 369 | let msg_place = self.deref_operand(msg.into())?; |
b7449926 | 370 | let msg = Symbol::intern(self.read_str(msg_place)?); |
a1dfa0c6 | 371 | let file_place = self.deref_operand(file.into())?; |
b7449926 XL |
372 | let file = Symbol::intern(self.read_str(file_place)?); |
373 | let line = self.read_scalar(line.into())?.to_u32()?; | |
374 | let col = self.read_scalar(col.into())?.to_u32()?; | |
416331ca | 375 | throw_panic!(Panic { msg, file, line, col }) |
b7449926 XL |
376 | } else { |
377 | return Ok(false); | |
378 | } | |
379 | } | |
e74abb32 XL |
380 | |
381 | pub fn exact_div( | |
382 | &mut self, | |
383 | a: ImmTy<'tcx, M::PointerTag>, | |
384 | b: ImmTy<'tcx, M::PointerTag>, | |
385 | dest: PlaceTy<'tcx, M::PointerTag>, | |
386 | ) -> InterpResult<'tcx> { | |
387 | // Performs an exact division, resulting in undefined behavior where | |
388 | // `x % y != 0` or `y == 0` or `x == T::min_value() && y == -1`. | |
389 | // First, check x % y != 0. | |
390 | if self.binary_op(BinOp::Rem, a, b)?.to_bits()? != 0 { | |
391 | // Then, check if `b` is -1, which is the "min_value / -1" case. | |
392 | let minus1 = Scalar::from_int(-1, dest.layout.size); | |
393 | let b = b.to_scalar().unwrap(); | |
394 | if b == minus1 { | |
395 | throw_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented") | |
396 | } else { | |
397 | throw_ub_format!( | |
398 | "exact_div: {} cannot be divided by {} without remainder", | |
399 | a.to_scalar().unwrap(), | |
400 | b, | |
401 | ) | |
402 | } | |
403 | } | |
404 | self.binop_ignore_overflow(BinOp::Div, a, b, dest) | |
405 | } | |
b7449926 | 406 | } |