Skip to content

Commit b08dd92

Browse files
committed
Auto merge of rust-lang#114720 - scottmcm:better-sub, r=workingjubilee
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`.
2 parents a6f8aa5 + ab6e2bc commit b08dd92

File tree

3 files changed

+58
-5
lines changed

3 files changed

+58
-5
lines changed

Diff for: library/core/src/ptr/const_ptr.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::*;
22
use crate::cmp::Ordering::{self, Equal, Greater, Less};
33
use crate::intrinsics::{self, const_eval_select};
4-
use crate::mem;
4+
use crate::mem::{self, SizedTypeProperties};
55
use crate::slice::{self, SliceIndex};
66

77
impl<T: ?Sized> *const T {
@@ -995,14 +995,23 @@ impl<T: ?Sized> *const T {
995995
#[stable(feature = "pointer_methods", since = "1.26.0")]
996996
#[must_use = "returns a new pointer rather than modifying its argument"]
997997
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
998+
// We could always go back to wrapping if unchecked becomes unacceptable
999+
#[rustc_allow_const_fn_unstable(const_int_unchecked_arith)]
9981000
#[inline(always)]
9991001
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
10001002
pub const unsafe fn sub(self, count: usize) -> Self
10011003
where
10021004
T: Sized,
10031005
{
1004-
// SAFETY: the caller must uphold the safety contract for `offset`.
1005-
unsafe { self.offset((count as isize).wrapping_neg()) }
1006+
if T::IS_ZST {
1007+
// Pointer arithmetic does nothing when the pointee is a ZST.
1008+
self
1009+
} else {
1010+
// SAFETY: the caller must uphold the safety contract for `offset`.
1011+
// Because the pointee is *not* a ZST, that means that `count` is
1012+
// at most `isize::MAX`, and thus the negation cannot overflow.
1013+
unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) }
1014+
}
10061015
}
10071016

10081017
/// Calculates the offset from a pointer in bytes (convenience for

Diff for: library/core/src/ptr/mut_ptr.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::*;
22
use crate::cmp::Ordering::{self, Equal, Greater, Less};
33
use crate::intrinsics::{self, const_eval_select};
4+
use crate::mem::SizedTypeProperties;
45
use crate::slice::{self, SliceIndex};
56

67
impl<T: ?Sized> *mut T {
@@ -1095,14 +1096,23 @@ impl<T: ?Sized> *mut T {
10951096
#[stable(feature = "pointer_methods", since = "1.26.0")]
10961097
#[must_use = "returns a new pointer rather than modifying its argument"]
10971098
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
1099+
// We could always go back to wrapping if unchecked becomes unacceptable
1100+
#[rustc_allow_const_fn_unstable(const_int_unchecked_arith)]
10981101
#[inline(always)]
10991102
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
11001103
pub const unsafe fn sub(self, count: usize) -> Self
11011104
where
11021105
T: Sized,
11031106
{
1104-
// SAFETY: the caller must uphold the safety contract for `offset`.
1105-
unsafe { self.offset((count as isize).wrapping_neg()) }
1107+
if T::IS_ZST {
1108+
// Pointer arithmetic does nothing when the pointee is a ZST.
1109+
self
1110+
} else {
1111+
// SAFETY: the caller must uphold the safety contract for `offset`.
1112+
// Because the pointee is *not* a ZST, that means that `count` is
1113+
// at most `isize::MAX`, and thus the negation cannot overflow.
1114+
unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) }
1115+
}
11061116
}
11071117

11081118
/// Calculates the offset from a pointer in bytes (convenience for

Diff for: tests/codegen/ptr-arithmetic.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// compile-flags: -O -Z merge-functions=disabled
2+
// ignore-debug (the extra assertions get in the way)
3+
4+
#![crate_type = "lib"]
5+
6+
// CHECK-LABEL: ptr @i32_add(
7+
// CHECK-SAME: [[WORD:i[0-9]+]] noundef %n)
8+
#[no_mangle]
9+
pub unsafe fn i32_add(p: *const i32, n: usize) -> *const i32 {
10+
// CHECK: %[[TEMP:.+]] = getelementptr inbounds i32, ptr %p, [[WORD]] %n
11+
// CHECK: ret ptr %[[TEMP]]
12+
p.add(n)
13+
}
14+
15+
// Ensure we tell LLVM that the negation in `sub` can't overflow.
16+
17+
// CHECK-LABEL: ptr @i32_sub(
18+
// CHECK-SAME: [[WORD:i[0-9]+]] noundef %n)
19+
#[no_mangle]
20+
pub unsafe fn i32_sub(p: *const i32, n: usize) -> *const i32 {
21+
// CHECK: %[[DELTA:.+]] = sub nsw [[WORD]] 0, %n
22+
// CHECK: %[[TEMP:.+]] = getelementptr inbounds i32, ptr %p, [[WORD]] %[[DELTA]]
23+
// CHECK: ret ptr %[[TEMP]]
24+
p.sub(n)
25+
}
26+
27+
// CHECK-LABEL: ptr @i32_offset(
28+
// CHECK-SAME: [[WORD:i[0-9]+]] noundef %d)
29+
#[no_mangle]
30+
pub unsafe fn i32_offset(p: *const i32, d: isize) -> *const i32 {
31+
// CHECK: %[[TEMP:.+]] = getelementptr inbounds i32, ptr %p, [[WORD]] %d
32+
// CHECK: ret ptr %[[TEMP]]
33+
p.offset(d)
34+
}

0 commit comments

Comments
 (0)