]> git.lizzy.rs Git - rust.git/blob - src/shims/intrinsics.rs
use expect_none and unwrap_none where it makes sense
[rust.git] / src / shims / intrinsics.rs
1 use rustc_apfloat::Float;
2 use rustc::mir;
3 use rustc::mir::interpret::{InterpResult, PointerArithmetic};
4 use rustc::ty::layout::{self, LayoutOf, Size, Align};
5 use rustc::ty;
6
7 use crate::{
8     PlaceTy, OpTy, Immediate, Scalar, Tag,
9     OperatorEvalContextExt
10 };
11
12 impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
13 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
14     fn call_intrinsic(
15         &mut self,
16         instance: ty::Instance<'tcx>,
17         args: &[OpTy<'tcx, Tag>],
18         dest: PlaceTy<'tcx, Tag>,
19     ) -> InterpResult<'tcx> {
20         let this = self.eval_context_mut();
21         if this.emulate_intrinsic(instance, args, dest)? {
22             return Ok(());
23         }
24         let tcx = &{this.tcx.tcx};
25         let substs = instance.substs;
26
27         // All these intrinsics take raw pointers, so if we access memory directly
28         // (as opposed to through a place), we have to remember to erase any tag
29         // that might still hang around!
30
31         let intrinsic_name = &*tcx.item_name(instance.def_id()).as_str();
32         match intrinsic_name {
33             "arith_offset" => {
34                 let offset = this.read_scalar(args[1])?.to_isize(this)?;
35                 let ptr = this.read_scalar(args[0])?.not_undef()?;
36
37                 let pointee_ty = substs.type_at(0);
38                 let pointee_size = this.layout_of(pointee_ty)?.size.bytes() as i64;
39                 let offset = offset.overflowing_mul(pointee_size).0;
40                 let result_ptr = ptr.ptr_wrapping_signed_offset(offset, this);
41                 this.write_scalar(result_ptr, dest)?;
42             }
43
44             "assume" => {
45                 let cond = this.read_scalar(args[0])?.to_bool()?;
46                 if !cond {
47                     throw_ub_format!("`assume` intrinsic called with `false`");
48                 }
49             }
50
51             "volatile_load" => {
52                 let place = this.deref_operand(args[0])?;
53                 this.copy_op(place.into(), dest)?;
54             }
55
56             "volatile_store" => {
57                 let place = this.deref_operand(args[0])?;
58                 this.copy_op(args[1], place.into())?;
59             }
60
61             "atomic_load" |
62             "atomic_load_relaxed" |
63             "atomic_load_acq" => {
64                 let place = this.deref_operand(args[0])?;
65                 let val = this.read_scalar(place.into())?; // make sure it fits into a scalar; otherwise it cannot be atomic
66
67                 // Check alignment requirements. Atomics must always be aligned to their size,
68                 // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
69                 // be 8-aligned).
70                 let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
71                 this.memory.check_ptr_access(place.ptr, place.layout.size, align)?;
72
73                 this.write_scalar(val, dest)?;
74             }
75
76             "atomic_store" |
77             "atomic_store_relaxed" |
78             "atomic_store_rel" => {
79                 let place = this.deref_operand(args[0])?;
80                 let val = this.read_scalar(args[1])?; // make sure it fits into a scalar; otherwise it cannot be atomic
81
82                 // Check alignment requirements. Atomics must always be aligned to their size,
83                 // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
84                 // be 8-aligned).
85                 let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
86                 this.memory.check_ptr_access(place.ptr, place.layout.size, align)?;
87
88                 this.write_scalar(val, place.into())?;
89             }
90
91             "atomic_fence_acq" |
92             "atomic_fence_rel" |
93             "atomic_fence_acqrel" |
94             "atomic_fence" => {
95                 // we are inherently singlethreaded and singlecored, this is a nop
96             }
97
98             _ if intrinsic_name.starts_with("atomic_xchg") => {
99                 let place = this.deref_operand(args[0])?;
100                 let new = this.read_scalar(args[1])?;
101                 let old = this.read_scalar(place.into())?;
102
103                 // Check alignment requirements. Atomics must always be aligned to their size,
104                 // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
105                 // be 8-aligned).
106                 let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
107                 this.memory.check_ptr_access(place.ptr, place.layout.size, align)?;
108
109                 this.write_scalar(old, dest)?; // old value is returned
110                 this.write_scalar(new, place.into())?;
111             }
112
113             _ if intrinsic_name.starts_with("atomic_cxchg") => {
114                 let place = this.deref_operand(args[0])?;
115                 let expect_old = this.read_immediate(args[1])?; // read as immediate for the sake of `binary_op()`
116                 let new = this.read_scalar(args[2])?;
117                 let old = this.read_immediate(place.into())?; // read as immediate for the sake of `binary_op()`
118
119                 // Check alignment requirements. Atomics must always be aligned to their size,
120                 // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
121                 // be 8-aligned).
122                 let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
123                 this.memory.check_ptr_access(place.ptr, place.layout.size, align)?;
124
125                 // binary_op will bail if either of them is not a scalar
126                 let eq = this.overflowing_binary_op(mir::BinOp::Eq, old, expect_old)?.0;
127                 let res = Immediate::ScalarPair(old.to_scalar_or_undef(), eq.into());
128                 this.write_immediate(res, dest)?; // old value is returned
129                 // update ptr depending on comparison
130                 if eq.to_bool()? {
131                     this.write_scalar(new, place.into())?;
132                 }
133             }
134
135             "atomic_or" |
136             "atomic_or_acq" |
137             "atomic_or_rel" |
138             "atomic_or_acqrel" |
139             "atomic_or_relaxed" |
140             "atomic_xor" |
141             "atomic_xor_acq" |
142             "atomic_xor_rel" |
143             "atomic_xor_acqrel" |
144             "atomic_xor_relaxed" |
145             "atomic_and" |
146             "atomic_and_acq" |
147             "atomic_and_rel" |
148             "atomic_and_acqrel" |
149             "atomic_and_relaxed" |
150             "atomic_nand" |
151             "atomic_nand_acq" |
152             "atomic_nand_rel" |
153             "atomic_nand_acqrel" |
154             "atomic_nand_relaxed" |
155             "atomic_xadd" |
156             "atomic_xadd_acq" |
157             "atomic_xadd_rel" |
158             "atomic_xadd_acqrel" |
159             "atomic_xadd_relaxed" |
160             "atomic_xsub" |
161             "atomic_xsub_acq" |
162             "atomic_xsub_rel" |
163             "atomic_xsub_acqrel" |
164             "atomic_xsub_relaxed" => {
165                 let place = this.deref_operand(args[0])?;
166                 if !place.layout.ty.is_integral() {
167                     bug!("Atomic arithmetic operations only work on integer types");
168                 }
169                 let rhs = this.read_immediate(args[1])?;
170                 let old = this.read_immediate(place.into())?;
171
172                 // Check alignment requirements. Atomics must always be aligned to their size,
173                 // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
174                 // be 8-aligned).
175                 let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
176                 this.memory.check_ptr_access(place.ptr, place.layout.size, align)?;
177
178                 this.write_immediate(*old, dest)?; // old value is returned
179                 let (op, neg) = match intrinsic_name.split('_').nth(1).unwrap() {
180                     "or" => (mir::BinOp::BitOr, false),
181                     "xor" => (mir::BinOp::BitXor, false),
182                     "and" => (mir::BinOp::BitAnd, false),
183                     "xadd" => (mir::BinOp::Add, false),
184                     "xsub" => (mir::BinOp::Sub, false),
185                     "nand" => (mir::BinOp::BitAnd, true),
186                     _ => bug!(),
187                 };
188                 // Atomics wrap around on overflow.
189                 let val = this.binary_op(op, old, rhs)?;
190                 let val = if neg {
191                     this.unary_op(mir::UnOp::Not, val)?
192                 } else {
193                     val
194                 };
195                 this.write_immediate(*val, place.into())?;
196             }
197
198             "breakpoint" => unimplemented!(), // halt miri
199
200             "copy" |
201             "copy_nonoverlapping" => {
202                 let elem_ty = substs.type_at(0);
203                 let elem_layout = this.layout_of(elem_ty)?;
204                 let elem_size = elem_layout.size.bytes();
205                 let count = this.read_scalar(args[2])?.to_usize(this)?;
206                 let elem_align = elem_layout.align.abi;
207
208                 let size = Size::from_bytes(count * elem_size);
209                 let src = this.read_scalar(args[0])?.not_undef()?;
210                 let src = this.memory.check_ptr_access(src, size, elem_align)?;
211                 let dest = this.read_scalar(args[1])?.not_undef()?;
212                 let dest = this.memory.check_ptr_access(dest, size, elem_align)?;
213
214                 if let (Some(src), Some(dest)) = (src, dest) {
215                     this.memory.copy(
216                         src,
217                         dest,
218                         size,
219                         intrinsic_name.ends_with("_nonoverlapping"),
220                     )?;
221                 }
222             }
223
224             "discriminant_value" => {
225                 let place = this.deref_operand(args[0])?;
226                 let discr_val = this.read_discriminant(place.into())?.0;
227                 this.write_scalar(Scalar::from_uint(discr_val, dest.layout.size), dest)?;
228             }
229
230             "sinf32" | "fabsf32" | "cosf32" | "sqrtf32" | "expf32" | "exp2f32" | "logf32" |
231             "log10f32" | "log2f32" | "floorf32" | "ceilf32" | "truncf32" | "roundf32" => {
232                 // FIXME: Using host floats.
233                 let f = f32::from_bits(this.read_scalar(args[0])?.to_u32()?);
234                 let f = match intrinsic_name {
235                     "sinf32" => f.sin(),
236                     "fabsf32" => f.abs(),
237                     "cosf32" => f.cos(),
238                     "sqrtf32" => f.sqrt(),
239                     "expf32" => f.exp(),
240                     "exp2f32" => f.exp2(),
241                     "logf32" => f.ln(),
242                     "log10f32" => f.log10(),
243                     "log2f32" => f.log2(),
244                     "floorf32" => f.floor(),
245                     "ceilf32" => f.ceil(),
246                     "truncf32" => f.trunc(),
247                     "roundf32" => f.round(),
248                     _ => bug!(),
249                 };
250                 this.write_scalar(Scalar::from_u32(f.to_bits()), dest)?;
251             }
252
253             "sinf64" | "fabsf64" | "cosf64" | "sqrtf64" | "expf64" | "exp2f64" | "logf64" |
254             "log10f64" | "log2f64" | "floorf64" | "ceilf64" | "truncf64" | "roundf64" => {
255                 // FIXME: Using host floats.
256                 let f = f64::from_bits(this.read_scalar(args[0])?.to_u64()?);
257                 let f = match intrinsic_name {
258                     "sinf64" => f.sin(),
259                     "fabsf64" => f.abs(),
260                     "cosf64" => f.cos(),
261                     "sqrtf64" => f.sqrt(),
262                     "expf64" => f.exp(),
263                     "exp2f64" => f.exp2(),
264                     "logf64" => f.ln(),
265                     "log10f64" => f.log10(),
266                     "log2f64" => f.log2(),
267                     "floorf64" => f.floor(),
268                     "ceilf64" => f.ceil(),
269                     "truncf64" => f.trunc(),
270                     "roundf64" => f.round(),
271                     _ => bug!(),
272                 };
273                 this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?;
274             }
275
276             "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => {
277                 let a = this.read_immediate(args[0])?;
278                 let b = this.read_immediate(args[1])?;
279                 let op = match intrinsic_name {
280                     "fadd_fast" => mir::BinOp::Add,
281                     "fsub_fast" => mir::BinOp::Sub,
282                     "fmul_fast" => mir::BinOp::Mul,
283                     "fdiv_fast" => mir::BinOp::Div,
284                     "frem_fast" => mir::BinOp::Rem,
285                     _ => bug!(),
286                 };
287                 this.binop_ignore_overflow(op, a, b, dest)?;
288             }
289
290             "minnumf32" | "maxnumf32" => {
291                 let a = this.read_scalar(args[0])?.to_f32()?;
292                 let b = this.read_scalar(args[1])?.to_f32()?;
293                 let res = if intrinsic_name.starts_with("min") {
294                     a.min(b)
295                 } else {
296                     a.max(b)
297                 };
298                 this.write_scalar(Scalar::from_f32(res), dest)?;
299             }
300
301             "minnumf64" | "maxnumf64" => {
302                 let a = this.read_scalar(args[0])?.to_f64()?;
303                 let b = this.read_scalar(args[1])?.to_f64()?;
304                 let res = if intrinsic_name.starts_with("min") {
305                     a.min(b)
306                 } else {
307                     a.max(b)
308                 };
309                 this.write_scalar(Scalar::from_f64(res), dest)?;
310             }
311
312             "exact_div" => {
313                 // Performs an exact division, resulting in undefined behavior where
314                 // `x % y != 0` or `y == 0` or `x == T::min_value() && y == -1`
315                 let a = this.read_immediate(args[0])?;
316                 let b = this.read_immediate(args[1])?;
317                 // check x % y != 0
318                 if this.overflowing_binary_op(mir::BinOp::Rem, a, b)?.0.to_bits(dest.layout.size)? != 0 {
319                     // Check if `b` is -1, which is the "min_value / -1" case.
320                     let minus1 = Scalar::from_int(-1, dest.layout.size);
321                     return Err(if b.to_scalar().unwrap() == minus1 {
322                         err_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented")
323                     } else {
324                         err_ub_format!("exact_div: {:?} cannot be divided by {:?} without remainder", *a, *b)
325                     }.into());
326                 }
327                 this.binop_ignore_overflow(mir::BinOp::Div, a, b, dest)?;
328             },
329
330             "forget" => {}
331
332             "likely" | "unlikely" => {
333                 // These just return their argument
334                 let b = this.read_immediate(args[0])?;
335                 this.write_immediate(*b, dest)?;
336             }
337
338             "init" => {
339                 // Check fast path: we don't want to force an allocation in case the destination is a simple value,
340                 // but we also do not want to create a new allocation with 0s and then copy that over.
341                 // FIXME: We do not properly validate in case of ZSTs and when doing it in memory!
342                 // However, this only affects direct calls of the intrinsic; calls to the stable
343                 // functions wrapping them do get their validation.
344                 // FIXME: should we check that the destination pointer is aligned even for ZSTs?
345                 if !dest.layout.is_zst() {
346                     match dest.layout.abi {
347                         layout::Abi::Scalar(ref s) => {
348                             let x = Scalar::from_int(0, s.value.size(this));
349                             this.write_scalar(x, dest)?;
350                         }
351                         layout::Abi::ScalarPair(ref s1, ref s2) => {
352                             let x = Scalar::from_int(0, s1.value.size(this));
353                             let y = Scalar::from_int(0, s2.value.size(this));
354                             this.write_immediate(Immediate::ScalarPair(x.into(), y.into()), dest)?;
355                         }
356                         _ => {
357                             // Do it in memory
358                             let mplace = this.force_allocation(dest)?;
359                             mplace.meta.unwrap_none();
360                             // not a zst, must be valid pointer
361                             let ptr = mplace.ptr.to_ptr()?;
362                             // we know the return place is in-bounds
363                             this.memory.get_mut(ptr.alloc_id)?.write_repeat(tcx, ptr, 0, dest.layout.size)?;
364                         }
365                     }
366                 }
367             }
368
369             "pref_align_of" => {
370                 let ty = substs.type_at(0);
371                 let layout = this.layout_of(ty)?;
372                 let align = layout.align.pref.bytes();
373                 let ptr_size = this.pointer_size();
374                 let align_val = Scalar::from_uint(align as u128, ptr_size);
375                 this.write_scalar(align_val, dest)?;
376             }
377
378             "move_val_init" => {
379                 let place = this.deref_operand(args[0])?;
380                 this.copy_op(args[1], place.into())?;
381             }
382
383             "offset" => {
384                 let offset = this.read_scalar(args[1])?.to_isize(this)?;
385                 let ptr = this.read_scalar(args[0])?.not_undef()?;
386                 let result_ptr = this.pointer_offset_inbounds(ptr, substs.type_at(0), offset)?;
387                 this.write_scalar(result_ptr, dest)?;
388             }
389
390             "panic_if_uninhabited" => {
391                 let ty = substs.type_at(0);
392                 let layout = this.layout_of(ty)?;
393                 if layout.abi.is_uninhabited() {
394                     throw_ub_format!("Trying to instantiate uninhabited type {}", ty)
395                 }
396             }
397
398             "powf32" => {
399                 // FIXME: Using host floats.
400                 let f = f32::from_bits(this.read_scalar(args[0])?.to_u32()?);
401                 let f2 = f32::from_bits(this.read_scalar(args[1])?.to_u32()?);
402                 this.write_scalar(
403                     Scalar::from_u32(f.powf(f2).to_bits()),
404                     dest,
405                 )?;
406             }
407
408             "powf64" => {
409                 // FIXME: Using host floats.
410                 let f = f64::from_bits(this.read_scalar(args[0])?.to_u64()?);
411                 let f2 = f64::from_bits(this.read_scalar(args[1])?.to_u64()?);
412                 this.write_scalar(
413                     Scalar::from_u64(f.powf(f2).to_bits()),
414                     dest,
415                 )?;
416             }
417
418             "fmaf32" => {
419                 let a = this.read_scalar(args[0])?.to_f32()?;
420                 let b = this.read_scalar(args[1])?.to_f32()?;
421                 let c = this.read_scalar(args[2])?.to_f32()?;
422                 let res = a.mul_add(b, c).value;
423                 this.write_scalar(
424                     Scalar::from_f32(res),
425                     dest,
426                 )?;
427             }
428
429             "fmaf64" => {
430                 let a = this.read_scalar(args[0])?.to_f64()?;
431                 let b = this.read_scalar(args[1])?.to_f64()?;
432                 let c = this.read_scalar(args[2])?.to_f64()?;
433                 let res = a.mul_add(b, c).value;
434                 this.write_scalar(
435                     Scalar::from_f64(res),
436                     dest,
437                 )?;
438             }
439
440             "powif32" => {
441                 // FIXME: Using host floats.
442                 let f = f32::from_bits(this.read_scalar(args[0])?.to_u32()?);
443                 let i = this.read_scalar(args[1])?.to_i32()?;
444                 this.write_scalar(
445                     Scalar::from_u32(f.powi(i).to_bits()),
446                     dest,
447                 )?;
448             }
449
450             "powif64" => {
451                 // FIXME: Using host floats.
452                 let f = f64::from_bits(this.read_scalar(args[0])?.to_u64()?);
453                 let i = this.read_scalar(args[1])?.to_i32()?;
454                 this.write_scalar(
455                     Scalar::from_u64(f.powi(i).to_bits()),
456                     dest,
457                 )?;
458             }
459
460             "size_of_val" => {
461                 let mplace = this.deref_operand(args[0])?;
462                 let (size, _) = this.size_and_align_of_mplace(mplace)?
463                     .expect("size_of_val called on extern type");
464                 let ptr_size = this.pointer_size();
465                 this.write_scalar(
466                     Scalar::from_uint(size.bytes() as u128, ptr_size),
467                     dest,
468                 )?;
469             }
470
471             "min_align_of_val" |
472             "align_of_val" => {
473                 let mplace = this.deref_operand(args[0])?;
474                 let (_, align) = this.size_and_align_of_mplace(mplace)?
475                     .expect("size_of_val called on extern type");
476                 let ptr_size = this.pointer_size();
477                 this.write_scalar(
478                     Scalar::from_uint(align.bytes(), ptr_size),
479                     dest,
480                 )?;
481             }
482
483             "unchecked_div" => {
484                 let l = this.read_immediate(args[0])?;
485                 let r = this.read_immediate(args[1])?;
486                 let rval = r.to_scalar()?.to_bits(args[1].layout.size)?;
487                 if rval == 0 {
488                     throw_ub_format!("Division by 0 in unchecked_div");
489                 }
490                 this.binop_ignore_overflow(
491                     mir::BinOp::Div,
492                     l,
493                     r,
494                     dest,
495                 )?;
496             }
497
498             "unchecked_rem" => {
499                 let l = this.read_immediate(args[0])?;
500                 let r = this.read_immediate(args[1])?;
501                 let rval = r.to_scalar()?.to_bits(args[1].layout.size)?;
502                 if rval == 0 {
503                     throw_ub_format!("Division by 0 in unchecked_rem");
504                 }
505                 this.binop_ignore_overflow(
506                     mir::BinOp::Rem,
507                     l,
508                     r,
509                     dest,
510                 )?;
511             }
512
513             "unchecked_add" | "unchecked_sub" | "unchecked_mul" => {
514                 let l = this.read_immediate(args[0])?;
515                 let r = this.read_immediate(args[1])?;
516                 let op = match intrinsic_name {
517                     "unchecked_add" => mir::BinOp::Add,
518                     "unchecked_sub" => mir::BinOp::Sub,
519                     "unchecked_mul" => mir::BinOp::Mul,
520                     _ => bug!(),
521                 };
522                 let (res, overflowed, _ty) = this.overflowing_binary_op(op, l, r)?;
523                 if overflowed {
524                     throw_ub_format!("Overflowing arithmetic in {}", intrinsic_name);
525                 }
526                 this.write_scalar(res, dest)?;
527             }
528
529             "uninit" => {
530                 // Check fast path: we don't want to force an allocation in case the destination is a simple value,
531                 // but we also do not want to create a new allocation with 0s and then copy that over.
532                 // FIXME: We do not properly validate in case of ZSTs and when doing it in memory!
533                 // However, this only affects direct calls of the intrinsic; calls to the stable
534                 // functions wrapping them do get their validation.
535                 // FIXME: should we check alignment for ZSTs?
536                 use crate::ScalarMaybeUndef;
537                 if !dest.layout.is_zst() {
538                     match dest.layout.abi {
539                         layout::Abi::Scalar(..) => {
540                             let x = ScalarMaybeUndef::Undef;
541                             this.write_immediate(Immediate::Scalar(x), dest)?;
542                         }
543                         layout::Abi::ScalarPair(..) => {
544                             let x = ScalarMaybeUndef::Undef;
545                             this.write_immediate(Immediate::ScalarPair(x, x), dest)?;
546                         }
547                         _ => {
548                             // Do it in memory
549                             let mplace = this.force_allocation(dest)?;
550                             mplace.meta.unwrap_none();
551                             let ptr = mplace.ptr.to_ptr()?;
552                             // We know the return place is in-bounds
553                             this.memory
554                                 .get_mut(ptr.alloc_id)?
555                                 .mark_definedness(ptr, dest.layout.size, false);
556                         }
557                     }
558                 }
559             }
560
561             "write_bytes" => {
562                 let ty = substs.type_at(0);
563                 let ty_layout = this.layout_of(ty)?;
564                 let val_byte = this.read_scalar(args[1])?.to_u8()?;
565                 let ptr = this.read_scalar(args[0])?.not_undef()?;
566                 let count = this.read_scalar(args[2])?.to_usize(this)?;
567                 let byte_count = ty_layout.size * count;
568                 match this.memory.check_ptr_access(ptr, byte_count, ty_layout.align.abi)? {
569                     Some(ptr) => {
570                         this.memory
571                             .get_mut(ptr.alloc_id)?
572                             .write_repeat(tcx, ptr, val_byte, byte_count)?;
573                     }
574                     None => {
575                         // Size is 0, nothing to do.
576                     }
577                 }
578             }
579
580             name => throw_unsup_format!("unimplemented intrinsic: {}", name),
581         }
582
583         Ok(())
584     }
585 }