1 //===-- examples/flang-omp-report-plugin/flang-omp-report-visitor.cpp -----===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "flang-omp-report-visitor.h"
10 
11 namespace Fortran {
12 namespace parser {
operator <(const ClauseInfo & a,const ClauseInfo & b)13 bool operator<(const ClauseInfo &a, const ClauseInfo &b) {
14   return a.clause < b.clause;
15 }
operator ==(const ClauseInfo & a,const ClauseInfo & b)16 bool operator==(const ClauseInfo &a, const ClauseInfo &b) {
17   return a.clause == b.clause && a.clauseDetails == b.clauseDetails;
18 }
operator !=(const ClauseInfo & a,const ClauseInfo & b)19 bool operator!=(const ClauseInfo &a, const ClauseInfo &b) { return !(a == b); }
20 
operator ==(const LogRecord & a,const LogRecord & b)21 bool operator==(const LogRecord &a, const LogRecord &b) {
22   return a.file == b.file && a.line == b.line && a.construct == b.construct &&
23       a.clauses == b.clauses;
24 }
operator !=(const LogRecord & a,const LogRecord & b)25 bool operator!=(const LogRecord &a, const LogRecord &b) { return !(a == b); }
26 
normalize_construct_name(std::string s)27 std::string OpenMPCounterVisitor::normalize_construct_name(std::string s) {
28   std::transform(s.begin(), s.end(), s.begin(),
29       [](unsigned char c) { return std::tolower(c); });
30   return s;
31 }
normalize_clause_name(const std::string & s)32 ClauseInfo OpenMPCounterVisitor::normalize_clause_name(const std::string &s) {
33   std::size_t start = s.find('(');
34   std::size_t end = s.find(')');
35   std::string clauseName;
36   if (start != std::string::npos && end != std::string::npos) {
37     clauseName = s.substr(0, start);
38     clauseDetails = s.substr(start + 1, end - start - 1);
39   } else {
40     clauseName = s;
41   }
42   std::transform(clauseName.begin(), clauseName.end(), clauseName.begin(),
43       [](unsigned char c) { return std::tolower(c); });
44   std::transform(clauseDetails.begin(), clauseDetails.end(),
45       clauseDetails.begin(), [](unsigned char c) { return std::tolower(c); });
46   return ClauseInfo{clauseName, clauseDetails};
47 }
getLocation(const OmpWrapperType & w)48 SourcePosition OpenMPCounterVisitor::getLocation(const OmpWrapperType &w) {
49   if (auto *val = std::get_if<const OpenMPConstruct *>(&w)) {
50     const OpenMPConstruct *o{*val};
51     return getLocation(*o);
52   }
53   return getLocation(*std::get<const OpenMPDeclarativeConstruct *>(w));
54 }
getLocation(const OpenMPDeclarativeConstruct & c)55 SourcePosition OpenMPCounterVisitor::getLocation(
56     const OpenMPDeclarativeConstruct &c) {
57   return std::visit(
58       [&](const auto &o) -> SourcePosition {
59         return parsing->allCooked().GetSourcePositionRange(o.source)->first;
60       },
61       c.u);
62 }
getLocation(const OpenMPConstruct & c)63 SourcePosition OpenMPCounterVisitor::getLocation(const OpenMPConstruct &c) {
64   return std::visit(
65       Fortran::common::visitors{
66           [&](const OpenMPStandaloneConstruct &c) -> SourcePosition {
67             return parsing->allCooked().GetSourcePositionRange(c.source)->first;
68           },
69           // OpenMPSectionsConstruct, OpenMPLoopConstruct,
70           // OpenMPBlockConstruct, OpenMPCriticalConstruct Get the source from
71           // the directive field.
72           [&](const auto &c) -> SourcePosition {
73             const CharBlock &source{std::get<0>(c.t).source};
74             return (parsing->allCooked().GetSourcePositionRange(source))->first;
75           },
76           [&](const OpenMPAtomicConstruct &c) -> SourcePosition {
77             return std::visit(
78                 [&](const auto &o) -> SourcePosition {
79                   const CharBlock &source{std::get<Verbatim>(o.t).source};
80                   return parsing->allCooked()
81                       .GetSourcePositionRange(source)
82                       ->first;
83                 },
84                 c.u);
85           },
86       },
87       c.u);
88 }
89 
getName(const OmpWrapperType & w)90 std::string OpenMPCounterVisitor::getName(const OmpWrapperType &w) {
91   if (auto *val = std::get_if<const OpenMPConstruct *>(&w)) {
92     const OpenMPConstruct *o{*val};
93     return getName(*o);
94   }
95   return getName(*std::get<const OpenMPDeclarativeConstruct *>(w));
96 }
getName(const OpenMPDeclarativeConstruct & c)97 std::string OpenMPCounterVisitor::getName(const OpenMPDeclarativeConstruct &c) {
98   return std::visit(
99       [&](const auto &o) -> std::string {
100         const CharBlock &source{std::get<Verbatim>(o.t).source};
101         return normalize_construct_name(source.ToString());
102       },
103       c.u);
104 }
getName(const OpenMPConstruct & c)105 std::string OpenMPCounterVisitor::getName(const OpenMPConstruct &c) {
106   return std::visit(
107       Fortran::common::visitors{
108           [&](const OpenMPStandaloneConstruct &c) -> std::string {
109             return std::visit(
110                 [&](const auto &c) {
111                   // Get source from the directive or verbatim fields
112                   const CharBlock &source{std::get<0>(c.t).source};
113                   return normalize_construct_name(source.ToString());
114                 },
115                 c.u);
116           },
117           [&](const OpenMPExecutableAllocate &c) -> std::string {
118             const CharBlock &source{std::get<0>(c.t).source};
119             return normalize_construct_name(source.ToString());
120           },
121           [&](const OpenMPDeclarativeAllocate &c) -> std::string {
122             const CharBlock &source{std::get<0>(c.t).source};
123             return normalize_construct_name(source.ToString());
124           },
125           [&](const OpenMPAtomicConstruct &c) -> std::string {
126             return std::visit(
127                 [&](const auto &c) {
128                   // Get source from the verbatim fields
129                   const CharBlock &source{std::get<Verbatim>(c.t).source};
130                   return "atomic-" +
131                       normalize_construct_name(source.ToString());
132                 },
133                 c.u);
134           },
135           // OpenMPSectionsConstruct, OpenMPLoopConstruct,
136           // OpenMPBlockConstruct, OpenMPCriticalConstruct Get the source from
137           // the directive field of the begin directive or from the verbatim
138           // field of the begin directive in Critical
139           [&](const auto &c) -> std::string {
140             const CharBlock &source{std::get<0>(std::get<0>(c.t).t).source};
141             return normalize_construct_name(source.ToString());
142           },
143       },
144       c.u);
145 }
146 
Pre(const OpenMPDeclarativeConstruct & c)147 bool OpenMPCounterVisitor::Pre(const OpenMPDeclarativeConstruct &c) {
148   OmpWrapperType *ow{new OmpWrapperType(&c)};
149   ompWrapperStack.push_back(ow);
150   return true;
151 }
Pre(const OpenMPConstruct & c)152 bool OpenMPCounterVisitor::Pre(const OpenMPConstruct &c) {
153   OmpWrapperType *ow{new OmpWrapperType(&c)};
154   ompWrapperStack.push_back(ow);
155   return true;
156 }
Pre(const OmpEndLoopDirective & c)157 bool OpenMPCounterVisitor::Pre(const OmpEndLoopDirective &c) { return true; }
Pre(const DoConstruct &)158 bool OpenMPCounterVisitor::Pre(const DoConstruct &) {
159   loopLogRecordStack.push_back(curLoopLogRecord);
160   return true;
161 }
162 
Post(const OpenMPDeclarativeConstruct &)163 void OpenMPCounterVisitor::Post(const OpenMPDeclarativeConstruct &) {
164   PostConstructsCommon();
165 }
Post(const OpenMPConstruct &)166 void OpenMPCounterVisitor::Post(const OpenMPConstruct &) {
167   PostConstructsCommon();
168 }
PostConstructsCommon()169 void OpenMPCounterVisitor::PostConstructsCommon() {
170   OmpWrapperType *curConstruct = ompWrapperStack.back();
171   std::sort(
172       clauseStrings[curConstruct].begin(), clauseStrings[curConstruct].end());
173 
174   SourcePosition s{getLocation(*curConstruct)};
175   LogRecord r{s.file.path(), s.line, getName(*curConstruct),
176       clauseStrings[curConstruct]};
177   constructClauses.push_back(r);
178 
179   // Keep track of loop log records if it can potentially have the
180   // nowait clause added on later.
181   if (const auto *oc = std::get_if<const OpenMPConstruct *>(curConstruct)) {
182     if (const auto *olc = std::get_if<OpenMPLoopConstruct>(&(*oc)->u)) {
183       const auto &beginLoopDir{
184           std::get<Fortran::parser::OmpBeginLoopDirective>(olc->t)};
185       const auto &beginDir{
186           std::get<Fortran::parser::OmpLoopDirective>(beginLoopDir.t)};
187       if (beginDir.v == llvm::omp::Directive::OMPD_do ||
188           beginDir.v == llvm::omp::Directive::OMPD_do_simd) {
189         curLoopLogRecord = &constructClauses.back();
190       }
191     }
192   }
193 
194   auto it = clauseStrings.find(curConstruct);
195   clauseStrings.erase(it);
196   ompWrapperStack.pop_back();
197   delete curConstruct;
198 }
Post(const OmpEndLoopDirective & c)199 void OpenMPCounterVisitor::Post(const OmpEndLoopDirective &c) {}
200 
Post(const OmpProcBindClause::Type & c)201 void OpenMPCounterVisitor::Post(const OmpProcBindClause::Type &c) {
202   clauseDetails += "type=" + OmpProcBindClause::EnumToString(c) + ";";
203 }
Post(const OmpDefaultClause::Type & c)204 void OpenMPCounterVisitor::Post(const OmpDefaultClause::Type &c) {
205   clauseDetails += "type=" + OmpDefaultClause::EnumToString(c) + ";";
206 }
Post(const OmpDefaultmapClause::ImplicitBehavior & c)207 void OpenMPCounterVisitor::Post(
208     const OmpDefaultmapClause::ImplicitBehavior &c) {
209   clauseDetails +=
210       "implicit_behavior=" + OmpDefaultmapClause::EnumToString(c) + ";";
211 }
Post(const OmpDefaultmapClause::VariableCategory & c)212 void OpenMPCounterVisitor::Post(
213     const OmpDefaultmapClause::VariableCategory &c) {
214   clauseDetails +=
215       "variable_category=" + OmpDefaultmapClause::EnumToString(c) + ";";
216 }
Post(const OmpScheduleModifierType::ModType & c)217 void OpenMPCounterVisitor::Post(const OmpScheduleModifierType::ModType &c) {
218   clauseDetails += "modifier=" + OmpScheduleModifierType::EnumToString(c) + ";";
219 }
Post(const OmpLinearModifier::Type & c)220 void OpenMPCounterVisitor::Post(const OmpLinearModifier::Type &c) {
221   clauseDetails += "modifier=" + OmpLinearModifier::EnumToString(c) + ";";
222 }
Post(const OmpDependenceType::Type & c)223 void OpenMPCounterVisitor::Post(const OmpDependenceType::Type &c) {
224   clauseDetails += "type=" + OmpDependenceType::EnumToString(c) + ";";
225 }
Post(const OmpMapType::Type & c)226 void OpenMPCounterVisitor::Post(const OmpMapType::Type &c) {
227   clauseDetails += "type=" + OmpMapType::EnumToString(c) + ";";
228 }
Post(const OmpScheduleClause::ScheduleType & c)229 void OpenMPCounterVisitor::Post(const OmpScheduleClause::ScheduleType &c) {
230   clauseDetails += "type=" + OmpScheduleClause::EnumToString(c) + ";";
231 }
Post(const OmpIfClause::DirectiveNameModifier & c)232 void OpenMPCounterVisitor::Post(const OmpIfClause::DirectiveNameModifier &c) {
233   clauseDetails += "name_modifier=" + OmpIfClause::EnumToString(c) + ";";
234 }
Post(const OmpCancelType::Type & c)235 void OpenMPCounterVisitor::Post(const OmpCancelType::Type &c) {
236   clauseDetails += "type=" + OmpCancelType::EnumToString(c) + ";";
237 }
Post(const OmpClause & c)238 void OpenMPCounterVisitor::Post(const OmpClause &c) {
239   PostClauseCommon(normalize_clause_name(c.source.ToString()));
240   clauseDetails.clear();
241 }
PostClauseCommon(const ClauseInfo & ci)242 void OpenMPCounterVisitor::PostClauseCommon(const ClauseInfo &ci) {
243   // The end loop construct (!$omp end do) can contain a nowait clause.
244   // The flang parser does not parse the end loop construct as part of
245   // the OpenMP construct for the loop construct. So the end loop is left
246   // hanging as a separate executable construct. If a nowait clause is seen in
247   // an end loop construct we have to find the associated loop construct and
248   // add nowait to its list of clauses. Note: This is not a bug in flang, the
249   // parse tree is corrected during semantic analysis.
250   if (ci.clause == "nowait") {
251     assert(curLoopLogRecord &&
252         "loop Construct should be visited before a nowait clause");
253     constructClauseCount[std::make_pair(
254         curLoopLogRecord->construct, ci.clause)]++;
255     curLoopLogRecord->clauses.push_back(ci);
256   } else {
257     assert(!ompWrapperStack.empty() &&
258         "Construct should be visited before clause");
259     constructClauseCount[std::make_pair(
260         getName(*ompWrapperStack.back()), ci.clause)]++;
261     clauseStrings[ompWrapperStack.back()].push_back(ci);
262   }
263 }
Post(const DoConstruct &)264 void OpenMPCounterVisitor::Post(const DoConstruct &) {
265   curLoopLogRecord = loopLogRecordStack.back();
266   loopLogRecordStack.pop_back();
267 }
268 } // namespace parser
269 } // namespace Fortran
270