Skip to content

Commit 6dd639e

Browse files
authored
[CIR][OpenACC] Implement 'routine' lowering + seq clause (#170207)
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 fae64ad commit 6dd639e

File tree

20 files changed

+395
-14
lines changed

20 files changed

+395
-14
lines changed

clang/include/clang/AST/ASTConsumer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ namespace clang {
2727
class VarDecl;
2828
class FunctionDecl;
2929
class ImportDecl;
30+
class OpenACCRoutineDecl;
3031

3132
/// ASTConsumer - This is an abstract interface that should be implemented by
3233
/// clients that read ASTs. This abstraction layer allows the client to be
@@ -116,6 +117,11 @@ class ASTConsumer {
116117
// variable has been instantiated.
117118
virtual void HandleCXXStaticMemberVarInstantiation(VarDecl *D) {}
118119

120+
/// Callback to handle the end-of-translation unit attachment of OpenACC
121+
/// routine declaration information.
122+
virtual void HandleOpenACCRoutineReference(const FunctionDecl *FD,
123+
const OpenACCRoutineDecl *RD) {}
124+
119125
/// Callback involved at the end of a translation unit to
120126
/// notify the consumer that a vtable for the given C++ class is
121127
/// required.

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+
/*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
@@ -2227,6 +2227,15 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
22272227

22282228
if (!cgf)
22292229
theModule.push_back(func);
2230+
2231+
if (this->getLangOpts().OpenACC) {
2232+
// We only have to handle this attribute, since OpenACCAnnotAttrs are
2233+
// handled via the end-of-TU work.
2234+
for (const auto *attr :
2235+
funcDecl->specific_attrs<OpenACCRoutineDeclAttr>())
2236+
emitOpenACCRoutineDecl(funcDecl, func, attr->getLocation(),
2237+
attr->Clauses);
2238+
}
22302239
}
22312240
return func;
22322241
}

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);

clang/lib/CIR/CodeGen/CIRGenerator.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,18 @@ void CIRGenerator::HandleCXXStaticMemberVarInstantiation(VarDecl *D) {
166166
cgm->handleCXXStaticMemberVarInstantiation(D);
167167
}
168168

169+
void CIRGenerator::HandleOpenACCRoutineReference(const FunctionDecl *FD,
170+
const OpenACCRoutineDecl *RD) {
171+
llvm::StringRef mangledName = cgm->getMangledName(FD);
172+
cir::FuncOp entry =
173+
mlir::dyn_cast_if_present<cir::FuncOp>(cgm->getGlobalValue(mangledName));
174+
175+
// if this wasn't generated, don't force it to be.
176+
if (!entry)
177+
return;
178+
cgm->emitOpenACCRoutineDecl(FD, entry, RD->getBeginLoc(), RD->clauses());
179+
}
180+
169181
void CIRGenerator::CompleteTentativeDefinition(VarDecl *d) {
170182
if (diags.hasErrorOccurred())
171183
return;

clang/lib/CIR/FrontendAction/CIRGenAction.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ class CIRGenConsumer : public clang::ASTConsumer {
8888
Gen->HandleCXXStaticMemberVarInstantiation(VD);
8989
}
9090

91+
void HandleOpenACCRoutineReference(const FunctionDecl *FD,
92+
const OpenACCRoutineDecl *RD) override {
93+
Gen->HandleOpenACCRoutineReference(FD, RD);
94+
}
95+
9196
void HandleInlineFunctionDefinition(FunctionDecl *D) override {
9297
Gen->HandleInlineFunctionDefinition(D);
9398
}

clang/lib/Sema/Sema.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,9 @@ void Sema::ActOnEndOfTranslationUnit() {
14971497

14981498
if (LangOpts.HLSL)
14991499
HLSL().ActOnEndOfTranslationUnit(getASTContext().getTranslationUnitDecl());
1500+
if (LangOpts.OpenACC)
1501+
OpenACC().ActOnEndOfTranslationUnit(
1502+
getASTContext().getTranslationUnitDecl());
15001503

15011504
// If there were errors, disable 'unused' warnings since they will mostly be
15021505
// noise. Don't warn for a use from a module: either we should warn on all

clang/lib/Sema/SemaOpenACC.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "clang/Sema/SemaOpenACC.h"
15+
#include "clang/AST/ASTConsumer.h"
1516
#include "clang/AST/DeclOpenACC.h"
1617
#include "clang/AST/StmtOpenACC.h"
1718
#include "clang/Basic/DiagnosticSema.h"
@@ -2457,7 +2458,8 @@ OpenACCRoutineDecl *SemaOpenACC::CheckRoutineDecl(
24572458
ArrayRef<const OpenACCClause *> Clauses, SourceLocation EndLoc) {
24582459
assert(LParenLoc.isValid());
24592460

2460-
if (FunctionDecl *FD = getFunctionFromRoutineName(FuncRef)) {
2461+
FunctionDecl *FD = nullptr;
2462+
if ((FD = getFunctionFromRoutineName(FuncRef))) {
24612463
// OpenACC 3.3 2.15:
24622464
// In C and C++, function static variables are not supported in functions to
24632465
// which a routine directive applies.
@@ -2509,11 +2511,9 @@ OpenACCRoutineDecl *SemaOpenACC::CheckRoutineDecl(
25092511
{DirLoc, BindLoc});
25102512
FD->addAttr(RAA);
25112513
// In case we are referencing not the 'latest' version, make sure we add
2512-
// the attribute to all declarations.
2513-
while (FD != FD->getMostRecentDecl()) {
2514-
FD = FD->getMostRecentDecl();
2515-
FD->addAttr(RAA);
2516-
}
2514+
// the attribute to all declarations after the 'found' one.
2515+
for (auto *CurFD : FD->redecls())
2516+
CurFD->addAttr(RAA->clone(getASTContext()));
25172517
}
25182518

25192519
LastRoutineDecl = OpenACCRoutineDecl::Create(
@@ -2522,9 +2522,20 @@ OpenACCRoutineDecl *SemaOpenACC::CheckRoutineDecl(
25222522
LastRoutineDecl->setAccess(AS_public);
25232523
getCurContext()->addDecl(LastRoutineDecl);
25242524

2525+
if (FD) {
2526+
// Add this attribute to the list of annotations so that codegen can visit
2527+
// it later. FD doesn't necessarily exist, but that case should be
2528+
// diagnosed.
2529+
RoutineRefList.emplace_back(FD, LastRoutineDecl);
2530+
}
25252531
return LastRoutineDecl;
25262532
}
25272533

2534+
void SemaOpenACC::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) {
2535+
for (auto [FD, RoutineDecl] : RoutineRefList)
2536+
SemaRef.Consumer.HandleOpenACCRoutineReference(FD, RoutineDecl);
2537+
}
2538+
25282539
DeclGroupRef SemaOpenACC::ActOnEndRoutineDeclDirective(
25292540
SourceLocation StartLoc, SourceLocation DirLoc, SourceLocation LParenLoc,
25302541
Expr *ReferencedFunc, SourceLocation RParenLoc,

0 commit comments

Comments
 (0)