Skip to content

Commit 2111aed

Browse files
committedAug 17, 2019
Auto merge of #63658 - RalfJung:miri-op, r=oli-obk
Refactor Miri ops (unary, binary) to have more types This is the part of #63448 that is just a refactoring. It helps that PR by making it easier to perform machine arithmetic. r? @oli-obk @eddyb
2 parents d65e272 + 5ac2045 commit 2111aed

File tree

9 files changed

+112
-92
lines changed

9 files changed

+112
-92
lines changed
 

‎src/librustc_mir/const_eval.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ use rustc::hir::def::DefKind;
1111
use rustc::hir::def_id::DefId;
1212
use rustc::mir::interpret::{ConstEvalErr, ErrorHandled, ScalarMaybeUndef};
1313
use rustc::mir;
14-
use rustc::ty::{self, TyCtxt};
14+
use rustc::ty::{self, Ty, TyCtxt, subst::Subst};
1515
use rustc::ty::layout::{self, LayoutOf, VariantIdx};
16-
use rustc::ty::subst::Subst;
1716
use rustc::traits::Reveal;
1817
use rustc_data_structures::fx::FxHashMap;
1918

@@ -415,7 +414,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
415414
_bin_op: mir::BinOp,
416415
_left: ImmTy<'tcx>,
417416
_right: ImmTy<'tcx>,
418-
) -> InterpResult<'tcx, (Scalar, bool)> {
417+
) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
419418
Err(
420419
ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
421420
)

‎src/librustc_mir/interpret/intrinsics.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
137137
let l = self.read_immediate(args[0])?;
138138
let r = self.read_immediate(args[1])?;
139139
let is_add = intrinsic_name == "saturating_add";
140-
let (val, overflowed) = self.binary_op(if is_add {
140+
let (val, overflowed, _ty) = self.overflowing_binary_op(if is_add {
141141
BinOp::Add
142142
} else {
143143
BinOp::Sub
@@ -184,7 +184,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
184184
"unchecked_shr" => BinOp::Shr,
185185
_ => bug!("Already checked for int ops")
186186
};
187-
let (val, overflowed) = self.binary_op(bin_op, l, r)?;
187+
let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, l, r)?;
188188
if overflowed {
189189
let layout = self.layout_of(substs.type_at(0))?;
190190
let r_val = r.to_scalar()?.to_bits(layout.size)?;

‎src/librustc_mir/interpret/machine.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::hash::Hash;
77

88
use rustc::hir::def_id::DefId;
99
use rustc::mir;
10-
use rustc::ty::{self, TyCtxt};
10+
use rustc::ty::{self, Ty, TyCtxt};
1111

1212
use super::{
1313
Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
@@ -176,7 +176,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
176176
bin_op: mir::BinOp,
177177
left: ImmTy<'tcx, Self::PointerTag>,
178178
right: ImmTy<'tcx, Self::PointerTag>,
179-
) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool)>;
179+
) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>;
180180

181181
/// Heap allocations via the `box` keyword.
182182
fn box_alloc(

‎src/librustc_mir/interpret/operand.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ impl<'tcx, Tag> Immediate<Tag> {
108108
// as input for binary and cast operations.
109109
#[derive(Copy, Clone, Debug)]
110110
pub struct ImmTy<'tcx, Tag=()> {
111-
pub imm: Immediate<Tag>,
111+
pub(crate) imm: Immediate<Tag>,
112112
pub layout: TyLayout<'tcx>,
113113
}
114114

@@ -155,7 +155,7 @@ impl<Tag> Operand<Tag> {
155155

156156
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
157157
pub struct OpTy<'tcx, Tag=()> {
158-
op: Operand<Tag>,
158+
op: Operand<Tag>, // Keep this private, it helps enforce invariants
159159
pub layout: TyLayout<'tcx>,
160160
}
161161

@@ -187,13 +187,22 @@ impl<'tcx, Tag> From<ImmTy<'tcx, Tag>> for OpTy<'tcx, Tag> {
187187
}
188188
}
189189

