Skip to content

Commit f10dd41

Browse files
authored
Merge pull request rust-lang#209 from RalfJung/ptrs
Make HashMap insertion work
2 parents 1a1d741 + d5c0316 commit f10dd41

File tree

11 files changed

+172
-33
lines changed

11 files changed

+172
-33
lines changed

src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub enum EvalError<'tcx> {
3333
ExecuteMemory,
3434
ArrayIndexOutOfBounds(Span, u64, u64),
3535
Math(Span, ConstMathErr),
36+
Intrinsic(String),
3637
OverflowingMath,
3738
InvalidChar(u128),
3839
OutOfMemory {
@@ -104,6 +105,8 @@ impl<'tcx> Error for EvalError<'tcx> {
104105
"array index out of bounds",
105106
EvalError::Math(..) =>
106107
"mathematical operation failed",
108+
EvalError::Intrinsic(..) =>
109+
"intrinsic failed",
107110
EvalError::OverflowingMath =>
108111
"attempted to do overflowing math",
109112
EvalError::NoMirFor(..) =>
@@ -168,6 +171,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> {
168171
write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span),
169172
EvalError::Math(span, ref err) =>
170173
write!(f, "{:?} at {:?}", err, span),
174+
EvalError::Intrinsic(ref err) =>
175+
write!(f, "{}", err),
171176
EvalError::InvalidChar(c) =>
172177
write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c),
173178
EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } =>

src/memory.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
546546
StaticKind::Immutable => " (immutable)",
547547
StaticKind::NotStatic => "",
548548
};
549-
trace!("{}({} bytes){}", msg, alloc.bytes.len(), immutable);
549+
trace!("{}({} bytes, alignment {}){}", msg, alloc.bytes.len(), alloc.align, immutable);
550550

551551
if !relocations.is_empty() {
552552
msg.clear();

src/operator.rs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use value::{
1313
bytes_to_f64,
1414
f32_to_bytes,
1515
f64_to_bytes,
16-
bytes_to_bool,
1716
};
1817

1918
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
@@ -190,19 +189,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
190189
}
191190
}
192191
// These work if one operand is a pointer, the other an integer
193-
Add | Sub
192+
Add | BitAnd | Sub
194193
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
195194
&& left.is_ptr() && right.is_bytes() => {
196195
// Cast to i128 is fine as we checked the kind to be ptr-sized
197-
let (res, over) = self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize)?;
198-
return Ok((PrimVal::Ptr(res), over));
196+
return self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize);
199197
}
200-
Add
198+
Add | BitAnd
201199
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
202200
&& left.is_bytes() && right.is_ptr() => {
203201
// This is a commutative operation, just swap the operands
204-
let (res, over) = self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize)?;
205-
return Ok((PrimVal::Ptr(res), over));
202+
return self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize);
206203
}
207204
_ => {}
208205
}
@@ -287,18 +284,40 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
287284
left: Pointer,
288285
right: i128,
289286
signed: bool,
290-
) -> EvalResult<'tcx, (Pointer, bool)> {
287+
) -> EvalResult<'tcx, (PrimVal, bool)> {
291288
use rustc::mir::BinOp::*;
292289

290+
fn map_to_primval((res, over) : (Pointer, bool)) -> (PrimVal, bool) {
291+
(PrimVal::Ptr(res), over)
292+
}
293+
293294
Ok(match bin_op {
294295
Sub =>
295296
// The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
296-
left.overflowing_signed_offset(-right, self.memory.layout),
297+
map_to_primval(left.overflowing_signed_offset(-right, self.memory.layout)),
297298
Add if signed =>
298-
left.overflowing_signed_offset(right, self.memory.layout),
299+
map_to_primval(left.overflowing_signed_offset(right, self.memory.layout)),
299300
Add if !signed =>
300-
left.overflowing_offset(right as u64, self.memory.layout),
301-
_ => bug!("ptr_int_arithmetic called on unsupported operation")
301+
map_to_primval(left.overflowing_offset(right as u64, self.memory.layout)),
302+
303+
BitAnd if !signed => {
304+
let base_mask : u64 = !(self.memory.get(left.alloc_id)?.align - 1);
305+
let right = right as u64;
306+
if right & base_mask == base_mask {
307+
// Case 1: The base address bits are all preserved, i.e., right is all-1 there
308+
(PrimVal::Ptr(Pointer::new(left.alloc_id, left.offset & right)), false)
309+
} else if right & base_mask == 0 {
310+
// Case 2: The base address bits are all taken away, i.e., right is all-0 there
311+
(PrimVal::from_u128((left.offset & right) as u128), false)
312+
} else {
313+
return Err(EvalError::ReadPointerAsBytes);
314+
}
315+
}
316+
317+
_ => {
318+
let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" });
319+
return Err(EvalError::Unimplemented(msg));
320+
}
302321
})
303322
}
304323
}
@@ -314,7 +333,7 @@ pub fn unary_op<'tcx>(
314333
let bytes = val.to_bytes()?;
315334

