Skip to content

Commit 39ee3aa

Browse files
committed
Auto merge of #49297 - scottmcm:offset-from, r=dtolnay
Introduce unsafe offset_from on pointers Adds intrinsics::exact_div to take advantage of the unsafe, which reduces the implementation from ```asm sub rcx, rdx mov rax, rcx sar rax, 63 shr rax, 62 lea rax, [rax + rcx] sar rax, 2 ret ``` down to ```asm sub rcx, rdx sar rcx, 2 mov rax, rcx ret ``` (for `*const i32`) See discussion on the `offset_to` tracking issue #41079 Some open questions - Would you rather I split the intrinsic PR from the library PR? - Do we even want the safe version of the API? #41079 (comment) I've added some text to its documentation that even if it's not UB, it's useless to use it between pointers into different objects. and todos - [x] ~~I need to make a codegen test~~ Done - [x] ~~Can the subtraction use nsw/nuw?~~ No, it can't #49297 (comment) - [x] ~~Should there be `usize` variants of this, like there are now `add` and `sub` that you almost always want over `offset`? For example, I imagine `sub_ptr` that returns `usize` and where it's UB if the distance is negative.~~ Can wait for later; C gives a signed result #41079 (comment), so we might as well, and this existing to go with `offset` makes sense.
2 parents d351805 + 6264952 commit 39ee3aa

File tree

8 files changed

+295
-4
lines changed

8 files changed

+295
-4
lines changed

