1111// ===----------------------------------------------------------------------===//
1212
1313#include " CIRGenFunction.h"
14+ #include " CIRGenOpenACCHelpers.h"
15+
1416#include " mlir/Dialect/OpenACC/OpenACC.h"
1517#include " clang/AST/DeclOpenACC.h"
18+ #include " llvm/Support/SaveAndRestore.h"
1619
1720using namespace clang ;
1821using namespace clang ::CIRGen;
@@ -96,6 +99,13 @@ struct OpenACCDeclareCleanup final : EHScopeStack::Cleanup {
9699};
97100} // namespace
98101
102+ void CIRGenModule::emitGlobalOpenACCDecl (const OpenACCConstructDecl *d) {
103+ if (const auto *rd = dyn_cast<OpenACCRoutineDecl>(d))
104+ emitGlobalOpenACCRoutineDecl (rd);
105+ else
106+ emitGlobalOpenACCDeclareDecl (cast<OpenACCDeclareDecl>(d));
107+ }
108+
99109void CIRGenFunction::emitOpenACCDeclare (const OpenACCDeclareDecl &d) {
100110 mlir::Location exprLoc = cgm.getLoc (d.getBeginLoc ());
101111 auto enterOp = mlir::acc::DeclareEnterOp::create (
@@ -109,15 +119,165 @@ void CIRGenFunction::emitOpenACCDeclare(const OpenACCDeclareDecl &d) {
109119 enterOp);
110120}
111121
122+ // Helper function that gets the declaration referenced by the declare clause.
123+ // This is a simplified verison of the work that `getOpenACCDataOperandInfo`
124+ // does, as it only has to get forms that 'declare' does.
125+ static const Decl *getDeclareReferencedDecl (const Expr *e) {
126+ const Expr *curVarExpr = e->IgnoreParenImpCasts ();
127+
128+ // Since we allow array sections, we have to unpack the array sections here.
129+ // We don't have to worry about other bounds, since only variable or array
130+ // name (plus array sections as an extension) are permitted.
131+ while (const auto *ase = dyn_cast<ArraySectionExpr>(curVarExpr))
132+ curVarExpr = ase->getBase ()->IgnoreParenImpCasts ();
133+
134+ if (const auto *dre = dyn_cast<DeclRefExpr>(curVarExpr))
135+ return dre->getFoundDecl ()->getCanonicalDecl ();
136+
137+ // MemberExpr is allowed when it is implicit 'this'.
138+ return cast<MemberExpr>(curVarExpr)->getMemberDecl ()->getCanonicalDecl ();
139+ }
140+
141+ template <typename BeforeOpTy, typename DataClauseTy>
142+ void CIRGenModule::emitGlobalOpenACCDeclareDataOperands (
143+ const Expr *varOperand, DataClauseTy dataClause,
144+ OpenACCModifierKind modifiers, bool structured, bool implicit,
145+ bool requiresDtor) {
146+ // This is a template argument so that we don't have to include all of
147+ // mlir::acc into CIRGenModule.
148+ static_assert (std::is_same_v<DataClauseTy, mlir::acc::DataClause>);
149+ mlir::Location exprLoc = getLoc (varOperand->getBeginLoc ());
150+ const Decl *refedDecl = getDeclareReferencedDecl (varOperand);
151+ StringRef varName = getMangledName (GlobalDecl{cast<VarDecl>(refedDecl)});
152+
153+ // We have to emit two separate functions in this case, an acc_ctor and an
154+ // acc_dtor. These two sections are/should remain reasonably equal, however
155+ // the order of the clauses/vs-enter&exit in them makes combining these two
156+ // sections not particularly attractive, so we have a bit of repetition.
157+ {
158+ mlir::OpBuilder::InsertionGuard guardCase (builder);
159+ auto ctorOp = mlir::acc::GlobalConstructorOp::create (
160+ builder, exprLoc, (varName + " _acc_ctor" ).str ());
161+ getModule ().push_back (ctorOp);
162+ mlir::Block *block = builder.createBlock (&ctorOp.getRegion (),
163+ ctorOp.getRegion ().end (), {}, {});
164+ builder.setInsertionPointToEnd (block);
165+ // These things are close enough to a function handling-wise we can just
166+ // create this here.
167+ CIRGenFunction cgf{*this , builder, true };
168+ llvm::SaveAndRestore<CIRGenFunction *> savedCGF (curCGF, &cgf);
169+ cgf.curFn = ctorOp;
170+ CIRGenFunction::SourceLocRAIIObject fnLoc{cgf, exprLoc};
171+
172+ // This gets the information we need, PLUS emits the bounds correctly, so we
173+ // have to do this in both enter and exit.
174+ CIRGenFunction::OpenACCDataOperandInfo inf =
175+ cgf.getOpenACCDataOperandInfo (varOperand);
176+ auto beforeOp =
177+ BeforeOpTy::create (builder, exprLoc, inf.varValue , structured, implicit,
178+ inf.name , inf.bounds );
179+ beforeOp.setDataClause (dataClause);
180+ beforeOp.setModifiers (convertOpenACCModifiers (modifiers));
181+
182+ mlir::acc::DeclareEnterOp::create (
183+ builder, exprLoc, mlir::acc::DeclareTokenType::get (&getMLIRContext ()),
184+ beforeOp.getResult ());
185+
186+ mlir::acc::TerminatorOp::create (builder, exprLoc);
187+ }
188+
189+ // copyin, create, and device_resident require a destructor, link does not. In
190+ // the case of the first three, they are all a 'getdeviceptr', followed by the
191+ // declare_exit, followed by a delete op in the destructor region.
192+ if (requiresDtor) {
193+ mlir::OpBuilder::InsertionGuard guardCase (builder);
194+ auto ctorOp = mlir::acc::GlobalDestructorOp::create (
195+ builder, exprLoc, (varName + " _acc_dtor" ).str ());
196+ getModule ().push_back (ctorOp);
197+ mlir::Block *block = builder.createBlock (&ctorOp.getRegion (),
198+ ctorOp.getRegion ().end (), {}, {});
199+ builder.setInsertionPointToEnd (block);
200+
201+ // These things are close enough to a function handling-wise we can just
202+ // create this here.
203+ CIRGenFunction cgf{*this , builder, true };
204+ llvm::SaveAndRestore<CIRGenFunction *> savedCGF (curCGF, &cgf);
205+ cgf.curFn = ctorOp;
206+ CIRGenFunction::SourceLocRAIIObject fnLoc{cgf, exprLoc};
207+
208+ CIRGenFunction::OpenACCDataOperandInfo inf =
209+ cgf.getOpenACCDataOperandInfo (varOperand);
210+ auto getDevPtr = mlir::acc::GetDevicePtrOp::create (
211+ builder, exprLoc, inf.varValue , structured, implicit, inf.name ,
212+ inf.bounds );
213+ getDevPtr.setDataClause (dataClause);
214+ getDevPtr.setModifiers (convertOpenACCModifiers (modifiers));
215+
216+ mlir::acc::DeclareExitOp::create (builder, exprLoc, /* token=*/ mlir::Value{},
217+ getDevPtr.getResult ());
218+ auto deleteOp = mlir::acc::DeleteOp::create (
219+ builder, exprLoc, getDevPtr, structured, implicit, inf.name , {});
220+ deleteOp.setDataClause (dataClause);
221+ deleteOp.setModifiers (convertOpenACCModifiers (modifiers));
222+ mlir::acc::TerminatorOp::create (builder, exprLoc);
223+ }
224+ }
225+ namespace {
226+ // This class emits all of the information for a 'declare' at a global/ns/class
227+ // scope. Each clause results in its own acc_ctor and acc_dtor for the variable.
228+ // This class creates those and emits them properly.
229+ // This behavior is unique/special enough from the emission of statement-level
230+ // clauses that it doesn't really make sense to use that clause visitor.
231+ class OpenACCGlobalDeclareClauseEmitter final
232+ : public OpenACCClauseVisitor<OpenACCGlobalDeclareClauseEmitter> {
233+ CIRGenModule &cgm;
234+ void clauseNotImplemented (const OpenACCClause &c) {
235+ cgm.errorNYI (c.getSourceRange (), " OpenACC Global Declare Clause" ,
236+ c.getClauseKind ());
237+ }
238+
239+ public:
240+ OpenACCGlobalDeclareClauseEmitter (CIRGenModule &cgm) : cgm(cgm) {}
241+
242+ void VisitClause (const OpenACCClause &clause) {
243+ clauseNotImplemented (clause);
244+ }
245+
246+ void emitClauses (ArrayRef<const OpenACCClause *> clauses) {
247+ this ->VisitClauseList (clauses);
248+ }
249+
250+ void VisitCopyInClause (const OpenACCCopyInClause &clause) {
251+ for (const Expr *var : clause.getVarList ())
252+ cgm.emitGlobalOpenACCDeclareDataOperands <mlir::acc::CopyinOp>(
253+ var, mlir::acc::DataClause::acc_copyin, clause.getModifierList (),
254+ /* structured=*/ true ,
255+ /* implicit=*/ false , /* requiresDtor=*/ true );
256+ }
257+
258+ void VisitCreateClause (const OpenACCCreateClause &clause) {
259+ for (const Expr *var : clause.getVarList ())
260+ cgm.emitGlobalOpenACCDeclareDataOperands <mlir::acc::CreateOp>(
261+ var, mlir::acc::DataClause::acc_create, clause.getModifierList (),
262+ /* structured=*/ true ,
263+ /* implicit=*/ false , /* requiresDtor=*/ true );
264+ }
265+ };
266+ } // namespace
267+
268+ void CIRGenModule::emitGlobalOpenACCDeclareDecl (const OpenACCDeclareDecl *d) {
269+ // Declare creates 1 'acc_ctor' and 0-1 'acc_dtor' per clause, since it needs
270+ // a unique one on a per-variable basis. We can just use a clause emitter to
271+ // do all the work.
272+ mlir::OpBuilder::InsertionGuard guardCase (builder);
273+ OpenACCGlobalDeclareClauseEmitter em{*this };
274+ em.emitClauses (d->clauses ());
275+ }
276+
112277void CIRGenFunction::emitOpenACCRoutine (const OpenACCRoutineDecl &d) {
113278 getCIRGenModule ().errorNYI (d.getSourceRange (), " OpenACC Routine Construct" );
114279}
115280
116- void CIRGenModule::emitGlobalOpenACCDecl (const OpenACCConstructDecl *d) {
117- if (isa<OpenACCRoutineDecl>(d))
118- errorNYI (d->getSourceRange (), " OpenACC Routine Construct" );
119- else if (isa<OpenACCDeclareDecl>(d))
120- errorNYI (d->getSourceRange (), " OpenACC Declare Construct" );
121- else
122- llvm_unreachable (" unknown OpenACC declaration kind?" );
281+ void CIRGenModule::emitGlobalOpenACCRoutineDecl (const OpenACCRoutineDecl *d) {
282+ errorNYI (d->getSourceRange (), " OpenACC Global Routine Construct" );
123283}
0 commit comments