Skip to content

[CIR] Add support for parsing complete records #147403

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 8, 2025

Conversation

andykaylor
Copy link
Contributor

When complete record support was initially added, the parsing support was left incomplete. This change adds the necessary parsing.

When complete record support was initially added, the parsing support was
left incomplete. This change adds the necessary parsing.
@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Jul 7, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 7, 2025

@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: Andy Kaylor (andykaylor)

Changes

When complete record support was initially added, the parsing support was left incomplete. This change adds the necessary parsing.


Full diff: https://github.com/llvm/llvm-project/pull/147403.diff

3 Files Affected:

  • (modified) clang/include/clang/CIR/Dialect/IR/CIRTypes.td (+12-1)
  • (modified) clang/lib/CIR/Dialect/IR/CIRTypes.cpp (+17-2)
  • (modified) clang/test/CIR/IR/struct.cir (+63-8)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 898c26d22f6d1..2e21ce1f44d11 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -552,7 +552,7 @@ def CIR_RecordType : CIR_Type<"Record", "record", [
       "bool":$padded,
       "RecordKind":$kind
     ), [{
-      return $_get($_ctxt, members, name, /*complete=*/true, packed, padded,
+      return $_get($_ctxt, members, name, /*incomplete=*/false, packed, padded,
                    kind);
     }]>,
 
@@ -564,6 +564,17 @@ def CIR_RecordType : CIR_Type<"Record", "record", [
       return $_get($_ctxt, /*members=*/llvm::ArrayRef<Type>{}, name,
                          /*incomplete=*/true, /*packed=*/false,
                          /*padded=*/false, kind);
+    }]>,
+
+    // Create an anonymous record type (always complete).
+    TypeBuilder<(ins
+      "llvm::ArrayRef<mlir::Type>":$members,
+      "bool":$packed,
+      "bool":$padded,
+      "RecordKind":$kind
+    ), [{
+      return $_get($_ctxt, members, mlir::StringAttr{}, /*incomplete=*/false,
+                      packed, padded, kind);
     }]>];
 
   let extraClassDeclaration = [{
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index c6760cf1618cb..40da5e60a93f9 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -96,6 +96,8 @@ Type RecordType::parse(mlir::AsmParser &parser) {
   FailureOr<AsmParser::CyclicParseReset> cyclicParseGuard;
   const llvm::SMLoc loc = parser.getCurrentLocation();
   const mlir::Location eLoc = parser.getEncodedSourceLoc(loc);
+  bool packed = false;
+  bool padded = false;
   RecordKind kind;
   mlir::MLIRContext *context = parser.getContext();
 
@@ -138,6 +140,12 @@ Type RecordType::parse(mlir::AsmParser &parser) {
     }
   }
 
+  if (parser.parseOptionalKeyword("packed").succeeded())
+    packed = true;
+
+  if (parser.parseOptionalKeyword("padded").succeeded())
+    padded = true;
+
   // Parse record members or lack thereof.
   bool incomplete = true;
   llvm::SmallVector<mlir::Type> members;
@@ -159,8 +167,15 @@ Type RecordType::parse(mlir::AsmParser &parser) {
   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 if (!name && !incomplete) { // Anonymous & complete
+    type = getChecked(eLoc, context, membersRef, packed, padded, kind);
+  } else if (!incomplete) { // Identified & complete
+    type = getChecked(eLoc, context, membersRef, name, packed, padded, kind);
+    // If the record has a self-reference, its type already exists in a
+    // incomplete state. In this case, we must complete it.
+    if (mlir::cast<RecordType>(type).isIncomplete())
+      mlir::cast<RecordType>(type).complete(membersRef, packed, padded);
+    assert(!cir::MissingFeatures::astRecordDeclAttr());
   } else { // anonymous & incomplete
     parser.emitError(loc, "anonymous records must be complete");
     return {};
diff --git a/clang/test/CIR/IR/struct.cir b/clang/test/CIR/IR/struct.cir
index 5ff5ff7cd1f69..85f475f643ee5 100644
--- a/clang/test/CIR/IR/struct.cir
+++ b/clang/test/CIR/IR/struct.cir
@@ -1,19 +1,74 @@
 // RUN: cir-opt %s | FileCheck %s
 
+!u8i = !cir.int<u, 8>
+!u16i = !cir.int<u, 16>
+!s32i = !cir.int<s, 32>
+!u32i = !cir.int<u, 32>
+
 !rec_C = !cir.record<class "C" incomplete>
 !rec_S = !cir.record<struct "S" incomplete>
 !rec_U = !cir.record<union "U" incomplete>
 
-// CHECK: !rec_C = !cir.record<class "C" incomplete>
-// CHECK: !rec_S = !cir.record<struct "S" incomplete>
-// CHECK: !rec_U = !cir.record<union "U" incomplete>
+// CHECK-DAG: !rec_C = !cir.record<class "C" incomplete>
+// CHECK-DAG: !rec_S = !cir.record<struct "S" incomplete>
+// CHECK-DAG: !rec_U = !cir.record<union "U" incomplete>
+
+!rec_anon_struct = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 5>}>
+!rec_anon_struct1 = !cir.record<struct {!cir.ptr<!u8i>, !cir.ptr<!u8i>, !cir.ptr<!u8i>}>
+!rec_S1 = !cir.record<struct "S1" {!s32i, !s32i}>
+!rec_Sc = !cir.record<struct "Sc" {!u8i, !u16i, !u32i}>
+
+// CHECK-DAG: !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 5>}>
+// CHECK-DAG: !cir.record<struct {!cir.ptr<!u8i>, !cir.ptr<!u8i>, !cir.ptr<!u8i>}>
+// CHECK-DAG: !rec_S1 = !cir.record<struct "S1" {!s32i, !s32i}>
+// CHECK-DAG: !rec_Sc = !cir.record<struct "Sc" {!u8i, !u16i, !u32i}>
+
+// Packed and padded structs
+!rec_P1 = !cir.record<struct "P1" packed {!s32i, !s32i}>
+!rec_P2 = !cir.record<struct "P2" padded {!u8i, !u16i, !u32i}>
+!rec_P3 = !cir.record<struct "P3" packed padded {!u8i, !u16i, !u32i}>
+
+// CHECK-DAG: !rec_P1 = !cir.record<struct "P1" packed {!s32i, !s32i}>
+// CHECK-DAG: !rec_P2 = !cir.record<struct "P2" padded {!u8i, !u16i, !u32i}>
+// CHECK-DAG: !rec_P3 = !cir.record<struct "P3" packed padded {!u8i, !u16i, !u32i}>
 
-module  {
-    cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!rec_S>
-    cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!rec_U>
-    cir.global external @p3 = #cir.ptr<null> : !cir.ptr<!rec_C>
-}
 
+// Complete a previously incomplete record
+!rec_A = !cir.record<class "A" incomplete>
+!rec_Ac = !cir.record<class "A" {!u8i, !s32i}>
+// CHECK-DAG: !rec_A = !cir.record<class "A" {!u8i, !s32i}>
+
+// Test recursive struct parsing/printing.
+!rec_Node = !cir.record<struct "Node" {!cir.ptr<!cir.record<struct "Node">>}>
+// CHECK-DAG: !cir.record<struct "Node" {!cir.ptr<!cir.record<struct "Node">>}>
+
+module  {
+  cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!rec_S>
+  cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!rec_U>
+  cir.global external @p3 = #cir.ptr<null> : !cir.ptr<!rec_C>
 // CHECK: cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!rec_S>
 // CHECK: cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!rec_U>
 // CHECK: cir.global external @p3 = #cir.ptr<null> : !cir.ptr<!rec_C>
+
+  // Dummy function to use types and force them to be printed.
+  cir.func @useTypes(%arg0: !rec_Node,
+                     %arg1: !rec_anon_struct1,
+                     %arg2: !rec_anon_struct,
+                     %arg3: !rec_S1,
+                     %arg4: !rec_Ac,
+                     %arg5: !rec_P1,
+                     %arg6: !rec_P2,
+                     %arg7: !rec_P3) {
+    cir.return
+  }
+
+  cir.func @structs() {
+    %0 = cir.alloca !cir.ptr<!cir.record<struct "Sc" {!u8i, !u16i, !u32i}>>, !cir.ptr<!cir.ptr<!cir.record<struct "Sc" {!u8i, !u16i, !u32i}>>>, ["sc", init]
+    %1 = cir.alloca !cir.ptr<!cir.record<union "U" incomplete>>, !cir.ptr<!cir.ptr<!cir.record<union "U" incomplete>>>, ["u", init]
+    cir.return
+  }
+
+// CHECK: cir.func @structs() {
+// CHECK:     %0 = cir.alloca !cir.ptr<!rec_Sc>, !cir.ptr<!cir.ptr<!rec_Sc>>, ["sc", init]
+// CHECK:     %1 = cir.alloca !cir.ptr<!rec_U>, !cir.ptr<!cir.ptr<!rec_U>>, ["u", init]
+}

@xlauko
Copy link
Contributor

xlauko commented Jul 8, 2025

Can you add to CIR_RecordType description an example with packed/padded flag?

Copy link
Contributor

@xlauko xlauko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@andykaylor andykaylor merged commit d09984e into llvm:main Jul 8, 2025
9 checks passed
@andykaylor andykaylor deleted the cir-parse-complete branch July 8, 2025 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants