Skip to content

Commit 1b5f77e

Browse files
committed
Implement Offset like the other binary operators, share code with the intrinsic
Also improve drop glue tests
1 parent 31cf66d commit 1b5f77e

File tree

4 files changed

+136
-128
lines changed

4 files changed

+136
-128
lines changed

src/eval_context.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -451,18 +451,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
451451
self.write_value(value, dest, dest_ty)?;
452452
}
453453

454-
BinaryOp(mir::BinOp::Offset, ref left, ref right) => {
455-
let pointer_ty = self.operand_ty(left);
456-
let pointee_ty = pointer_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;
457-
// FIXME: assuming here that type size is < i64::max_value()
458-
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
459-
let offset = self.eval_operand_to_primval(right)?.to_i128()? as i64;
460-
461-
let ptr = self.eval_operand_to_primval(left)?.to_ptr()?;
462-
let result_ptr = ptr.signed_offset(offset * pointee_size);
463-
self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?;
464-
}
465-
466454
BinaryOp(bin_op, ref left, ref right) => {
467455
// ignore overflow bit, rustc inserts check branches for us
468456
self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)?;
@@ -853,6 +841,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
853841
}
854842
}
855843

844+
pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> {
845+
// FIXME: assuming here that type size is < i64::max_value()
846+
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
847+
// FIXME: Check overflow, out-of-bounds
848+
Ok(ptr.signed_offset(offset * pointee_size))
849+
}
850+
856851
pub(super) fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> {
857852
let value = self.eval_operand(op)?;
858853
let ty = self.operand_ty(op);

src/operator.rs

Lines changed: 122 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use rustc::mir;
2-
use rustc::ty::Ty;
2+
use rustc::ty::{self, Ty};
33

44
use error::{EvalError, EvalResult};
55
use eval_context::EvalContext;
@@ -25,11 +25,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
2525
) -> EvalResult<'tcx, (PrimVal, bool)> {
2626
let left_ty = self.operand_ty(left);
2727
let right_ty = self.operand_ty(right);
28-
let left_kind = self.ty_to_primval_kind(left_ty)?;
29-
let right_kind = self.ty_to_primval_kind(right_ty)?;
3028
let left_val = self.eval_operand_to_primval(left)?;
3129
let right_val = self.eval_operand_to_primval(right)?;
32-
binary_op(op, left_val, left_kind, right_val, right_kind)
30+
self.binary_op(op, left_val, left_ty, right_val, right_ty)
3331
}
3432

3533
/// Applies the binary operation `op` to the two operands and writes a tuple of the result
@@ -132,119 +130,141 @@ macro_rules! f64_arithmetic {
132130
)
133131
}
134132

135-
/// Returns the result of the specified operation and whether it overflowed.
136-
pub fn binary_op<'tcx>(
137-
bin_op: mir::BinOp,
138-
left: PrimVal,
139-
left_kind: PrimValKind,
140-
right: PrimVal,
141-
right_kind: PrimValKind,
142-
) -> EvalResult<'tcx, (PrimVal, bool)> {
143-
use rustc::mir::BinOp::*;
144-
use value::PrimValKind::*;
145-
146-
// FIXME(solson): Temporary hack. It will go away when we get rid of Pointer's ability to store
147-
// plain bytes, and leave that to PrimVal::Bytes.
148-
fn normalize(val: PrimVal) -> PrimVal {
149-
if let PrimVal::Ptr(ptr) = val {
150-
if let Ok(bytes) = ptr.to_int() {
151-
return PrimVal::Bytes(bytes as u128);
133+
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
134+
/// Returns the result of the specified operation and whether it overflowed.
135+
pub fn binary_op(
136+
&self,
137+
bin_op: mir::BinOp,
138+
left: PrimVal,
139+
left_ty: Ty<'tcx>,
140+
right: PrimVal,
141+
right_ty: Ty<'tcx>,
142+
) -> EvalResult<'tcx, (PrimVal, bool)> {
143+
use rustc::mir::BinOp::*;
144+
use value::PrimValKind::*;
145+
146+
// FIXME(solson): Temporary hack. It will go away when we get rid of Pointer's ability to store
147+
// plain bytes, and leave that to PrimVal::Bytes.
148+
fn normalize(val: PrimVal) -> PrimVal {
149+
if let PrimVal::Ptr(ptr) = val {
150+
if let Ok(bytes) = ptr.to_int() {
151+
return PrimVal::Bytes(bytes as u128);
152+
}
152153
}
154+
val
153155
}
154-
val
155-
}
156-
let (left, right) = (normalize(left), normalize(right));
156+
let (left, right) = (normalize(left), normalize(right));
157157

158-
let (l, r) = match (left, right) {
159-
(PrimVal::Bytes(left_bytes), PrimVal::Bytes(right_bytes)) => (left_bytes, right_bytes),
160-
161-
(PrimVal::Ptr(left_ptr), PrimVal::Ptr(right_ptr)) => {
162-
if left_ptr.alloc_id == right_ptr.alloc_id {
163-
// If the pointers are into the same allocation, fall through to the more general
164-
// match later, which will do comparisons on the pointer offsets.
165-
(left_ptr.offset as u128, right_ptr.offset as u128)
166-
} else {
167-
return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false));
168-
}
158+
// Offset is handled early, before we dispatch to unrelated_ptr_ops. We have to also catch the case where both arguments *are* convertible to integers.
159+
if bin_op == Offset {
160+
let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;
161+
let ptr = self.pointer_offset(left.to_ptr()?, pointee_ty, right.to_bytes()? as i64)?;
162+
return Ok((PrimVal::Ptr(ptr), false));
169163
}
170164

171-
(PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) |
172-
(PrimVal::Bytes(bytes), PrimVal::Ptr(ptr)) => {
173-
return Ok((unrelated_ptr_ops(bin_op, ptr, Pointer::from_int(bytes as u64))?, false));
174-
}
165+
let (l, r) = match (left, right) {
166+
(PrimVal::Bytes(left_bytes), PrimVal::Bytes(right_bytes)) => (left_bytes, right_bytes),
167+
168+
(PrimVal::Ptr(left_ptr), PrimVal::Ptr(right_ptr)) => {
169+
if left_ptr.alloc_id == right_ptr.alloc_id {
170+
// If the pointers are into the same allocation, fall through to the more general
171+
// match later, which will do comparisons on the pointer offsets.
172+
(left_ptr.offset as u128, right_ptr.offset as u128)
173+
} else {
174+
return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false));
175+
}
176+
}
175177

176-
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
177-
};
178+
(PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) |
179+
(PrimVal::Bytes(bytes), PrimVal::Ptr(ptr)) => {
180+
return Ok((unrelated_ptr_ops(bin_op, ptr, Pointer::from_int(bytes as u64))?, false));
181+
}
178182

