Skip to content

Conversation

mmha
Copy link
Contributor

@mmha mmha commented Oct 14, 2025

This patch implements visitors for MemberExpr, UnaryDeref, StringLiteral, CompoundLiteralExpr and PredefinedExpr inside aggregate expressions.

@mmha mmha requested a review from erichkeane October 14, 2025 15:17
@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Oct 14, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 14, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clangir

Author: Morris Hafner (mmha)

Changes

This patch implements visitors for MemberExpr, UnaryDeref, StringLiteral, CompoundLiteralExpr and PredefinedExpr inside aggregate expressions.


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

4 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp (+30-17)
  • (modified) clang/lib/CIR/CodeGen/CIRGenValue.h (+2)
  • (added) clang/test/CIR/CodeGen/agg-expr-lvalue.c (+111)
  • (modified) clang/test/CIR/CodeGen/compound_literal.cpp (+27)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 901b937e4e3e7..e59bec4106bc5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -170,23 +170,11 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
   void VisitConstantExpr(ConstantExpr *e) {
     cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitConstantExpr");
   }
-  void VisitMemberExpr(MemberExpr *e) {
-    cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitMemberExpr");
-  }
-  void VisitUnaryDeref(UnaryOperator *e) {
-    cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitUnaryDeref");
-  }
-  void VisitStringLiteral(StringLiteral *e) {
-    cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitStringLiteral");
-  }
-  void VisitCompoundLiteralExpr(CompoundLiteralExpr *e) {
-    cgf.cgm.errorNYI(e->getSourceRange(),
-                     "AggExprEmitter: VisitCompoundLiteralExpr");
-  }
-  void VisitPredefinedExpr(const PredefinedExpr *e) {
-    cgf.cgm.errorNYI(e->getSourceRange(),
-                     "AggExprEmitter: VisitPredefinedExpr");
-  }
+  void VisitMemberExpr(MemberExpr *e) { emitAggLoadOfLValue(e); }
+  void VisitUnaryDeref(UnaryOperator *e) { emitAggLoadOfLValue(e); }
+  void VisitStringLiteral(StringLiteral *e) { emitAggLoadOfLValue(e); }
+  void VisitCompoundLiteralExpr(CompoundLiteralExpr *e);
+  void VisitPredefinedExpr(const PredefinedExpr *e) { emitAggLoadOfLValue(e); }
   void VisitBinaryOperator(const BinaryOperator *e) {
     cgf.cgm.errorNYI(e->getSourceRange(),
                      "AggExprEmitter: VisitBinaryOperator");
@@ -325,6 +313,31 @@ void AggExprEmitter::emitAggLoadOfLValue(const Expr *e) {
   emitFinalDestCopy(e->getType(), lv);
 }
 
+void AggExprEmitter::VisitCompoundLiteralExpr(CompoundLiteralExpr *e) {
+  if (dest.isPotentiallyAliased() && e->getType().isPODType(cgf.getContext())) {
+    // For a POD type, just emit a load of the lvalue + a copy, because our
+    // compound literal might alias the destination.
+    emitAggLoadOfLValue(e);
+    return;
+  }
+
+  AggValueSlot slot = ensureSlot(cgf.getLoc(e->getSourceRange()), e->getType());
+
+  // Block-scope compound literals are destroyed at the end of the enclosing
+  // scope in C.
+  bool destruct =
+      !cgf.getLangOpts().CPlusPlus && !slot.isExternallyDestructed();
+  if (destruct)
+    slot.setExternallyDestructed();
+
+  cgf.emitAggExpr(e->getInitializer(), slot);
+
+  if (destruct)
+    if ([[maybe_unused]] QualType::DestructionKind dtorKind =
+            e->getType().isDestructedType())
+      cgf.cgm.errorNYI(e->getSourceRange(), "compound literal with destructor");
+}
+
 void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy,
                                    QualType arrayQTy, Expr *e,
                                    ArrayRef<Expr *> args, Expr *arrayFiller) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h
