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
