[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