commit
f10dd417cb
11 changed files with 172 additions and 33 deletions
|
|
@ -33,6 +33,7 @@ pub enum EvalError<'tcx> {
|
|||
ExecuteMemory,
|
||||
ArrayIndexOutOfBounds(Span, u64, u64),
|
||||
Math(Span, ConstMathErr),
|
||||
Intrinsic(String),
|
||||
OverflowingMath,
|
||||
InvalidChar(u128),
|
||||
OutOfMemory {
|
||||
|
|
@ -104,6 +105,8 @@ impl<'tcx> Error for EvalError<'tcx> {
|
|||
"array index out of bounds",
|
||||
EvalError::Math(..) =>
|
||||
"mathematical operation failed",
|
||||
EvalError::Intrinsic(..) =>
|
||||
"intrinsic failed",
|
||||
EvalError::OverflowingMath =>
|
||||
"attempted to do overflowing math",
|
||||
EvalError::NoMirFor(..) =>
|
||||
|
|
@ -168,6 +171,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> {
|
|||
write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span),
|
||||
EvalError::Math(span, ref err) =>
|
||||
write!(f, "{:?} at {:?}", err, span),
|
||||
EvalError::Intrinsic(ref err) =>
|
||||
write!(f, "{}", err),
|
||||
EvalError::InvalidChar(c) =>
|
||||
write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c),
|
||||
EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } =>
|
||||
|
|
|
|||
|
|
@ -546,7 +546,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
StaticKind::Immutable => " (immutable)",
|
||||
StaticKind::NotStatic => "",
|
||||
};
|
||||
trace!("{}({} bytes){}", msg, alloc.bytes.len(), immutable);
|
||||
trace!("{}({} bytes, alignment {}){}", msg, alloc.bytes.len(), alloc.align, immutable);
|
||||
|
||||
if !relocations.is_empty() {
|
||||
msg.clear();
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ use value::{
|
|||
bytes_to_f64,
|
||||
f32_to_bytes,
|
||||
f64_to_bytes,
|
||||
bytes_to_bool,
|
||||
};
|
||||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
|
|
@ -190,19 +189,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
// These work if one operand is a pointer, the other an integer
|
||||
Add | Sub
|
||||
Add | BitAnd | Sub
|
||||
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
|
||||
&& left.is_ptr() && right.is_bytes() => {
|
||||
// Cast to i128 is fine as we checked the kind to be ptr-sized
|
||||
let (res, over) = self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize)?;
|
||||
return Ok((PrimVal::Ptr(res), over));
|
||||
return self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize);
|
||||
}
|
||||
Add
|
||||
Add | BitAnd
|
||||
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
|
||||
&& left.is_bytes() && right.is_ptr() => {
|
||||
// This is a commutative operation, just swap the operands
|
||||
let (res, over) = self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize)?;
|
||||
return Ok((PrimVal::Ptr(res), over));
|
||||
return self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -287,18 +284,40 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
left: Pointer,
|
||||
right: i128,
|
||||
signed: bool,
|
||||
) -> EvalResult<'tcx, (Pointer, bool)> {
|
||||
) -> EvalResult<'tcx, (PrimVal, bool)> {
|
||||
use rustc::mir::BinOp::*;
|
||||
|
||||
fn map_to_primval((res, over) : (Pointer, bool)) -> (PrimVal, bool) {
|
||||
(PrimVal::Ptr(res), over)
|
||||
}
|
||||
|
||||
Ok(match bin_op {
|
||||
Sub =>
|
||||
// The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
|
||||
left.overflowing_signed_offset(-right, self.memory.layout),
|
||||
map_to_primval(left.overflowing_signed_offset(-right, self.memory.layout)),
|
||||
Add if signed =>
|
||||
left.overflowing_signed_offset(right, self.memory.layout),
|
||||
map_to_primval(left.overflowing_signed_offset(right, self.memory.layout)),
|
||||
Add if !signed =>
|
||||
left.overflowing_offset(right as u64, self.memory.layout),
|
||||
_ => bug!("ptr_int_arithmetic called on unsupported operation")
|
||||
map_to_primval(left.overflowing_offset(right as u64, self.memory.layout)),
|
||||
|
||||
BitAnd if !signed => {
|
||||
let base_mask : u64 = !(self.memory.get(left.alloc_id)?.align - 1);
|
||||
let right = right as u64;
|
||||
if right & base_mask == base_mask {
|
||||
// Case 1: The base address bits are all preserved, i.e., right is all-1 there
|
||||
(PrimVal::Ptr(Pointer::new(left.alloc_id, left.offset & right)), false)
|
||||
} else if right & base_mask == 0 {
|
||||
// Case 2: The base address bits are all taken away, i.e., right is all-0 there
|
||||
(PrimVal::from_u128((left.offset & right) as u128), false)
|
||||
} else {
|
||||
return Err(EvalError::ReadPointerAsBytes);
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" });
|
||||
return Err(EvalError::Unimplemented(msg));
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -314,7 +333,7 @@ pub fn unary_op<'tcx>(
|
|||
let bytes = val.to_bytes()?;
|
||||
|
||||
let result_bytes = match (un_op, val_kind) {
|
||||
(Not, Bool) => !bytes_to_bool(bytes) as u128,
|
||||
(Not, Bool) => !val.to_bool()? as u128,
|
||||
|
||||
(Not, U8) => !(bytes as u8) as u128,
|
||||
(Not, U16) => !(bytes as u16) as u128,
|
||||
|
|
|
|||
|
|
@ -154,12 +154,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
"ctpop" |
|
||||
"cttz" |
|
||||
"cttz_nonzero" |
|
||||
"ctlz" |
|
||||
"ctlz_nonzero" |
|
||||
"bswap" => {
|
||||
let ty = substs.type_at(0);
|
||||
let num = self.value_to_primval(arg_vals[0], ty)?;
|
||||
let num = self.value_to_primval(arg_vals[0], ty)?.to_bytes()?;
|
||||
let kind = self.ty_to_primval_kind(ty)?;
|
||||
let num = numeric_intrinsic(intrinsic_name, num, kind)?;
|
||||
let num = if intrinsic_name.ends_with("_nonzero") {
|
||||
if num == 0 {
|
||||
return Err(EvalError::Intrinsic(format!("{} called on 0", intrinsic_name)))
|
||||
}
|
||||
numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), num, kind)?
|
||||
} else {
|
||||
numeric_intrinsic(intrinsic_name, num, kind)?
|
||||
};
|
||||
self.write_primval(dest, num, ty)?;
|
||||
}
|
||||
|
||||
|
|
@ -538,13 +547,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
fn numeric_intrinsic<'tcx>(
|
||||
name: &str,
|
||||
val: PrimVal,
|
||||
bytes: u128,
|
||||
kind: PrimValKind
|
||||
) -> EvalResult<'tcx, PrimVal> {
|
||||
macro_rules! integer_intrinsic {
|
||||
($method:ident) => ({
|
||||
let bytes = val.to_bytes()?;
|
||||
|
||||
use value::PrimValKind::*;
|
||||
let result_bytes = match kind {
|
||||
I8 => (bytes as i8).$method() as u128,
|
||||
|
|
@ -557,7 +564,7 @@ fn numeric_intrinsic<'tcx>(
|
|||
U64 => (bytes as u64).$method() as u128,
|
||||
I128 => (bytes as i128).$method() as u128,
|
||||
U128 => bytes.$method() as u128,
|
||||
_ => bug!("invalid `{}` argument: {:?}", name, val),
|
||||
_ => bug!("invalid `{}` argument: {:?}", name, bytes),
|
||||
};
|
||||
|
||||
PrimVal::Bytes(result_bytes)
|
||||
|
|
|
|||
|
|
@ -23,12 +23,6 @@ pub(super) fn f64_to_bytes(f: f64) -> u128 {
|
|||
unsafe { transmute::<f64, u64>(f) as u128 }
|
||||
}
|
||||
|
||||
pub(super) fn bytes_to_bool(n: u128) -> bool {
|
||||
// FIXME(solson): Can we reach here due to user error?
|
||||
debug_assert!(n == 0 || n == 1, "bytes interpreted as bool were {}", n);
|
||||
n & 1 == 1
|
||||
}
|
||||
|
||||
/// A `Value` represents a single self-contained Rust value.
|
||||
///
|
||||
/// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve
|
||||
|
|
|
|||
37
tests/compile-fail/bitop-beyond-alignment.rs
Normal file
37
tests/compile-fail/bitop-beyond-alignment.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::mem;
|
||||
|
||||
enum Tag<A> {
|
||||
Tag2(A)
|
||||
}
|
||||
|
||||
struct Rec {
|
||||
c8: u8,
|
||||
t: Tag<u64>
|
||||
}
|
||||
|
||||
fn mk_rec() -> Rec {
|
||||
return Rec { c8:0, t:Tag::Tag2(0) };
|
||||
}
|
||||
|
||||
fn is_u64_aligned(u: &Tag<u64>) -> bool {
|
||||
let p: usize = unsafe { mem::transmute(u) };
|
||||
let u64_align = std::mem::align_of::<u64>();
|
||||
return (p & (u64_align + 1)) == 0; //~ ERROR a raw memory access tried to access part of a pointer value as raw bytes
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let x = mk_rec();
|
||||
assert!(is_u64_aligned(&x.t));
|
||||
}
|
||||
15
tests/compile-fail/ctlz_nonzero.rs
Normal file
15
tests/compile-fail/ctlz_nonzero.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#![feature(intrinsics)]
|
||||
|
||||
mod rusti {
|
||||
extern "rust-intrinsic" {
|
||||
pub fn ctlz_nonzero<T>(x: T) -> T;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
unsafe {
|
||||
use rusti::*;
|
||||
|
||||
ctlz_nonzero(0u8); //~ ERROR: ctlz_nonzero called on 0
|
||||
}
|
||||
}
|
||||
15
tests/compile-fail/cttz_nonzero.rs
Normal file
15
tests/compile-fail/cttz_nonzero.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#![feature(intrinsics)]
|
||||
|
||||
mod rusti {
|
||||
extern "rust-intrinsic" {
|
||||
pub fn cttz_nonzero<T>(x: T) -> T;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
unsafe {
|
||||
use rusti::*;
|
||||
|
||||
cttz_nonzero(0u8); //~ ERROR: cttz_nonzero called on 0
|
||||
}
|
||||
}
|
||||
|
|
@ -2,14 +2,24 @@ use std::collections::{self, HashMap};
|
|||
use std::hash::BuildHasherDefault;
|
||||
|
||||
fn main() {
|
||||
let map : HashMap<String, i32, BuildHasherDefault<collections::hash_map::DefaultHasher>> = Default::default();
|
||||
let mut map : HashMap<i32, i32, BuildHasherDefault<collections::hash_map::DefaultHasher>> = Default::default();
|
||||
map.insert(0, 0);
|
||||
assert_eq!(map.values().fold(0, |x, y| x+y), 0);
|
||||
|
||||
// TODO: This performs bit operations on the least significant bit of a pointer
|
||||
// for i in 0..33 {
|
||||
// map.insert(format!("key_{}", i), i);
|
||||
// assert_eq!(map.values().fold(0, |x, y| x+y), i*(i+1)/2);
|
||||
// }
|
||||
let table_base = map.get(&0).unwrap() as *const _;
|
||||
|
||||
let num = 22; // large enough to trigger a resize
|
||||
for i in 1..num {
|
||||
map.insert(i, i);
|
||||
}
|
||||
assert!(table_base != map.get(&0).unwrap() as *const _); // make sure relocation happened
|
||||
assert_eq!(map.values().fold(0, |x, y| x+y), num*(num-1)/2); // check the right things are in the table now
|
||||
|
||||
// Inserting again replaces the existing entries
|
||||
for i in 0..num {
|
||||
map.insert(i, num-1-i);
|
||||
}
|
||||
assert_eq!(map.values().fold(0, |x, y| x+y), num*(num-1)/2);
|
||||
|
||||
// TODO: Test Entry API
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ mod rusti {
|
|||
extern "rust-intrinsic" {
|
||||
pub fn ctpop<T>(x: T) -> T;
|
||||
pub fn ctlz<T>(x: T) -> T;
|
||||
pub fn ctlz_nonzero<T>(x: T) -> T;
|
||||
pub fn cttz<T>(x: T) -> T;
|
||||
pub fn cttz_nonzero<T>(x: T) -> T;
|
||||
pub fn bswap<T>(x: T) -> T;
|
||||
}
|
||||
}
|
||||
|
|
@ -68,6 +70,21 @@ pub fn main() {
|
|||
assert_eq!(ctlz(100u32), 25); assert_eq!(ctlz(100i32), 25);
|
||||
assert_eq!(ctlz(100u64), 57); assert_eq!(ctlz(100i64), 57);
|
||||
|
||||
assert_eq!(ctlz_nonzero(1u8), 7); assert_eq!(ctlz_nonzero(1i8), 7);
|
||||
assert_eq!(ctlz_nonzero(1u16), 15); assert_eq!(ctlz_nonzero(1i16), 15);
|
||||
assert_eq!(ctlz_nonzero(1u32), 31); assert_eq!(ctlz_nonzero(1i32), 31);
|
||||
assert_eq!(ctlz_nonzero(1u64), 63); assert_eq!(ctlz_nonzero(1i64), 63);
|
||||
|
||||
assert_eq!(ctlz_nonzero(10u8), 4); assert_eq!(ctlz_nonzero(10i8), 4);
|
||||
assert_eq!(ctlz_nonzero(10u16), 12); assert_eq!(ctlz_nonzero(10i16), 12);
|
||||
assert_eq!(ctlz_nonzero(10u32), 28); assert_eq!(ctlz_nonzero(10i32), 28);
|
||||
assert_eq!(ctlz_nonzero(10u64), 60); assert_eq!(ctlz_nonzero(10i64), 60);
|
||||
|
||||
assert_eq!(ctlz_nonzero(100u8), 1); assert_eq!(ctlz_nonzero(100i8), 1);
|
||||
assert_eq!(ctlz_nonzero(100u16), 9); assert_eq!(ctlz_nonzero(100i16), 9);
|
||||
assert_eq!(ctlz_nonzero(100u32), 25); assert_eq!(ctlz_nonzero(100i32), 25);
|
||||
assert_eq!(ctlz_nonzero(100u64), 57); assert_eq!(ctlz_nonzero(100i64), 57);
|
||||
|
||||
assert_eq!(cttz(-1i8 as u8), 0); assert_eq!(cttz(-1i8), 0);
|
||||
assert_eq!(cttz(-1i16 as u16), 0); assert_eq!(cttz(-1i16), 0);
|
||||
assert_eq!(cttz(-1i32 as u32), 0); assert_eq!(cttz(-1i32), 0);
|
||||
|
|
@ -93,6 +110,26 @@ pub fn main() {
|
|||
assert_eq!(cttz(100u32), 2); assert_eq!(cttz(100i32), 2);
|
||||
assert_eq!(cttz(100u64), 2); assert_eq!(cttz(100i64), 2);
|
||||
|
||||
assert_eq!(cttz_nonzero(-1i8 as u8), 0); assert_eq!(cttz_nonzero(-1i8), 0);
|
||||
assert_eq!(cttz_nonzero(-1i16 as u16), 0); assert_eq!(cttz_nonzero(-1i16), 0);
|
||||
assert_eq!(cttz_nonzero(-1i32 as u32), 0); assert_eq!(cttz_nonzero(-1i32), 0);
|
||||
assert_eq!(cttz_nonzero(-1i64 as u64), 0); assert_eq!(cttz_nonzero(-1i64), 0);
|
||||
|
||||
assert_eq!(cttz_nonzero(1u8), 0); assert_eq!(cttz_nonzero(1i8), 0);
|
||||
assert_eq!(cttz_nonzero(1u16), 0); assert_eq!(cttz_nonzero(1i16), 0);
|
||||
assert_eq!(cttz_nonzero(1u32), 0); assert_eq!(cttz_nonzero(1i32), 0);
|
||||
assert_eq!(cttz_nonzero(1u64), 0); assert_eq!(cttz_nonzero(1i64), 0);
|
||||
|
||||
assert_eq!(cttz_nonzero(10u8), 1); assert_eq!(cttz_nonzero(10i8), 1);
|
||||
assert_eq!(cttz_nonzero(10u16), 1); assert_eq!(cttz_nonzero(10i16), 1);
|
||||
assert_eq!(cttz_nonzero(10u32), 1); assert_eq!(cttz_nonzero(10i32), 1);
|
||||
assert_eq!(cttz_nonzero(10u64), 1); assert_eq!(cttz_nonzero(10i64), 1);
|
||||
|
||||
assert_eq!(cttz_nonzero(100u8), 2); assert_eq!(cttz_nonzero(100i8), 2);
|
||||
assert_eq!(cttz_nonzero(100u16), 2); assert_eq!(cttz_nonzero(100i16), 2);
|
||||
assert_eq!(cttz_nonzero(100u32), 2); assert_eq!(cttz_nonzero(100i32), 2);
|
||||
assert_eq!(cttz_nonzero(100u64), 2); assert_eq!(cttz_nonzero(100i64), 2);
|
||||
|
||||
assert_eq!(bswap(0x0Au8), 0x0A); // no-op
|
||||
assert_eq!(bswap(0x0Ai8), 0x0A); // no-op
|
||||
assert_eq!(bswap(0x0A0Bu16), 0x0B0A);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ fn mk_rec() -> Rec {
|
|||
fn is_u64_aligned(u: &Tag<u64>) -> bool {
|
||||
let p: usize = unsafe { mem::transmute(u) };
|
||||
let u64_align = std::mem::align_of::<u64>();
|
||||
return (p & (u64_align - 1)) == 0; //~ ERROR a raw memory access tried to access part of a pointer value as raw bytes
|
||||
return (p & (u64_align - 1)) == 0;
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
Loading…
Add table
Add a link
Reference in a new issue