Author: eleviant
Date: 2026-07-21T12:44:23+02:00
New Revision: 6d8d3f851820a61054000dbeaed3ab8e212685d3

URL: 
https://github.com/llvm/llvm-project/commit/6d8d3f851820a61054000dbeaed3ab8e212685d3
DIFF: 
https://github.com/llvm/llvm-project/commit/6d8d3f851820a61054000dbeaed3ab8e212685d3.diff

LOG: [clang] Add flag for making pointer subtraction defined (#196392)

The C and C++ standards require both operands of 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 assumptions once the computed address
escapes the originating object bounds.

- `sdiv exact` assumes %op1 is divisable by %op2 otherwise it is a
poison value.

The first issue may be addressed with -fwrapv-pointer command line
option, however there is no option in clang to mitigate the second
issue. Patch adds a new -fdefined-pointer-subtraction to address this.

Added: 
    clang/test/CodeGen/ptr-subtract-stable.c

Modified: 
    clang/docs/ReleaseNotes.md
    clang/docs/UsersManual.md
    clang/include/clang/Basic/LangOptions.def
    clang/include/clang/Options/Options.td
    clang/lib/CodeGen/CGExprScalar.cpp
    clang/lib/Driver/ToolChains/Clang.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 21f0eb1b7cf9f..28e4c98f63bdf 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -115,6 +115,9 @@ latest release, please see the [Clang Web 
Site](https://clang.llvm.org) or the
 
 ### New Compiler Flags
 
+- New option `-fdefined-pointer-subtraction` added to preserve stable semantics
+  when subtracting pointers to unrelated objects.
+
 ### Deprecated Compiler Flags
 
 ### Modified Compiler Flags

diff  --git a/clang/docs/UsersManual.md b/clang/docs/UsersManual.md
index 9bccf8e175298..54891619f1924 100644
--- a/clang/docs/UsersManual.md
+++ b/clang/docs/UsersManual.md
@@ -3002,6 +3002,22 @@ are listed below.
    ``aligned`` attribute, this option is ignored.
 ```
 
+```{eval-rst}
+.. option:: -fdefined-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.
+
+  ``-fdefined-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)=
 

diff  --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index 1fb491a54a278..0bccf316f2daf 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -534,6 +534,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 4afb089e8a51f..07fec9e9aac21 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -4933,6 +4933,19 @@ 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 fdefined_pointer_subtraction :
+  Flag<["-"], "fdefined-pointer-subtraction">, Group<f_Group>,
+  Visibility<[ClangOption, CC1Option]>,
+  HelpText<
+    "Define subtraction of pointers not aligned to object boundaries"
+  >,
+  DocBrief<
+    "When subtracting two pointers, do not assume that the byte 
diff erence is an "
+    "exact multiple of the pointee type size, thereby preventing such 
subtraction "
+    "from resulting in undefined behavior. Users should prefer casting 
pointers "
+    "to ``char *`` before subtracting them rather than relying on this flag."
+  >,
+  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 81016d3d5d308..8783b43846434 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -5021,6 +5021,8 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
     divisor = CGF.CGM.getSize(elementSize);
   }
 
+  if (CGF.getLangOpts().StablePointerSubtraction)
+    return Builder.CreateSDiv(
diff InChars, divisor, "sub.ptr.div");
   // Otherwise, do a full sdiv. This uses the "exact" form of sdiv, since
   // pointer 
diff erence in C is only defined in the case where both operands
   // are pointing to elements of an array.

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 8c9b98795b194..fb24d0b877ca6 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -5386,6 +5386,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
       CmdArgs.push_back("-funified-lto");
   }
 
+  if (Args.hasArg(options::OPT_fdefined_pointer_subtraction))
+    CmdArgs.push_back("-fdefined-pointer-subtraction");
+
   // If CollectArgsForIntegratedAssembler() isn't called below, claim the args
   // it claims when not running an assembler. Otherwise, clang would emit
   // "argument unused" warnings for assembler flags when e.g. adding "-E" to

diff  --git a/clang/test/CodeGen/ptr-subtract-stable.c 
b/clang/test/CodeGen/ptr-subtract-stable.c
new file mode 100644
index 0000000000000..88581fa51c4b1
--- /dev/null
+++ b/clang/test/CodeGen/ptr-subtract-stable.c
@@ -0,0 +1,15 @@
+// RUN: %clang -S -emit-llvm -O2 --target=x86_64-windows-msvc 
-fdefined-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;
+}
+


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to