[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