Skip to content

Conversation

erichkeane
Copy link
Collaborator

These four operators have an initial value of 0, so they are able to use C/C++ 'zero init'. This patch adds the infrastructure to the Sema init calculations to differentiate based on the reduction operator, then enables emission of the inits in CodeGen (which should work for all
inits, once generated).

The rest of this test is just updating validation to make sure that the inits happen correctly for all 4 operators.

These four operators have an initial value of 0, so they are able to use
C/C++ 'zero init'.  This patch adds the infrastructure to the Sema init
calculations to differentiate based on the reduction operator, then
enables emission of the inits in CodeGen (which should work for all
    inits, once generated).

The rest of this test is just updating validation to make sure that the
inits happen correctly for all 4 operators.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" ClangIR Anything related to the ClangIR project labels Aug 28, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 28, 2025

@llvm/pr-subscribers-clangir

Author: Erich Keane (erichkeane)

Changes

These four operators have an initial value of 0, so they are able to use C/C++ 'zero init'. This patch adds the infrastructure to the Sema init calculations to differentiate based on the reduction operator, then enables emission of the inits in CodeGen (which should work for all
inits, once generated).

The rest of this test is just updating validation to make sure that the inits happen correctly for all 4 operators.


Patch is 366.11 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/155924.diff

23 Files Affected:

  • (modified) clang/include/clang/Sema/SemaOpenACC.h (+8-1)
  • (modified) clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp (+15-7)
  • (modified) clang/lib/Sema/SemaOpenACC.cpp (+47-2)
  • (modified) clang/lib/Sema/SemaOpenACCClause.cpp (+3-2)
  • (modified) clang/lib/Sema/TreeTransform.h (+5-5)
  • (modified) clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-default-ops.cpp (+196-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-float.cpp (+120-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-inline-ops.cpp (+200-20)
  • (modified) clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-int.cpp (+112-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-outline-ops.cpp (+200-20)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-default-ops.c (+168-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-default-ops.cpp (+197-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-float.c (+120-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-float.cpp (+120-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-inline-ops.cpp (+200-20)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-int.c (+112-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-int.cpp (+109-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-outline-ops.cpp (+200-20)
  • (modified) clang/test/CIR/CodeGenOpenACC/loop-reduction-clause-default-ops.cpp (+196-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/loop-reduction-clause-float.cpp (+120-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/loop-reduction-clause-inline-ops.cpp (+200-20)
  • (modified) clang/test/CIR/CodeGenOpenACC/loop-reduction-clause-int.cpp (+112-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/loop-reduction-clause-outline-ops.cpp (+200-20)
diff --git a/clang/include/clang/Sema/SemaOpenACC.h b/clang/include/clang/Sema/SemaOpenACC.h
index e7b62aafc060b..42e86582c3b06 100644
--- a/clang/include/clang/Sema/SemaOpenACC.h
+++ b/clang/include/clang/Sema/SemaOpenACC.h
@@ -244,7 +244,14 @@ class SemaOpenACC : public SemaBase {
   //  'temporary' created for the init (in the case of a copy), such as with
   //  firstprivate.
   std::pair<VarDecl *, VarDecl *> CreateInitRecipe(OpenACCClauseKind CK,
-                                                   const Expr *VarExpr);
+                                                   const Expr *VarExpr) {
+    assert(CK != OpenACCClauseKind::Reduction);
+    return CreateInitRecipe(CK, OpenACCReductionOperator::Invalid, VarExpr);
+  }
+  std::pair<VarDecl *, VarDecl *>
+  CreateInitRecipe(OpenACCClauseKind CK,
+                   OpenACCReductionOperator ReductionOperator,
+                   const Expr *VarExpr);
 
 public:
   ComputeConstructInfo &getActiveComputeConstructInfo() {
diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp b/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp
index 0f2ca2dbe75c7..37533368af4b2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp
@@ -425,6 +425,7 @@ class OpenACCClauseCIREmitter final
         &recipe.getCopyRegion(), recipe.getCopyRegion().end(),
         {mainOp.getType(), mainOp.getType()}, {loc, loc});
     builder.setInsertionPointToEnd(&recipe.getCopyRegion().back());
+    CIRGenFunction::LexicalScope ls(cgf, loc, block);
 
     mlir::BlockArgument fromArg = block->getArgument(0);
     mlir::BlockArgument toArg = block->getArgument(1);
@@ -457,11 +458,6 @@ class OpenACCClauseCIREmitter final
                             RecipeTy recipe, const VarDecl *varRecipe,
                             const VarDecl *temporary) {
     assert(varRecipe && "Required recipe variable not set?");
-    if constexpr (std::is_same_v<RecipeTy, mlir::acc::ReductionRecipeOp>) {
-      // We haven't implemented the 'init' recipe for Reduction yet, so NYI
-      // it.
-      cgf.cgm.errorNYI(exprRange, "OpenACC Reduction recipe init");
-    }
 
     CIRGenFunction::AutoVarEmission tempDeclEmission{
         CIRGenFunction::AutoVarEmission::invalid()};
@@ -469,9 +465,12 @@ class OpenACCClauseCIREmitter final
 
     // Do the 'init' section of the recipe IR, which does an alloca, then the
     // initialization (except for firstprivate).
-    builder.createBlock(&recipe.getInitRegion(), recipe.getInitRegion().end(),
-                        {mainOp.getType()}, {loc});
+    mlir::Block *block = builder.createBlock(&recipe.getInitRegion(),
+                                             recipe.getInitRegion().end(),
+                                             {mainOp.getType()}, {loc});
     builder.setInsertionPointToEnd(&recipe.getInitRegion().back());
+    CIRGenFunction::LexicalScope ls(cgf, loc, block);
+
     tempDeclEmission =
         cgf.emitAutoVarAlloca(*varRecipe, builder.saveInsertionPoint());
 
@@ -496,6 +495,13 @@ class OpenACCClauseCIREmitter final
         cgf.cgm.errorNYI(exprRange, "private default-init recipe");
       }
       cgf.emitAutoVarInit(tempDeclEmission);
+    } else if constexpr (std::is_same_v<RecipeTy,
+                                        mlir::acc::ReductionRecipeOp>) {
+      // Unlike Private, the recipe here is always required as it has to do
+      // init, not just 'default' init.
+      if (!varRecipe->getInit())
+        cgf.cgm.errorNYI(exprRange, "reduction init recipe");
+      cgf.emitAutoVarInit(tempDeclEmission);
     }
 
     mlir::acc::YieldOp::create(builder, locEnd);
@@ -527,6 +533,7 @@ class OpenACCClauseCIREmitter final
         &recipe.getCombinerRegion(), recipe.getCombinerRegion().end(),
         {mainOp.getType(), mainOp.getType()}, {loc, loc});
     builder.setInsertionPointToEnd(&recipe.getCombinerRegion().back());
+    CIRGenFunction::LexicalScope ls(cgf, loc, block);
 
     mlir::BlockArgument lhsArg = block->getArgument(0);
 
@@ -544,6 +551,7 @@ class OpenACCClauseCIREmitter final
     mlir::Block *block = builder.createBlock(
         &destroyRegion, destroyRegion.end(), {mainOp.getType()}, {loc});
     builder.setInsertionPointToEnd(&destroyRegion.back());
+    CIRGenFunction::LexicalScope ls(cgf, loc, block);
 
     mlir::Type elementTy =
         mlir::cast<cir::PointerType>(mainOp.getType()).getPointee();
diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp
index 1457522406531..5386682b3a991 100644
--- a/clang/lib/Sema/SemaOpenACC.cpp
+++ b/clang/lib/Sema/SemaOpenACC.cpp
@@ -2590,7 +2590,9 @@ SemaOpenACC::ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc) {
 }
 
 std::pair<VarDecl *, VarDecl *>
-SemaOpenACC::CreateInitRecipe(OpenACCClauseKind CK, const Expr *VarExpr) {
+SemaOpenACC::CreateInitRecipe(OpenACCClauseKind CK,
+                              OpenACCReductionOperator ReductionOperator,
+                              const Expr *VarExpr) {
   // Strip off any array subscripts/array section exprs to get to the type of
   // the variable.
   while (isa_and_present<ArraySectionExpr, ArraySubscriptExpr>(VarExpr)) {
@@ -2722,7 +2724,50 @@ SemaOpenACC::CreateInitRecipe(OpenACCClauseKind CK, const Expr *VarExpr) {
                                      /*TreatUnavailableAsInvalid=*/false);
       Init = InitSeq.Perform(SemaRef.SemaRef, Entity, Kind, InitExpr, &VarTy);
     } else if (CK == OpenACCClauseKind::Reduction) {
-      // TODO: OpenACC: Implement this for whatever reduction needs.
+      // How we initialize the reduction variable depends on the operator used,
+      // according to the chart in OpenACC 3.3 section 2.6.15.
+
+      switch (ReductionOperator) {
+      case OpenACCReductionOperator::Invalid:
+        // This can only happen when there is an error, and since these inits
+        // are used for code generation, we can just ignore/not bother doing any
+        // initialization here.
+        break;
+      case OpenACCReductionOperator::Multiplication:
+      case OpenACCReductionOperator::Max:
+      case OpenACCReductionOperator::Min:
+      case OpenACCReductionOperator::BitwiseAnd:
+      case OpenACCReductionOperator::And:
+        // TODO: OpenACC: figure out init for these.
+        break;
+
+      case OpenACCReductionOperator::Addition:
+      case OpenACCReductionOperator::BitwiseOr:
+      case OpenACCReductionOperator::BitwiseXOr:
+      case OpenACCReductionOperator::Or: {
+        // +, |, ^, and || all use 0 for their initializers, so we can just
+        // use 'zero init' here and not bother with the rest of the
+        // array/compound type/etc contents.
+        auto *Zero = IntegerLiteral::Create(
+            getASTContext(),
+            llvm::APInt(getASTContext().getTypeSize(getASTContext().IntTy), 0),
+            getASTContext().IntTy, VarExpr->getBeginLoc());
+        Expr *InitExpr = new (getASTContext())
+            InitListExpr(getASTContext(), VarExpr->getBeginLoc(), Zero,
+                         VarExpr->getEndLoc());
+        // we set this to void so that the initialization sequence generation
+        // will get this type correct/etc.
+        InitExpr->setType(getASTContext().VoidTy);
+
+        InitializationKind Kind = InitializationKind::CreateForInit(
+            Recipe->getLocation(), /*DirectInit=*/true, InitExpr);
+        InitializationSequence InitSeq(SemaRef.SemaRef, Entity, Kind, InitExpr,
+                                       /*TopLevelOfInitList=*/false,
+                                       /*TreatUnavailableAsInvalid=*/false);
+        Init = InitSeq.Perform(SemaRef.SemaRef, Entity, Kind, InitExpr, &VarTy);
+        break;
+      }
+      }
     } else {
       llvm_unreachable("Unknown clause kind in CreateInitRecipe");
     }
diff --git a/clang/lib/Sema/SemaOpenACCClause.cpp b/clang/lib/Sema/SemaOpenACCClause.cpp
index 43ae4f4d3011c..677b74fb4e336 100644
--- a/clang/lib/Sema/SemaOpenACCClause.cpp
+++ b/clang/lib/Sema/SemaOpenACCClause.cpp
@@ -1784,9 +1784,10 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitReductionClause(
       ValidVars.push_back(Res.get());
 
       VarDecl *InitRecipe =
-          SemaRef.CreateInitRecipe(OpenACCClauseKind::Reduction, Res.get())
+          SemaRef
+              .CreateInitRecipe(OpenACCClauseKind::Reduction,
+                                Clause.getReductionOp(), Res.get())
               .first;
-      // TODO: OpenACC: Create the reduction operation recipe here too.
       Recipes.push_back({InitRecipe});
     }
   }
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 827e568c83b38..aa1bb3232d6fa 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -12430,11 +12430,11 @@ void OpenACCClauseTransform<Derived>::VisitReductionClause(
       if (OrigRecipes.RecipeDecl)
         InitRecipe = OrigRecipes.RecipeDecl;
        else
-         InitRecipe =
-             Self.getSema()
-                 .OpenACC()
-                 .CreateInitRecipe(OpenACCClauseKind::Reduction, Res.get())
-                 .first;
+         InitRecipe = Self.getSema()
+                          .OpenACC()
+                          .CreateInitRecipe(OpenACCClauseKind::Reduction,
+                                            C.getReductionOp(), Res.get())
+                          .first;
 
       Recipes.push_back({InitRecipe});
     }
diff --git a/clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-default-ops.cpp b/clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-default-ops.cpp
index a3f62b4af3c27..7469c7249caab 100644
--- a/clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-default-ops.cpp
+++ b/clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-default-ops.cpp
@@ -9,9 +9,45 @@ struct DefaultOperators {
 
 // CHECK: acc.reduction.recipe @reduction_lor__ZTSA5_16DefaultOperators : !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> reduction_operator <lor> init {
 // CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>{{.*}})
-// CHECK-NEXT: cir.alloca !cir.array<!rec_DefaultOperators x 5>, !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>, ["openacc.reduction.init"]
-// TODO OpenACC: Expecting an initialization to... SOME value here.
+// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca !cir.array<!rec_DefaultOperators x 5>, !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>, ["openacc.reduction.init", init]
+// CHECK-NEXT: %[[TEMP_ITR:.*]] = cir.alloca !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, ["arrayinit.temp"]
+// CHECK-NEXT: %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[ALLOCA]] : !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[GET_I:.*]] = cir.get_member %[[DECAY]][0] {name = "i"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!s32i>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_I]] : !s32i, !cir.ptr<!s32i>
+// CHECK-NEXT: %[[GET_F:.*]] = cir.get_member %[[DECAY]][1] {name = "f"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.float
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_F]] : !cir.float, !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[GET_D:.*]] = cir.get_member %[[DECAY]][2] {name = "d"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.double
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_D]] : !cir.double, !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[FIRST_IDX:.*]] = cir.const #cir.int<1> : !s64i
+// CHECK-NEXT: %[[ITEM_ONE:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_DefaultOperators>, %[[FIRST_IDX]] : !s64i), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: cir.store {{.*}} %[[ITEM_ONE]], %[[TEMP_ITR]] : !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>
+// CHECK-NEXT: %[[LAST_IDX:.*]] = cir.const #cir.int<5> : !s64i
+// CHECK-NEXT: %[[END_ITR:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_DefaultOperators>, %[[LAST_IDX]] : !s64i), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: cir.do {
+// CHECK-NEXT: %[[TEMP_LOAD:.*]] = cir.load {{.*}} %[[TEMP_ITR]] : !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[GET_I:.*]] = cir.get_member %[[TEMP_LOAD]][0] {name = "i"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!s32i>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_I]] : !s32i, !cir.ptr<!s32i>
+// CHECK-NEXT: %[[GET_F:.*]] = cir.get_member %[[TEMP_LOAD]][1] {name = "f"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.float
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_F]] : !cir.float, !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[GET_D:.*]] = cir.get_member %[[TEMP_LOAD]][2] {name = "d"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.double
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_D]] : !cir.double, !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> : !s64i
+// CHECK-NEXT: %[[NEXT_ITEM:.*]] = cir.ptr_stride(%[[TEMP_LOAD]] : !cir.ptr<!rec_DefaultOperators>, %[[ONE]] : !s64i), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: cir.store {{.*}} %[[NEXT_ITEM]], %[[TEMP_ITR]] : !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>
+// CHECK-NEXT: cir.yield
+// CHECK-NEXT: } while {
+// CHECK-NEXT: %[[TEMP_LOAD:.*]] = cir.load {{.*}} %[[TEMP_ITR]] : !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[CMP:.*]] = cir.cmp(ne, %[[TEMP_LOAD]], %[[END_ITR]]) : !cir.ptr<!rec_DefaultOperators>, !cir.bool
+// CHECK-NEXT: cir.condition(%[[CMP]]) 
+// CHECK-NEXT: }
 // CHECK-NEXT: acc.yield
+//
 // CHECK-NEXT: } combiner {
 // CHECK-NEXT: ^bb0(%[[LHSARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> {{.*}}, %[[RHSARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> {{.*}})
 // TODO OpenACC: Expecting combination operation here
@@ -31,9 +67,45 @@ struct DefaultOperators {
 
 // CHECK-NEXT: acc.reduction.recipe @reduction_xor__ZTSA5_16DefaultOperators : !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> reduction_operator <xor> init {
 // CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>{{.*}})
-// CHECK-NEXT: cir.alloca !cir.array<!rec_DefaultOperators x 5>, !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>, ["openacc.reduction.init"]
-// TODO OpenACC: Expecting an initialization to... SOME value here.
+// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca !cir.array<!rec_DefaultOperators x 5>, !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>, ["openacc.reduction.init", init]
+// CHECK-NEXT: %[[TEMP_ITR:.*]] = cir.alloca !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, ["arrayinit.temp"]
+// CHECK-NEXT: %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[ALLOCA]] : !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[GET_I:.*]] = cir.get_member %[[DECAY]][0] {name = "i"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!s32i>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_I]] : !s32i, !cir.ptr<!s32i>
+// CHECK-NEXT: %[[GET_F:.*]] = cir.get_member %[[DECAY]][1] {name = "f"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.float
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_F]] : !cir.float, !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[GET_D:.*]] = cir.get_member %[[DECAY]][2] {name = "d"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.double
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_D]] : !cir.double, !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[FIRST_IDX:.*]] = cir.const #cir.int<1> : !s64i
+// CHECK-NEXT: %[[ITEM_ONE:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_DefaultOperators>, %[[FIRST_IDX]] : !s64i), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: cir.store {{.*}} %[[ITEM_ONE]], %[[TEMP_ITR]] : !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>
+// CHECK-NEXT: %[[LAST_IDX:.*]] = cir.const #cir.int<5> : !s64i
+// CHECK-NEXT: %[[END_ITR:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_DefaultOperators>, %[[LAST_IDX]] : !s64i), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: cir.do {
+// CHECK-NEXT: %[[TEMP_LOAD:.*]] = cir.load {{.*}} %[[TEMP_ITR]] : !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[GET_I:.*]] = cir.get_member %[[TEMP_LOAD]][0] {name = "i"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!s32i>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_I]] : !s32i, !cir.ptr<!s32i>
+// CHECK-NEXT: %[[GET_F:.*]] = cir.get_member %[[TEMP_LOAD]][1] {name = "f"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.float
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_F]] : !cir.float, !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[GET_D:.*]] = cir.get_member %[[TEMP_LOAD]][2] {name = "d"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.double
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_D]] : !cir.double, !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> : !s64i
+// CHECK-NEXT: %[[NEXT_ITEM:.*]] = cir.ptr_stride(%[[TEMP_LOAD]] : !cir.ptr<!rec_DefaultOperators>, %[[ONE]] : !s64i), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: cir.store {{.*}} %[[NEXT_ITEM]], %[[TEMP_ITR]] : !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>
+// CHECK-NEXT: cir.yield
+// CHECK-NEXT: } while {
+// CHECK-NEXT: %[[TEMP_LOAD:.*]] = cir.load {{.*}} %[[TEMP_ITR]] : !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[CMP:.*]] = cir.cmp(ne, %[[TEMP_LOAD]], %[[END_ITR]]) : !cir.ptr<!rec_DefaultOperators>, !cir.bool
+// CHECK-NEXT: cir.condition(%[[CMP]]) 
+// CHECK-NEXT: }
 // CHECK-NEXT: acc.yield
