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

>From 34b219ffb5412599ea7548cc7579bff454343b95 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Thu, 14 Aug 2025 22:44:35 +0200
Subject: [PATCH] [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          |  70 ++++++--
 clang/test/CIR/CodeGen/statement-exprs.c      | 166 ++++++++++++++++++
 5 files changed, 275 insertions(+), 12 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 c3e77c99cca35..1ef82a2edf9d3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1157,9 +1157,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);
@@ -1396,6 +1401,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 d1e4a14824011..92cce9c5f9ba5 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,30 +24,79 @@ 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());
   mlir::OpBuilder::InsertPoint scopeInsPt;
   builder.create<cir::ScopeOp>(
-      scopeLoc, [&](mlir::OpBuilder &b, mlir::Type &type, mlir::Location loc) {
+      scopeLoc, /*scopeBuilder=*/
+      [&](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());
-    emitCompoundStmtWithoutScope(s);
+    LexicalScope lexScope{*this, scopeLoc, builder.getInsertionBlock()};
+    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: }

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

Reply via email to