Tell LLVM that the negation in <*const T>::sub
cannot overflow
Today it's just `sub` <https://rust.godbolt.org/z/8EzEPnMr5>; with this PR it's `sub nsw`.
This commit is contained in:
parent
8e7fd55131
commit
ab6e2bc3d0
@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
use crate::cmp::Ordering::{self, Equal, Greater, Less};
|
||||
use crate::intrinsics::{self, const_eval_select};
|
||||
use crate::mem;
|
||||
use crate::mem::{self, SizedTypeProperties};
|
||||
use crate::slice::{self, SliceIndex};
|
||||
|
||||
impl<T: ?Sized> *const T {
|
||||
@ -995,14 +995,23 @@ pub const fn guaranteed_ne(self, other: *const T) -> Option<bool>
|
||||
#[stable(feature = "pointer_methods", since = "1.26.0")]
|
||||
#[must_use = "returns a new pointer rather than modifying its argument"]
|
||||
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
|
||||
// We could always go back to wrapping if unchecked becomes unacceptable
|
||||
#[rustc_allow_const_fn_unstable(const_int_unchecked_arith)]
|
||||
#[inline(always)]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub const unsafe fn sub(self, count: usize) -> Self
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
// SAFETY: the caller must uphold the safety contract for `offset`.
|
||||
unsafe { self.offset((count as isize).wrapping_neg()) }
|
||||
if T::IS_ZST {
|
||||
// Pointer arithmetic does nothing when the pointee is a ZST.
|
||||
self
|
||||
} else {
|
||||
// SAFETY: the caller must uphold the safety contract for `offset`.
|
||||
// Because the pointee is *not* a ZST, that means that `count` is
|
||||
// at most `isize::MAX`, and thus the negation cannot overflow.
|
||||
unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the offset from a pointer in bytes (convenience for
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::*;
|
||||
use crate::cmp::Ordering::{self, Equal, Greater, Less};
|
||||
use crate::intrinsics::{self, const_eval_select};
|
||||
use crate::mem::SizedTypeProperties;
|
||||
use crate::slice::{self, SliceIndex};
|
||||
|
||||
impl<T: ?Sized> *mut T {
|
||||
@ -1095,14 +1096,23 @@ pub const fn guaranteed_ne(self, other: *mut T) -> Option<bool>
|
||||
#[stable(feature = "pointer_methods", since = "1.26.0")]
|
||||
#[must_use = "returns a new pointer rather than modifying its argument"]
|
||||
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
|
||||
// We could always go back to wrapping if unchecked becomes unacceptable
|
||||
#[rustc_allow_const_fn_unstable(const_int_unchecked_arith)]
|
||||
#[inline(always)]
|
||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||
pub const unsafe fn sub(self, count: usize) -> Self
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
// SAFETY: the caller must uphold the safety contract for `offset`.
|
||||
unsafe { self.offset((count as isize).wrapping_neg()) }
|
||||
if T::IS_ZST {
|
||||
// Pointer arithmetic does nothing when the pointee is a ZST.
|
||||
self
|
||||
} else {
|
||||
// SAFETY: the caller must uphold the safety contract for `offset`.
|
||||
// Because the pointee is *not* a ZST, that means that `count` is
|
||||
// at most `isize::MAX`, and thus the negation cannot overflow.
|
||||
unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the offset from a pointer in bytes (convenience for
|
||||
|
34
tests/codegen/ptr-arithmetic.rs
Normal file
34
tests/codegen/ptr-arithmetic.rs
Normal file
@ -0,0 +1,34 @@
|
||||
// compile-flags: -O -Z merge-functions=disabled
|
||||
// ignore-debug (the extra assertions get in the way)
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
// CHECK-LABEL: ptr @i32_add(
|
||||
// CHECK-SAME: [[WORD:i[0-9]+]] noundef %n)
|
||||
#[no_mangle]
|
||||
pub unsafe fn i32_add(p: *const i32, n: usize) -> *const i32 {
|
||||
// CHECK: %[[TEMP:.+]] = getelementptr inbounds i32, ptr %p, [[WORD]] %n
|
||||
// CHECK: ret ptr %[[TEMP]]
|
||||
p.add(n)
|
||||
}
|
||||
|
||||
// Ensure we tell LLVM that the negation in `sub` can't overflow.
|
||||
|
||||
// CHECK-LABEL: ptr @i32_sub(
|
||||
// CHECK-SAME: [[WORD:i[0-9]+]] noundef %n)
|
||||
#[no_mangle]
|
||||
pub unsafe fn i32_sub(p: *const i32, n: usize) -> *const i32 {
|
||||
// CHECK: %[[DELTA:.+]] = sub nsw [[WORD]] 0, %n
|
||||
// CHECK: %[[TEMP:.+]] = getelementptr inbounds i32, ptr %p, [[WORD]] %[[DELTA]]
|
||||
// CHECK: ret ptr %[[TEMP]]
|
||||
p.sub(n)
|
||||
}
|
||||
|
||||
// CHECK-LABEL: ptr @i32_offset(
|
||||
// CHECK-SAME: [[WORD:i[0-9]+]] noundef %d)
|
||||
#[no_mangle]
|
||||
pub unsafe fn i32_offset(p: *const i32, d: isize) -> *const i32 {
|
||||
// CHECK: %[[TEMP:.+]] = getelementptr inbounds i32, ptr %p, [[WORD]] %d
|
||||
// CHECK: ret ptr %[[TEMP]]
|
||||
p.offset(d)
|
||||
}
|
Loading…
Reference in New Issue
Block a user