https://github.com/erichkeane created 
https://github.com/llvm/llvm-project/pull/207033

Since we do field based GEPs, it isn't really possible to do a get-address-of a 
field without representing all the fields in a CIR struct.  This patch makes 
sure we do that, so we can do the proper GEPs.

This patch also as a drive-by fixes the case where we were searching for the 
'path' inside of an empty base, which we don't represent (and probably should 
not need to?).

This patch ALSO adds some NYI diagnostics for cases where we try to get the 
address of a `no-unique-address` field, which we are not currently 
representing.  In the future, we are going to have to start representing 
no-unique-address fields inside of a struct as well (plus figure out how to 
lower it properly in a way that gets the offsets right), but it isn't clear 
this is immediately necessary (as you'd have to take an address of one to 
notice?).

>From e132f1d7365893662b7dee4f2a9eeef21df37f6e Mon Sep 17 00:00:00 2001
From: erichkeane <[email protected]>
Date: Tue, 30 Jun 2026 10:47:06 -0700
Subject: [PATCH] [CIR] Represent 'empty' fields in structs/unions

Since we do field based GEPs, it isn't really possible to do a
get-address-of a field without representing all the fields in a CIR
struct.  This patch makes sure we do that, so we can do the proper GEPs.

This patch also as a drive-by fixes the case where we were searching for
the 'path' inside of an empty base, which we don't represent (and
probably should not need to?).

This patch ALSO adds some NYI diagnostics for cases where we try to get
the address of a `no-unique-address` field, which we are not currently
representing.  In the future, we are going to have to start representing
no-unique-address fields inside of a struct as well (plus figure out
how to lower it properly in a way that gets the offsets right), but it
isn't clear this is immediately necessary (as you'd have to take an
address of one to notice?).
---
 clang/include/clang/CIR/MissingFeatures.h     |   2 +-
 clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp  |   5 +
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |  15 ++
 .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp |  16 +-
 .../CIR/CodeGen/cross-reference-globals.c     |  19 +-
 .../test/CIR/CodeGen/paren-list-agg-init.cpp  |  15 +-
 .../CodeGen/pointer-to-empty-data-member.cpp  | 240 ++++++++++++++++++
 7 files changed, 293 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/pointer-to-empty-data-member.cpp

