https://github.com/eleviant created 
https://github.com/llvm/llvm-project/pull/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 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.

>From 8012d852118f1045a7f2afd2ee082db4cf4b5336 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <[email protected]>
Date: Tue, 14 Apr 2026 16:17:11 +0200
Subject: [PATCH] [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 d60f1b37be50e..e090e815c956a 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -4982,6 +4982,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 c6e8644905964..4ed068a0481b8 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];
+}
+

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

Reply via email to