1414
1515#include  " mlir/Dialect/Arith/IR/Arith.h" 
1616#include  " mlir/Dialect/OpenACC/OpenACC.h" 
17+ #include  " llvm/ADT/TypeSwitch.h" 
1718namespace  clang  {
1819//  Simple type-trait to see if the first template arg is one of the list, so we
1920//  can tell whether to `if-constexpr` a bunch of stuff.
@@ -36,6 +37,76 @@ template <typename ToTest> constexpr bool isCombinedType = false;
3637template  <typename  T>
3738constexpr  bool  isCombinedType<CombinedConstructClauseInfo<T>> = true ;
3839
40+ namespace  {
41+ struct  DataOperandInfo  {
42+   mlir::Location beginLoc;
43+   mlir::Value varValue;
44+   llvm::StringRef name;
45+   mlir::ValueRange bounds;
46+ 
47+   DataOperandInfo (mlir::Location beginLoc, mlir::Value varValue,
48+                   llvm::StringRef name, mlir::ValueRange bounds)
49+       : beginLoc(beginLoc), varValue(varValue), name(name), bounds(bounds) {}
50+ };
51+ 
52+ inline  mlir::Value createIntExpr (CIRGen::CIRGenFunction &cgf,
53+                                  CIRGen::CIRGenBuilderTy &builder,
54+                                  const  Expr *intExpr) {
55+   mlir::Value expr = cgf.emitScalarExpr (intExpr);
56+   mlir::Location exprLoc = cgf.cgm .getLoc (intExpr->getBeginLoc ());
57+ 
58+   mlir::IntegerType targetType = mlir::IntegerType::get (
59+       &cgf.getMLIRContext (), cgf.getContext ().getIntWidth (intExpr->getType ()),
60+       intExpr->getType ()->isSignedIntegerOrEnumerationType ()
61+           ? mlir::IntegerType::SignednessSemantics::Signed
62+           : mlir::IntegerType::SignednessSemantics::Unsigned);
63+ 
64+   auto  conversionOp = builder.create <mlir::UnrealizedConversionCastOp>(
65+       exprLoc, targetType, expr);
66+   return  conversionOp.getResult (0 );
67+ }
68+ 
69+ //  A helper function that gets the information from an operand to a data
70+ //  clause, so that it can be used to emit the data operations.
71+ inline  DataOperandInfo getDataOperandInfo (CIRGen::CIRGenFunction &cgf,
72+                                           CIRGen::CIRGenBuilderTy &builder,
73+                                           OpenACCDirectiveKind DK,
74+                                           const  Expr *E) {
75+   //  TODO: OpenACC: Cache was different enough as to need a separate
76+   //  `ActOnCacheVar`, so we are going to need to do some investigations here
77+   //  when it comes to implement this for cache.
78+   assert (DK != OpenACCDirectiveKind::Cache &&
79+          " Cache has different enough functionality we need to investigate " 
80+          " whether this function works for it"  );
81+   const  Expr *curVarExpr = E->IgnoreParenImpCasts ();
82+ 
83+   mlir::Location exprLoc = cgf.cgm .getLoc (curVarExpr->getBeginLoc ());
84+   llvm::SmallVector<mlir::Value> bounds;
85+ 
86+   //  TODO: OpenACC: Assemble the list of bounds.
87+   if  (isa<ArraySectionExpr, ArraySubscriptExpr>(curVarExpr)) {
88+     cgf.cgm .errorNYI (curVarExpr->getSourceRange (),
89+                      " OpenACC data clause array subscript/section"  );
90+     return  {exprLoc, {}, {}, bounds};
91+   }
92+ 
93+   //  TODO: OpenACC: if this is a member expr, emit the VarPtrPtr correctly.
94+   if  (const  auto  *ME = dyn_cast<MemberExpr>(curVarExpr)) {
95+     cgf.cgm .errorNYI (curVarExpr->getSourceRange (),
96+                      " OpenACC Data clause member expr"  );
97+     return  {exprLoc, {}, {}, bounds};
98+   }
99+ 
100+   //  Sema has made sure that only 4 types of things can get here, array
101+   //  subscript, array section, member expr, or DRE to a var decl (or the former
102+   //  3 wrapping a var-decl), so we should be able to assume this is right.
103+   const  auto  *DRE = cast<DeclRefExpr>(curVarExpr);
104+   const  auto  *VD = cast<VarDecl>(DRE->getFoundDecl ()->getCanonicalDecl ());
105+   return  {exprLoc, cgf.emitDeclRefLValue (DRE).getPointer (), VD->getName (),
106+           bounds};
107+ }
108+ } //   namespace
109+ 
39110template  <typename  OpTy>
40111class  OpenACCClauseCIREmitter  final 
41112    : public OpenACCClauseVisitor<OpenACCClauseCIREmitter<OpTy>> {
@@ -54,6 +125,11 @@ class OpenACCClauseCIREmitter final
54125  SourceLocation dirLoc;
55126
56127  llvm::SmallVector<mlir::acc::DeviceType> lastDeviceTypeValues;
128+   //  Keep track of the async-clause so that we can shortcut updating the data
129+   //  operands async clauses.
130+   bool  hasAsyncClause = false ;
131+   //  Keep track of the data operands so that we can update their async clauses.
132+   llvm::SmallVector<mlir::Operation *> dataOperands;
57133
58134  void  setLastDeviceTypeClause (const  OpenACCDeviceTypeClause &clause) {
59135    lastDeviceTypeValues.clear ();
@@ -70,18 +146,7 @@ class OpenACCClauseCIREmitter final
70146  }
71147
72148  mlir::Value createIntExpr (const  Expr *intExpr) {
73-     mlir::Value expr = cgf.emitScalarExpr (intExpr);
74-     mlir::Location exprLoc = cgf.cgm .getLoc (intExpr->getBeginLoc ());
75- 
76-     mlir::IntegerType targetType = mlir::IntegerType::get (
77-         &cgf.getMLIRContext (), cgf.getContext ().getIntWidth (intExpr->getType ()),
78-         intExpr->getType ()->isSignedIntegerOrEnumerationType ()
79-             ? mlir::IntegerType::SignednessSemantics::Signed
80-             : mlir::IntegerType::SignednessSemantics::Unsigned);
81- 
82-     auto  conversionOp = builder.create <mlir::UnrealizedConversionCastOp>(
83-         exprLoc, targetType, expr);
84-     return  conversionOp.getResult (0 );
149+     return  clang::createIntExpr (cgf, builder, intExpr);
85150  }
86151
87152  //  'condition' as an OpenACC grammar production is used for 'if' and (some
@@ -157,6 +222,103 @@ class OpenACCClauseCIREmitter final
157222    computeEmitter.Visit (&c);
158223  }
159224
225+   template  <typename  BeforeOpTy, typename  AfterOpTy>
226+   void  addDataOperand (const  Expr *varOperand, mlir::acc::DataClause dataClause,
227+                       bool  structured, bool  implicit) {
228+     DataOperandInfo opInfo =
229+         getDataOperandInfo (cgf, builder, dirKind, varOperand);
230+ 
231+     //  TODO: OpenACC: we should comprehend the 'modifier-list' here for the data
232+     //  operand. At the moment, we don't have a uniform way to assign these
233+     //  properly, and the dialect cannot represent anything other than 'readonly'
234+     //  and 'zero' on copyin/copyout/create, so for now, we skip it.
235+ 
236+     auto  beforeOp =
237+         builder.create <BeforeOpTy>(opInfo.beginLoc , opInfo.varValue , structured,
238+                                    implicit, opInfo.name , opInfo.bounds );
239+     operation.getDataClauseOperandsMutable ().append (beforeOp.getResult ());
240+ 
241+     AfterOpTy afterOp;
242+     {
243+       mlir::OpBuilder::InsertionGuard guardCase (builder);
244+       builder.setInsertionPointAfter (operation);
245+       afterOp = builder.create <AfterOpTy>(opInfo.beginLoc , beforeOp.getResult (),
246+                                           opInfo.varValue , structured, implicit,
247+                                           opInfo.name , opInfo.bounds );
248+     }
249+ 
250+     //  Set the 'rest' of the info for both operations.
251+     beforeOp.setDataClause (dataClause);
252+     afterOp.setDataClause (dataClause);
253+ 
254+     //  Make sure we record these, so 'async' values can be updated later.
255+     dataOperands.push_back (beforeOp.getOperation ());
256+     dataOperands.push_back (afterOp.getOperation ());
257+   }
258+ 
259+   //  Helper function that covers for the fact that we don't have this function
260+   //  on all operation types.
261+   mlir::ArrayAttr getAsyncOnlyAttr () {
262+     if  constexpr  (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
263+                                mlir::acc::KernelsOp, mlir::acc::DataOp>)
264+       return  operation.getAsyncOnlyAttr ();
265+ 
266+     //  Note: 'wait' has async as well, but it cannot have data clauses, so we
267+     //  don't have to handle them here.
268+ 
269+     llvm_unreachable (" getting asyncOnly when clause not valid on operation?"  );
270+   }
271+ 
272+   //  Helper function that covers for the fact that we don't have this function
273+   //  on all operation types.
274+   mlir::ArrayAttr getAsyncOperandsDeviceTypeAttr () {
275+     if  constexpr  (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
276+                                mlir::acc::KernelsOp, mlir::acc::DataOp>)
277+       return  operation.getAsyncOperandsDeviceTypeAttr ();
278+ 
279+     //  Note: 'wait' has async as well, but it cannot have data clauses, so we
280+     //  don't have to handle them here.
281+ 
282+     llvm_unreachable (
283+         " getting asyncOperandsDeviceType when clause not valid on operation?"  );
284+   }
285+ 
286+   //  Helper function that covers for the fact that we don't have this function
287+   //  on all operation types.
288+   mlir::OperandRange getAsyncOperands () {
289+     if  constexpr  (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
290+                                mlir::acc::KernelsOp, mlir::acc::DataOp>)
291+       return  operation.getAsyncOperands ();
292+ 
293+     //  Note: 'wait' has async as well, but it cannot have data clauses, so we
294+     //  don't have to handle them here.
295+ 
296+     llvm_unreachable (
297+         " getting asyncOperandsDeviceType when clause not valid on operation?"  );
298+   }
299+ 
300+   //  The 'data' clauses all require that we add the 'async' values from the
301+   //  operation to them. We've collected the data operands along the way, so use
302+   //  that list to get the current 'async' values.
303+   void  updateDataOperandAsyncValues () {
304+     if  (!hasAsyncClause || dataOperands.empty ())
305+       return ;
306+ 
307+     //  TODO: OpenACC: Handle this correctly for combined constructs.
308+ 
309+     for  (mlir::Operation *dataOp : dataOperands) {
310+       llvm::TypeSwitch<mlir::Operation *, void >(dataOp)
311+           .Case <ACC_DATA_ENTRY_OPS, ACC_DATA_EXIT_OPS>([&](auto  op) {
312+             op.setAsyncOnlyAttr (getAsyncOnlyAttr ());
313+             op.setAsyncOperandsDeviceTypeAttr (getAsyncOperandsDeviceTypeAttr ());
314+             op.getAsyncOperandsMutable ().assign (getAsyncOperands ());
315+           })
316+           .Default ([&](mlir::Operation *) {
317+             llvm_unreachable (" Not a data operation?"  );
318+           });
319+     }
320+   }
321+ 
160322public: 
161323  OpenACCClauseCIREmitter (OpTy &operation, CIRGen::CIRGenFunction &cgf,
162324                          CIRGen::CIRGenBuilderTy &builder,
@@ -168,6 +330,14 @@ class OpenACCClauseCIREmitter final
168330    clauseNotImplemented (clause);
169331  }
170332
333+   //  The entry point for the CIR emitter. All users should use this rather than
334+   //  'visitClauseList', as this also handles the things that have to happen
335+   //  'after' the clauses are all visited.
336+   void  emitClauses (ArrayRef<const  OpenACCClause *> clauses) {
337+     this ->VisitClauseList (clauses);
338+     updateDataOperandAsyncValues ();
339+   }
340+ 
171341  void  VisitDefaultClause (const  OpenACCDefaultClause &clause) {
172342    //  This type-trait checks if 'op'(the first arg) is one of the mlir::acc
173343    //  operations listed in the rest of the arguments.
@@ -250,14 +420,26 @@ class OpenACCClauseCIREmitter final
250420  }
251421
252422  void  VisitAsyncClause (const  OpenACCAsyncClause &clause) {
423+     hasAsyncClause = true ;
253424    if  constexpr  (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
254425                               mlir::acc::KernelsOp, mlir::acc::DataOp>) {
255426      if  (!clause.hasIntExpr ())
256427        operation.addAsyncOnly (builder.getContext (), lastDeviceTypeValues);
257-       else 
258-         operation.addAsyncOperand (builder.getContext (),
259-                                   createIntExpr (clause.getIntExpr ()),
428+       else  {
429+ 
430+         mlir::Value intExpr;
431+         {
432+           //  Async int exprs can be referenced by the data operands, which means
433+           //  that the int-exprs have to appear before them.  IF there is a data
434+           //  operand already, set the insertion point to 'before' it.
435+           mlir::OpBuilder::InsertionGuard guardCase (builder);
436+           if  (!dataOperands.empty ())
437+             builder.setInsertionPoint (dataOperands.front ());
438+           intExpr = createIntExpr (clause.getIntExpr ());
439+         }
440+         operation.addAsyncOperand (builder.getContext (), intExpr,
260441                                  lastDeviceTypeValues);
442+       }
261443    } else  if  constexpr  (isOneOfTypes<OpTy, mlir::acc::WaitOp>) {
262444      //  Wait doesn't have a device_type, so its handling here is slightly
263445      //  different.
@@ -527,6 +709,20 @@ class OpenACCClauseCIREmitter final
527709      llvm_unreachable (" Unknown construct kind in VisitGangClause"  );
528710    }
529711  }
712+ 
713+   void  VisitCopyClause (const  OpenACCCopyClause &clause) {
714+     if  constexpr  (isOneOfTypes<OpTy, mlir::acc::ParallelOp, mlir::acc::SerialOp,
715+                                mlir::acc::KernelsOp>) {
716+       for  (auto  Var : clause.getVarList ())
717+         addDataOperand<mlir::acc::CopyinOp, mlir::acc::CopyoutOp>(
718+             Var, mlir::acc::DataClause::acc_copy, /* structured=*/ true ,
719+             /* implicit=*/ false );
720+     } else  {
721+       //  TODO: When we've implemented this for everything, switch this to an
722+       //  unreachable. data, declare, combined constructs remain.
723+       return  clauseNotImplemented (clause);
724+     }
725+   }
530726};
531727
532728template  <typename  OpTy>
0 commit comments