llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Andy Kaylor (andykaylor)

<details>
<summary>Changes</summary>

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.

---

Patch is 25.95 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/135105.diff


11 Files Affected:

- (modified) clang/include/clang/CIR/Dialect/IR/CIRTypes.h (+4) 
- (modified) clang/include/clang/CIR/Dialect/IR/CIRTypes.td (+114-1) 
- (added) clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h (+116) 
- (modified) clang/include/clang/CIR/MissingFeatures.h (+3) 
- (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.h (+39) 
- (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+4) 
- (modified) clang/lib/CIR/CodeGen/CIRGenTypes.cpp (+75-2) 
- (modified) clang/lib/CIR/CodeGen/CIRGenTypes.h (+8) 
- (modified) clang/lib/CIR/Dialect/IR/CIRTypes.cpp (+173-3) 
- (added) clang/test/CIR/CodeGen/struct.c (+13) 
- (added) clang/test/CIR/IR/struct.cir (+9) 


``````````diff
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...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/135105
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to