index 25b6ecb503a6e..c4ff351afa871 100644
--- a/clang/lib/CIR/CodeGen/CIRGenValue.h
+++ b/clang/lib/CIR/CodeGen/CIRGenValue.h
@@ -390,6 +390,8 @@ class AggValueSlot {
 
   IsZeroed_t isZeroed() const { return IsZeroed_t(zeroedFlag); }
 
+  IsAliased_t isPotentiallyAliased() const { return IsAliased_t(aliasedFlag); }
+
   RValue asRValue() const {
     if (isIgnored())
       return RValue::getIgnored();
diff --git a/clang/test/CIR/CodeGen/agg-expr-lvalue.c b/clang/test/CIR/CodeGen/agg-expr-lvalue.c
new file mode 100644
index 0000000000000..c826f8fa829d0
--- /dev/null
+++ b/clang/test/CIR/CodeGen/agg-expr-lvalue.c
@@ -0,0 +1,111 @@
+// 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 Point {
+  int x, y;
+};
+
+struct Line {
+  struct Point start;
+  struct Point end;
+};
+
+// AggExprEmitter::VisitMemberExpr
+void test_member_in_array(void) {
+  struct Line line = {{1, 2}, {3, 4}};
+  struct Point arr[1] = {line.start};
+}
+
+// CIR-LABEL: cir.func{{.*}} @test_member_in_array
+// CIR:   %[[LINE:.*]] = cir.alloca !rec_Line{{.*}}, ["line", init]
+// CIR:   %[[ARR:.*]] = cir.alloca !cir.array<!rec_Point x 1>{{.*}}, ["arr", init]
+// CIR:   %[[MEMBER:.*]] = cir.get_member %[[LINE]][0] {name = "start"}
+// CIR:   cir.copy
+
+// LLVM-LABEL: define{{.*}} @test_member_in_array
+// LLVM:   %[[LINE:.*]] = alloca %struct.Line
+// LLVM:   %[[ARR:.*]] = alloca [1 x %struct.Point]
+// LLVM:   %[[MEMBER:.*]] = getelementptr{{.*}}%struct.Line{{.*}}%[[LINE]]{{.*}}i32 0, i32 0
+// LLVM:   call void @llvm.memcpy
+
+// OGCG-LABEL: define{{.*}} @test_member_in_array
+// OGCG:   %[[LINE:.*]] = alloca %struct.Line
+// OGCG:   %[[ARR:.*]] = alloca [1 x %struct.Point]
+// OGCG:   %[[MEMBER:.*]] = getelementptr{{.*}}%struct.Line{{.*}}%[[LINE]]{{.*}}i32 0, i32 0
+// OGCG:   call void @llvm.memcpy
+
+// AggExprEmitter::VisitMemberExpr
+void test_member_arrow_in_array(void) {
+  struct Line *line_ptr;
+  struct Point arr[1] = {line_ptr->start};
+}
+
+// CIR-LABEL: cir.func{{.*}} @test_member_arrow_in_array
+// CIR:   %[[PTR:.*]] = cir.alloca !cir.ptr<!rec_Line>{{.*}}, ["line_ptr"]
+// CIR:   %[[ARR:.*]] = cir.alloca !cir.array<!rec_Point x 1>{{.*}}, ["arr", init]
+// CIR:   %[[LOADED:.*]] = cir.load{{.*}}%[[PTR]]
+// CIR:   %[[MEMBER:.*]] = cir.get_member %[[LOADED]][0] {name = "start"}
+// CIR:   cir.copy
+
+// LLVM-LABEL: define{{.*}} @test_member_arrow_in_array
+// LLVM:   %[[PTR:.*]] = alloca ptr
+// LLVM:   %[[ARR:.*]] = alloca [1 x %struct.Point]
+// LLVM:   %[[LOADED:.*]] = load ptr{{.*}}%[[PTR]]
+// LLVM:   %[[MEMBER:.*]] = getelementptr{{.*}}%struct.Line{{.*}}%[[LOADED]]{{.*}}i32 0, i32 0
+// LLVM:   call void @llvm.memcpy
+
+// OGCG-LABEL: define{{.*}} @test_member_arrow_in_array
+// OGCG:   %[[PTR:.*]] = alloca ptr
+// OGCG:   %[[ARR:.*]] = alloca [1 x %struct.Point]
+// OGCG:   %[[LOADED:.*]] = load ptr{{.*}}%[[PTR]]
+// OGCG:   %[[MEMBER:.*]] = getelementptr{{.*}}%struct.Line{{.*}}%[[LOADED]]{{.*}}i32 0, i32 0
+// OGCG:   call void @llvm.memcpy
+
+// AggExprEmitter::VisitUnaryDeref
+void test_deref_in_array(void) {
+  struct Point *ptr;
+  struct Point arr[1] = {*ptr};
+}
+
+// CIR-LABEL: cir.func{{.*}} @test_deref_in_array
+// CIR:   %[[PTR:.*]] = cir.alloca !cir.ptr<!rec_Point>{{.*}}, ["ptr"]
+// CIR:   %[[ARR:.*]] = cir.alloca !cir.array<!rec_Point x 1>{{.*}}, ["arr", init]
+// CIR:   %[[LOADED:.*]] = cir.load{{.*}}%[[PTR]]
+// CIR:   cir.copy
+
+// LLVM-LABEL: define{{.*}} @test_deref_in_array
+// LLVM:   %[[PTR:.*]] = alloca ptr
+// LLVM:   %[[ARR:.*]] = alloca [1 x %struct.Point]
+// LLVM:   %[[LOADED:.*]] = load ptr{{.*}}%[[PTR]]
+// LLVM:   call void @llvm.memcpy
+
+// OGCG-LABEL: define{{.*}} @test_deref_in_array
+// OGCG:   %[[PTR:.*]] = alloca ptr
+// OGCG:   %[[ARR:.*]] = alloca [1 x %struct.Point]
+// OGCG:   %[[LOADED:.*]] = load ptr{{.*}}%[[PTR]]
+// OGCG:   call void @llvm.memcpy
+
+// AggExprEmitter::VisitStringLiteral
+void test_string_array_in_array(void) {
+    char matrix[2][6] = {"hello", "world"};
+}
+  
+// CIR-LABEL: cir.func{{.*}} @test_string_array_in_array
+// CIR:   cir.alloca !cir.array<!cir.array<!s8i x 6> x 2>, {{.*}}, ["matrix", init]
+// CIR:   cir.get_global
+// CIR:   cir.copy
+// CIR:   cir.get_global
+// CIR:   cir.copy
+
+// LLVM-LABEL: define{{.*}} @test_string_array_in_array
+// LLVM:   alloca [2 x [6 x i8]]
+// LLVM:   call void @llvm.memcpy
+// LLVM:   call void @llvm.memcpy
+
+// OGCG-LABEL: define{{.*}} @test_string_array_in_array
+// OGCG:   alloca [2 x [6 x i8]]
+// OGCG:   call void @llvm.memcpy{{.*}}@__const.test_string_array_in_array
diff --git a/clang/test/CIR/CodeGen/compound_literal.cpp b/clang/test/CIR/CodeGen/compound_literal.cpp
index a92af95c62a1b..30a1dc03c449b 100644
--- a/clang/test/CIR/CodeGen/compound_literal.cpp
+++ b/clang/test/CIR/CodeGen/compound_literal.cpp
@@ -97,3 +97,30 @@ void foo3() {
 // OGCG: %[[TMP:.*]] = load <4 x i32>, ptr %[[CL_ADDR]], align 16
 // OGCG: store <4 x i32> %[[TMP]], ptr %[[A_ADDR]], align 16
 
+struct Point {
+  int x, y;
+};
+
+void foo4() {
+  Point p = (Point){5, 10};
+}
+
+// CIR-LABEL: @_Z4foo4v
+// CIR:   %[[P:.*]] = cir.alloca !rec_Point, !cir.ptr<!rec_Point>, ["p", init]
+// CIR:   %[[P_X:.*]] = cir.get_member %[[P]][0] {name = "x"}
+// CIR:   %[[FIVE:.*]] = cir.const #cir.int<5> : !s32i
+// CIR:   cir.store{{.*}} %[[FIVE]], %[[P_X]]
+// CIR:   %[[P_Y:.*]] = cir.get_member %[[P]][1] {name = "y"}
+// CIR:   %[[TEN:.*]] = cir.const #cir.int<10> : !s32i
+// CIR:   cir.store{{.*}} %[[TEN]], %[[P_Y]]
+
+// LLVM-LABEL: @_Z4foo4v
+// LLVM:   %[[P:.*]] = alloca %struct.Point
+// LLVM:   %[[P_X:.*]] = getelementptr %struct.Point, ptr %[[P]], i32 0, i32 0
+// LLVM:   store i32 5, ptr %[[P_X]]
+// LLVM:   %[[P_Y:.*]] = getelementptr %struct.Point, ptr %[[P]], i32 0, i32 1
+// LLVM:   store i32 10, ptr %[[P_Y]]
+
+// OGCG-LABEL: @_Z4foo4v
+// OGCG:   %[[P:.*]] = alloca %struct.Point
+// OGCG:   call void @llvm.memcpy{{.*}}(ptr{{.*}} %[[P]], ptr{{.*}} @__const._Z4foo4v.p

Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

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

Looks good, except a possibly missing test case

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

LGTM once visitors all get tested (or continue NYI)

mmha added 2 commits October 17, 2025 12:52
This patch implements visitors for most of the aggregate expressions yielding lvalues.
@mmha mmha force-pushed the cir-aggrexpr-lvalue branch from 153b1b8 to 6767d2b Compare October 20, 2025 14:57
@mmha mmha merged commit cd05383 into llvm:main Oct 20, 2025
15 of 18 checks passed
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.

4 participants