llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clangir

Author: Erich Keane (erichkeane)

<details>
<summary>Changes</summary>

Anonymous structs/unions cause an 'indirect field' declaration, which contains 
the path with how to get through it.  We were missing handling in 2 places:
1- At emitTopLevelDecl, which we just need to ignore it (as the global
    itself handles the emit)
2- When generating the get-member pointer value. This just requires us to 
properly follow the 'field' chain.

---
Full diff: https://github.com/llvm/llvm-project/pull/219505.diff


4 Files Affected:

- (modified) clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp (+3-3) 
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+26-6) 
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+4-2) 
- (added) clang/test/CIR/CodeGen/pointer-to-data-member-indirect-field.cpp 
(+92) 


``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index b3651960bd2cb..385568c7f6f0f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -1463,13 +1463,13 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const 
APValue &value,
     }
 
     auto cirTy = mlir::cast<cir::DataMemberType>(cgm.convertType(destType));
-    const auto *fieldDecl = cast<FieldDecl>(memberDecl);
     const auto *mpt = destType->castAs<MemberPointerType>();
     const auto *destClass = mpt->getMostRecentCXXRecordDecl();
 
     // Empty [[no_unique_address]] fields have no CIR field index; represent 
the
     // pointer-to-data-member by its concrete byte offset.