190-
impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag>
191-
{
190+
impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> {
192191
#[inline]
193192
pub fn from_scalar(val: Scalar<Tag>, layout: TyLayout<'tcx>) -> Self {
194193
ImmTy { imm: val.into(), layout }
195194
}
196195

196+
#[inline]
197+
pub fn from_uint(i: impl Into<u128>, layout: TyLayout<'tcx>) -> Self {
198+
Self::from_scalar(Scalar::from_uint(i, layout.size), layout)
199+
}
200+
201+
#[inline]
202+
pub fn from_int(i: impl Into<i128>, layout: TyLayout<'tcx>) -> Self {
203+
Self::from_scalar(Scalar::from_int(i, layout.size), layout)
204+
}
205+
197206
#[inline]
198207
pub fn to_bits(self) -> InterpResult<'tcx, u128> {
199208
self.to_scalar()?.to_bits(self.layout.size)

‎src/librustc_mir/interpret/operator.rs

+74-49
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use rustc::mir;
2-
use rustc::ty::{self, layout::TyLayout};
2+
use rustc::ty::{self, Ty, layout::{TyLayout, LayoutOf}};
33
use syntax::ast::FloatTy;
44
use rustc_apfloat::Float;
55
use rustc::mir::interpret::{InterpResult, Scalar};
@@ -17,7 +17,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
1717
right: ImmTy<'tcx, M::PointerTag>,
1818
dest: PlaceTy<'tcx, M::PointerTag>,
1919
) -> InterpResult<'tcx> {
20-
let (val, overflowed) = self.binary_op(op, left, right)?;
20+
let (val, overflowed, ty) = self.overflowing_binary_op(op, left, right)?;
21+
debug_assert_eq!(
22+
self.tcx.intern_tup(&[ty, self.tcx.types.bool]),
23+
dest.layout.ty,
24+
"type mismatch for result of {:?}", op,
25+
);
2126
let val = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into());
2227
self.write_immediate(val, dest)
2328
}
@@ -31,7 +36,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
3136
right: ImmTy<'tcx, M::PointerTag>,
3237
dest: PlaceTy<'tcx, M::PointerTag>,
3338
) -> InterpResult<'tcx> {
34-
let (val, _overflowed) = self.binary_op(op, left, right)?;
39+
let (val, _overflowed, ty) = self.overflowing_binary_op(op, left, right)?;
40+
assert_eq!(ty, dest.layout.ty, "type mismatch for result of {:?}", op);
3541
self.write_scalar(val, dest)
3642
}
3743
}
@@ -42,7 +48,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
4248
bin_op: mir::BinOp,
4349
l: char,
4450
r: char,
45-
) -> (Scalar<M::PointerTag>, bool) {
51+
) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) {
4652
use rustc::mir::BinOp::*;
4753

4854
let res = match bin_op {
@@ -54,15 +60,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
5460
Ge => l >= r,
5561
_ => bug!("Invalid operation on char: {:?}", bin_op),
5662
};
57-
return (Scalar::from_bool(res), false);
63+
return (Scalar::from_bool(res), false, self.tcx.types.bool);
5864
}
5965

6066
fn binary_bool_op(
6167
&self,
6268
bin_op: mir::BinOp,
6369
l: bool,
6470
r: bool,
65-
) -> (Scalar<M::PointerTag>, bool) {
71+
) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) {
6672
use rustc::mir::BinOp::*;
6773

6874
let res = match bin_op {
@@ -77,32 +83,33 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
7783
BitXor => l ^ r,
7884
_ => bug!("Invalid operation on bool: {:?}", bin_op),
7985
};
80-
return (Scalar::from_bool(res), false);
86+
return (Scalar::from_bool(res), false, self.tcx.types.bool);
8187
}
8288

