1 //! Intrinsics and other functions that the miri engine executes without
2 //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE
5 use syntax::symbol::Symbol;
7 use rustc::ty::layout::{LayoutOf, Primitive, Size};
9 use rustc::mir::interpret::{
10 EvalResult, EvalErrorKind, Scalar,
14 Machine, PlaceTy, OpTy, EvalContext,
18 fn numeric_intrinsic<'tcx, Tag>(
22 ) -> EvalResult<'tcx, Scalar<Tag>> {
23 let size = match kind {
24 Primitive::Int(integer, _) => integer.size(),
25 _ => bug!("invalid `{}` argument: {:?}", name, bits),
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),
36 Ok(Scalar::from_uint(bits_out, size))
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(
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;
49 let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..];
50 match intrinsic_name {
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)?;
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)?;
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)?;
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)?;
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))?,
92 let out_val = if intrinsic_name.ends_with("_nonzero") {
94 return err!(Intrinsic(format!("{} called on 0", intrinsic_name)));
96 numeric_intrinsic(intrinsic_name.trim_end_matches("_nonzero"), bits, kind)?
98 numeric_intrinsic(intrinsic_name, bits, kind)?
100 self.write_scalar(out_val, dest)?;
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")
120 self.binop_ignore_overflow(bin_op, lhs, rhs, dest)?;
122 self.binop_with_overflow(bin_op, lhs, rhs, dest)?;
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(if is_add {
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))
150 // Positive overflow not possible for similar reason
152 Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits))
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))
166 self.write_scalar(val, dest)?;
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")
176 let (val, overflowed) = self.binary_op(bin_op, l, r)?;
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),
184 self.write_scalar(val, dest)?;
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)
198 (val_bits >> shift_bits) | (val_bits << inv_shift_bits)
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)?;
205 self.copy_op_transmute(args[0], dest)?;
208 _ => return Ok(false),
214 /// "Intercept" a function call because we have something special to do for it.
215 /// Returns `true` if an intercept happened.
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])?;
229 self.binop_with_overflow(op, l, r, dest)?;
231 self.binop_ignore_overflow(op, l, r, dest)?;
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)?,
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)
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)?,
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());