+//
 // CHECK-NEXT: } combiner {
 // CHECK-NEXT: ^bb0(%[[LHSARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> {{.*}}, %[[RHSARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> {{.*}})
 // TODO OpenACC: Expecting combination operation here
@@ -42,9 +114,45 @@ struct DefaultOperators {
 
 // CHECK-NEXT: acc.reduction.recipe @reduction_ior__ZTSA5_16DefaultOperators : !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> reduction_operator <ior> init {
 // CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>{{.*}})
-// CHECK-NEXT: cir.alloca !cir.array<!rec_DefaultOperators x 5>, !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>, ["openacc.reduction.init"]
-// TODO OpenACC: Expecting an initialization to... SOME value here.
+// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca !cir.array<!rec_DefaultOperators x 5>, !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>, ["openacc.reduction.init", init]
+// CHECK-NEXT: %[[TEMP_ITR:.*]] = cir.alloca !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, ["arrayinit.temp"]
+// CHECK-NEXT: %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[ALLOCA]] : !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[GET_I:.*]] = cir.get_member %[[DECAY]][0] {name = "i"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!s32i>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_I]] : !s32i, !cir.ptr<!s32i>
+// CHECK-NEXT: %[[GET_F:.*]] = cir.get_member %[[DECAY]][1] {name = "f"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.float
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_F]] : !cir.float, !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[GET_D:.*]] = cir.get_member %[[DECAY]][2] {name = "d"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.double
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_D]] : !cir.d...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Aug 28, 2025