src/libcore/intrinsics.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,11 @@ extern "rust-intrinsic" {
13141314
/// [`std::u32::overflowing_mul`](../../std/primitive.u32.html#method.overflowing_mul)
13151315
pub fn mul_with_overflow<T>(x: T, y: T) -> (T, bool);
13161316

1317+
/// Performs an exact division, resulting in undefined behavior where
1318+
/// `x % y != 0` or `y == 0` or `x == T::min_value() && y == -1`
1319+
#[cfg(not(stage0))]
1320+
pub fn exact_div<T>(x: T, y: T) -> T;
1321+
13171322
/// Performs an unchecked division, resulting in undefined behavior
13181323
/// where y = 0 or x = `T::min_value()` and y = -1
13191324
pub fn unchecked_div<T>(x: T, y: T) -> T;
@@ -1396,3 +1401,8 @@ extern "rust-intrinsic" {
13961401
/// Probably will never become stable.
13971402
pub fn nontemporal_store<T>(ptr: *mut T, val: T);
13981403
}
1404+
1405+
#[cfg(stage0)]
1406+
pub unsafe fn exact_div<T>(a: T, b: T) -> T {
1407+
unchecked_div(a, b)
1408+
}

src/libcore/ptr.rs

+227-2
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ impl<T: ?Sized> *const T {
669669
/// `mem::size_of::<T>()` then the result of the division is rounded towards
670670
/// zero.
671671
///
672-
/// This function returns `None` if `T` is a zero-sized typed.
672+
/// This function returns `None` if `T` is a zero-sized type.
673673
///
674674
/// # Examples
675675
///
@@ -700,6 +700,124 @@ impl<T: ?Sized> *const T {
700700
}
701701
}
702702

703+
/// Calculates the distance between two pointers. The returned value is in
704+
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
705+
///
706+
/// This function is the inverse of [`offset`].
707+
///
708+
/// [`offset`]: #method.offset
709+
/// [`wrapping_offset_from`]: #method.wrapping_offset_from
710+
///
711+
/// # Safety
712+
///
713+
/// If any of the following conditions are violated, the result is Undefined
714+
/// Behavior:
715+
///
716+
/// * Both the starting and other pointer must be either in bounds or one
717+
/// byte past the end of the same allocated object.
718+
///
719+
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`.
720+
///
721+
/// * The distance between the pointers, in bytes, must be an exact multiple
722+
/// of the size of `T`.
723+
///
724+
/// * The distance being in bounds cannot rely on "wrapping around" the address space.
725+
///
726+
/// The compiler and standard library generally try to ensure allocations
727+
/// never reach a size where an offset is a concern. For instance, `Vec`
728+
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
729+
/// `ptr_into_vec.offset_from(vec.as_ptr())` is always safe.
730+
///
731+
/// Most platforms fundamentally can't even construct such an allocation.
732+
/// For instance, no known 64-bit platform can ever serve a request
733+
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
734+
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
735+
/// more than `isize::MAX` bytes with things like Physical Address
736+
/// Extension. As such, memory acquired directly from allocators or memory
737+
/// mapped files *may* be too large to handle with this function.
738+
///
739+
/// Consider using [`wrapping_offset_from`] instead if these constraints are
740+
/// difficult to satisfy. The only advantage of this method is that it
741+
/// enables more aggressive compiler optimizations.
742+
///
743+
/// # Panics
744+
///
745+
/// This function panics if `T` is a Zero-Sized Type ("ZST").
746+
///
747+
/// # Examples
748+
///
749+
/// Basic usage:
750+
///
751+
/// ```
752+
/// #![feature(ptr_offset_from)]
753+
///
754+
/// let a = [0; 5];
755+
/// let ptr1: *const i32 = &a[1];
756+
/// let ptr2: *const i32 = &a[3];
757+
/// unsafe {
758+
/// assert_eq!(ptr2.offset_from(ptr1), 2);
759+
/// assert_eq!(ptr1.offset_from(ptr2), -2);
760+
/// assert_eq!(ptr1.offset(2), ptr2);
761+
/// assert_eq!(ptr2.offset(-2), ptr1);
762+
/// }
763+
/// ```
764+
#[unstable(feature = "ptr_offset_from", issue = "41079")]
765+
#[inline]
766+
pub unsafe fn offset_from(self, origin: *const T) -> isize where T: Sized {
767+
let pointee_size = mem::size_of::<T>();
768+
assert!(0 < pointee_size && pointee_size <= isize::max_value() as usize);
769+
770+
// This is the same sequence that Clang emits for pointer subtraction.
771+
// It can be neither `nsw` nor `nuw` because the input is treated as
772+
// unsigned but then the output is treated as signed, so neither works.
773+
let d = isize::wrapping_sub(self as _, origin as _);
774+
intrinsics::exact_div(d, pointee_size as _)
775+
}
776+
777+
/// Calculates the distance between two pointers. The returned value is in
778+
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
779+
///
780+
/// If the address different between the two pointers is not a multiple of
781+
/// `mem::size_of::<T>()` then the result of the division is rounded towards
782+
/// zero.
783+
///
784+
/// Though this method is safe for any two pointers, note that its result
785+
/// will be mostly useless if the two pointers aren't into the same allocated
786+
/// object, for example if they point to two different local variables.
787+
///
788+
/// # Panics
789+
///
790+
/// This function panics if `T` is a zero-sized type.
791+
///
792+
/// # Examples
793+
///
794+
/// Basic usage:
795+
///
796+
/// ```
797+
/// #![feature(ptr_wrapping_offset_from)]
798+
///
799+
/// let a = [0; 5];
800+
/// let ptr1: *const i32 = &a[1];
801+
/// let ptr2: *const i32 = &a[3];
802+
/// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2);
803+
/// assert_eq!(ptr1.wrapping_offset_from(ptr2), -2);
804+
/// assert_eq!(ptr1.wrapping_offset(2), ptr2);
805+
/// assert_eq!(ptr2.wrapping_offset(-2), ptr1);
806+
///
807+
/// let ptr1: *const i32 = 3 as _;
808+
/// let ptr2: *const i32 = 13 as _;
809+
/// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2);
810+
/// ```
811+
#[unstable(feature = "ptr_wrapping_offset_from", issue = "41079")]
812+
#[inline]
813+
pub fn wrapping_offset_from(self, origin: *const T) -> isize where T: Sized {
814+
let pointee_size = mem::size_of::<T>();
815+
assert!(0 < pointee_size && pointee_size <= isize::max_value() as usize);
816+
817+
let d = isize::wrapping_sub(self as _, origin as _);
818+
d.wrapping_div(pointee_size as _)
819+
}
820+
703821
/// Calculates the offset from a pointer (convenience for `.offset(count as isize)`).
704822
///
705823
/// `count` is in units of T; e.g. a `count` of 3 represents a pointer
@@ -1316,7 +1434,7 @@ impl<T: ?Sized> *mut T {
13161434
/// `mem::size_of::<T>()` then the result of the division is rounded towards
13171435
/// zero.
13181436
///
1319-
/// This function returns `None` if `T` is a zero-sized typed.
1437+
/// This function returns `None` if `T` is a zero-sized type.
13201438
///
13211439
/// # Examples
13221440
///
@@ -1347,6 +1465,113 @@ impl<T: ?Sized> *mut T {
13471465
}
13481466
}
13491467

1468+
/// Calculates the distance between two pointers. The returned value is in
1469+
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
1470+
///
1471+
/// This function is the inverse of [`offset`].
1472+
///
1473+
/// [`offset`]: #method.offset-1
1474+
/// [`wrapping_offset_from`]: #method.wrapping_offset_from-1
1475+
///
1476+
/// # Safety
1477+
///
1478+
/// If any of the following conditions are violated, the result is Undefined
1479+
/// Behavior:
1480+
///
1481+
/// * Both the starting and other pointer must be either in bounds or one
1482+
/// byte past the end of the same allocated object.
1483+
///
1484+
/// * The distance between the pointers, **in bytes**, cannot overflow an `isize`.
1485+
///
1486+
/// * The distance between the pointers, in bytes, must be an exact multiple
1487+
/// of the size of `T`.
1488+
///
1489+
/// * The distance being in bounds cannot rely on "wrapping around" the address space.
1490+
///
1491+
/// The compiler and standard library generally try to ensure allocations
1492+
/// never reach a size where an offset is a concern. For instance, `Vec`
1493+
/// and `Box` ensure they never allocate more than `isize::MAX` bytes, so
1494+
/// `ptr_into_vec.offset_from(vec.as_ptr())` is always safe.
1495+
///
1496+
/// Most platforms fundamentally can't even construct such an allocation.
1497+
/// For instance, no known 64-bit platform can ever serve a request
1498+
/// for 2<sup>63</sup> bytes due to page-table limitations or splitting the address space.
1499+
/// However, some 32-bit and 16-bit platforms may successfully serve a request for
1500+
/// more than `isize::MAX` bytes with things like Physical Address
1501+
/// Extension. As such, memory acquired directly from allocators or memory
1502+
/// mapped files *may* be too large to handle with this function.
1503+
///
1504+
/// Consider using [`wrapping_offset_from`] instead if these constraints are
1505+
/// difficult to satisfy. The only advantage of this method is that it
1506+
/// enables more aggressive compiler optimizations.
1507+
///
1508+
/// # Panics
1509+
///
1510+
/// This function panics if `T` is a Zero-Sized Type ("ZST").
1511+
///
1512+
/// # Examples
1513+
///
1514+
/// Basic usage:
1515+
///
1516+
/// ```
1517+
/// #![feature(ptr_offset_from)]
1518+
///
1519+
/// let mut a = [0; 5];
1520+
/// let ptr1: *mut i32 = &mut a[1];
1521+
/// let ptr2: *mut i32 = &mut a[3];
1522+
/// unsafe {
1523+
/// assert_eq!(ptr2.offset_from(ptr1), 2);
1524+
/// assert_eq!(ptr1.offset_from(ptr2), -2);
1525+
/// assert_eq!(ptr1.offset(2), ptr2);
1526+
/// assert_eq!(ptr2.offset(-2), ptr1);
1527+
/// }
1528+
/// ```
1529+
#[unstable(feature = "ptr_offset_from", issue = "41079")]
1530+
#[inline]
1531+
pub unsafe fn offset_from(self, origin: *const T) -> isize where T: Sized {
1532+
(self as *const T).offset_from(origin)
1533+
}
1534+
1535+
/// Calculates the distance between two pointers. The returned value is in
1536+
/// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
1537+
///
1538+
/// If the address different between the two pointers is not a multiple of
1539+
/// `mem::size_of::<T>()` then the result of the division is rounded towards
1540+
/// zero.
1541+
///
1542+
/// Though this method is safe for any two pointers, note that its result
1543+
/// will be mostly useless if the two pointers aren't into the same allocated
1544+
/// object, for example if they point to two different local variables.
1545+
///
1546+
/// # Panics
1547+
///
1548+
/// This function panics if `T` is a zero-sized type.
1549+
///
1550+
/// # Examples
1551+
///
1552+
/// Basic usage:
1553+
///
1554+
/// ```
1555+
/// #![feature(ptr_wrapping_offset_from)]
1556+
///
1557+
/// let mut a = [0; 5];
1558+
/// let ptr1: *mut i32 = &mut a[1];
1559+
/// let ptr2: *mut i32 = &mut a[3];
1560+
/// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2);
1561+
/// assert_eq!(ptr1.wrapping_offset_from(ptr2), -2);
1562+
/// assert_eq!(ptr1.wrapping_offset(2), ptr2);
1563+
/// assert_eq!(ptr2.wrapping_offset(-2), ptr1);
1564+
///
1565+
/// let ptr1: *mut i32 = 3 as _;
1566+
/// let ptr2: *mut i32 = 13 as _;
1567+
/// assert_eq!(ptr2.wrapping_offset_from(ptr1), 2);
1568+
/// ```
1569+
#[unstable(feature = "ptr_wrapping_offset_from", issue = "41079")]
1570+
#[inline]
1571+
pub fn wrapping_offset_from(self, origin: *const T) -> isize where T: Sized {
1572+
(self as *const T).wrapping_offset_from(origin)
1573+
}
1574+
13501575
/// Computes the byte offset that needs to be applied in order to
13511576
/// make the pointer aligned to `align`.
13521577
/// If it is not possible to align the pointer, the implementation returns

src/librustc_llvm/ffi.rs

+5
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,11 @@ extern "C" {
935935
RHS: ValueRef,
936936
Name: *const c_char)
937937
-> ValueRef;
938+
pub fn LLVMBuildExactUDiv(B: BuilderRef,
939+
LHS: ValueRef,
940+
RHS: ValueRef,
941+
Name: *const c_char)
942+
-> ValueRef;
938943
pub fn LLVMBuildSDiv(B: BuilderRef,
939944
LHS: ValueRef,
940945
RHS: ValueRef,

src/librustc_trans/builder.rs

+7
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
344344
}
345345
}
346346

