[clang] [clang] Add flag for relaxed pointer subtraction (PR #196392)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 30 09:01:03 PDT 2026
https://github.com/eleviant updated https://github.com/llvm/llvm-project/pull/196392
>From b0ead085c553fd8970cde24a060f7002632db31d Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Tue, 14 Apr 2026 16:17:11 +0200
Subject: [PATCH 1/4] [clang] Introduce -fstable-pointer-subtraction flag
The C and C++ standards require both operands of a pointer subtraction to
refer to elements of the same array object. Clang/LLVM currently relies on
this rule in several optimizations:
* `inbounds` GEP introduces UB-based assumptions once the computed address
escapes the bounds of the originating object.
* `sdiv exact` assumes that the byte offset between two pointers is an exact
multiple of the pointee size when lowering pointer subtraction.
While `-fwrapv-pointer` allows Clang to emit regular GEPs instead of
`inbounds` GEPs, there is currently no equivalent mechanism to avoid
generating `sdiv exact` for pointer subtraction operations.
This becomes an issue for low-level code such as kernels and boot loaders,
where pointer arithmetic is often performed over externally defined memory
layouts rather than well-typed C objects. In such cases, the assumptions
required by `sdiv exact` may not hold, causing the result to become poison
and enabling incorrect optimizations.
Introduce `-fstable-pointer-subtraction` to suppress these assumptions and
preserve stable pointer subtraction semantics in the generated IR.
---
clang/include/clang/Basic/LangOptions.def | 2 ++
clang/include/clang/Options/Options.td | 7 +++++++
clang/lib/CodeGen/CGExprScalar.cpp | 2 ++
clang/test/CodeGen/ptr-subtract-stable.c | 15 +++++++++++++++
4 files changed, 26 insertions(+)
create mode 100644 clang/test/CodeGen/ptr-subtract-stable.c
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index d68784b7efbcd..b33a8b1e28027 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -532,6 +532,8 @@ LANGOPT(EnableLifetimeSafetyTUAnalysis, 1, 0, Benign, "Lifetime safety at transl
LANGOPT(PreserveVec3Type, 1, 0, NotCompatible, "Preserve 3-component vector type")
LANGOPT(Reflection , 1, 0, NotCompatible, "C++26 Reflection")
+LANGOPT(StablePointerSubtraction, 1, 0, NotCompatible, "Make unaligned pointer subtraction stable")
+
#undef LANGOPT
#undef ENUM_LANGOPT
#undef VALUE_LANGOPT
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 3a3952a4397be..77c6061ee102a 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -4879,6 +4879,13 @@ def fno_wrapv : Flag<["-"], "fno-wrapv">, Group<f_Group>,
def fwrapv_pointer : Flag<["-"], "fwrapv-pointer">, Group<f_Group>,
Visibility<[ClangOption, CLOption, CC1Option, FlangOption, FC1Option]>,
HelpText<"Treat pointer overflow as two's complement">;
+def fstable_pointer_subtraction :
+ Flag<["-"], "fstable-pointer-subtraction">, Group<f_Group>,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<
+ "Allow stable subtraction of pointers not aligned to object boundaries"
+ >,
+ MarshallingInfoFlag<LangOpts<"StablePointerSubtraction">>;
def fno_wrapv_pointer : Flag<["-"], "fno-wrapv-pointer">, Group<f_Group>,
Visibility<[ClangOption, CLOption, FlangOption]>;
def fwritable_strings : Flag<["-"], "fwritable-strings">, Group<f_Group>,
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 18ed6570730f4..7e1222f3e453b 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -5031,6 +5031,8 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
divisor = CGF.CGM.getSize(elementSize);
}
+ if (CGF.getLangOpts().StablePointerSubtraction)
+ return Builder.CreateSDiv(diffInChars, divisor, "sub.ptr.div");
// Otherwise, do a full sdiv. This uses the "exact" form of sdiv, since
// pointer difference in C is only defined in the case where both operands
// are pointing to elements of an array.
diff --git a/clang/test/CodeGen/ptr-subtract-stable.c b/clang/test/CodeGen/ptr-subtract-stable.c
new file mode 100644
index 0000000000000..d18dd16cb4982
--- /dev/null
+++ b/clang/test/CodeGen/ptr-subtract-stable.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -emit-llvm -O2 -triple x86_64-windows-msvc -fstable-pointer-subtraction -fms-extensions %s -o - | FileCheck %s
+
+// Check that pointer subtraction isn't nuw/nsv and sdiv isn't exact
+// CHECK-LABEL: i64 @sub(ptr noundef %p, ptr noundef %q)
+// CHECK-NEXT: entry:
+// CHECK-NEXT: %sub.ptr.lhs.cast = ptrtoint ptr %p to i64
+// CHECK-NEXT: %sub.ptr.rhs.cast = ptrtoint ptr %q to i64
+// CHECK-NEXT: %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast
+// CHECK-NEXT: %sub.ptr.div = sdiv i64 %sub.ptr.sub, 4
+// CHECK-NEXT: ret i64 %sub.ptr.div
+
+__declspec(noinline) long long sub(long* p, long* q) {
+ return p - q;
+}
+
>From b432acc8a93820f9201050c2b05afa1e99057859 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Wed, 27 May 2026 13:30:22 +0200
Subject: [PATCH 2/4] Add DocBrief
---
clang/include/clang/Options/Options.td | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 77c6061ee102a..a2d5573ef8c76 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -4885,6 +4885,12 @@ def fstable_pointer_subtraction :
HelpText<
"Allow stable subtraction of pointers not aligned to object boundaries"
>,
+ DocBrief<
+ "When subtracting two pointers, do not assume that the byte difference is an "
+ "exact multiple of the pointee type size. The computed result is rounded "
+ "toward zero instead of producing a poison value. Users should prefer casting "
+ "pointers to ``char *`` before subtracting instead of relying on this flag."
+ >,
MarshallingInfoFlag<LangOpts<"StablePointerSubtraction">>;
def fno_wrapv_pointer : Flag<["-"], "fno-wrapv-pointer">, Group<f_Group>,
Visibility<[ClangOption, CLOption, FlangOption]>;
>From 664acc143e5e9a62863f7e16f008175879d67284 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Tue, 2 Jun 2026 13:19:54 +0200
Subject: [PATCH 3/4] Added documentation
---
clang/docs/UsersManual.rst | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 05a1ab7c2022a..58f91c7bda50a 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -2761,6 +2761,20 @@ are listed below.
If ``-falign-functions`` is specified, or if the function has an
``aligned`` attribute, this option is ignored.
+.. option:: -fstable-pointer-subtraction
+
+ The C and C++ standards require both operands of a pointer subtraction to
+ refer to elements of the same array object. Clang normally exploits this
+ rule when lowering pointer subtraction operations, for example by emitting
+ IR constructs such as ``sdiv exact`` that rely on the computed byte offset
+ being an exact multiple of the pointee size.
+
+ ``-fstable-pointer-subtraction`` disables these assumptions and emits IR
+ that preserves the behavior of pointer subtraction even when the standard
+ requirements are violated. This is primarily intended for low-level code,
+ such as kernels and boot loaders, that performs pointer arithmetic over
+ externally defined memory layouts rather than ordinary C or C++ objects.
+
.. _strict_aliasing:
Strict Aliasing
>From 839c90f7e16ea137484bf4a934ad16ffeec544d0 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Tue, 30 Jun 2026 17:47:31 +0200
Subject: [PATCH 4/4] Add release notes
---
clang/docs/ReleaseNotes.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index b372e5b58068b..92eef018bffa2 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -366,6 +366,8 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
- New option `-f[no-]strict-bool` added to control whether Clang can assume
that `bool` values loaded from memory cannot have a bit pattern other
than 0 or 1.
+- New option `-fstable-pointer-subtraction` added to preserve stable semantics
+ when subtracting pointers to unrelated objects.
- New option `-fcrash-diagnostics-tar` added to create an archive of crash
reproducer files for easier bug filing.
- There are a new pair of flags for riscv32 called `-mzilsd-word-align` and
More information about the cfe-commits
mailing list