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