]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/intrinsics.rs
Auto merge of #55650 - nikic:funnel-shift, r=nagisa
[rust.git] / src / librustc_mir / interpret / intrinsics.rs
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.
4 //
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.
10
11 //! Intrinsics and other functions that the miri engine executes without
12 //! looking at their MIR.  Intrinsics/functions supported here are shared by CTFE
13 //! and miri.
14
15 use syntax::symbol::Symbol;
16 use rustc::ty;
17 use rustc::ty::layout::{LayoutOf, Primitive};
18 use rustc::mir::BinOp;
19 use rustc::mir::interpret::{
20     EvalResult, EvalErrorKind, Scalar,
21 };
22
23 use super::{
24     Machine, PlaceTy, OpTy, EvalContext,
25 };
26
27
28 fn numeric_intrinsic<'tcx, Tag>(
29     name: &str,
30     bits: u128,
31     kind: Primitive,
32 ) -> EvalResult<'tcx, Scalar<Tag>> {
33     let size = match kind {
34         Primitive::Int(integer, _) => integer.size(),
35         _ => bug!("invalid `{}` argument: {:?}", name, bits),
36     };
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),
45     };
46     Ok(Scalar::from_uint(bits_out, size))
47 }
48
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(
52         &mut self,
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;
58
59         let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..];
60         match intrinsic_name {
61             "min_align_of" => {
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)?;
66             }
67
68             "needs_drop" => {
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)?;
73             }
74
75             "size_of" => {
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)?;
80             }
81
82             "type_id" => {
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)?;
87             }
88             | "ctpop"
89             | "cttz"
90             | "cttz_nonzero"
91             | "ctlz"
92             | "ctlz_nonzero"
93             | "bswap"
94             | "bitreverse" => {
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))?,
101                 };
102                 let out_val = if intrinsic_name.ends_with("_nonzero") {
103                     if bits == 0 {
104                         return err!(Intrinsic(format!("{} called on 0", intrinsic_name)));
105                     }
106                     numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), bits, kind)?
107                 } else {
108                     numeric_intrinsic(intrinsic_name, bits, kind)?
109                 };
110                 self.write_scalar(out_val, dest)?;
111             }
112             | "overflowing_add"
113             | "overflowing_sub"
114             | "overflowing_mul"
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")
128                 };
129                 if ignore_overflow {
130                     self.binop_ignore_overflow(bin_op, lhs, rhs, dest)?;
131                 } else {
132                     self.binop_with_overflow(bin_op, lhs, rhs, dest)?;
133                 }
134             }
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")
142                 };
143                 let (val, overflowed) = self.binary_op_imm(bin_op, l, r)?;
144                 if overflowed {
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),
149                     ));
150                 }
151                 self.write_scalar(val, dest)?;
152             }
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)
164                 } else {
165                     (val_bits >> shift_bits) | (val_bits << inv_shift_bits)
166                 };
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)?;
170             }
171             "transmute" => {
172                 self.copy_op_transmute(args[0], dest)?;
173             }
174
175             _ => return Ok(false),
176         }
177
178         Ok(true)
179     }
180
181     /// "Intercept" a function call because we have something special to do for it.
182     /// Returns whether an intercept happened.
183     pub fn hook_fn(
184         &mut self,
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])?;
195             if oflo {
196                 self.binop_with_overflow(op, l, r, dest)?;
197             } else {
198                 self.binop_ignore_overflow(op, l, r, dest)?;
199             }
200             return Ok(true);
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)?,
211             );
212
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)
223             let msg = args[0];
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)?,
230             );
231
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());
239         } else {
240             return Ok(false);
241         }
242     }
243 }