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_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")
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_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")
143 let (val
, overflowed
) = self.binary_op_val(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
)?
;
154 self.copy_op_transmute(args
[0], dest
)?
;
157 _
=> return Ok(false),
163 /// "Intercept" a function call because we have something special to do for it.
164 /// Returns whether an intercept happened.
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])?
;
178 self.binop_with_overflow(op
, l
, r
, dest
)?
;
180 self.binop_ignore_overflow(op
, l
, r
, dest
)?
;
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)?
,
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)
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)?
,
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());