Skip to content

Commit 25f2213

Browse files
committed
[CIR][OpenACC] Implement 'routine' lowering + seq clause
The 'routine' construct just adds a acc.routine element to the global module, which contains all of the information about the directive. it contains a reference to the function, which also contains a reference to the acc.routine, which this generates. This handles both the implicit-func version (where the routine is spelled without parens, and just applies to the next function) and the explicit-func version (where the routine is spelled with the func name in parens). The AST stores the directive in an OpenACCRoutineDeclAttr in the implicit case, so we can emit that when we hit the function declaration. The explicit case is held in an OpenACCRoutineAnnotAttr on the function, however, when we emit the function we haven't necessarily seen the construct yet, so we can't depend on that attribute. Instead, we save up the list in Sema so that we can emit them all at the end. This results in the tests getting really hard to read (because ordering is a little awkward based on spelling, with no way to fix it), so we instead split the tests up based on topic. One last thing: Flang spends some time determining if the clause lists of two routines on the same function are identical, and omits the duplicates. However, it seems to do a poor job on this when the ordering isn't the same, or references are slightly different. This patch doesn't bother trying that, and instead emits all, trusting the ACC dialect to remove duplicates/handle duplicates gracefully. Note; This doesn't cause emission of functions that would otherwise not be emitted, but DOES emit routine references based on which function they are attached to.
1 parent 637a230 commit 25f2213

File tree

20 files changed

+503
-131
lines changed

20 files changed

+503
-131
lines changed

clang/include/clang/AST/ASTConsumer.h