179-
// These ops can have an RHS with a different numeric type.
180-
if bin_op == Shl || bin_op == Shr {
181-
return match bin_op {
182-
Shl => int_shift!(left_kind, overflowing_shl, l, r as u32),
183-
Shr => int_shift!(left_kind, overflowing_shr, l, r as u32),
184-
_ => bug!("it has already been checked that this is a shift op"),
183+
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
185184
};
186-
}
187185

188-
if left_kind != right_kind {
189-
let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op);
190-
return Err(EvalError::Unimplemented(msg));
191-
}
186+
let left_kind = self.ty_to_primval_kind(left_ty)?;
187+
let right_kind = self.ty_to_primval_kind(right_ty)?;
192188

193-
let val = match (bin_op, left_kind) {
194-
(Eq, F32) => PrimVal::from_bool(bytes_to_f32(l) == bytes_to_f32(r)),
195-
(Ne, F32) => PrimVal::from_bool(bytes_to_f32(l) != bytes_to_f32(r)),
196-
(Lt, F32) => PrimVal::from_bool(bytes_to_f32(l) < bytes_to_f32(r)),
197-
(Le, F32) => PrimVal::from_bool(bytes_to_f32(l) <= bytes_to_f32(r)),
198-
(Gt, F32) => PrimVal::from_bool(bytes_to_f32(l) > bytes_to_f32(r)),
199-
(Ge, F32) => PrimVal::from_bool(bytes_to_f32(l) >= bytes_to_f32(r)),
200-
201-
(Eq, F64) => PrimVal::from_bool(bytes_to_f64(l) == bytes_to_f64(r)),
202-
(Ne, F64) => PrimVal::from_bool(bytes_to_f64(l) != bytes_to_f64(r)),
203-
(Lt, F64) => PrimVal::from_bool(bytes_to_f64(l) < bytes_to_f64(r)),
204-
(Le, F64) => PrimVal::from_bool(bytes_to_f64(l) <= bytes_to_f64(r)),
205-
(Gt, F64) => PrimVal::from_bool(bytes_to_f64(l) > bytes_to_f64(r)),
206-
(Ge, F64) => PrimVal::from_bool(bytes_to_f64(l) >= bytes_to_f64(r)),
207-
208-
(Add, F32) => f32_arithmetic!(+, l, r),
209-
(Sub, F32) => f32_arithmetic!(-, l, r),
210-
(Mul, F32) => f32_arithmetic!(*, l, r),
211-
(Div, F32) => f32_arithmetic!(/, l, r),
212-
(Rem, F32) => f32_arithmetic!(%, l, r),
213-
214-
(Add, F64) => f64_arithmetic!(+, l, r),
215-
(Sub, F64) => f64_arithmetic!(-, l, r),
216-
(Mul, F64) => f64_arithmetic!(*, l, r),
217-
(Div, F64) => f64_arithmetic!(/, l, r),
218-
(Rem, F64) => f64_arithmetic!(%, l, r),
219-
220-
(Eq, _) => PrimVal::from_bool(l == r),
221-
(Ne, _) => PrimVal::from_bool(l != r),
222-
(Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)),
223-
(Lt, _) => PrimVal::from_bool(l < r),
224-
(Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)),
225-
(Le, _) => PrimVal::from_bool(l <= r),
226-
(Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)),
227-
(Gt, _) => PrimVal::from_bool(l > r),
228-
(Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)),
229-
(Ge, _) => PrimVal::from_bool(l >= r),
230-
231-
(BitOr, _) => PrimVal::Bytes(l | r),
232-
(BitAnd, _) => PrimVal::Bytes(l & r),
233-
(BitXor, _) => PrimVal::Bytes(l ^ r),
234-
235-
(Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r),
236-
(Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r),
237-
(Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r),
238-
(Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r),
239-
(Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r),
189+
// These ops can have an RHS with a different numeric type.
190+
if bin_op == Shl || bin_op == Shr {
191+
return match bin_op {
192+
Shl => int_shift!(left_kind, overflowing_shl, l, r as u32),
193+
Shr => int_shift!(left_kind, overflowing_shr, l, r as u32),
194+
_ => bug!("it has already been checked that this is a shift op"),
195+
};
196+
}
197+
if bin_op == Offset {
198+
// We permit offset-by-0 in any case. Drop glue actually does this, and it's probably (TM) fine for LLVM.
199+
if left_kind == PrimValKind::Ptr && right_kind.is_int() && r == 0 {
200+
return Ok((PrimVal::Bytes(l), false));
201+
} else {
202+
let msg = format!("unimplemented Offset: {:?}, {:?}", left, right);
203+
return Err(EvalError::Unimplemented(msg));
204+
}
205+
}
240206

241-
_ => {
207+
if left_kind != right_kind {
242208
let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op);
243209
return Err(EvalError::Unimplemented(msg));
244210
}
245-
};
246211

247-
Ok((val, false))
212+
let val = match (bin_op, left_kind) {
213+
(Eq, F32) => PrimVal::from_bool(bytes_to_f32(l) == bytes_to_f32(r)),
214+
(Ne, F32) => PrimVal::from_bool(bytes_to_f32(l) != bytes_to_f32(r)),
215+
(Lt, F32) => PrimVal::from_bool(bytes_to_f32(l) < bytes_to_f32(r)),
216+
(Le, F32) => PrimVal::from_bool(bytes_to_f32(l) <= bytes_to_f32(r)),
217+
(Gt, F32) => PrimVal::from_bool(bytes_to_f32(l) > bytes_to_f32(r)),
218+
(Ge, F32) => PrimVal::from_bool(bytes_to_f32(l) >= bytes_to_f32(r)),
219+
220+
(Eq, F64) => PrimVal::from_bool(bytes_to_f64(l) == bytes_to_f64(r)),
221+
(Ne, F64) => PrimVal::from_bool(bytes_to_f64(l) != bytes_to_f64(r)),
222+
(Lt, F64) => PrimVal::from_bool(bytes_to_f64(l) < bytes_to_f64(r)),
223+
(Le, F64) => PrimVal::from_bool(bytes_to_f64(l) <= bytes_to_f64(r)),
224+
(Gt, F64) => PrimVal::from_bool(bytes_to_f64(l) > bytes_to_f64(r)),
225+
(Ge, F64) => PrimVal::from_bool(bytes_to_f64(l) >= bytes_to_f64(r)),
226+
227+
(Add, F32) => f32_arithmetic!(+, l, r),
228+
(Sub, F32) => f32_arithmetic!(-, l, r),
229+
(Mul, F32) => f32_arithmetic!(*, l, r),
230+
(Div, F32) => f32_arithmetic!(/, l, r),
231+
(Rem, F32) => f32_arithmetic!(%, l, r),
232+
233+
(Add, F64) => f64_arithmetic!(+, l, r),
234+
(Sub, F64) => f64_arithmetic!(-, l, r),
235+
(Mul, F64) => f64_arithmetic!(*, l, r),
236+
(Div, F64) => f64_arithmetic!(/, l, r),
237+
(Rem, F64) => f64_arithmetic!(%, l, r),
238+
239+
(Eq, _) => PrimVal::from_bool(l == r),
240+
(Ne, _) => PrimVal::from_bool(l != r),
241+
(Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)),
242+
(Lt, _) => PrimVal::from_bool(l < r),
243+
(Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)),
244+
(Le, _) => PrimVal::from_bool(l <= r),
245+
(Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)),
246+
(Gt, _) => PrimVal::from_bool(l > r),
247+
(Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)),
248+
(Ge, _) => PrimVal::from_bool(l >= r),
249+
250+
(BitOr, _) => PrimVal::Bytes(l | r),
251+
(BitAnd, _) => PrimVal::Bytes(l & r),
252+
(BitXor, _) => PrimVal::Bytes(l ^ r),
253+
254+
(Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r),
255+
(Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r),
256+
(Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r),
257+
(Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r),
258+
(Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r),
259+
260+
_ => {
261+
let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op);
262+
return Err(EvalError::Unimplemented(msg));
263+
}
264+
};
265+
266+
Ok((val, false))
267+
}
248268
}
249269