-    if (cgm.isEmptyFieldForMemberPointer(fieldDecl)) {
+    if (const auto *fieldDecl = dyn_cast<FieldDecl>(memberDecl);
+        fieldDecl && cgm.isEmptyFieldForMemberPointer(fieldDecl)) {
       const ASTContext &astContext = cgm.getASTContext();
       CharUnits offset =
           astContext.getMemberPointerPathAdjustment(value) +
@@ -1478,7 +1478,7 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const 
APValue &value,
     }
 
     std::optional<llvm::SmallVector<int32_t>> path =
-        cgm.buildMemberPath(destClass, fieldDecl);
+        cgm.buildMemberPath(destClass, memberDecl);
     if (!path)
       return {};
     return builder.getDataMemberAttr(cirTy, *path);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index cc4ce9a786327..d61743de3a8e4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2338,7 +2338,7 @@ mlir::Value CIRGenModule::emitMemberPointerConstant(const 
UnaryOperator *e) {
 
   mlir::Location loc = getLoc(e->getSourceRange());
 
-  const auto *decl = cast<DeclRefExpr>(e->getSubExpr())->getDecl();
+  const ValueDecl *decl = cast<DeclRefExpr>(e->getSubExpr())->getDecl();
 
   // A member function pointer.
   if (const auto *methodDecl = dyn_cast<CXXMethodDecl>(decl)) {
@@ -2357,13 +2357,13 @@ mlir::Value 
CIRGenModule::emitMemberPointerConstant(const UnaryOperator *e) {
 
   // Otherwise, a member data pointer.
   auto ty = mlir::cast<cir::DataMemberType>(convertType(e->getType()));
-  const auto *fieldDecl = cast<FieldDecl>(decl);
   const auto *mpt = e->getType()->castAs<MemberPointerType>();
   const auto *destClass = mpt->getMostRecentCXXRecordDecl();
 
   // Empty [[no_unique_address]] fields have no CIR field index; represent the
   // pointer-to-data-member by its concrete byte offset within the class.
-  if (isEmptyFieldForMemberPointer(fieldDecl)) {
+  if (const auto *fieldDecl = dyn_cast<FieldDecl>(decl);
+      fieldDecl && isEmptyFieldForMemberPointer(fieldDecl)) {
     // This function should ONLY be accessed in reference to itself, I don't 
see
     // any cases/couldn't find any cases where anything else could get here, 
and
     // classic-codegen does the same.
@@ -2377,7 +2377,7 @@ mlir::Value CIRGenModule::emitMemberPointerConstant(const 
UnaryOperator *e) {
   }
 
   std::optional<llvm::SmallVector<int32_t>> path =
-      buildMemberPath(destClass, fieldDecl);
+      buildMemberPath(destClass, decl);
   if (!path)
     return {};
   return cir::ConstantOp::create(builder, loc,
@@ -2386,9 +2386,24 @@ mlir::Value 
CIRGenModule::emitMemberPointerConstant(const UnaryOperator *e) {
 
 std::optional<llvm::SmallVector<int32_t>>
 CIRGenModule::buildMemberPath(const CXXRecordDecl *destClass,
-                              const FieldDecl *field) {
+                              const ValueDecl *decl) {
   llvm::SmallVector<int32_t> path;
-  if (!findFieldMemberPath(destClass, field, path))
+
+  // Members of an anonymous struct/union have an IndirectFieldDecl, which
+  // contains the whole chain of how to get to it, so to get the 'path', we dig
+  // through those rather than searching.
+  if (const auto *indirectField = dyn_cast<IndirectFieldDecl>(decl)) {
+    const CXXRecordDecl *currentClass = destClass;
+    for (const NamedDecl *nd : indirectField->chain()) {
+      const auto *field = cast<FieldDecl>(nd);
+      if (!findFieldMemberPath(currentClass, field, path))
+        return std::nullopt;
+      currentClass = field->getType()->getAsCXXRecordDecl();
+    }
+    return path;
+  }
+
+  if (!findFieldMemberPath(destClass, cast<FieldDecl>(decl), path))
     return std::nullopt;
   return path;
 }
@@ -2549,6 +2564,11 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) {
     assert(!cir::MissingFeatures::generateDebugInfo());
     break;
 
+  // Indirect fields from global anonymous structs and unions can be
+  // ignored; only the actual variable requires IR gen support.
+  case Decl::IndirectField:
+    break;
+
   // No code generation needed.
   case Decl::ClassTemplate:
   case Decl::Concept:
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h 
b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 7ea47ba6d5d24..012478cc2ad23 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -728,10 +728,12 @@ class CIRGenModule : public CIRGenTypeCache {
   /// member, depending on the type of mpt.
   mlir::TypedAttr emitNullMemberAttr(QualType t, const MemberPointerType *mpt);
 
-  /// Build a GEP-style field-index path from \p destClass to \p field.
+  /// Build a GEP-style field-index path from \p destClass to \p decl.
+  /// \p decl may be a FieldDecl, or an IndirectFieldDecl(in the case of an
+  /// anonymous struct/union).
   /// Returns std::nullopt and emits errorNYI for virtual-base paths.
   std::optional<llvm::SmallVector<int32_t>>
-  buildMemberPath(const CXXRecordDecl *destClass, const FieldDecl *field);
+  buildMemberPath(const CXXRecordDecl *destClass, const ValueDecl *decl);
 
   /// Returns true if \p field is an empty field that isn't laid out in the CIR
   /// record (e.g. a [[no_unique_address]] empty member). Such fields have no
diff --git a/clang/test/CIR/CodeGen/pointer-to-data-member-indirect-field.cpp 
b/clang/test/CIR/CodeGen/pointer-to-data-member-indirect-field.cpp
new file mode 100644
index 0000000000000..c4917344260f8
--- /dev/null
+++ b/clang/test/CIR/CodeGen/pointer-to-data-member-indirect-field.cpp
@@ -0,0 +1,92 @@
+// 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-BEFORE --input-file=%t-before.cir %s
+// RUN: FileCheck --check-prefix=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 --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 --input-file=%t.ll %s
+
+struct A {
+  int b;
+  union { int c; };
+};
+
+int A::*pt_anon_union_member = &A::c;
+// CIR-BEFORE: cir.global external @pt_anon_union_member = 
#cir.data_member<[1, 0]> : !cir.data_member<!s32i in !rec_A>
+// CIR-AFTER: cir.global external @pt_anon_union_member = #cir.int<4> : !s64i
+// LLVM: @pt_anon_union_member = global i64 4
+
+struct B {
+  int b;
+  union {
+    struct {
+      int c;
+      int e;
+    };
+    float f;
+  };
+};
+
+int B::*pt_nested_anon_first = &B::c;
+// CIR-BEFORE: cir.global external @pt_nested_anon_first = 
#cir.data_member<[1, 0, 0]> : !cir.data_member<!s32i in !rec_B>
+// CIR-AFTER: cir.global external @pt_nested_anon_first = #cir.int<4> : !s64i
+// LLVM: @pt_nested_anon_first = global i64 4
+
+int B::*pt_nested_anon_second = &B::e;
+// CIR-BEFORE: cir.global external @pt_nested_anon_second = 
#cir.data_member<[1, 0, 1]> : !cir.data_member<!s32i in !rec_B>
+// CIR-AFTER: cir.global external @pt_nested_anon_second = #cir.int<8> : !s64i
+// LLVM: @pt_nested_anon_second = global i64 8
+
+static union { int gx; float gy; };
+// CIR-BEFORE: cir.global "private" internal dso_local @_Z2gx = #cir.zero : 
!rec_anon2E3 {alignment = 4 : i64}
+// CIR-AFTER: cir.global "private" internal dso_local @_Z2gx = #cir.zero : 
!rec_anon2E3 {alignment = 4 : i64}
+// LLVM: @_Z2gx = internal global %union.anon{{.*}} zeroinitializer, align 4
+
+int test_use(A &a, int A::*member) {
+  return a.*member;
+}
+
+// CIR-BEFORE-LABEL: cir.func {{.*}}@_Z8test_useR1AMS_i(
+// CIR-BEFORE: %[[LOAD_ARG:.*]] = cir.load %{{.*}} : 
!cir.ptr<!cir.ptr<!rec_A>>, !cir.ptr<!rec_A>
+// CIR-BEFORE: %[[LOAD_PTR:.*]] = cir.load align(8) %{{.*}} : 
!cir.ptr<!cir.data_member<!s32i in !rec_A>>, !cir.data_member<!s32i in !rec_A>
+// CIR-BEFORE: cir.get_runtime_member %[[LOAD_ARG]][%[[LOAD_PTR]] : 
!cir.data_member<!s32i in !rec_A>] : !cir.ptr<!rec_A> -> !cir.ptr<!s32i>
+
+// CIR-AFTER-LABEL: cir.func {{.*}}@_Z8test_useR1AMS_i(
+// CIR-AFTER: %[[LOAD_ARG:.*]] = cir.load %{{.*}} : 
!cir.ptr<!cir.ptr<!rec_A>>, !cir.ptr<!rec_A>
+// CIR-AFTER: %[[LOAD_PTR:.*]] = cir.load align(8) %{{.*}} : !cir.ptr<!s64i>, 
!s64i
+// CIR-AFTER: %[[ARG_TO_CHARPTR:.*]] = cir.cast bitcast %[[LOAD_ARG]] : 
!cir.ptr<!rec_A> -> !cir.ptr<!s8i>
+// CIR-AFTER: %[[OFFSET:.*]] = cir.ptr_stride %[[ARG_TO_CHARPTR]], 
%[[LOAD_PTR]] : (!cir.ptr<!s8i>, !s64i) -> !cir.ptr<!s8i>
+// CIR-AFTER: cir.cast bitcast %[[OFFSET]] : !cir.ptr<!s8i> -> !cir.ptr<!s32i>
+
+// LLVM-LABEL: define {{.*}}i32 @_Z8test_useR1AMS_i(
+// LLVM: %[[LOAD_ARG:.*]] = load ptr, ptr %{{.*}}, align 8
+// LLVM: %[[LOAD_PTR:.*]] = load i64, ptr %{{.*}}, align 8
+// LLVM: getelementptr {{.*}}i8, ptr %[[LOAD_ARG]], i64 %[[LOAD_PTR]]
+
+int test_call_use(A &a) {
+  return test_use(a, &A::c);
+}
+
+// CIR-BEFORE-LABEL: cir.func {{.*}}@_Z13test_call_useR1A
+// CIR-BEFORE: %[[MEMBER:.*]] = cir.const #cir.data_member<[1, 0]> : 
!cir.data_member<!s32i in !rec_A>
+// CIR-BEFORE: cir.call @_Z8test_useR1AMS_i(%{{.*}}, %[[MEMBER]])
+
+// CIR-AFTER-LABEL: cir.func {{.*}}@_Z13test_call_useR1A
+// CIR-AFTER:   %[[MEMBER:.*]] = cir.const #cir.int<4> : !s64i
+// CIR-AFTER:   cir.call @_Z8test_useR1AMS_i({{.*}}, %[[MEMBER]])
+
+// LLVM-LABEL: define {{.*}} i32 @_Z13test_call_useR1A(
+// LLVM:   call {{.*}} i32 @_Z8test_useR1AMS_i({{.*}}, i64 4)
+
+int use_global_anon_union() { return gy; }
+
+// CIR-BEFORE-LABEL: cir.func {{.*}}@_Z21use_global_anon_unionv()
+// CIR-BEFORE: %[[GET_GLOB:.*]] = cir.get_global @_Z2gx : 
!cir.ptr<!rec_anon2E3>
+// CIR-BEFORE: cir.get_member %[[GET_GLOB]][1] {name = "gy"} : 
!cir.ptr<!rec_anon2E3> -> !cir.ptr<!cir.float>
+
+// CIR-AFTER-LABEL: cir.func {{.*}}@_Z21use_global_anon_unionv()
+// CIR-AFTER: %[[GET_GLOB:.*]] = cir.get_global @_Z2gx : !cir.ptr<!rec_anon2E3>
+// CIR-AFTER: cir.get_member %[[GET_GLOB]][1] {name = "gy"} : 
!cir.ptr<!rec_anon2E3> -> !cir.ptr<!cir.float>
+
+// LLVM-LABEL: define {{.*}}i32 @_Z21use_global_anon_unionv()
+// LLVM: load float, ptr @_Z2gx, align 4

``````````

</details>


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

Reply via email to