Skip to content

Commit

Permalink
Merge pull request rust-lang#235 from RalfJung/intrinsics
Browse files Browse the repository at this point in the history
Implement some missing intrinsics
  • Loading branch information
oli-obk authored Jul 4, 2017
2 parents fa05ca9 + 19d6ad7 commit 10ec543
Show file tree
Hide file tree
Showing 12 changed files with 321 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
fn copy(&mut self, src: PrimVal, dest: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx> {
let size = self.type_size(ty)?.expect("cannot copy from an unsized type");
let align = self.type_align(ty)?;
self.memory.copy(src, dest, size, align)?;
self.memory.copy(src, dest, size, align, false)?;
Ok(())
}

Expand Down
8 changes: 7 additions & 1 deletion src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
Ok(())
}

pub fn copy(&mut self, src: PrimVal, dest: PrimVal, size: u64, align: u64) -> EvalResult<'tcx> {
pub fn copy(&mut self, src: PrimVal, dest: PrimVal, size: u64, align: u64, nonoverlapping: bool) -> EvalResult<'tcx> {
if size == 0 {
return Ok(());
}
Expand All @@ -675,6 +675,12 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
unsafe {
assert_eq!(size as usize as u64, size);
if src.alloc_id == dest.alloc_id {
if nonoverlapping {
if (src.offset <= dest.offset && src.offset + size > dest.offset) ||
(dest.offset <= src.offset && dest.offset + size > src.offset) {
return Err(EvalError::Intrinsic(format!("copy_nonoverlapping called on overlapping ranges")));
}
}
ptr::copy(src_bytes, dest_bytes, size as usize);
} else {
ptr::copy_nonoverlapping(src_bytes, dest_bytes, size as usize);
Expand Down
27 changes: 14 additions & 13 deletions src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,20 @@ macro_rules! int_shift {
($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
let l = $l;
let r = $r;
let r_wrapped = r as u32;
match $kind {
I8 => overflow!($int_op, l as i8, r),
I16 => overflow!($int_op, l as i16, r),
I32 => overflow!($int_op, l as i32, r),
I64 => overflow!($int_op, l as i64, r),
I128 => overflow!($int_op, l as i128, r),
U8 => overflow!($int_op, l as u8, r),
U16 => overflow!($int_op, l as u16, r),
U32 => overflow!($int_op, l as u32, r),
U64 => overflow!($int_op, l as u64, r),
U128 => overflow!($int_op, l as u128, r),
I8 => overflow!($int_op, l as i8, r_wrapped),
I16 => overflow!($int_op, l as i16, r_wrapped),
I32 => overflow!($int_op, l as i32, r_wrapped),
I64 => overflow!($int_op, l as i64, r_wrapped),
I128 => overflow!($int_op, l as i128, r_wrapped),
U8 => overflow!($int_op, l as u8, r_wrapped),
U16 => overflow!($int_op, l as u16, r_wrapped),
U32 => overflow!($int_op, l as u32, r_wrapped),
U64 => overflow!($int_op, l as u64, r_wrapped),
U128 => overflow!($int_op, l as u128, r_wrapped),
_ => bug!("int_shift should only be called on int primvals"),
}
}.map(|(val, over)| (val, over || r != r_wrapped as u128))
})
}

Expand Down Expand Up @@ -227,8 +228,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// These ops can have an RHS with a different numeric type.
if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) {
return match bin_op {
Shl => int_shift!(left_kind, overflowing_shl, l, r as u32),
Shr => int_shift!(left_kind, overflowing_shr, l, r as u32),
Shl => int_shift!(left_kind, overflowing_shl, l, r),
Shr => int_shift!(left_kind, overflowing_shr, l, r),
_ => bug!("it has already been checked that this is a shift op"),
};
}
Expand Down
37 changes: 35 additions & 2 deletions src/terminator/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {

"copy" |
"copy_nonoverlapping" => {
// FIXME: check whether overlapping occurs
let elem_ty = substs.type_at(0);
let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value");
if elem_size != 0 {
let elem_align = self.type_align(elem_ty)?;
let src = arg_vals[0].read_ptr(&self.memory)?;
let dest = arg_vals[1].read_ptr(&self.memory)?;
let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?;
self.memory.copy(src, dest, count * elem_size, elem_align)?;
self.memory.copy(src, dest, count * elem_size, elem_align, intrinsic_name.ends_with("_nonoverlapping"))?;
}
}

Expand Down Expand Up @@ -401,6 +400,40 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.write_value_to_ptr(arg_vals[0], PrimVal::Ptr(ptr), src_ty)?;
}