347+
pub fn exactudiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
348+
self.count_insn("exactudiv");
349+
unsafe {
350+
llvm::LLVMBuildExactUDiv(self.llbuilder, lhs, rhs, noname())
351+
}
352+
}
353+
347354
pub fn sdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef {
348355
self.count_insn("sdiv");
349356
unsafe {

src/librustc_trans/intrinsic.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>,
289289
"ctlz" | "ctlz_nonzero" | "cttz" | "cttz_nonzero" | "ctpop" | "bswap" |
290290
"bitreverse" | "add_with_overflow" | "sub_with_overflow" |
291291
"mul_with_overflow" | "overflowing_add" | "overflowing_sub" | "overflowing_mul" |
292-
"unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" => {
292+
"unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" | "exact_div" => {
293293
let ty = arg_tys[0];
294294
match int_type_width_signed(ty, cx) {
295295
Some((width, signed)) =>
@@ -343,6 +343,12 @@ pub fn trans_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>,
343343
"overflowing_add" => bx.add(args[0].immediate(), args[1].immediate()),
344344
"overflowing_sub" => bx.sub(args[0].immediate(), args[1].immediate()),
345345
"overflowing_mul" => bx.mul(args[0].immediate(), args[1].immediate()),
346+
"exact_div" =>
347+
if signed {
348+
bx.exactsdiv(args[0].immediate(), args[1].immediate())
349+
} else {
350+
bx.exactudiv(args[0].immediate(), args[1].immediate())
351+
},
346352
"unchecked_div" =>
347353
if signed {
348354
bx.sdiv(args[0].immediate(), args[1].immediate())

src/librustc_typeck/check/intrinsic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
283283
(1, vec![param(0), param(0)],
284284
tcx.intern_tup(&[param(0), tcx.types.bool])),
285285

286-
"unchecked_div" | "unchecked_rem" =>
286+
"unchecked_div" | "unchecked_rem" | "exact_div" =>
287287
(1, vec![param(0), param(0)], param(0)),
288288
"unchecked_shl" | "unchecked_shr" =>
289289
(1, vec![param(0), param(0)], param(0)),

src/rustllvm/RustWrapper.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -1492,3 +1492,11 @@ LLVMRustBuildVectorReduceFMax(LLVMBuilderRef, LLVMValueRef, bool) {
14921492
return nullptr;
14931493
}
14941494
#endif
1495+
1496+
#if LLVM_VERSION_LT(4, 0)
1497+
extern "C" LLVMValueRef
1498+
LLVMBuildExactUDiv(LLVMBuilderRef B, LLVMValueRef LHS,
1499+
LLVMValueRef RHS, const char *Name) {
1500+
return wrap(unwrap(B)->CreateExactUDiv(unwrap(LHS), unwrap(RHS), Name));
1501+
}
1502+
#endif

src/test/codegen/exact_div.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2018 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+
// compile-flags: -C no-prepopulate-passes
12+
13+
#![crate_type = "lib"]
14+
#![feature(core_intrinsics)]
15+
16+
use std::intrinsics::exact_div;
17+
18+
// CHECK-LABEL: @exact_sdiv
19+
#[no_mangle]
20+
pub unsafe fn exact_sdiv(x: i32, y: i32) -> i32 {
21+
// CHECK: sdiv exact
22+
exact_div(x, y)
23+
}
24+
25+
// CHECK-LABEL: @exact_udiv
26+
#[no_mangle]
27+
pub unsafe fn exact_udiv(x: u32, y: u32) -> u32 {
28+
// CHECK: udiv exact
29+
exact_div(x, y)
30+
}

0 commit comments

Comments
 (0)