[clang] [CIR] Upstream minimal support for structure types (PR #135105)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 11 16:44:09 PDT 2025
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/135105
>From 3b19527073c656b631a990397a00030487b3f595 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 9 Apr 2025 12:45:57 -0700
Subject: [PATCH 1/5] [CIR] Upstream minimal support for structure types
This change adds minimal support for structure types. To keep the initial
change small, only incomplete declarations are being supported in this
patch. More complete support will follow.
---
clang/include/clang/CIR/Dialect/IR/CIRTypes.h | 4 +
.../include/clang/CIR/Dialect/IR/CIRTypes.td | 115 +++++++++++-
.../clang/CIR/Dialect/IR/CIRTypesDetails.h | 116 ++++++++++++
clang/include/clang/CIR/MissingFeatures.h | 3 +
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 39 ++++
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 4 +
clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 77 +++++++-
clang/lib/CIR/CodeGen/CIRGenTypes.h | 9 +-
clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 176 +++++++++++++++++-
clang/test/CIR/CodeGen/struct.c | 13 ++
clang/test/CIR/IR/struct.cir | 9 +
11 files changed, 558 insertions(+), 7 deletions(-)
create mode 100644 clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
create mode 100644 clang/test/CIR/CodeGen/struct.c
create mode 100644 clang/test/CIR/IR/struct.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
index 7b0fcbc7cc98f..d2c407b2fd686 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
@@ -20,6 +20,10 @@
namespace cir {
+namespace detail {
+struct StructTypeStorage;
+} // namespace detail
+
bool isAnyFloatingPointType(mlir::Type t);
bool isFPOrFPVectorTy(mlir::Type);
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index e285c0f28f113..bcdaf54bbd84f 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -400,13 +400,126 @@ def VoidPtr : Type<
"cir::VoidType::get($_builder.getContext()))"> {
}
+//===----------------------------------------------------------------------===//
+// StructType
+//
+// The base type for all RecordDecls.
+//===----------------------------------------------------------------------===//
+
+def CIR_StructType : CIR_Type<"Struct", "struct",
+ [
+ DeclareTypeInterfaceMethods<DataLayoutTypeInterface>,
+ MutableType,
+ ]> {
+ let summary = "CIR struct type";
+ let description = [{
+ Each unique clang::RecordDecl is mapped to a `cir.struct` and any object in
+ C/C++ that has a struct type will have a `cir.struct` in CIR.
+
+ There are three possible formats for this type:
+
+ - Identified and complete structs: unique name and a known body.
+ - Identified and incomplete structs: unique name and unknown body.
+ - Anonymous structs: no name and a known body.
+
+ Identified structs are uniqued by their name, and anonymous structs are
+ uniqued by their body. This means that two anonymous structs with the same
+ body will be the same type, and two identified structs with the same name
+ will be the same type. Attempting to build a struct with an existing name,
+ but a different body will result in an error.
+
+ A few examples:
+
+ ```mlir
+ !complete = !cir.struct<struct "complete" {!cir.int<u, 8>}>
+ !incomplete = !cir.struct<struct "incomplete" incomplete>
+ !anonymous = !cir.struct<struct {!cir.int<u, 8>}>
+ ```
+
+ Incomplete structs are mutable, meaning they can be later completed with a
+ body automatically updating in place every type in the code that uses the
+ incomplete struct. Mutability allows for recursive types to be represented,
+ meaning the struct can have members that refer to itself. This is useful for
+ representing recursive records and is implemented through a special syntax.
+ In the example below, the `Node` struct has a member that is a pointer to a
+ `Node` struct:
+
+ ```mlir
+ !struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct
+ "Node">>}>
+ ```
+ }];
+
+ let parameters = (ins
+ OptionalArrayRefParameter<"mlir::Type">:$members,
+ OptionalParameter<"mlir::StringAttr">:$name,
+ "bool":$incomplete,
+ "bool":$packed,
+ "bool":$padded,
+ "StructType::RecordKind":$kind
+ );
+
+ // StorageClass is defined in C++ for mutability.
+ let storageClass = "StructTypeStorage";
+ let genStorageClass = 0;
+
+ let skipDefaultBuilders = 1;
+ let genVerifyDecl = 1;
+
+ let builders = [
+ // Create an identified and incomplete struct type.
+ TypeBuilder<(ins
+ "mlir::StringAttr":$name,
+ "RecordKind":$kind
+ ), [{
+ return $_get($_ctxt, /*members=*/llvm::ArrayRef<Type>{}, name,
+ /*incomplete=*/true, /*packed=*/false,
+ /*padded=*/false, kind);
+ }]>];
+
+ let extraClassDeclaration = [{
+ using Base::verifyInvariants;
+
+ enum RecordKind : uint32_t { Class, Union, Struct };
+
+ 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(); };
+ bool isIncomplete() const;
+
+ 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:
+ return "struct";
+ }
+ llvm_unreachable("Invalid value for StructType::getKind()");
+ }
+ std::string getPrefixedName() {
+ return getKindAsStr() + "." + getName().getValue().str();
+ }
+ }];
+
+ let hasCustomAssemblyFormat = 1;
+}
+
+// Note CIRStructType is used instead of CIR_StructType
+// because of tablegen conflicts.
+def CIRStructType : Type<
+ CPred<"::mlir::isa<::cir::StructType>($_self)">, "CIR struct type">;
+
//===----------------------------------------------------------------------===//
// Global type constraints
//===----------------------------------------------------------------------===//
def CIR_AnyType : AnyTypeOf<[
CIR_VoidType, CIR_BoolType, CIR_ArrayType, CIR_IntType, CIR_AnyFloat,
- CIR_PointerType, CIR_FuncType
+ CIR_PointerType, CIR_FuncType, CIR_StructType
]>;
#endif // MLIR_CIR_DIALECT_CIR_TYPES
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
new file mode 100644
index 0000000000000..91bfb814a609c
--- /dev/null
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains implementation details, such as storage structures, of
+// CIR dialect types.
+//
+//===----------------------------------------------------------------------===//
+#ifndef CIR_DIALECT_IR_CIRTYPESDETAILS_H
+#define CIR_DIALECT_IR_CIRTYPESDETAILS_H
+
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/Support/LogicalResult.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+#include "llvm/ADT/Hashing.h"
+
+namespace cir {
+namespace detail {
+
+//===----------------------------------------------------------------------===//
+// CIR StructTypeStorage
+//===----------------------------------------------------------------------===//
+
+/// Type storage for CIR record types.
+struct StructTypeStorage : public mlir::TypeStorage {
+ struct KeyTy {
+ llvm::ArrayRef<mlir::Type> members;
+ mlir::StringAttr name;
+ bool incomplete;
+ bool packed;
+ bool padded;
+ StructType::RecordKind kind;
+
+ KeyTy(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
+ bool incomplete, bool packed, bool padded,
+ StructType::RecordKind kind)
+ : members(members), name(name), incomplete(incomplete), packed(packed),
+ padded(padded), kind(kind) {}
+ };
+
+ llvm::ArrayRef<mlir::Type> members;
+ mlir::StringAttr name;
+ bool incomplete;
+ bool packed;
+ bool padded;
+ StructType::RecordKind kind;
+
+ StructTypeStorage(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
+ bool incomplete, bool packed, bool padded,
+ StructType::RecordKind kind)
+ : members(members), name(name), incomplete(incomplete), packed(packed),
+ padded(padded), kind(kind) {}
+
+ KeyTy getAsKey() const {
+ return KeyTy(members, name, incomplete, packed, padded, kind);
+ }
+
+ bool operator==(const KeyTy &key) const {
+ if (name)
+ return (name == key.name) && (kind == key.kind);
+ return (members == key.members) && (name == key.name) &&
+ (incomplete == key.incomplete) && (packed == key.packed) &&
+ (padded == key.padded) && (kind == key.kind);
+ }
+
+ static llvm::hash_code hashKey(const KeyTy &key) {
+ if (key.name)
+ return llvm::hash_combine(key.name, key.kind);
+ return llvm::hash_combine(key.members, key.incomplete, key.packed,
+ key.padded, key.kind);
+ }
+
+ static StructTypeStorage *construct(mlir::TypeStorageAllocator &allocator,
+ const KeyTy &key) {
+ return new (allocator.allocate<StructTypeStorage>())
+ StructTypeStorage(allocator.copyInto(key.members), key.name,
+ key.incomplete, key.packed, key.padded, key.kind);
+ }
+
+ /// Mutates the members and attributes an identified struct.
+ ///
+ /// Once a record is mutated, it is marked as complete, preventing further
+ /// mutations. Anonymous structs are always complete and cannot be mutated.
+ /// This method does not fail if a mutation of a complete struct does not
+ /// change the struct.
+ llvm::LogicalResult mutate(mlir::TypeStorageAllocator &allocator,
+ llvm::ArrayRef<mlir::Type> members, bool packed,
+ bool padded) {
+ // Anonymous structs cannot mutate.
+ if (!name)
+ return llvm::failure();
+
+ // Mutation of complete structs are allowed if they change nothing.
+ if (!incomplete)
+ return mlir::success((this->members == members) &&
+ (this->packed == packed) &&
+ (this->padded == padded));
+
+ // Mutate incomplete struct.
+ this->members = allocator.copyInto(members);
+ this->packed = packed;
+ this->padded = padded;
+
+ incomplete = false;
+ return llvm::success();
+ }
+};
+
+} // namespace detail
+} // namespace cir
+
+#endif // CIR_DIALECT_IR_CIRTYPESDETAILS_H
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index d6a28d4324b32..be6919557f7e1 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -101,6 +101,9 @@ struct MissingFeatures {
static bool mayHaveIntegerOverflow() { return false; }
static bool shouldReverseUnaryCondOnBoolExpr() { return false; }
+ // StructType
+ static bool structTypeLayoutInfo() { return false; }
+
// Misc
static bool cxxABI() { return false; }
static bool tryEmitAsConstant() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 0d8568742e960..08c2a60b50e29 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -20,11 +20,24 @@ namespace clang::CIRGen {
class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
const CIRGenTypeCache &typeCache;
+ llvm::StringMap<unsigned> recordNames;
public:
CIRGenBuilderTy(mlir::MLIRContext &mlirContext, const CIRGenTypeCache &tc)
: CIRBaseBuilderTy(mlirContext), typeCache(tc) {}
+ std::string getUniqueAnonRecordName() { return getUniqueRecordName("anon"); }
+
+ std::string getUniqueRecordName(const std::string &baseName) {
+ auto it = recordNames.find(baseName);
+ if (it == recordNames.end()) {
+ recordNames[baseName] = 0;
+ return baseName;
+ }
+
+ return baseName + "." + std::to_string(recordNames[baseName]++);
+ }
+
cir::LongDoubleType getLongDoubleTy(const llvm::fltSemantics &format) const {
if (&format == &llvm::APFloat::IEEEdouble())
return cir::LongDoubleType::get(getContext(), typeCache.DoubleTy);
@@ -37,6 +50,32 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
llvm_unreachable("Unsupported format for long double");
}
+ /// Get a CIR record kind from a AST declaration tag.
+ cir::StructType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
+ switch (kind) {
+ case clang::TagTypeKind::Struct:
+ return cir::StructType::Struct;
+ case clang::TagTypeKind::Union:
+ return cir::StructType::Union;
+ case clang::TagTypeKind::Class:
+ return cir::StructType::Class;
+ case clang::TagTypeKind::Interface:
+ llvm_unreachable("interface records are NYI");
+ case clang::TagTypeKind::Enum:
+ llvm_unreachable("enum records are NYI");
+ }
+ }
+
+ /// Get an incomplete CIR struct type.
+ cir::StructType getIncompleteStructTy(llvm::StringRef name,
+ const clang::RecordDecl *rd) {
+ const mlir::StringAttr nameAttr = getStringAttr(name);
+ cir::StructType::RecordKind kind = cir::StructType::RecordKind::Struct;
+ if (rd)
+ kind = getRecordKind(rd->getTagKind());
+ return getType<cir::StructType>(nameAttr, kind);
+ }
+
bool isSized(mlir::Type ty) {
if (mlir::isa<cir::PointerType, cir::ArrayType, cir::BoolType,
cir::IntType>(ty))
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index fd11523ebba61..e4919ed76b450 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -589,6 +589,10 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) {
case Decl::OpenACCDeclare:
emitGlobalOpenACCDecl(cast<OpenACCDeclareDecl>(decl));
break;
+
+ case Decl::Record:
+ assert(!cir::MissingFeatures::generateDebugInfo());
+ break;
}
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index a5978a4ad9085..6829b1000598a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -86,10 +86,80 @@ mlir::Type CIRGenTypes::convertFunctionTypeInternal(QualType qft) {
return cir::FuncType::get(SmallVector<mlir::Type, 1>{}, cgm.VoidTy);
}
+// This is CIR's version of CodeGenTypes::addRecordTypeName. It isn't shareable
+// because CIR has different uniquing requirements.
+std::string CIRGenTypes::getRecordTypeName(const clang::RecordDecl *recordDecl,
+ StringRef suffix) {
+ llvm::SmallString<256> typeName;
+ llvm::raw_svector_ostream outStream(typeName);
+
+ PrintingPolicy policy = recordDecl->getASTContext().getPrintingPolicy();
+ policy.SuppressInlineNamespace = false;
+
+ if (recordDecl->getIdentifier()) {
+ if (recordDecl->getDeclContext())
+ recordDecl->printQualifiedName(outStream, policy);
+ else
+ recordDecl->printName(outStream, policy);
+
+ // Ensure each template specialization has a unique name.
+ if (auto *templateSpecialization =
+ llvm::dyn_cast<ClassTemplateSpecializationDecl>(recordDecl)) {
+ outStream << '<';
+ const ArrayRef<TemplateArgument> args =
+ templateSpecialization->getTemplateArgs().asArray();
+ const auto printer = [&policy, &outStream](const TemplateArgument &arg) {
+ /// Print this template argument to the given output stream.
+ arg.print(policy, outStream, /*IncludeType=*/true);
+ };
+ llvm::interleaveComma(args, outStream, printer);
+ outStream << '>';
+ }
+ } else if (auto *typedefNameDecl = recordDecl->getTypedefNameForAnonDecl()) {
+ if (typedefNameDecl->getDeclContext())
+ typedefNameDecl->printQualifiedName(outStream, policy);
+ else
+ typedefNameDecl->printName(outStream);
+ } else {
+ outStream << builder.getUniqueAnonRecordName();
+ }
+
+ if (!suffix.empty())
+ outStream << suffix;
+
+ return builder.getUniqueRecordName(std::string(typeName));
+}
+
+/// Lay out a tagged decl type like struct or union.
+mlir::Type CIRGenTypes::convertRecordDeclType(const clang::RecordDecl *rd) {
+ // TagDecl's are not necessarily unique, instead use the (clang) type
+ // connected to the decl.
+ const Type *key = astContext.getTagDeclType(rd).getTypePtr();
+ cir::StructType entry = recordDeclTypes[key];
+
+ // Handle forward decl / incomplete types.
+ if (!entry) {
+ auto name = getRecordTypeName(rd, "");
+ entry = builder.getIncompleteStructTy(name, rd);
+ recordDeclTypes[key] = entry;
+ }
+
+ rd = rd->getDefinition();
+ if (!rd || !rd->isCompleteDefinition() || entry.isComplete())
+ return entry;
+
+ cgm.errorNYI(rd->getSourceRange(), "Complete record type");
+ return entry;
+}
+
mlir::Type CIRGenTypes::convertType(QualType type) {
type = astContext.getCanonicalType(type);
const Type *ty = type.getTypePtr();
+ // Process record types before the type cache lookup.
+ if (const auto *recordType = dyn_cast<RecordType>(type))
+ return convertRecordDeclType(recordType->getDecl());
+
// Has the type already been processed?
TypeCacheTy::iterator tci = typeCache.find(ty);
if (tci != typeCache.end())
@@ -100,9 +170,11 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
mlir::Type resultType = nullptr;
switch (ty->getTypeClass()) {
+ case Type::Record:
+ llvm_unreachable("Should have been handled above");
+
case Type::Builtin: {
switch (cast<BuiltinType>(ty)->getKind()) {
-
// void
case BuiltinType::Void:
resultType = cgm.VoidTy;
@@ -236,7 +308,8 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
}
default:
- cgm.errorNYI(SourceLocation(), "processing of type", type);
+ cgm.errorNYI(SourceLocation(), "processing of type",
+ type->getTypeClassName());
resultType = cgm.SInt32Ty;
break;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h
index 60661ba0a3beb..98eb43b7b169e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -45,11 +45,13 @@ class CIRGenTypes {
clang::ASTContext &astContext;
CIRGenBuilderTy &builder;
+ /// Contains the CIR type for any converted RecordDecl
+ llvm::DenseMap<const clang::Type *, cir::StructType> recordDeclTypes;
+
/// Hold memoized CIRGenFunctionInfo results
llvm::FoldingSet<CIRGenFunctionInfo> functionInfos;
llvm::SmallPtrSet<const CIRGenFunctionInfo *, 4> functionsBeingProcessed;
-
/// Heper for convertType.
mlir::Type convertFunctionTypeInternal(clang::QualType ft);
@@ -72,6 +74,11 @@ class CIRGenTypes {
/// Convert a Clang type into a mlir::Type.
mlir::Type convertType(clang::QualType type);
+ mlir::Type convertRecordDeclType(const clang::RecordDecl *recordDecl);
+
+ std::string getRecordTypeName(const clang::RecordDecl *,
+ llvm::StringRef suffix);
+
/// Convert type T into an mlir::Type. This differs from convertType in that
/// it is used to convert to the memory representation for a type. For
/// example, the scalar representation for bool is i1, but the memory
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 356f7f6244db8..4ff7e1fdbb7c8 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -14,6 +14,7 @@
#include "mlir/IR/DialectImplementation.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/Dialect/IR/CIRTypesDetails.h"
#include "clang/CIR/MissingFeatures.h"
#include "llvm/ADT/TypeSwitch.h"
@@ -53,9 +54,13 @@ Type CIRDialect::parseType(DialectAsmParser &parser) const {
if (parseResult.has_value())
return genType;
- // TODO(CIR) Attempt to parse as a raw C++ type.
- parser.emitError(typeLoc) << "unknown CIR type: " << mnemonic;
- return Type();
+ // Type is not tablegen'd: try to parse as a raw C++ type.
+ return StringSwitch<function_ref<Type()>>(mnemonic)
+ .Case("struct", [&] { return StructType::parse(parser); })
+ .Default([&] {
+ parser.emitError(typeLoc) << "unknown CIR type: " << mnemonic;
+ return Type();
+ })();
}
void CIRDialect::printType(Type type, DialectAsmPrinter &os) const {
@@ -67,6 +72,171 @@ void CIRDialect::printType(Type type, DialectAsmPrinter &os) const {
llvm::report_fatal_error("printer is missing a handler for this type");
}
+//===----------------------------------------------------------------------===//
+// StructType Definitions
+//===----------------------------------------------------------------------===//
+
+Type StructType::parse(mlir::AsmParser &parser) {
+ FailureOr<AsmParser::CyclicParseReset> cyclicParseGuard;
+ const auto loc = parser.getCurrentLocation();
+ const auto eLoc = parser.getEncodedSourceLoc(loc);
+ RecordKind kind;
+ auto *context = parser.getContext();
+
+ if (parser.parseLess())
+ return {};
+
+ // TODO(cir): in the future we should probably separate types for different
+ // source language declarations such as cir.class, cir.union, and cir.struct
+ if (parser.parseOptionalKeyword("struct").succeeded())
+ 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 struct type");
+ return {};
+ }
+
+ mlir::StringAttr name;
+ parser.parseOptionalAttribute(name);
+
+ // Is a self reference: ensure referenced type was parsed.
+ if (name && parser.parseOptionalGreater().succeeded()) {
+ auto type = getChecked(eLoc, context, name, kind);
+ if (succeeded(parser.tryStartCyclicParse(type))) {
+ parser.emitError(loc, "invalid self-reference within record");
+ return {};
+ }
+ return type;
+ }
+
+ // Is a named record definition: ensure name has not been parsed yet.
+ if (name) {
+ auto type = getChecked(eLoc, context, name, kind);
+ cyclicParseGuard = parser.tryStartCyclicParse(type);
+ if (failed(cyclicParseGuard)) {
+ parser.emitError(loc, "record already defined");
+ return {};
+ }
+ }
+
+ // Parse record members or lack thereof.
+ bool incomplete = true;
+ llvm::SmallVector<mlir::Type> members;
+ if (parser.parseOptionalKeyword("incomplete").failed()) {
+ incomplete = false;
+ const auto delimiter = AsmParser::Delimiter::Braces;
+ const auto parseElementFn = [&parser, &members]() {
+ return parser.parseType(members.emplace_back());
+ };
+ if (parser.parseCommaSeparatedList(delimiter, parseElementFn).failed())
+ return {};
+ }
+
+ if (parser.parseGreater())
+ return {};
+
+ // Try to create the proper record type.
+ ArrayRef<mlir::Type> membersRef(members); // Needed for template deduction.
+ mlir::Type type = {};
+ if (name && incomplete) { // Identified & incomplete
+ type = getChecked(eLoc, context, name, kind);
+ } else if (!incomplete) { // complete
+ parser.emitError(loc, "complete structs are not yet supported");
+ } else { // anonymous & incomplete
+ parser.emitError(loc, "anonymous structs must be complete");
+ return {};
+ }
+
+ return type;
+}
+
+void StructType::print(mlir::AsmPrinter &printer) const {
+ FailureOr<AsmPrinter::CyclicPrintReset> cyclicPrintGuard;
+ printer << '<';
+
+ switch (getKind()) {
+ case RecordKind::Struct:
+ printer << "struct ";
+ break;
+ case RecordKind::Union:
+ printer << "union ";
+ break;
+ case RecordKind::Class:
+ printer << "class ";
+ break;
+ }
+
+ if (getName())
+ printer << getName();
+
+ // Current type has already been printed: print as self reference.
+ cyclicPrintGuard = printer.tryStartCyclicPrint(*this);
+ if (failed(cyclicPrintGuard)) {
+ printer << '>';
+ return;
+ }
+
+ // Type not yet printed: continue printing the entire record.
+ printer << ' ';
+
+ if (isIncomplete()) {
+ printer << "incomplete";
+ } else {
+ printer << "{";
+ llvm::interleaveComma(getMembers(), printer);
+ printer << "}";
+ }
+
+ printer << '>';
+}
+
+mlir::LogicalResult
+StructType::verify(function_ref<mlir::InFlightDiagnostic()> emitError,
+ llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
+ bool incomplete, bool packed, bool padded,
+ StructType::RecordKind kind) {
+ if (name && name.getValue().empty()) {
+ emitError() << "identified structs cannot have an empty name";
+ return mlir::failure();
+ }
+ return mlir::success();
+}
+
+::llvm::ArrayRef<mlir::Type> StructType::getMembers() const {
+ return getImpl()->members;
+}
+
+bool StructType::isIncomplete() const { return getImpl()->incomplete; }
+
+mlir::StringAttr StructType::getName() const { return getImpl()->name; }
+
+bool StructType::getIncomplete() const { return getImpl()->incomplete; }
+
+cir::StructType::RecordKind StructType::getKind() const {
+ return getImpl()->kind;
+}
+
+//===----------------------------------------------------------------------===//
+// Data Layout information for types
+//===----------------------------------------------------------------------===//
+
+llvm::TypeSize
+StructType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout,
+ ::mlir::DataLayoutEntryListRef params) const {
+ assert(!cir::MissingFeatures::structTypeLayoutInfo());
+ return llvm::TypeSize::getFixed(8);
+}
+
+uint64_t
+StructType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
+ ::mlir::DataLayoutEntryListRef params) const {
+ assert(!cir::MissingFeatures::structTypeLayoutInfo());
+ return 4;
+}
+
//===----------------------------------------------------------------------===//
// IntType Definitions
//===----------------------------------------------------------------------===//
diff --git a/clang/test/CIR/CodeGen/struct.c b/clang/test/CIR/CodeGen/struct.c
new file mode 100644
index 0000000000000..4a57ee18061a5
--- /dev/null
+++ b/clang/test/CIR/CodeGen/struct.c
@@ -0,0 +1,13 @@
+// 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
+
+// Declaration with an incomplete struct type.
+struct U *p;
+
+// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "U" incomplete>>
+// LLVM: @p = dso_local global ptr null
+// OGCG: @p = global ptr null, align 8
diff --git a/clang/test/CIR/IR/struct.cir b/clang/test/CIR/IR/struct.cir
new file mode 100644
index 0000000000000..bfe98ba19a66e
--- /dev/null
+++ b/clang/test/CIR/IR/struct.cir
@@ -0,0 +1,9 @@
+// RUN: cir-opt %s | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+
+module {
+ cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "U" incomplete>>
+}
+
+// CHECK: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "U" incomplete>>
>From 72b4b2d4e3b58f81c05920937a9045f158008e72 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 10 Apr 2025 15:36:24 -0700
Subject: [PATCH 2/5] Address review feedback
---
clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 7 ++++++-
clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 10 ++--------
clang/test/CIR/CodeGen/struct.c | 9 +++++++++
clang/test/CIR/IR/struct.cir | 2 --
4 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 58797c5dd253a..a17bca19a84cd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -260,6 +260,9 @@ void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d,
void CIRGenFunction::emitDecl(const Decl &d) {
switch (d.getKind()) {
+ case Decl::Record: // struct/union/class X;
+ assert(!cir::MissingFeatures::generateDebugInfo());
+ return;
case Decl::Var: {
const VarDecl &vd = cast<VarDecl>(d);
assert(vd.isLocalVarDecl() &&
@@ -274,7 +277,9 @@ void CIRGenFunction::emitDecl(const Decl &d) {
emitOpenACCRoutine(cast<OpenACCRoutineDecl>(d));
return;
default:
- cgm.errorNYI(d.getSourceRange(), "emitDecl: unhandled decl type");
+ cgm.errorNYI(d.getSourceRange(),
+ std::string("emitDecl: unhandled decl type: ") +
+ d.getDeclKindName());
}
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 6829b1000598a..8587cdb2e657e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -97,10 +97,7 @@ std::string CIRGenTypes::getRecordTypeName(const clang::RecordDecl *recordDecl,
policy.SuppressInlineNamespace = false;
if (recordDecl->getIdentifier()) {
- if (recordDecl->getDeclContext())
- recordDecl->printQualifiedName(outStream, policy);
- else
- recordDecl->printName(outStream, policy);
+ recordDecl->printQualifiedName(outStream, policy);
// Ensure each template specialization has a unique name.
if (auto *templateSpecialization =
@@ -116,10 +113,7 @@ std::string CIRGenTypes::getRecordTypeName(const clang::RecordDecl *recordDecl,
outStream << '>';
}
} else if (auto *typedefNameDecl = recordDecl->getTypedefNameForAnonDecl()) {
- if (typedefNameDecl->getDeclContext())
- typedefNameDecl->printQualifiedName(outStream, policy);
- else
- typedefNameDecl->printName(outStream);
+ typedefNameDecl->printQualifiedName(outStream, policy);
} else {
outStream << builder.getUniqueAnonRecordName();
}
diff --git a/clang/test/CIR/CodeGen/struct.c b/clang/test/CIR/CodeGen/struct.c
index 4a57ee18061a5..f0ad05ec23ad8 100644
--- a/clang/test/CIR/CodeGen/struct.c
+++ b/clang/test/CIR/CodeGen/struct.c
@@ -11,3 +11,12 @@ struct U *p;
// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "U" incomplete>>
// LLVM: @p = dso_local global ptr null
// OGCG: @p = global ptr null, align 8
+
+void f(void) {
+ struct U2 *p;
+}
+
+// CIR: cir.func @f()
+// CIR-NEXT: cir.alloca !cir.ptr<!cir.struct<struct "U2" incomplete>>,
+// CIR-SAME: !cir.ptr<!cir.ptr<!cir.struct<struct "U2" incomplete>>>, ["p"]
+// CIR-NEXT: cir.return
diff --git a/clang/test/CIR/IR/struct.cir b/clang/test/CIR/IR/struct.cir
index bfe98ba19a66e..7779132ecee96 100644
--- a/clang/test/CIR/IR/struct.cir
+++ b/clang/test/CIR/IR/struct.cir
@@ -1,7 +1,5 @@
// RUN: cir-opt %s | FileCheck %s
-!s32i = !cir.int<s, 32>
-
module {
cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "U" incomplete>>
}
>From 2223527d38f71aac9e4a41714921405ff658e398 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 11 Apr 2025 12:39:47 -0700
Subject: [PATCH 3/5] Address review feedback
---
.../clang/CIR/Dialect/IR/CIRTypesDetails.h | 10 ++++---
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 6 ++--
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 1 +
clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 30 +++++++------------
clang/test/CIR/CodeGen/struct.c | 24 ++++++++++-----
clang/test/CIR/IR/struct.cir | 6 ++--
6 files changed, 41 insertions(+), 36 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
index 91bfb814a609c..f96dc53c45056 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
@@ -53,7 +53,9 @@ struct StructTypeStorage : public mlir::TypeStorage {
bool incomplete, bool packed, bool padded,
StructType::RecordKind kind)
: members(members), name(name), incomplete(incomplete), packed(packed),
- padded(padded), kind(kind) {}
+ padded(padded), kind(kind) {
+ assert(name || !incomplete && "Incomplete structs must have a name");
+ }
KeyTy getAsKey() const {
return KeyTy(members, name, incomplete, packed, padded, kind);
@@ -62,9 +64,9 @@ struct StructTypeStorage : public mlir::TypeStorage {
bool operator==(const KeyTy &key) const {
if (name)
return (name == key.name) && (kind == key.kind);
- return (members == key.members) && (name == key.name) &&
- (incomplete == key.incomplete) && (packed == key.packed) &&
- (padded == key.padded) && (kind == key.kind);
+ return std::tie(members, name, incomplete, packed, padded, kind) ==
+ std::tie(key.members, key.name, key.incomplete, key.packed,
+ key.padded, key.kind);
}
static llvm::hash_code hashKey(const KeyTy &key) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 08c2a60b50e29..78b5239066916 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -62,11 +62,13 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
case clang::TagTypeKind::Interface:
llvm_unreachable("interface records are NYI");
case clang::TagTypeKind::Enum:
- llvm_unreachable("enum records are NYI");
+ llvm_unreachable("enums are not records");
}
}
- /// Get an incomplete CIR struct type.
+ /// Get an incomplete CIR struct type. If we have a complete record
+ /// declaration, we may create an incomplete type and then add the
+ /// members, so \p rd here may be complete.
cir::StructType getIncompleteStructTy(llvm::StringRef name,
const clang::RecordDecl *rd) {
const mlir::StringAttr nameAttr = getStringAttr(name);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index e4919ed76b450..ddcdc0d269708 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -591,6 +591,7 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) {
break;
case Decl::Record:
+ case Decl::CXXRecord:
assert(!cir::MissingFeatures::generateDebugInfo());
break;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 8587cdb2e657e..9856da177fde7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -95,28 +95,16 @@ std::string CIRGenTypes::getRecordTypeName(const clang::RecordDecl *recordDecl,
PrintingPolicy policy = recordDecl->getASTContext().getPrintingPolicy();
policy.SuppressInlineNamespace = false;
+ policy.AlwaysIncludeTypeForTemplateArgument = true;
+ policy.PrintCanonicalTypes = true;
+ policy.SuppressTagKeyword = true;
- if (recordDecl->getIdentifier()) {
- recordDecl->printQualifiedName(outStream, policy);
-
- // Ensure each template specialization has a unique name.
- if (auto *templateSpecialization =
- llvm::dyn_cast<ClassTemplateSpecializationDecl>(recordDecl)) {
- outStream << '<';
- const ArrayRef<TemplateArgument> args =
- templateSpecialization->getTemplateArgs().asArray();
- const auto printer = [&policy, &outStream](const TemplateArgument &arg) {
- /// Print this template argument to the given output stream.
- arg.print(policy, outStream, /*IncludeType=*/true);
- };
- llvm::interleaveComma(args, outStream, printer);
- outStream << '>';
- }
- } else if (auto *typedefNameDecl = recordDecl->getTypedefNameForAnonDecl()) {
+ if (recordDecl->getIdentifier())
+ astContext.getRecordType(recordDecl).print(outStream, policy);
+ else if (auto *typedefNameDecl = recordDecl->getTypedefNameForAnonDecl())
typedefNameDecl->printQualifiedName(outStream, policy);
- } else {
+ else
outStream << builder.getUniqueAnonRecordName();
- }
if (!suffix.empty())
outStream << suffix;
@@ -131,7 +119,9 @@ mlir::Type CIRGenTypes::convertRecordDeclType(const clang::RecordDecl *rd) {
const Type *key = astContext.getTagDeclType(rd).getTypePtr();
cir::StructType entry = recordDeclTypes[key];
- // Handle forward decl / incomplete types.
+ // If we don't have an entry for this record yet, create one.
+ // We create an incomplete type initially. If `rd` is complete, we will
+ // add the members below.
if (!entry) {
auto name = getRecordTypeName(rd, "");
entry = builder.getIncompleteStructTy(name, rd);
diff --git a/clang/test/CIR/CodeGen/struct.c b/clang/test/CIR/CodeGen/struct.c
index f0ad05ec23ad8..543f6f9e1ddf5 100644
--- a/clang/test/CIR/CodeGen/struct.c
+++ b/clang/test/CIR/CodeGen/struct.c
@@ -5,18 +5,26 @@
// 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
-// Declaration with an incomplete struct type.
-struct U *p;
+struct IncompleteS *p;
-// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "U" incomplete>>
+// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "IncompleteS" incomplete>>
// LLVM: @p = dso_local global ptr null
// OGCG: @p = global ptr null, align 8
void f(void) {
- struct U2 *p;
+ struct IncompleteS *p;
}
-// CIR: cir.func @f()
-// CIR-NEXT: cir.alloca !cir.ptr<!cir.struct<struct "U2" incomplete>>,
-// CIR-SAME: !cir.ptr<!cir.ptr<!cir.struct<struct "U2" incomplete>>>, ["p"]
-// CIR-NEXT: cir.return
+// CIR: cir.func @f()
+// CIR-NEXT: cir.alloca !cir.ptr<!cir.struct<struct "IncompleteS" incomplete>>,
+// CIR-SAME: !cir.ptr<!cir.ptr<!cir.struct<struct "IncompleteS" incomplete>>>, ["p"]
+// CIR-NEXT: cir.return
+
+// LLVM: define void @f()
+// LLVM-NEXT: %[[P:.*]] = alloca ptr, i64 1, align 8
+// LLVM-NEXT: ret void
+
+// OGCG: define{{.*}} void @f()
+// OGCG-NEXT: entry:
+// OGCG-NEXT: %[[P:.*]] = alloca ptr, align 8
+// OGCG-NEXT: ret void
diff --git a/clang/test/CIR/IR/struct.cir b/clang/test/CIR/IR/struct.cir
index 7779132ecee96..58d2eefbae8cc 100644
--- a/clang/test/CIR/IR/struct.cir
+++ b/clang/test/CIR/IR/struct.cir
@@ -1,7 +1,9 @@
// RUN: cir-opt %s | FileCheck %s
module {
- cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "U" incomplete>>
+ cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "S" incomplete>>
+ cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!cir.struct<union "U" incomplete>>
}
-// CHECK: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "U" incomplete>>
+// CHECK: cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "S" incomplete>>
+// CHECK: cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!cir.struct<union "U" incomplete>>
>From 5e85698e2df208b42e276477cd12535b41100137 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 11 Apr 2025 14:08:13 -0700
Subject: [PATCH 4/5] Add test cases
---
clang/test/CIR/CodeGen/struct.cpp | 31 +++++++++++++++++++++++++++++++
clang/test/CIR/CodeGen/union.c | 30 ++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+)
create mode 100644 clang/test/CIR/CodeGen/struct.cpp
create mode 100644 clang/test/CIR/CodeGen/union.c
diff --git a/clang/test/CIR/CodeGen/struct.cpp b/clang/test/CIR/CodeGen/struct.cpp
new file mode 100644
index 0000000000000..5eda476363903
--- /dev/null
+++ b/clang/test/CIR/CodeGen/struct.cpp
@@ -0,0 +1,31 @@
+// 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
+
+struct IncompleteS;
+IncompleteS *p;
+
+// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "IncompleteS" incomplete>>
+// LLVM: @p = dso_local global ptr null
+// OGCG: @p = global ptr null, align 8
+
+void f(void) {
+ IncompleteS *p;
+}
+
+// CIR: cir.func @f()
+// CIR-NEXT: cir.alloca !cir.ptr<!cir.struct<struct "IncompleteS" incomplete>>,
+// CIR-SAME: !cir.ptr<!cir.ptr<!cir.struct<struct "IncompleteS" incomplete>>>, ["p"]
+// CIR-NEXT: cir.return
+
+// LLVM: define void @f()
+// LLVM-NEXT: %[[P:.*]] = alloca ptr, i64 1, align 8
+// LLVM-NEXT: ret void
+
+// OGCG: define{{.*}} void @_Z1fv()
+// OGCG-NEXT: entry:
+// OGCG-NEXT: %[[P:.*]] = alloca ptr, align 8
+// OGCG-NEXT: ret void
diff --git a/clang/test/CIR/CodeGen/union.c b/clang/test/CIR/CodeGen/union.c
new file mode 100644
index 0000000000000..e3599e3c1ffa3
--- /dev/null
+++ b/clang/test/CIR/CodeGen/union.c
@@ -0,0 +1,30 @@
+// 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
+
+union IncompleteU *p;
+
+// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<union "IncompleteU" incomplete>>
+// LLVM: @p = dso_local global ptr null
+// OGCG: @p = global ptr null, align 8
+
+void f(void) {
+ union IncompleteU *p;
+}
+
+// CIR: cir.func @f()
+// CIR-NEXT: cir.alloca !cir.ptr<!cir.struct<union "IncompleteU" incomplete>>,
+// CIR-SAME: !cir.ptr<!cir.ptr<!cir.struct<union "IncompleteU" incomplete>>>, ["p"]
+// CIR-NEXT: cir.return
+
+// LLVM: define void @f()
+// LLVM-NEXT: %[[P:.*]] = alloca ptr, i64 1, align 8
+// LLVM-NEXT: ret void
+
+// OGCG: define{{.*}} void @f()
+// OGCG-NEXT: entry:
+// OGCG-NEXT: %[[P:.*]] = alloca ptr, align 8
+// OGCG-NEXT: ret void
>From ca8c49f223057be24c2b38b11e1f28518541ed3b Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 11 Apr 2025 14:34:57 -0700
Subject: [PATCH 5/5] Change `cir.struct` to `cir.record`
---
clang/include/clang/CIR/Dialect/IR/CIRTypes.h | 2 +-
.../include/clang/CIR/Dialect/IR/CIRTypes.td | 64 +++++++++----------
.../clang/CIR/Dialect/IR/CIRTypesDetails.h | 36 +++++------
clang/include/clang/CIR/MissingFeatures.h | 4 +-
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 15 ++---
clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 4 +-
clang/lib/CIR/CodeGen/CIRGenTypes.h | 2 +-
clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 47 ++++++--------
clang/test/CIR/CodeGen/struct.c | 6 +-
clang/test/CIR/CodeGen/struct.cpp | 6 +-
clang/test/CIR/CodeGen/union.c | 6 +-
clang/test/CIR/IR/struct.cir | 8 +--
12 files changed, 95 insertions(+), 105 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
index d2c407b2fd686..074b8d07e0e80 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
@@ -21,7 +21,7 @@
namespace cir {
namespace detail {
-struct StructTypeStorage;
+struct RecordTypeStorage;
} // namespace detail
bool isAnyFloatingPointType(mlir::Type t);
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index bcdaf54bbd84f..c60af47f09def 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -401,52 +401,51 @@ def VoidPtr : Type<
}
//===----------------------------------------------------------------------===//
-// StructType
+// RecordType
//
// The base type for all RecordDecls.
//===----------------------------------------------------------------------===//
-def CIR_StructType : CIR_Type<"Struct", "struct",
+def CIR_RecordType : CIR_Type<"Record", "record",
[
DeclareTypeInterfaceMethods<DataLayoutTypeInterface>,
MutableType,
]> {
- let summary = "CIR struct type";
+ let summary = "CIR record type";
let description = [{
- Each unique clang::RecordDecl is mapped to a `cir.struct` and any object in
- C/C++ that has a struct type will have a `cir.struct` in CIR.
+ Each unique clang::RecordDecl is mapped to a `cir.record` and any object in
+ C/C++ that has a struct or class type will have a `cir.record` in CIR.
There are three possible formats for this type:
- - Identified and complete structs: unique name and a known body.
- - Identified and incomplete structs: unique name and unknown body.
- - Anonymous structs: no name and a known body.
+ - Identified and complete records: unique name and a known body.
+ - Identified and incomplete records: unique name and unknown body.
+ - Anonymous records: no name and a known body.
- Identified structs are uniqued by their name, and anonymous structs are
- uniqued by their body. This means that two anonymous structs with the same
- body will be the same type, and two identified structs with the same name
- will be the same type. Attempting to build a struct with an existing name,
+ Identified records are uniqued by their name, and anonymous records are
+ uniqued by their body. This means that two anonymous records with the same
+ body will be the same type, and two identified records with the same name
+ will be the same type. Attempting to build a record with an existing name,
but a different body will result in an error.
A few examples:
```mlir
- !complete = !cir.struct<struct "complete" {!cir.int<u, 8>}>
- !incomplete = !cir.struct<struct "incomplete" incomplete>
- !anonymous = !cir.struct<struct {!cir.int<u, 8>}>
+ !complete = !cir.record<struct "complete" {!cir.int<u, 8>}>
+ !incomplete = !cir.record<struct "incomplete" incomplete>
+ !anonymous = !cir.record<struct {!cir.int<u, 8>}>
```
- Incomplete structs are mutable, meaning they can be later completed with a
+ Incomplete records are mutable, meaning they can be later completed with a
body automatically updating in place every type in the code that uses the
- incomplete struct. Mutability allows for recursive types to be represented,
- meaning the struct can have members that refer to itself. This is useful for
+ incomplete record. Mutability allows for recursive types to be represented,
+ meaning the record can have members that refer to itself. This is useful for
representing recursive records and is implemented through a special syntax.
- In the example below, the `Node` struct has a member that is a pointer to a
- `Node` struct:
+ In the example below, the `Node` record has a member that is a pointer to a
+ `Node` record:
```mlir
- !struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct
- "Node">>}>
+ !s = !cir.record<struct "Node" {!cir.ptr<!cir.record<struct "Node">>}>
```
}];
@@ -456,18 +455,18 @@ def CIR_StructType : CIR_Type<"Struct", "struct",
"bool":$incomplete,
"bool":$packed,
"bool":$padded,
- "StructType::RecordKind":$kind
+ "RecordType::RecordKind":$kind
);
// StorageClass is defined in C++ for mutability.
- let storageClass = "StructTypeStorage";
+ let storageClass = "RecordTypeStorage";
let genStorageClass = 0;
let skipDefaultBuilders = 1;
let genVerifyDecl = 1;
let builders = [
- // Create an identified and incomplete struct type.
+ // Create an identified and incomplete record type.
TypeBuilder<(ins
"mlir::StringAttr":$name,
"RecordKind":$kind
@@ -480,9 +479,8 @@ def CIR_StructType : CIR_Type<"Struct", "struct",
let extraClassDeclaration = [{
using Base::verifyInvariants;
- enum RecordKind : uint32_t { Class, Union, Struct };
+ enum RecordKind : uint32_t { 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(); };
@@ -491,14 +489,12 @@ def CIR_StructType : CIR_Type<"Struct", "struct",
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:
return "struct";
}
- llvm_unreachable("Invalid value for StructType::getKind()");
+ llvm_unreachable("Invalid value for RecordType::getKind()");
}
std::string getPrefixedName() {
return getKindAsStr() + "." + getName().getValue().str();
@@ -508,10 +504,10 @@ def CIR_StructType : CIR_Type<"Struct", "struct",
let hasCustomAssemblyFormat = 1;
}
-// Note CIRStructType is used instead of CIR_StructType
+// Note CIRRecordType is used instead of CIR_RecordType
// because of tablegen conflicts.
-def CIRStructType : Type<
- CPred<"::mlir::isa<::cir::StructType>($_self)">, "CIR struct type">;
+def CIRRecordType : Type<
+ CPred<"::mlir::isa<::cir::RecordType>($_self)">, "CIR record type">;
//===----------------------------------------------------------------------===//
// Global type constraints
@@ -519,7 +515,7 @@ def CIRStructType : Type<
def CIR_AnyType : AnyTypeOf<[
CIR_VoidType, CIR_BoolType, CIR_ArrayType, CIR_IntType, CIR_AnyFloat,
- CIR_PointerType, CIR_FuncType, CIR_StructType
+ CIR_PointerType, CIR_FuncType, CIR_RecordType
]>;
#endif // MLIR_CIR_DIALECT_CIR_TYPES
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
index f96dc53c45056..00500c763a464 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
@@ -22,22 +22,22 @@ namespace cir {
namespace detail {
//===----------------------------------------------------------------------===//
-// CIR StructTypeStorage
+// CIR RecordTypeStorage
//===----------------------------------------------------------------------===//
/// Type storage for CIR record types.
-struct StructTypeStorage : public mlir::TypeStorage {
+struct RecordTypeStorage : public mlir::TypeStorage {
struct KeyTy {
llvm::ArrayRef<mlir::Type> members;
mlir::StringAttr name;
bool incomplete;
bool packed;
bool padded;
- StructType::RecordKind kind;
+ RecordType::RecordKind kind;
KeyTy(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
bool incomplete, bool packed, bool padded,
- StructType::RecordKind kind)
+ RecordType::RecordKind kind)
: members(members), name(name), incomplete(incomplete), packed(packed),
padded(padded), kind(kind) {}
};
@@ -47,14 +47,14 @@ struct StructTypeStorage : public mlir::TypeStorage {
bool incomplete;
bool packed;
bool padded;
- StructType::RecordKind kind;
+ RecordType::RecordKind kind;
- StructTypeStorage(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
+ RecordTypeStorage(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
bool incomplete, bool packed, bool padded,
- StructType::RecordKind kind)
+ RecordType::RecordKind kind)
: members(members), name(name), incomplete(incomplete), packed(packed),
padded(padded), kind(kind) {
- assert(name || !incomplete && "Incomplete structs must have a name");
+ assert(name || !incomplete && "Incomplete records must have a name");
}
KeyTy getAsKey() const {
@@ -76,33 +76,33 @@ struct StructTypeStorage : public mlir::TypeStorage {
key.padded, key.kind);
}
- static StructTypeStorage *construct(mlir::TypeStorageAllocator &allocator,
+ static RecordTypeStorage *construct(mlir::TypeStorageAllocator &allocator,
const KeyTy &key) {
- return new (allocator.allocate<StructTypeStorage>())
- StructTypeStorage(allocator.copyInto(key.members), key.name,
+ return new (allocator.allocate<RecordTypeStorage>())
+ RecordTypeStorage(allocator.copyInto(key.members), key.name,
key.incomplete, key.packed, key.padded, key.kind);
}
- /// Mutates the members and attributes an identified struct.
+ /// Mutates the members and attributes an identified record.
///
/// Once a record is mutated, it is marked as complete, preventing further
- /// mutations. Anonymous structs are always complete and cannot be mutated.
- /// This method does not fail if a mutation of a complete struct does not
- /// change the struct.
+ /// mutations. Anonymous records are always complete and cannot be mutated.
+ /// This method does not fail if a mutation of a complete record does not
+ /// change the record.
llvm::LogicalResult mutate(mlir::TypeStorageAllocator &allocator,
llvm::ArrayRef<mlir::Type> members, bool packed,
bool padded) {
- // Anonymous structs cannot mutate.
+ // Anonymous records cannot mutate.
if (!name)
return llvm::failure();
- // Mutation of complete structs are allowed if they change nothing.
+ // Mutation of complete records are allowed if they change nothing.
if (!incomplete)
return mlir::success((this->members == members) &&
(this->packed == packed) &&
(this->padded == padded));
- // Mutate incomplete struct.
+ // Mutate incomplete record.
this->members = allocator.copyInto(members);
this->packed = packed;
this->padded = padded;
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index be6919557f7e1..6f2fd2cb2b3ad 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -101,8 +101,8 @@ struct MissingFeatures {
static bool mayHaveIntegerOverflow() { return false; }
static bool shouldReverseUnaryCondOnBoolExpr() { return false; }
- // StructType
- static bool structTypeLayoutInfo() { return false; }
+ // RecordType
+ static bool recordTypeLayoutInfo() { return false; }
// Misc
static bool cxxABI() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 78b5239066916..d98416734b56e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -51,14 +51,13 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
}
/// Get a CIR record kind from a AST declaration tag.
- cir::StructType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
+ cir::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
switch (kind) {
+ case clang::TagTypeKind::Class:
case clang::TagTypeKind::Struct:
- return cir::StructType::Struct;
+ return cir::RecordType::Struct;
case clang::TagTypeKind::Union:
- return cir::StructType::Union;
- case clang::TagTypeKind::Class:
- return cir::StructType::Class;
+ return cir::RecordType::Union;
case clang::TagTypeKind::Interface:
llvm_unreachable("interface records are NYI");
case clang::TagTypeKind::Enum:
@@ -69,13 +68,13 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
/// Get an incomplete CIR struct type. If we have a complete record
/// declaration, we may create an incomplete type and then add the
/// members, so \p rd here may be complete.
- cir::StructType getIncompleteStructTy(llvm::StringRef name,
+ cir::RecordType getIncompleteRecordTy(llvm::StringRef name,
const clang::RecordDecl *rd) {
const mlir::StringAttr nameAttr = getStringAttr(name);
- cir::StructType::RecordKind kind = cir::StructType::RecordKind::Struct;
+ cir::RecordType::RecordKind kind = cir::RecordType::RecordKind::Struct;
if (rd)
kind = getRecordKind(rd->getTagKind());
- return getType<cir::StructType>(nameAttr, kind);
+ return getType<cir::RecordType>(nameAttr, kind);
}
bool isSized(mlir::Type ty) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 9856da177fde7..26350628704b7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -117,14 +117,14 @@ mlir::Type CIRGenTypes::convertRecordDeclType(const clang::RecordDecl *rd) {
// TagDecl's are not necessarily unique, instead use the (clang) type
// connected to the decl.
const Type *key = astContext.getTagDeclType(rd).getTypePtr();
- cir::StructType entry = recordDeclTypes[key];
+ cir::RecordType entry = recordDeclTypes[key];
// If we don't have an entry for this record yet, create one.
// We create an incomplete type initially. If `rd` is complete, we will
// add the members below.
if (!entry) {
auto name = getRecordTypeName(rd, "");
- entry = builder.getIncompleteStructTy(name, rd);
+ entry = builder.getIncompleteRecordTy(name, rd);
recordDeclTypes[key] = entry;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h
index 98eb43b7b169e..fd855bf958ccb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -46,7 +46,7 @@ class CIRGenTypes {
CIRGenBuilderTy &builder;
/// Contains the CIR type for any converted RecordDecl
- llvm::DenseMap<const clang::Type *, cir::StructType> recordDeclTypes;
+ llvm::DenseMap<const clang::Type *, cir::RecordType> recordDeclTypes;
/// Hold memoized CIRGenFunctionInfo results
llvm::FoldingSet<CIRGenFunctionInfo> functionInfos;
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 4ff7e1fdbb7c8..9357dc1fcd379 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -56,7 +56,7 @@ Type CIRDialect::parseType(DialectAsmParser &parser) const {
// Type is not tablegen'd: try to parse as a raw C++ type.
return StringSwitch<function_ref<Type()>>(mnemonic)
- .Case("struct", [&] { return StructType::parse(parser); })
+ .Case("record", [&] { return RecordType::parse(parser); })
.Default([&] {
parser.emitError(typeLoc) << "unknown CIR type: " << mnemonic;
return Type();
@@ -73,10 +73,10 @@ void CIRDialect::printType(Type type, DialectAsmPrinter &os) const {
}
//===----------------------------------------------------------------------===//
-// StructType Definitions
+// RecordType Definitions
//===----------------------------------------------------------------------===//
-Type StructType::parse(mlir::AsmParser &parser) {
+Type RecordType::parse(mlir::AsmParser &parser) {
FailureOr<AsmParser::CyclicParseReset> cyclicParseGuard;
const auto loc = parser.getCurrentLocation();
const auto eLoc = parser.getEncodedSourceLoc(loc);
@@ -87,15 +87,13 @@ Type StructType::parse(mlir::AsmParser &parser) {
return {};
// TODO(cir): in the future we should probably separate types for different
- // source language declarations such as cir.class, cir.union, and cir.struct
+ // source language declarations such as cir.record and cir.union
if (parser.parseOptionalKeyword("struct").succeeded())
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 struct type");
+ parser.emitError(loc, "unknown record type");
return {};
}
@@ -144,16 +142,16 @@ Type StructType::parse(mlir::AsmParser &parser) {
if (name && incomplete) { // Identified & incomplete
type = getChecked(eLoc, context, name, kind);
} else if (!incomplete) { // complete
- parser.emitError(loc, "complete structs are not yet supported");
+ parser.emitError(loc, "complete records are not yet supported");
} else { // anonymous & incomplete
- parser.emitError(loc, "anonymous structs must be complete");
+ parser.emitError(loc, "anonymous records must be complete");
return {};
}
return type;
}
-void StructType::print(mlir::AsmPrinter &printer) const {
+void RecordType::print(mlir::AsmPrinter &printer) const {
FailureOr<AsmPrinter::CyclicPrintReset> cyclicPrintGuard;
printer << '<';
@@ -164,9 +162,6 @@ void StructType::print(mlir::AsmPrinter &printer) const {
case RecordKind::Union:
printer << "union ";
break;
- case RecordKind::Class:
- printer << "class ";
- break;
}
if (getName())
@@ -194,28 +189,28 @@ void StructType::print(mlir::AsmPrinter &printer) const {
}
mlir::LogicalResult
-StructType::verify(function_ref<mlir::InFlightDiagnostic()> emitError,
+RecordType::verify(function_ref<mlir::InFlightDiagnostic()> emitError,
llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
bool incomplete, bool packed, bool padded,
- StructType::RecordKind kind) {
+ RecordType::RecordKind kind) {
if (name && name.getValue().empty()) {
- emitError() << "identified structs cannot have an empty name";
+ emitError() << "identified records cannot have an empty name";
return mlir::failure();
}
return mlir::success();
}
-::llvm::ArrayRef<mlir::Type> StructType::getMembers() const {
+::llvm::ArrayRef<mlir::Type> RecordType::getMembers() const {
return getImpl()->members;
}
-bool StructType::isIncomplete() const { return getImpl()->incomplete; }
+bool RecordType::isIncomplete() const { return getImpl()->incomplete; }
-mlir::StringAttr StructType::getName() const { return getImpl()->name; }
+mlir::StringAttr RecordType::getName() const { return getImpl()->name; }
-bool StructType::getIncomplete() const { return getImpl()->incomplete; }
+bool RecordType::getIncomplete() const { return getImpl()->incomplete; }
-cir::StructType::RecordKind StructType::getKind() const {
+cir::RecordType::RecordKind RecordType::getKind() const {
return getImpl()->kind;
}
@@ -224,16 +219,16 @@ cir::StructType::RecordKind StructType::getKind() const {
//===----------------------------------------------------------------------===//
llvm::TypeSize
-StructType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout,
+RecordType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout,
::mlir::DataLayoutEntryListRef params) const {
- assert(!cir::MissingFeatures::structTypeLayoutInfo());
+ assert(!cir::MissingFeatures::recordTypeLayoutInfo());
return llvm::TypeSize::getFixed(8);
}
uint64_t
-StructType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
+RecordType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
::mlir::DataLayoutEntryListRef params) const {
- assert(!cir::MissingFeatures::structTypeLayoutInfo());
+ assert(!cir::MissingFeatures::recordTypeLayoutInfo());
return 4;
}
@@ -602,5 +597,5 @@ void CIRDialect::registerTypes() {
>();
// Register raw C++ types.
- // TODO(CIR) addTypes<StructType>();
+ // TODO(CIR) addTypes<RecordType>();
}
diff --git a/clang/test/CIR/CodeGen/struct.c b/clang/test/CIR/CodeGen/struct.c
index 543f6f9e1ddf5..4edd591e609a2 100644
--- a/clang/test/CIR/CodeGen/struct.c
+++ b/clang/test/CIR/CodeGen/struct.c
@@ -7,7 +7,7 @@
struct IncompleteS *p;
-// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "IncompleteS" incomplete>>
+// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.record<struct "IncompleteS" incomplete>>
// LLVM: @p = dso_local global ptr null
// OGCG: @p = global ptr null, align 8
@@ -16,8 +16,8 @@ void f(void) {
}
// CIR: cir.func @f()
-// CIR-NEXT: cir.alloca !cir.ptr<!cir.struct<struct "IncompleteS" incomplete>>,
-// CIR-SAME: !cir.ptr<!cir.ptr<!cir.struct<struct "IncompleteS" incomplete>>>, ["p"]
+// CIR-NEXT: cir.alloca !cir.ptr<!cir.record<struct "IncompleteS" incomplete>>,
+// CIR-SAME: !cir.ptr<!cir.ptr<!cir.record<struct "IncompleteS" incomplete>>>, ["p"]
// CIR-NEXT: cir.return
// LLVM: define void @f()
diff --git a/clang/test/CIR/CodeGen/struct.cpp b/clang/test/CIR/CodeGen/struct.cpp
index 5eda476363903..6197340a7d36b 100644
--- a/clang/test/CIR/CodeGen/struct.cpp
+++ b/clang/test/CIR/CodeGen/struct.cpp
@@ -8,7 +8,7 @@
struct IncompleteS;
IncompleteS *p;
-// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "IncompleteS" incomplete>>
+// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.record<struct "IncompleteS" incomplete>>
// LLVM: @p = dso_local global ptr null
// OGCG: @p = global ptr null, align 8
@@ -17,8 +17,8 @@ void f(void) {
}
// CIR: cir.func @f()
-// CIR-NEXT: cir.alloca !cir.ptr<!cir.struct<struct "IncompleteS" incomplete>>,
-// CIR-SAME: !cir.ptr<!cir.ptr<!cir.struct<struct "IncompleteS" incomplete>>>, ["p"]
+// CIR-NEXT: cir.alloca !cir.ptr<!cir.record<struct "IncompleteS" incomplete>>,
+// CIR-SAME: !cir.ptr<!cir.ptr<!cir.record<struct "IncompleteS" incomplete>>>, ["p"]
// CIR-NEXT: cir.return
// LLVM: define void @f()
diff --git a/clang/test/CIR/CodeGen/union.c b/clang/test/CIR/CodeGen/union.c
index e3599e3c1ffa3..075d0d2315508 100644
--- a/clang/test/CIR/CodeGen/union.c
+++ b/clang/test/CIR/CodeGen/union.c
@@ -7,7 +7,7 @@
union IncompleteU *p;
-// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<union "IncompleteU" incomplete>>
+// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.record<union "IncompleteU" incomplete>>
// LLVM: @p = dso_local global ptr null
// OGCG: @p = global ptr null, align 8
@@ -16,8 +16,8 @@ void f(void) {
}
// CIR: cir.func @f()
-// CIR-NEXT: cir.alloca !cir.ptr<!cir.struct<union "IncompleteU" incomplete>>,
-// CIR-SAME: !cir.ptr<!cir.ptr<!cir.struct<union "IncompleteU" incomplete>>>, ["p"]
+// CIR-NEXT: cir.alloca !cir.ptr<!cir.record<union "IncompleteU" incomplete>>,
+// CIR-SAME: !cir.ptr<!cir.ptr<!cir.record<union "IncompleteU" incomplete>>>, ["p"]
// CIR-NEXT: cir.return
// LLVM: define void @f()
diff --git a/clang/test/CIR/IR/struct.cir b/clang/test/CIR/IR/struct.cir
index 58d2eefbae8cc..b6ed1d78b354a 100644
--- a/clang/test/CIR/IR/struct.cir
+++ b/clang/test/CIR/IR/struct.cir
@@ -1,9 +1,9 @@
// RUN: cir-opt %s | FileCheck %s
module {
- cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "S" incomplete>>
- cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!cir.struct<union "U" incomplete>>
+ cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!cir.record<struct "S" incomplete>>
+ cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!cir.record<union "U" incomplete>>
}
-// CHECK: cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "S" incomplete>>
-// CHECK: cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!cir.struct<union "U" incomplete>>
+// CHECK: cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!cir.record<struct "S" incomplete>>
+// CHECK: cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!cir.record<union "U" incomplete>>
More information about the cfe-commits
mailing list