"unchecked_shl" => {
let bits = self.type_size(dest_ty)?.expect("intrinsic can't be called on unsized type") as u128 * 8;
let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?;
if rhs >= bits {
return Err(EvalError::Intrinsic(format!("Overflowing shift by {} in unchecked_shl", rhs)));
}
self.intrinsic_overflowing(mir::BinOp::Shl, &args[0], &args[1], dest, dest_ty)?;
}

"unchecked_shr" => {
let bits = self.type_size(dest_ty)?.expect("intrinsic can't be called on unsized type") as u128 * 8;
let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?;
if rhs >= bits {
return Err(EvalError::Intrinsic(format!("Overflowing shift by {} in unchecked_shr", rhs)));
}
self.intrinsic_overflowing(mir::BinOp::Shr, &args[0], &args[1], dest, dest_ty)?;
}

"unchecked_div" => {
let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?;
if rhs == 0 {
return Err(EvalError::Intrinsic(format!("Division by 0 in unchecked_div")));
}
self.intrinsic_overflowing(mir::BinOp::Div, &args[0], &args[1], dest, dest_ty)?;
}

"unchecked_rem" => {
let rhs = self.value_to_primval(arg_vals[1], substs.type_at(0))?.to_bytes()?;
if rhs == 0 {
return Err(EvalError::Intrinsic(format!("Division by 0 in unchecked_rem")));
}
self.intrinsic_overflowing(mir::BinOp::Rem, &args[0], &args[1], dest, dest_ty)?;
}

"uninit" => {
let size = dest_layout.size(&self.tcx.data_layout).bytes();
let uninit = |this: &mut Self, val: Value| {
Expand Down
24 changes: 24 additions & 0 deletions tests/compile-fail/copy_nonoverlapping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2015 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.

#![feature(core_intrinsics)]

use std::intrinsics::*;

//error-pattern: copy_nonoverlapping called on overlapping ranges

fn main() {
let mut data = [0u8; 16];
unsafe {
let a = &data[0] as *const _;
let b = &mut data[1] as *mut _;
std::ptr::copy_nonoverlapping(a, b, 2);
}
}
15 changes: 15 additions & 0 deletions tests/compile-fail/div-by-zero-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2015 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(const_err)]

fn main() {
let _n = 1 / 0; //~ ERROR: DivisionByZero
}
21 changes: 21 additions & 0 deletions tests/compile-fail/div-by-zero.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2015 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.

#![feature(core_intrinsics)]

use std::intrinsics::*;

//error-pattern: Division by 0 in unchecked_div

fn main() {
unsafe {
let _n = unchecked_div(1i64, 0);
}
}
16 changes: 16 additions & 0 deletions tests/compile-fail/overflowing-rsh-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2015 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(exceeding_bitshifts, const_err)]

fn main() {
// Make sure we catch overflows that would be hidden by first casting the RHS to u32
let _n = 1i64 >> (u32::max_value() as i64 + 1); //~ Overflow(Shr)
}
File renamed without changes.
21 changes: 21 additions & 0 deletions tests/compile-fail/overflowing-unchecked-rsh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2015 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.

#![feature(core_intrinsics)]

use std::intrinsics::*;

//error-pattern: Overflowing shift by 64 in unchecked_shr

fn main() {
unsafe {
let _n = unchecked_shr(1i64, 64);
}
}
Loading

0 comments on commit 10ec543

Please sign in to comment.