[clang] cd7d2c3 - [CIR] Upstream minimal support for structure types (#135105)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 14 14:40:23 PDT 2025
Author: Andy Kaylor
Date: 2025-04-14T14:40:19-07:00
New Revision: cd7d2c3bf89c9f0e6b7467d9d5ac87ddc829975c
URL: https://github.com/llvm/llvm-project/commit/cd7d2c3bf89c9f0e6b7467d9d5ac87ddc829975c
DIFF: https://github.com/llvm/llvm-project/commit/cd7d2c3bf89c9f0e6b7467d9d5ac87ddc829975c.diff
LOG: [CIR] Upstream minimal support for structure types (#135105)
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.
Added:
clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
clang/test/CIR/CodeGen/struct.c
clang/test/CIR/CodeGen/struct.cpp
clang/test/CIR/CodeGen/union.c
clang/test/CIR/IR/struct.cir
Modified:
clang/include/clang/CIR/Dialect/IR/CIRTypes.h
clang/include/clang/CIR/Dialect/IR/CIRTypes.td
clang/include/clang/CIR/MissingFeatures.h
clang/lib/CIR/CodeGen/CIRGenBuilder.h
clang/lib/CIR/CodeGen/CIRGenDecl.cpp
clang/lib/CIR/CodeGen/CIRGenModule.cpp
clang/lib/CIR/CodeGen/CIRGenTypes.cpp
clang/lib/CIR/CodeGen/CIRGenTypes.h
clang/lib/CIR/Dialect/IR/CIRTypes.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
index 7b0fcbc7cc98f..074b8d07e0e80 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 RecordTypeStorage;
+} // 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..c60af47f09def 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -400,13 +400,122 @@ def VoidPtr : Type<
"cir::VoidType::get($_builder.getContext()))"> {
}
+//===----------------------------------------------------------------------===//
+// RecordType
+//
+// The base type for all RecordDecls.
+//===----------------------------------------------------------------------===//
+
+def CIR_RecordType : CIR_Type<"Record", "record",
+ [
+ DeclareTypeInterfaceMethods<DataLayoutTypeInterface>,
+ MutableType,
+ ]> {
+ let summary = "CIR record type";
+ let description = [{
+ 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 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 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
diff erent body will result in an error.
+
+ A few examples:
+
+ ```mlir
+ !complete = !cir.record<struct "complete" {!cir.int<u, 8>}>
+ !incomplete = !cir.record<struct "incomplete" incomplete>
+ !anonymous = !cir.record<struct {!cir.int<u, 8>}>
+ ```
+
+ 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 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` record has a member that is a pointer to a
+ `Node` record:
+
+ ```mlir
+ !s = !cir.record<struct "Node" {!cir.ptr<!cir.record<struct "Node">>}>
+ ```
+ }];
+
+ let parameters = (ins
+ OptionalArrayRefParameter<"mlir::Type">:$members,
+ OptionalParameter<"mlir::StringAttr">:$name,
+ "bool":$incomplete,
+ "bool":$packed,
+ "bool":$padded,
+ "RecordType::RecordKind":$kind
+ );
+
+ // StorageClass is defined in C++ for mutability.
+ let storageClass = "RecordTypeStorage";
+ let genStorageClass = 0;
+
+ let skipDefaultBuilders = 1;
+ let genVerifyDecl = 1;
+
+ let builders = [
+ // Create an identified and incomplete record 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 { Struct, Union };
+
+ 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::Union:
+ return "union";
+ case RecordKind::Struct:
+ return "struct";
+ }
+ llvm_unreachable("Invalid value for RecordType::getKind()");
+ }
+ std::string getPrefixedName() {
+ return getKindAsStr() + "." + getName().getValue().str();
+ }
+ }];
+
+ let hasCustomAssemblyFormat = 1;
+}
+
+// Note CIRRecordType is used instead of CIR_RecordType
+// because of tablegen conflicts.
+def CIRRecordType : Type<
+ CPred<"::mlir::isa<::cir::RecordType>($_self)">, "CIR record 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_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
new file mode 100644
index 0000000000000..72ead2381ee47
--- /dev/null
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
@@ -0,0 +1,118 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 RecordTypeStorage
+//===----------------------------------------------------------------------===//
+
+/// Type storage for CIR record types.
+struct RecordTypeStorage : public mlir::TypeStorage {
+ struct KeyTy {
+ llvm::ArrayRef<mlir::Type> members;
+ mlir::StringAttr name;
+ bool incomplete;
+ bool packed;
+ bool padded;
+ RecordType::RecordKind kind;
+
+ KeyTy(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
+ bool incomplete, bool packed, bool padded,
+ RecordType::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;
+ RecordType::RecordKind kind;
+
+ RecordTypeStorage(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
+ bool incomplete, bool packed, bool padded,
+ RecordType::RecordKind kind)
+ : members(members), name(name), incomplete(incomplete), packed(packed),
+ padded(padded), kind(kind) {
+ assert(name || !incomplete && "Incomplete records must have a name");
+ }
+
+ 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 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) {
+ 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 RecordTypeStorage *construct(mlir::TypeStorageAllocator &allocator,
+ const KeyTy &key) {
+ 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 record.
+ ///
+ /// Once a record is mutated, it is marked as complete, preventing further
+ /// 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 records cannot mutate.
+ if (!name)
+ return llvm::failure();
+
+ // 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 record.
+ 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..6f2fd2cb2b3ad 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; }
+ // RecordType
+ static bool recordTypeLayoutInfo() { 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..d98416734b56e 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,33 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
llvm_unreachable("Unsupported format for long double");
}
+ /// Get a CIR record kind from a AST declaration tag.
+ cir::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
+ switch (kind) {
+ case clang::TagTypeKind::Class:
+ case clang::TagTypeKind::Struct:
+ return cir::RecordType::Struct;
+ case clang::TagTypeKind::Union:
+ return cir::RecordType::Union;
+ case clang::TagTypeKind::Interface:
+ llvm_unreachable("interface records are NYI");
+ case clang::TagTypeKind::Enum:
+ llvm_unreachable("enums are not records");
+ }
+ }
+
+ /// 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::RecordType getIncompleteRecordTy(llvm::StringRef name,
+ const clang::RecordDecl *rd) {
+ const mlir::StringAttr nameAttr = getStringAttr(name);
+ cir::RecordType::RecordKind kind = cir::RecordType::RecordKind::Struct;
+ if (rd)
+ kind = getRecordKind(rd->getTagKind());
+ return getType<cir::RecordType>(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/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 58797c5dd253a..3d15a70d1d6ac 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/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index fd11523ebba61..ddcdc0d269708 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -589,6 +589,11 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) {
case Decl::OpenACCDeclare:
emitGlobalOpenACCDecl(cast<OpenACCDeclareDecl>(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 a5978a4ad9085..26350628704b7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -86,10 +86,64 @@ 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
diff erent 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;
+ policy.AlwaysIncludeTypeForTemplateArgument = true;
+ policy.PrintCanonicalTypes = true;
+ policy.SuppressTagKeyword = true;
+
+ if (recordDecl->getIdentifier())
+ astContext.getRecordType(recordDecl).print(outStream, policy);
+ else if (auto *typedefNameDecl = recordDecl->getTypedefNameForAnonDecl())
+ typedefNameDecl->printQualifiedName(outStream, policy);
+ 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::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.getIncompleteRecordTy(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 +154,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 +292,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..fd855bf958ccb 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::RecordType> 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
diff ers 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..9b0177f159084 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("record", [&] { return RecordType::parse(parser); })
+ .Default([&] {
+ parser.emitError(typeLoc) << "unknown CIR type: " << mnemonic;
+ return Type();
+ })();
}
void CIRDialect::printType(Type type, DialectAsmPrinter &os) const {
@@ -67,6 +72,166 @@ void CIRDialect::printType(Type type, DialectAsmPrinter &os) const {
llvm::report_fatal_error("printer is missing a handler for this type");
}
+//===----------------------------------------------------------------------===//
+// RecordType Definitions
+//===----------------------------------------------------------------------===//
+
+Type RecordType::parse(mlir::AsmParser &parser) {
+ FailureOr<AsmParser::CyclicParseReset> cyclicParseGuard;
+ const llvm::SMLoc loc = parser.getCurrentLocation();
+ const mlir::Location eLoc = parser.getEncodedSourceLoc(loc);
+ RecordKind kind;
+ mlir::MLIRContext *context = parser.getContext();
+
+ if (parser.parseLess())
+ return {};
+
+ // TODO(cir): in the future we should probably separate types for
diff erent
+ // 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 {
+ parser.emitError(loc, "unknown record type");
+ return {};
+ }
+
+ mlir::StringAttr name;
+ parser.parseOptionalAttribute(name);
+
+ // Is a self reference: ensure referenced type was parsed.
+ if (name && parser.parseOptionalGreater().succeeded()) {
+ RecordType 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) {
+ RecordType 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 records are not yet supported");
+ } else { // anonymous & incomplete
+ parser.emitError(loc, "anonymous records must be complete");
+ return {};
+ }
+
+ return type;
+}
+
+void RecordType::print(mlir::AsmPrinter &printer) const {
+ FailureOr<AsmPrinter::CyclicPrintReset> cyclicPrintGuard;
+ printer << '<';
+
+ switch (getKind()) {
+ case RecordKind::Struct:
+ printer << "struct ";
+ break;
+ case RecordKind::Union:
+ printer << "union ";
+ 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
+RecordType::verify(function_ref<mlir::InFlightDiagnostic()> emitError,
+ llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
+ bool incomplete, bool packed, bool padded,
+ RecordType::RecordKind kind) {
+ if (name && name.getValue().empty()) {
+ emitError() << "identified records cannot have an empty name";
+ return mlir::failure();
+ }
+ return mlir::success();
+}
+
+::llvm::ArrayRef<mlir::Type> RecordType::getMembers() const {
+ return getImpl()->members;
+}
+
+bool RecordType::isIncomplete() const { return getImpl()->incomplete; }
+
+mlir::StringAttr RecordType::getName() const { return getImpl()->name; }
+
+bool RecordType::getIncomplete() const { return getImpl()->incomplete; }
+
+cir::RecordType::RecordKind RecordType::getKind() const {
+ return getImpl()->kind;
+}
+
+//===----------------------------------------------------------------------===//
+// Data Layout information for types
+//===----------------------------------------------------------------------===//
+
+llvm::TypeSize
+RecordType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout,
+ ::mlir::DataLayoutEntryListRef params) const {
+ assert(!cir::MissingFeatures::recordTypeLayoutInfo());
+ return llvm::TypeSize::getFixed(8);
+}
+
+uint64_t
+RecordType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
+ ::mlir::DataLayoutEntryListRef params) const {
+ assert(!cir::MissingFeatures::recordTypeLayoutInfo());
+ return 4;
+}
+
//===----------------------------------------------------------------------===//
// IntType Definitions
//===----------------------------------------------------------------------===//
@@ -432,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
new file mode 100644
index 0000000000000..4edd591e609a2
--- /dev/null
+++ b/clang/test/CIR/CodeGen/struct.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
+
+struct IncompleteS *p;
+
+// 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
+
+void f(void) {
+ struct IncompleteS *p;
+}
+
+// CIR: cir.func @f()
+// 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()
+// 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/CodeGen/struct.cpp b/clang/test/CIR/CodeGen/struct.cpp
new file mode 100644
index 0000000000000..6197340a7d36b
--- /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.record<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.record<struct "IncompleteS" incomplete>>,
+// CIR-SAME: !cir.ptr<!cir.ptr<!cir.record<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..075d0d2315508
--- /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.record<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.record<union "IncompleteU" incomplete>>,
+// CIR-SAME: !cir.ptr<!cir.ptr<!cir.record<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
diff --git a/clang/test/CIR/IR/struct.cir b/clang/test/CIR/IR/struct.cir
new file mode 100644
index 0000000000000..b6ed1d78b354a
--- /dev/null
+++ b/clang/test/CIR/IR/struct.cir
@@ -0,0 +1,9 @@
+// RUN: cir-opt %s | FileCheck %s
+
+module {
+ 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.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