Implement Offset like the other binary operators, share code with the intrinsic
Also improve drop glue tests
This commit is contained in:
parent
31cf66d0e8
commit
1b5f77e4c1
4 changed files with 135 additions and 127 deletions
|
@ -451,18 +451,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
self.write_value(value, dest, dest_ty)?;
|
self.write_value(value, dest, dest_ty)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
BinaryOp(mir::BinOp::Offset, ref left, ref right) => {
|
|
||||||
let pointer_ty = self.operand_ty(left);
|
|
||||||
let pointee_ty = pointer_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;
|
|
||||||
// FIXME: assuming here that type size is < i64::max_value()
|
|
||||||
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
|
|
||||||
let offset = self.eval_operand_to_primval(right)?.to_i128()? as i64;
|
|
||||||
|
|
||||||
let ptr = self.eval_operand_to_primval(left)?.to_ptr()?;
|
|
||||||
let result_ptr = ptr.signed_offset(offset * pointee_size);
|
|
||||||
self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
BinaryOp(bin_op, ref left, ref right) => {
|
BinaryOp(bin_op, ref left, ref right) => {
|
||||||
// ignore overflow bit, rustc inserts check branches for us
|
// ignore overflow bit, rustc inserts check branches for us
|
||||||
self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)?;
|
self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)?;
|
||||||
|
@ -853,6 +841,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> {
|
||||||
|
// FIXME: assuming here that type size is < i64::max_value()
|
||||||
|
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
|
||||||
|
// FIXME: Check overflow, out-of-bounds
|
||||||
|
Ok(ptr.signed_offset(offset * pointee_size))
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> {
|
pub(super) fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> {
|
||||||
let value = self.eval_operand(op)?;
|
let value = self.eval_operand(op)?;
|
||||||
let ty = self.operand_ty(op);
|
let ty = self.operand_ty(op);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use rustc::mir;
|
use rustc::mir;
|
||||||
use rustc::ty::Ty;
|
use rustc::ty::{self, Ty};
|
||||||
|
|
||||||
use error::{EvalError, EvalResult};
|
use error::{EvalError, EvalResult};
|
||||||
use eval_context::EvalContext;
|
use eval_context::EvalContext;
|
||||||
|
@ -25,11 +25,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
) -> EvalResult<'tcx, (PrimVal, bool)> {
|
) -> EvalResult<'tcx, (PrimVal, bool)> {
|
||||||
let left_ty = self.operand_ty(left);
|
let left_ty = self.operand_ty(left);
|
||||||
let right_ty = self.operand_ty(right);
|
let right_ty = self.operand_ty(right);
|
||||||
let left_kind = self.ty_to_primval_kind(left_ty)?;
|
|
||||||
let right_kind = self.ty_to_primval_kind(right_ty)?;
|
|
||||||
let left_val = self.eval_operand_to_primval(left)?;
|
let left_val = self.eval_operand_to_primval(left)?;
|
||||||
let right_val = self.eval_operand_to_primval(right)?;
|
let right_val = self.eval_operand_to_primval(right)?;
|
||||||
binary_op(op, left_val, left_kind, right_val, right_kind)
|
self.binary_op(op, left_val, left_ty, right_val, right_ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies the binary operation `op` to the two operands and writes a tuple of the result
|
/// Applies the binary operation `op` to the two operands and writes a tuple of the result
|
||||||
|
@ -132,13 +130,15 @@ macro_rules! f64_arithmetic {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
/// Returns the result of the specified operation and whether it overflowed.
|
/// Returns the result of the specified operation and whether it overflowed.
|
||||||
pub fn binary_op<'tcx>(
|
pub fn binary_op(
|
||||||
|
&self,
|
||||||
bin_op: mir::BinOp,
|
bin_op: mir::BinOp,
|
||||||
left: PrimVal,
|
left: PrimVal,
|
||||||
left_kind: PrimValKind,
|
left_ty: Ty<'tcx>,
|
||||||
right: PrimVal,
|
right: PrimVal,
|
||||||
right_kind: PrimValKind,
|
right_ty: Ty<'tcx>,
|
||||||
) -> EvalResult<'tcx, (PrimVal, bool)> {
|
) -> EvalResult<'tcx, (PrimVal, bool)> {
|
||||||
use rustc::mir::BinOp::*;
|
use rustc::mir::BinOp::*;
|
||||||
use value::PrimValKind::*;
|
use value::PrimValKind::*;
|
||||||
|
@ -155,6 +155,13 @@ pub fn binary_op<'tcx>(
|
||||||
}
|
}
|
||||||
let (left, right) = (normalize(left), normalize(right));
|
let (left, right) = (normalize(left), normalize(right));
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
if bin_op == Offset {
|
||||||
|
let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;
|
||||||
|
let ptr = self.pointer_offset(left.to_ptr()?, pointee_ty, right.to_bytes()? as i64)?;
|
||||||
|
return Ok((PrimVal::Ptr(ptr), false));
|
||||||
|
}
|
||||||
|
|
||||||
let (l, r) = match (left, right) {
|
let (l, r) = match (left, right) {
|
||||||
(PrimVal::Bytes(left_bytes), PrimVal::Bytes(right_bytes)) => (left_bytes, right_bytes),
|
(PrimVal::Bytes(left_bytes), PrimVal::Bytes(right_bytes)) => (left_bytes, right_bytes),
|
||||||
|
|
||||||
|
@ -176,6 +183,9 @@ pub fn binary_op<'tcx>(
|
||||||
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
|
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let left_kind = self.ty_to_primval_kind(left_ty)?;
|
||||||
|
let right_kind = self.ty_to_primval_kind(right_ty)?;
|
||||||
|
|
||||||
// These ops can have an RHS with a different numeric type.
|
// These ops can have an RHS with a different numeric type.
|
||||||
if bin_op == Shl || bin_op == Shr {
|
if bin_op == Shl || bin_op == Shr {
|
||||||
return match bin_op {
|
return match bin_op {
|
||||||
|
@ -184,6 +194,15 @@ pub fn binary_op<'tcx>(
|
||||||
_ => bug!("it has already been checked that this is a shift op"),
|
_ => bug!("it has already been checked that this is a shift op"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if bin_op == Offset {
|
||||||
|
// We permit offset-by-0 in any case. Drop glue actually does this, and it's probably (TM) fine for LLVM.
|
||||||
|
if left_kind == PrimValKind::Ptr && right_kind.is_int() && r == 0 {
|
||||||
|
return Ok((PrimVal::Bytes(l), false));
|
||||||
|
} else {
|
||||||
|
let msg = format!("unimplemented Offset: {:?}, {:?}", left, right);
|
||||||
|
return Err(EvalError::Unimplemented(msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if left_kind != right_kind {
|
if left_kind != right_kind {
|
||||||
let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op);
|
let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op);
|
||||||
|
@ -246,6 +265,7 @@ pub fn binary_op<'tcx>(
|
||||||
|
|
||||||
Ok((val, false))
|
Ok((val, false))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp, left: Pointer, right: Pointer) -> EvalResult<'tcx, PrimVal> {
|
fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp, left: Pointer, right: Pointer) -> EvalResult<'tcx, PrimVal> {
|
||||||
use rustc::mir::BinOp::*;
|
use rustc::mir::BinOp::*;
|
||||||
|
|
|
@ -7,7 +7,6 @@ use rustc::ty::{self, Ty};
|
||||||
use error::{EvalError, EvalResult};
|
use error::{EvalError, EvalResult};
|
||||||
use eval_context::EvalContext;
|
use eval_context::EvalContext;
|
||||||
use lvalue::{Lvalue, LvalueExtra};
|
use lvalue::{Lvalue, LvalueExtra};
|
||||||
use operator;
|
|
||||||
use value::{PrimVal, PrimValKind, Value};
|
use value::{PrimVal, PrimValKind, Value};
|
||||||
|
|
||||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
|
@ -103,8 +102,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
Value::ByRef(_) => bug!("just read the value, can't be byref"),
|
Value::ByRef(_) => bug!("just read the value, can't be byref"),
|
||||||
Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"),
|
Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"),
|
||||||
};
|
};
|
||||||
let kind = self.ty_to_primval_kind(ty)?;
|
let (val, _) = self.binary_op(mir::BinOp::Eq, old, ty, expect_old, ty)?;
|
||||||
let (val, _) = operator::binary_op(mir::BinOp::Eq, old, kind, expect_old, kind)?;
|
|
||||||
let dest = self.force_allocation(dest)?.to_ptr();
|
let dest = self.force_allocation(dest)?.to_ptr();
|
||||||
self.write_pair_to_ptr(old, val, dest, dest_ty)?;
|
self.write_pair_to_ptr(old, val, dest, dest_ty)?;
|
||||||
self.write_primval(Lvalue::from_ptr(ptr), change, ty)?;
|
self.write_primval(Lvalue::from_ptr(ptr), change, ty)?;
|
||||||
|
@ -125,7 +123,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
Value::ByValPair(..) => bug!("atomic_xadd_relaxed doesn't work with nonprimitives"),
|
Value::ByValPair(..) => bug!("atomic_xadd_relaxed doesn't work with nonprimitives"),
|
||||||
};
|
};
|
||||||
self.write_primval(dest, old, ty)?;
|
self.write_primval(dest, old, ty)?;
|
||||||
let kind = self.ty_to_primval_kind(ty)?;
|
|
||||||
let op = match intrinsic_name.split('_').nth(1).unwrap() {
|
let op = match intrinsic_name.split('_').nth(1).unwrap() {
|
||||||
"or" => mir::BinOp::BitOr,
|
"or" => mir::BinOp::BitOr,
|
||||||
"xor" => mir::BinOp::BitXor,
|
"xor" => mir::BinOp::BitXor,
|
||||||
|
@ -135,7 +132,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
_ => bug!(),
|
_ => bug!(),
|
||||||
};
|
};
|
||||||
// FIXME: what do atomics do on overflow?
|
// FIXME: what do atomics do on overflow?
|
||||||
let (val, _) = operator::binary_op(op, old, kind, change, kind)?;
|
let (val, _) = self.binary_op(op, old, ty, change, ty)?;
|
||||||
self.write_primval(Lvalue::from_ptr(ptr), val, ty)?;
|
self.write_primval(Lvalue::from_ptr(ptr), val, ty)?;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -219,7 +216,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
|
|
||||||
"fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => {
|
"fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => {
|
||||||
let ty = substs.type_at(0);
|
let ty = substs.type_at(0);
|
||||||
let kind = self.ty_to_primval_kind(ty)?;
|
|
||||||
let a = self.value_to_primval(arg_vals[0], ty)?;
|
let a = self.value_to_primval(arg_vals[0], ty)?;
|
||||||
let b = self.value_to_primval(arg_vals[1], ty)?;
|
let b = self.value_to_primval(arg_vals[1], ty)?;
|
||||||
let op = match intrinsic_name {
|
let op = match intrinsic_name {
|
||||||
|
@ -230,7 +226,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
"frem_fast" => mir::BinOp::Rem,
|
"frem_fast" => mir::BinOp::Rem,
|
||||||
_ => bug!(),
|
_ => bug!(),
|
||||||
};
|
};
|
||||||
let result = operator::binary_op(op, a, kind, b, kind)?;
|
let result = self.binary_op(op, a, ty, b, ty)?;
|
||||||
self.write_primval(dest, result.0, dest_ty)?;
|
self.write_primval(dest, result.0, dest_ty)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,13 +294,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
"offset" => {
|
"offset" => {
|
||||||
let pointee_ty = substs.type_at(0);
|
|
||||||
// FIXME: assuming here that type size is < i64::max_value()
|
|
||||||
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
|
|
||||||
let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64;
|
let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64;
|
||||||
|
|
||||||
let ptr = arg_vals[0].read_ptr(&self.memory)?;
|
let ptr = arg_vals[0].read_ptr(&self.memory)?;
|
||||||
let result_ptr = ptr.signed_offset(offset * pointee_size);
|
let result_ptr = self.pointer_offset(ptr, substs.type_at(0), offset)?;
|
||||||
self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?;
|
self.write_primval(dest, PrimVal::Ptr(result_ptr), dest_ty)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
struct Bar(i32); // ZSTs are tested separately
|
struct Bar(u16); // ZSTs are tested separately
|
||||||
|
|
||||||
static mut DROP_COUNT: usize = 0;
|
static mut DROP_COUNT: usize = 0;
|
||||||
|
|
||||||
impl Drop for Bar {
|
impl Drop for Bar {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
assert_eq!(self.0 as usize, unsafe { DROP_COUNT }); // tests whether we are called at a valid address
|
||||||
unsafe { DROP_COUNT += 1; }
|
unsafe { DROP_COUNT += 1; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let b = [Bar(0), Bar(0), Bar(0), Bar(0)];
|
let b = [Bar(0), Bar(1), Bar(2), Bar(3)];
|
||||||
assert_eq!(unsafe { DROP_COUNT }, 0);
|
assert_eq!(unsafe { DROP_COUNT }, 0);
|
||||||
drop(b);
|
drop(b);
|
||||||
assert_eq!(unsafe { DROP_COUNT }, 4);
|
assert_eq!(unsafe { DROP_COUNT }, 4);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue