diff --git a/flang/examples/CMakeLists.txt b/flang/examples/CMakeLists.txt --- a/flang/examples/CMakeLists.txt +++ b/flang/examples/CMakeLists.txt @@ -12,3 +12,4 @@ ) add_subdirectory(PrintFlangFunctionNames) +add_subdirectory(flang-omp-report-plugin) diff --git a/flang/examples/flang-omp-report-plugin/CMakeLists.txt b/flang/examples/flang-omp-report-plugin/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/flang/examples/flang-omp-report-plugin/CMakeLists.txt @@ -0,0 +1,6 @@ +add_llvm_library( + flangOmpReport + MODULE + flang-omp-report.cpp + flang-omp-report-support.cpp +) diff --git a/flang/examples/flang-omp-report-plugin/flang-omp-report-support.h b/flang/examples/flang-omp-report-plugin/flang-omp-report-support.h new file mode 100644 --- /dev/null +++ b/flang/examples/flang-omp-report-plugin/flang-omp-report-support.h @@ -0,0 +1,159 @@ +//===-- examples/flang-omp-report-plugin/flang-omp-report-support.h -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "flang/Common/Fortran-features.h" +#include "flang/Common/default-kinds.h" +#include "flang/Evaluate/expression.h" +#include "flang/Parser/characters.h" +#include "flang/Parser/message.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Parser/parsing.h" +#include "flang/Parser/provenance.h" +#include "flang/Parser/source.h" +#include "flang/Parser/unparse.h" +#include "flang/Semantics/expression.h" +#include "flang/Semantics/semantics.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Fortran::parser; +struct SummaryRecord { + int numConstructs; + int numClauses; + friend bool operator==(const SummaryRecord &a, const SummaryRecord &b) { + return a.numConstructs == b.numConstructs && a.numClauses == b.numClauses; + } + friend bool operator!=(const SummaryRecord &a, const SummaryRecord &b) { + return !(a == b); + } +}; +struct ClauseSummary { + std::string clause; + int numClauses; + friend bool operator==(const ClauseSummary &a, const ClauseSummary &b) { + return a.clause == b.clause && a.numClauses == b.numClauses; + } + friend bool operator!=(const ClauseSummary &a, const ClauseSummary &b) { + return !(a == b); + } +}; +struct ConstructSummary { + std::string construct; + int numConstructs; + std::vector clauses; + friend bool operator==(const ConstructSummary &a, const ConstructSummary &b) { + return a.construct == b.construct && a.numConstructs == b.numConstructs && + a.clauses == b.clauses; + } + friend bool operator!=(const ConstructSummary &a, const ConstructSummary &b) { + return !(a == b); + } +}; +struct ClauseInfo { + std::string clause; + std::string clauseDetails; + ClauseInfo() {} + ClauseInfo(const std::string &c, const std::string &cd) + : clause{c}, clauseDetails{cd} {} + ClauseInfo(const std::pair &p) + : clause{std::get<0>(p)}, clauseDetails{std::get<1>(p)} {} + friend bool operator<(const ClauseInfo &a, const ClauseInfo &b) { + return a.clause < b.clause; + } + friend bool operator==(const ClauseInfo &a, const ClauseInfo &b) { + return a.clause == b.clause && a.clauseDetails == b.clauseDetails; + } + friend bool operator!=(const ClauseInfo &a, const ClauseInfo &b) { + return !(a == b); + } +}; +struct LogRecord { + std::string file; + int line; + std::string construct; + std::vector clauses; + friend bool operator==(const LogRecord &a, const LogRecord &b) { + return a.file == b.file && a.line == b.line && a.construct == b.construct && + a.clauses == b.clauses; + } + friend bool operator!=(const LogRecord &a, const LogRecord &b) { + return !(a == b); + } +}; + +using OmpWrapperType = + std::variant; + +struct OpenMPCounterVisitor { + std::string normalize_construct_name(std::string s); + ClauseInfo normalize_clause_name(const std::string &s); + SourcePosition getLocation(const OmpWrapperType &w); + SourcePosition getLocation(const OpenMPDeclarativeConstruct &c); + SourcePosition getLocation(const OpenMPConstruct &c); + + std::string getName(const OmpWrapperType &w); + std::string getName(const OpenMPDeclarativeConstruct &c); + std::string getName(const OpenMPConstruct &c); + + template bool Pre(const A &) { return true; } + template void Post(const A &) {} + bool Pre(const OpenMPDeclarativeConstruct &c); + bool Pre(const OpenMPConstruct &c); + bool Pre(const OmpEndLoopDirective &c); + bool Pre(const DoConstruct &); + + void Post(const OpenMPDeclarativeConstruct &); + void Post(const OpenMPConstruct &); + void PostConstructsCommon(); + void Post(const OmpEndLoopDirective &c); + + void Post(const OmpProcBindClause::Type &c); + void Post(const OmpDefaultClause::Type &c); + void Post(const OmpDefaultmapClause::ImplicitBehavior &c); + void Post(const OmpDefaultmapClause::VariableCategory &c); + void Post(const OmpScheduleModifierType::ModType &c); + void Post(const OmpLinearModifier::Type &c); + void Post(const OmpDependenceType::Type &c); + void Post(const OmpMapType::Type &c); + void Post(const OmpScheduleClause::ScheduleType &c); + void Post(const OmpIfClause::DirectiveNameModifier &c); + void Post(const OmpCancelType::Type &c); + void Post(const OmpClause &c); + void PostClauseCommon(const ClauseInfo &ci); + void Post(const DoConstruct &); + + int clauseCounter{0}, constructCounter{0}; + std::string clauseDetails{""}; + std::map ompConstructCounter; + std::map ompClauseCounter; + std::map, int> constructClauseCount; + std::deque + constructClauses; // curLoopLogRecord and loopLogRecordStack store + // pointers to this datastructure's entries. Hence a + // vector cannot be used since pointers are invalidated + // on resize. Next best option seems to be deque. Also a + // list cannot be used since YAML gen requires a + // datastructure which can be accessed through indices. + LogRecord *curLoopLogRecord{nullptr}; + std::vector loopLogRecordStack; + std::vector ompWrapperStack; + std::map> clauseStrings; + bool isEndLoopDirective{false}; + Parsing *parsing{nullptr}; +}; + +void OpenMPStatisticsParseTree(Parsing &parsing); +void SummarizeResults(); diff --git a/flang/examples/flang-omp-report-plugin/flang-omp-report-support.cpp b/flang/examples/flang-omp-report-plugin/flang-omp-report-support.cpp new file mode 100644 --- /dev/null +++ b/flang/examples/flang-omp-report-plugin/flang-omp-report-support.cpp @@ -0,0 +1,462 @@ +//===-- examples/flang-omp-report-plugin/flang-omp-report-support.cpp -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "flang-omp-report-support.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +std::string OpenMPCounterVisitor::normalize_construct_name(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { return std::tolower(c); }); + return s; +} +ClauseInfo OpenMPCounterVisitor::normalize_clause_name(const std::string &s) { + std::size_t start = s.find('('); + std::size_t end = s.find(')'); + std::string clauseName; + if (start != std::string::npos && end != std::string::npos) { + clauseName = s.substr(0, start); + clauseDetails = s.substr(start + 1, end - start - 1); + } else { + clauseName = s; + } + std::transform(clauseName.begin(), clauseName.end(), clauseName.begin(), + [](unsigned char c) { return std::tolower(c); }); + std::transform(clauseDetails.begin(), clauseDetails.end(), + clauseDetails.begin(), [](unsigned char c) { return std::tolower(c); }); + return ClauseInfo{clauseName, clauseDetails}; +} +SourcePosition OpenMPCounterVisitor::getLocation(const OmpWrapperType &w) { + if (auto *val = std::get_if(&w)) { + const OpenMPConstruct *o{*val}; + return getLocation(*o); + } + return getLocation(*std::get(w)); +} +SourcePosition OpenMPCounterVisitor::getLocation( + const OpenMPDeclarativeConstruct &c) { + return std::visit( + [&](const auto &o) -> SourcePosition { + return parsing->allCooked().GetSourcePositionRange(o.source)->first; + }, + c.u); +} +SourcePosition OpenMPCounterVisitor::getLocation(const OpenMPConstruct &c) { + return std::visit( + Fortran::common::visitors{ + [&](const OpenMPStandaloneConstruct &c) -> SourcePosition { + return parsing->allCooked().GetSourcePositionRange(c.source)->first; + }, + // OpenMPSectionsConstruct, OpenMPLoopConstruct, + // OpenMPBlockConstruct, OpenMPCriticalConstruct Get the source from + // the directive field. + [&](const auto &c) -> SourcePosition { + const CharBlock &source{std::get<0>(c.t).source}; + return (parsing->allCooked().GetSourcePositionRange(source))->first; + }, + [&](const OpenMPAtomicConstruct &c) -> SourcePosition { + return std::visit( + [&](const auto &o) -> SourcePosition { + const CharBlock &source{std::get(o.t).source}; + return parsing->allCooked() + .GetSourcePositionRange(source) + ->first; + }, + c.u); + }, + }, + c.u); +} + +std::string OpenMPCounterVisitor::getName(const OmpWrapperType &w) { + if (auto *val = std::get_if(&w)) { + const OpenMPConstruct *o{*val}; + return getName(*o); + } + return getName(*std::get(w)); +} +std::string OpenMPCounterVisitor::getName(const OpenMPDeclarativeConstruct &c) { + return std::visit( + [&](const auto &o) -> std::string { + const CharBlock &source{std::get(o.t).source}; + return normalize_construct_name(source.ToString()); + }, + c.u); +} +std::string OpenMPCounterVisitor::getName(const OpenMPConstruct &c) { + return std::visit( + Fortran::common::visitors{ + [&](const OpenMPStandaloneConstruct &c) -> std::string { + return std::visit( + [&](const auto &c) { + // Get source from the directive or verbatim fields + const CharBlock &source{std::get<0>(c.t).source}; + return normalize_construct_name(source.ToString()); + }, + c.u); + }, + [&](const OpenMPExecutableAllocate &c) -> std::string { + const CharBlock &source{std::get<0>(c.t).source}; + return normalize_construct_name(source.ToString()); + }, + [&](const OpenMPDeclarativeAllocate &c) -> std::string { + const CharBlock &source{std::get<0>(c.t).source}; + return normalize_construct_name(source.ToString()); + }, + [&](const OpenMPAtomicConstruct &c) -> std::string { + return std::visit( + [&](const auto &c) { + // Get source from the verbatim fields + const CharBlock &source{std::get(c.t).source}; + return "atomic-" + + normalize_construct_name(source.ToString()); + }, + c.u); + }, + // OpenMPSectionsConstruct, OpenMPLoopConstruct, + // OpenMPBlockConstruct, OpenMPCriticalConstruct Get the source from + // the directive field of the begin directive or from the verbatim + // field of the begin directive in Critical + [&](const auto &c) -> std::string { + const CharBlock &source{std::get<0>(std::get<0>(c.t).t).source}; + return normalize_construct_name(source.ToString()); + }, + }, + c.u); +} + +bool OpenMPCounterVisitor::Pre(const OpenMPDeclarativeConstruct &c) { + OmpWrapperType *ow{new OmpWrapperType(&c)}; + ompWrapperStack.push_back(ow); + return true; +} +bool OpenMPCounterVisitor::Pre(const OpenMPConstruct &c) { + OmpWrapperType *ow{new OmpWrapperType(&c)}; + ompWrapperStack.push_back(ow); + return true; +} +bool OpenMPCounterVisitor::Pre(const OmpEndLoopDirective &c) { + isEndLoopDirective = true; + return true; +} +bool OpenMPCounterVisitor::Pre(const DoConstruct &) { + loopLogRecordStack.push_back(curLoopLogRecord); + return true; +} + +void OpenMPCounterVisitor::Post(const OpenMPDeclarativeConstruct &) { + PostConstructsCommon(); +} +void OpenMPCounterVisitor::Post(const OpenMPConstruct &) { + PostConstructsCommon(); +} +void OpenMPCounterVisitor::PostConstructsCommon() { + ++constructCounter; + OmpWrapperType *curConstruct = ompWrapperStack.back(); + ompConstructCounter[normalize_construct_name(getName(*curConstruct))]++; + std::sort( + clauseStrings[curConstruct].begin(), clauseStrings[curConstruct].end()); + + SourcePosition s{getLocation(*curConstruct)}; + LogRecord r{s.file.path(), s.line, getName(*curConstruct), + clauseStrings[curConstruct]}; + constructClauses.push_back(r); + + // Keep track of loop log records if it can potentially have the + // nowait clause added on later. + if (const auto *oc = std::get_if(curConstruct)) { + if (const auto *olc = std::get_if(&(*oc)->u)) { + const auto &beginLoopDir{ + std::get(olc->t)}; + const auto &beginDir{ + std::get(beginLoopDir.t)}; + if (beginDir.v == llvm::omp::Directive::OMPD_do || + beginDir.v == llvm::omp::Directive::OMPD_do_simd) { + curLoopLogRecord = &constructClauses.back(); + } + } + } + + auto it = clauseStrings.find(curConstruct); + clauseStrings.erase(it); + ompWrapperStack.pop_back(); + delete curConstruct; +} +void OpenMPCounterVisitor::Post(const OmpEndLoopDirective &c) { + isEndLoopDirective = false; +} + +void OpenMPCounterVisitor::Post(const OmpProcBindClause::Type &c) { + clauseDetails += "type=" + OmpProcBindClause::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpDefaultClause::Type &c) { + clauseDetails += "type=" + OmpDefaultClause::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post( + const OmpDefaultmapClause::ImplicitBehavior &c) { + clauseDetails += + "implicit_behavior=" + OmpDefaultmapClause::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post( + const OmpDefaultmapClause::VariableCategory &c) { + clauseDetails += + "variable_category=" + OmpDefaultmapClause::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpScheduleModifierType::ModType &c) { + clauseDetails += "modifier=" + OmpScheduleModifierType::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpLinearModifier::Type &c) { + clauseDetails += "modifier=" + OmpLinearModifier::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpDependenceType::Type &c) { + clauseDetails += "type=" + OmpDependenceType::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpMapType::Type &c) { + clauseDetails += "type=" + OmpMapType::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpScheduleClause::ScheduleType &c) { + clauseDetails += "type=" + OmpScheduleClause::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpIfClause::DirectiveNameModifier &c) { + clauseDetails += "name_modifier=" + OmpIfClause::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpCancelType::Type &c) { + clauseDetails += "type=" + OmpCancelType::EnumToString(c) + ";"; +} +void OpenMPCounterVisitor::Post(const OmpClause &c) { + PostClauseCommon(normalize_clause_name(c.source.ToString())); + clauseDetails.clear(); +} +void OpenMPCounterVisitor::PostClauseCommon(const ClauseInfo &ci) { + ++clauseCounter; + ompClauseCounter[ci.clause]++; + // The end loop construct (!$omp end do) can contain a nowait clause. + // The flang parser does not parse the end loop construct as part of + // the OpenMP construct for the loop construct. So the end loop is left + // hanging as a separate executable construct. If a nowait clause is seen in + // an end loop construct we have to find the associated loop construct and + // add nowait to its list of clauses. Note: This is not a bug in flang, the + // parse tree is corrected during semantic analysis. + if (ci.clause == "nowait" && isEndLoopDirective) { + assert(curLoopLogRecord && + "loop Construct should be visited before a nowait clause"); + constructClauseCount[std::make_pair( + curLoopLogRecord->construct, ci.clause)]++; + curLoopLogRecord->clauses.push_back(ci); + } else { + assert(!ompWrapperStack.empty() && + "Construct should be visited before clause"); + constructClauseCount[std::make_pair( + getName(*ompWrapperStack.back()), ci.clause)]++; + clauseStrings[ompWrapperStack.back()].push_back(ci); + } +} +void OpenMPCounterVisitor::Post(const DoConstruct &) { + curLoopLogRecord = loopLogRecordStack.back(); + loopLogRecordStack.pop_back(); +} + +OpenMPCounterVisitor visitor; +void OpenMPStatisticsParseTree(Fortran::parser::Parsing &parsing) { + visitor.parsing = &parsing; + const Program &parseTree{*parsing.parseTree()}; + Fortran::parser::Walk(parseTree, visitor); +} + +static void getSortedConstructSummary( + std::vector &csv, OpenMPCounterVisitor &visitor) { + typedef std::function + CmpFuncP; + CmpFuncP cmpCS = [](const ConstructSummary &a, const ConstructSummary &b) { + // Sort by: + return ( + // 1. Number of times constructs appear + (a.numConstructs > b.numConstructs) || + // 2. If 1. gives equality, compare the number of clauses + ((a.numConstructs == b.numConstructs) && + (a.clauses.size() > b.clauses.size())) || + // 3. If 1. and 2. give equality, compare names (lexicographically) + (((a.numConstructs == b.numConstructs) && + (a.clauses.size() == b.clauses.size()) && + (a.construct >= b.construct)))); + }; + + for (auto &p : visitor.ompConstructCounter) { + ConstructSummary cs; + cs.construct = p.first; + cs.numConstructs = p.second; + for (auto elem : visitor.constructClauseCount) { + if (p.first == elem.first.first) { + ClauseSummary c{elem.first.second, elem.second}; + cs.clauses.emplace_back(c); + } + } + csv.push_back(cs); + } + std::stable_sort(csv.begin(), csv.end(), cmpCS); +} + +static void getSortedClauseSummary( + std::vector &csv, OpenMPCounterVisitor &visitor) { + typedef std::function + CmpFuncCS; + CmpFuncCS cmpCS = [](const ClauseSummary &a, const ClauseSummary &b) { + // Sort by: + return ( + // 1. Number of times this clause is present + (a.numClauses > b.numClauses) || + // 2. If 1. gives equality, compare names (lexicographically) + ((a.numClauses == b.numClauses) && (a.clause > b.clause))); + }; + for (auto &p : visitor.ompClauseCounter) { + ClauseSummary cs; + cs.clause = p.first; + cs.numClauses = p.second; + csv.push_back(cs); + } + std::stable_sort(csv.begin(), csv.end(), cmpCS); +} + +class Dumper { + void openStream(llvm::StringRef fileName) { + std::error_code EC; + OS = new llvm::raw_fd_ostream( + fileName, EC, llvm::sys::fs::OpenFlags::OF_Text); + if (EC) { + llvm::errs() << "Opening file = " << fileName << "\n"; + } + } + + void closeStream() { delete OS; } + + std::string getFileName(const std::string &fileSuffix) { + std::string retStr{"tmp"}; + std::string fileExt{".yaml"}; + return retStr + fileSuffix + fileExt; + } + + template void dump(llvm::StringRef fileName, T &r) { + openStream(fileName); + llvm::yaml::Output yout(*OS); + yout << r; + closeStream(); + } + + llvm::raw_ostream *OS; + +public: + void dump(SummaryRecord &sr, std::vector &cosv, + std::vector &clsv, std::deque &lrv) { + dump(getFileName("-log"), lrv); + } +}; + +std::istream &operator>>(std::istream &is, std::deque &vlr) { + std::string ignore_line; + std::getline(is, ignore_line); + + LogRecord lr; + while (std::getline(is, lr.file, ',')) { + lr.clauses.clear(); + std::string tmp; + std::getline(is, tmp, ','); + lr.line = std::stoi(tmp); + std::string tmp2; + std::getline(is, tmp2); + size_t commaLoc{tmp2.find(',')}; + if (commaLoc != std::string::npos) { + lr.construct = tmp2.substr(0, commaLoc); + std::string clauses_str{ + tmp2.substr(commaLoc + 1, tmp2.length() - commaLoc)}; + std::istringstream iss{clauses_str}; + std::string clause; + while (std::getline(iss, clause, ',')) { + std::string clauseDetails; + std::getline(iss, clauseDetails, ','); + if (clauseDetails.empty()) { + lr.clauses.push_back(std::make_pair(clause, clauseDetails)); + } else { + lr.clauses.push_back(std::make_pair( + clause, clauseDetails.substr(1, clauseDetails.length() - 2))); + } + } + } else { + lr.construct = tmp2; + } + vlr.push_back(lr); + } + return is; +} + +LLVM_YAML_IS_SEQUENCE_VECTOR(ConstructSummary) +LLVM_YAML_IS_SEQUENCE_VECTOR(ClauseSummary) +LLVM_YAML_IS_SEQUENCE_VECTOR(LogRecord) +LLVM_YAML_IS_SEQUENCE_VECTOR(ClauseInfo) +namespace llvm { +namespace yaml { +using llvm::yaml::IO; +using llvm::yaml::MappingTraits; +template +struct SequenceTraits, + std::enable_if_t::flow>::value>> + : SequenceTraitsImpl, SequenceElementTraits::flow> {}; +template <> struct MappingTraits { + static void mapping(IO &io, SummaryRecord &info) { + io.mapRequired("numConstructs", info.numConstructs); + io.mapRequired("numClauses", info.numClauses); + } +}; +template <> struct MappingTraits { + static void mapping(IO &io, ClauseSummary &info) { + io.mapRequired("clause", info.clause); + io.mapRequired("numClauses", info.numClauses); + } +}; +template <> struct MappingTraits { + static void mapping(IO &io, ConstructSummary &info) { + io.mapRequired("construct", info.construct); + io.mapRequired("numConstructs", info.numConstructs); + io.mapRequired("clauses", info.clauses); + } +}; +template <> struct MappingTraits { + static void mapping(IO &io, ClauseInfo &info) { + io.mapRequired("clause", info.clause); + io.mapRequired("details", info.clauseDetails); + } +}; +template <> struct MappingTraits { + static void mapping(IO &io, LogRecord &info) { + io.mapRequired("file", info.file); + io.mapRequired("line", info.line); + io.mapRequired("construct", info.construct); + io.mapRequired("clauses", info.clauses); + } +}; +} // namespace yaml +} // namespace llvm + +void SummarizeResults() { + SummaryRecord sr{visitor.constructCounter, visitor.clauseCounter}; + std::vector cosv; + getSortedConstructSummary(cosv, visitor); + std::vector clsv; + getSortedClauseSummary(clsv, visitor); + { + std::unique_ptr d = std::make_unique(); + d->dump(sr, cosv, clsv, visitor.constructClauses); + } +} diff --git a/flang/examples/flang-omp-report-plugin/flang-omp-report.h b/flang/examples/flang-omp-report-plugin/flang-omp-report.h new file mode 100644 --- /dev/null +++ b/flang/examples/flang-omp-report-plugin/flang-omp-report.h @@ -0,0 +1,7 @@ +//===-- examples/flang-omp-report-plugin/flang-omp-report.h ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// diff --git a/flang/examples/flang-omp-report-plugin/flang-omp-report.cpp b/flang/examples/flang-omp-report-plugin/flang-omp-report.cpp new file mode 100644 --- /dev/null +++ b/flang/examples/flang-omp-report-plugin/flang-omp-report.cpp @@ -0,0 +1,26 @@ +//===-- examples/flang-omp-report-plugin/flang-omp-report.cpp -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "flang-omp-report-support.h" + +#include "flang/Frontend/CompilerInstance.h" +#include "flang/Frontend/FrontendActions.h" +#include "flang/Frontend/FrontendPluginRegistry.h" +#include "flang/Parser/dump-parse-tree.h" +#include "flang/Parser/parsing.h" + +using namespace Fortran::frontend; + +class FlangOmpReport : public PluginParseTreeAction { + void ExecuteAction() override { + OpenMPStatisticsParseTree(instance().parsing()); + SummarizeResults(); + } +}; + +static FrontendPluginRegistry::Add X("flang-omp-report", + "Generate a YAML summary of OpenMP constructs and clauses"); diff --git a/flang/test/Examples/Inputs/omp-atomic.f90 b/flang/test/Examples/Inputs/omp-atomic.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Examples/Inputs/omp-atomic.f90 @@ -0,0 +1,20 @@ +! Check OpenMP 2.13.6 atomic Construct + + a = 1.0 + !$omp parallel num_threads(4) shared(a) + !$omp atomic seq_cst, read + b = a + + !$omp atomic seq_cst write + a = b + !$omp end atomic + + !$omp atomic capture seq_cst + b = a + a = a + 1 + !$omp end atomic + + !$omp atomic + a = a + 1 + !$omp end parallel +end diff --git a/flang/test/Examples/Inputs/omp-declarative-directive.f90 b/flang/test/Examples/Inputs/omp-declarative-directive.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Examples/Inputs/omp-declarative-directive.f90 @@ -0,0 +1,24 @@ +! Check OpenMP declarative directives + +! 2.8.2 declare-simd + +subroutine declare_simd_1(a, b) + real(8), intent(inout) :: a, b + !$omp declare simd(declare_simd_1) aligned(a) + a = 3.14 + b +end subroutine declare_simd_1 + +! 2.10.6 declare-target +! 2.15.2 threadprivate + +module m2 +contains + subroutine foo + !$omp declare target + integer, parameter :: N=10000, M=1024 + integer :: i + real :: Q(N, N), R(N,M), S(M,M) + end subroutine foo +end module m2 + +end diff --git a/flang/test/Examples/Inputs/omp-device-constructs.f90 b/flang/test/Examples/Inputs/omp-device-constructs.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Examples/Inputs/omp-device-constructs.f90 @@ -0,0 +1,48 @@ +! Check OpenMP clause validity for the following directives: +! 2.10 Device constructs +program main + + real(8) :: arrayA(256), arrayB(256) + integer :: N + + arrayA = 1.414 + arrayB = 3.14 + N = 256 + + !$omp target map(arrayA) + do i = 1, N + a = 3.14 + enddo + !$omp end target + + !$omp target device(0) + do i = 1, N + a = 3.14 + enddo + !$omp end target + + !$omp target defaultmap(tofrom:scalar) + do i = 1, N + a = 3.14 + enddo + !$omp end target + + !$omp teams num_teams(3) thread_limit(10) default(shared) private(i) shared(a) + do i = 1, N + a = 3.14 + enddo + !$omp end teams + + !$omp target map(tofrom:a) + do i = 1, N + a = 3.14 + enddo + !$omp end target + + !$omp target data device(0) map(to:a) + do i = 1, N + a = 3.14 + enddo + !$omp end target data + +end program main diff --git a/flang/test/Examples/atomic-yaml.f90 b/flang/test/Examples/atomic-yaml.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Examples/atomic-yaml.f90 @@ -0,0 +1,39 @@ +! Check the flang-omp-report plugin for omp-atomic.f90 + +! REQUIRES: plugins + +! RUN: %flang_fc1 -load %llvmshlibdir/flangOmpReport.so -plugin flang-omp-report -fopenmp %S/Inputs/omp-atomic.f90 +! RUN: FileCheck %s --input-file=%T/../tmp-log.yaml + +! CHECK:--- +! CHECK-NEXT:- file: '{{[^"]*}}omp-atomic.f90' +! CHECK-NEXT: line: 5 +! CHECK-NEXT: construct: atomic-read +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: seq_cst +! CHECK-NEXT: details: '' +! CHECK-NEXT:- file: '{{[^"]*}}omp-atomic.f90' +! CHECK-NEXT: line: 8 +! CHECK-NEXT: construct: atomic-write +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: seq_cst +! CHECK-NEXT: details: '' +! CHECK-NEXT:- file: '{{[^"]*}}omp-atomic.f90' +! CHECK-NEXT: line: 12 +! CHECK-NEXT: construct: atomic-capture +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: seq_cst +! CHECK-NEXT: details: '' +! CHECK-NEXT:- file: '{{[^"]*}}omp-atomic.f90' +! CHECK-NEXT: line: 17 +! CHECK-NEXT: construct: atomic-atomic +! CHECK-NEXT: clauses: [] +! CHECK-NEXT:- file: '{{[^"]*}}omp-atomic.f90' +! CHECK-NEXT: line: 4 +! CHECK-NEXT: construct: parallel +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: num_threads +! CHECK-NEXT: details: '4' +! CHECK-NEXT: - clause: shared +! CHECK-NEXT: details: a +! CHECK-NEXT:... diff --git a/flang/test/Examples/declarative-yaml.f90 b/flang/test/Examples/declarative-yaml.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Examples/declarative-yaml.f90 @@ -0,0 +1,19 @@ +! Check the flang-omp-report plugin for omp-declarative-directive.f90 + +! REQUIRES: plugins + +! RUN: %flang_fc1 -load %llvmshlibdir/flangOmpReport.so -plugin flang-omp-report -fopenmp %S/Inputs/omp-declarative-directive.f90 +! RUN: FileCheck %s --input-file=%T/../tmp-log.yaml + +! CHECK:--- +! CHECK-NEXT:- file: '{{[^"]*}}omp-declarative-directive.f90' +! CHECK-NEXT: line: 7 +! CHECK-NEXT: construct: declare simd +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: aligned +! CHECK-NEXT: details: a +! CHECK-NEXT:- file: '{{[^"]*}}omp-declarative-directive.f90' +! CHECK-NEXT: line: 17 +! CHECK-NEXT: construct: declare target +! CHECK-NEXT: clauses: [] +! CHECK-NEXT:... diff --git a/flang/test/Examples/device-yaml.f90 b/flang/test/Examples/device-yaml.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Examples/device-yaml.f90 @@ -0,0 +1,55 @@ +! Check flang-omp-report --femit-yaml for omp-device-constructs.f90 + +! REQUIRES: plugins + +!RUN: %flang_fc1 -load %llvmshlibdir/flangOmpReport.so -plugin flang-omp-report -fopenmp %S/Inputs/omp-device-constructs.f90 +!RUN: cat %T/../tmp-log.yaml | FileCheck %s + +! CHECK: --- +! CHECK-NEXT: - file: '{{[^"]*}}omp-device-constructs.f90' +! CHECK-NEXT: line: 12 +! CHECK-NEXT: construct: target +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: map +! CHECK-NEXT: details: arraya +! CHECK-NEXT: - file: '{{[^"]*}}omp-device-constructs.f90' +! CHECK-NEXT: line: 18 +! CHECK-NEXT: construct: target +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: device +! CHECK-NEXT: details: '0' +! CHECK-NEXT: - file: '{{[^"]*}}omp-device-constructs.f90' +! CHECK-NEXT: line: 24 +! CHECK-NEXT: construct: target +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: defaultmap +! CHECK-NEXT: details: 'tofrom:scalar' +! CHECK-NEXT: - file: '{{[^"]*}}omp-device-constructs.f90' +! CHECK-NEXT: line: 30 +! CHECK-NEXT: construct: teams +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: default +! CHECK-NEXT: details: shared +! CHECK-NEXT: - clause: num_teams +! CHECK-NEXT: details: '3' +! CHECK-NEXT: - clause: private +! CHECK-NEXT: details: i +! CHECK-NEXT: - clause: shared +! CHECK-NEXT: details: a +! CHECK-NEXT: - clause: thread_limit +! CHECK-NEXT: details: '10' +! CHECK-NEXT: - file: '{{[^"]*}}omp-device-constructs.f90' +! CHECK-NEXT: line: 36 +! CHECK-NEXT: construct: target +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: map +! CHECK-NEXT: details: 'tofrom:a' +! CHECK-NEXT: - file: '{{[^"]*}}omp-device-constructs.f90' +! CHECK-NEXT: line: 42 +! CHECK-NEXT: construct: target data +! CHECK-NEXT: clauses: +! CHECK-NEXT: - clause: device +! CHECK-NEXT: details: '0' +! CHECK-NEXT: - clause: map +! CHECK-NEXT: details: 'to:a' +! CHECK-NEXT: ...