diff --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index 9a1546fe14e65..43a1e77b42908 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -120,7 +120,7 @@ struct MissingFeatures {
 
   // RecordType
   static bool astRecordDeclAttr() { return false; }
-  static bool zeroSizeRecordMembers() { return false; }
+  static bool noUniqueAddressLayout() { return false; }
 
   // Coroutines
   static bool coroOutsideFrameMD() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 9ad172211ea60..1dde19b380b91 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -1422,6 +1422,11 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const 
APValue &value,
     const auto *fieldDecl = cast<FieldDecl>(memberDecl);
     const auto *mpt = destType->castAs<MemberPointerType>();
     const auto *destClass = mpt->getMostRecentCXXRecordDecl();
+    if (fieldDecl->hasAttr<NoUniqueAddressAttr>()) {
+      assert(!cir::MissingFeatures::noUniqueAddressLayout());
+      cgm.errorNYI("ConstExprEmitter::tryEmitPrivate: no_unique_address 
field");
+      return {};
+    }
     std::optional<llvm::SmallVector<int32_t>> path =
         cgm.buildMemberPath(destClass, fieldDecl);
     if (!path)
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 304f71f693c6b..4d1fa30296898 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2297,6 +2297,14 @@ mlir::Value 
CIRGenModule::emitMemberPointerConstant(const UnaryOperator *e) {
   const auto *fieldDecl = cast<FieldDecl>(decl);
   const auto *mpt = e->getType()->castAs<MemberPointerType>();
   const auto *destClass = mpt->getMostRecentCXXRecordDecl();
+
+  if (fieldDecl->hasAttr<NoUniqueAddressAttr>()) {
+    assert(!cir::MissingFeatures::noUniqueAddressLayout());
+    errorNYI(e->getExprLoc(),
+             "emitMemberPointerConstant: no_unique_address field");
+    return {};
+  }
+
   std::optional<llvm::SmallVector<int32_t>> path =
       buildMemberPath(destClass, fieldDecl);
   if (!path)
@@ -2308,6 +2316,8 @@ mlir::Value CIRGenModule::emitMemberPointerConstant(const 
UnaryOperator *e) {
 std::optional<llvm::SmallVector<int32_t>>
 CIRGenModule::buildMemberPath(const CXXRecordDecl *destClass,
                               const FieldDecl *field) {
+  assert(!field->hasAttr<NoUniqueAddressAttr>());
+
   llvm::SmallVector<int32_t> path;
   if (!findFieldMemberPath(destClass, field, path))
     return std::nullopt;
@@ -2362,6 +2372,11 @@ bool CIRGenModule::findFieldMemberPath(const 
CXXRecordDecl *currentClass,
       continue;
     }
 
+    // If a base class doesn't participate in layout, the field cannot be in 
it,
+    // skip it.
+    if (!layout.hasNonVirtualBaseCIRField(baseDecl))
+      continue;
+
     auto baseFieldIdx =
         static_cast<int32_t>(layout.getNonVirtualBaseCIRFieldNo(baseDecl));
     path.push_back(baseFieldIdx);
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp 
b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index e33b2065ecb67..3b248ed9a0125 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -578,9 +578,19 @@ void CIRRecordLowering::accumulateFields() {
       field = accumulateBitFields(field, fieldEnd);
       assert((field == fieldEnd || !field->isBitField()) &&
              "Failed to accumulate all the bitfields");
-    } else if (isEmptyFieldForLayout(astContext, *field)) {
-      // TODO(cir): do we want to do anything special about zero size members?
-      assert(!cir::MissingFeatures::zeroSizeRecordMembers());
+    } else if (isEmptyFieldForLayout(astContext, *field) &&
+               field->isPotentiallyOverlapping()) {
+      // We lay out normal empty fields, as they are required for GEPs/getting
+      // function pointers. However 'no-unique-address' lends some additional
+      // complexity. These fields take up no real space, but would also have to
+      // be the correct 'GEP' offset to work here, but then mess with the
+      // layout.  We likely need to come up with a new 'type' to support these,
+      // then figure out some way to lower these to the correct offset, likely
+      // by calculating the correct offset into the struct, and forming a
+      // replacement by-byte GEP in LowerToLLVM.  However, this is only a
+      // problem with taking the address of one of these, so it is in practice
+      // not a horrifyingly problematic issue.
+      assert(!cir::MissingFeatures::noUniqueAddressLayout());
       ++field;
     } else {
       // Use base subobject layout for potentially-overlapping fields,
diff --git a/clang/test/CIR/CodeGen/cross-reference-globals.c 
b/clang/test/CIR/CodeGen/cross-reference-globals.c
index fbf613123f85a..ea8f20aab9947 100644
--- a/clang/test/CIR/CodeGen/cross-reference-globals.c
+++ b/clang/test/CIR/CodeGen/cross-reference-globals.c
@@ -19,9 +19,10 @@ struct B2 { struct E2 e; int x; int y; };
 extern struct B2 b2;
 int *a2 = &b2.y;
 struct B2 b2 = { {}, 10, 20 };
-// CIR-DAG: cir.global external @b2 = #cir.const_record<{#cir.int<10> : !s32i, 
#cir.int<20> : !s32i}> : !rec_B2
-// CIR-DAG: cir.global external @a2 = #cir.global_view<@b2, [1 : i32]> : 
!cir.ptr<!s32i>
-// LLVM-DAG: @b2 = global %struct.B2 { i32 10, i32 20 }
+// CIR-DAG: cir.global external @b2 = #cir.const_record<{#cir.zero : !rec_E2, 
#cir.int<10> : !s32i, #cir.int<20> : !s32i}> : !rec_B2
+// CIR-DAG: cir.global external @a2 = #cir.global_view<@b2, [2 : i32]> : 
!cir.ptr<!s32i>
+// LLVMCIR-DAG: @b2 = global %struct.B2 { %struct.E2 zeroinitializer, i32 10, 
i32 20 }
+// OGCG-DAG: @b2 = global %struct.B2 { i32 10, i32 20 }
 // LLVM-DAG: @a2 = global ptr getelementptr {{.*}}(i8, ptr @b2, i64 4)
 
 struct E3 {};
@@ -35,9 +36,9 @@ struct B3 {
 extern struct B3 b3;
 int *a3 = &b3.inner.v;
 struct B3 b3 = { .x = 7, .self = (int *)&b3 };
-// CIR-DAG: cir.global external @b3 = #cir.const_record<{#cir.int<7> : !s32i, 
#cir.zero : !rec_In3, #cir.global_view<@b3> : !cir.ptr<!s32i>}> : !rec_B3
-// CIR-DAG: cir.global external @a3 = #cir.global_view<@b3, [1 : i32, 1 : 
i32]> : !cir.ptr<!s32i>
-// LLVMCIR-DAG: @b3 = global %struct.B3 { i32 7, %struct.In3 zeroinitializer, 
ptr @b3 }
+// CIR-DAG: cir.global external @b3 = #cir.const_record<{#cir.zero : !rec_E3, 
#cir.int<7> : !s32i, #cir.zero : !rec_In3, #cir.global_view<@b3> : 
!cir.ptr<!s32i>}> : !rec_B3
+// CIR-DAG: cir.global external @a3 = #cir.global_view<@b3, [2 : i32, 1 : 
i32]> : !cir.ptr<!s32i>
+// LLVMCIR-DAG: @b3 = global %struct.B3 { %struct.E3 zeroinitializer, i32 7, 
%struct.In3 zeroinitializer, ptr @b3 }
 // OGCG-DAG:    @b3 = global { i32, %struct.In3, [4 x i8], ptr } { i32 7, 
%struct.In3 zeroinitializer, [4 x i8] zeroinitializer, ptr @b3 }
 // LLVM-DAG: @a3 = global ptr getelementptr {{.*}}(i8, ptr @b3, i64 8)
 
