]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/intrinsics.rs
Auto merge of #58341 - alexreg:cosmetic-2-doc-comments, r=steveklabnik
[rust.git] / src / librustc_mir / interpret / intrinsics.rs
1 //! Intrinsics and other functions that the miri engine executes without
2 //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE
3 //! and miri.
4
5 use syntax::symbol::Symbol;
6 use rustc::ty;
7 use rustc::ty::layout::{LayoutOf, Primitive, Size};
8 use rustc::mir::BinOp;
9 use rustc::mir::interpret::{
10     EvalResult, EvalErrorKind, Scalar,
11 };
12
13 use super::{
14     Machine, PlaceTy, OpTy, EvalContext,
15 };
16
17
18 fn numeric_intrinsic<'tcx, Tag>(
19     name: &str,
20     bits: u128,
21     kind: Primitive,
22 ) -> EvalResult<'tcx, Scalar<Tag>> {
23     let size = match kind {
24         Primitive::Int(integer, _) => integer.size(),
25         _ => bug!("invalid `{}` argument: {:?}", name, bits),
26     };
27     let extra = 128 - size.bits() as u128;
28     let bits_out = match name {
29         "ctpop" => bits.count_ones() as u128,
30         "ctlz" => bits.leading_zeros() as u128 - extra,
31         "cttz" => (bits << extra).trailing_zeros() as u128 - extra,
32         "bswap" => (bits << extra).swap_bytes(),
33         "bitreverse" => (bits << extra).reverse_bits(),
34         _ => bug!("not a numeric intrinsic: {}", name),
35     };
36     Ok(Scalar::from_uint(bits_out, size))
37 }
38
39 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
40     /// Returns `true` if emulation happened.
41     pub fn emulate_intrinsic(
42         &mut self,
43         instance: ty::Instance<'tcx>,
44         args: &[OpTy<'tcx, M::PointerTag>],
45         dest: PlaceTy<'tcx, M::PointerTag>,
46     ) -> EvalResult<'tcx, bool> {
47         let substs = instance.substs;
48
49         let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..];
50         match intrinsic_name {
51             "min_align_of" => {
52                 let elem_ty = substs.type_at(0);
53                 let elem_align = self.layout_of(elem_ty)?.align.abi.bytes();
54                 let align_val = Scalar::from_uint(elem_align, dest.layout.size);
55                 self.write_scalar(align_val, dest)?;
56             }
57
58             "needs_drop" => {
59                 let ty = substs.type_at(0);
60                 let ty_needs_drop = ty.needs_drop(self.tcx.tcx, self.param_env);
61                 let val = Scalar::from_bool(ty_needs_drop);
62                 self.write_scalar(val, dest)?;
63             }
64
65             "size_of" => {
66                 let ty = substs.type_at(0);
67                 let size = self.layout_of(ty)?.size.bytes() as u128;
68                 let size_val = Scalar::from_uint(size, dest.layout.size);
69                 self.write_scalar(size_val, dest)?;
70             }
71
72             "type_id" => {
73                 let ty = substs.type_at(0);
74                 let type_id = self.tcx.type_id_hash(ty) as u128;
75                 let id_val = Scalar::from_uint(type_id, dest.layout.size);
76                 self.write_scalar(id_val, dest)?;
77             }
78             | "ctpop"
79             | "cttz"
80             | "cttz_nonzero"
81             | "ctlz"
82             | "ctlz_nonzero"
83             | "bswap"
84             | "bitreverse" => {
85                 let ty = substs.type_at(0);
86                 let layout_of = self.layout_of(ty)?;
87                 let bits = self.read_scalar(args[0])?.to_bits(layout_of.size)?;
88                 let kind = match layout_of.abi {
89                     ty::layout::Abi::Scalar(ref scalar) => scalar.value,
90                     _ => Err(::rustc::mir::interpret::EvalErrorKind::TypeNotPrimitive(ty))?,
91                 };
92                 let out_val = if intrinsic_name.ends_with("_nonzero") {
93                     if bits == 0 {
94                         return err!(Intrinsic(format!("{} called on 0", intrinsic_name)));
95                     }
96                     numeric_intrinsic(intrinsic_name.trim_end_matches("_nonzero"), bits, kind)?
97                 } else {
98                     numeric_intrinsic(intrinsic_name, bits, kind)?
99                 };
100                 self.write_scalar(out_val, dest)?;
101             }
102             | "overflowing_add"
103             | "overflowing_sub"
104             | "overflowing_mul"
105             | "add_with_overflow"
106             | "sub_with_overflow"
107             | "mul_with_overflow" => {
108                 let lhs = self.read_immediate(args[0])?;
109                 let rhs = self.read_immediate(args[1])?;
110                 let (bin_op, ignore_overflow) = match intrinsic_name {
111                     "overflowing_add" => (BinOp::Add, true),
112                     "overflowing_sub" => (BinOp::Sub, true),
113                     "overflowing_mul" => (BinOp::Mul, true),
114                     "add_with_overflow" => (BinOp::Add, false),
115                     "sub_with_overflow" => (BinOp::Sub, false),
116                     "mul_with_overflow" => (BinOp::Mul, false),
117                     _ => bug!("Already checked for int ops")
118                 };
119                 if ignore_overflow {
120                     self.binop_ignore_overflow(bin_op, lhs, rhs, dest)?;
121                 } else {
122                     self.binop_with_overflow(bin_op, lhs, rhs, dest)?;
123                 }
124             }
125             "saturating_add" | "saturating_sub" => {
126                 let l = self.read_immediate(args[0])?;
127                 let r = self.read_immediate(args[1])?;
128                 let is_add = intrinsic_name == "saturating_add";
129                 let (val, overflowed) = self.binary_op_imm(if is_add {
130                     BinOp::Add
131                 } else {
132                     BinOp::Sub
133                 }, l, r)?;
134                 let val = if overflowed {
135                     let num_bits = l.layout.size.bits();
136                     if l.layout.abi.is_signed() {
137                         // For signed ints the saturated value depends on the sign of the first
138                         // term since the sign of the second term can be inferred from this and
139                         // the fact that the operation has overflowed (if either is 0 no
140                         // overflow can occur)
141                         let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?;
142                         let first_term_positive = first_term & (1 << (num_bits-1)) == 0;
143                         if first_term_positive {
144                             // Negative overflow not possible since the positive first term
145                             // can only increase an (in range) negative term for addition
146                             // or corresponding negated positive term for subtraction
147                             Scalar::from_uint((1u128 << (num_bits - 1)) - 1,  // max positive
148                                 Size::from_bits(num_bits))
149                         } else {
150                             // Positive overflow not possible for similar reason
151                             // max negative
152                             Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits))
153                         }
154                     } else {  // unsigned
155                         if is_add {
156                             // max unsigned
157                             Scalar::from_uint(u128::max_value() >> (128 - num_bits),
158                                 Size::from_bits(num_bits))
159                         } else {  // underflow to 0
160                             Scalar::from_uint(0u128, Size::from_bits(num_bits))
161                         }
162                     }
163                 } else {
164                     val
165                 };
166                 self.write_scalar(val, dest)?;
167             }
168             "unchecked_shl" | "unchecked_shr" => {
169                 let l = self.read_immediate(args[0])?;
170                 let r = self.read_immediate(args[1])?;
171                 let bin_op = match intrinsic_name {
172                     "unchecked_shl" => BinOp::Shl,
173                     "unchecked_shr" => BinOp::Shr,
174                     _ => bug!("Already checked for int ops")
175                 };
176                 let (val, overflowed) = self.binary_op_imm(bin_op, l, r)?;
177                 if overflowed {
178                     let layout = self.layout_of(substs.type_at(0))?;
179                     let r_val =  r.to_scalar()?.to_bits(layout.size)?;
180                     return err!(Intrinsic(
181                         format!("Overflowing shift by {} in {}", r_val, intrinsic_name),
182                     ));
183                 }
184                 self.write_scalar(val, dest)?;
185             }
186             "rotate_left" | "rotate_right" => {
187                 // rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
188                 // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
189                 let layout = self.layout_of(substs.type_at(0))?;
190                 let val_bits = self.read_scalar(args[0])?.to_bits(layout.size)?;
191                 let raw_shift_bits = self.read_scalar(args[1])?.to_bits(layout.size)?;
192                 let width_bits = layout.size.bits() as u128;
193                 let shift_bits = raw_shift_bits % width_bits;
194                 let inv_shift_bits = (width_bits - raw_shift_bits) % width_bits;
195                 let result_bits = if intrinsic_name == "rotate_left" {
196                     (val_bits << shift_bits) | (val_bits >> inv_shift_bits)
197                 } else {
198                     (val_bits >> shift_bits) | (val_bits << inv_shift_bits)
199                 };
200                 let truncated_bits = self.truncate(result_bits, layout);
201                 let result = Scalar::from_uint(truncated_bits, layout.size);
202                 self.write_scalar(result, dest)?;
203             }
204             "transmute" => {
205                 self.copy_op_transmute(args[0], dest)?;
206             }
207
208             _ => return Ok(false),
209         }
210
211         Ok(true)
212     }
213
214     /// "Intercept" a function call because we have something special to do for it.
215     /// Returns `true` if an intercept happened.
216     pub fn hook_fn(
217         &mut self,
218         instance: ty::Instance<'tcx>,
219         args: &[OpTy<'tcx, M::PointerTag>],
220         dest: Option<PlaceTy<'tcx, M::PointerTag>>,
221     ) -> EvalResult<'tcx, bool> {
222         let def_id = instance.def_id();
223         // Some fn calls are actually BinOp intrinsics
224         if let Some((op, oflo)) = self.tcx.is_binop_lang_item(def_id) {
225             let dest = dest.expect("128 lowerings can't diverge");
226             let l = self.read_immediate(args[0])?;
227             let r = self.read_immediate(args[1])?;
228             if oflo {
229                 self.binop_with_overflow(op, l, r, dest)?;
230             } else {
231                 self.binop_ignore_overflow(op, l, r, dest)?;
232             }
233             return Ok(true);
234         } else if Some(def_id) == self.tcx.lang_items().panic_fn() {
235             assert!(args.len() == 1);
236             // &(&'static str, &'static str, u32, u32)
237             let place = self.deref_operand(args[0])?;
238             let (msg, file, line, col) = (
239                 self.mplace_field(place, 0)?,
240                 self.mplace_field(place, 1)?,
241                 self.mplace_field(place, 2)?,
242                 self.mplace_field(place, 3)?,
243             );
244
245             let msg_place = self.deref_operand(msg.into())?;
246             let msg = Symbol::intern(self.read_str(msg_place)?);
247             let file_place = self.deref_operand(file.into())?;
248             let file = Symbol::intern(self.read_str(file_place)?);
249             let line = self.read_scalar(line.into())?.to_u32()?;
250             let col = self.read_scalar(col.into())?.to_u32()?;
251             return Err(EvalErrorKind::Panic { msg, file, line, col }.into());
252         } else if Some(def_id) == self.tcx.lang_items().begin_panic_fn() {
253             assert!(args.len() == 2);
254             // &'static str, &(&'static str, u32, u32)
255             let msg = args[0];
256             let place = self.deref_operand(args[1])?;
257             let (file, line, col) = (
258                 self.mplace_field(place, 0)?,
259                 self.mplace_field(place, 1)?,
260                 self.mplace_field(place, 2)?,
261             );
262
263             let msg_place = self.deref_operand(msg.into())?;
264             let msg = Symbol::intern(self.read_str(msg_place)?);
265             let file_place = self.deref_operand(file.into())?;
266             let file = Symbol::intern(self.read_str(file_place)?);
267             let line = self.read_scalar(line.into())?.to_u32()?;
268             let col = self.read_scalar(col.into())?.to_u32()?;
269             return Err(EvalErrorKind::Panic { msg, file, line, col }.into());
270         } else {
271             return Ok(false);
272         }
273     }
274 }