]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/interpret/intrinsics.rs
New upstream version 1.31.0~beta.4+dfsg1
[rustc.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_value(args[0])?;
119 let rhs = self.read_value(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_value(args[0])?;
137 let r = self.read_value(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_val(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 "transmute" => {
154 self.copy_op_transmute(args[0], dest)?;
155 }
156
157 _ => return Ok(false),
158 }
159
160 Ok(true)
161 }
162
163 /// "Intercept" a function call because we have something special to do for it.
164 /// Returns whether an intercept happened.
165 pub fn hook_fn(
166 &mut self,
167 instance: ty::Instance<'tcx>,
168 args: &[OpTy<'tcx, M::PointerTag>],
169 dest: Option<PlaceTy<'tcx, M::PointerTag>>,
170 ) -> EvalResult<'tcx, bool> {
171 let def_id = instance.def_id();
172 // Some fn calls are actually BinOp intrinsics
173 if let Some((op, oflo)) = self.tcx.is_binop_lang_item(def_id) {
174 let dest = dest.expect("128 lowerings can't diverge");
175 let l = self.read_value(args[0])?;
176 let r = self.read_value(args[1])?;
177 if oflo {
178 self.binop_with_overflow(op, l, r, dest)?;
179 } else {
180 self.binop_ignore_overflow(op, l, r, dest)?;
181 }
182 return Ok(true);
183 } else if Some(def_id) == self.tcx.lang_items().panic_fn() {
184 assert!(args.len() == 1);
185 // &(&'static str, &'static str, u32, u32)
186 let ptr = self.read_value(args[0])?;
187 let place = self.ref_to_mplace(ptr)?;
188 let (msg, file, line, col) = (
189 self.mplace_field(place, 0)?,
190 self.mplace_field(place, 1)?,
191 self.mplace_field(place, 2)?,
192 self.mplace_field(place, 3)?,
193 );
194
195 let msg_place = self.ref_to_mplace(self.read_value(msg.into())?)?;
196 let msg = Symbol::intern(self.read_str(msg_place)?);
197 let file_place = self.ref_to_mplace(self.read_value(file.into())?)?;
198 let file = Symbol::intern(self.read_str(file_place)?);
199 let line = self.read_scalar(line.into())?.to_u32()?;
200 let col = self.read_scalar(col.into())?.to_u32()?;
201 return Err(EvalErrorKind::Panic { msg, file, line, col }.into());
202 } else if Some(def_id) == self.tcx.lang_items().begin_panic_fn() {
203 assert!(args.len() == 2);
204 // &'static str, &(&'static str, u32, u32)
205 let msg = args[0];
206 let ptr = self.read_value(args[1])?;
207 let place = self.ref_to_mplace(ptr)?;
208 let (file, line, col) = (
209 self.mplace_field(place, 0)?,
210 self.mplace_field(place, 1)?,
211 self.mplace_field(place, 2)?,
212 );
213
214 let msg_place = self.ref_to_mplace(self.read_value(msg.into())?)?;
215 let msg = Symbol::intern(self.read_str(msg_place)?);
216 let file_place = self.ref_to_mplace(self.read_value(file.into())?)?;
217 let file = Symbol::intern(self.read_str(file_place)?);
218 let line = self.read_scalar(line.into())?.to_u32()?;
219 let col = self.read_scalar(col.into())?.to_u32()?;
220 return Err(EvalErrorKind::Panic { msg, file, line, col }.into());
221 } else {
222 return Ok(false);
223 }
224 }
225 }