Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 126 additions & 117 deletions clang/include/clang/AST/ASTConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,123 +27,132 @@ namespace clang {
class VarDecl;
class FunctionDecl;
class ImportDecl;

/// ASTConsumer - This is an abstract interface that should be implemented by
/// clients that read ASTs. This abstraction layer allows the client to be
/// independent of the AST producer (e.g. parser vs AST dump file reader, etc).
class ASTConsumer {
/// Whether this AST consumer also requires information about
/// semantic analysis.
bool SemaConsumer = false;

friend class SemaConsumer;

public:
ASTConsumer() = default;

virtual ~ASTConsumer() {}

/// Initialize - This is called to initialize the consumer, providing the
/// ASTContext.
virtual void Initialize(ASTContext &Context) {}

/// HandleTopLevelDecl - Handle the specified top-level declaration. This is
/// called by the parser to process every top-level Decl*.
///
/// \returns true to continue parsing, or false to abort parsing.
virtual bool HandleTopLevelDecl(DeclGroupRef D);

/// This callback is invoked each time an inline (method or friend)
/// function definition in a class is completed.
virtual void HandleInlineFunctionDefinition(FunctionDecl *D) {}

/// HandleInterestingDecl - Handle the specified interesting declaration. This
/// is called by the AST reader when deserializing things that might interest
/// the consumer. The default implementation forwards to HandleTopLevelDecl.
virtual void HandleInterestingDecl(DeclGroupRef D);

/// HandleTranslationUnit - This method is called when the ASTs for entire
/// translation unit have been parsed.
virtual void HandleTranslationUnit(ASTContext &Ctx) {}

/// HandleTagDeclDefinition - This callback is invoked each time a TagDecl
/// (e.g. struct, union, enum, class) is completed. This allows the client to
/// hack on the type, which can occur at any point in the file (because these
/// can be defined in declspecs).
virtual void HandleTagDeclDefinition(TagDecl *D) {}

/// This callback is invoked the first time each TagDecl is required to
/// be complete.
virtual void HandleTagDeclRequiredDefinition(const TagDecl *D) {}

/// Invoked when a function is implicitly instantiated.
/// Note that at this point it does not have a body, its body is
/// instantiated at the end of the translation unit and passed to
/// HandleTopLevelDecl.
virtual void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) {}

/// Handle the specified top-level declaration that occurred inside
/// and ObjC container.
/// The default implementation ignored them.
virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D);

/// Handle an ImportDecl that was implicitly created due to an
/// inclusion directive.
/// The default implementation passes it to HandleTopLevelDecl.
virtual void HandleImplicitImportDecl(ImportDecl *D);

/// CompleteTentativeDefinition - Callback invoked at the end of a translation
/// unit to notify the consumer that the given tentative definition should be
/// completed.
///
/// The variable declaration itself will be a tentative
/// definition. If it had an incomplete array type, its type will
/// have already been changed to an array of size 1. However, the
/// declaration remains a tentative definition and has not been
/// modified by the introduction of an implicit zero initializer.
virtual void CompleteTentativeDefinition(VarDecl *D) {}

/// CompleteExternalDeclaration - Callback invoked at the end of a translation
/// unit to notify the consumer that the given external declaration should be
/// completed.
virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {}

/// Callback invoked when an MSInheritanceAttr has been attached to a
/// CXXRecordDecl.
virtual void AssignInheritanceModel(CXXRecordDecl *RD) {}

/// HandleCXXStaticMemberVarInstantiation - Tell the consumer that this
// variable has been instantiated.
virtual void HandleCXXStaticMemberVarInstantiation(VarDecl *D) {}

/// Callback involved at the end of a translation unit to
/// notify the consumer that a vtable for the given C++ class is
/// required.
///
/// \param RD The class whose vtable was used.
virtual void HandleVTable(CXXRecordDecl *RD) {}

/// If the consumer is interested in entities getting modified after
/// their initial creation, it should return a pointer to
/// an ASTMutationListener here.
virtual ASTMutationListener *GetASTMutationListener() { return nullptr; }

/// If the consumer is interested in entities being deserialized from
/// AST files, it should return a pointer to a ASTDeserializationListener here
virtual ASTDeserializationListener *GetASTDeserializationListener() {
return nullptr;
}

/// PrintStats - If desired, print any statistics.
virtual void PrintStats() {}

