https://github.com/andykaylor created 
https://github.com/llvm/llvm-project/pull/142690

When cir::RecordType was initially upstreamed, we decided that there was no 
reason to distinguish between RecordKind::Class and RecordKind::Struct since 
they are semantically equivalent. I think this was a mistake because classic 
codegen does preserve the distinction (which is visible in the AST) and 
preserving the distinction in CIR will aid the possibility of eventually using 
the classic codegen lit tests with CIR.

This change introduces RecordKind::Class, which is already present in the 
incubator implementation.

>From 6c207a657ad961ab0330f8bca8a01797103a5fd5 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akay...@nvidia.com>
Date: Tue, 3 Jun 2025 16:09:20 -0700
Subject: [PATCH] [CIR] Introduce cir::RecordKind::Class

When cir::RecordType was initially upstreamed, we decided that there
was no reason to distinguish between RecordKind::Class and
RecordKind::Struct since they are semantically equivalent. I think this
was a mistake because classic codegen does preserve the distinction
(which is visible in the AST) and preserving the distinction in CIR
will aid the possibility of eventually using the classic codegen lit
tests with CIR.

This change introduces RecordKind::Class, which is already present in the
incubator implementation.
---
 .../include/clang/CIR/Dialect/IR/CIRTypes.td  | 10 +++++-
 clang/lib/CIR/CodeGen/CIRGenBuilder.h         |  1 +
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         |  5 +++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  2 ++
 clang/test/CIR/CodeGen/class.cpp              | 34 +++++++++++++++++++
 clang/test/CIR/IR/struct.cir                  |  4 +++
 6 files changed, 55 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/CIR/CodeGen/class.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 9c0af8d3eaa5f..75eb9cc045a48 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -431,6 +431,11 @@ def CIR_RecordType : CIR_Type<"Record", "record",
     will be the same type. Attempting to build a record with an existing name,
     but a different body will result in an error.
 
+    Each record type will have a `RecordKind` that is either `Class`, `Struct`,
+    or `Union`, depending on the C/C++ type that it is representing. Note that
+    `Class` and `Struct` are semantically identical, but the kind preserves the
+    keyword that was used to declare the type in the original source code.
+
     A few examples:
 
     ```mlir
@@ -482,8 +487,9 @@ def CIR_RecordType : CIR_Type<"Record", "record",
   let extraClassDeclaration = [{
     using Base::verifyInvariants;
 
-    enum RecordKind : uint32_t { Struct, Union };
+    enum RecordKind : uint32_t { Class, Struct, Union };
 
+    bool isClass() const { return getKind() == RecordKind::Class; };
     bool isStruct() const { return getKind() == RecordKind::Struct; };
     bool isUnion() const { return getKind() == RecordKind::Union; };
     bool isComplete() const { return !isIncomplete(); };
@@ -493,6 +499,8 @@ def CIR_RecordType : CIR_Type<"Record", "record",
     size_t getNumElements() const { return getMembers().size(); };
     std::string getKindAsStr() {
       switch (getKind()) {
+      case RecordKind::Class:
+        return "class";
       case RecordKind::Union:
         return "union";
       case RecordKind::Struct:
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index b8548487d5b31..5f17b5d58acaa 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -83,6 +83,7 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
   cir::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
     switch (kind) {
     case clang::TagTypeKind::Class:
+      return cir::RecordType::Class;
     case clang::TagTypeKind::Struct:
       return cir::RecordType::Struct;
     case clang::TagTypeKind::Union:
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 900871c3c2cba..3543c139bbf79 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -98,6 +98,8 @@ Type RecordType::parse(mlir::AsmParser &parser) {
     kind = RecordKind::Struct;
   else if (parser.parseOptionalKeyword("union").succeeded())
     kind = RecordKind::Union;
+  else if (parser.parseOptionalKeyword("class").succeeded())
+    kind = RecordKind::Class;
   else {
     parser.emitError(loc, "unknown record type");
     return {};
@@ -168,6 +170,9 @@ void RecordType::print(mlir::AsmPrinter &printer) const {
   case RecordKind::Union:
     printer << "union ";
     break;
+  case RecordKind::Class:
+    printer << "class ";
+    break;
   }
 
   if (getName())
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index f61e85ce3ccec..909b8efd95b88 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -1584,6 +1584,7 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter 
&converter,
     // Convert struct members.
     llvm::SmallVector<mlir::Type> llvmMembers;
     switch (type.getKind()) {
+    case cir::RecordType::Class:
     case cir::RecordType::Struct:
       for (mlir::Type ty : type.getMembers())
         llvmMembers.push_back(convertTypeForMemory(converter, dataLayout, ty));
@@ -1767,6 +1768,7 @@ mlir::LogicalResult 
CIRToLLVMGetMemberOpLowering::matchAndRewrite(
   assert(recordTy && "expected record type");
 
   switch (recordTy.getKind()) {
+  case cir::RecordType::Class:
   case cir::RecordType::Struct: {
     // Since the base address is a pointer to an aggregate, the first offset
     // is always zero. The second offset tell us which member it will access.
diff --git a/clang/test/CIR/CodeGen/class.cpp b/clang/test/CIR/CodeGen/class.cpp
new file mode 100644
index 0000000000000..dd280d3d906ba
--- /dev/null
+++ b/clang/test/CIR/CodeGen/class.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -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 -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// CIR: !rec_IncompleteC = !cir.record<class "IncompleteC" incomplete>
+// CIR: !rec_CompleteC = !cir.record<class "CompleteC" {!s32i, !s8i}>
+
+// Note: LLVM and OGCG do not emit the type for incomplete classes.
+
+// LLVM: %class.CompleteC = type { i32, i8 }
+
+// OGCG: %class.CompleteC = type { i32, i8 }
+
+class IncompleteC;
+IncompleteC *p;
+
+// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!rec_IncompleteC>
+// LLVM: @p = global ptr null
+// OGCG: @p = global ptr null, align 8
+
+class CompleteC {
+public:    
+  int a;
+  char b;
+};
+
+CompleteC cc;
+
+// CIR:       cir.global external @cc = #cir.zero : !rec_CompleteC
+// LLVM:  @cc = global %class.CompleteC zeroinitializer
+// OGCG:  @cc = global %class.CompleteC zeroinitializer
diff --git a/clang/test/CIR/IR/struct.cir b/clang/test/CIR/IR/struct.cir
index 7f0ce07631182..5ff5ff7cd1f69 100644
--- a/clang/test/CIR/IR/struct.cir
+++ b/clang/test/CIR/IR/struct.cir
@@ -1,15 +1,19 @@
 // RUN: cir-opt %s | FileCheck %s
 
+!rec_C = !cir.record<class "C" incomplete>
 !rec_S = !cir.record<struct "S" incomplete>
 !rec_U = !cir.record<union "U" incomplete>
 
+// CHECK: !rec_C = !cir.record<class "C" incomplete>
 // CHECK: !rec_S = !cir.record<struct "S" incomplete>
 // CHECK: !rec_U = !cir.record<union "U" incomplete>
 
 module  {
     cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!rec_S>
     cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!rec_U>
+    cir.global external @p3 = #cir.ptr<null> : !cir.ptr<!rec_C>
 }
 
 // CHECK: cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!rec_S>
 // CHECK: cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!rec_U>
+// CHECK: cir.global external @p3 = #cir.ptr<null> : !cir.ptr<!rec_C>

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to