Author: Andy Kaylor
Date: 2025-03-12T14:38:19-07:00
New Revision: 64b94105d5c7a1a578dc2fb846747411686b666b

URL: 
https://github.com/llvm/llvm-project/commit/64b94105d5c7a1a578dc2fb846747411686b666b
DIFF: 
https://github.com/llvm/llvm-project/commit/64b94105d5c7a1a578dc2fb846747411686b666b.diff

LOG: [CIR] Upstream support for emitting ignored statements (#130869)

This adds support for emitting ClangIR for statements whose value is
ignored. The test case being added (CIR/CodeGen/basic.c) tests a few
more things. The "f1" test case is the only part that's immediately
relevant to this change, but the other cases were part of the same test
in the incubator and they are supported so I brought in the entire test.

Added: 
    clang/test/CIR/CodeGen/basic.c

Modified: 
    clang/include/clang/CIR/MissingFeatures.h
    clang/lib/CIR/CodeGen/CIRGenExpr.cpp
    clang/lib/CIR/CodeGen/CIRGenFunction.h
    clang/lib/CIR/CodeGen/CIRGenStmt.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index ddfe654009644..e8d3eff79d0b2 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -83,6 +83,7 @@ struct MissingFeatures {
   static bool emitNullabilityCheck() { return false; }
   static bool astVarDeclInterface() { return false; }
   static bool stackSaveOp() { return false; }
+  static bool aggValueSlot() { return false; }
 };
 
 } // namespace cir

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 07fb4cf8f1513..5b81fe172e645 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -165,6 +165,34 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr 
*e) {
   return LValue();
 }
 
+/// Emit code to compute the specified expression which
+/// can have any type.  The result is returned as an RValue struct.
+RValue CIRGenFunction::emitAnyExpr(const Expr *e) {
+  switch (CIRGenFunction::getEvaluationKind(e->getType())) {
+  case cir::TEK_Scalar:
+    return RValue::get(emitScalarExpr(e));
+  case cir::TEK_Complex:
+    cgm.errorNYI(e->getSourceRange(), "emitAnyExpr: complex type");
+    return RValue::get(nullptr);
+  case cir::TEK_Aggregate:
+    cgm.errorNYI(e->getSourceRange(), "emitAnyExpr: aggregate type");
+    return RValue::get(nullptr);
+  }
+  llvm_unreachable("bad evaluation kind");
+}
+
+/// Emit code to compute the specified expression, ignoring the result.
+void CIRGenFunction::emitIgnoredExpr(const Expr *e) {
+  if (e->isPRValue()) {
+    assert(!cir::MissingFeatures::aggValueSlot());
+    emitAnyExpr(e);
+    return;
+  }
+
+  // Just emit it as an l-value and drop the result.
+  emitLValue(e);
+}
+
 mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
                                        mlir::Location loc,
                                        CharUnits alignment) {

diff  --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 72445f62232a4..5ab882666f3e0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -154,6 +154,12 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   const clang::LangOptions &getLangOpts() const { return cgm.getLangOpts(); }
 
+  /// Emit code to compute the specified expression which can have any type. 
The
+  /// result is returned as an RValue struct. If this is an aggregate
+  /// expression, the aggloc/agglocvolatile arguments indicate where the result
+  /// should be returned.
+  RValue emitAnyExpr(const clang::Expr *e);
+
   void finishFunction(SourceLocation endLoc);
   mlir::LogicalResult emitFunctionBody(const clang::Stmt *body);
 
@@ -170,6 +176,10 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s);
 
+  /// Emit code to compute the specified expression,
+  /// ignoring the result.
+  void emitIgnoredExpr(const clang::Expr *e);
+
   mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s);
 
   mlir::LogicalResult emitReturnStmt(const clang::ReturnStmt &s);