Lines changed: 126 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -27,123 +27,132 @@ namespace clang {
2727
class VarDecl;
2828
class FunctionDecl;
2929
class ImportDecl;
30-
31-
/// ASTConsumer - This is an abstract interface that should be implemented by
32-
/// clients that read ASTs. This abstraction layer allows the client to be
33-
/// independent of the AST producer (e.g. parser vs AST dump file reader, etc).
34-
class ASTConsumer {
35-
/// Whether this AST consumer also requires information about
36-
/// semantic analysis.
37-
bool SemaConsumer = false;
38-
39-
friend class SemaConsumer;
40-
41-
public:
42-
ASTConsumer() = default;
43-
44-
virtual ~ASTConsumer() {}
45-
46-
/// Initialize - This is called to initialize the consumer, providing the
47-
/// ASTContext.
48-
virtual void Initialize(ASTContext &Context) {}
49-
50-
/// HandleTopLevelDecl - Handle the specified top-level declaration. This is
51-
/// called by the parser to process every top-level Decl*.
52-
///
53-
/// \returns true to continue parsing, or false to abort parsing.
54-
virtual bool HandleTopLevelDecl(DeclGroupRef D);
55-
56-
/// This callback is invoked each time an inline (method or friend)
57-
/// function definition in a class is completed.
58-
virtual void HandleInlineFunctionDefinition(FunctionDecl *D) {}
59-
60-
/// HandleInterestingDecl - Handle the specified interesting declaration. This
61-
/// is called by the AST reader when deserializing things that might interest
62-
/// the consumer. The default implementation forwards to HandleTopLevelDecl.
63-
virtual void HandleInterestingDecl(DeclGroupRef D);
64-
65-
/// HandleTranslationUnit - This method is called when the ASTs for entire
66-
/// translation unit have been parsed.
67-
virtual void HandleTranslationUnit(ASTContext &Ctx) {}
68-
69-
/// HandleTagDeclDefinition - This callback is invoked each time a TagDecl
70-
/// (e.g. struct, union, enum, class) is completed. This allows the client to
71-
/// hack on the type, which can occur at any point in the file (because these
72-
/// can be defined in declspecs).
73-
virtual void HandleTagDeclDefinition(TagDecl *D) {}
74-
75-
/// This callback is invoked the first time each TagDecl is required to
76-
/// be complete.
77-
virtual void HandleTagDeclRequiredDefinition(const TagDecl *D) {}
78-
79-
/// Invoked when a function is implicitly instantiated.
80-
/// Note that at this point it does not have a body, its body is
81-
/// instantiated at the end of the translation unit and passed to
82-
/// HandleTopLevelDecl.
83-
virtual void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) {}
84-
85-
/// Handle the specified top-level declaration that occurred inside
86-
/// and ObjC container.
87-
/// The default implementation ignored them.
88-
virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D);
89-
90-
/// Handle an ImportDecl that was implicitly created due to an
91-
/// inclusion directive.
92-
/// The default implementation passes it to HandleTopLevelDecl.
93-
virtual void HandleImplicitImportDecl(ImportDecl *D);
94-
95-
/// CompleteTentativeDefinition - Callback invoked at the end of a translation
96-
/// unit to notify the consumer that the given tentative definition should be
97-
/// completed.
98-
///
99-
/// The variable declaration itself will be a tentative
100-
/// definition. If it had an incomplete array type, its type will
101-
/// have already been changed to an array of size 1. However, the
102-
/// declaration remains a tentative definition and has not been
103-
/// modified by the introduction of an implicit zero initializer.
104-
virtual void CompleteTentativeDefinition(VarDecl *D) {}
105-
106-
/// CompleteExternalDeclaration - Callback invoked at the end of a translation
107-
/// unit to notify the consumer that the given external declaration should be
108-
/// completed.
109-
virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {}
110-
111-
/// Callback invoked when an MSInheritanceAttr has been attached to a
112-
/// CXXRecordDecl.
113-
virtual void AssignInheritanceModel(CXXRecordDecl *RD) {}
114-
115-
/// HandleCXXStaticMemberVarInstantiation - Tell the consumer that this
116-
// variable has been instantiated.
117-
virtual void HandleCXXStaticMemberVarInstantiation(VarDecl *D) {}
118-
119-
/// Callback involved at the end of a translation unit to
120-
/// notify the consumer that a vtable for the given C++ class is
121-
/// required.
122-
///
123-
/// \param RD The class whose vtable was used.
124-
virtual void HandleVTable(CXXRecordDecl *RD) {}
125-
126-
/// If the consumer is interested in entities getting modified after
127-
/// their initial creation, it should return a pointer to
128-
/// an ASTMutationListener here.
129-
virtual ASTMutationListener *GetASTMutationListener() { return nullptr; }
130-
131-
/// If the consumer is interested in entities being deserialized from
132-
/// AST files, it should return a pointer to a ASTDeserializationListener here
133-
virtual ASTDeserializationListener *GetASTDeserializationListener() {
134-
return nullptr;
135-
}
136-
137-
/// PrintStats - If desired, print any statistics.
138-
virtual void PrintStats() {}
139-
140-
/// This callback is called for each function if the Parser was
141-
/// initialized with \c SkipFunctionBodies set to \c true.
142-
///
143-
/// \return \c true if the function's body should be skipped. The function
144-
/// body may be parsed anyway if it is needed (for instance, if it contains
145-
/// the code completion point or is constexpr).
146-
virtual bool shouldSkipFunctionBody(Decl *D) { return true; }
30+
class OpenACCRoutineDecl;
31+
32+
/// ASTConsumer - This is an abstract interface that should be implemented by
33+
/// clients that read ASTs. This abstraction layer allows the client to be
34+
/// independent of the AST producer (e.g. parser vs AST dump file reader,
35+
/// etc).
36+
class ASTConsumer {
37+
/// Whether this AST consumer also requires information about
38+
/// semantic analysis.
39+
bool SemaConsumer = false;
40+
41+
friend class SemaConsumer;
42+
43+
public:
44+
ASTConsumer() = default;
45+
46+
virtual ~ASTConsumer() {}
47+
48+
/// Initialize - This is called to initialize the consumer, providing the
49+
/// ASTContext.
50+
virtual void Initialize(ASTContext &Context) {}
51+
52+
/// HandleTopLevelDecl - Handle the specified top-level declaration. This
53+
/// is called by the parser to process every top-level Decl*.
54+
///
55+
/// \returns true to continue parsing, or false to abort parsing.
56+
virtual bool HandleTopLevelDecl(DeclGroupRef D);
57+
58+
/// This callback is invoked each time an inline (method or friend)
59+
/// function definition in a class is completed.
60+
virtual void HandleInlineFunctionDefinition(FunctionDecl *D) {}
61+
62+
/// HandleInterestingDecl - Handle the specified interesting declaration.
63+
/// This is called by the AST reader when deserializing things that might
64+
/// interest the consumer. The default implementation forwards to
65+
/// HandleTopLevelDecl.
66+
virtual void HandleInterestingDecl(DeclGroupRef D);
67+
68+
/// HandleTranslationUnit - This method is called when the ASTs for entire
69+
/// translation unit have been parsed.
70+
virtual void HandleTranslationUnit(ASTContext &Ctx) {}
71+
72+
/// HandleTagDeclDefinition - This callback is invoked each time a TagDecl
73+
/// (e.g. struct, union, enum, class) is completed. This allows the client
74+
/// to hack on the type, which can occur at any point in the file (because
75+
/// these can be defined in declspecs).
76+
virtual void HandleTagDeclDefinition(TagDecl *D) {}
77+
78+
/// This callback is invoked the first time each TagDecl is required to
79+
/// be complete.
80+
virtual void HandleTagDeclRequiredDefinition(const TagDecl *D) {}
81+
82+
/// Invoked when a function is implicitly instantiated.
83+
/// Note that at this point it does not have a body, its body is
84+
/// instantiated at the end of the translation unit and passed to
85+
/// HandleTopLevelDecl.
86+
virtual void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) {}
87+
88+
/// Handle the specified top-level declaration that occurred inside
89+
/// and ObjC container.
90+
/// The default implementation ignored them.
91+
virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D);
92+
93+
/// Handle an ImportDecl that was implicitly created due to an
94+
/// inclusion directive.
95+
/// The default implementation passes it to HandleTopLevelDecl.
96+
virtual void HandleImplicitImportDecl(ImportDecl *D);
97+
98+
/// CompleteTentativeDefinition - Callback invoked at the end of a
99+
/// translation unit to notify the consumer that the given tentative
100+
/// definition should be completed.
101+
///
102+
/// The variable declaration itself will be a tentative
103+
/// definition. If it had an incomplete array type, its type will
104+
/// have already been changed to an array of size 1. However, the
105+
/// declaration remains a tentative definition and has not been
106+
/// modified by the introduction of an implicit zero initializer.
107+
virtual void CompleteTentativeDefinition(VarDecl *D) {}
108+
109+
/// CompleteExternalDeclaration - Callback invoked at the end of a
110+
/// translation unit to notify the consumer that the given external
111+
/// declaration should be completed.
112+
virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {}
113+
114+
/// Callback invoked when an MSInheritanceAttr has been attached to a
115+
/// CXXRecordDecl.
116+
virtual void AssignInheritanceModel(CXXRecordDecl *RD) {}
117+
118+
/// HandleCXXStaticMemberVarInstantiation - Tell the consumer that this
119+
// variable has been instantiated.
120+
virtual void HandleCXXStaticMemberVarInstantiation(VarDecl *D) {}
121+
122+
/// Callback to handle the end-of-translation unit attachment of OpenACC
123+
/// routine declaration information.
124+
virtual void HandleOpenACCRoutineReference(const FunctionDecl *FD,
125+
const OpenACCRoutineDecl *RD) {}
126+
127+
/// Callback involved at the end of a translation unit to
128+
/// notify the consumer that a vtable for the given C++ class is
129+
/// required.
130+
///
131+
/// \param RD The class whose vtable was used.
132+
virtual void HandleVTable(CXXRecordDecl *RD) {}
133+
134+
/// If the consumer is interested in entities getting modified after
135+
/// their initial creation, it should return a pointer to
136+
/// an ASTMutationListener here.
137+
virtual ASTMutationListener *GetASTMutationListener() { return nullptr; }
138+
139+
/// If the consumer is interested in entities being deserialized from
140+
/// AST files, it should return a pointer to a ASTDeserializationListener
141+
/// here
142+
virtual ASTDeserializationListener *GetASTDeserializationListener() {
143+
return nullptr;
144+
}
145+
146+
/// PrintStats - If desired, print any statistics.
147+
virtual void PrintStats() {}
148+
149+
/// This callback is called for each function if the Parser was
150+
/// initialized with \c SkipFunctionBodies set to \c true.
151+
///
152+
/// \return \c true if the function's body should be skipped. The function
153+
/// body may be parsed anyway if it is needed (for instance, if it contains
154+
/// the code completion point or is constexpr).
155+
virtual bool shouldSkipFunctionBody(Decl *D) { return true; }
147156
};
148157