@llvm/pr-subscribers-clang

Author: Erich Keane (erichkeane)

Changes

These four operators have an initial value of 0, so they are able to use C/C++ 'zero init'. This patch adds the infrastructure to the Sema init calculations to differentiate based on the reduction operator, then enables emission of the inits in CodeGen (which should work for all
inits, once generated).

The rest of this test is just updating validation to make sure that the inits happen correctly for all 4 operators.


Patch is 366.11 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/155924.diff

23 Files Affected:

  • (modified) clang/include/clang/Sema/SemaOpenACC.h (+8-1)
  • (modified) clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp (+15-7)
  • (modified) clang/lib/Sema/SemaOpenACC.cpp (+47-2)
  • (modified) clang/lib/Sema/SemaOpenACCClause.cpp (+3-2)
  • (modified) clang/lib/Sema/TreeTransform.h (+5-5)
  • (modified) clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-default-ops.cpp (+196-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-float.cpp (+120-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-inline-ops.cpp (+200-20)
  • (modified) clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-int.cpp (+112-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-outline-ops.cpp (+200-20)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-default-ops.c (+168-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-default-ops.cpp (+197-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-float.c (+120-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-float.cpp (+120-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-inline-ops.cpp (+200-20)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-int.c (+112-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-int.cpp (+109-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-outline-ops.cpp (+200-20)
  • (modified) clang/test/CIR/CodeGenOpenACC/loop-reduction-clause-default-ops.cpp (+196-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/loop-reduction-clause-float.cpp (+120-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/loop-reduction-clause-inline-ops.cpp (+200-20)
  • (modified) clang/test/CIR/CodeGenOpenACC/loop-reduction-clause-int.cpp (+112-16)
  • (modified) clang/test/CIR/CodeGenOpenACC/loop-reduction-clause-outline-ops.cpp (+200-20)
diff --git a/clang/include/clang/Sema/SemaOpenACC.h b/clang/include/clang/Sema/SemaOpenACC.h
index e7b62aafc060b..42e86582c3b06 100644
--- a/clang/include/clang/Sema/SemaOpenACC.h
+++ b/clang/include/clang/Sema/SemaOpenACC.h
@@ -244,7 +244,14 @@ class SemaOpenACC : public SemaBase {
   //  'temporary' created for the init (in the case of a copy), such as with
   //  firstprivate.
   std::pair<VarDecl *, VarDecl *> CreateInitRecipe(OpenACCClauseKind CK,
-                                                   const Expr *VarExpr);
+                                                   const Expr *VarExpr) {
+    assert(CK != OpenACCClauseKind::Reduction);
+    return CreateInitRecipe(CK, OpenACCReductionOperator::Invalid, VarExpr);
+  }
+  std::pair<VarDecl *, VarDecl *>
+  CreateInitRecipe(OpenACCClauseKind CK,
+                   OpenACCReductionOperator ReductionOperator,
+                   const Expr *VarExpr);
 
 public:
   ComputeConstructInfo &getActiveComputeConstructInfo() {
diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp b/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp
index 0f2ca2dbe75c7..37533368af4b2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenOpenACCClause.cpp
@@ -425,6 +425,7 @@ class OpenACCClauseCIREmitter final
         &recipe.getCopyRegion(), recipe.getCopyRegion().end(),
         {mainOp.getType(), mainOp.getType()}, {loc, loc});
     builder.setInsertionPointToEnd(&recipe.getCopyRegion().back());
+    CIRGenFunction::LexicalScope ls(cgf, loc, block);
 
     mlir::BlockArgument fromArg = block->getArgument(0);
     mlir::BlockArgument toArg = block->getArgument(1);
@@ -457,11 +458,6 @@ class OpenACCClauseCIREmitter final
                             RecipeTy recipe, const VarDecl *varRecipe,
                             const VarDecl *temporary) {
     assert(varRecipe && "Required recipe variable not set?");
-    if constexpr (std::is_same_v<RecipeTy, mlir::acc::ReductionRecipeOp>) {
-      // We haven't implemented the 'init' recipe for Reduction yet, so NYI
-      // it.
-      cgf.cgm.errorNYI(exprRange, "OpenACC Reduction recipe init");
-    }
 
     CIRGenFunction::AutoVarEmission tempDeclEmission{
         CIRGenFunction::AutoVarEmission::invalid()};
@@ -469,9 +465,12 @@ class OpenACCClauseCIREmitter final
 
     // Do the 'init' section of the recipe IR, which does an alloca, then the
     // initialization (except for firstprivate).
-    builder.createBlock(&recipe.getInitRegion(), recipe.getInitRegion().end(),
-                        {mainOp.getType()}, {loc});
+    mlir::Block *block = builder.createBlock(&recipe.getInitRegion(),
+                                             recipe.getInitRegion().end(),
+                                             {mainOp.getType()}, {loc});
     builder.setInsertionPointToEnd(&recipe.getInitRegion().back());
+    CIRGenFunction::LexicalScope ls(cgf, loc, block);
+
     tempDeclEmission =
         cgf.emitAutoVarAlloca(*varRecipe, builder.saveInsertionPoint());
 
@@ -496,6 +495,13 @@ class OpenACCClauseCIREmitter final
         cgf.cgm.errorNYI(exprRange, "private default-init recipe");
       }
       cgf.emitAutoVarInit(tempDeclEmission);
+    } else if constexpr (std::is_same_v<RecipeTy,
+                                        mlir::acc::ReductionRecipeOp>) {
+      // Unlike Private, the recipe here is always required as it has to do
+      // init, not just 'default' init.
+      if (!varRecipe->getInit())
+        cgf.cgm.errorNYI(exprRange, "reduction init recipe");
+      cgf.emitAutoVarInit(tempDeclEmission);
     }
 
     mlir::acc::YieldOp::create(builder, locEnd);
@@ -527,6 +533,7 @@ class OpenACCClauseCIREmitter final
         &recipe.getCombinerRegion(), recipe.getCombinerRegion().end(),
         {mainOp.getType(), mainOp.getType()}, {loc, loc});
     builder.setInsertionPointToEnd(&recipe.getCombinerRegion().back());
+    CIRGenFunction::LexicalScope ls(cgf, loc, block);
 
     mlir::BlockArgument lhsArg = block->getArgument(0);
 
@@ -544,6 +551,7 @@ class OpenACCClauseCIREmitter final
     mlir::Block *block = builder.createBlock(
         &destroyRegion, destroyRegion.end(), {mainOp.getType()}, {loc});
     builder.setInsertionPointToEnd(&destroyRegion.back());
+    CIRGenFunction::LexicalScope ls(cgf, loc, block);
 
     mlir::Type elementTy =
         mlir::cast<cir::PointerType>(mainOp.getType()).getPointee();
diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp
index 1457522406531..5386682b3a991 100644
--- a/clang/lib/Sema/SemaOpenACC.cpp
+++ b/clang/lib/Sema/SemaOpenACC.cpp
@@ -2590,7 +2590,9 @@ SemaOpenACC::ActOnOpenACCAsteriskSizeExpr(SourceLocation AsteriskLoc) {
 }
 
 std::pair<VarDecl *, VarDecl *>
-SemaOpenACC::CreateInitRecipe(OpenACCClauseKind CK, const Expr *VarExpr) {
+SemaOpenACC::CreateInitRecipe(OpenACCClauseKind CK,
+                              OpenACCReductionOperator ReductionOperator,
+                              const Expr *VarExpr) {
   // Strip off any array subscripts/array section exprs to get to the type of
   // the variable.
   while (isa_and_present<ArraySectionExpr, ArraySubscriptExpr>(VarExpr)) {
@@ -2722,7 +2724,50 @@ SemaOpenACC::CreateInitRecipe(OpenACCClauseKind CK, const Expr *VarExpr) {
                                      /*TreatUnavailableAsInvalid=*/false);
       Init = InitSeq.Perform(SemaRef.SemaRef, Entity, Kind, InitExpr, &VarTy);
     } else if (CK == OpenACCClauseKind::Reduction) {
-      // TODO: OpenACC: Implement this for whatever reduction needs.
+      // How we initialize the reduction variable depends on the operator used,
+      // according to the chart in OpenACC 3.3 section 2.6.15.
+
+      switch (ReductionOperator) {
+      case OpenACCReductionOperator::Invalid:
+        // This can only happen when there is an error, and since these inits
+        // are used for code generation, we can just ignore/not bother doing any
+        // initialization here.
+        break;
+      case OpenACCReductionOperator::Multiplication:
+      case OpenACCReductionOperator::Max:
+      case OpenACCReductionOperator::Min:
+      case OpenACCReductionOperator::BitwiseAnd:
+      case OpenACCReductionOperator::And:
+        // TODO: OpenACC: figure out init for these.
+        break;
+
+      case OpenACCReductionOperator::Addition:
+      case OpenACCReductionOperator::BitwiseOr:
+      case OpenACCReductionOperator::BitwiseXOr:
+      case OpenACCReductionOperator::Or: {
+        // +, |, ^, and || all use 0 for their initializers, so we can just
+        // use 'zero init' here and not bother with the rest of the
+        // array/compound type/etc contents.
+        auto *Zero = IntegerLiteral::Create(
+            getASTContext(),
+            llvm::APInt(getASTContext().getTypeSize(getASTContext().IntTy), 0),
+            getASTContext().IntTy, VarExpr->getBeginLoc());
+        Expr *InitExpr = new (getASTContext())
+            InitListExpr(getASTContext(), VarExpr->getBeginLoc(), Zero,
+                         VarExpr->getEndLoc());
+        // we set this to void so that the initialization sequence generation
+        // will get this type correct/etc.
+        InitExpr->setType(getASTContext().VoidTy);
+
+        InitializationKind Kind = InitializationKind::CreateForInit(
+            Recipe->getLocation(), /*DirectInit=*/true, InitExpr);
+        InitializationSequence InitSeq(SemaRef.SemaRef, Entity, Kind, InitExpr,
+                                       /*TopLevelOfInitList=*/false,
+                                       /*TreatUnavailableAsInvalid=*/false);
+        Init = InitSeq.Perform(SemaRef.SemaRef, Entity, Kind, InitExpr, &VarTy);
+        break;
+      }
+      }
     } else {
       llvm_unreachable("Unknown clause kind in CreateInitRecipe");
     }
diff --git a/clang/lib/Sema/SemaOpenACCClause.cpp b/clang/lib/Sema/SemaOpenACCClause.cpp
index 43ae4f4d3011c..677b74fb4e336 100644
--- a/clang/lib/Sema/SemaOpenACCClause.cpp
+++ b/clang/lib/Sema/SemaOpenACCClause.cpp
@@ -1784,9 +1784,10 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitReductionClause(
       ValidVars.push_back(Res.get());
 
       VarDecl *InitRecipe =
-          SemaRef.CreateInitRecipe(OpenACCClauseKind::Reduction, Res.get())
+          SemaRef
+              .CreateInitRecipe(OpenACCClauseKind::Reduction,
+                                Clause.getReductionOp(), Res.get())
               .first;
-      // TODO: OpenACC: Create the reduction operation recipe here too.
       Recipes.push_back({InitRecipe});
     }
   }
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 827e568c83b38..aa1bb3232d6fa 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -12430,11 +12430,11 @@ void OpenACCClauseTransform<Derived>::VisitReductionClause(
       if (OrigRecipes.RecipeDecl)
         InitRecipe = OrigRecipes.RecipeDecl;
        else
-         InitRecipe =
-             Self.getSema()
-                 .OpenACC()
-                 .CreateInitRecipe(OpenACCClauseKind::Reduction, Res.get())
-                 .first;
+         InitRecipe = Self.getSema()
+                          .OpenACC()
+                          .CreateInitRecipe(OpenACCClauseKind::Reduction,
+                                            C.getReductionOp(), Res.get())
+                          .first;
 
       Recipes.push_back({InitRecipe});
     }
diff --git a/clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-default-ops.cpp b/clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-default-ops.cpp
index a3f62b4af3c27..7469c7249caab 100644
--- a/clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-default-ops.cpp
+++ b/clang/test/CIR/CodeGenOpenACC/combined-reduction-clause-default-ops.cpp
@@ -9,9 +9,45 @@ struct DefaultOperators {
 
 // CHECK: acc.reduction.recipe @reduction_lor__ZTSA5_16DefaultOperators : !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> reduction_operator <lor> init {
 // CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>{{.*}})
-// CHECK-NEXT: cir.alloca !cir.array<!rec_DefaultOperators x 5>, !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>, ["openacc.reduction.init"]
-// TODO OpenACC: Expecting an initialization to... SOME value here.
+// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca !cir.array<!rec_DefaultOperators x 5>, !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>, ["openacc.reduction.init", init]
+// CHECK-NEXT: %[[TEMP_ITR:.*]] = cir.alloca !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, ["arrayinit.temp"]
+// CHECK-NEXT: %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[ALLOCA]] : !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[GET_I:.*]] = cir.get_member %[[DECAY]][0] {name = "i"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!s32i>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_I]] : !s32i, !cir.ptr<!s32i>
+// CHECK-NEXT: %[[GET_F:.*]] = cir.get_member %[[DECAY]][1] {name = "f"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.float
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_F]] : !cir.float, !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[GET_D:.*]] = cir.get_member %[[DECAY]][2] {name = "d"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.double
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_D]] : !cir.double, !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[FIRST_IDX:.*]] = cir.const #cir.int<1> : !s64i
+// CHECK-NEXT: %[[ITEM_ONE:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_DefaultOperators>, %[[FIRST_IDX]] : !s64i), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: cir.store {{.*}} %[[ITEM_ONE]], %[[TEMP_ITR]] : !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>
+// CHECK-NEXT: %[[LAST_IDX:.*]] = cir.const #cir.int<5> : !s64i
+// CHECK-NEXT: %[[END_ITR:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_DefaultOperators>, %[[LAST_IDX]] : !s64i), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: cir.do {
+// CHECK-NEXT: %[[TEMP_LOAD:.*]] = cir.load {{.*}} %[[TEMP_ITR]] : !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[GET_I:.*]] = cir.get_member %[[TEMP_LOAD]][0] {name = "i"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!s32i>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_I]] : !s32i, !cir.ptr<!s32i>
+// CHECK-NEXT: %[[GET_F:.*]] = cir.get_member %[[TEMP_LOAD]][1] {name = "f"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.float
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_F]] : !cir.float, !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[GET_D:.*]] = cir.get_member %[[TEMP_LOAD]][2] {name = "d"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.double
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_D]] : !cir.double, !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> : !s64i
+// CHECK-NEXT: %[[NEXT_ITEM:.*]] = cir.ptr_stride(%[[TEMP_LOAD]] : !cir.ptr<!rec_DefaultOperators>, %[[ONE]] : !s64i), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: cir.store {{.*}} %[[NEXT_ITEM]], %[[TEMP_ITR]] : !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>
+// CHECK-NEXT: cir.yield
+// CHECK-NEXT: } while {
+// CHECK-NEXT: %[[TEMP_LOAD:.*]] = cir.load {{.*}} %[[TEMP_ITR]] : !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[CMP:.*]] = cir.cmp(ne, %[[TEMP_LOAD]], %[[END_ITR]]) : !cir.ptr<!rec_DefaultOperators>, !cir.bool
+// CHECK-NEXT: cir.condition(%[[CMP]]) 
+// CHECK-NEXT: }
 // CHECK-NEXT: acc.yield
+//
 // CHECK-NEXT: } combiner {
 // CHECK-NEXT: ^bb0(%[[LHSARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> {{.*}}, %[[RHSARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> {{.*}})
 // TODO OpenACC: Expecting combination operation here
@@ -31,9 +67,45 @@ struct DefaultOperators {
 
 // CHECK-NEXT: acc.reduction.recipe @reduction_xor__ZTSA5_16DefaultOperators : !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> reduction_operator <xor> init {
 // CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>{{.*}})
-// CHECK-NEXT: cir.alloca !cir.array<!rec_DefaultOperators x 5>, !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>, ["openacc.reduction.init"]
-// TODO OpenACC: Expecting an initialization to... SOME value here.
+// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca !cir.array<!rec_DefaultOperators x 5>, !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>, ["openacc.reduction.init", init]
+// CHECK-NEXT: %[[TEMP_ITR:.*]] = cir.alloca !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, ["arrayinit.temp"]
+// CHECK-NEXT: %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[ALLOCA]] : !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[GET_I:.*]] = cir.get_member %[[DECAY]][0] {name = "i"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!s32i>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_I]] : !s32i, !cir.ptr<!s32i>
+// CHECK-NEXT: %[[GET_F:.*]] = cir.get_member %[[DECAY]][1] {name = "f"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.float
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_F]] : !cir.float, !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[GET_D:.*]] = cir.get_member %[[DECAY]][2] {name = "d"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.double
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_D]] : !cir.double, !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[FIRST_IDX:.*]] = cir.const #cir.int<1> : !s64i
+// CHECK-NEXT: %[[ITEM_ONE:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_DefaultOperators>, %[[FIRST_IDX]] : !s64i), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: cir.store {{.*}} %[[ITEM_ONE]], %[[TEMP_ITR]] : !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>
+// CHECK-NEXT: %[[LAST_IDX:.*]] = cir.const #cir.int<5> : !s64i
+// CHECK-NEXT: %[[END_ITR:.*]] = cir.ptr_stride(%[[DECAY]] : !cir.ptr<!rec_DefaultOperators>, %[[LAST_IDX]] : !s64i), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: cir.do {
+// CHECK-NEXT: %[[TEMP_LOAD:.*]] = cir.load {{.*}} %[[TEMP_ITR]] : !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[GET_I:.*]] = cir.get_member %[[TEMP_LOAD]][0] {name = "i"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!s32i>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_I]] : !s32i, !cir.ptr<!s32i>
+// CHECK-NEXT: %[[GET_F:.*]] = cir.get_member %[[TEMP_LOAD]][1] {name = "f"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.float
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_F]] : !cir.float, !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[GET_D:.*]] = cir.get_member %[[TEMP_LOAD]][2] {name = "d"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.double
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_D]] : !cir.double, !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> : !s64i
+// CHECK-NEXT: %[[NEXT_ITEM:.*]] = cir.ptr_stride(%[[TEMP_LOAD]] : !cir.ptr<!rec_DefaultOperators>, %[[ONE]] : !s64i), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: cir.store {{.*}} %[[NEXT_ITEM]], %[[TEMP_ITR]] : !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>
+// CHECK-NEXT: cir.yield
+// CHECK-NEXT: } while {
+// CHECK-NEXT: %[[TEMP_LOAD:.*]] = cir.load {{.*}} %[[TEMP_ITR]] : !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[CMP:.*]] = cir.cmp(ne, %[[TEMP_LOAD]], %[[END_ITR]]) : !cir.ptr<!rec_DefaultOperators>, !cir.bool
+// CHECK-NEXT: cir.condition(%[[CMP]]) 
+// CHECK-NEXT: }
 // CHECK-NEXT: acc.yield
