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