1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Intrinsics and other functions that the miri engine executes without
12 //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE
15 use syntax::symbol::Symbol;
17 use rustc::ty::layout::{LayoutOf, Primitive};
18 use rustc::mir::BinOp;
19 use rustc::mir::interpret::{
20 EvalResult, EvalErrorKind, Scalar,
24 Machine, PlaceTy, OpTy, EvalContext,
28 fn numeric_intrinsic<'tcx, Tag>(
32 ) -> EvalResult<'tcx, Scalar<Tag>> {
33 let size = match kind {
34 Primitive::Int(integer, _) => integer.size(),
35 _ => bug!("invalid `{}` argument: {:?}", name, bits),
37 let extra = 128 - size.bits() as u128;
38 let bits_out = match name {
39 "ctpop" => bits.count_ones() as u128,
40 "ctlz" => bits.leading_zeros() as u128 - extra,
41 "cttz" => (bits << extra).trailing_zeros() as u128 - extra,
42 "bswap" => (bits << extra).swap_bytes(),
43 "bitreverse" => (bits << extra).reverse_bits(),
44 _ => bug!("not a numeric intrinsic: {}", name),
46 Ok(Scalar::from_uint(bits_out, size))
49 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
50 /// Returns whether emulation happened.
51 pub fn emulate_intrinsic(
53 instance: ty::Instance<'tcx>,
54 args: &[OpTy<'tcx, M::PointerTag>],
55 dest: PlaceTy<'tcx, M::PointerTag>,
56 ) -> EvalResult<'tcx, bool> {
57 let substs = instance.substs;
59 let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..];
60 match intrinsic_name {
62 let elem_ty = substs.type_at(0);
63 let elem_align = self.layout_of(elem_ty)?.align.abi();
64 let align_val = Scalar::from_uint(elem_align, dest.layout.size);
65 self.write_scalar(align_val, dest)?;
69 let ty = substs.type_at(0);
70 let ty_needs_drop = ty.needs_drop(self.tcx.tcx, self.param_env);
71 let val = Scalar::from_bool(ty_needs_drop);
72 self.write_scalar(val, dest)?;
76 let ty = substs.type_at(0);
77 let size = self.layout_of(ty)?.size.bytes() as u128;
78 let size_val = Scalar::from_uint(size, dest.layout.size);
79 self.write_scalar(size_val, dest)?;
83 let ty = substs.type_at(0);
84 let type_id = self.tcx.type_id_hash(ty) as u128;
85 let id_val = Scalar::from_uint(type_id, dest.layout.size);
86 self.write_scalar(id_val, dest)?;
95 let ty = substs.type_at(0);
96 let layout_of = self.layout_of(ty)?;
97 let bits = self.read_scalar(args[0])?.to_bits(layout_of.size)?;
98 let kind = match layout_of.abi {
99 ty::layout::Abi::Scalar(ref scalar) => scalar.value,
100 _ => Err(::rustc::mir::interpret::EvalErrorKind::TypeNotPrimitive(ty))?,
102 let out_val = if intrinsic_name.ends_with("_nonzero") {
104 return err!(Intrinsic(format!("{} called on 0", intrinsic_name)));
106 numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), bits, kind)?
108 numeric_intrinsic(intrinsic_name, bits, kind)?
110 self.write_scalar(out_val, dest)?;
115 | "add_with_overflow"
116 | "sub_with_overflow"
117 | "mul_with_overflow" => {
118 let lhs = self.read_immediate(args[0])?;
119 let rhs = self.read_immediate(args[1])?;
120 let (bin_op, ignore_overflow) = match intrinsic_name {
121 "overflowing_add" => (BinOp::Add, true),
122 "overflowing_sub" => (BinOp::Sub, true),
123 "overflowing_mul" => (BinOp::Mul, true),
124 "add_with_overflow" => (BinOp::Add, false),
125 "sub_with_overflow" => (BinOp::Sub, false),
126 "mul_with_overflow" => (BinOp::Mul, false),
127 _ => bug!("Already checked for int ops")
130 self.binop_ignore_overflow(bin_op, lhs, rhs, dest)?;
132 self.binop_with_overflow(bin_op, lhs, rhs, dest)?;
135 "unchecked_shl" | "unchecked_shr" => {
136 let l = self.read_immediate(args[0])?;
137 let r = self.read_immediate(args[1])?;
138 let bin_op = match intrinsic_name {
139 "unchecked_shl" => BinOp::Shl,
140 "unchecked_shr" => BinOp::Shr,
141 _ => bug!("Already checked for int ops")
143 let (val, overflowed) = self.binary_op_imm(bin_op, l, r)?;
145 let layout = self.layout_of(substs.type_at(0))?;
146 let r_val = r.to_scalar()?.to_bits(layout.size)?;
147 return err!(Intrinsic(
148 format!("Overflowing shift by {} in {}", r_val, intrinsic_name),
151 self.write_scalar(val, dest)?;
153 "rotate_left" | "rotate_right" => {
154 // rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
155 // rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
156 let layout = self.layout_of(substs.type_at(0))?;
157 let val_bits = self.read_scalar(args[0])?.to_bits(layout.size)?;
158 let raw_shift_bits = self.read_scalar(args[1])?.to_bits(layout.size)?;
159 let width_bits = layout.size.bits() as u128;
160 let shift_bits = raw_shift_bits % width_bits;
161 let inv_shift_bits = (width_bits - raw_shift_bits) % width_bits;
162 let result_bits = if intrinsic_name == "rotate_left" {
163 (val_bits << shift_bits) | (val_bits >> inv_shift_bits)
165 (val_bits >> shift_bits) | (val_bits << inv_shift_bits)
167 let truncated_bits = self.truncate(result_bits, layout);
168 let result = Scalar::from_uint(truncated_bits, layout.size);
169 self.write_scalar(result, dest)?;
172 self.copy_op_transmute(args[0], dest)?;
175 _ => return Ok(false),
181 /// "Intercept" a function call because we have something special to do for it.
182 /// Returns whether an intercept happened.
185 instance: ty::Instance<'tcx>,
186 args: &[OpTy<'tcx, M::PointerTag>],
187 dest: Option<PlaceTy<'tcx, M::PointerTag>>,
188 ) -> EvalResult<'tcx, bool> {
189 let def_id = instance.def_id();
190 // Some fn calls are actually BinOp intrinsics
191 if let Some((op, oflo)) = self.tcx.is_binop_lang_item(def_id) {
192 let dest = dest.expect("128 lowerings can't diverge");
193 let l = self.read_immediate(args[0])?;
194 let r = self.read_immediate(args[1])?;
196 self.binop_with_overflow(op, l, r, dest)?;
198 self.binop_ignore_overflow(op, l, r, dest)?;
201 } else if Some(def_id) == self.tcx.lang_items().panic_fn() {
202 assert!(args.len() == 1);
203 // &(&'static str, &'static str, u32, u32)
204 let ptr = self.read_immediate(args[0])?;
205 let place = self.ref_to_mplace(ptr)?;
206 let (msg, file, line, col) = (
207 self.mplace_field(place, 0)?,
208 self.mplace_field(place, 1)?,
209 self.mplace_field(place, 2)?,
210 self.mplace_field(place, 3)?,
213 let msg_place = self.ref_to_mplace(self.read_immediate(msg.into())?)?;
214 let msg = Symbol::intern(self.read_str(msg_place)?);
215 let file_place = self.ref_to_mplace(self.read_immediate(file.into())?)?;
216 let file = Symbol::intern(self.read_str(file_place)?);
217 let line = self.read_scalar(line.into())?.to_u32()?;
218 let col = self.read_scalar(col.into())?.to_u32()?;
219 return Err(EvalErrorKind::Panic { msg, file, line, col }.into());
220 } else if Some(def_id) == self.tcx.lang_items().begin_panic_fn() {
221 assert!(args.len() == 2);
222 // &'static str, &(&'static str, u32, u32)
224 let ptr = self.read_immediate(args[1])?;
225 let place = self.ref_to_mplace(ptr)?;
226 let (file, line, col) = (
227 self.mplace_field(place, 0)?,
228 self.mplace_field(place, 1)?,
229 self.mplace_field(place, 2)?,
232 let msg_place = self.ref_to_mplace(self.read_immediate(msg.into())?)?;
233 let msg = Symbol::intern(self.read_str(msg_place)?);
234 let file_place = self.ref_to_mplace(self.read_immediate(file.into())?)?;
235 let file = Symbol::intern(self.read_str(file_place)?);
236 let line = self.read_scalar(line.into())?.to_u32()?;
237 let col = self.read_scalar(col.into())?.to_u32()?;
238 return Err(EvalErrorKind::Panic { msg, file, line, col }.into());