diff  --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp 
b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index ed5d87a39704a..7a38f9838b290 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -55,11 +55,160 @@ mlir::LogicalResult CIRGenFunction::emitStmt(const Stmt *s,
   if (mlir::succeeded(emitSimpleStmt(s, useCurrentScope)))
     return mlir::success();
 
-  // Only a subset of simple statements are supported at the moment.  When more
-  // kinds of statements are supported, a
-  //     switch (s->getStmtClass()) {
-  // will be added here.
-  return mlir::failure();
+  switch (s->getStmtClass()) {
+
+#define STMT(Type, Base)
+#define ABSTRACT_STMT(Op)
+#define EXPR(Type, Base) case Stmt::Type##Class:
+#include "clang/AST/StmtNodes.inc"
+    {
+      // Remember the block we came in on.
+      mlir::Block *incoming = builder.getInsertionBlock();
+      assert(incoming && "expression emission must have an insertion point");
+
+      emitIgnoredExpr(cast<Expr>(s));
+
+      mlir::Block *outgoing = builder.getInsertionBlock();
+      assert(outgoing && "expression emission cleared block!");
+      return mlir::success();
+    }
+
+  case Stmt::OMPScopeDirectiveClass:
+  case Stmt::OMPErrorDirectiveClass:
+  case Stmt::NoStmtClass:
+  case Stmt::CXXCatchStmtClass:
+  case Stmt::SEHExceptStmtClass:
+  case Stmt::SEHFinallyStmtClass:
+  case Stmt::MSDependentExistsStmtClass:
+  case Stmt::NullStmtClass:
+  case Stmt::CompoundStmtClass:
+  case Stmt::DeclStmtClass:
+  case Stmt::LabelStmtClass:
+  case Stmt::AttributedStmtClass:
+  case Stmt::GotoStmtClass:
+  case Stmt::BreakStmtClass:
+  case Stmt::ContinueStmtClass:
+  case Stmt::DefaultStmtClass:
+  case Stmt::CaseStmtClass:
+  case Stmt::SEHLeaveStmtClass:
+  case Stmt::SYCLKernelCallStmtClass:
+  case Stmt::IfStmtClass:
+  case Stmt::SwitchStmtClass:
+  case Stmt::ForStmtClass:
+  case Stmt::WhileStmtClass:
+  case Stmt::DoStmtClass:
+  case Stmt::CoroutineBodyStmtClass:
+  case Stmt::CoreturnStmtClass:
+  case Stmt::CXXTryStmtClass:
+  case Stmt::CXXForRangeStmtClass:
+  case Stmt::IndirectGotoStmtClass:
+  case Stmt::ReturnStmtClass:
+  case Stmt::GCCAsmStmtClass:
+  case Stmt::MSAsmStmtClass:
+  case Stmt::OMPParallelDirectiveClass:
+  case Stmt::OMPTaskwaitDirectiveClass:
+  case Stmt::OMPTaskyieldDirectiveClass:
+  case Stmt::OMPBarrierDirectiveClass:
+  case Stmt::CapturedStmtClass:
+  case Stmt::ObjCAtTryStmtClass:
+  case Stmt::ObjCAtThrowStmtClass:
+  case Stmt::ObjCAtSynchronizedStmtClass:
+  case Stmt::ObjCForCollectionStmtClass:
+  case Stmt::ObjCAutoreleasePoolStmtClass:
+  case Stmt::SEHTryStmtClass:
+  case Stmt::OMPMetaDirectiveClass:
+  case Stmt::OMPCanonicalLoopClass:
+  case Stmt::OMPSimdDirectiveClass:
+  case Stmt::OMPTileDirectiveClass:
+  case Stmt::OMPUnrollDirectiveClass:
+  case Stmt::OMPForDirectiveClass:
+  case Stmt::OMPForSimdDirectiveClass:
+  case Stmt::OMPSectionsDirectiveClass:
+  case Stmt::OMPSectionDirectiveClass:
+  case Stmt::OMPSingleDirectiveClass:
+  case Stmt::OMPMasterDirectiveClass:
+  case Stmt::OMPCriticalDirectiveClass:
+  case Stmt::OMPParallelForDirectiveClass:
+  case Stmt::OMPParallelForSimdDirectiveClass:
+  case Stmt::OMPParallelMasterDirectiveClass:
+  case Stmt::OMPParallelSectionsDirectiveClass:
+  case Stmt::OMPTaskDirectiveClass:
+  case Stmt::OMPTaskgroupDirectiveClass:
+  case Stmt::OMPFlushDirectiveClass:
+  case Stmt::OMPDepobjDirectiveClass:
+  case Stmt::OMPScanDirectiveClass:
+  case Stmt::OMPOrderedDirectiveClass:
+  case Stmt::OMPAtomicDirectiveClass:
+  case Stmt::OMPTargetDirectiveClass:
+  case Stmt::OMPTeamsDirectiveClass:
+  case Stmt::OMPCancellationPointDirectiveClass:
+  case Stmt::OMPCancelDirectiveClass:
+  case Stmt::OMPTargetDataDirectiveClass:
+  case Stmt::OMPTargetEnterDataDirectiveClass:
+  case Stmt::OMPTargetExitDataDirectiveClass:
+  case Stmt::OMPTargetParallelDirectiveClass:
+  case Stmt::OMPTargetParallelForDirectiveClass:
+  case Stmt::OMPTaskLoopDirectiveClass:
+  case Stmt::OMPTaskLoopSimdDirectiveClass:
+  case Stmt::OMPMaskedTaskLoopDirectiveClass:
+  case Stmt::OMPMaskedTaskLoopSimdDirectiveClass:
+  case Stmt::OMPMasterTaskLoopDirectiveClass:
+  case Stmt::OMPMasterTaskLoopSimdDirectiveClass:
+  case Stmt::OMPParallelGenericLoopDirectiveClass:
+  case Stmt::OMPParallelMaskedDirectiveClass:
+  case Stmt::OMPParallelMaskedTaskLoopDirectiveClass:
+  case Stmt::OMPParallelMaskedTaskLoopSimdDirectiveClass:
+  case Stmt::OMPParallelMasterTaskLoopDirectiveClass:
+  case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass:
+  case Stmt::OMPDistributeDirectiveClass:
+  case Stmt::OMPDistributeParallelForDirectiveClass:
+  case Stmt::OMPDistributeParallelForSimdDirectiveClass:
+  case Stmt::OMPDistributeSimdDirectiveClass:
+  case Stmt::OMPTargetParallelGenericLoopDirectiveClass:
+  case Stmt::OMPTargetParallelForSimdDirectiveClass:
+  case Stmt::OMPTargetSimdDirectiveClass:
+  case Stmt::OMPTargetTeamsGenericLoopDirectiveClass:
+  case Stmt::OMPTargetUpdateDirectiveClass:
+  case Stmt::OMPTeamsDistributeDirectiveClass:
+  case Stmt::OMPTeamsDistributeSimdDirectiveClass:
+  case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass:
+  case Stmt::OMPTeamsDistributeParallelForDirectiveClass:
+  case Stmt::OMPTeamsGenericLoopDirectiveClass:
+  case Stmt::OMPTargetTeamsDirectiveClass:
+  case Stmt::OMPTargetTeamsDistributeDirectiveClass:
+  case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
+  case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
+  case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
+  case Stmt::OMPInteropDirectiveClass:
+  case Stmt::OMPDispatchDirectiveClass:
+  case Stmt::OMPGenericLoopDirectiveClass:
+  case Stmt::OMPReverseDirectiveClass:
+  case Stmt::OMPInterchangeDirectiveClass:
+  case Stmt::OMPAssumeDirectiveClass:
+  case Stmt::OMPMaskedDirectiveClass:
+  case Stmt::OMPStripeDirectiveClass:
+  case Stmt::OpenACCComputeConstructClass:
+  case Stmt::OpenACCLoopConstructClass:
+  case Stmt::OpenACCCombinedConstructClass:
+  case Stmt::OpenACCDataConstructClass:
+  case Stmt::OpenACCEnterDataConstructClass:
+  case Stmt::OpenACCExitDataConstructClass:
+  case Stmt::OpenACCHostDataConstructClass:
+  case Stmt::OpenACCWaitConstructClass:
+  case Stmt::OpenACCInitConstructClass:
+  case Stmt::OpenACCShutdownConstructClass:
+  case Stmt::OpenACCSetConstructClass:
+  case Stmt::OpenACCUpdateConstructClass:
+  case Stmt::OpenACCCacheConstructClass:
+  case Stmt::OpenACCAtomicConstructClass:
+  case Stmt::ObjCAtCatchStmtClass:
+  case Stmt::ObjCAtFinallyStmtClass:
+    cgm.errorNYI(s->getSourceRange(),
+                 std::string("emitStmt: ") + s->getStmtClassName());
+    return mlir::failure();
+  }
+
+  llvm_unreachable("Unexpected statement class");
 }
 
 mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s,
