Skip to content

[CIR] Introduce cir::RecordKind::Class #142690

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 4, 2025
Merged

Conversation

andykaylor
Copy link
Contributor

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.

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.
@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Jun 3, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 3, 2025

@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: Andy Kaylor (andykaylor)

Changes

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.


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

6 Files Affected:

  • (modified) clang/include/clang/CIR/Dialect/IR/CIRTypes.td (+9-1)
  • (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.h (+1)
  • (modified) clang/lib/CIR/Dialect/IR/CIRTypes.cpp (+5)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+2)
  • (added) clang/test/CIR/CodeGen/class.cpp (+34)
  • (modified) clang/test/CIR/IR/struct.cir (+4)
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>

Copy link
Contributor

@xlauko xlauko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@andykaylor andykaylor merged commit 68a346f into llvm:main Jun 4, 2025
16 of 17 checks passed
rorth pushed a commit to rorth/llvm-project that referenced this pull request Jun 11, 2025
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.
DhruvSrivastavaX pushed a commit to DhruvSrivastavaX/lldb-for-aix that referenced this pull request Jun 12, 2025
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants