https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/153677

>From 2e799c0cd75e6e79735f591e1d72015047b414dc Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Thu, 14 Aug 2025 22:44:35 +0200
Subject: [PATCH 1/8] [CIR] Implement Statement Expressions

This patch adds support for statement expressions. It also changes 
emitCompoundStmt and emitCompoundStmtWithoutScope to accept an Address that the 
optional result is written to. This allows the creation of the alloca ahead of 
the creation of the scope which saves us from hoisting the alloca to its parent 
scope.
---
 clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp |   6 +
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    |  15 ++
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |  30 +++-
 clang/lib/CIR/CodeGen/CIRGenStmt.cpp          |  65 ++++++-
 clang/test/CIR/CodeGen/statement-exprs.c      | 166 ++++++++++++++++++
 5 files changed, 272 insertions(+), 10 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/statement-exprs.c

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 6b6ac701e6867..9d85aacb76ee0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -69,6 +69,12 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
   void Visit(Expr *e) { StmtVisitor<AggExprEmitter>::Visit(e); }
 
   void VisitCallExpr(const CallExpr *e);
+  void VisitStmtExpr(const StmtExpr *e) {
+    CIRGenFunction::StmtExprEvaluation eval(cgf);
+    Address retAlloca =
+        cgf.createMemTemp(e->getType(), cgf.getLoc(e->getSourceRange()));
+    cgf.emitCompoundStmt(*e->getSubStmt(), &retAlloca, dest);
+  }
 
   void VisitDeclRefExpr(DeclRefExpr *e) { emitAggLoadOfLValue(e); }
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 8649bab91ce8e..e577175aada18 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -185,6 +185,21 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
   mlir::Value VisitCastExpr(CastExpr *e);
   mlir::Value VisitCallExpr(const CallExpr *e);
 