@@ -106,16 +255,11 @@ mlir::LogicalResult CIRGenFunction::emitReturnStmt(const 
ReturnStmt &s) {
     // this section will do nothing.  But for now a ReturnOp is necessary.
     builder.create<ReturnOp>(loc);
   } else if (rv->getType()->isVoidType()) {
-    // No return value. Emit the return expression for its side effects.
-    // TODO(CIR): Once emitAnyExpr(e) has been upstreamed, get rid of the check
-    // and just call emitAnyExpr(rv) here.
-    if (CIRGenFunction::hasScalarEvaluationKind(rv->getType())) {
-      emitScalarExpr(rv);
-    } else {
-      getCIRGenModule().errorNYI(s.getSourceRange(),
-                                 "non-scalar function return type");
+    // Make sure not to return anything, but evaluate the expression
+    // for side effects.
+    if (rv) {
+      emitAnyExpr(rv);
     }
-    builder.create<ReturnOp>(loc);
   } else if (fnRetTy->isReferenceType()) {
     getCIRGenModule().errorNYI(s.getSourceRange(),
                                "function return type that is a reference");

diff  --git a/clang/test/CIR/CodeGen/basic.c b/clang/test/CIR/CodeGen/basic.c
new file mode 100644
index 0000000000000..754f11f1361ba
--- /dev/null
+++ b/clang/test/CIR/CodeGen/basic.c
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-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 -Wno-unused-value 
-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 -Wno-unused-value 
-emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+int f1(int i);
+
+int f1(int i) {
+  i;
+  return i;
+}
+
+//      CIR: module
+// CIR-NEXT: cir.func @f1(%arg0: !cir.int<s, 32> loc({{.*}})) -> !cir.int<s, 
32>
+// CIR-NEXT:   %[[I_PTR:.*]] = cir.alloca !cir.int<s, 32>, 
!cir.ptr<!cir.int<s, 32>>, ["i", init] {alignment = 4 : i64}
+// CIR-NEXT:   cir.store %arg0, %[[I_PTR]] : !cir.int<s, 32>, 
!cir.ptr<!cir.int<s, 32>>
+// CIR-NEXT:   %[[I_IGNORED:.*]] = cir.load %[[I_PTR]] : !cir.ptr<!cir.int<s, 
32>>, !cir.int<s, 32>
+// CIR-NEXT:   %[[I:.*]] = cir.load %[[I_PTR]] : !cir.ptr<!cir.int<s, 32>>, 
!cir.int<s, 32>
+// CIR-NEXT:   cir.return %[[I]] : !cir.int<s, 32>
+
+//      LLVM: define i32 @f1(i32 %[[I:.*]])
+// LLVM-NEXT:   %[[I_PTR:.*]] = alloca i32, i64 1, align 4
+// LLVM-NEXT:   store i32 %[[I]], ptr %[[I_PTR]], align 4
+// LLVM-NEXT:   %[[I_IGNORED:.*]] = load i32, ptr %[[I_PTR]], align 4
+// LLVM-NEXT:   %[[I:.*]] = load i32, ptr %[[I_PTR]], align 4
+// LLVM-NEXT:   ret i32 %[[I]]
+
+//      OGCG: define{{.*}} i32 @f1(i32 noundef %[[I:.*]])
+// OGCG-NEXT: entry:
+// OGCG-NEXT:   %[[I_PTR:.*]] = alloca i32, align 4
+// OGCG-NEXT:   store i32 %[[I]], ptr %[[I_PTR]], align 4
+// OGCG-NEXT:   %[[I_IGNORED:.*]] = load i32, ptr %[[I_PTR]], align 4
+// OGCG-NEXT:   %[[I:.*]] = load i32, ptr %[[I_PTR]], align 4
+// OGCG-NEXT:   ret i32 %[[I]]
+
+int f2(void) { return 3; }
+
+//      CIR: cir.func @f2() -> !cir.int<s, 32>
+// CIR-NEXT:   %[[THREE:.*]] = cir.const #cir.int<3> : !cir.int<s, 32>
+// CIR-NEXT:   cir.return %[[THREE]] : !cir.int<s, 32>
+
+//      LLVM: define i32 @f2()
+// LLVM-NEXT:   ret i32 3
+
+//      OGCG: define{{.*}} i32 @f2()
+// OGCG-NEXT: entry:
+// OGCG-NEXT:   ret i32 3
+
+int f3(void) {
+  int i = 3;
+  return i;
+}
+
+//      CIR: cir.func @f3() -> !cir.int<s, 32>
+// CIR-NEXT:   %[[I_PTR:.*]] = cir.alloca !cir.int<s, 32>, 
!cir.ptr<!cir.int<s, 32>>, ["i", init] {alignment = 4 : i64}
+// CIR-NEXT:   %[[THREE:.*]] = cir.const #cir.int<3> : !cir.int<s, 32>
+// CIR-NEXT:   cir.store %[[THREE]], %[[I_PTR]] : !cir.int<s, 32>, 
!cir.ptr<!cir.int<s, 32>>
+// CIR-NEXT:   %[[I:.*]] = cir.load %[[I_PTR]] : !cir.ptr<!cir.int<s, 32>>, 
!cir.int<s, 32>
+// CIR-NEXT:   cir.return %[[I]] : !cir.int<s, 32>
+
+//      LLVM: define i32 @f3()
+// LLVM-NEXT:   %[[I_PTR:.*]] = alloca i32, i64 1, align 4
+// LLVM-NEXT:   store i32 3, ptr %[[I_PTR]], align 4
+// LLVM-NEXT:   %[[I:.*]] = load i32, ptr %[[I_PTR]], align 4
+// LLVM-NEXT:   ret i32 %[[I]]
+
+//      OGCG: define{{.*}} i32 @f3
+// OGCG-NEXT: entry:
+// OGCG-NEXT:   %[[I_PTR:.*]] = alloca i32, align 4
+// OGCG-NEXT:   store i32 3, ptr %[[I_PTR]], align 4
+// OGCG-NEXT:   %[[I:.*]] = load i32, ptr %[[I_PTR]], align 4
+// OGCG-NEXT:   ret i32 %[[I]]


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

Reply via email to