149158
} // end namespace clang.

clang/include/clang/CIR/CIRGenerator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ class CIRGenerator : public clang::ASTConsumer {
8181
void HandleTagDeclDefinition(clang::TagDecl *d) override;
8282
void HandleTagDeclRequiredDefinition(const clang::TagDecl *D) override;
8383
void HandleCXXStaticMemberVarInstantiation(clang::VarDecl *D) override;
84+
void
85+
HandleOpenACCRoutineReference(const clang::FunctionDecl *FD,
86+
const clang::OpenACCRoutineDecl *RD) override;
8487
void CompleteTentativeDefinition(clang::VarDecl *d) override;
8588
void HandleVTable(clang::CXXRecordDecl *rd) override;
8689

clang/include/clang/Sema/SemaOpenACC.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,16 @@ class Scope;
3737
class SemaOpenACC : public SemaBase {
3838
public:
3939
using DeclGroupPtrTy = OpaquePtr<DeclGroupRef>;
40+
using RoutineRefListTy = std::pair<FunctionDecl *, OpenACCRoutineDecl *>;
4041

4142
private:
43+
// We save a list of routine clauses that refer to a different function(that
44+
// is, routine-with-a-name) so that we can do the emission at the 'end'. We
45+
// have to do this, since functions can be emitted before they are referenced,
46+
// and the OpenACCRoutineDecl isn't necessarily emitted, as it might be in a
47+
// function/etc. So we do these emits at the end of the TU.
48+
llvm::SmallVector<RoutineRefListTy> RoutineRefList;
49+
4250
struct ComputeConstructInfo {
4351
/// Which type of compute construct we are inside of, which we can use to
4452
/// determine whether we should add loops to the above collection. We can
@@ -752,6 +760,7 @@ class SemaOpenACC : public SemaBase {
752760
};
753761

754762
SemaOpenACC(Sema &S);
763+
void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU);
755764

756765
// Called when we encounter a 'while' statement, before looking at its 'body'.
757766
void ActOnWhileStmt(SourceLocation WhileLoc);

clang/lib/CIR/CodeGen/CIRGenDeclOpenACC.cpp

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,82 @@ void CIRGenModule::emitGlobalOpenACCDeclareDecl(const OpenACCDeclareDecl *d) {
287287
}
288288

289289
void CIRGenFunction::emitOpenACCRoutine(const OpenACCRoutineDecl &d) {
290-
getCIRGenModule().errorNYI(d.getSourceRange(), "OpenACC Routine Construct");
290+
// Do nothing here. The OpenACCRoutineDeclAttr handles the implicit name
291+
// cases, and the end-of-TU handling manages the named cases. This is
292+
// necessary because these references aren't necessarily emitted themselves,
293+
// but can be named anywhere.
291294
}
292295

293296
void CIRGenModule::emitGlobalOpenACCRoutineDecl(const OpenACCRoutineDecl *d) {
294-
errorNYI(d->getSourceRange(), "OpenACC Global Routine Construct");
297+
// Do nothing here. The OpenACCRoutineDeclAttr handles the implicit name
298+
// cases, and the end-of-TU handling manages the named cases. This is
299+
// necessary because these references aren't necessarily emitted themselves,
300+
// but can be named anywhere.
301+
}
302+
303+
namespace {
304+
class OpenACCRoutineClauseEmitter final
305+
: public OpenACCClauseVisitor<OpenACCRoutineClauseEmitter> {
306+
CIRGen::CIRGenBuilderTy &builder;
307+
mlir::acc::RoutineOp routineOp;
308+
llvm::SmallVector<mlir::acc::DeviceType> lastDeviceTypeValues;
309+
310+
public:
311+
OpenACCRoutineClauseEmitter(CIRGen::CIRGenBuilderTy &builder,
312+
mlir::acc::RoutineOp routineOp)
313+
: builder(builder), routineOp(routineOp) {}
314+
315+
void emitClauses(ArrayRef<const OpenACCClause *> clauses) {
316+
this->VisitClauseList(clauses);
317+
}
318+
319+
void VisitClause(const OpenACCClause &clause) {
320+
llvm_unreachable("Invalid OpenACC clause on routine");
321+
}
322+
323+
void VisitSeqClause(const OpenACCSeqClause &clause) {
324+
routineOp.addSeq(builder.getContext(), lastDeviceTypeValues);
325+
}
326+
};
327+
} // namespace
328+
329+
void CIRGenModule::emitOpenACCRoutineDecl(
330+
const clang::FunctionDecl *funcDecl, cir::FuncOp func,
331+
SourceLocation pragmaLoc, ArrayRef<const OpenACCClause *> clauses) {
332+
mlir::OpBuilder::InsertionGuard guardCase(builder);
333+
// These need to appear at the global module.
334+
builder.setInsertionPointToEnd(&getModule().getBodyRegion().front());
335+
336+
mlir::Location routineLoc = getLoc(pragmaLoc);
337+
338+
std::stringstream routineNameSS;
339+
// This follows the same naming format as Flang.
340+
routineNameSS << "acc_routine_" << routineCounter++;
341+
std::string routineName = routineNameSS.str();
342+
343+
// There isn't a good constructor for RoutineOp that just takes a location +
344+
// name + function, so we use one that creates an otherwise RoutineOp and
345+
// count on the visitor/emitter to fill these in.
346+
auto routineOp = mlir::acc::RoutineOp::create(
347+
builder, routineLoc, routineName,
348+
mlir::SymbolRefAttr::get(builder.getContext(), func.getName()), {}, {},
349+
{}, {}, {}, {}, {}, /*hasNoHost=*/false, /*implicit=*/false, {}, {}, {});
350+
351+
// We have to add a pointer going the other direction via an acc.routine_info,
352+
// from the func to the routine.
353+
llvm::SmallVector<mlir::SymbolRefAttr> funcRoutines;
354+
if (auto routineInfo =
355+
func.getOperation()->getAttrOfType<mlir::acc::RoutineInfoAttr>(
356+
mlir::acc::getRoutineInfoAttrName()))
357+
funcRoutines.append(routineInfo.getAccRoutines().begin(),
358+
routineInfo.getAccRoutines().end());
359+
360+
funcRoutines.push_back(
361+
mlir::SymbolRefAttr::get(builder.getContext(), routineName));
362+
func.getOperation()->setAttr(
363+
mlir::acc::getRoutineInfoAttrName(),
364+
mlir::acc::RoutineInfoAttr::get(func.getContext(), funcRoutines));
365+
366+
OpenACCRoutineClauseEmitter emitter{builder, routineOp};
367+
emitter.emitClauses(clauses);
295368
}

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2234,6 +2234,15 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
22342234

22352235
if (!cgf)
22362236
theModule.push_back(func);
2237+
2238+
if (this->getLangOpts().OpenACC) {
2239+
// We only have to handle this attribute, since OpenACCAnnotAttrs are
2240+
// handled via the end-of-TU work.
2241+
for (const auto *attr :
2242+
funcDecl->specific_attrs<OpenACCRoutineDeclAttr>())
2243+
emitOpenACCRoutineDecl(funcDecl, func, attr->getLocation(),
2244+
attr->Clauses);
2245+
}
22372246
}
22382247
return func;
22392248
}

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,12 @@ class CIRGenModule : public CIRGenTypeCache {
461461
OpenACCModifierKind modifiers,
462462
bool structured, bool implicit,
463463
bool requiresDtor);
464+
// Each of the acc.routine operations must have a unique name, so we just use
465+
// an integer counter. This is how Flang does it, so it seems reasonable.
466+
unsigned routineCounter = 0;
467+
void emitOpenACCRoutineDecl(const clang::FunctionDecl *funcDecl,
468+
cir::FuncOp func, SourceLocation pragmaLoc,
469+
ArrayRef<const OpenACCClause *> clauses);
464470

465471
// C++ related functions.
466472
void emitDeclContext(const DeclContext *dc);

0 commit comments

Comments
 (0)