Skip to content

Commit c66e7fa

Browse files
committed
Auto merge of #43903 - oli-obk:alignto, r=aturon
Add align_offset intrinsic see rust-lang/rfcs#2043 for details and the plan towards stabilization (reexport in `core::mem` via various convenience functions) as per @scottmcm 's [comment](rust-lang/rfcs#2043 (comment)), this is just the intrinsic (which is obviously unstable).
2 parents b58e31a + be96ad2 commit c66e7fa

File tree

4 files changed

+99
-1
lines changed

4 files changed

+99
-1
lines changed

src/libcore/intrinsics.rs

+77
Original file line numberDiff line numberDiff line change
@@ -1343,4 +1343,81 @@ extern "rust-intrinsic" {
13431343
/// on MSVC it's `*mut [usize; 2]`. For more information see the compiler's
13441344
/// source as well as std's catch implementation.
13451345
pub fn try(f: fn(*mut u8), data: *mut u8, local_ptr: *mut u8) -> i32;
1346+
1347+
/// Computes the byte offset that needs to be applied to `ptr` in order to
1348+
/// make it aligned to `align`.
1349+
/// If it is not possible to align `ptr`, the implementation returns
1350+
/// `usize::max_value()`.
1351+
///
1352+
/// There are no guarantees whatsover that offsetting the pointer will not
1353+
/// overflow or go beyond the allocation that `ptr` points into.
1354+
/// It is up to the caller to ensure that the returned offset is correct
1355+
/// in all terms other than alignment.
1356+
///
1357+
/// # Examples
1358+
///
1359+
/// Accessing adjacent `u8` as `u16`
1360+
///
1361+
/// ```
1362+
/// # #![feature(core_intrinsics)]
1363+
/// # fn foo(n: usize) {
1364+
/// # use std::intrinsics::align_offset;
1365+
/// # use std::mem::align_of;
1366+
/// # unsafe {
1367+
/// let x = [5u8, 6u8, 7u8, 8u8, 9u8];
1368+
/// let ptr = &x[n] as *const u8;
1369+
/// let offset = align_offset(ptr as *const (), align_of::<u16>());
1370+
/// if offset < x.len() - n - 1 {
1371+
/// let u16_ptr = ptr.offset(offset as isize) as *const u16;
1372+
/// assert_ne!(*u16_ptr, 500);
1373+
/// } else {
1374+
/// // while the pointer can be aligned via `offset`, it would point
1375+
/// // outside the allocation
1376+
/// }
1377+
/// # } }
1378+
/// ```
1379+
#[cfg(not(stage0))]
1380+
pub fn align_offset(ptr: *const (), align: usize) -> usize;
1381+
}
1382+
1383+
#[cfg(stage0)]
1384+
/// Computes the byte offset that needs to be applied to `ptr` in order to
1385+
/// make it aligned to `align`.
1386+
/// If it is not possible to align `ptr`, the implementation returns
1387+
/// `usize::max_value()`.
1388+
///
1389+
/// There are no guarantees whatsover that offsetting the pointer will not
1390+
/// overflow or go beyond the allocation that `ptr` points into.
1391+
/// It is up to the caller to ensure that the returned offset is correct
1392+
/// in all terms other than alignment.
1393+
///
1394+
/// # Examples
1395+
///
1396+
/// Accessing adjacent `u8` as `u16`
1397+
///
1398+
/// ```
1399+
/// # #![feature(core_intrinsics)]
1400+
/// # fn foo(n: usize) {
1401+
/// # use std::intrinsics::align_offset;
1402+
/// # use std::mem::align_of;
1403+
/// # unsafe {
1404+
/// let x = [5u8, 6u8, 7u8, 8u8, 9u8];
1405+
/// let ptr = &x[n] as *const u8;
1406+
/// let offset = align_offset(ptr as *const (), align_of::<u16>());
1407+
/// if offset < x.len() - n - 1 {
1408+
/// let u16_ptr = ptr.offset(offset as isize) as *const u16;
1409+
/// assert_ne!(*u16_ptr, 500);
1410+
/// } else {
1411+
/// // while the pointer can be aligned via `offset`, it would point
1412+
/// // outside the allocation
1413+
/// }
1414+
/// # } }
1415+
/// ```
1416+
pub unsafe fn align_offset(ptr: *const (), align: usize) -> usize {
1417+
let offset = ptr as usize % align;
1418+
if offset == 0 {
1419+
0
1420+
} else {
1421+
align - offset
1422+
}
13461423
}

src/libcore/str/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use fmt;
2323
use iter::{Map, Cloned, FusedIterator};
2424
use slice::{self, SliceIndex};
2525
use mem;
26+
use intrinsics::align_offset;
2627

2728
pub mod pattern;
2829

@@ -1468,7 +1469,10 @@ fn run_utf8_validation(v: &[u8]) -> Result<(), Utf8Error> {
14681469
// When the pointer is aligned, read 2 words of data per iteration
14691470
// until we find a word containing a non-ascii byte.
14701471
let ptr = v.as_ptr();
1471-
let align = (ptr as usize + index) & (usize_bytes - 1);
1472+
let align = unsafe {
1473+
// the offset is safe, because `index` is guaranteed inbounds
1474+
align_offset(ptr.offset(index as isize) as *const (), usize_bytes)
1475+
};
14721476
if align == 0 {
14731477
while index < blocks_end {
14741478
unsafe {

src/librustc_trans/intrinsic.rs

+12
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,18 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bcx: &Builder<'a, 'tcx>,
383383
_ => C_null(llret_ty)
384384
}
385385
}
386+
387+
"align_offset" => {
388+
// `ptr as usize`
389+
let ptr_val = bcx.ptrtoint(llargs[0], bcx.ccx.int_type());
390+
// `ptr_val % align`
391+
let offset = bcx.urem(ptr_val, llargs[1]);
392+
let zero = C_null(bcx.ccx.int_type());
393+
// `offset == 0`
394+
let is_zero = bcx.icmp(llvm::IntPredicate::IntEQ, offset, zero);
395+
// `if offset == 0 { 0 } else { offset - align }`
396+
bcx.select(is_zero, zero, bcx.sub(offset, llargs[1]))
397+
}
386398
name if name.starts_with("simd_") => {
387399
generic_simd_intrinsic(bcx, name,
388400
callee_ty,

src/librustc_typeck/check/intrinsic.rs

+5
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,11 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
313313
(0, vec![tcx.mk_fn_ptr(fn_ty), mut_u8, mut_u8], tcx.types.i32)
314314
}
315315

316+
"align_offset" => {
317+
let ptr_ty = tcx.mk_imm_ptr(tcx.mk_nil());
318+
(0, vec![ptr_ty, tcx.types.usize], tcx.types.usize)
319+
},
320+
316321
ref other => {
317322
struct_span_err!(tcx.sess, it.span, E0093,
318323
"unrecognized intrinsic function: `{}`",

0 commit comments

Comments
 (0)