[clang] [CIR] Upstream minimal support for structure types (PR #135105)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 11 14:35:45 PDT 2025
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/135105
>From 80fa219739a4a6424ec55047fecb41f04d3cf844 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 | 8 +
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(+), 6 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 3188429ea3b1b..bc52988dfc750 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -82,6 +82,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 61a747254b3d0..0a9ef5fcd5471 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 d2259a9c41d22..4bd186f68f8c9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -490,6 +490,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 68aee63c2b22d..8644b9a62320a 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 4021206e979e1..a3b7eae6d0767 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -43,6 +43,9 @@ class CIRGenTypes {
clang::ASTContext &astContext;
CIRGenBuilderTy &builder;
+ /// Contains the CIR type for any converted RecordDecl
+ llvm::DenseMap<const clang::Type *, cir::StructType> recordDeclTypes;
+
/// Heper for convertType.
mlir::Type convertFunctionTypeInternal(clang::QualType ft);
@@ -65,6 +68,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 6b688d2679ef8408e6e161e004c41a5ae9fb8be1 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 b8e72f299acb6..8e377ad5caeb0 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,6 +277,8 @@ 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 8644b9a62320a..0745645918d1b 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 476db59f32de5f6b00ee6d04642eca7f55cd0aad 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 0a9ef5fcd5471..a00e623097d54 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 4bd186f68f8c9..21318d22563f3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -492,6 +492,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 0745645918d1b..f229cbfd67077 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 6f460581d0cfab0c3c2ee608a9dc135006d8eb1c 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 badf64fa39d6c1192b91cf8a7ebca4aaa4fac2d3 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 bc52988dfc750..56beaefc9ed05 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -82,8 +82,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 a00e623097d54..bd7a748f634e9 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 f229cbfd67077..e453948f850de 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 a3b7eae6d0767..0c5db94ff126c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.h
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h
@@ -44,7 +44,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;
/// Heper for convertType.
mlir::Type convertFunctionTypeInternal(clang::QualType ft);
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