/// This callback is called for each function if the Parser was
/// initialized with \c SkipFunctionBodies set to \c true.
///
/// \return \c true if the function's body should be skipped. The function
/// body may be parsed anyway if it is needed (for instance, if it contains
/// the code completion point or is constexpr).
virtual bool shouldSkipFunctionBody(Decl *D) { return true; }
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
/// independent of the AST producer (e.g. parser vs AST dump file reader,
/// etc).
class ASTConsumer {
/// Whether this AST consumer also requires information about
/// semantic analysis.
bool SemaConsumer = false;

friend class SemaConsumer;

public:
ASTConsumer() = default;

virtual ~ASTConsumer() {}

/// Initialize - This is called to initialize the consumer, providing the
/// ASTContext.
virtual void Initialize(ASTContext &Context) {}

/// HandleTopLevelDecl - Handle the specified top-level declaration. This
/// is called by the parser to process every top-level Decl*.
///
/// \returns true to continue parsing, or false to abort parsing.
virtual bool HandleTopLevelDecl(DeclGroupRef D);

/// This callback is invoked each time an inline (method or friend)
/// function definition in a class is completed.
virtual void HandleInlineFunctionDefinition(FunctionDecl *D) {}

/// HandleInterestingDecl - Handle the specified interesting declaration.
/// This is called by the AST reader when deserializing things that might
/// interest the consumer. The default implementation forwards to
/// HandleTopLevelDecl.
virtual void HandleInterestingDecl(DeclGroupRef D);

/// HandleTranslationUnit - This method is called when the ASTs for entire
/// translation unit have been parsed.
virtual void HandleTranslationUnit(ASTContext &Ctx) {}

/// HandleTagDeclDefinition - This callback is invoked each time a TagDecl
/// (e.g. struct, union, enum, class) is completed. This allows the client
/// to hack on the type, which can occur at any point in the file (because
/// these can be defined in declspecs).
virtual void HandleTagDeclDefinition(TagDecl *D) {}

/// This callback is invoked the first time each TagDecl is required to
/// be complete.
virtual void HandleTagDeclRequiredDefinition(const TagDecl *D) {}

/// Invoked when a function is implicitly instantiated.
/// Note that at this point it does not have a body, its body is
/// instantiated at the end of the translation unit and passed to
/// HandleTopLevelDecl.
virtual void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) {}

/// Handle the specified top-level declaration that occurred inside
/// and ObjC container.
/// The default implementation ignored them.
virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef D);

/// Handle an ImportDecl that was implicitly created due to an
/// inclusion directive.
/// The default implementation passes it to HandleTopLevelDecl.
virtual void HandleImplicitImportDecl(ImportDecl *D);

/// CompleteTentativeDefinition - Callback invoked at the end of a
/// translation unit to notify the consumer that the given tentative
/// definition should be completed.
///
/// The variable declaration itself will be a tentative
/// definition. If it had an incomplete array type, its type will
/// have already been changed to an array of size 1. However, the
/// declaration remains a tentative definition and has not been
/// modified by the introduction of an implicit zero initializer.
virtual void CompleteTentativeDefinition(VarDecl *D) {}

/// CompleteExternalDeclaration - Callback invoked at the end of a
/// translation unit to notify the consumer that the given external
/// declaration should be completed.
virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {}

/// Callback invoked when an MSInheritanceAttr has been attached to a
/// CXXRecordDecl.
virtual void AssignInheritanceModel(CXXRecordDecl *RD) {}

/// HandleCXXStaticMemberVarInstantiation - Tell the consumer that this
// 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.
///
/// \param RD The class whose vtable was used.
virtual void HandleVTable(CXXRecordDecl *RD) {}

/// If the consumer is interested in entities getting modified after
/// their initial creation, it should return a pointer to
/// an ASTMutationListener here.
virtual ASTMutationListener *GetASTMutationListener() { return nullptr; }

/// If the consumer is interested in entities being deserialized from
/// AST files, it should return a pointer to a ASTDeserializationListener
/// here
virtual ASTDeserializationListener *GetASTDeserializationListener() {
return nullptr;
}

/// PrintStats - If desired, print any statistics.
virtual void PrintStats() {}

/// This callback is called for each function if the Parser was
/// initialized with \c SkipFunctionBodies set to \c true.
///
/// \return \c true if the function's body should be skipped. The function
/// body may be parsed anyway if it is needed (for instance, if it contains
/// the code completion point or is constexpr).
virtual bool shouldSkipFunctionBody(Decl *D) { return true; }
};

} // end namespace clang.
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/CIR/CIRGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Sema/SemaOpenACC.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,16 @@ class Scope;
class SemaOpenACC : public SemaBase {
public:
using DeclGroupPtrTy = OpaquePtr<DeclGroupRef>;
using RoutineRefListTy = std::pair<FunctionDecl *, OpenACCRoutineDecl *>;

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<RoutineRefListTy> 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
Expand Down Expand Up @@ -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);
Expand Down
77 changes: 75 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenDeclOpenACC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<OpenACCRoutineClauseEmitter> {
CIRGen::CIRGenBuilderTy &builder;
mlir::acc::RoutineOp routineOp;
llvm::SmallVector<mlir::acc::DeviceType> lastDeviceTypeValues;

public:
OpenACCRoutineClauseEmitter(CIRGen::CIRGenBuilderTy &builder,
mlir::acc::RoutineOp routineOp)
: builder(builder), routineOp(routineOp) {}

void emitClauses(ArrayRef<const OpenACCClause *> 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<const OpenACCClause *> 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 +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're welcome to add it! :) Seems useful to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps I'll do so! I'll have to see what infra is around to do that.

// 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()), {}, {},
{}, {}, {}, {}, {}, /*hasNoHost=*/false, /*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<mlir::SymbolRefAttr> funcRoutines;
if (auto routineInfo =
func.getOperation()->getAttrOfType<mlir::acc::RoutineInfoAttr>(
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);
}
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<OpenACCRoutineDeclAttr>())
emitOpenACCRoutineDecl(funcDecl, func, attr->getLocation(),
attr->Clauses);
}
}
return func;
}
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<const OpenACCClause *> clauses);

// C++ related functions.
void emitDeclContext(const DeclContext *dc);
Expand Down
Loading
Loading