+  mlir::Value VisitStmtExpr(StmtExpr *e) {
+    CIRGenFunction::StmtExprEvaluation eval(cgf);
+    if (e->getType()->isVoidType()) {
+      cgf.emitCompoundStmt(*e->getSubStmt());
+      return {};
+    }
+
+    Address retAlloca =
+        cgf.createMemTemp(e->getType(), cgf.getLoc(e->getSourceRange()));
+    cgf.emitCompoundStmt(*e->getSubStmt(), &retAlloca);
+
+    return cgf.emitLoadOfScalar(cgf.makeAddrLValue(retAlloca, e->getType()),
+                                e->getExprLoc());
+  }
+
   mlir::Value VisitArraySubscriptExpr(ArraySubscriptExpr *e) {
     if (e->getBase()->getType()->isVectorType()) {
       assert(!cir::MissingFeatures::scalableVectors());
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 122989a26b4a8..f27ae8d1b7b9a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1174,9 +1174,14 @@ class CIRGenFunction : public CIRGenTypeCache {
   LValue emitScalarCompoundAssignWithComplex(const CompoundAssignOperator *e,
                                              mlir::Value &result);
 
-  void emitCompoundStmt(const clang::CompoundStmt &s);
+  Address emitCompoundStmt(const clang::CompoundStmt &s,
+                           Address *lastValue = nullptr,
+                           AggValueSlot slot = AggValueSlot::ignored());
 
-  void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s);
+  Address
+  emitCompoundStmtWithoutScope(const clang::CompoundStmt &s,
+                               Address *lastValue = nullptr,
+                               AggValueSlot slot = AggValueSlot::ignored());
 
   void emitDecl(const clang::Decl &d, bool evaluateConditionDecl = false);
   mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s);
@@ -1413,6 +1418,27 @@ class CIRGenFunction : public CIRGenTypeCache {
   // we know if a temporary should be destroyed conditionally.
   ConditionalEvaluation *outermostConditional = nullptr;
 
+  /// An RAII object to record that we're evaluating a statement
+  /// expression.
+  class StmtExprEvaluation {
+    CIRGenFunction &cgf;
+
+    /// We have to save the outermost conditional: cleanups in a
+    /// statement expression aren't conditional just because the
+    /// StmtExpr is.
+    ConditionalEvaluation *savedOutermostConditional;
+
+  public:
+    StmtExprEvaluation(CIRGenFunction &cgf)
+        : cgf(cgf), savedOutermostConditional(cgf.outermostConditional) {
+      cgf.outermostConditional = nullptr;
+    }
+
+    ~StmtExprEvaluation() {
+      cgf.outermostConditional = savedOutermostConditional;
+    }
+  };
+
   template <typename FuncTy>
   ConditionalInfo emitConditionalBlocks(const AbstractConditionalOperator *e,
                                         const FuncTy &branchGenFunc);
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp 
b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index 16e03f68ba5d0..5fbbec222ac16 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -14,6 +14,7 @@
 #include "CIRGenFunction.h"
 
 #include "mlir/IR/Builders.h"
+#include "mlir/IR/Location.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/StmtOpenACC.h"
@@ -23,16 +24,62 @@ using namespace clang;
 using namespace clang::CIRGen;
 using namespace cir;
 
-void CIRGenFunction::emitCompoundStmtWithoutScope(const CompoundStmt &s) {
-  for (auto *curStmt : s.body()) {
-    if (emitStmt(curStmt, /*useCurrentScope=*/false).failed())
-      getCIRGenModule().errorNYI(curStmt->getSourceRange(),
-                                 std::string("emitCompoundStmtWithoutScope: ") 
+
-                                     curStmt->getStmtClassName());
+Address CIRGenFunction::emitCompoundStmtWithoutScope(const CompoundStmt &s,
+                                                     Address *lastValue,
+                                                     AggValueSlot slot) {
+  const Stmt *exprResult = s.getStmtExprResult();
+  assert((!lastValue || (lastValue && exprResult)) &&
+         "If lastValue is not null then the CompoundStmt must have a "
+         "StmtExprResult");
+
+  Address retAlloca = Address::invalid();
+
+  for (Stmt *curStmt : s.body()) {
+    // We have to special case labels here. They are statements, but when put
+    // at the end of a statement expression, they yield the value of their
+    // subexpression. Handle this by walking through all labels we encounter,
+    // emitting them before we evaluate the subexpr.
+    // Similar issues arise for attributed statements.
+    if (lastValue && exprResult == curStmt) {
+      while (!isa<Expr>(exprResult)) {
+        if (const auto *ls = dyn_cast<LabelStmt>(exprResult)) {
+          if (emitLabel(*ls->getDecl()).failed())
+            return Address::invalid();
+          exprResult = ls->getSubStmt();
+        } else if (const auto *as = dyn_cast<AttributedStmt>(exprResult)) {
+          // FIXME: Update this if we ever have attributes that affect the
+          // semantics of an expression.
+          exprResult = as->getSubStmt();
+        } else {
+          llvm_unreachable("Unknown value statement");
+        }
+      }
+
+      const Expr *e = cast<Expr>(exprResult);
+      QualType exprTy = e->getType();
+      if (hasAggregateEvaluationKind(exprTy)) {
+        emitAggExpr(e, slot);
+      } else {
+        // We can't return an RValue here because there might be cleanups at
+        // the end of the StmtExpr.  Because of that, we have to emit the 
result
+        // here into a temporary alloca.
+        emitAnyExprToMem(e, *lastValue, Qualifiers(),
+                         /*IsInit*/ false);
+      }
+    } else {
+      if (emitStmt(curStmt, /*useCurrentScope=*/false).failed())
+        return Address::invalid();
+    }
   }
+
+  return retAlloca;
 }
 
-void CIRGenFunction::emitCompoundStmt(const CompoundStmt &s) {
+Address CIRGenFunction::emitCompoundStmt(const CompoundStmt &s,
+                                         Address *lastValue,
+                                         AggValueSlot slot) {
+  Address retAlloca = Address::invalid();
+
   // Add local scope to track new declared variables.
   SymTableScopeTy varScope(symbolTable);
   mlir::Location scopeLoc = getLoc(s.getSourceRange());
@@ -45,8 +92,10 @@ void CIRGenFunction::emitCompoundStmt(const CompoundStmt &s) 
{
     mlir::OpBuilder::InsertionGuard guard(builder);
     builder.restoreInsertionPoint(scopeInsPt);
     LexicalScope lexScope(*this, scopeLoc, builder.getInsertionBlock());
-    emitCompoundStmtWithoutScope(s);
+    retAlloca = emitCompoundStmtWithoutScope(s, lastValue, slot);
   }
+
+  return retAlloca;
 }
 
 void CIRGenFunction::emitStopPoint(const Stmt *s) {
diff --git a/clang/test/CIR/CodeGen/statement-exprs.c 
b/clang/test/CIR/CodeGen/statement-exprs.c
new file mode 100644
index 0000000000000..67927bbcc1bdc
--- /dev/null
+++ b/clang/test/CIR/CodeGen/statement-exprs.c
@@ -0,0 +1,166 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
%t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+int f19(void) {
+  return ({ 3;;4;; });
+}
+
+// CIR: cir.func dso_local @f19() -> !s32i
+// CIR:   %[[RETVAL:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+// CIR:   %[[TMP:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["tmp"]
+// CIR:   cir.scope {
+// CIR:     %[[C3:.+]] = cir.const #cir.int<3> : !s32i
+// CIR:     %[[C4:.+]] = cir.const #cir.int<4> : !s32i
+// CIR:     cir.store {{.*}} %[[C4]], %[[TMP]] : !s32i, !cir.ptr<!s32i>
+// CIR:   }
+// CIR:   %[[TMP_VAL:.+]] = cir.load {{.*}} %[[TMP]] : !cir.ptr<!s32i>, !s32i
+// CIR:   cir.store %[[TMP_VAL]], %[[RETVAL]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[RES:.+]] = cir.load %[[RETVAL]] : !cir.ptr<!s32i>, !s32i
+// CIR:   cir.return %[[RES]] : !s32i
+
+// LLVM: define dso_local i32 @f19()
+// LLVM:   %[[VAR1:.+]] = alloca i32, i64 1
+// LLVM:   %[[VAR2:.+]] = alloca i32, i64 1
+// LLVM:   br label %[[LBL3:.+]]
+// LLVM: [[LBL3]]:
+// LLVM:     store i32 4, ptr %[[VAR2]]
+// LLVM:     br label %[[LBL4:.+]]
+// LLVM: [[LBL4]]:
+// LLVM:     %[[V1:.+]] = load i32, ptr %[[VAR2]]
+// LLVM:     store i32 %[[V1]], ptr %[[VAR1]]
+// LLVM:     %[[RES:.+]] = load i32, ptr %[[VAR1]]
+// LLVM:     ret i32 %[[RES]]
+
+// OGCG: define dso_local i32 @f19()
+// OGCG: entry:
+// OGCG:   %[[TMP:.+]] = alloca i32
+// OGCG:   store i32 4, ptr %[[TMP]]
+// OGCG:   %[[TMP_VAL:.+]] = load i32, ptr %[[TMP]]
+// OGCG:   ret i32 %[[TMP_VAL]]
+
+
+int nested(void) {
+  ({123;});
+  {
+    int bar = 987;
+    return ({ ({ int asdf = 123; asdf; }); ({9999;}); });
+  }
+}
+
+// CIR: cir.func dso_local @nested() -> !s32i
+// CIR:   %[[RETVAL:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+// CIR:   %[[TMP_OUTER:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["tmp"]
+// CIR:   cir.scope {
+// CIR:     %[[C123_OUTER:.+]] = cir.const #cir.int<123> : !s32i
+// CIR:     cir.store {{.*}} %[[C123_OUTER]], %[[TMP_OUTER]] : !s32i, 
!cir.ptr<!s32i>
+// CIR:   }
+// CIR:   %[[LOAD_TMP_OUTER:.+]] = cir.load {{.*}} %[[TMP_OUTER]] : 
!cir.ptr<!s32i>, !s32i
+// CIR:   cir.scope {
+// CIR:     %[[BAR:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["bar", init]
+// CIR:     %[[TMP_BARRET:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["tmp"]
+// CIR:     %[[C987:.+]] = cir.const #cir.int<987> : !s32i
+// CIR:     cir.store {{.*}} %[[C987]], %[[BAR]] : !s32i, !cir.ptr<!s32i>
+// CIR:     cir.scope {
+// CIR:       %[[TMP1:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["tmp"]
+// CIR:       %[[TMP2:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["tmp"]
+// CIR:       cir.scope {
+// CIR:         %[[ASDF:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["asdf", 
init]
+// CIR:         %[[C123_INNER:.+]] = cir.const #cir.int<123> : !s32i
+// CIR:         cir.store {{.*}} %[[C123_INNER]], %[[ASDF]] : !s32i, 
!cir.ptr<!s32i>
+// CIR:         %[[LOAD_ASDF:.+]] = cir.load {{.*}} %[[ASDF]] : 
!cir.ptr<!s32i>, !s32i
+// CIR:         cir.store {{.*}} %[[LOAD_ASDF]], %[[TMP1]] : !s32i, 
!cir.ptr<!s32i>
+// CIR:       }
+// CIR:       %[[V1:.+]] = cir.load {{.*}} %[[TMP1]] : !cir.ptr<!s32i>, !s32i
+// CIR:       cir.scope {
+// CIR:         %[[C9999:.+]] = cir.const #cir.int<9999> : !s32i
+// CIR:         cir.store {{.*}} %[[C9999]], %[[TMP2]] : !s32i, !cir.ptr<!s32i>
+// CIR:       }
+// CIR:       %[[V2:.+]] = cir.load {{.*}} %[[TMP2]] : !cir.ptr<!s32i>, !s32i
+// CIR:       cir.store {{.*}} %[[V2]], %[[TMP_BARRET]] : !s32i, 
!cir.ptr<!s32i>
+// CIR:     }
+// CIR:     %[[BARRET_VAL:.+]] = cir.load {{.*}} %[[TMP_BARRET]] : 
!cir.ptr<!s32i>, !s32i
+// CIR:     cir.store %[[BARRET_VAL]], %[[RETVAL]] : !s32i, !cir.ptr<!s32i>
+// CIR:     %[[RES:.+]] = cir.load %[[RETVAL]] : !cir.ptr<!s32i>, !s32i
+// CIR:     cir.return %[[RES]] : !s32i
+// CIR:   }
+// CIR:   %[[FINAL_RES:.+]] = cir.load %[[RETVAL]] : !cir.ptr<!s32i>, !s32i
+// CIR:   cir.return %[[FINAL_RES]] : !s32i
+
+// LLVM: define dso_local i32 @nested()
+// LLVM:   %[[VAR1:.+]] = alloca i32, i64 1
+// LLVM:   %[[VAR2:.+]] = alloca i32, i64 1
+// LLVM:   %[[VAR3:.+]] = alloca i32, i64 1
+// LLVM:   %[[VAR4:.+]] = alloca i32, i64 1
+// LLVM:   %[[VAR5:.+]] = alloca i32, i64 1
+// LLVM:   %[[VAR6:.+]] = alloca i32, i64 1
+// LLVM:   %[[VAR7:.+]] = alloca i32, i64 1
+// LLVM:   br label %[[LBL8:.+]]
+// LLVM: [[LBL8]]:
+// LLVM:     store i32 123, ptr %[[VAR7]]
+// LLVM:     br label %[[LBL9:.+]]
+// LLVM: [[LBL9]]:
+// LLVM:     br label %[[LBL10:.+]]
+// LLVM: [[LBL10]]:
+// LLVM:     store i32 987, ptr %[[VAR1]]
+// LLVM:     br label %[[LBL11:.+]]
+// LLVM: [[LBL11]]:
+// LLVM:     br label %[[LBL12:.+]]
+// LLVM: [[LBL12]]:
+// LLVM:     store i32 123, ptr %[[VAR5]]
+// LLVM:     %[[V1:.+]] = load i32, ptr %[[VAR5]]
+// LLVM:     store i32 %[[V1]], ptr %[[VAR3]]
+// LLVM:     br label %[[LBL14:.+]]
+// LLVM: [[LBL14]]:
+// LLVM:     br label %[[LBL15:.+]]
+// LLVM: [[LBL15]]:
+// LLVM:     store i32 9999, ptr %[[VAR4]]
+// LLVM:     br label %[[LBL16:.+]]
+// LLVM: [[LBL16]]:
+// LLVM:     %[[V2:.+]] = load i32, ptr %[[VAR4]]
+// LLVM:     store i32 %[[V2]], ptr %[[VAR2]]
+// LLVM:     br label %[[LBL18:.+]]
+// LLVM: [[LBL18]]:
+// LLVM:     %[[V3:.+]] = load i32, ptr %[[VAR2]]
+// LLVM:     store i32 %[[V3]], ptr %[[VAR6]]
+// LLVM:     %[[RES:.+]] = load i32, ptr %[[VAR6]]
+// LLVM:     ret i32 %[[RES]]
+
+// OGCG: define dso_local i32 @nested()
+// OGCG: entry:
+// OGCG:   %[[TMP_OUTER:.+]] = alloca i32
+// OGCG:   %[[BAR:.+]] = alloca i32
+// OGCG:   %[[ASDF:.+]] = alloca i32
+// OGCG:   %[[TMP1:.+]] = alloca i32
+// OGCG:   %[[TMP2:.+]] = alloca i32
+// OGCG:   %[[TMP3:.+]] = alloca i32
+// OGCG:   store i32 123, ptr %[[TMP_OUTER]]
+// OGCG:   %[[OUTER_VAL:.+]] = load i32, ptr %[[TMP_OUTER]]
+// OGCG:   store i32 987, ptr %[[BAR]]
+// OGCG:   store i32 123, ptr %[[ASDF]]
+// OGCG:   %[[ASDF_VAL:.+]] = load i32, ptr %[[ASDF]]
+// OGCG:   store i32 %[[ASDF_VAL]], ptr %[[TMP1]]
+// OGCG:   %[[TMP1_VAL:.+]] = load i32, ptr %[[TMP1]]
+// OGCG:   store i32 9999, ptr %[[TMP3]]
+// OGCG:   %[[TMP3_VAL:.+]] = load i32, ptr %[[TMP3]]
+// OGCG:   store i32 %[[TMP3_VAL]], ptr %[[TMP2]]
+// OGCG:   %[[RES:.+]] = load i32, ptr %[[TMP2]]
+// OGCG:   ret i32 %[[RES]]
+
+void empty() {
+  return ({;;;;});
+}
+
+// CIR: cir.func no_proto dso_local @empty()
+// CIR-NEXT:   cir.return
+
+// LLVM: define dso_local void @empty()
+// LLVM:   ret void
+// LLVM: }
+
+// OGCG: define dso_local void @empty()
+// OGCG:   ret void
+// OGCG: }

>From 0b1de3374046d76a6e397ac3c5ce0342642685cd Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Tue, 19 Aug 2025 09:19:42 +0200
Subject: [PATCH 2/8] emitCompoundStmt should return a LogicalResult

---
 clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp |  2 +-
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    |  4 +-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp      |  7 +---
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |  8 ++--
 clang/lib/CIR/CodeGen/CIRGenStmt.cpp          | 40 +++++++------------
 5 files changed, 24 insertions(+), 37 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 9d85aacb76ee0..2a3c98c458256 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -73,7 +73,7 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
     CIRGenFunction::StmtExprEvaluation eval(cgf);
     Address retAlloca =
         cgf.createMemTemp(e->getType(), cgf.getLoc(e->getSourceRange()));
-    cgf.emitCompoundStmt(*e->getSubStmt(), &retAlloca, dest);
+    (void)cgf.emitCompoundStmt(*e->getSubStmt(), &retAlloca, dest);
   }
 
   void VisitDeclRefExpr(DeclRefExpr *e) { emitAggLoadOfLValue(e); }
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index e577175aada18..a23bdfc02ad2d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -188,13 +188,13 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
   mlir::Value VisitStmtExpr(StmtExpr *e) {
     CIRGenFunction::StmtExprEvaluation eval(cgf);
     if (e->getType()->isVoidType()) {
-      cgf.emitCompoundStmt(*e->getSubStmt());
+      (void)cgf.emitCompoundStmt(*e->getSubStmt());
       return {};
     }
 
     Address retAlloca =
         cgf.createMemTemp(e->getType(), cgf.getLoc(e->getSourceRange()));
-    cgf.emitCompoundStmt(*e->getSubStmt(), &retAlloca);
+    (void)cgf.emitCompoundStmt(*e->getSubStmt(), &retAlloca);
 
     return cgf.emitLoadOfScalar(cgf.makeAddrLValue(retAlloca, e->getType()),
                                 e->getExprLoc());
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 917afa8e78021..7d003dbbf3207 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -490,13 +490,10 @@ mlir::LogicalResult 
CIRGenFunction::emitFunctionBody(const clang::Stmt *body) {
   // We start with function level scope for variables.
   SymTableScopeTy varScope(symbolTable);
 
-  auto result = mlir::LogicalResult::success();
   if (const CompoundStmt *block = dyn_cast<CompoundStmt>(body))
-    emitCompoundStmtWithoutScope(*block);
-  else
-    result = emitStmt(body, /*useCurrentScope=*/true);
+    return emitCompoundStmtWithoutScope(*block);
 
-  return result;
+  return emitStmt(body, /*useCurrentScope=*/true);
 }
 
 static void eraseEmptyAndUnusedBlocks(cir::FuncOp func) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index f27ae8d1b7b9a..6e3f5b1f4afac 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1174,11 +1174,11 @@ class CIRGenFunction : public CIRGenTypeCache {
   LValue emitScalarCompoundAssignWithComplex(const CompoundAssignOperator *e,
                                              mlir::Value &result);
 
-  Address emitCompoundStmt(const clang::CompoundStmt &s,
-                           Address *lastValue = nullptr,
-                           AggValueSlot slot = AggValueSlot::ignored());
+  mlir::LogicalResult
+  emitCompoundStmt(const clang::CompoundStmt &s, Address *lastValue = nullptr,
+                   AggValueSlot slot = AggValueSlot::ignored());
 
-  Address
+  mlir::LogicalResult
   emitCompoundStmtWithoutScope(const clang::CompoundStmt &s,
                                Address *lastValue = nullptr,
                                AggValueSlot slot = AggValueSlot::ignored());
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp 
b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index 5fbbec222ac16..d94a9c5f2f9a3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -15,6 +15,7 @@
 
 #include "mlir/IR/Builders.h"
 #include "mlir/IR/Location.h"
+#include "mlir/Support/LLVM.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/StmtOpenACC.h"
@@ -24,16 +25,13 @@ using namespace clang;
 using namespace clang::CIRGen;
 using namespace cir;
 
-Address CIRGenFunction::emitCompoundStmtWithoutScope(const CompoundStmt &s,
-                                                     Address *lastValue,
-                                                     AggValueSlot slot) {
+mlir::LogicalResult CIRGenFunction::emitCompoundStmtWithoutScope(
+    const CompoundStmt &s, Address *lastValue, AggValueSlot slot) {
   const Stmt *exprResult = s.getStmtExprResult();
   assert((!lastValue || (lastValue && exprResult)) &&
          "If lastValue is not null then the CompoundStmt must have a "
          "StmtExprResult");
 
-  Address retAlloca = Address::invalid();
-
   for (Stmt *curStmt : s.body()) {
     // We have to special case labels here. They are statements, but when put
     // at the end of a statement expression, they yield the value of their
@@ -44,7 +42,7 @@ Address CIRGenFunction::emitCompoundStmtWithoutScope(const 
CompoundStmt &s,
       while (!isa<Expr>(exprResult)) {
         if (const auto *ls = dyn_cast<LabelStmt>(exprResult)) {
           if (emitLabel(*ls->getDecl()).failed())
-            return Address::invalid();
+            return mlir::failure();
           exprResult = ls->getSubStmt();
         } else if (const auto *as = dyn_cast<AttributedStmt>(exprResult)) {
           // FIXME: Update this if we ever have attributes that affect the
@@ -68,18 +66,16 @@ Address CIRGenFunction::emitCompoundStmtWithoutScope(const 
CompoundStmt &s,
       }
     } else {
       if (emitStmt(curStmt, /*useCurrentScope=*/false).failed())
-        return Address::invalid();
+        return mlir::failure();
     }
   }
 
-  return retAlloca;
+  return mlir::success();
 }
 
-Address CIRGenFunction::emitCompoundStmt(const CompoundStmt &s,
-                                         Address *lastValue,
-                                         AggValueSlot slot) {
-  Address retAlloca = Address::invalid();
-
+mlir::LogicalResult CIRGenFunction::emitCompoundStmt(const CompoundStmt &s,
+                                                     Address *lastValue,
+                                                     AggValueSlot slot) {
   // Add local scope to track new declared variables.
   SymTableScopeTy varScope(symbolTable);
   mlir::Location scopeLoc = getLoc(s.getSourceRange());
@@ -88,14 +84,10 @@ Address CIRGenFunction::emitCompoundStmt(const CompoundStmt 
&s,
       scopeLoc, [&](mlir::OpBuilder &b, mlir::Type &type, mlir::Location loc) {
         scopeInsPt = b.saveInsertionPoint();
       });
-  {
-    mlir::OpBuilder::InsertionGuard guard(builder);
-    builder.restoreInsertionPoint(scopeInsPt);
-    LexicalScope lexScope(*this, scopeLoc, builder.getInsertionBlock());
-    retAlloca = emitCompoundStmtWithoutScope(s, lastValue, slot);
-  }
-
-  return retAlloca;
+  mlir::OpBuilder::InsertionGuard guard(builder);
+  builder.restoreInsertionPoint(scopeInsPt);
+  LexicalScope lexScope(*this, scopeLoc, builder.getInsertionBlock());
+  return emitCompoundStmtWithoutScope(s, lastValue, slot);
 }
 
 void CIRGenFunction::emitStopPoint(const Stmt *s) {
@@ -298,10 +290,8 @@ mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const 
Stmt *s,
     return emitDeclStmt(cast<DeclStmt>(*s));
   case Stmt::CompoundStmtClass:
     if (useCurrentScope)
-      emitCompoundStmtWithoutScope(cast<CompoundStmt>(*s));
-    else
-      emitCompoundStmt(cast<CompoundStmt>(*s));
-    break;
+      return emitCompoundStmtWithoutScope(cast<CompoundStmt>(*s));
+    return emitCompoundStmt(cast<CompoundStmt>(*s));
   case Stmt::GotoStmtClass:
     return emitGotoStmt(cast<GotoStmt>(*s));
   case Stmt::ContinueStmtClass:

>From 4109e94c45452775fa57091067e58d894ec6bc79 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Fri, 15 Aug 2025 14:02:38 +0200
Subject: [PATCH 3/8] * Update OpenACC test * Failed function definition will
 not lead to FuncOp being erased * Try to emit statements after a failure in a
 compound statement

---
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp                  | 1 -
 clang/lib/CIR/CodeGen/CIRGenStmt.cpp                      | 8 ++++----
 clang/test/CIR/CodeGenOpenACC/openacc-not-implemented.cpp | 3 +--
 3 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 7d003dbbf3207..706c14ae962a8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -558,7 +558,6 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl 
gd, cir::FuncOp fn,
       emitImplicitAssignmentOperatorBody(args);
     } else if (body) {
       if (mlir::failed(emitFunctionBody(body))) {
-        fn.erase();
         return nullptr;
       }
     } else {
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp 
b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index d94a9c5f2f9a3..b73f9bf8b90d5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -27,12 +27,13 @@ using namespace cir;
 
 mlir::LogicalResult CIRGenFunction::emitCompoundStmtWithoutScope(
     const CompoundStmt &s, Address *lastValue, AggValueSlot slot) {
+  mlir::LogicalResult result = mlir::success();
   const Stmt *exprResult = s.getStmtExprResult();
   assert((!lastValue || (lastValue && exprResult)) &&
          "If lastValue is not null then the CompoundStmt must have a "
          "StmtExprResult");
 
-  for (Stmt *curStmt : s.body()) {
+  for (const Stmt *curStmt : s.body()) {
     // We have to special case labels here. They are statements, but when put
     // at the end of a statement expression, they yield the value of their
     // subexpression. Handle this by walking through all labels we encounter,
@@ -66,11 +67,10 @@ mlir::LogicalResult 
CIRGenFunction::emitCompoundStmtWithoutScope(
       }
     } else {
       if (emitStmt(curStmt, /*useCurrentScope=*/false).failed())
-        return mlir::failure();
+        result = mlir::failure();
     }
   }
-
-  return mlir::success();
+  return result;
 }
 
 mlir::LogicalResult CIRGenFunction::emitCompoundStmt(const CompoundStmt &s,
diff --git a/clang/test/CIR/CodeGenOpenACC/openacc-not-implemented.cpp 
b/clang/test/CIR/CodeGenOpenACC/openacc-not-implemented.cpp
index da45aca13e7f9..e6d33aeb3c7e5 100644
--- a/clang/test/CIR/CodeGenOpenACC/openacc-not-implemented.cpp
+++ b/clang/test/CIR/CodeGenOpenACC/openacc-not-implemented.cpp
@@ -2,8 +2,7 @@
 
 void HelloWorld(int *A, int *B, int *C, int N) {
 
-// expected-error@+2{{ClangIR code gen Not Yet Implemented: OpenACC Atomic 
Construct}}
-// expected-error@+1{{ClangIR code gen Not Yet Implemented: 
emitCompoundStmtWithoutScope: OpenACCAtomicConstruct}}
+// expected-error@+1{{ClangIR code gen Not Yet Implemented: OpenACC Atomic 
Construct}}
 #pragma acc atomic
   N = N + 1;
 

>From f6a5e30e7793a7866c111cada1a5d2c064acc2a0 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Fri, 15 Aug 2025 14:47:50 +0200
Subject: [PATCH 4/8] Add tests Fix member exprs of statement exprs yielding an
 aggregate

---
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp     |  10 +--
 clang/test/CIR/CodeGen/statement-exprs.c | 108 +++++++++++++++++++++++
 2 files changed, 113 insertions(+), 5 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index ffbdcd4e1d8de..95412a8da0ff7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1175,11 +1175,11 @@ static void pushTemporaryCleanup(CIRGenFunction &cgf,
                                         ->getBaseElementTypeUnsafe()
                                         ->getAs<clang::RecordType>()) {
     // Get the destructor for the reference temporary.
-    auto *classDecl =
-        cast<CXXRecordDecl>(rt->getOriginalDecl()->getDefinitionOrSelf());
-    if (!classDecl->hasTrivialDestructor())
-      referenceTemporaryDtor =
-          classDecl->getDefinitionOrSelf()->getDestructor();
+    if (const auto *classDecl = dyn_cast<CXXRecordDecl>(
+            rt->getOriginalDecl()->getDefinitionOrSelf())) {
+      if (!classDecl->hasTrivialDestructor())
+        referenceTemporaryDtor = classDecl->getDestructor();
+    }
   }
 
   if (!referenceTemporaryDtor)
diff --git a/clang/test/CIR/CodeGen/statement-exprs.c 
b/clang/test/CIR/CodeGen/statement-exprs.c
index 67927bbcc1bdc..927a868336b59 100644
--- a/clang/test/CIR/CodeGen/statement-exprs.c
+++ b/clang/test/CIR/CodeGen/statement-exprs.c
@@ -164,3 +164,111 @@ void empty() {
 // OGCG: define dso_local void @empty()
 // OGCG:   ret void
 // OGCG: }
+
+void empty2() { ({ }); }
+
+// CIR: @empty2
+// CIR-NEXT: cir.return
+
+// LLVM: @empty2()
+// LLVM:   ret void
+// LLVM: }
+
+// OGCG: @empty2()
+// OGCG:   ret void
+// OGCG: }
+
+
+// Yields an out-of-scope scalar.
+void test2() { ({int x = 3; x; }); }
+// CIR: @test2
+// CIR: %[[RETVAL:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>
+// CIR: cir.scope {
+// CIR:   %[[VAR:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init]
+//          [...]
+// CIR:   %[[TMP:.+]] = cir.load{{.*}} %[[VAR]] : !cir.ptr<!s32i>, !s32i
+// CIR:   cir.store{{.*}} %[[TMP]], %[[RETVAL]] : !s32i, !cir.ptr<!s32i>
+// CIR: }
+// CIR: %{{.+}} = cir.load{{.*}} %[[RETVAL]] : !cir.ptr<!s32i>, !s32i
+
+// LLVM: define dso_local void @test2()
+// LLVM:   %[[X:.+]] = alloca i32, i64 1
+// LLVM:   %[[TMP:.+]] = alloca i32, i64 1
+// LLVM:   br label %[[LBL3:.+]]
+// LLVM: [[LBL3]]:
+// LLVM:     store i32 3, ptr %[[X]]
+// LLVM:     %[[X_VAL:.+]] = load i32, ptr %[[X]]
+// LLVM:     store i32 %[[X_VAL]], ptr %[[TMP]]
+// LLVM:     br label %[[LBL5:.+]]
+// LLVM: [[LBL5]]:
+// LLVM:     ret void
+
+// OGCG: define dso_local void @test2()
+// OGCG: entry:
+// OGCG:   %[[X:.+]] = alloca i32
+// OGCG:   %[[TMP:.+]] = alloca i32
+// OGCG:   store i32 3, ptr %[[X]]
+// OGCG:   %[[X_VAL:.+]] = load i32, ptr %[[X]]
+// OGCG:   store i32 %[[X_VAL]], ptr %[[TMP]]
+// OGCG:   %[[TMP_VAL:.+]] = load i32, ptr %[[TMP]]
+// OGCG:   ret void
+
+// Yields an aggregate.
+struct S { int x; };
+int test3() { return ({ struct S s = {1}; s; }).x; }
+// CIR: cir.func no_proto dso_local @test3() -> !s32i
+// CIR:   %[[RETVAL:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+// CIR:   %[[YIELDVAL:.+]] = cir.scope {
+// CIR:     %[[REF_TMP0:.+]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, 
["ref.tmp0"]
+// CIR:     %[[TMP:.+]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["tmp"]
+// CIR:     cir.scope {
+// CIR:       %[[S:.+]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["s", init]
+// CIR:       %[[GEP_X_S:.+]] = cir.get_member %[[S]][0] {name = "x"} : 
!cir.ptr<!rec_S> -> !cir.ptr<!s32i>
+// CIR:       %[[C1:.+]] = cir.const #cir.int<1> : !s32i
+// CIR:       cir.store {{.*}} %[[C1]], %[[GEP_X_S]] : !s32i, !cir.ptr<!s32i>
+// CIR:     }
+// CIR:     %[[GEP_X_TMP:.+]] = cir.get_member %[[REF_TMP0]][0] {name = "x"} : 
!cir.ptr<!rec_S> -> !cir.ptr<!s32i>
+// CIR:     %[[XVAL:.+]] = cir.load {{.*}} %[[GEP_X_TMP]] : !cir.ptr<!s32i>, 
!s32i
+// CIR:     cir.yield %[[XVAL]] : !s32i
+// CIR:   } : !s32i
+// CIR:   cir.store %[[YIELDVAL]], %[[RETVAL]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[RES:.+]] = cir.load %[[RETVAL]] : !cir.ptr<!s32i>, !s32i
+// CIR:   cir.return %[[RES]] : !s32i
+
+// LLVM: define dso_local i32 @test3()
+// LLVM:   %[[VAR1:.+]] = alloca %struct.S, i64 1
+// LLVM:   %[[VAR2:.+]] = alloca %struct.S, i64 1
+// LLVM:   %[[VAR3:.+]] = alloca %struct.S, i64 1
+// LLVM:   %[[VAR4:.+]] = alloca i32, i64 1
+// LLVM:   br label %[[LBL5:.+]]
+// LLVM: [[LBL5]]:
+// LLVM:     br label %[[LBL6:.+]]
+// LLVM: [[LBL6]]:
+// LLVM:     %[[GEP_S:.+]] = getelementptr %struct.S, ptr %[[VAR3]], i32 0, 
i32 0
+// LLVM:     store i32 1, ptr %[[GEP_S]]
+// LLVM:     br label %[[LBL8:.+]]
+// LLVM: [[LBL8]]:
+// LLVM:     %[[GEP_VAR1:.+]] = getelementptr %struct.S, ptr %[[VAR1]], i32 0, 
i32 0
+// LLVM:     %[[LOAD_X:.+]] = load i32, ptr %[[GEP_VAR1]]
+// LLVM:     br label %[[LBL11:.+]]
+// LLVM: [[LBL11]]:
+// LLVM:     %[[PHI:.+]] = phi i32 [ %[[LOAD_X]], %[[LBL8]] ]
+// LLVM:     store i32 %[[PHI]], ptr %[[VAR4]]
+// LLVM:     %[[RES:.+]] = load i32, ptr %[[VAR4]]
+// LLVM:     ret i32 %[[RES]]
+
+// OGCG: define dso_local i32 @test3()
+// OGCG: entry:
+// OGCG:   %[[REF_TMP:.+]] = alloca %struct.S
+// OGCG:   %[[S:.+]] = alloca %struct.S
+// OGCG:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[S]], ptr align 4 
@__const.test3.s, i64 4, i1 false)
+// OGCG:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[REF_TMP]], ptr 
align 4 %[[S]], i64 4, i1 false)
+// OGCG:   %[[GEP:.+]] = getelementptr inbounds nuw %struct.S, ptr 
%[[REF_TMP]], i32 0, i32 0
+// OGCG:   %[[XVAL:.+]] = load i32, ptr %[[GEP]]
+// OGCG:   ret i32 %[[XVAL]]
+
+// Expression is wrapped in an expression attribute (just ensure it does not 
crash).
+void test4(int x) { ({[[gsl::suppress("foo")]] x;}); }
+// CIR: @test4
+// LLVM: @test4
+// OGCG: @test4

>From ae8cb820d0399d71d2f65dc1e2ad7110cb0d61de Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Fri, 15 Aug 2025 15:29:43 +0200
Subject: [PATCH 5/8] Add C++ tests

---
 clang/test/CIR/CodeGen/stmt-expr.cpp | 90 ++++++++++++++++++++++++++++
 1 file changed, 90 insertions(+)
 create mode 100644 clang/test/CIR/CodeGen/stmt-expr.cpp

diff --git a/clang/test/CIR/CodeGen/stmt-expr.cpp 
b/clang/test/CIR/CodeGen/stmt-expr.cpp
new file mode 100644
index 0000000000000..9e3911f638ba7
--- /dev/null
+++ b/clang/test/CIR/CodeGen/stmt-expr.cpp
@@ -0,0 +1,90 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases 
-fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases 
-fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases 
-emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+class A {
+public:
+  A(): x(0) {}
+  A(A &a) : x(a.x) {}
+  int x;
+  void Foo() {}
+};
+
+void test1() {
+  ({
+    A a;
+    a;
+  }).Foo();
+}
+
+// CIR: cir.func dso_local @_Z5test1v()
+// CIR:   cir.scope {
+// CIR:     %[[REF_TMP0:.+]] = cir.alloca !rec_A, !cir.ptr<!rec_A>, 
["ref.tmp0"]
+// CIR:     %[[TMP:.+]]      = cir.alloca !rec_A, !cir.ptr<!rec_A>, ["tmp"]
+// CIR:     cir.scope {
+// CIR:       %[[A:.+]] = cir.alloca !rec_A, !cir.ptr<!rec_A>, ["a", init]
+// CIR:       cir.call @_ZN1AC2Ev(%[[A]]) : (!cir.ptr<!rec_A>) -> ()
+// CIR:       cir.call @_ZN1AC2ERS_(%[[REF_TMP0]], %[[A]]) : 
(!cir.ptr<!rec_A>, !cir.ptr<!rec_A>) -> ()
+// CIR:     }
+// CIR:     cir.call @_ZN1A3FooEv(%[[REF_TMP0]]) : (!cir.ptr<!rec_A>) -> ()
+// CIR:   }
+// CIR:   cir.return
+
+// LLVM: define dso_local void @_Z5test1v()
+// LLVM:   %[[VAR1:.+]] = alloca %class.A, i64 1
+// LLVM:   %[[VAR2:.+]] = alloca %class.A, i64 1
+// LLVM:   %[[VAR3:.+]] = alloca %class.A, i64 1
+// LLVM:   br label %[[LBL4:.+]]
+// LLVM: [[LBL4]]:
+// LLVM:     br label %[[LBL5:.+]]
+// LLVM: [[LBL5]]:
+// LLVM:     call void @_ZN1AC2Ev(ptr %[[VAR3]])
+// LLVM:     call void @_ZN1AC2ERS_(ptr %[[VAR1]], ptr %[[VAR3]])
+// LLVM:     br label %[[LBL6:.+]]
+// LLVM: [[LBL6]]:
+// LLVM:     call void @_ZN1A3FooEv(ptr %[[VAR1]])
+// LLVM:     br label %[[LBL7:.+]]
+// LLVM: [[LBL7]]:
+// LLVM:     ret void
+
+// OGCG: define dso_local void @_Z5test1v()
+// OGCG: entry:
+// OGCG:   %[[REF_TMP:.+]] = alloca %class.A
+// OGCG:   %[[A:.+]]       = alloca %class.A
+// OGCG:   call void @_ZN1AC2Ev(ptr {{.*}} %[[A]])
+// OGCG:   call void @_ZN1AC2ERS_(ptr {{.*}} %[[REF_TMP]], ptr {{.*}} %[[A]])
+// OGCG:   call void @_ZN1A3FooEv(ptr {{.*}} %[[REF_TMP]])
+// OGCG:   ret void
+
+struct with_dtor {
+  ~with_dtor();
+};
+
+void cleanup() {
+  ({ with_dtor wd; });
+}
+
+// CIR: cir.func dso_local @_Z7cleanupv()
+// CIR:   cir.scope {
+// CIR:     %[[WD:.+]] = cir.alloca !rec_with_dtor, !cir.ptr<!rec_with_dtor>, 
["wd"]
+// CIR:     cir.call @_ZN9with_dtorD1Ev(%[[WD]]) nothrow : 
(!cir.ptr<!rec_with_dtor>) -> ()
+// CIR:   }
+// CIR:   cir.return
+
+// LLVM: define dso_local void @_Z7cleanupv()
+// LLVM:   %[[WD:.+]] = alloca %struct.with_dtor, i64 1
+// LLVM:   br label %[[LBL2:.+]]
+// LLVM: [[LBL2]]:
+// LLVM:     call void @_ZN9with_dtorD1Ev(ptr %[[WD]])
+// LLVM:     br label %[[LBL3:.+]]
+// LLVM: [[LBL3]]:
+// LLVM:     ret void
+
+// OGCG: define dso_local void @_Z7cleanupv()
+// OGCG: entry:
+// OGCG:   %[[WD:.+]] = alloca %struct.with_dtor
+// OGCG:   call void @_ZN9with_dtorD1Ev(ptr {{.*}} %[[WD]])
+// OGCG:   ret void

>From d21ca8ce12cbc1d9c68a2386b553851b2a377a33 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Fri, 15 Aug 2025 19:59:21 +0200
Subject: [PATCH 6/8] revert line change

---
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 95412a8da0ff7..039d53e1ee0ce 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1178,7 +1178,8 @@ static void pushTemporaryCleanup(CIRGenFunction &cgf,
     if (const auto *classDecl = dyn_cast<CXXRecordDecl>(
             rt->getOriginalDecl()->getDefinitionOrSelf())) {
       if (!classDecl->hasTrivialDestructor())
-        referenceTemporaryDtor = classDecl->getDestructor();
+        referenceTemporaryDtor =
+            classDecl->getDefinitionOrSelf()->getDestructor();
     }
   }
 

>From 69b5d4ee9dbd2dc0b36664cfada97963b09fc4e6 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Mon, 18 Aug 2025 00:09:41 +0200
Subject: [PATCH 7/8] introduce emitStmtWithResult

---
 clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 73 ++++++++++++++++------------
 1 file changed, 42 insertions(+), 31 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp 
b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index b73f9bf8b90d5..3b0eabe92284b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -25,6 +25,44 @@ using namespace clang;
 using namespace clang::CIRGen;
 using namespace cir;
 
+static mlir::LogicalResult emitStmtWithResult(CIRGenFunction &cgf,
+                                              const Stmt *exprResult,
+                                              AggValueSlot slot,
+                                              Address *lastValue) {
+  // We have to special case labels here. They are statements, but when put
+  // at the end of a statement expression, they yield the value of their
+  // subexpression. Handle this by walking through all labels we encounter,
+  // emitting them before we evaluate the subexpr.
+  // Similar issues arise for attributed statements.
+  while (!isa<Expr>(exprResult)) {
+    if (const auto *ls = dyn_cast<LabelStmt>(exprResult)) {
+      if (cgf.emitLabel(*ls->getDecl()).failed())
+        return mlir::failure();
+      exprResult = ls->getSubStmt();
+    } else if (const auto *as = dyn_cast<AttributedStmt>(exprResult)) {
+      // FIXME: Update this if we ever have attributes that affect the
+      // semantics of an expression.
+      exprResult = as->getSubStmt();
+    } else {
+      llvm_unreachable("Unknown value statement");
+    }
+  }
+
+  const Expr *e = cast<Expr>(exprResult);
+  QualType exprTy = e->getType();
+  if (cgf.hasAggregateEvaluationKind(exprTy)) {
+    cgf.emitAggExpr(e, slot);
+  } else {
+    // We can't return an RValue here because there might be cleanups at
+    // the end of the StmtExpr.  Because of that, we have to emit the result
+    // here into a temporary alloca.
+    cgf.emitAnyExprToMem(e, *lastValue, Qualifiers(),
+                         /*IsInit*/ false);
+  }
+
+  return mlir::success();
+}
+
 mlir::LogicalResult CIRGenFunction::emitCompoundStmtWithoutScope(
     const CompoundStmt &s, Address *lastValue, AggValueSlot slot) {
   mlir::LogicalResult result = mlir::success();
@@ -34,37 +72,10 @@ mlir::LogicalResult 
CIRGenFunction::emitCompoundStmtWithoutScope(
          "StmtExprResult");
 
   for (const Stmt *curStmt : s.body()) {
-    // We have to special case labels here. They are statements, but when put
-    // at the end of a statement expression, they yield the value of their
-    // subexpression. Handle this by walking through all labels we encounter,
-    // emitting them before we evaluate the subexpr.
-    // Similar issues arise for attributed statements.
-    if (lastValue && exprResult == curStmt) {
-      while (!isa<Expr>(exprResult)) {
-        if (const auto *ls = dyn_cast<LabelStmt>(exprResult)) {
-          if (emitLabel(*ls->getDecl()).failed())
-            return mlir::failure();
-          exprResult = ls->getSubStmt();
-        } else if (const auto *as = dyn_cast<AttributedStmt>(exprResult)) {
-          // FIXME: Update this if we ever have attributes that affect the
-          // semantics of an expression.
-          exprResult = as->getSubStmt();
-        } else {
-          llvm_unreachable("Unknown value statement");
-        }
-      }
-
-      const Expr *e = cast<Expr>(exprResult);
-      QualType exprTy = e->getType();
-      if (hasAggregateEvaluationKind(exprTy)) {
-        emitAggExpr(e, slot);
-      } else {
-        // We can't return an RValue here because there might be cleanups at
-        // the end of the StmtExpr.  Because of that, we have to emit the 
result
-        // here into a temporary alloca.
-        emitAnyExprToMem(e, *lastValue, Qualifiers(),
-                         /*IsInit*/ false);
-      }
+    const bool saveResult = lastValue && exprResult == curStmt;
+    if (saveResult) {
+      if (emitStmtWithResult(*this, exprResult, slot, lastValue).failed())
+        result = mlir::failure();
     } else {
       if (emitStmt(curStmt, /*useCurrentScope=*/false).failed())
         result = mlir::failure();

>From 3485be8b121183c9c936f0fc3ac664a7110cd91c Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Mon, 18 Aug 2025 16:51:05 +0200
Subject: [PATCH 8/8] Only look for temporary dtors if necessary

---
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 42 +++++++++++++++-------------
 1 file changed, 23 insertions(+), 19 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 039d53e1ee0ce..39b0fef62dcaa 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1170,29 +1170,33 @@ static void pushTemporaryCleanup(CIRGenFunction &cgf,
     return;
   }
 
-  CXXDestructorDecl *referenceTemporaryDtor = nullptr;
-  if (const clang::RecordType *rt = e->getType()
-                                        ->getBaseElementTypeUnsafe()
-                                        ->getAs<clang::RecordType>()) {
-    // Get the destructor for the reference temporary.
-    if (const auto *classDecl = dyn_cast<CXXRecordDecl>(
-            rt->getOriginalDecl()->getDefinitionOrSelf())) {
-      if (!classDecl->hasTrivialDestructor())
-        referenceTemporaryDtor =
-            classDecl->getDefinitionOrSelf()->getDestructor();
-    }
-  }
-
-  if (!referenceTemporaryDtor)
+  const QualType::DestructionKind dk = e->getType().isDestructedType();
+  if (dk == QualType::DK_none)
     return;
 
-  // Call the destructor for the temporary.
   switch (m->getStorageDuration()) {
   case SD_Static:
-  case SD_Thread:
-    cgf.cgm.errorNYI(e->getSourceRange(),
-                     "pushTemporaryCleanup: static/thread storage duration");
-    return;
+  case SD_Thread: {
+    CXXDestructorDecl *referenceTemporaryDtor = nullptr;
+    if (const clang::RecordType *rt = e->getType()
+                                          ->getBaseElementTypeUnsafe()
+                                          ->getAs<clang::RecordType>()) {
+      // Get the destructor for the reference temporary.
+      if (const auto *classDecl = dyn_cast<CXXRecordDecl>(
+              rt->getOriginalDecl()->getDefinitionOrSelf())) {
+        if (!classDecl->hasTrivialDestructor())
+          referenceTemporaryDtor =
+              classDecl->getDefinitionOrSelf()->getDestructor();
+      }
+    }
+
+    if (!referenceTemporaryDtor)
+      return;
+
+    cgf.cgm.errorNYI(e->getSourceRange(), "pushTemporaryCleanup: static/thread 
"
+                                          "storage duration with destructors");
+    break;
+  }
 
   case SD_FullExpression:
     cgf.pushDestroy(NormalAndEHCleanup, referenceTemporary, e->getType(),

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to