8389
fn binary_float_op<F: Float + Into<Scalar<M::PointerTag>>>(
8490
&self,
8591
bin_op: mir::BinOp,
92+
ty: Ty<'tcx>,
8693
l: F,
8794
r: F,
88-
) -> (Scalar<M::PointerTag>, bool) {
95+
) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) {
8996
use rustc::mir::BinOp::*;
9097

91-
let val = match bin_op {
92-
Eq => Scalar::from_bool(l == r),
93-
Ne => Scalar::from_bool(l != r),
94-
Lt => Scalar::from_bool(l < r),
95-
Le => Scalar::from_bool(l <= r),
96-
Gt => Scalar::from_bool(l > r),
97-
Ge => Scalar::from_bool(l >= r),
98-
Add => (l + r).value.into(),
99-
Sub => (l - r).value.into(),
100-
Mul => (l * r).value.into(),
101-
Div => (l / r).value.into(),
102-
Rem => (l % r).value.into(),
98+
let (val, ty) = match bin_op {
99+
Eq => (Scalar::from_bool(l == r), self.tcx.types.bool),
100+
Ne => (Scalar::from_bool(l != r), self.tcx.types.bool),
101+
Lt => (Scalar::from_bool(l < r), self.tcx.types.bool),
102+
Le => (Scalar::from_bool(l <= r), self.tcx.types.bool),
103+
Gt => (Scalar::from_bool(l > r), self.tcx.types.bool),
104+
Ge => (Scalar::from_bool(l >= r), self.tcx.types.bool),
105+
Add => ((l + r).value.into(), ty),
106+
Sub => ((l - r).value.into(), ty),
107+
Mul => ((l * r).value.into(), ty),
108+
Div => ((l / r).value.into(), ty),
109+
Rem => ((l % r).value.into(), ty),
103110
_ => bug!("invalid float op: `{:?}`", bin_op),
104111
};
105-
return (val, false);
112+
return (val, false, ty);
106113
}
107114

108115
fn binary_int_op(
@@ -113,7 +120,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
113120
left_layout: TyLayout<'tcx>,
114121
r: u128,
115122
right_layout: TyLayout<'tcx>,
116-
) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> {
123+
) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> {
117124
use rustc::mir::BinOp::*;
118125

119126
// Shift ops can have an RHS with a different numeric type.
@@ -142,7 +149,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
142149
}
143150
};
144151
let truncated = self.truncate(result, left_layout);
145-
return Ok((Scalar::from_uint(truncated, size), oflo));
152+
return Ok((Scalar::from_uint(truncated, size), oflo, left_layout.ty));
146153
}
147154

148155
// For the remaining ops, the types must be the same on both sides
@@ -167,7 +174,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
167174
if let Some(op) = op {
168175
let l = self.sign_extend(l, left_layout) as i128;
169176
let r = self.sign_extend(r, right_layout) as i128;
170-
return Ok((Scalar::from_bool(op(&l, &r)), false));
177+
return Ok((Scalar::from_bool(op(&l, &r)), false, self.tcx.types.bool));
171178
}
172179
let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
173180
Div if r == 0 => throw_panic!(DivisionByZero),
@@ -187,7 +194,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
187194
Rem | Div => {
188195
// int_min / -1
189196
if r == -1 && l == (1 << (size.bits() - 1)) {
190-
return Ok((Scalar::from_uint(l, size), true));
197+
return Ok((Scalar::from_uint(l, size), true, left_layout.ty));
191198
}
192199
},
193200
_ => {},
@@ -202,25 +209,24 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
202209
// this may be out-of-bounds for the result type, so we have to truncate ourselves
203210
let result = result as u128;
204211
let truncated = self.truncate(result, left_layout);
205-
return Ok((Scalar::from_uint(truncated, size), oflo));
212+
return Ok((Scalar::from_uint(truncated, size), oflo, left_layout.ty));
206213
}
207214
}
208215

209216
let size = left_layout.size;
210217

211-
// only ints left
212-
let val = match bin_op {
213-
Eq => Scalar::from_bool(l == r),
214-
Ne => Scalar::from_bool(l != r),
218+
let (val, ty) = match bin_op {
219+
Eq => (Scalar::from_bool(l == r), self.tcx.types.bool),
220+
Ne => (Scalar::from_bool(l != r), self.tcx.types.bool),
215221

216-
Lt => Scalar::from_bool(l < r),
217-
Le => Scalar::from_bool(l <= r),
218-
Gt => Scalar::from_bool(l > r),
219-
Ge => Scalar::from_bool(l >= r),
222+
Lt => (Scalar::from_bool(l < r), self.tcx.types.bool),
223+
Le => (Scalar::from_bool(l <= r), self.tcx.types.bool),
224+
Gt => (Scalar::from_bool(l > r), self.tcx.types.bool),
225+
Ge => (Scalar::from_bool(l >= r), self.tcx.types.bool),
220226

221-
BitOr => Scalar::from_uint(l | r, size),
222-
BitAnd => Scalar::from_uint(l & r, size),
223-
BitXor => Scalar::from_uint(l ^ r, size),
227+
BitOr => (Scalar::from_uint(l | r, size), left_layout.ty),
228+
BitAnd => (Scalar::from_uint(l & r, size), left_layout.ty),
229+
BitXor => (Scalar::from_uint(l ^ r, size), left_layout.ty),
224230

225231
Add | Sub | Mul | Rem | Div => {
226232
debug_assert!(!left_layout.abi.is_signed());
@@ -236,7 +242,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
236242
};
237243
let (result, oflo) = op(l, r);
238244
let truncated = self.truncate(result, left_layout);
239-
return Ok((Scalar::from_uint(truncated, size), oflo || truncated != result));
245+
return Ok((
246+
Scalar::from_uint(truncated, size),
247+
oflo || truncated != result,
248+
left_layout.ty,
249+
));
240250
}
241251

242252
_ => {
@@ -250,17 +260,17 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
250260
}
251261
};
252262

