[clang] Rewrite offsetof with __builtin_offsetof with -fms-kernel (PR #193804)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 27 03:01:18 PDT 2026
https://github.com/eleviant updated https://github.com/llvm/llvm-project/pull/193804
>From 10961176c65a0ee7bc86c5ed70683f9e6a470534 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Fri, 10 Apr 2026 20:13:58 +0200
Subject: [PATCH 1/2] Rewrite offsetof with __builtin_offsetof with -fms-kernel
Patch rewrites portion of AST if it is detected as offset calculation,
e.g (size_t)&(((MyStruct*)0)->field). This expression is being rewritten
by this patch as (size_t)__builtin_offsetof(MyStruct, field). Patch may
affect compiler warnings, see test cases for details.
---
clang/include/clang/Sema/Sema.h | 4 +
clang/lib/Sema/CMakeLists.txt | 1 +
clang/lib/Sema/SemaCast.cpp | 6 +
clang/lib/Sema/SemaMSKernel.cpp | 71 +++
clang/test/CodeGen/MSKernel/ast-bytecode-c.c | 455 ++++++++++++++++++
.../CodeGen/MSKernel/containing-record.cpp | 57 +++
clang/test/CodeGen/MSKernel/nullptr.cl | 25 +
clang/test/CodeGen/MSKernel/offsetof.cpp | 39 ++
clang/test/CodeGen/MSKernel/struct-decl.c | 114 +++++
9 files changed, 772 insertions(+)
create mode 100644 clang/lib/Sema/SemaMSKernel.cpp
create mode 100644 clang/test/CodeGen/MSKernel/ast-bytecode-c.c
create mode 100644 clang/test/CodeGen/MSKernel/containing-record.cpp
create mode 100644 clang/test/CodeGen/MSKernel/nullptr.cl
create mode 100644 clang/test/CodeGen/MSKernel/offsetof.cpp
create mode 100644 clang/test/CodeGen/MSKernel/struct-decl.c
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 20f7d84cfc475..57262373486f7 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12430,6 +12430,10 @@ class Sema final : public SemaBase {
DeclarationName Name);
bool RebuildNestedNameSpecifierInCurrentInstantiation(CXXScopeSpec &SS);
+ /// Do MS kernel specific AST transformations, e.g replace
+ /// &((type*)0)->member with __builtin_offsetof(type, member)
+ ExprResult TransformForMSKernel(Expr *UOp);
+
ExprResult RebuildExprInCurrentInstantiation(Expr *E);
/// Rebuild the template parameters now that we know we're in a current
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 0ebf56ecffe69..8113e6714a076 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -67,6 +67,7 @@ add_clang_library(clangSema
SemaLoongArch.cpp
SemaM68k.cpp
SemaMIPS.cpp
+ SemaMSKernel.cpp
SemaMSP430.cpp
SemaModule.cpp
SemaNVPTX.cpp
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 0040f3aa1a891..589c978d82248 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -3478,6 +3478,12 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc,
Op.checkQualifiedDestType();
+ if (Op.Kind == CK_PointerToIntegral && Context.getLangOpts().Kernel) {
+ ExprResult Transformed =
+ TransformForMSKernel(Op.SrcExpr.get()->IgnoreParenImpCasts());
+ if (Transformed.isUsable())
+ return BuildCStyleCastExpr(LPLoc, CastTypeInfo, RPLoc, Transformed.get());
+ }
return Op.complete(CStyleCastExpr::Create(
Context, Op.ResultType, Op.ValueKind, Op.Kind, Op.SrcExpr.get(),
&Op.BasePath, CurFPFeatureOverrides(), CastTypeInfo, LPLoc, RPLoc));
diff --git a/clang/lib/Sema/SemaMSKernel.cpp b/clang/lib/Sema/SemaMSKernel.cpp
new file mode 100644
index 0000000000000..6d70ebf4411ac
--- /dev/null
+++ b/clang/lib/Sema/SemaMSKernel.cpp
@@ -0,0 +1,71 @@
+#include "TreeTransform.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaConsumer.h"
+
+using namespace clang;
+
+namespace {
+ExprResult TransformUnaryOperator(Sema &SemaRef, UnaryOperator *UO) {
+ if (UO->getOpcode() != UO_AddrOf)
+ return {};
+
+ SmallVector<Sema::OffsetOfComponent, 4> Components;
+ Expr *Current = UO->getSubExpr()->IgnoreParens();
+ while (true) {
+ // Strip away the "noise" added by array decays or parentheses
+ Current = Current->IgnoreParenImpCasts();
+ Sema::OffsetOfComponent Comp;
+ Comp.LocStart = Current->getBeginLoc();
+ Comp.LocEnd = Current->getEndLoc();
+
+ if (auto *ME = dyn_cast<MemberExpr>(Current)) {
+ Comp.isBrackets = false;
+ Comp.U.IdentInfo = ME->getMemberDecl()->getIdentifier();
+ Components.push_back(Comp);
+ Current = ME->getBase();
+ } else if (auto *ASE = dyn_cast<ArraySubscriptExpr>(Current)) {
+ Comp.isBrackets = true;
+ // In offsetof, the index must be an expression
+ Comp.U.E = ASE->getIdx();
+ Components.push_back(Comp);
+ Current = ASE->getBase();
+ } else {
+ // No more members or subscripts
+ break;
+ }
+ }
+ // Verify we ended at a Null Pointer Cast
+ Expr *Base = Current->IgnoreParenCasts();
+ if (!Components.empty() &&
+ Base->isNullPointerConstant(SemaRef.Context,
+ Expr::NPC_ValueDependentIsNotNull)) {
+ // Don't treat &((MyStruct*)0)[1] as an offsetof expression
+ if (Components.back().isBrackets)
+ return {};
+ // Targets like amdgcn, where nullptr != 0, are ignored
+ if (SemaRef.Context.getTargetNullPointerValue(Current->getType()))
+ return {};
+ std::reverse(Components.begin(), Components.end());
+
+ // Get the root structure type (e.g., MyStruct)
+ TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(
+ Current->getType()->getPointeeType(), Current->getBeginLoc());
+ return SemaRef.BuildBuiltinOffsetOf(UO->getBeginLoc(), TInfo, Components,
+ UO->getEndLoc());
+ }
+
+ return {};
+}
+} // end anonymous namespace
+
+ExprResult clang::Sema::TransformForMSKernel(Expr *E) {
+ auto *UO = dyn_cast_or_null<UnaryOperator>(E);
+ if (!UO)
+ return {};
+ ExprResult NewUO = TransformUnaryOperator(*this, UO);
+ return NewUO.isUsable() ? NewUO : ExprResult();
+}
diff --git a/clang/test/CodeGen/MSKernel/ast-bytecode-c.c b/clang/test/CodeGen/MSKernel/ast-bytecode-c.c
new file mode 100644
index 0000000000000..c83feccd4d486
--- /dev/null
+++ b/clang/test/CodeGen/MSKernel/ast-bytecode-c.c
@@ -0,0 +1,455 @@
+// This test is a modified version of AST/ByteCode/c.c
+// Original test fails in case entire LLVM test suite is compiled with -fms-kernel
+// Changes are minor and reflect differences in handling of __builtin_offsetof vs
+// offsetof and long type size in Windows vs Linux (4 vs 8)
+// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -verify=expected,all -std=c11 -Wcast-qual %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -verify=pedantic,pedantic-expected,all -std=c11 -Wcast-qual -pedantic %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -verify=ref,all -std=c11 -Wcast-qual %s
+// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -verify=pedantic,pedantic-ref,all -std=c11 -Wcast-qual -pedantic %s
+
+typedef __INTPTR_TYPE__ intptr_t;
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+
+_Static_assert(1, "");
+
+_Static_assert(__objc_yes, "");
+_Static_assert(!__objc_no, "");
+
+_Static_assert(0 != 1, "");
+_Static_assert(1.0 == 1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
+ // pedantic-expected-warning {{not an integer constant expression}}
+_Static_assert(1 && 1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
+ // pedantic-expected-warning {{not an integer constant expression}}
+_Static_assert( (5 > 4) + (3 > 2) == 2, "");
+_Static_assert(!!1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
+ // pedantic-expected-warning {{not an integer constant expression}}
+_Static_assert(!!1, "");
+
+_Static_assert(!(_Bool){(void*)0}, ""); // pedantic-ref-warning {{not an integer constant expression}} \
+ // pedantic-expected-warning {{not an integer constant expression}}
+
+int a = (1 == 1 ? 5 : 3);
+_Static_assert(a == 5, ""); // all-error {{not an integral constant expression}}
+
+const int DiscardedPtrToIntCast = ((intptr_t)((void*)0), 0); // all-warning {{left operand of comma operator has no effect}}
+
+const int b = 3;
+_Static_assert(b == 3, ""); // pedantic-ref-warning {{not an integer constant expression}} \
+ // pedantic-expected-warning {{not an integer constant expression}}
+
+const int c; // all-note {{declared here}}
+_Static_assert(c == 0, ""); // ref-error {{not an integral constant expression}} \
+ // ref-note {{initializer of 'c' is unknown}} \
+ // pedantic-ref-error {{not an integral constant expression}} \
+ // pedantic-ref-note {{initializer of 'c' is unknown}} \
+ // expected-error {{not an integral constant expression}} \
+ // expected-note {{initializer of 'c' is unknown}} \
+ // pedantic-expected-error {{not an integral constant expression}} \
+ // pedantic-expected-note {{initializer of 'c' is unknown}}
+
+_Static_assert(&c != 0, ""); // ref-warning {{always true}} \
+ // pedantic-ref-warning {{always true}} \
+ // pedantic-ref-warning {{is a GNU extension}} \
+ // expected-warning {{always true}} \
+ // pedantic-expected-warning {{always true}} \
+ // pedantic-expected-warning {{is a GNU extension}}
+_Static_assert(&a != 0, ""); // ref-warning {{always true}} \
+ // pedantic-ref-warning {{always true}} \
+ // pedantic-ref-warning {{is a GNU extension}} \
+ // expected-warning {{always true}} \
+ // pedantic-expected-warning {{always true}} \
+ // pedantic-expected-warning {{is a GNU extension}}
+_Static_assert((&c + 1) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
+ // pedantic-expected-warning {{is a GNU extension}}
+_Static_assert((&a + 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
+ // pedantic-ref-note {{100 of non-array}} \
+ // pedantic-expected-note {{100 of non-array}} \
+ // pedantic-expected-warning {{is a GNU extension}}
+_Static_assert((&a - 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
+ // pedantic-expected-warning {{is a GNU extension}} \
+ // pedantic-ref-note {{-100 of non-array}} \
+ // pedantic-expected-note {{-100 of non-array}}
+/// extern variable of a composite type.
+extern struct Test50S Test50;
+_Static_assert(&Test50 != (void*)0, ""); // all-warning {{always true}} \
+ // pedantic-warning {{is a GNU extension}} \
+ // pedantic-note {{this conversion is not allowed in a constant expression}}
+
+struct y {int x,y;};
+int a2[(intptr_t)&((struct y*)0)->y];
+
+const struct y *yy = (struct y*)0;
+const intptr_t L = (intptr_t)(&(yy->y)); // all-error {{not a compile-time constant}}
+
+_Static_assert((long)&((struct y*)0)->y > 0, ""); // all-warning {{cast to smaller integer type 'long' from 'int *'}}
+
+const ptrdiff_t m = &m + 137 - &m;
+_Static_assert(m == 137, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+/// from test/Sema/switch.c, used to cause an assertion failure.
+void f (int z) {
+ while (z) {
+ default: z--; // all-error {{'default' statement not in switch}}
+ }
+}
+
+int expr;
+int chooseexpr[__builtin_choose_expr(1, 1, expr)];
+
+int somefunc(int i) {
+ return (i, 65537) * 65537; // all-warning {{left operand of comma operator has no effect}} \
+ // all-warning {{overflow in expression; result is 131'073 with type 'int'}}
+}
+
+#pragma clang diagnostic ignored "-Wpointer-to-int-cast"
+struct ArrayStruct {
+ char n[1];
+};
+
+// With -fms-kernel we replace offsetof expression with __builtin_offsetof
+// This eliminates original warning (folded to constant array), but
+// introduces another one (zero size array is an extension)
+char name2[(int)&((struct ArrayStruct*)0)->n]; // pedantic-warning {{zero size arrays are an extension}}
+
+_Static_assert(sizeof(name2) == 0, "");
+
+#ifdef __SIZEOF_INT128__
+void *PR28739d = &(&PR28739d)[(__int128)(unsigned long)-1];
+#endif
+
+extern float global_float;
+struct XX { int a, *b; };
+struct XY { int before; struct XX xx, *xp; float* after; } xy[] = {
+ 0, 0, &xy[0].xx.a, &xy[0].xx, &global_float,
+ [1].xx = 0, &xy[1].xx.a, &xy[1].xx, &global_float,
+ 0, // all-note {{previous initialization is here}}
+ 0, // all-note {{previous initialization is here}}
+ [2].before = 0, // all-warning {{initializer overrides prior initialization of this subobject}}
+ 0, // all-warning {{initializer overrides prior initialization of this subobject}}
+ &xy[2].xx.a, &xy[2].xx, &global_float
+};
+
+void t14(void) {
+ int array[256] = { 0 }; // expected-note {{array 'array' declared here}} \
+ // pedantic-expected-note {{array 'array' declared here}} \
+ // ref-note {{array 'array' declared here}} \
+ // pedantic-ref-note {{array 'array' declared here}}
+ const char b = -1;
+ int val = array[b]; // expected-warning {{array index -1 is before the beginning of the array}} \
+ // pedantic-expected-warning {{array index -1 is before the beginning of the array}} \
+ // ref-warning {{array index -1 is before the beginning of the array}} \
+ // pedantic-ref-warning {{array index -1 is before the beginning of the array}}
+
+}
+
+void bar_0(void) {
+ struct C {
+ const int a;
+ int b;
+ };
+
+ const struct C S = {0, 0};
+
+ *(int *)(&S.a) = 0; // all-warning {{cast from 'const int *' to 'int *' drops const qualifier}}
+ *(int *)(&S.b) = 0; // all-warning {{cast from 'const int *' to 'int *' drops const qualifier}}
+}
+
+/// Complex-to-bool casts.
+const int A = ((_Complex double)1.0 ? 21 : 1);
+_Static_assert(A == 21, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+const int CTI1 = ((_Complex double){0.0, 1.0}); // pedantic-ref-warning {{extension}} \
+ // pedantic-expected-warning {{extension}}
+_Static_assert(CTI1 == 0, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+const _Bool CTB2 = (_Bool)(_Complex double){0.0, 1.0}; // pedantic-ref-warning {{extension}} \
+ // pedantic-expected-warning {{extension}}
+_Static_assert(CTB2, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+const _Bool CTB3 = (_Complex double){0.0, 1.0}; // pedantic-ref-warning {{extension}} \
+ // pedantic-expected-warning {{extension}}
+_Static_assert(CTB3, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+
+void nonComplexToComplexCast(void) {
+ _Complex double z = *(_Complex double *)&(struct { double r, i; }){0.0, 1.0};
+}
+
+int t1 = sizeof(int);
+void test4(void) {
+ t1 = sizeof(int);
+}
+
+void localCompoundLiteral(void) {
+ struct S { int x, y; } s = {}; // pedantic-expected-warning {{use of an empty initializer}} \
+ // pedantic-ref-warning {{use of an empty initializer}}
+ struct T {
+ int i;
+ struct S s;
+ } t1 = { 1, {} }; // pedantic-expected-warning {{use of an empty initializer}} \
+ // pedantic-ref-warning {{use of an empty initializer}}
+
+ struct T t3 = {
+ (int){}, // pedantic-expected-warning {{use of an empty initializer}} \
+ // pedantic-ref-warning {{use of an empty initializer}}
+ {} // pedantic-expected-warning {{use of an empty initializer}} \
+ // pedantic-ref-warning {{use of an empty initializer}}
+ };
+}
+
+/// struct copy
+struct StrA {int a; };
+const struct StrA sa = { 12 };
+const struct StrA * const sb = &sa;
+const struct StrA sc = *sb;
+_Static_assert(sc.a == 12, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+struct ComplexS {
+ int a;
+ float b;
+ struct StrA sa[2];
+};
+const struct ComplexS CS = {12, 23.0f, {{1}, {2}}};
+const struct ComplexS CS2 = CS;
+_Static_assert(CS2.sa[0].a == 1, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+_Static_assert(((void*)0 + 1) != (void*)0, ""); // pedantic-expected-warning {{arithmetic on a pointer to void is a GNU extension}} \
+ // pedantic-expected-warning {{not an integer constant expression}} \
+ // pedantic-expected-note {{cannot perform pointer arithmetic on null pointer}} \
+ // pedantic-ref-warning {{arithmetic on a pointer to void is a GNU extension}} \
+ // pedantic-ref-warning {{not an integer constant expression}} \
+ // pedantic-ref-note {{cannot perform pointer arithmetic on null pointer}}
+
+typedef __INTPTR_TYPE__ intptr_t;
+int array[(intptr_t)(int*)1]; // ref-warning {{variable length array folded to constant array}} \
+ // pedantic-ref-warning {{variable length array folded to constant array}} \
+ // expected-warning {{variable length array folded to constant array}} \
+ // pedantic-expected-warning {{variable length array folded to constant array}}
+
+int castViaInt[*(int*)(unsigned long)"test"]; // ref-error {{variable length array}} \
+ // pedantic-ref-error {{variable length array}} \
+ // expected-error {{variable length array}} \
+ // pedantic-expected-error {{variable length array}} \
+ // all-warning {{cast to 'int *' from smaller integer type 'unsigned long'}}
+
+const void (*const funcp)(void) = (void*)123; // pedantic-warning {{converts between void pointer and function pointer}} \
+ // pedantic-expected-note {{this conversion is not allowed in a constant expression}}
+_Static_assert(funcp == (void*)0, ""); // all-error {{failed due to requirement 'funcp == (void *)0'}} \
+ // pedantic-warning {{expression is not an integer constant expression}}
+_Static_assert(funcp == (void*)123, ""); // pedantic-warning {{equality comparison between function pointer and void pointer}} \
+ // pedantic-warning {{expression is not an integer constant expression}}
+
+void unaryops(void) {
+ (void)(++(struct x {unsigned x;}){3}.x);
+ (void)(--(struct y {unsigned x;}){3}.x);
+ (void)(++(struct z {float x;}){3}.x);
+ (void)(--(struct w {float x;}){3}.x);
+
+ (void)((struct xx {unsigned x;}){3}.x++);
+ (void)((struct yy {unsigned x;}){3}.x--);
+ (void)((struct zz {float x;}){3}.x++);
+ (void)((struct ww {float x;}){3}.x--);
+}
+
+/// This used to fail because we didn't properly mark the struct
+/// initialized through a CompoundLiteralExpr as initialized.
+struct TestStruct {
+ int a;
+ int b;
+};
+int Y __attribute__((annotate(
+ "GlobalValAnnotationWithArgs",
+ 42,
+ (struct TestStruct) { .a = 1, .b = 2 }
+)));
+
+#ifdef __SIZEOF_INT128__
+const int *p = &b;
+const __int128 K = (__int128)(int*)0;
+const unsigned __int128 KU = (unsigned __int128)(int*)0;
+#endif
+
+
+int test3(void) {
+ int a[2];
+ a[0] = test3; // all-error {{incompatible pointer to integer conversion assigning to 'int' from 'int (void)'}}
+ return 0;
+}
+/// This tests that we have full type info, even for values we cannot read.
+int dummyarray[5];
+_Static_assert(&dummyarray[0] < &dummyarray[1], ""); // pedantic-warning {{GNU extension}}
+
+void addrlabelexpr(void) {
+ a0: ;
+ static void *ps[] = { &&a0 }; // pedantic-warning {{use of GNU address-of-label extension}}
+}
+
+extern void cv2;
+void *foo5 (void)
+{
+ return &cv2; // pedantic-warning{{address of an expression of type 'void'}}
+}
+
+__attribute__((weak)) const unsigned int test10_bound = 10;
+char test10_global[test10_bound]; // all-error {{variable length array declaration not allowed at file scope}}
+void test10(void) {
+ char test10_local[test10_bound] = "help"; // all-error {{variable-sized object may not be initialized}}
+}
+
+void SuperSpecialFunc(void) {
+const int SuperSpecialCase = 10;
+_Static_assert((sizeof(SuperSpecialCase) == 12 && SuperSpecialCase == 3) || SuperSpecialCase == 10, ""); // pedantic-warning {{GNU extension}}
+}
+
+
+void T1(void) {
+ static int *y[1] = {({ static int _x = 20; (void*)0;})}; // all-error {{initializer element is not a compile-time constant}} \
+ // pedantic-warning {{use of GNU statement expression extension}}
+}
+
+enum teste1 test1f(void), (*test1)(void) = test1f; // pedantic-warning {{ISO C forbids forward references to 'enum' types}}
+enum teste1 { TEST1 };
+
+void func(void) {
+ _Static_assert(func + 1 - func == 1, ""); // pedantic-warning {{arithmetic on a pointer to the function type}} \
+ // pedantic-warning {{arithmetic on pointers to the function type}} \
+ // pedantic-warning {{not an integer constant expression}}
+ _Static_assert(func + 0xdead000000000000UL - 0xdead000000000000UL == func, ""); // pedantic-warning 2{{arithmetic on a pointer to the function type}} \
+ // pedantic-warning {{not an integer constant expression}} \
+ // pedantic-note {{cannot refer to element 16045481047390945280 of non-array object in a constant expression}}
+ _Static_assert(func + 1 != func, ""); // pedantic-warning {{arithmetic on a pointer to the function type}} \
+ // pedantic-warning {{expression is not an integer constant expression}}
+ func + 0xdead000000000000UL; // all-warning {{expression result unused}} \
+ // pedantic-warning {{arithmetic on a pointer to the function type}}
+ func - 0xdead000000000000UL; // all-warning {{expression result unused}} \
+ // pedantic-warning {{arithmetic on a pointer to the function type}}
+}
+
+void foo3 (void)
+{
+ void* x = 0;
+ void* y = &*x;
+}
+
+static void *FooTable[1] = {
+ [0] = (void *[1]) { // 1
+ [0] = (void *[1]) { // 2
+ [0] = (void *[1]) {} // pedantic-warning {{use of an empty initializer}}
+ },
+ }
+};
+
+int strcmp(const char *, const char *); // all-note {{passing argument to parameter here}}
+#define S "\x01\x02\x03\x04\x05\x06\x07\x08"
+const char _str[] = {S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7]};
+const unsigned char _str2[] = {S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7]};
+const int compared = strcmp(_str, (const char *)_str2); // all-error {{initializer element is not a compile-time constant}}
+
+
+const int compared2 = strcmp(strcmp, _str); // all-error {{incompatible pointer types}} \
+ // all-error {{initializer element is not a compile-time constant}}
+
+int foo(x) // all-warning {{a function definition without a prototype is deprecated in all versions of C}}
+int x;
+{
+ return x;
+}
+
+void bar() { // pedantic-warning {{a function declaration without a prototype}}
+ int x;
+ x = foo(); // all-warning {{too few arguments}}
+}
+
+int *_b = &a;
+void discardedCmp(void)
+{
+ (*_b) = ((&a == &a) , a); // all-warning {{left operand of comma operator has no effect}}
+}
+
+/// ArraySubscriptExpr that's not an lvalue
+typedef unsigned char U __attribute__((vector_size(1)));
+void nonLValueASE(U f) { f[0] = f[((U)(U){0})[0]]; }
+
+static char foo_(a) // all-warning {{definition without a prototype}}
+ char a;
+{
+ return 'a';
+}
+static void bar_(void) {
+ foo_(foo_(1));
+}
+
+void foo2(void*);
+void bar2(void) {
+ int a[2][3][4][5]; // all-note {{array 'a' declared here}}
+ foo2(&a[0][4]); // all-warning {{array index 4 is past the end of the array}}
+}
+
+void plainComplex(void) {
+ _Complex cd; // all-warning {{_Complex double}}
+ cd = *(_Complex *)&(struct { double r, i; }){0.0, 0.0}; // all-warning {{_Complex double}}
+}
+
+/// This test results in an ImplicitValueInitExpr with DiscardResult set.
+struct M{
+ char c;
+};
+typedef struct S64 {
+ struct M m;
+ char a[64];
+} I64;
+
+_Static_assert((((I64){}, 1)), ""); // all-warning {{left operand of comma operator has no effect}} \
+ // pedantic-warning {{use of an empty initializer is a C23 extension}} \
+ // pedantic-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
+
+#define V(N) __attribute__((vector_size(N)))
+#define C2 (VC2){0, 1}
+char func_(void);
+typedef V(2) char VC2;
+void CopyArrayToFnPtr(void) { *(VC2 *)func_ = C2; }
+
+_Complex double returnsComplex(); // pedantic-warning {{a function declaration without a prototype is deprecated in all versions of C}}
+void callReturnsComplex(void) {
+ _Complex double c;
+ c = returnsComplex(0.); // all-warning {{passing arguments to 'returnsComplex' without a prototype is deprecated in all versions of C and is not supported in C23}}
+}
+
+int complexMul[2 * (22222222222wb + 2i) == 2]; // all-warning {{'_BitInt' suffix for literals is a C23 extension}} \
+ // pedantic-warning {{imaginary constants are a C2y extension}} \
+ // all-warning {{variable length array folded to constant array as an extension}}
+
+int complexDiv[2 / (22222222222wb + 2i) == 2]; // all-warning {{'_BitInt' suffix for literals is a C23 extension}} \
+ // pedantic-warning {{imaginary constants are a C2y extension}} \
+ // all-warning {{variable length array folded to constant array as an extension}}
+
+
+
+int i = 0;
+void intPtrCmp1(void) { &i + 1 == 2; } // all-warning {{comparison between pointer and integer}} \
+ // all-warning {{equality comparison result unused}}
+void intPtrCmp2(void) { 2 == &i + 1; } // all-warning {{comparison between pointer and integer}} \
+ // all-warning {{equality comparison result unused}}
+
+/// evaluateStrlen on a non-block pointer.
+char *strcpy(char *restrict s1, const char *restrict s2);
+void strcpy_fn(char *x) {
+ strcpy(x, (char*)&strcpy_fn);
+}
+
+/// strcpy on a non-integer pointer.
+void strcpyDouble(void) {
+ char buf[1];
+ const double test_buf[] = {'4', '2'};
+ __builtin_strcpy(buf, test_buf + 1); // all-error {{incompatible pointer types}}
+}
+
+int *iptr;
+void ignoredConditional(void) { *iptr = (((_Complex double)1.0 ? 2 : 3), a); } // all-warning {{left operand of comma operator has no effect}}
diff --git a/clang/test/CodeGen/MSKernel/containing-record.cpp b/clang/test/CodeGen/MSKernel/containing-record.cpp
new file mode 100644
index 0000000000000..4e8b8e6abc849
--- /dev/null
+++ b/clang/test/CodeGen/MSKernel/containing-record.cpp
@@ -0,0 +1,57 @@
+// Normally this file can't be compiled due to
+// nullptr cast in offset calculation
+// RUN: not %clang_cc1 -ast-dump %s -o - 2>&1 | FileCheck %s --check-prefix=AST-ORIG
+// Kernel variant works OK.
+// RUN: %clang_cc1 -fms-kernel -ast-dump %s -o - | FileCheck %s --check-prefix=AST-NEW
+
+// AST-ORIG: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
+// AST-ORIG: FunctionDecl
+// AST-ORIG-NEXT: ParmVarDecl
+// AST-ORIG-NEXT: CompoundStmt
+// AST-ORIG-NEXT: ReturnStmt
+// AST-ORIG-NEXT: ParenExpr
+// AST-ORIG-NEXT: CStyleCastExpr
+// AST-ORIG-NEXT: ParenExpr
+// AST-ORIG-NEXT: BinaryOperator
+// AST-ORIG-NEXT: CStyleCastExpr
+// AST-ORIG-NEXT: ImplicitCastExpr
+// AST-ORIG-NEXT: ParenExpr
+// AST-ORIG-NEXT: DeclRefExpr
+// AST-ORIG-NEXT: CStyleCastExpr
+// AST-ORIG-NEXT: ParenExpr
+// AST-ORIG-NEXT: UnaryOperator
+// AST-ORIG-NEXT: MemberExpr
+// AST-ORIG-NEXT: ParenExpr
+// AST-ORIG-NEXT: CStyleCastExpr
+// AST-ORIG-NEXT: ImplicitCastExpr
+// AST-ORIG-NEXT: IntegerLiteral
+
+// AST-NEW: FunctionDecl
+// AST-NEW-NEXT: ParmVarDecl
+// AST-NEW-NEXT: CompoundStmt
+// AST-NEW-NEXT: ReturnStmt
+// AST-NEW-NEXT: ParenExpr
+// AST-NEW-NEXT: CStyleCastExpr
+// AST-NEW-NEXT: ParenExpr
+// AST-NEW-NEXT: BinaryOperator
+// AST-NEW-NEXT: CStyleCastExpr
+// AST-NEW-NEXT: ImplicitCastExpr
+// AST-NEW-NEXT: ParenExpr
+// AST-NEW-NEXT: DeclRefExpr
+// AST-NEW-NEXT: CStyleCastExpr
+// AST-NEW-NEXT: ImplicitCastExpr
+// AST-NEW-NEXT: OffsetOfExpr
+
+typedef char* PCHAR;
+typedef unsigned long long ULONG_PTR;
+
+#define CONTAINING_RECORD_UB(address, type, field) ((type *)( \
+ (PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))
+
+
+struct MyStruct { int a; int b; int c; };
+
+constexpr MyStruct* get_container_ub(int* p) {
+ return CONTAINING_RECORD_UB(p, MyStruct, b);
+}
+
diff --git a/clang/test/CodeGen/MSKernel/nullptr.cl b/clang/test/CodeGen/MSKernel/nullptr.cl
new file mode 100644
index 0000000000000..9b4b66639bd3a
--- /dev/null
+++ b/clang/test/CodeGen/MSKernel/nullptr.cl
@@ -0,0 +1,25 @@
+// On amdgcn nullptr is -1. We don't rewrite AST in such case
+// RUN: %clang_cc1 -fms-kernel -no-enable-noundef-analysis %s -cl-std=CL2.0 -include opencl-c.h -triple amdgcn -emit-llvm -o - | FileCheck %s
+
+typedef struct {
+ private char *p1;
+ local char *p2;
+ constant char *p3;
+ global char *p4;
+ generic char *p5;
+} StructTy1;
+
+typedef struct {
+ constant char *p3;
+ global char *p4;
+ generic char *p5;
+} StructTy2;
+
+
+// CHECK: @fold_int5 ={{.*}} local_unnamed_addr addrspace(1) global i32 3, align 4
+int fold_int5 = (int) &((private StructTy1*)0)->p2;
+
+// CHECK: @fold_int5_local ={{.*}} local_unnamed_addr addrspace(1) global i32 3, align 4
+int fold_int5_local = (int) &((local StructTy1*)0)->p2;
+
+
diff --git a/clang/test/CodeGen/MSKernel/offsetof.cpp b/clang/test/CodeGen/MSKernel/offsetof.cpp
new file mode 100644
index 0000000000000..864a78a70d642
--- /dev/null
+++ b/clang/test/CodeGen/MSKernel/offsetof.cpp
@@ -0,0 +1,39 @@
+// Check that sum_offsets1 and sum_offsets2 are identical
+// with and without -fms-kernel
+// RUN: %clang_cc1 -fms-kernel -fms-extensions -O2 -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -fms-extensions -O2 -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s
+
+// CHECK: define dso_local noundef i64 @"?sum_offsets1@@YA_KXZ"()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: ret i64 632
+
+// CHECK: define dso_local noundef i64 @"?sum_offsets2@@YA_KXZ"()
+// CHECK-NEXT: entry:
+// CHECK-NEXT ret i64 632
+
+typedef unsigned long long ULONG_PTR;
+
+#define MY_OFFSETOF(type, field) (ULONG_PTR)(&((type *)0)->field)
+
+namespace foo {
+typedef struct MyStruct {
+ char b;
+ struct X {
+ long m0[10], m1;
+ } x[10];
+ long c;
+} MyStruct;
+}
+
+__declspec(noinline) ULONG_PTR sum_offsets1() {
+ return MY_OFFSETOF(foo::MyStruct, x[1].m0[2]) +
+ MY_OFFSETOF(foo::MyStruct, x[2].m1) +
+ MY_OFFSETOF(foo::MyStruct, c);
+}
+
+__declspec(noinline) ULONG_PTR sum_offsets2() {
+ return __builtin_offsetof(foo::MyStruct, x[1].m0[2]) +
+ __builtin_offsetof(foo::MyStruct, x[2].m1) +
+ __builtin_offsetof(foo::MyStruct, c);
+}
+
diff --git a/clang/test/CodeGen/MSKernel/struct-decl.c b/clang/test/CodeGen/MSKernel/struct-decl.c
new file mode 100644
index 0000000000000..b6b25bd3f1c95
--- /dev/null
+++ b/clang/test/CodeGen/MSKernel/struct-decl.c
@@ -0,0 +1,114 @@
+// This one of two tests which failed when LLVM test suite
+// Replacing offsetof with __builtin_offsetof slightly affects
+// warning messages. Compare with Sema/struct-decl.c
+// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -Wno-pointer-to-int-cast -fsyntax-only -verify %s
+// PR3459
+struct bar {
+ char n[1];
+};
+
+struct foo {
+ char name[(int)&((struct bar *)0)->n];
+ char name2[(int)&((struct bar *)0)->n - 1]; // expected-error {{'name2' declared as an array with a negative size}}
+};
+
+// PR3430
+struct s {
+ struct st {
+ int v;
+ } *ts;
+};
+
+struct st;
+
+int foo(void) {
+ struct st *f;
+ return f->v + f[0].v;
+}
+
+// PR3642, PR3671
+struct pppoe_tag {
+ short tag_type;
+ char tag_data[];
+};
+struct datatag {
+ struct pppoe_tag hdr; //expected-warning{{field 'hdr' with variable sized type 'struct pppoe_tag' not at the end of a struct or class is a GNU extension}}
+ char data;
+};
+
+
+// PR4092
+struct s0 {
+ char a; // expected-note {{previous declaration is here}}
+ char a; // expected-error {{duplicate member 'a'}}
+};
+
+struct s0 f0(void) {}
+
+// This previously triggered an assertion failure.
+struct x0 {
+ unsigned int x1;
+};
+
+static struct test1 { // expected-warning {{'static' ignored on this declaration}}
+ int x;
+};
+const struct test2 { // expected-warning {{'const' ignored on this declaration}}
+ int x;
+};
+inline struct test3 { // expected-error {{'inline' can only appear on functions}}
+ int x;
+};
+
+struct hiding_1 {};
+struct hiding_2 {};
+void test_hiding(void) {
+ struct hiding_1 *hiding_1(void);
+ extern struct hiding_2 *hiding_2;
+ struct hiding_1 *p = hiding_1();
+ struct hiding_2 *q = hiding_2;
+}
+
+struct PreserveAttributes {};
+typedef struct __attribute__((noreturn)) PreserveAttributes PreserveAttributes_t; // expected-warning {{'noreturn' attribute only applies to functions and methods}}
+
+// PR46255
+struct FlexibleArrayMem {
+ int a;
+ int b[];
+};
+
+struct FollowedByNamed {
+ struct FlexibleArrayMem a; // expected-warning {{field 'a' with variable sized type 'struct FlexibleArrayMem' not at the end of a struct or class is a GNU extension}}
+ int i;
+};
+
+struct FollowedByUnNamed {
+ struct FlexibleArrayMem a; // expected-warning {{field 'a' with variable sized type 'struct FlexibleArrayMem' not at the end of a struct or class is a GNU extension}}
+ struct {
+ int i;
+ };
+};
+
+struct InAnonymous {
+ struct { // expected-warning-re {{field '' with variable sized type 'struct InAnonymous::(anonymous at {{.+}})' not at the end of a struct or class is a GNU extension}}
+
+ struct FlexibleArrayMem a;
+ };
+ int i;
+};
+struct InAnonymousFollowedByAnon {
+ struct { // expected-warning-re {{field '' with variable sized type 'struct InAnonymousFollowedByAnon::(anonymous at {{.+}})' not at the end of a struct or class is a GNU extension}}
+
+ struct FlexibleArrayMem a;
+ };
+ struct {
+ int i;
+ };
+};
+
+// This is the behavior in C++ as well, so making sure we reproduce it here.
+struct InAnonymousFollowedByEmpty {
+ struct FlexibleArrayMem a; // expected-warning {{field 'a' with variable sized type 'struct FlexibleArrayMem' not at the end of a struct or class is a GNU extension}}
+ struct {};
+};
>From 4dfad37730a65de767d824b22b0a845581370218 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Mon, 27 Apr 2026 12:00:43 +0200
Subject: [PATCH 2/2] Define triple to fix builder failure
---
clang/test/CodeGen/MSKernel/containing-record.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/clang/test/CodeGen/MSKernel/containing-record.cpp b/clang/test/CodeGen/MSKernel/containing-record.cpp
index 4e8b8e6abc849..54f4482ed73cf 100644
--- a/clang/test/CodeGen/MSKernel/containing-record.cpp
+++ b/clang/test/CodeGen/MSKernel/containing-record.cpp
@@ -1,8 +1,8 @@
// Normally this file can't be compiled due to
// nullptr cast in offset calculation
-// RUN: not %clang_cc1 -ast-dump %s -o - 2>&1 | FileCheck %s --check-prefix=AST-ORIG
+// RUN: not %clang_cc1 -triple x86_64-pc-win32 -ast-dump %s -o - 2>&1 | FileCheck %s --check-prefix=AST-ORIG
// Kernel variant works OK.
-// RUN: %clang_cc1 -fms-kernel -ast-dump %s -o - | FileCheck %s --check-prefix=AST-NEW
+// RUN: %clang_cc1 -triple x86_64-pc-win32 -fms-kernel -ast-dump %s -o - | FileCheck %s --check-prefix=AST-NEW
// AST-ORIG: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
// AST-ORIG: FunctionDecl
@@ -39,7 +39,6 @@
// AST-NEW-NEXT: ParenExpr
// AST-NEW-NEXT: DeclRefExpr
// AST-NEW-NEXT: CStyleCastExpr
-// AST-NEW-NEXT: ImplicitCastExpr
// AST-NEW-NEXT: OffsetOfExpr
typedef char* PCHAR;
More information about the cfe-commits
mailing list