https://github.com/eleviant updated 
https://github.com/llvm/llvm-project/pull/196392

>From 73e961607430f7b0871352c9e5ae2d228b8b2d95 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 14 Apr 2026 16:17:11 +0200
Subject: [PATCH 1/2] [clang] Relax pointer subtraction assumptions under
 -fms-kernel

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 perfectly aligned offsets for pointer difference
  computations.

- `sdiv exact` may be folded into `ashr` for power-of-two element sizes,
  changing rounding semantics.

Under `-fms-kernel`, these assumptions are too strict and may break valid
kernel-style code patterns.

Mitigate the issue by applying `-fwrapv-pointer` semantics and replacing
`sdiv exact` with regular `sdiv` when `-fms-kernel` is enabled.
---
 clang/lib/CodeGen/CGExprScalar.cpp        |  3 ++
 clang/lib/Frontend/CompilerInvocation.cpp |  2 +-
 clang/test/CodeGen/MSKernel/subptr.c      | 43 +++++++++++++++++++++++
 3 files changed, 47 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/CodeGen/MSKernel/subptr.c

diff --git a/clang/lib/CodeGen/CGExprScalar.cpp 
b/clang/lib/CodeGen/CGExprScalar.cpp
index c8a8ec7b6d928..9062252383a3d 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -4990,6 +4990,9 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
     divisor = CGF.CGM.getSize(elementSize);
   }
 
+  // With -fms-kernel we emit normal sdiv to mitigate UB.
+  if (CGF.getLangOpts().Kernel)
+    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/lib/Frontend/CompilerInvocation.cpp 
b/clang/lib/Frontend/CompilerInvocation.cpp
index 60749104252af..1aa75acd2f59a 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -4215,7 +4215,7 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, 
ArgList &Args,
   }
   else if (Args.hasArg(OPT_fwrapv))
     Opts.setSignedOverflowBehavior(LangOptions::SOB_Defined);
-  if (Args.hasArg(OPT_fwrapv_pointer))
+  if (Args.hasArg(OPT_fwrapv_pointer) || Opts.Kernel)
     Opts.PointerOverflowDefined = true;
 
   Opts.MSCompatibilityVersion = 0;
diff --git a/clang/test/CodeGen/MSKernel/subptr.c 
b/clang/test/CodeGen/MSKernel/subptr.c
new file mode 100644
index 0000000000000..20d6bbb5024b5
--- /dev/null
+++ b/clang/test/CodeGen/MSKernel/subptr.c
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -emit-llvm -O2 -triple x86_64-windows-msvc -fms-kernel 
-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
+
+// The getelementptr instructions using indexes which can't be statically
+// evaluated must not be inbounds
+// CHECK-LABEL: i32 @weird_func(ptr noundef %mem, i32 noundef %i, i32 noundef 
%j)
+// CHECK-NOT:   getelementptr inbounds
+
+// The value of x can be negative, so compiler can't prove we
+// always stay inbounds, even though max possible value of x
+// is less than the size of array
+// CHECK-LABEL: i32 @foo(i8 noundef %x)
+// CHECK-NOT:   getelementptr inbounds
+
+typedef struct S {
+  long a,b;
+  long v[2];
+} S;
+
+__declspec(noinline) long long sub(long* p, long* q) {
+  return p - q;
+}
+
+int weird_func(void* mem, long i, long j) {
+  S* ps = (S*)mem;
+  long* q = &ps->v[i];
+  long* p = &ps->v[j];
+  return sub(p, q);
+}
+
+long _g[256];
+long foo(char x) {
+  return _g[x];
+}
+

>From 80614ea4612605b6917d3cc1ce9fd6688615d77b Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Wed, 13 May 2026 17:45:46 +0200
Subject: [PATCH 2/2] Moved subtraction logic under 
 -fstable-pointer-subtraction

---
 clang/include/clang/Basic/LangOptions.def |  2 ++
 clang/include/clang/Options/Options.td    |  7 ++++
 clang/lib/CodeGen/CGExprScalar.cpp        |  3 +-
 clang/lib/Frontend/CompilerInvocation.cpp |  2 +-
 clang/test/CodeGen/MSKernel/subptr.c      | 43 -----------------------
 clang/test/CodeGen/ptr-subtract-stable.c  | 15 ++++++++
 6 files changed, 26 insertions(+), 46 deletions(-)
 delete mode 100644 clang/test/CodeGen/MSKernel/subptr.c
 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 3bbb40ba09f05..c7fcc6939e3cb 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -530,6 +530,8 @@ LANGOPT(EnableLifetimeSafetyTUAnalysis, 1, 0, 
NotCompatible, "Lifetime safety at
 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 753e3ac1b74a5..fddc2270ce718 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -4812,6 +4812,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 9062252383a3d..fad45dd7ebed8 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -4990,8 +4990,7 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
     divisor = CGF.CGM.getSize(elementSize);
   }
 
-  // With -fms-kernel we emit normal sdiv to mitigate UB.
-  if (CGF.getLangOpts().Kernel)
+  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
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp 
b/clang/lib/Frontend/CompilerInvocation.cpp
index 1aa75acd2f59a..60749104252af 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -4215,7 +4215,7 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, 
ArgList &Args,
   }
   else if (Args.hasArg(OPT_fwrapv))
     Opts.setSignedOverflowBehavior(LangOptions::SOB_Defined);
-  if (Args.hasArg(OPT_fwrapv_pointer) || Opts.Kernel)
+  if (Args.hasArg(OPT_fwrapv_pointer))
     Opts.PointerOverflowDefined = true;
 
   Opts.MSCompatibilityVersion = 0;
diff --git a/clang/test/CodeGen/MSKernel/subptr.c 
b/clang/test/CodeGen/MSKernel/subptr.c
deleted file mode 100644
index 20d6bbb5024b5..0000000000000
--- a/clang/test/CodeGen/MSKernel/subptr.c
+++ /dev/null
@@ -1,43 +0,0 @@
-// RUN: %clang_cc1 -emit-llvm -O2 -triple x86_64-windows-msvc -fms-kernel 
-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
-
-// The getelementptr instructions using indexes which can't be statically
-// evaluated must not be inbounds
-// CHECK-LABEL: i32 @weird_func(ptr noundef %mem, i32 noundef %i, i32 noundef 
%j)
-// CHECK-NOT:   getelementptr inbounds
-
-// The value of x can be negative, so compiler can't prove we
-// always stay inbounds, even though max possible value of x
-// is less than the size of array
-// CHECK-LABEL: i32 @foo(i8 noundef %x)
-// CHECK-NOT:   getelementptr inbounds
-
-typedef struct S {
-  long a,b;
-  long v[2];
-} S;
-
-__declspec(noinline) long long sub(long* p, long* q) {
-  return p - q;
-}
-
-int weird_func(void* mem, long i, long j) {
-  S* ps = (S*)mem;
-  long* q = &ps->v[i];
-  long* p = &ps->v[j];
-  return sub(p, q);
-}
-
-long _g[256];
-long foo(char x) {
-  return _g[x];
-}
-
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;
+}
+

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

Reply via email to