+//
 // CHECK-NEXT: } combiner {
 // CHECK-NEXT: ^bb0(%[[LHSARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> {{.*}}, %[[RHSARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> {{.*}})
 // TODO OpenACC: Expecting combination operation here
@@ -42,9 +114,45 @@ struct DefaultOperators {
 
 // CHECK-NEXT: acc.reduction.recipe @reduction_ior__ZTSA5_16DefaultOperators : !cir.ptr<!cir.array<!rec_DefaultOperators x 5>> reduction_operator <ior> init {
 // CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>{{.*}})
-// CHECK-NEXT: cir.alloca !cir.array<!rec_DefaultOperators x 5>, !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>, ["openacc.reduction.init"]
-// TODO OpenACC: Expecting an initialization to... SOME value here.
+// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca !cir.array<!rec_DefaultOperators x 5>, !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>, ["openacc.reduction.init", init]
+// CHECK-NEXT: %[[TEMP_ITR:.*]] = cir.alloca !cir.ptr<!rec_DefaultOperators>, !cir.ptr<!cir.ptr<!rec_DefaultOperators>>, ["arrayinit.temp"]
+// CHECK-NEXT: %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[ALLOCA]] : !cir.ptr<!cir.array<!rec_DefaultOperators x 5>>), !cir.ptr<!rec_DefaultOperators>
+// CHECK-NEXT: %[[GET_I:.*]] = cir.get_member %[[DECAY]][0] {name = "i"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!s32i>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_I]] : !s32i, !cir.ptr<!s32i>
+// CHECK-NEXT: %[[GET_F:.*]] = cir.get_member %[[DECAY]][1] {name = "f"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.float
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_F]] : !cir.float, !cir.ptr<!cir.float>
+// CHECK-NEXT: %[[GET_D:.*]] = cir.get_member %[[DECAY]][2] {name = "d"} : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!cir.double>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.fp<0{{.*}}> : !cir.double
+// CHECK-NEXT: cir.store {{.*}} %[[ZERO]], %[[GET_D]] : !cir.d...
[truncated]