@@ -53,9 +54,9 @@ extern struct B4 b4_fwd;
 struct A4 { int *target; };
 struct A4 a4 = { .target = &b4_fwd.inner.q };
 struct B4 b4_fwd = { .x = 11, .self = (int *)&b4_fwd };
-// CIR-DAG: cir.global external @b4_fwd = #cir.const_record<{#cir.int<11> : 
!s32i, #cir.zero : !rec_In4, #cir.global_view<@b4_fwd> : !cir.ptr<!s32i>}> : 
!rec_B4
-// CIR-DAG: cir.global external @a4 = 
#cir.const_record<{#cir.global_view<@b4_fwd, [1 : i32, 1 : i32]> : 
!cir.ptr<!s32i>}> : !rec_A4
-// LLVMCIR-DAG: @b4_fwd = global %struct.B4 { i32 11, %struct.In4 
zeroinitializer, ptr @b4_fwd }
+// CIR-DAG: cir.global external @b4_fwd = #cir.const_record<{#cir.zero : 
!rec_E4, #cir.int<11> : !s32i, #cir.zero : !rec_In4, #cir.global_view<@b4_fwd> 
: !cir.ptr<!s32i>}> : !rec_B4
+// CIR-DAG: cir.global external @a4 = 
#cir.const_record<{#cir.global_view<@b4_fwd, [2 : i32, 1 : i32]> : 
!cir.ptr<!s32i>}> : !rec_A4
+// LLVMCIR-DAG: @b4_fwd = global %struct.B4 { %struct.E4 zeroinitializer, i32 
11, %struct.In4 zeroinitializer, ptr @b4_fwd }
 // OGCG-DAG:    @b4_fwd = global { i32, %struct.In4, [4 x i8], ptr } { i32 11, 