253-
Ok((val, false))
263+
Ok((val, false, ty))
254264
}
255265

256-
/// Returns the result of the specified operation and whether it overflowed.
257-
#[inline]
258-
pub fn binary_op(
266+
/// Returns the result of the specified operation, whether it overflowed, and
267+
/// the result type.
268+
pub fn overflowing_binary_op(
259269
&self,
260270
bin_op: mir::BinOp,
261271
left: ImmTy<'tcx, M::PointerTag>,
262272
right: ImmTy<'tcx, M::PointerTag>,
263-
) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> {
273+
) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> {
264274
trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
265275
bin_op, *left, left.layout.ty, *right, right.layout.ty);
266276

@@ -279,11 +289,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
279289
}
280290
ty::Float(fty) => {
281291
assert_eq!(left.layout.ty, right.layout.ty);
292+
let ty = left.layout.ty;
282293
let left = left.to_scalar()?;
283294
let right = right.to_scalar()?;
284295
Ok(match fty {
285-
FloatTy::F32 => self.binary_float_op(bin_op, left.to_f32()?, right.to_f32()?),
286-
FloatTy::F64 => self.binary_float_op(bin_op, left.to_f64()?, right.to_f64()?),
296+
FloatTy::F32 =>
297+
self.binary_float_op(bin_op, ty, left.to_f32()?, right.to_f32()?),
298+
FloatTy::F64 =>
299+
self.binary_float_op(bin_op, ty, left.to_f64()?, right.to_f64()?),
287300
})
288301
}
289302
_ if left.layout.ty.is_integral() => {
@@ -312,11 +325,23 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
312325
}
313326
}
314327

328+
/// Typed version of `checked_binary_op`, returning an `ImmTy`. Also ignores overflows.
329+
#[inline]
330+
pub fn binary_op(
331+
&self,
332+
bin_op: mir::BinOp,
333+
left: ImmTy<'tcx, M::PointerTag>,
334+
right: ImmTy<'tcx, M::PointerTag>,
335+
) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
336+
let (val, _overflow, ty) = self.overflowing_binary_op(bin_op, left, right)?;
337+
Ok(ImmTy::from_scalar(val, self.layout_of(ty)?))
338+
}
339+
315340
pub fn unary_op(
316341
&self,
317342
un_op: mir::UnOp,
318343
val: ImmTy<'tcx, M::PointerTag>,
319-
) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
344+
) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
320345
use rustc::mir::UnOp::*;
321346

322347
let layout = val.layout;
@@ -330,15 +355,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
330355
Not => !val,
331356
_ => bug!("Invalid bool op {:?}", un_op)
332357
};
333-
Ok(Scalar::from_bool(res))
358+
Ok(ImmTy::from_scalar(Scalar::from_bool(res), self.layout_of(self.tcx.types.bool)?))
334359
}
335360
ty::Float(fty) => {
336361
let res = match (un_op, fty) {
337362
(Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
338363
(Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
339364
_ => bug!("Invalid float op {:?}", un_op)
340365
};
341-
Ok(res)
366+
Ok(ImmTy::from_scalar(res, layout))
342367
}
343368
_ => {
344369
assert!(layout.ty.is_integral());
@@ -351,7 +376,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
351376
}
352377
};
353378
// res needs tuncating
354-
Ok(Scalar::from_uint(self.truncate(res, layout), layout.size))
379+
Ok(ImmTy::from_uint(self.truncate(res, layout), layout))
355380
}
356381
}
357382
}

‎src/librustc_mir/interpret/place.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub enum Place<Tag=(), Id=AllocId> {
4545

4646
#[derive(Copy, Clone, Debug)]
4747
pub struct PlaceTy<'tcx, Tag=()> {
48-
place: Place<Tag>,
48+
place: Place<Tag>, // Keep this private, it helps enforce invariants
4949
pub layout: TyLayout<'tcx>,
5050
}
5151

0 commit comments

Comments
 (0)
Please sign in to comment.