diff --git a/clang/include/clang/AST/ASTConsumer.h b/clang/include/clang/AST/ASTConsumer.h index 447f2592d2359..a1ef187ee2069 100644 --- a/clang/include/clang/AST/ASTConsumer.h +++ b/clang/include/clang/AST/ASTConsumer.h @@ -27,6 +27,7 @@ namespace clang { class VarDecl; class FunctionDecl; class ImportDecl; + class OpenACCRoutineDecl; /// ASTConsumer - This is an abstract interface that should be implemented by /// clients that read ASTs. This abstraction layer allows the client to be @@ -116,6 +117,11 @@ class ASTConsumer { // variable has been instantiated. virtual void HandleCXXStaticMemberVarInstantiation(VarDecl *D) {} + /// Callback to handle the end-of-translation unit attachment of OpenACC + /// routine declaration information. + virtual void HandleOpenACCRoutineReference(const FunctionDecl *FD, + const OpenACCRoutineDecl *RD) {} + /// Callback involved at the end of a translation unit to /// notify the consumer that a vtable for the given C++ class is /// required. diff --git a/clang/include/clang/CIR/CIRGenerator.h b/clang/include/clang/CIR/CIRGenerator.h index 5ea11463ffa9f..31dead2d7b585 100644 --- a/clang/include/clang/CIR/CIRGenerator.h +++ b/clang/include/clang/CIR/CIRGenerator.h @@ -81,6 +81,9 @@ class CIRGenerator : public clang::ASTConsumer { void HandleTagDeclDefinition(clang::TagDecl *d) override; void HandleTagDeclRequiredDefinition(const clang::TagDecl *D) override; void HandleCXXStaticMemberVarInstantiation(clang::VarDecl *D) override; + void + HandleOpenACCRoutineReference(const clang::FunctionDecl *FD, + const clang::OpenACCRoutineDecl *RD) override; void CompleteTentativeDefinition(clang::VarDecl *d) override; void HandleVTable(clang::CXXRecordDecl *rd) override; diff --git a/clang/include/clang/Sema/SemaOpenACC.h b/clang/include/clang/Sema/SemaOpenACC.h index f751e985ae0ff..b5e3ecab36d22 100644 --- a/clang/include/clang/Sema/SemaOpenACC.h +++ b/clang/include/clang/Sema/SemaOpenACC.h @@ -37,8 +37,16 @@ class Scope; class SemaOpenACC : public SemaBase { public: using DeclGroupPtrTy = OpaquePtr; + using RoutineRefListTy = std::pair; private: + // We save a list of routine clauses that refer to a different function(that + // is, routine-with-a-name) so that we can do the emission at the 'end'. We + // have to do this, since functions can be emitted before they are referenced, + // and the OpenACCRoutineDecl isn't necessarily emitted, as it might be in a + // function/etc. So we do these emits at the end of the TU. + llvm::SmallVector RoutineRefList; + struct ComputeConstructInfo { /// Which type of compute construct we are inside of, which we can use to /// determine whether we should add loops to the above collection. We can @@ -752,6 +760,7 @@ class SemaOpenACC : public SemaBase { }; SemaOpenACC(Sema &S); + void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU); // Called when we encounter a 'while' statement, before looking at its 'body'. void ActOnWhileStmt(SourceLocation WhileLoc); diff --git a/clang/lib/CIR/CodeGen/CIRGenDeclOpenACC.cpp b/clang/lib/CIR/CodeGen/CIRGenDeclOpenACC.cpp index d52986db49ea6..0b3a877202fb1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDeclOpenACC.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDeclOpenACC.cpp @@ -287,9 +287,82 @@ void CIRGenModule::emitGlobalOpenACCDeclareDecl(const OpenACCDeclareDecl *d) { } void CIRGenFunction::emitOpenACCRoutine(const OpenACCRoutineDecl &d) { - getCIRGenModule().errorNYI(d.getSourceRange(), "OpenACC Routine Construct"); + // Do nothing here. The OpenACCRoutineDeclAttr handles the implicit name + // cases, and the end-of-TU handling manages the named cases. This is + // necessary because these references aren't necessarily emitted themselves, + // but can be named anywhere. } void CIRGenModule::emitGlobalOpenACCRoutineDecl(const OpenACCRoutineDecl *d) { - errorNYI(d->getSourceRange(), "OpenACC Global Routine Construct"); + // Do nothing here. The OpenACCRoutineDeclAttr handles the implicit name + // cases, and the end-of-TU handling manages the named cases. This is + // necessary because these references aren't necessarily emitted themselves, + // but can be named anywhere. +} + +namespace { +class OpenACCRoutineClauseEmitter final + : public OpenACCClauseVisitor { + CIRGen::CIRGenBuilderTy &builder; + mlir::acc::RoutineOp routineOp; + llvm::SmallVector lastDeviceTypeValues; + +public: + OpenACCRoutineClauseEmitter(CIRGen::CIRGenBuilderTy &builder, + mlir::acc::RoutineOp routineOp) + : builder(builder), routineOp(routineOp) {} + + void emitClauses(ArrayRef clauses) { + this->VisitClauseList(clauses); + } + + void VisitClause(const OpenACCClause &clause) { + llvm_unreachable("Invalid OpenACC clause on routine"); + } + + void VisitSeqClause(const OpenACCSeqClause &clause) { + routineOp.addSeq(builder.getContext(), lastDeviceTypeValues); + } +}; +} // namespace + +void CIRGenModule::emitOpenACCRoutineDecl( + const clang::FunctionDecl *funcDecl, cir::FuncOp func, + SourceLocation pragmaLoc, ArrayRef clauses) { + mlir::OpBuilder::InsertionGuard guardCase(builder); + // These need to appear at the global module. + builder.setInsertionPointToEnd(&getModule().getBodyRegion().front()); + + mlir::Location routineLoc = getLoc(pragmaLoc); + + std::stringstream routineNameSS; + // This follows the same naming format as Flang. + routineNameSS << "acc_routine_" << routineCounter++; + std::string routineName = routineNameSS.str(); + + // There isn't a good constructor for RoutineOp that just takes a location + + // name + function, so we use one that creates an otherwise RoutineOp and + // count on the visitor/emitter to fill these in. + auto routineOp = mlir::acc::RoutineOp::create( + builder, routineLoc, routineName, + mlir::SymbolRefAttr::get(builder.getContext(), func.getName()), + /*implicit=*/false); + + // We have to add a pointer going the other direction via an acc.routine_info, + // from the func to the routine. + llvm::SmallVector funcRoutines; + if (auto routineInfo = + func.getOperation()->getAttrOfType( + mlir::acc::getRoutineInfoAttrName())) + funcRoutines.append(routineInfo.getAccRoutines().begin(), + routineInfo.getAccRoutines().end()); + + funcRoutines.push_back( + mlir::SymbolRefAttr::get(builder.getContext(), routineName)); + func.getOperation()->setAttr( + mlir::acc::getRoutineInfoAttrName(), + mlir::acc::RoutineInfoAttr::get(func.getContext(), funcRoutines)); + + OpenACCRoutineClauseEmitter emitter{builder, routineOp}; + emitter.emitClauses(clauses); } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 809c24f8aa670..df8b053f07915 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -2234,6 +2234,15 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name, if (!cgf) theModule.push_back(func); + + if (this->getLangOpts().OpenACC) { + // We only have to handle this attribute, since OpenACCAnnotAttrs are + // handled via the end-of-TU work. + for (const auto *attr : + funcDecl->specific_attrs()) + emitOpenACCRoutineDecl(funcDecl, func, attr->getLocation(), + attr->Clauses); + } } return func; } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 6600d086f8f61..d7aee8ebf4d7a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -461,6 +461,12 @@ class CIRGenModule : public CIRGenTypeCache { OpenACCModifierKind modifiers, bool structured, bool implicit, bool requiresDtor); + // Each of the acc.routine operations must have a unique name, so we just use + // an integer counter. This is how Flang does it, so it seems reasonable. + unsigned routineCounter = 0; + void emitOpenACCRoutineDecl(const clang::FunctionDecl *funcDecl, + cir::FuncOp func, SourceLocation pragmaLoc, + ArrayRef clauses); // C++ related functions. void emitDeclContext(const DeclContext *dc); diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp b/clang/lib/CIR/CodeGen/CIRGenerator.cpp index aa4d9eba35c04..0208eeea7146a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenerator.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp @@ -166,6 +166,18 @@ void CIRGenerator::HandleCXXStaticMemberVarInstantiation(VarDecl *D) { cgm->handleCXXStaticMemberVarInstantiation(D); } +void CIRGenerator::HandleOpenACCRoutineReference(const FunctionDecl *FD, + const OpenACCRoutineDecl *RD) { + llvm::StringRef mangledName = cgm->getMangledName(FD); + cir::FuncOp entry = + mlir::dyn_cast_if_present(cgm->getGlobalValue(mangledName)); + + // if this wasn't generated, don't force it to be. + if (!entry) + return; + cgm->emitOpenACCRoutineDecl(FD, entry, RD->getBeginLoc(), RD->clauses()); +} + void CIRGenerator::CompleteTentativeDefinition(VarDecl *d) { if (diags.hasErrorOccurred()) return; diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp index 67bb5657d4001..daec8ae409e0f 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -88,6 +88,11 @@ class CIRGenConsumer : public clang::ASTConsumer { Gen->HandleCXXStaticMemberVarInstantiation(VD); } + void HandleOpenACCRoutineReference(const FunctionDecl *FD, + const OpenACCRoutineDecl *RD) override { + Gen->HandleOpenACCRoutineReference(FD, RD); + } + void HandleInlineFunctionDefinition(FunctionDecl *D) override { Gen->HandleInlineFunctionDefinition(D); } diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 1541b2cc95d8c..d32d7b960288d 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1497,6 +1497,9 @@ void Sema::ActOnEndOfTranslationUnit() { if (LangOpts.HLSL) HLSL().ActOnEndOfTranslationUnit(getASTContext().getTranslationUnitDecl()); + if (LangOpts.OpenACC) + OpenACC().ActOnEndOfTranslationUnit( + getASTContext().getTranslationUnitDecl()); // If there were errors, disable 'unused' warnings since they will mostly be // noise. Don't warn for a use from a module: either we should warn on all diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp index f0f3832e160cd..1115efbb8305c 100644 --- a/clang/lib/Sema/SemaOpenACC.cpp +++ b/clang/lib/Sema/SemaOpenACC.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Sema/SemaOpenACC.h" +#include "clang/AST/ASTConsumer.h" #include "clang/AST/DeclOpenACC.h" #include "clang/AST/StmtOpenACC.h" #include "clang/Basic/DiagnosticSema.h" @@ -2457,7 +2458,8 @@ OpenACCRoutineDecl *SemaOpenACC::CheckRoutineDecl( ArrayRef Clauses, SourceLocation EndLoc) { assert(LParenLoc.isValid()); - if (FunctionDecl *FD = getFunctionFromRoutineName(FuncRef)) { + FunctionDecl *FD = nullptr; + if ((FD = getFunctionFromRoutineName(FuncRef))) { // OpenACC 3.3 2.15: // In C and C++, function static variables are not supported in functions to // which a routine directive applies. @@ -2509,11 +2511,9 @@ OpenACCRoutineDecl *SemaOpenACC::CheckRoutineDecl( {DirLoc, BindLoc}); FD->addAttr(RAA); // In case we are referencing not the 'latest' version, make sure we add - // the attribute to all declarations. - while (FD != FD->getMostRecentDecl()) { - FD = FD->getMostRecentDecl(); - FD->addAttr(RAA); - } + // the attribute to all declarations after the 'found' one. + for (auto *CurFD : FD->redecls()) + CurFD->addAttr(RAA->clone(getASTContext())); } LastRoutineDecl = OpenACCRoutineDecl::Create( @@ -2522,9 +2522,20 @@ OpenACCRoutineDecl *SemaOpenACC::CheckRoutineDecl( LastRoutineDecl->setAccess(AS_public); getCurContext()->addDecl(LastRoutineDecl); + if (FD) { + // Add this attribute to the list of annotations so that codegen can visit + // it later. FD doesn't necessarily exist, but that case should be + // diagnosed. + RoutineRefList.emplace_back(FD, LastRoutineDecl); + } return LastRoutineDecl; } +void SemaOpenACC::ActOnEndOfTranslationUnit(TranslationUnitDecl *TU) { + for (auto [FD, RoutineDecl] : RoutineRefList) + SemaRef.Consumer.HandleOpenACCRoutineReference(FD, RoutineDecl); +} + DeclGroupRef SemaOpenACC::ActOnEndRoutineDeclDirective( SourceLocation StartLoc, SourceLocation DirLoc, SourceLocation LParenLoc, Expr *ReferencedFunc, SourceLocation RParenLoc, diff --git a/clang/test/CIR/CodeGenOpenACC/openacc-not-implemented-global.cpp b/clang/test/CIR/CodeGenOpenACC/openacc-not-implemented-global.cpp deleted file mode 100644 index a5e4694c6f5e6..0000000000000 --- a/clang/test/CIR/CodeGenOpenACC/openacc-not-implemented-global.cpp +++ /dev/null @@ -1,6 +0,0 @@ -// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fopenacc -fclangir -emit-cir %s -o %t.cir -verify -// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fopenacc -fclangir -emit-llvm %s -o %t-cir.ll -verify - -void foo() {} -// expected-error@+1{{ClangIR code gen Not Yet Implemented: OpenACC Global Routine Construct}} -#pragma acc routine(foo) seq diff --git a/clang/test/CIR/CodeGenOpenACC/routine-anon-ns.cpp b/clang/test/CIR/CodeGenOpenACC/routine-anon-ns.cpp new file mode 100644 index 0000000000000..7c0a2edee5257 --- /dev/null +++ b/clang/test/CIR/CodeGenOpenACC/routine-anon-ns.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fopenacc -Wno-openacc-self-if-potential-conflict -emit-cir -fclangir %s -o - | FileCheck %s + +namespace { +#pragma acc routine seq + void NSFunc1(){} +#pragma acc routine seq + auto Lambda1 = [](){}; + + auto Lambda2 = [](){}; +} // namespace + +#pragma acc routine(NSFunc1) seq +#pragma acc routine(Lambda2) seq +void force_emit() { + NSFunc1(); + Lambda1(); + Lambda2(); +} + +// CHECK: cir.func{{.*}} @[[F1_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[F1_R_NAME:.*]], @[[F1_R2_NAME:.*]]]>} +// CHECK: cir.func {{.*}}lambda{{.*}} @[[L1_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[L1_R_NAME:.*]]]>} +// CHECK: cir.func {{.*}}lambda{{.*}} @[[L2_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[L2_R_NAME:.*]]]>} +// +// CHECK: acc.routine @[[F1_R_NAME]] func(@[[F1_NAME]]) seq +// CHECK: acc.routine @[[L1_R_NAME]] func(@[[L1_NAME]]) seq +// CHECK: acc.routine @[[F1_R2_NAME]] func(@[[F1_NAME]]) seq +// CHECK: acc.routine @[[L2_R_NAME]] func(@[[L2_NAME]]) seq diff --git a/clang/test/CIR/CodeGenOpenACC/routine-globals.cpp b/clang/test/CIR/CodeGenOpenACC/routine-globals.cpp new file mode 100644 index 0000000000000..5f125bbce6cb8 --- /dev/null +++ b/clang/test/CIR/CodeGenOpenACC/routine-globals.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -fopenacc -Wno-openacc-self-if-potential-conflict -emit-cir -fclangir %s -o - | FileCheck %s + +#pragma acc routine seq +auto Lambda1 = [](){}; + +auto Lambda2 = [](){}; +#pragma acc routine(Lambda2) seq +#pragma acc routine(Lambda2) seq + +#pragma acc routine seq +int GlobalFunc1(); + +int GlobalFunc2(); +#pragma acc routine(GlobalFunc2) seq +#pragma acc routine(GlobalFunc1) seq + +void force_emit() { + Lambda1(); + Lambda2(); + GlobalFunc1(); + GlobalFunc2(); +} + +// CHECK: cir.func {{.*}}lambda{{.*}} @[[L1_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[L1_R_NAME:.*]]]>} +// CHECK: cir.func {{.*}}lambda{{.*}} @[[L2_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[L2_R_NAME:.*]], @[[L2_R2_NAME:.*]]]>} +// +// CHECK: cir.func{{.*}} @[[G1_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[G1_R_NAME:.*]], @[[G1_R2_NAME:.*]]]>} +// CHECK: cir.func{{.*}} @[[G2_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[G2_R_NAME:.*]]]>} + +// CHECK: acc.routine @[[L1_R_NAME]] func(@[[L1_NAME]]) seq +// CHECK: acc.routine @[[G1_R_NAME]] func(@[[G1_NAME]]) seq +// CHECK: acc.routine @[[L2_R_NAME]] func(@[[L2_NAME]]) seq +// CHECK: acc.routine @[[L2_R2_NAME]] func(@[[L2_NAME]]) seq +// CHECK: acc.routine @[[G2_R_NAME]] func(@[[G2_NAME]]) seq +// CHECK: acc.routine @[[G1_R2_NAME]] func(@[[G1_NAME]]) seq diff --git a/clang/test/CIR/CodeGenOpenACC/routine-globals2.cpp b/clang/test/CIR/CodeGenOpenACC/routine-globals2.cpp new file mode 100644 index 0000000000000..e1aa5046684da --- /dev/null +++ b/clang/test/CIR/CodeGenOpenACC/routine-globals2.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -fopenacc -Wno-openacc-self-if-potential-conflict -emit-cir -fclangir %s -o - | FileCheck %s + +#pragma acc routine seq +void GlobalFunc4(); +#pragma acc routine(GlobalFunc4) seq + +#pragma acc routine seq +#pragma acc routine seq +void GlobalFunc5(); +#pragma acc routine(GlobalFunc5) seq +#pragma acc routine(GlobalFunc5) seq + +void GlobalFunc6(); +void GlobalFunc6(); +#pragma acc routine(GlobalFunc6) seq +void GlobalFunc6(){} + +void GlobalFunc7(){} +#pragma acc routine(GlobalFunc7) seq + +void force_emit() { + GlobalFunc4(); + GlobalFunc5(); + GlobalFunc6(); + GlobalFunc7(); +} + +// CHECK: cir.func{{.*}} @[[G6_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[G6_R_NAME:.*]]]>} +// CHECK: cir.func{{.*}} @[[G7_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[G7_R_NAME:.*]]]>} + +// CHECK: cir.func{{.*}} @[[G4_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[G4_R_NAME:.*]], @[[G4_R2_NAME:.*]]]>} +// CHECK: cir.func{{.*}} @[[G5_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[G5_R_NAME:.*]], @[[G5_R1_NAME:.*]], @[[G5_R2_NAME:.*]], @[[G5_R3_NAME:.*]]]>} + +// CHECK: acc.routine @[[G4_R_NAME]] func(@[[G4_NAME]]) seq +// CHECK: acc.routine @[[G5_R_NAME]] func(@[[G5_NAME]]) seq +// CHECK: acc.routine @[[G5_R1_NAME]] func(@[[G5_NAME]]) seq +// +// CHECK: acc.routine @[[G4_R2_NAME]] func(@[[G4_NAME]]) seq +// +// CHECK: acc.routine @[[G5_R2_NAME]] func(@[[G5_NAME]]) seq +// CHECK: acc.routine @[[G5_R3_NAME]] func(@[[G5_NAME]]) seq +// +// CHECK: acc.routine @[[G6_R_NAME]] func(@[[G6_NAME]]) seq +// CHECK: acc.routine @[[G7_R_NAME]] func(@[[G7_NAME]]) seq diff --git a/clang/test/CIR/CodeGenOpenACC/routine-locals.cpp b/clang/test/CIR/CodeGenOpenACC/routine-locals.cpp new file mode 100644 index 0000000000000..d338a9cea0d09 --- /dev/null +++ b/clang/test/CIR/CodeGenOpenACC/routine-locals.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fopenacc -Wno-openacc-self-if-potential-conflict -emit-cir -fclangir %s -o - | FileCheck %s + +void GlobalFunc(); +void InFunc() { + +#pragma acc routine(GlobalFunc) seq + GlobalFunc(); + +#pragma acc routine seq + auto Lambda1 = [](){}; + Lambda1(); + + auto Lambda2 = [](){}; +#pragma acc routine(Lambda2) seq + Lambda2(); +}; + +// CHECK: cir.func{{.*}} @[[G1_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[G1_R_NAME:.*]]]>} +// CHECK: cir.func {{.*}}lambda{{.*}} @[[L1_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[L1_R_NAME:.*]]]>} +// CHECK: cir.func {{.*}}lambda{{.*}} @[[L2_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[L2_R_NAME:.*]]]>} + +// CHECK: acc.routine @[[L1_R_NAME]] func(@[[L1_NAME]]) seq +// CHECK: acc.routine @[[G1_R_NAME]] func(@[[G1_NAME]]) seq +// CHECK: acc.routine @[[L2_R_NAME]] func(@[[L2_NAME]]) seq diff --git a/clang/test/CIR/CodeGenOpenACC/routine-members.cpp b/clang/test/CIR/CodeGenOpenACC/routine-members.cpp new file mode 100644 index 0000000000000..713500cfe3868 --- /dev/null +++ b/clang/test/CIR/CodeGenOpenACC/routine-members.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -fopenacc -Wno-openacc-self-if-potential-conflict -emit-cir -fclangir %s -o - | FileCheck %s + +struct S { +#pragma acc routine seq + void MemFunc1(); + void MemFunc2(); +#pragma acc routine(S::MemFunc2) seq + void MemFunc3(); +#pragma acc routine(S::MemFunc3) seq + +#pragma acc routine seq + static void StaticMemFunc1(); + static void StaticMemFunc2(); + static void StaticMemFunc3(); +#pragma acc routine(StaticMemFunc3) seq + +#pragma acc routine seq + static constexpr auto StaticLambda1 = [](){}; + static constexpr auto StaticLambda2 = [](){}; +}; +#pragma acc routine(S::MemFunc2) seq +#pragma acc routine(S::StaticLambda2) seq +#pragma acc routine(S::StaticMemFunc2) seq + +void force_emit() { + S{}.MemFunc1(); + S{}.MemFunc2(); + S{}.MemFunc3(); + S::StaticMemFunc1(); + S::StaticMemFunc2(); + S::StaticMemFunc3(); + S::StaticLambda1(); + S::StaticLambda2(); +} + +// CHECK: cir.func{{.*}} @[[MEM1_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[MEM1_R_NAME:.*]]]>} +// CHECK: cir.func{{.*}} @[[MEM2_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[MEM2_R_NAME:.*]], @[[MEM2_R2_NAME:.*]]]>} +// CHECK: cir.func{{.*}} @[[MEM3_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[MEM3_R_NAME:.*]]]>} +// +// CHECK: cir.func{{.*}} @[[STATICMEM1_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[STATICMEM1_R_NAME:.*]]]>} +// CHECK: cir.func{{.*}} @[[STATICMEM2_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[STATICMEM2_R_NAME:.*]]]>} +// CHECK: cir.func{{.*}} @[[STATICMEM3_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[STATICMEM3_R_NAME:.*]]]>} +// +// CHECK: cir.func {{.*}}lambda{{.*}} @[[L1_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[L1_R_NAME:.*]]]>} +// CHECK: cir.func {{.*}}lambda{{.*}} @[[L2_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[L2_R_NAME:.*]]]>} +// +// CHECK: acc.routine @[[MEM1_R_NAME]] func(@[[MEM1_NAME]]) seq +// CHECK: acc.routine @[[STATICMEM1_R_NAME]] func(@[[STATICMEM1_NAME]]) seq +// CHECK: acc.routine @[[L1_R_NAME]] func(@[[L1_NAME]]) seq +// CHECK: acc.routine @[[MEM2_R_NAME]] func(@[[MEM2_NAME]]) seq +// CHECK: acc.routine @[[MEM3_R_NAME]] func(@[[MEM3_NAME]]) seq +// CHECK: acc.routine @[[STATICMEM3_R_NAME]] func(@[[STATICMEM3_NAME]]) seq +// CHECK: acc.routine @[[MEM2_R2_NAME]] func(@[[MEM2_NAME]]) seq +// CHECK: acc.routine @[[L2_R_NAME]] func(@[[L2_NAME]]) seq +// CHECK: acc.routine @[[STATICMEM2_R_NAME]] func(@[[STATICMEM2_NAME]]) seq diff --git a/clang/test/CIR/CodeGenOpenACC/routine-ns.cpp b/clang/test/CIR/CodeGenOpenACC/routine-ns.cpp new file mode 100644 index 0000000000000..9d1d677e79db8 --- /dev/null +++ b/clang/test/CIR/CodeGenOpenACC/routine-ns.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -fopenacc -Wno-openacc-self-if-potential-conflict -emit-cir -fclangir %s -o - | FileCheck %s + +namespace NS1 { +#pragma acc routine seq + int NSFunc1(); +#pragma acc routine seq + auto Lambda1 = [](){}; + + auto Lambda2 = [](){}; +} // namespace NS1 + +#pragma acc routine(NS1::NSFunc1) seq +#pragma acc routine(NS1::Lambda2) seq + +void force_emit() { + NS1::NSFunc1(); + NS1::Lambda1(); + NS1::Lambda2(); +} + +// CHECK: cir.func{{.*}} @[[F1_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[F1_R_NAME:.*]], @[[F1_R2_NAME:.*]]]>} +// CHECK: cir.func {{.*}}lambda{{.*}} @[[L1_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[L1_R_NAME:.*]]]>} +// CHECK: cir.func {{.*}}lambda{{.*}} @[[L2_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[L2_R_NAME:.*]]]>} +// +// CHECK: acc.routine @[[F1_R_NAME]] func(@[[F1_NAME]]) seq +// CHECK: acc.routine @[[L1_R_NAME]] func(@[[L1_NAME]]) seq +// CHECK: acc.routine @[[F1_R2_NAME]] func(@[[F1_NAME]]) seq +// CHECK: acc.routine @[[L2_R_NAME]] func(@[[L2_NAME]]) seq diff --git a/clang/test/CIR/CodeGenOpenACC/routine-templ.cpp b/clang/test/CIR/CodeGenOpenACC/routine-templ.cpp new file mode 100644 index 0000000000000..419442220a1ba --- /dev/null +++ b/clang/test/CIR/CodeGenOpenACC/routine-templ.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -fopenacc -Wno-openacc-self-if-potential-conflict -emit-cir -fclangir %s -o - | FileCheck %s + +#pragma acc routine seq +template +void func(){} + +void use() { + func(); + func(); +} + +// CHECK: cir.func{{.*}} @[[T1_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[T1_R_NAME:.*]]]>} +// CHECK: cir.func{{.*}} @[[T2_NAME:[^\(]*]]({{.*}}){{.*}} attributes {acc.routine_info = #acc.routine_info<[@[[T2_R_NAME:.*]]]>} +// +// CHECK: acc.routine @[[T1_R_NAME]] func(@[[T1_NAME]]) seq +// CHECK: acc.routine @[[T2_R_NAME]] func(@[[T2_NAME]]) seq diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td index b8317b4a1d2ec..be05b9d6fbddc 100644 --- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td +++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td @@ -3232,6 +3232,18 @@ def OpenACC_RoutineOp : OpenACC_Op<"routine", [IsolatedFromAbove]> { OptionalAttr:$gangDimDeviceType); let extraClassDeclaration = [{ + // 'create' function to generate an 'empty' routine. + static RoutineOp create(::mlir::OpBuilder & builder, + ::mlir::Location location, + ::llvm::StringRef sym_name, + mlir::SymbolRefAttr func_name, bool implicit) { + return create(builder, location, sym_name, func_name, /*bindIDName=*/{}, + /*bindStrName=*/{}, /*bindIdNameDeviceType=*/{}, + /*bindStrnameDeviceType=*/{}, /*worker=*/{}, /*vector=*/{}, + /*seq=*/{}, /*nohost=*/false, implicit, /*gang=*/{}, + /*gangDim=*/{}, /*gangDimDeviceType=*/{}); + } + static StringRef getGangDimKeyword() { return "dim"; } /// Return true if the op has the worker attribute for the @@ -3267,6 +3279,9 @@ def OpenACC_RoutineOp : OpenACC_Op<"routine", [IsolatedFromAbove]> { std::optional<::std::variant> getBindNameValue(); std::optional<::std::variant> getBindNameValue(mlir::acc::DeviceType deviceType); + + // Add an entry to the 'seq' attribute for each additional device types. + void addSeq(MLIRContext *, llvm::ArrayRef); }]; let assemblyFormat = [{ diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp index 841d1d781f1a1..565af9b38cdf4 100644 --- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp +++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp @@ -4293,6 +4293,12 @@ RoutineOp::getGangDimValue(mlir::acc::DeviceType deviceType) { return std::nullopt; } +void RoutineOp::addSeq(MLIRContext *context, + llvm::ArrayRef effectiveDeviceTypes) { + setSeqAttr(addDeviceTypeAffectedOperandHelper(context, getSeqAttr(), + effectiveDeviceTypes)); +} + //===----------------------------------------------------------------------===// // InitOp //===----------------------------------------------------------------------===//