@@ -425,6 +425,7 @@ class OpenACCClauseCIREmitter final
&recipe.getCopyRegion(), recipe.getCopyRegion().end(),
{mainOp.getType(), mainOp.getType()}, {loc, loc});
builder.setInsertionPointToEnd(&recipe.getCopyRegion().back());
CIRGenFunction::LexicalScope ls(cgf, loc, block);
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this also need a cir::ScopeOp or is that already handled by some existing region? I have the same question about other places you inserted a LexicalScope below. That is, what is the purpose of the LexicalScope here?

// CHECK-NEXT: %[[TEMP_ITR:.*]] = cir.alloca !cir.ptr<!cir.float>, !cir.ptr<!cir.ptr<!cir.float>>, ["arrayinit.temp"]
// CHECK-NEXT: %[[DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[ALLOCA]] : !cir.ptr<!cir.array<!cir.float x 5>>), !cir.ptr<!cir.float>
// CHECK-NEXT: %[[ZERO_INT:.*]] = cir.const #cir.int<0> : !s32i
// CHECK-NEXT: %[[ZERO:.*]] = cir.cast(int_to_float, %[[ZERO_INT]] : !s32i), !cir.float
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems wrong. We're initializing the first element of the array to (float)(int)(0) but then the rest of the array gets initialized to (float)0.0? I think this is happening because you constructed an ILE with a single integer zero element. Could you have just constructed an empty list and gotten consistent zero-initialization for every element?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" 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.

3 participants