From 1dc3a2b5fa6de4aab7854b122213971217f13d77 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 16 Aug 2017 15:41:09 +0200 Subject: [PATCH 1/2] Add align_offset intrinsic see https://github.com/rust-lang/rfcs/pull/2043 for details --- src/libcore/intrinsics.rs | 77 ++++++++++++++++++++++++++ src/libcore/str/mod.rs | 6 +- src/librustc_trans/intrinsic.rs | 12 ++++ src/librustc_typeck/check/intrinsic.rs | 5 ++ 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index ad776c8605ac8..d87b0475e0c3a 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1343,4 +1343,81 @@ extern "rust-intrinsic" { /// on MSVC it's `*mut [usize; 2]`. For more information see the compiler's /// source as well as std's catch implementation. pub fn try(f: fn(*mut u8), data: *mut u8, local_ptr: *mut u8) -> i32; + + /// Computes the byte offset that needs to be applied to `ptr` in order to + /// make it aligned to `align`. + /// If it is not possible to align `ptr`, the implementation returns + /// `usize::max_value()`. + /// + /// There are no guarantees whatsover that offsetting the pointer will not + /// overflow or go beyond the allocation that `ptr` points into. + /// It is up to the caller to ensure that the returned offset is correct + /// in all terms other than alignment. + /// + /// # Examples + /// + /// Accessing adjacent `u8` as `u16` + /// + /// ``` + /// # #![feature(core_intrinsics)] + /// # fn foo(n: usize) { + /// # use std::intrinsics::align_offset; + /// # use std::mem::align_of; + /// # unsafe { + /// let x = [5u8, 6u8, 7u8, 8u8, 9u8]; + /// let ptr = &x[n] as *const u8; + /// let offset = align_offset(ptr as *const (), align_of::()); + /// if offset < x.len() - n - 1 { + /// let u16_ptr = ptr.offset(offset as isize) as *const u16; + /// *u16_ptr = 500; + /// } else { + /// // while the pointer can be aligned via `offset`, it would point + /// // outside the allocation + /// } + /// # } } + /// ``` + #[cfg(not(stage0))] + pub fn align_offset(ptr: *const (), align: usize) -> usize; +} + +#[cfg(stage0)] +/// Computes the byte offset that needs to be applied to `ptr` in order to +/// make it aligned to `align`. +/// If it is not possible to align `ptr`, the implementation returns +/// `usize::max_value()`. +/// +/// There are no guarantees whatsover that offsetting the pointer will not +/// overflow or go beyond the allocation that `ptr` points into. +/// It is up to the caller to ensure that the returned offset is correct +/// in all terms other than alignment. +/// +/// # Examples +/// +/// Accessing adjacent `u8` as `u16` +/// +/// ``` +/// # #![feature(core_intrinsics)] +/// # fn foo(n: usize) { +/// # use std::intrinsics::align_offset; +/// # use std::mem::align_of; +/// # unsafe { +/// let x = [5u8, 6u8, 7u8, 8u8, 9u8]; +/// let ptr = &x[n] as *const u8; +/// let offset = align_offset(ptr as *const (), align_of::()); +/// if offset < x.len() - n - 1 { +/// let u16_ptr = ptr.offset(offset as isize) as *const u16; +/// *u16_ptr = 500; +/// } else { +/// // while the pointer can be aligned via `offset`, it would point +/// // outside the allocation +/// } +/// # } } +/// ``` +pub unsafe fn align_offset(ptr: *const (), align: usize) -> usize { + let offset = ptr as usize % align; + if offset == 0 { + 0 + } else { + align - offset + } } diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 4c99fe97dafa3..ed399bef44926 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -23,6 +23,7 @@ use fmt; use iter::{Map, Cloned, FusedIterator}; use slice::{self, SliceIndex}; use mem; +use intrinsics::align_offset; pub mod pattern; @@ -1468,7 +1469,10 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> { // When the pointer is aligned, read 2 words of data per iteration // until we find a word containing a non-ascii byte. let ptr = v.as_ptr(); - let align = (ptr as usize + index) & (usize_bytes - 1); + let align = unsafe { + // the offset is safe, because `index` is guaranteed inbounds + align_offset(ptr.offset(index as isize) as *const (), usize_bytes) + }; if align == 0 { while index < blocks_end { unsafe { diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 033ef988571dd..9a3c8a5079a2f 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -383,6 +383,18 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, _ => C_null(llret_ty) } } + + "align_offset" => { + // `ptr as usize` + let ptr_val = bcx.ptrtoint(llargs[0], bcx.ccx.int_type()); + // `ptr_val % align` + let offset = bcx.urem(ptr_val, llargs[1]); + let zero = C_null(bcx.ccx.int_type()); + // `offset == 0` + let is_zero = bcx.icmp(llvm::IntPredicate::IntEQ, offset, zero); + // `if offset == 0 { 0 } else { offset - align }` + bcx.select(is_zero, zero, bcx.sub(offset, llargs[1])) + } name if name.starts_with("simd_") => { generic_simd_intrinsic(bcx, name, callee_ty, diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 96643ae72abad..6fee7e58633fc 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -313,6 +313,11 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (0, vec![tcx.mk_fn_ptr(fn_ty), mut_u8, mut_u8], tcx.types.i32) } + "align_offset" => { + let ptr_ty = tcx.mk_imm_ptr(tcx.mk_nil()); + (0, vec![ptr_ty, tcx.types.usize], tcx.types.usize) + }, + ref other => { struct_span_err!(tcx.sess, it.span, E0093, "unrecognized intrinsic function: `{}`", From be96ad2c85b680be68d38ab7aa115f0b9c32b3d5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 22 Aug 2017 12:48:20 +0200 Subject: [PATCH 2/2] Update intrinsics.rs --- src/libcore/intrinsics.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index d87b0475e0c3a..607f6f3701799 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1369,7 +1369,7 @@ extern "rust-intrinsic" { /// let offset = align_offset(ptr as *const (), align_of::()); /// if offset < x.len() - n - 1 { /// let u16_ptr = ptr.offset(offset as isize) as *const u16; - /// *u16_ptr = 500; + /// assert_ne!(*u16_ptr, 500); /// } else { /// // while the pointer can be aligned via `offset`, it would point /// // outside the allocation @@ -1406,7 +1406,7 @@ extern "rust-intrinsic" { /// let offset = align_offset(ptr as *const (), align_of::()); /// if offset < x.len() - n - 1 { /// let u16_ptr = ptr.offset(offset as isize) as *const u16; -/// *u16_ptr = 500; +/// assert_ne!(*u16_ptr, 500); /// } else { /// // while the pointer can be aligned via `offset`, it would point /// // outside the allocation