316335
let result_bytes = match (un_op, val_kind) {
317-
(Not, Bool) => !bytes_to_bool(bytes) as u128,
336+
(Not, Bool) => !val.to_bool()? as u128,
318337

319338
(Not, U8) => !(bytes as u8) as u128,
320339
(Not, U16) => !(bytes as u16) as u128,

src/terminator/intrinsic.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
154154

155155
"ctpop" |
156156
"cttz" |
157+
"cttz_nonzero" |
157158
"ctlz" |
159+
"ctlz_nonzero" |
158160
"bswap" => {
159161
let ty = substs.type_at(0);
160-
let num = self.value_to_primval(arg_vals[0], ty)?;
162+
let num = self.value_to_primval(arg_vals[0], ty)?.to_bytes()?;
161163
let kind = self.ty_to_primval_kind(ty)?;
162-
let num = numeric_intrinsic(intrinsic_name, num, kind)?;
164+
let num = if intrinsic_name.ends_with("_nonzero") {
165+
if num == 0 {
166+
return Err(EvalError::Intrinsic(format!("{} called on 0", intrinsic_name)))
167+
}
168+
numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), num, kind)?
169+
} else {
170+
numeric_intrinsic(intrinsic_name, num, kind)?
171+
};
163172
self.write_primval(dest, num, ty)?;
164173
}
165174

@@ -538,13 +547,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
538547

