Skip to content

Commit

Permalink
Merge pull request rust-lang#209 from RalfJung/ptrs
Browse files Browse the repository at this point in the history
Make HashMap insertion work
  • Loading branch information
oli-obk authored Jun 23, 2017
2 parents 1a1d741 + d5c0316 commit f10dd41
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 33 deletions.
5 changes: 5 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub enum EvalError<'tcx> {
ExecuteMemory,
ArrayIndexOutOfBounds(Span, u64, u64),
Math(Span, ConstMathErr),
Intrinsic(String),
OverflowingMath,
InvalidChar(u128),
OutOfMemory {
Expand Down Expand Up @@ -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(..) =>
Expand Down Expand Up @@ -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 } =>
Expand Down
2 changes: 1 addition & 1 deletion src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
45 changes: 32 additions & 13 deletions src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use value::{
bytes_to_f64,
f32_to_bytes,
f64_to_bytes,
bytes_to_bool,
};

impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Expand Down Expand Up @@ -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);
}
_ => {}
}
Expand Down Expand Up @@ -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));
}
})
}
}
Expand All @@ -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,
Expand Down
19 changes: 13 additions & 6 deletions src/terminator/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
}

Expand Down Expand Up @@ -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,
Expand All @@ -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)
Expand Down
6 changes: 0 additions & 6 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 37 additions & 0 deletions tests/compile-fail/bitop-beyond-alignment.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 0 deletions tests/compile-fail/ctlz_nonzero.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 0 deletions tests/compile-fail/cttz_nonzero.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
22 changes: 16 additions & 6 deletions tests/run-pass-fullmir/hashmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
37 changes: 37 additions & 0 deletions tests/run-pass/intrinsics-integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down

0 comments on commit f10dd41

Please sign in to comment.