Skip to content

Commit 9d3fdee

Browse files
committed
Auto merge of #856 - RalfJung:type_dispatch_first, r=RalfJung
Adjust for ptr_op changes This is the Miri side of rust-lang/rust#62946.
2 parents 247786d + 5efacf6 commit 9d3fdee

6 files changed

+29
-224
lines changed

rust-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
d7270712cb446aad0817040bbca73a8d024f67b0
1+
8e917f48382c6afaf50568263b89d35fba5d98e4

src/machine.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,13 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
179179
}
180180

181181
#[inline(always)]
182-
fn ptr_op(
182+
fn binary_ptr_op(
183183
ecx: &rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
184184
bin_op: mir::BinOp,
185185
left: ImmTy<'tcx, Tag>,
186186
right: ImmTy<'tcx, Tag>,
187187
) -> InterpResult<'tcx, (Scalar<Tag>, bool)> {
188-
ecx.ptr_op(bin_op, left, right)
188+
ecx.binary_ptr_op(bin_op, left, right)
189189
}
190190

191191
fn box_alloc(

src/operator.rs

+25-206
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc::ty::{Ty, layout::{Size, LayoutOf}};
1+
use rustc::ty::{Ty, layout::LayoutOf};
22
use rustc::mir;
33

44
use crate::*;
@@ -9,21 +9,13 @@ pub trait EvalContextExt<'tcx> {
99
ptr: Pointer<Tag>
1010
) -> InterpResult<'tcx>;
1111

12-
fn ptr_op(
12+
fn binary_ptr_op(
1313
&self,
1414
bin_op: mir::BinOp,
1515
left: ImmTy<'tcx, Tag>,
1616
right: ImmTy<'tcx, Tag>,
1717
) -> InterpResult<'tcx, (Scalar<Tag>, bool)>;
1818

19-
fn ptr_int_arithmetic(
20-
&self,
21-
bin_op: mir::BinOp,
22-
left: Pointer<Tag>,
23-
right: u128,
24-
signed: bool,
25-
) -> InterpResult<'tcx, (Scalar<Tag>, bool)>;
26-
2719
fn ptr_eq(
2820
&self,
2921
left: Scalar<Tag>,
@@ -46,7 +38,7 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
4638
ptr.check_in_alloc(size, CheckInAllocMsg::InboundsTest)
4739
}
4840

49-
fn ptr_op(
41+
fn binary_ptr_op(
5042
&self,
5143
bin_op: mir::BinOp,
5244
left: ImmTy<'tcx, Tag>,
@@ -56,24 +48,9 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
5648

5749
trace!("ptr_op: {:?} {:?} {:?}", *left, bin_op, *right);
5850

59-
// Treat everything of integer *type* at integer *value*.
60-
if left.layout.ty.is_integral() {
61-
// This is actually an integer operation, so dispatch back to the core engine.
62-
// TODO: Once intptrcast is the default, librustc_mir should never even call us
63-
// for integer types.
64-
assert!(right.layout.ty.is_integral());
65-
let l_bits = self.force_bits(left.imm.to_scalar()?, left.layout.size)?;
66-
let r_bits = self.force_bits(right.imm.to_scalar()?, right.layout.size)?;
67-
68-
let left = ImmTy::from_scalar(Scalar::from_uint(l_bits, left.layout.size), left.layout);
69-
let right = ImmTy::from_scalar(Scalar::from_uint(r_bits, left.layout.size), right.layout);
70-
71-
return self.binary_op(bin_op, left, right);
72-
}
73-
74-
// Operations that support fat pointers
75-
match bin_op {
51+
Ok(match bin_op {
7652
Eq | Ne => {
53+
// This supports fat pointers.
7754
let eq = match (*left, *right) {
7855
(Immediate::Scalar(left), Immediate::Scalar(right)) =>
7956
self.ptr_eq(left.not_undef()?, right.not_undef()?)?,
@@ -82,103 +59,38 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
8259
self.ptr_eq(left2.not_undef()?, right2.not_undef()?)?,
8360
_ => bug!("Type system should not allow comparing Scalar with ScalarPair"),
8461
};
85-
return Ok((Scalar::from_bool(if bin_op == Eq { eq } else { !eq }), false));
62+
(Scalar::from_bool(if bin_op == Eq { eq } else { !eq }), false)
8663
}
87-
_ => {},
88-
}
8964

90-
// Now we expect no more fat pointers.
91-
let left_layout = left.layout;
92-
let left = left.to_scalar()?;
93-
let right_layout = right.layout;
94-
let right = right.to_scalar()?;
95-
debug_assert!(left.is_ptr() || right.is_ptr() || bin_op == Offset);
65+
Lt | Le | Gt | Ge => {
66+
// Just compare the integers.
67+
// TODO: Do we really want to *always* do that, even when comparing two live in-bounds pointers?
68+
let left = self.force_bits(left.to_scalar()?, left.layout.size)?;
69+
let right = self.force_bits(right.to_scalar()?, right.layout.size)?;
70+
let res = match bin_op {
71+
Lt => left < right,
72+
Le => left <= right,
73+
Gt => left > right,
74+
Ge => left >= right,
75+
_ => bug!("We already established it has to be one of these operators."),
76+
};
77+
(Scalar::from_bool(res), false)
78+
}
9679

97-
Ok(match bin_op {
9880
Offset => {
99-
let pointee_ty = left_layout.ty
81+
let pointee_ty = left.layout.ty
10082
.builtin_deref(true)
10183
.expect("Offset called on non-ptr type")
10284
.ty;
10385
let ptr = self.pointer_offset_inbounds(
104-
left,
86+
left.to_scalar()?,
10587
pointee_ty,
106-
right.to_isize(self)?,
88+
right.to_scalar()?.to_isize(self)?,
10789
)?;
10890
(ptr, false)
10991
}
110-
// These need both to be pointer, and fail if they are not in the same location
111-
Lt | Le | Gt | Ge | Sub if left.is_ptr() && right.is_ptr() => {
112-
let left = left.to_ptr().expect("we checked is_ptr");
113-
let right = right.to_ptr().expect("we checked is_ptr");
114-
if left.alloc_id == right.alloc_id {
115-
let res = match bin_op {
116-
Lt => left.offset < right.offset,
117-
Le => left.offset <= right.offset,
118-
Gt => left.offset > right.offset,
119-
Ge => left.offset >= right.offset,
120-
Sub => {
121-
// subtract the offsets
122-
let left_offset = Scalar::from_uint(left.offset.bytes(), self.memory().pointer_size());
123-
let right_offset = Scalar::from_uint(right.offset.bytes(), self.memory().pointer_size());
124-
let layout = self.layout_of(self.tcx.types.usize)?;
125-
return self.binary_op(
126-
Sub,
127-
ImmTy::from_scalar(left_offset, layout),
128-
ImmTy::from_scalar(right_offset, layout),
129-
)
130-
}
131-
_ => bug!("We already established it has to be one of these operators."),
132-
};
133-
(Scalar::from_bool(res), false)
134-
} else {
135-
// Both are pointers, but from different allocations.
136-
throw_unsup!(InvalidPointerMath)
137-
}
138-
}
139-
Gt | Ge if left.is_ptr() && right.is_bits() => {
140-
// "ptr >[=] integer" can be tested if the integer is small enough.
141-
let left = left.to_ptr().expect("we checked is_ptr");
142-
let right = right.to_bits(self.memory().pointer_size()).expect("we checked is_bits");
143-
let (_alloc_size, alloc_align) = self.memory()
144-
.get_size_and_align(left.alloc_id, AllocCheck::MaybeDead)
145-
.expect("alloc info with MaybeDead cannot fail");
146-
let min_ptr_val = u128::from(alloc_align.bytes()) + u128::from(left.offset.bytes());
147-
let result = match bin_op {
148-
Gt => min_ptr_val > right,
149-
Ge => min_ptr_val >= right,
150-
_ => bug!(),
151-
};
152-
if result {
153-
// Definitely true!
154-
(Scalar::from_bool(true), false)
155-
} else {
156-
// Sorry, can't tell.
157-
throw_unsup!(InvalidPointerMath)
158-
}
159-
}
160-
// These work if the left operand is a pointer, and the right an integer
161-
Add | BitAnd | Sub | Rem if left.is_ptr() && right.is_bits() => {
162-
// Cast to i128 is fine as we checked the kind to be ptr-sized
163-
self.ptr_int_arithmetic(
164-
bin_op,
165-
left.to_ptr().expect("we checked is_ptr"),
166-
right.to_bits(self.memory().pointer_size()).expect("we checked is_bits"),
167-
right_layout.abi.is_signed(),
168-
)?
169-
}
170-
// Commutative operators also work if the integer is on the left
171-
Add | BitAnd if left.is_bits() && right.is_ptr() => {
172-
// This is a commutative operation, just swap the operands
173-
self.ptr_int_arithmetic(
174-
bin_op,
175-
right.to_ptr().expect("we checked is_ptr"),
176-
left.to_bits(self.memory().pointer_size()).expect("we checked is_bits"),
177-
left_layout.abi.is_signed(),
178-
)?
179-
}
180-
// Nothing else works
181-
_ => throw_unsup!(InvalidPointerMath),
92+
93+
_ => bug!("Invalid operator on pointers: {:?}", bin_op)
18294
})
18395
}
18496

@@ -195,99 +107,6 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
195107
Ok(left == right)
196108
}
197109

198-
fn ptr_int_arithmetic(
199-
&self,
200-
bin_op: mir::BinOp,
201-
left: Pointer<Tag>,
202-
right: u128,
203-
signed: bool,
204-
) -> InterpResult<'tcx, (Scalar<Tag>, bool)> {
205-
use rustc::mir::BinOp::*;
206-
207-
fn map_to_primval((res, over): (Pointer<Tag>, bool)) -> (Scalar<Tag>, bool) {
208-
(Scalar::Ptr(res), over)
209-
}
210-
211-
Ok(match bin_op {
212-
Sub =>
213-
// The only way this can overflow is by underflowing, so signdeness of the right
214-
// operands does not matter.
215-
map_to_primval(left.overflowing_signed_offset(-(right as i128), self)),
216-
Add if signed =>
217-
map_to_primval(left.overflowing_signed_offset(right as i128, self)),
218-
Add if !signed =>
219-
map_to_primval(left.overflowing_offset(Size::from_bytes(right as u64), self)),
220-
221-
BitAnd if !signed => {
222-
let ptr_base_align = self.memory().get_size_and_align(left.alloc_id, AllocCheck::MaybeDead)
223-
.expect("alloc info with MaybeDead cannot fail")
224-
.1.bytes();
225-
let base_mask = {
226-
// FIXME: use `interpret::truncate`, once that takes a `Size` instead of a `Layout`.
227-
let shift = 128 - self.memory().pointer_size().bits();
228-
let value = !(ptr_base_align as u128 - 1);
229-
// Truncate (shift left to drop out leftover values, shift right to fill with zeroes).
230-
(value << shift) >> shift
231-
};
232-
let ptr_size = self.memory().pointer_size();
233-
trace!("ptr BitAnd, align {}, operand {:#010x}, base_mask {:#010x}",
234-
ptr_base_align, right, base_mask);
235-
if right & base_mask == base_mask {
236-
// Case 1: the base address bits are all preserved, i.e., right is all-1 there.
237-
let offset = (left.offset.bytes() as u128 & right) as u64;
238-
(
239-
Scalar::Ptr(Pointer::new_with_tag(
240-
left.alloc_id,
241-
Size::from_bytes(offset),
242-
left.tag,
243-
)),
244-
false,
245-
)
246-
} else if right & base_mask == 0 {
247-
// Case 2: the base address bits are all taken away, i.e., right is all-0 there.
248-
let v = Scalar::from_uint((left.offset.bytes() as u128) & right, ptr_size);
249-
(v, false)
250-
} else {
251-
throw_unsup!(ReadPointerAsBytes);
252-
}
253-
}
254-
255-
Rem if !signed => {
256-
// Doing modulo a divisor of the alignment is allowed.
257-
// (Intuition: modulo a divisor leaks less information.)
258-
let ptr_base_align = self.memory().get_size_and_align(left.alloc_id, AllocCheck::MaybeDead)
259-
.expect("alloc info with MaybeDead cannot fail")
260-
.1.bytes();
261-
let right = right as u64;
262-
let ptr_size = self.memory().pointer_size();
263-
if right == 1 {
264-
// Modulo 1 is always 0.
265-
(Scalar::from_uint(0u32, ptr_size), false)
266-
} else if ptr_base_align % right == 0 {
267-
// The base address would be cancelled out by the modulo operation, so we can
268-
// just take the modulo of the offset.
269-
(
270-
Scalar::from_uint((left.offset.bytes() % right) as u128, ptr_size),
271-
false,
272-
)
273-
} else {
274-
throw_unsup!(ReadPointerAsBytes);
275-
}
276-
}
277-
278-
_ => {
279-
let msg = format!(
280-
"unimplemented binary op on pointer {:?}: {:?}, {:?} ({})",
281-
bin_op,
282-
left,
283-
right,
284-
if signed { "signed" } else { "unsigned" }
285-
);
286-
throw_unsup!(Unimplemented(msg));
287-
}
288-
})
289-
}
290-
291110
/// Raises an error if the offset moves the pointer outside of its allocation.
292111
/// We consider ZSTs their own huge allocation that doesn't overlap with anything (and nothing
293112
/// moves in there because the size is 0). We also consider the NULL pointer its own separate

src/shims/intrinsics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
306306
match dest.layout.abi {
307307
layout::Abi::Scalar(ref s) => {
308308
let x = Scalar::from_int(0, s.value.size(this));
309-
this.write_immediate(Immediate::Scalar(x.into()), dest)?;
309+
this.write_scalar(x, dest)?;
310310
}
311311
layout::Abi::ScalarPair(ref s1, ref s2) => {
312312
let x = Scalar::from_int(0, s1.value.size(this));

tests/compile-fail/pointers_to_different_allocations_are_unorderable.rs

-7
This file was deleted.

tests/compile-fail/ptr_ge_integer.rs

-7
This file was deleted.

0 commit comments

Comments
 (0)