539548
fn numeric_intrinsic<'tcx>(
540549
name: &str,
541-
val: PrimVal,
550+
bytes: u128,
542551
kind: PrimValKind
543552
) -> EvalResult<'tcx, PrimVal> {
544553
macro_rules! integer_intrinsic {
545554
($method:ident) => ({
546-
let bytes = val.to_bytes()?;
547-
548555
use value::PrimValKind::*;
549556
let result_bytes = match kind {
550557
I8 => (bytes as i8).$method() as u128,
@@ -557,7 +564,7 @@ fn numeric_intrinsic<'tcx>(
557564
U64 => (bytes as u64).$method() as u128,
558565
I128 => (bytes as i128).$method() as u128,
559566
U128 => bytes.$method() as u128,
560-
_ => bug!("invalid `{}` argument: {:?}", name, val),
567+
_ => bug!("invalid `{}` argument: {:?}", name, bytes),
561568
};
562569

563570
PrimVal::Bytes(result_bytes)

src/value.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@ pub(super) fn f64_to_bytes(f: f64) -> u128 {
2323
unsafe { transmute::<f64, u64>(f) as u128 }
2424
}
2525

26-
pub(super) fn bytes_to_bool(n: u128) -> bool {
27-
// FIXME(solson): Can we reach here due to user error?
28-
debug_assert!(n == 0 || n == 1, "bytes interpreted as bool were {}", n);
29-
n & 1 == 1
30-
}
31-
3226
/// A `Value` represents a single self-contained Rust value.
3327
///
3428
/// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![allow(dead_code)]
12+
13+
use std::mem;
14+
15+
enum Tag<A> {
16+
Tag2(A)
17+
}
18+
19+
struct Rec {
20+
c8: u8,
21+
t: Tag<u64>
22+
}
23+
24+
fn mk_rec() -> Rec {
25+
return Rec { c8:0, t:Tag::Tag2(0) };
26+
}
27+
28+
fn is_u64_aligned(u: &Tag<u64>) -> bool {
29+
let p: usize = unsafe { mem::transmute(u) };
30+
let u64_align = std::mem::align_of::<u64>();
31+
return (p & (u64_align + 1)) == 0; //~ ERROR a raw memory access tried to access part of a pointer value as raw bytes
32+
}
33+
34+
pub fn main() {
35+
let x = mk_rec();
36+
assert!(is_u64_aligned(&x.t));
37+
}

tests/compile-fail/ctlz_nonzero.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![feature(intrinsics)]
2+
3+
mod rusti {
4+
extern "rust-intrinsic" {
5+
pub fn ctlz_nonzero<T>(x: T) -> T;
6+
}
7+
}
8+
9+
pub fn main() {
10+
unsafe {
11+
use rusti::*;
12+
13+
ctlz_nonzero(0u8); //~ ERROR: ctlz_nonzero called on 0
14+
}
15+
}

tests/compile-fail/cttz_nonzero.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![feature(intrinsics)]
2+
3+
mod rusti {
4+
extern "rust-intrinsic" {
5+
pub fn cttz_nonzero<T>(x: T) -> T;
6+
}
7+
}
8+
9+
pub fn main() {
10+
unsafe {
11+
use rusti::*;
12+
13+
cttz_nonzero(0u8); //~ ERROR: cttz_nonzero called on 0
14+
}
15+
}

tests/run-pass-fullmir/hashmap.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,24 @@ use std::collections::{self, HashMap};
22
use std::hash::BuildHasherDefault;
33

44
fn main() {
5-
let map : HashMap<String, i32, BuildHasherDefault<collections::hash_map::DefaultHasher>> = Default::default();
5+
let mut map : HashMap<i32, i32, BuildHasherDefault<collections::hash_map::DefaultHasher>> = Default::default();
6+
map.insert(0, 0);
67
assert_eq!(map.values().fold(0, |x, y| x+y), 0);
78

8-
// TODO: This performs bit operations on the least significant bit of a pointer
9-
// for i in 0..33 {
10-
// map.insert(format!("key_{}", i), i);
11-
// assert_eq!(map.values().fold(0, |x, y| x+y), i*(i+1)/2);
12-
// }
9+
let table_base = map.get(&0).unwrap() as *const _;
10+
11+
let num = 22; // large enough to trigger a resize
12+
for i in 1..num {
13+
map.insert(i, i);
14+
}
15+
assert!(table_base != map.get(&0).unwrap() as *const _); // make sure relocation happened
16+
assert_eq!(map.values().fold(0, |x, y| x+y), num*(num-1)/2); // check the right things are in the table now
17+
18+
// Inserting again replaces the existing entries
19+
for i in 0..num {
20+
map.insert(i, num-1-i);
21+
}
22+
assert_eq!(map.values().fold(0, |x, y| x+y), num*(num-1)/2);
1323

1424
// TODO: Test Entry API
1525
}

tests/run-pass/intrinsics-integer.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ mod rusti {
1414
extern "rust-intrinsic" {
1515
pub fn ctpop<T>(x: T) -> T;
1616
pub fn ctlz<T>(x: T) -> T;
17+
pub fn ctlz_nonzero<T>(x: T) -> T;
1718
pub fn cttz<T>(x: T) -> T;
19+
pub fn cttz_nonzero<T>(x: T) -> T;
1820
pub fn bswap<T>(x: T) -> T;
1921
}
2022
}
@@ -68,6 +70,21 @@ pub fn main() {
6870
assert_eq!(ctlz(100u32), 25); assert_eq!(ctlz(100i32), 25);
6971
assert_eq!(ctlz(100u64), 57); assert_eq!(ctlz(100i64), 57);
7072

73+
assert_eq!(ctlz_nonzero(1u8), 7); assert_eq!(ctlz_nonzero(1i8), 7);
74+
assert_eq!(ctlz_nonzero(1u16), 15); assert_eq!(ctlz_nonzero(1i16), 15);
75+
assert_eq!(ctlz_nonzero(1u32), 31); assert_eq!(ctlz_nonzero(1i32), 31);
76+
assert_eq!(ctlz_nonzero(1u64), 63); assert_eq!(ctlz_nonzero(1i64), 63);
77+
78+
assert_eq!(ctlz_nonzero(10u8), 4); assert_eq!(ctlz_nonzero(10i8), 4);
79+
assert_eq!(ctlz_nonzero(10u16), 12); assert_eq!(ctlz_nonzero(10i16), 12);
80+
assert_eq!(ctlz_nonzero(10u32), 28); assert_eq!(ctlz_nonzero(10i32), 28);
81+
assert_eq!(ctlz_nonzero(10u64), 60); assert_eq!(ctlz_nonzero(10i64), 60);
82+
83+
assert_eq!(ctlz_nonzero(100u8), 1); assert_eq!(ctlz_nonzero(100i8), 1);
84+
assert_eq!(ctlz_nonzero(100u16), 9); assert_eq!(ctlz_nonzero(100i16), 9);
85+
assert_eq!(ctlz_nonzero(100u32), 25); assert_eq!(ctlz_nonzero(100i32), 25);
86+
assert_eq!(ctlz_nonzero(100u64), 57); assert_eq!(ctlz_nonzero(100i64), 57);
87+
7188
assert_eq!(cttz(-1i8 as u8), 0); assert_eq!(cttz(-1i8), 0);
7289
assert_eq!(cttz(-1i16 as u16), 0); assert_eq!(cttz(-1i16), 0);
7390
assert_eq!(cttz(-1i32 as u32), 0); assert_eq!(cttz(-1i32), 0);
@@ -93,6 +110,26 @@ pub fn main() {
93110
assert_eq!(cttz(100u32), 2); assert_eq!(cttz(100i32), 2);
94111
assert_eq!(cttz(100u64), 2); assert_eq!(cttz(100i64), 2);
95112

113+
assert_eq!(cttz_nonzero(-1i8 as u8), 0); assert_eq!(cttz_nonzero(-1i8), 0);
114+
assert_eq!(cttz_nonzero(-1i16 as u16), 0); assert_eq!(cttz_nonzero(-1i16), 0);
115+
assert_eq!(cttz_nonzero(-1i32 as u32), 0); assert_eq!(cttz_nonzero(-1i32), 0);
116+
assert_eq!(cttz_nonzero(-1i64 as u64), 0); assert_eq!(cttz_nonzero(-1i64), 0);
117+
118+
assert_eq!(cttz_nonzero(1u8), 0); assert_eq!(cttz_nonzero(1i8), 0);
119+
assert_eq!(cttz_nonzero(1u16), 0); assert_eq!(cttz_nonzero(1i16), 0);
120+
assert_eq!(cttz_nonzero(1u32), 0); assert_eq!(cttz_nonzero(1i32), 0);
121+
assert_eq!(cttz_nonzero(1u64), 0); assert_eq!(cttz_nonzero(1i64), 0);
122+
123+
assert_eq!(cttz_nonzero(10u8), 1); assert_eq!(cttz_nonzero(10i8), 1);
124+
assert_eq!(cttz_nonzero(10u16), 1); assert_eq!(cttz_nonzero(10i16), 1);
125+
assert_eq!(cttz_nonzero(10u32), 1); assert_eq!(cttz_nonzero(10i32), 1);
126+
assert_eq!(cttz_nonzero(10u64), 1); assert_eq!(cttz_nonzero(10i64), 1);
127+
128+
assert_eq!(cttz_nonzero(100u8), 2); assert_eq!(cttz_nonzero(100i8), 2);
129+
assert_eq!(cttz_nonzero(100u16), 2); assert_eq!(cttz_nonzero(100i16), 2);
130+
assert_eq!(cttz_nonzero(100u32), 2); assert_eq!(cttz_nonzero(100i32), 2);
131+
assert_eq!(cttz_nonzero(100u64), 2); assert_eq!(cttz_nonzero(100i64), 2);
132+
96133
assert_eq!(bswap(0x0Au8), 0x0A); // no-op
97134
assert_eq!(bswap(0x0Ai8), 0x0A); // no-op
98135
assert_eq!(bswap(0x0A0Bu16), 0x0B0A);

tests/compile-fail/tag-align-dyn-u64.rs renamed to tests/run-pass/tag-align-dyn-u64.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ fn mk_rec() -> Rec {
2828
fn is_u64_aligned(u: &Tag<u64>) -> bool {
2929
let p: usize = unsafe { mem::transmute(u) };
3030
let u64_align = std::mem::align_of::<u64>();
31-
return (p & (u64_align - 1)) == 0; //~ ERROR a raw memory access tried to access part of a pointer value as raw bytes
31+
return (p & (u64_align - 1)) == 0;
3232
}
3333

3434
pub fn main() {

0 commit comments

Comments
 (0)