250270
fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp, left: Pointer, right: Pointer) -> EvalResult<'tcx, PrimVal> {

src/terminator/intrinsic.rs

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use rustc::ty::{self, Ty};
77
use error::{EvalError, EvalResult};
88
use eval_context::EvalContext;
99
use lvalue::{Lvalue, LvalueExtra};
10-
use operator;
1110
use value::{PrimVal, PrimValKind, Value};
1211

1312
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
@@ -103,8 +102,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
103102
Value::ByRef(_) => bug!("just read the value, can't be byref"),
104103
Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"),
105104
};
106-
let kind = self.ty_to_primval_kind(ty)?;
107-
let (val, _) = operator::binary_op(mir::BinOp::Eq, old, kind, expect_old, kind)?;
105+
let (val, _) = self.binary_op(mir::BinOp::Eq, old, ty, expect_old, ty)?;
108106
let dest = self.force_allocation(dest)?.to_ptr();
109107
self.write_pair_to_ptr(old, val, dest, dest_ty)?;
110108
self.write_primval(Lvalue::from_ptr(ptr), change, ty)?;
@@ -125,7 +123,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
125123
Value::ByValPair(..) => bug!("atomic_xadd_relaxed doesn't work with nonprimitives"),
126124
};
127125
self.write_primval(dest, old, ty)?;
128-
let kind = self.ty_to_primval_kind(ty)?;
129126
let op = match intrinsic_name.split('_').nth(1).unwrap() {
130127
"or" => mir::BinOp::BitOr,
131128
"xor" => mir::BinOp::BitXor,
@@ -135,7 +132,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
135132
_ => bug!(),
136133
};
137134
// FIXME: what do atomics do on overflow?
138-
let (val, _) = operator::binary_op(op, old, kind, change, kind)?;
135+
let (val, _) = self.binary_op(op, old, ty, change, ty)?;
139136
self.write_primval(Lvalue::from_ptr(ptr), val, ty)?;
140137
},
141138

@@ -219,7 +216,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
219216

220217
"fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => {
221218
let ty = substs.type_at(0);
222-
let kind = self.ty_to_primval_kind(ty)?;
223219
let a = self.value_to_primval(arg_vals[0], ty)?;
224220
let b = self.value_to_primval(arg_vals[1], ty)?;
225221
let op = match intrinsic_name {
@@ -230,7 +226,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
230226
"frem_fast" => mir::BinOp::Rem,
231227
_ => bug!(),
232228
};
233-
let result = operator::binary_op(op, a, kind, b, kind)?;
229+
let result = self.binary_op(op, a, ty, b, ty)?;
234230
self.write_primval(dest, result.0, dest_ty)?;
235231
}
236232

@@ -298,13 +294,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
298294
}
299295

300296
"offset" => {
301-
let pointee_ty = substs.type_at(0);
302-
// FIXME: assuming here that type size is < i64::max_value()
303-
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
304297
let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64;
305-
306298
let ptr = arg_vals[0].read_ptr(&self.memory)?;
307-
let result_ptr = ptr.signed_offset(offset * pointee_size);
299+
let result_ptr = self.pointer_offset(ptr, substs.type_at(0), offset)?;
308300
self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?;
309301
}
310302

tests/run-pass/call_drop_on_array_elements.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
struct Bar(i32); // ZSTs are tested separately
1+
struct Bar(u16); // ZSTs are tested separately
22

33
static mut DROP_COUNT: usize = 0;
44

55
impl Drop for Bar {
66
fn drop(&mut self) {
7+
assert_eq!(self.0 as usize, unsafe { DROP_COUNT }); // tests whether we are called at a valid address
78
unsafe { DROP_COUNT += 1; }
89
}
910
}
1011

1112
fn main() {
12-
let b = [Bar(0), Bar(0), Bar(0), Bar(0)];
13+
let b = [Bar(0), Bar(1), Bar(2), Bar(3)];
1314
assert_eq!(unsafe { DROP_COUNT }, 0);
1415
drop(b);
1516
assert_eq!(unsafe { DROP_COUNT }, 4);

0 commit comments

Comments
 (0)