%struct.In4 zeroinitializer, [4 x i8] zeroinitializer, ptr @b4_fwd }
 // LLVM-DAG: @a4 = global %struct.A4 { ptr getelementptr {{.*}}(i8, ptr 
@b4_fwd, i64 8) }
 
diff --git a/clang/test/CIR/CodeGen/paren-list-agg-init.cpp 
b/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
index 3f03854c5cac5..4abd00de6b5c4 100644
--- a/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
+++ b/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
@@ -65,8 +65,9 @@ struct F {
   F (F &&f) = default;
 };
 
-// LLVM-DAG: [[STRUCT_G:%.*]] = type <{ i32, [4 x i8] }>
-// CIR-DAG: ![[STRUCT_G:.*]] = !cir.struct<"G" packed padded {!s32i, 
!cir.array<!u8i x 4>}>
+// LLVMCIR-DAG: [[STRUCT_G:%.*]] = type <{ i32, %struct.F, [3 x i8] }>
+// OGCG-DAG:    [[STRUCT_G:%.*]] = type <{ i32, [4 x i8] }>
+// CIR-DAG: ![[STRUCT_G:.*]] = !cir.struct<"G" packed padded {!s32i, !rec_F, 
!cir.array<!u8i x 3>}>
 struct G {
   int a;
   F f;
@@ -91,14 +92,16 @@ namespace gh61145 {
     ~Vec();
   };
 
-  // LLVM-DAG: [[STRUCT_S1:%.*]] = type { i8 }
-  // CIR-DAG: ![[STRUCT_S1:.*]] = !cir.struct<"gh61145::S1" padded {!u8i}>
+  // LLVMCIR-DAG: [[STRUCT_S1:%.*]] = type { %"struct.gh61145::Vec" }
+  // OGCG-DAG:    [[STRUCT_S1:%.*]] = type { i8 }
+  // CIR-DAG: ![[STRUCT_S1:.*]] = !cir.struct<"gh61145::S1" 
{!rec_gh611453A3AVec}>
   struct S1 {
     Vec v;
   };
 
-  // LLVM-DAG: [[STRUCT_S2:%.*]] = type { i8, i8 }
-  // CIR-DAG: ![[STRUCT_S2:.*]] = !cir.struct<"gh61145::S2" padded {!u8i, 
!s8i}>
+  // LLVMCIR-DAG: [[STRUCT_S2:%.*]] = type { %"struct.gh61145::Vec", i8 }
+  // OGCG-DAG:    [[STRUCT_S2:%.*]] = type { i8, i8 }
+  // CIR-DAG: ![[STRUCT_S2:.*]] = !cir.struct<"gh61145::S2" 
{!rec_gh611453A3AVec, !s8i}>
   struct S2 {
     Vec v;
     char c;
diff --git a/clang/test/CIR/CodeGen/pointer-to-empty-data-member.cpp 
b/clang/test/CIR/CodeGen/pointer-to-empty-data-member.cpp
new file mode 100644
index 0000000000000..5d368f27033f5
--- /dev/null
+++ b/clang/test/CIR/CodeGen/pointer-to-empty-data-member.cpp
@@ -0,0 +1,240 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir 
-Wno-unused-value -emit-cir -mmlir -mlir-print-ir-before=cir-cxxabi-lowering %s 
-o %t.cir 2> %t-before.cir
+// RUN: FileCheck --check-prefix=CIR,CIR-BEFORE --input-file=%t-before.cir %s
+// RUN: FileCheck --check-prefix=CIR,CIR-AFTER --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir 
-Wno-unused-value -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM,LLVMCIR --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 
-Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=LLVM,OGCG --input-file=%t.ll %s
+
+struct Empty {};
+// CIR-DAG: !rec_Empty = !cir.struct<"Empty" padded {!u8i}>
+// LLVMCIR-DAG: %struct.Empty = type { i8 }
+
+struct HasEmpty {
+  int size;
+  Empty s;
+};
+// CIR-DAG: !rec_HasEmpty = !cir.struct<"HasEmpty" {!s32i, !rec_Empty}>
+// LLVMCIR-DAG: %struct.HasEmpty = type { i32, %struct.Empty }
+// OGCG-DAG:    %struct.HasEmpty = type { i32, [4 x i8] }
+
+const HasEmpty globalHE = {1, {}};
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZL8globalHE = 
#cir.const_record<{#cir.int<1> : !s32i, #cir.zero : !rec_Empty}> : !rec_HasEmpty
+// LLVMCIR-DAG: @_ZL8globalHE = internal constant %struct.HasEmpty { i32 1, 
%struct.Empty zeroinitializer }
+// OGCG-DAG:    @_ZL8globalHE = internal constant %struct.HasEmpty { i32 1, [4 
x i8] undef }
+
+struct HasEmpty2 {
+  Empty s;
+  int size;
+};
+// CIR-DAG: !rec_HasEmpty2 = !cir.struct<"HasEmpty2" {!rec_Empty, !s32i}>
+// LLVMCIR-DAG: %struct.HasEmpty2 = type { %struct.Empty, i32 }
+// OGCG-DAG:    %struct.HasEmpty2 = type { [4 x i8], i32 }
+
+const HasEmpty2 globalHE2 = {{}, 1};
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZL9globalHE2 = 
#cir.const_record<{#cir.zero : !rec_Empty, #cir.int<1> : !s32i}> : 
!rec_HasEmpty2
+// LLVMCIR-DAG: @_ZL9globalHE2 = internal constant %struct.HasEmpty2 { 
%struct.Empty zeroinitializer, i32 1 }
+// OGCG-DAG:    @_ZL9globalHE2 = internal constant %struct.HasEmpty2 { [4 x 
i8] undef, i32 1 }
+
+// Not referenced enough to be emitted in 'after'.
+struct EmptyBase{};
+// CIR-BEFORE-DAG: !rec_EmptyBase = !cir.struct<"EmptyBase" padded {!u8i}>
+
+struct Base { int i; };
+// CIR-DAG: !rec_Base = !cir.struct<"Base" {!s32i}>
+// LLVMCIR-DAG: %struct.Base = type { i32 }
+struct D : EmptyBase, Base {};
+// CIR-DAG: !rec_D = !cir.struct<"D" {!rec_Base}>
+// LLVMCIR-DAG: %struct.D = type { %struct.Base }
+int D::* d_i = &D::i;
+// CIR-BEFORE-DAG: cir.global external @d_i = #cir.data_member<[0, 0]> : 
!cir.data_member<!s32i in !rec_D>
+// CIR-AFTER-DAG: cir.global external @d_i = #cir.int<0> : !s64i
+// LLVM-DAG: @d_i = global i64 0
+
+struct EmptyBase2 { Empty s; };
+// CIR-DAG: !rec_EmptyBase2 = !cir.struct<"EmptyBase2" {!rec_Empty}>
+// LLVMCIR-DAG: %struct.EmptyBase2 = type { %struct.Empty }
+struct Base2 { int i; };
+// CIR-DAG: !rec_Base2 = !cir.struct<"Base2" {!s32i}>
+// LLVMCIR-DAG: %struct.Base2 = type { i32 }
+struct D2 : EmptyBase2, Base2 {};
+// CIR-DAG: !rec_D2 = !cir.struct<"D2" {!rec_EmptyBase2, !rec_Base2}>
+// LLVMCIR-DAG: %struct.D2 = type { %struct.EmptyBase2, %struct.Base2 }
+Empty D2::* d2_s = &D2::s;
+// CIR-BEFORE-DAG: cir.global external @d2_s = #cir.data_member<[0, 0]> : 
!cir.data_member<!rec_Empty in !rec_D2>
+// CIR-AFTER-DAG: cir.global external @d2_s = #cir.int<0> : !s64i
+// LLVM-DAG: @d2_s = global i64 0
+int D2::* d2_i = &D2::i;
+// CIR-BEFORE-DAG: cir.global external @d2_i = #cir.data_member<[1, 0]> : 
!cir.data_member<!s32i in !rec_D2>
+// CIR-AFTER-DAG: cir.global external @d2_i = #cir.int<4> : !s64i
+// LLVM-DAG: @d2_i = global i64 4
+
+const D globalD = {{}, 1};
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZL7globalD = 
#cir.const_record<{#cir.const_record<{#cir.int<1> : !s32i}> : !rec_Base}> : 
!rec_D
+// LLVMCIR-DAG: @_ZL7globalD = internal constant %struct.D { %struct.Base { 
i32 1 } }
+// OGCG-DAG:    @_ZL7globalD = internal constant { i32 } { i32 1 }, align 4
+const D2 globalD2 = {{}, 1};
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZL8globalD2 = 
#cir.const_record<{#cir.zero : !rec_EmptyBase2, #cir.const_record<{#cir.int<1> 
: !s32i}> : !rec_Base2}> : !rec_D2
+// LLVMCIR-DAG: @_ZL8globalD2 = internal constant %struct.D2 { 
%struct.EmptyBase2 zeroinitializer, %struct.Base2 { i32 1 } }
+// OGCG-DAG:    @_ZL8globalD2 = internal constant { [4 x i8], i32 } { [4 x i8] 
undef, i32 1 }
+
+struct hasNUA {
+  [[no_unique_address]] EmptyBase eb1;
+  [[no_unique_address]] EmptyBase eb2;
+  [[no_unique_address]] EmptyBase eb3;
+  [[no_unique_address]] EmptyBase eb4;
+  [[no_unique_address]] EmptyBase eb5;
+  [[no_unique_address]] EmptyBase eb6;
+  int i;
+};
+// FIXME(cir): We should represent eb1-6 somehow
+// CIR-DAG: !rec_hasNUA = !cir.struct<"hasNUA" padded {!s32i, !cir.array<!u8i 
x 4>}>
+// LLVM-DAG: %struct.hasNUA = type { i32, [4 x i8] }
+
+const hasNUA nua = {{},{},{},{},{},{}, 1};
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZL3nua = 
#cir.const_record<{#cir.int<1> : !s32i, #cir.zero : !cir.array<!u8i x 4>}> : 
!rec_hasNUA
+// LLVMCIR-DAG: @_ZL3nua = internal constant %struct.hasNUA { i32 1, [4 x i8] 
zeroinitializer }
+// OGCG-DAG:    @_ZL3nua = internal constant %struct.hasNUA { i32 1, [4 x i8] 
undef }
+
+// FIXME(cir): These are still an NYI, and we should enable tests here once we
+// figure out how to represent these in IR.
+//EmptyBase hasNUA::* eb1 = &hasNUA::eb1;
+//EmptyBase hasNUA::* eb2 = &hasNUA::eb2;
+//EmptyBase hasNUA::* eb3 = &hasNUA::eb3;
+//EmptyBase hasNUA::* eb4 = &hasNUA::eb4;
+//EmptyBase hasNUA::* eb5 = &hasNUA::eb5;
+//EmptyBase hasNUA::* eb6 = &hasNUA::eb6;
+int hasNUA::* nua_i = &hasNUA::i;
+// CIR-BEFORE-DAG: cir.global external @nua_i = #cir.data_member<[0]> : 
!cir.data_member<!s32i in !rec_hasNUA>
+// CIR-AFTER-DAG: cir.global external @nua_i = #cir.int<0> : !s64i
+// LLVM-DAG: @nua_i = global i64 0
+
+union U1 {
+  EmptyBase eb;
+};
+// Rewrite of the data_member makes this unreferenced 'after'.
+// CIR-BEFORE-DAG: !rec_U1 = !cir.union<"U1" {!rec_EmptyBase}>
+
+EmptyBase U1::* u1eb = &U1::eb;
+// CIR-BEFORE-DAG: cir.global external @u1eb = #cir.data_member<[0]> : 
!cir.data_member<!rec_EmptyBase in !rec_U1>
+// CIR-AFTER-DAG: cir.global external @u1eb = #cir.int<0> : !s64i
+// LLVM-DAG: @u1eb = global i64 0
+
+union U2 {
+  int i;
+  EmptyBase eb;
+};
+// Rewrite of the data_member makes this unreferenced 'after'.
+// CIR-BEFORE-DAG: !rec_U2 = !cir.union<"U2" {!s32i, !rec_EmptyBase}>
+
+int U2::* u2i = &U2::i;
+// CIR-BEFORE-DAG: cir.global external @u2i = #cir.data_member<[0]> : 
!cir.data_member<!s32i in !rec_U2>
+// CIR-AFTER-DAG: cir.global external @u2i = #cir.int<0> : !s64i
+// LLVM-DAG: @u2i = global i64 0
+EmptyBase U2::* u2eb = &U2::eb;
+// CIR-BEFORE-DAG: cir.global external @u2eb = #cir.data_member<[1]> : 
!cir.data_member<!rec_EmptyBase in !rec_U2>
+// CIR-AFTER-DAG: cir.global external @u2eb = #cir.int<0> : !s64i
+// LLVM-DAG: @u2eb = global i64 0
+
+union U3 {
+  EmptyBase eb;
+  int i;
+};
+// Rewrite of the data_member makes this unreferenced 'after'.
+// CIR-BEFORE-DAG: !rec_U3 = !cir.union<"U3" {!rec_EmptyBase, !s32i}>
+
+int U3::* u3i = &U3::i;
+// CIR-BEFORE-DAG: cir.global external @u3i = #cir.data_member<[1]> : 
!cir.data_member<!s32i in !rec_U3>
+// CIR-AFTER-DAG: cir.global external @u3i = #cir.int<0> : !s64i
+// LLVM-DAG: @u3i = global i64 0
+EmptyBase U3::* u3eb = &U3::eb;
+// CIR-BEFORE-DAG: cir.global external @u3eb = #cir.data_member<[0]> : 
!cir.data_member<!rec_EmptyBase in !rec_U3>
+// CIR-AFTER-DAG: cir.global external @u3eb = #cir.int<0> : !s64i
+// LLVM-DAG: @u3eb = global i64 0
+
+union U4 {
+  EmptyBase eb;
+  EmptyBase eb2;
+  int i;
+};
+// CIR-BEFORE-DAG: !rec_U4 = !cir.union<"U4" {!rec_EmptyBase, !rec_EmptyBase, 
!s32i}>
+
+int U4::* u4i = &U4::i;
+// CIR-BEFORE-DAG: cir.global external @u4i = #cir.data_member<[2]> : 
!cir.data_member<!s32i in !rec_U4>
+// CIR-AFTER-DAG: cir.global external @u4i = #cir.int<0> : !s64i
+// LLVM-DAG: @u4i = global i64 0
+EmptyBase U4::* u4eb = &U4::eb;
+// CIR-BEFORE-DAG: cir.global external @u4eb = #cir.data_member<[0]> : 
!cir.data_member<!rec_EmptyBase in !rec_U4>
+// CIR-AFTER-DAG: cir.global external @u4eb = #cir.int<0> : !s64i
+// LLVM-DAG: @u4eb = global i64 0
+EmptyBase U4::* u4eb2 = &U4::eb2;
+// CIR-BEFORE-DAG: cir.global external @u4eb2 = #cir.data_member<[1]> : 
!cir.data_member<!rec_EmptyBase in !rec_U4>
+// CIR-AFTER-DAG: cir.global external @u4eb2 = #cir.int<0> : !s64i
+// LLVM-DAG: @u4eb2 = global i64 0
+
+union U5 {
+  int i;
+  EmptyBase eb;
+  EmptyBase eb2;
+};
+// CIR-BEFORE-DAG: !rec_U5 = !cir.union<"U5" {!s32i, !rec_EmptyBase, 
!rec_EmptyBase}>
+
+int U5::* u5i = &U5::i;
+// CIR-BEFORE-DAG: cir.global external @u5i = #cir.data_member<[0]> : 
!cir.data_member<!s32i in !rec_U5>
+// CIR-AFTER-DAG: cir.global external @u5i = #cir.int<0> : !s64i
+// LLVM-DAG: @u5i = global i64 0
+EmptyBase U5::* u5eb = &U5::eb;
+// CIR-BEFORE-DAG: cir.global external @u5eb = #cir.data_member<[1]> : 
!cir.data_member<!rec_EmptyBase in !rec_U5>
+// CIR-AFTER-DAG: cir.global external @u5eb = #cir.int<0> : !s64i
+// LLVM-DAG: @u5eb = global i64 0
+EmptyBase U5::* u5eb2 = &U5::eb2;
+// CIR-BEFORE-DAG: cir.global external @u5eb2 = #cir.data_member<[2]> : 
!cir.data_member<!rec_EmptyBase in !rec_U5>
+// CIR-AFTER-DAG: cir.global external @u5eb2 = #cir.int<0> : !s64i
+// LLVM-DAG: @u5eb2 = global i64 0
+
+union U6 {
+  [[no_unique_address]]
+  EmptyBase eb;
+  [[no_unique_address]]
+  EmptyBase eb2;
+  int i;
+};
+// CIR-BEFORE-DAG: !rec_U6 = !cir.union<"U6" {!rec_EmptyBase, !rec_EmptyBase, 
!s32i}>
+int U6::* u6i = &U6::i;
+// CIR-BEFORE-DAG: cir.global external @u6i = #cir.data_member<[2]> : 
!cir.data_member<!s32i in !rec_U6>
+// CIR-AFTER-DAG: cir.global external @u6i = #cir.int<0> : !s64i
+// LLVM-DAG: @u6i = global i64 0
+
+// FIXME(cir): See above.
+//EmptyBase U6::* u6eb = &U6::eb;
+//EmptyBase U6::* u6eb2 = &U6::eb2;
+
+union U7 {
+  int i;
+  [[no_unique_address]]
+  EmptyBase eb;
+  [[no_unique_address]]
+  EmptyBase eb2;
+};
+// CIR-BEFORE-DAG: !rec_U7 = !cir.union<"U7" {!s32i, !rec_EmptyBase, 
!rec_EmptyBase}>
+int U7::* u7i = &U7::i;
+// CIR-BEFORE-DAG: cir.global external @u7i = #cir.data_member<[0]> : 
!cir.data_member<!s32i in !rec_U7>
+// CIR-AFTER-DAG: cir.global external @u7i = #cir.int<0> : !s64i
+// LLVM-DAG: @u7i = global i64 0
+
+// FIXME(cir): See above.
+//EmptyBase U7::* u7eb = &U7::eb;
+//EmptyBase U7::* u7eb2 = &U7::eb2;
+
+void uses() {
+  auto x = &HasEmpty::s;
+  auto y = &HasEmpty2::s;
+
+  globalHE.size;
+  globalHE2.size;
+
+  globalD.i;
+  globalD2.i;
+
+  nua.i;
+}

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

Reply via email to