Skip to content

Commit 1cb98c8

Browse files
committed
[NFC][flang][OpenMP] Split DataSharing and Clause processors
This started as an experiment to reduce the compilation time of iterating over `Lower/OpenMP.cpp` a bit since it is too slow at the moment. Trying to do that, I split the `DataSharingProcessor` and `ClauseProcessor` into their own files and extracted some shared code into a util file. This resulted is a slightly better orgnaization of the OpenMP lowering code and hence opening this NFC. As for the compilation time, this unfortunately does not affect it much (it shaves off a few seconds of `OpenMP.cpp` compilation) since from what I learned the bottleneck is in `DirectivesCommon.h` and `PFTBuilder.h` which both consume a lot of time in template instantiation it seems.
1 parent 118a2a5 commit 1cb98c8

8 files changed

+2245
-2034
lines changed

flang/lib/Lower/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_flang_library(FortranLower
55
Allocatable.cpp
66
Bridge.cpp
77
CallInterface.cpp
8+
ClauseProcessor.cpp
89
Coarray.cpp
910
ComponentPath.cpp
1011
ConvertArrayConstructor.cpp
@@ -16,6 +17,7 @@ add_flang_library(FortranLower
1617
ConvertType.cpp
1718
ConvertVariable.cpp
1819
CustomIntrinsicCall.cpp
20+
DataSharingProcessor.cpp
1921
DumpEvaluateExpr.cpp
2022
HlfirIntrinsics.cpp
2123
HostAssociations.cpp
@@ -25,6 +27,7 @@ add_flang_library(FortranLower
2527
Mangler.cpp
2628
OpenACC.cpp
2729
OpenMP.cpp
30+
OpenMPUtils.cpp
2831
PFTBuilder.cpp
2932
Runtime.cpp
3033
SymbolMap.cpp

flang/lib/Lower/ClauseProcessor.cpp

Lines changed: 1298 additions & 0 deletions
Large diffs are not rendered by default.

flang/lib/Lower/ClauseProcessor.h

Lines changed: 414 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
//===-- DataSharingProcessor.cpp --------------------------------*- C++ -*-===//
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+
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "DataSharingProcessor.h"
14+
15+
#include "OpenMPUtils.h"
16+
#include "flang/Lower/PFTBuilder.h"
17+
#include "flang/Optimizer/Builder/Todo.h"
18+
#include "flang/Semantics/tools.h"
19+
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
20+
21+
namespace Fortran {
22+
namespace lower {
23+
namespace omp {
24+
25+
void DataSharingProcessor::processStep1() {
26+
collectSymbolsForPrivatization();
27+
collectDefaultSymbols();
28+
privatize();
29+
defaultPrivatize();
30+
insertBarrier();
31+
}
32+
33+
void DataSharingProcessor::processStep2(mlir::Operation *op, bool isLoop) {
34+
insPt = firOpBuilder.saveInsertionPoint();
35+
copyLastPrivatize(op);
36+
firOpBuilder.restoreInsertionPoint(insPt);
37+
38+
if (isLoop) {
39+
// push deallocs out of the loop
40+
firOpBuilder.setInsertionPointAfter(op);
41+
insertDeallocs();
42+
} else {
43+
// insert dummy instruction to mark the insertion position
44+
mlir::Value undefMarker = firOpBuilder.create<fir::UndefOp>(
45+
op->getLoc(), firOpBuilder.getIndexType());
46+
insertDeallocs();
47+
firOpBuilder.setInsertionPointAfter(undefMarker.getDefiningOp());
48+
}
49+
}
50+
51+
void DataSharingProcessor::insertDeallocs() {
52+
for (const Fortran::semantics::Symbol *sym : privatizedSymbols)
53+
if (Fortran::semantics::IsAllocatable(sym->GetUltimate())) {
54+
converter.createHostAssociateVarCloneDealloc(*sym);
55+
}
56+
}
57+
58+
void DataSharingProcessor::cloneSymbol(const Fortran::semantics::Symbol *sym) {
59+
// Privatization for symbols which are pre-determined (like loop index
60+
// variables) happen separately, for everything else privatize here.
61+
if (sym->test(Fortran::semantics::Symbol::Flag::OmpPreDetermined))
62+
return;
63+
bool success = converter.createHostAssociateVarClone(*sym);
64+
(void)success;
65+
assert(success && "Privatization failed due to existing binding");
66+
}
67+
68+
void DataSharingProcessor::copyFirstPrivateSymbol(
69+
const Fortran::semantics::Symbol *sym) {
70+
if (sym->test(Fortran::semantics::Symbol::Flag::OmpFirstPrivate))
71+
converter.copyHostAssociateVar(*sym);
72+
}
73+
74+
void DataSharingProcessor::copyLastPrivateSymbol(
75+
const Fortran::semantics::Symbol *sym,
76+
[[maybe_unused]] mlir::OpBuilder::InsertPoint *lastPrivIP) {
77+
if (sym->test(Fortran::semantics::Symbol::Flag::OmpLastPrivate))
78+
converter.copyHostAssociateVar(*sym, lastPrivIP);
79+
}
80+
81+
void DataSharingProcessor::collectOmpObjectListSymbol(
82+
const Fortran::parser::OmpObjectList &ompObjectList,
83+
llvm::SetVector<const Fortran::semantics::Symbol *> &symbolSet) {
84+
for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) {
85+
Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject);
86+
symbolSet.insert(sym);
87+
}
88+
}
89+
90+
void DataSharingProcessor::collectSymbolsForPrivatization() {
91+
bool hasCollapse = false;
92+
for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
93+
if (const auto &privateClause =
94+
std::get_if<Fortran::parser::OmpClause::Private>(&clause.u)) {
95+
collectOmpObjectListSymbol(privateClause->v, privatizedSymbols);
96+
} else if (const auto &firstPrivateClause =
97+
std::get_if<Fortran::parser::OmpClause::Firstprivate>(
98+
&clause.u)) {
99+
collectOmpObjectListSymbol(firstPrivateClause->v, privatizedSymbols);
100+
} else if (const auto &lastPrivateClause =
101+
std::get_if<Fortran::parser::OmpClause::Lastprivate>(
102+
&clause.u)) {
103+
collectOmpObjectListSymbol(lastPrivateClause->v, privatizedSymbols);
104+
hasLastPrivateOp = true;
105+
} else if (std::get_if<Fortran::parser::OmpClause::Collapse>(&clause.u)) {
106+
hasCollapse = true;
107+
}
108+
}
109+
110+
if (hasCollapse && hasLastPrivateOp)
111+
TODO(converter.getCurrentLocation(), "Collapse clause with lastprivate");
112+
}
113+
114+
bool DataSharingProcessor::needBarrier() {
115+
for (const Fortran::semantics::Symbol *sym : privatizedSymbols) {
116+
if (sym->test(Fortran::semantics::Symbol::Flag::OmpFirstPrivate) &&
117+
sym->test(Fortran::semantics::Symbol::Flag::OmpLastPrivate))
118+
return true;
119+
}
120+
return false;
121+
}
122+
123+
void DataSharingProcessor::insertBarrier() {
124+
// Emit implicit barrier to synchronize threads and avoid data races on
125+
// initialization of firstprivate variables and post-update of lastprivate
126+
// variables.
127+
// FIXME: Emit barrier for lastprivate clause when 'sections' directive has
128+
// 'nowait' clause. Otherwise, emit barrier when 'sections' directive has
129+
// both firstprivate and lastprivate clause.
130+
// Emit implicit barrier for linear clause. Maybe on somewhere else.
131+
if (needBarrier())
132+
firOpBuilder.create<mlir::omp::BarrierOp>(converter.getCurrentLocation());
133+
}
134+
135+
void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) {
136+
bool cmpCreated = false;
137+
mlir::OpBuilder::InsertPoint localInsPt = firOpBuilder.saveInsertionPoint();
138+
for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
139+
if (std::get_if<Fortran::parser::OmpClause::Lastprivate>(&clause.u)) {
140+
// TODO: Add lastprivate support for simd construct
141+
if (mlir::isa<mlir::omp::SectionOp>(op)) {
142+
if (&eval == &eval.parentConstruct->getLastNestedEvaluation()) {
143+
// For `omp.sections`, lastprivatized variables occur in
144+
// lexically final `omp.section` operation. The following FIR
145+
// shall be generated for the same:
146+
//
147+
// omp.sections lastprivate(...) {
148+
// omp.section {...}
149+
// omp.section {...}
150+
// omp.section {
151+
// fir.allocate for `private`/`firstprivate`
152+
// <More operations here>
153+
// fir.if %true {
154+
// ^%lpv_update_blk
155+
// }
156+
// }
157+
// }
158+
//
159+
// To keep code consistency while handling privatization
160+
// through this control flow, add a `fir.if` operation
161+
// that always evaluates to true, in order to create
162+
// a dedicated sub-region in `omp.section` where
163+
// lastprivate FIR can reside. Later canonicalizations
164+
// will optimize away this operation.
165+
if (!eval.lowerAsUnstructured()) {
166+
auto ifOp = firOpBuilder.create<fir::IfOp>(
167+
op->getLoc(),
168+
firOpBuilder.createIntegerConstant(
169+
op->getLoc(), firOpBuilder.getIntegerType(1), 0x1),
170+
/*else*/ false);
171+
firOpBuilder.setInsertionPointToStart(
172+
&ifOp.getThenRegion().front());
173+
174+
const Fortran::parser::OpenMPConstruct *parentOmpConstruct =
175+
eval.parentConstruct->getIf<Fortran::parser::OpenMPConstruct>();
176+
assert(parentOmpConstruct &&
177+
"Expected a valid enclosing OpenMP construct");
178+
const Fortran::parser::OpenMPSectionsConstruct *sectionsConstruct =
179+
std::get_if<Fortran::parser::OpenMPSectionsConstruct>(
180+
&parentOmpConstruct->u);
181+
assert(sectionsConstruct &&
182+
"Expected an enclosing omp.sections construct");
183+
const Fortran::parser::OmpClauseList &sectionsEndClauseList =
184+
std::get<Fortran::parser::OmpClauseList>(
185+
std::get<Fortran::parser::OmpEndSectionsDirective>(
186+
sectionsConstruct->t)
187+
.t);
188+
for (const Fortran::parser::OmpClause &otherClause :
189+
sectionsEndClauseList.v)
190+
if (std::get_if<Fortran::parser::OmpClause::Nowait>(
191+
&otherClause.u))
192+
// Emit implicit barrier to synchronize threads and avoid data
193+
// races on post-update of lastprivate variables when `nowait`
194+
// clause is present.
195+
firOpBuilder.create<mlir::omp::BarrierOp>(
196+
converter.getCurrentLocation());
197+
firOpBuilder.setInsertionPointToStart(
198+
&ifOp.getThenRegion().front());
199+
lastPrivIP = firOpBuilder.saveInsertionPoint();
200+
firOpBuilder.setInsertionPoint(ifOp);
201+
insPt = firOpBuilder.saveInsertionPoint();
202+
} else {
203+
// Lastprivate operation is inserted at the end
204+
// of the lexically last section in the sections
205+
// construct
206+
mlir::OpBuilder::InsertPoint unstructuredSectionsIP =
207+
firOpBuilder.saveInsertionPoint();
208+
mlir::Operation *lastOper = op->getRegion(0).back().getTerminator();
209+
firOpBuilder.setInsertionPoint(lastOper);
210+
lastPrivIP = firOpBuilder.saveInsertionPoint();
211+
firOpBuilder.restoreInsertionPoint(unstructuredSectionsIP);
212+
}
213+
}
214+
} else if (mlir::isa<mlir::omp::WsLoopOp>(op)) {
215+
// Update the original variable just before exiting the worksharing
216+
// loop. Conversion as follows:
217+
//
218+
// omp.wsloop {
219+
// omp.wsloop { ...
220+
// ... store
221+
// store ===> %v = arith.addi %iv, %step
222+
// omp.yield %cmp = %step < 0 ? %v < %ub : %v > %ub
223+
// } fir.if %cmp {
224+
// fir.store %v to %loopIV
225+
// ^%lpv_update_blk:
226+
// }
227+
// omp.yield
228+
// }
229+
//
230+
231+
// Only generate the compare once in presence of multiple LastPrivate
232+
// clauses.
233+
if (cmpCreated)
234+
continue;
235+
cmpCreated = true;
236+
237+
mlir::Location loc = op->getLoc();
238+
mlir::Operation *lastOper = op->getRegion(0).back().getTerminator();
239+
firOpBuilder.setInsertionPoint(lastOper);
240+
241+
mlir::Value iv = op->getRegion(0).front().getArguments()[0];
242+
mlir::Value ub =
243+
mlir::dyn_cast<mlir::omp::WsLoopOp>(op).getUpperBound()[0];
244+
mlir::Value step = mlir::dyn_cast<mlir::omp::WsLoopOp>(op).getStep()[0];
245+
246+
// v = iv + step
247+
// cmp = step < 0 ? v < ub : v > ub
248+
mlir::Value v = firOpBuilder.create<mlir::arith::AddIOp>(loc, iv, step);
249+
mlir::Value zero =
250+
firOpBuilder.createIntegerConstant(loc, step.getType(), 0);
251+
mlir::Value negativeStep = firOpBuilder.create<mlir::arith::CmpIOp>(
252+
loc, mlir::arith::CmpIPredicate::slt, step, zero);
253+
mlir::Value vLT = firOpBuilder.create<mlir::arith::CmpIOp>(
254+
loc, mlir::arith::CmpIPredicate::slt, v, ub);
255+
mlir::Value vGT = firOpBuilder.create<mlir::arith::CmpIOp>(
256+
loc, mlir::arith::CmpIPredicate::sgt, v, ub);
257+
mlir::Value cmpOp = firOpBuilder.create<mlir::arith::SelectOp>(
258+
loc, negativeStep, vLT, vGT);
259+
260+
auto ifOp = firOpBuilder.create<fir::IfOp>(loc, cmpOp, /*else*/ false);
261+
firOpBuilder.setInsertionPointToStart(&ifOp.getThenRegion().front());
262+
assert(loopIV && "loopIV was not set");
263+
firOpBuilder.create<fir::StoreOp>(op->getLoc(), v, loopIV);
264+
lastPrivIP = firOpBuilder.saveInsertionPoint();
265+
} else {
266+
TODO(converter.getCurrentLocation(),
267+
"lastprivate clause in constructs other than "
268+
"simd/worksharing-loop");
269+
}
270+
}
271+
}
272+
firOpBuilder.restoreInsertionPoint(localInsPt);
273+
}
274+
275+
void DataSharingProcessor::collectSymbols(
276+
Fortran::semantics::Symbol::Flag flag) {
277+
converter.collectSymbolSet(eval, defaultSymbols, flag,
278+
/*collectSymbols=*/true,
279+
/*collectHostAssociatedSymbols=*/true);
280+
for (Fortran::lower::pft::Evaluation &e : eval.getNestedEvaluations()) {
281+
if (e.hasNestedEvaluations())
282+
converter.collectSymbolSet(e, symbolsInNestedRegions, flag,
283+
/*collectSymbols=*/true,
284+
/*collectHostAssociatedSymbols=*/false);
285+
else
286+
converter.collectSymbolSet(e, symbolsInParentRegions, flag,
287+
/*collectSymbols=*/false,
288+
/*collectHostAssociatedSymbols=*/true);
289+
}
290+
}
291+
292+
void DataSharingProcessor::collectDefaultSymbols() {
293+
for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
294+
if (const auto &defaultClause =
295+
std::get_if<Fortran::parser::OmpClause::Default>(&clause.u)) {
296+
if (defaultClause->v.v ==
297+
Fortran::parser::OmpDefaultClause::Type::Private)
298+
collectSymbols(Fortran::semantics::Symbol::Flag::OmpPrivate);
299+
else if (defaultClause->v.v ==
300+
Fortran::parser::OmpDefaultClause::Type::Firstprivate)
301+
collectSymbols(Fortran::semantics::Symbol::Flag::OmpFirstPrivate);
302+
}
303+
}
304+
}
305+
306+
void DataSharingProcessor::privatize() {
307+
for (const Fortran::semantics::Symbol *sym : privatizedSymbols) {
308+
if (const auto *commonDet =
309+
sym->detailsIf<Fortran::semantics::CommonBlockDetails>()) {
310+
for (const auto &mem : commonDet->objects()) {
311+
cloneSymbol(&*mem);
312+
copyFirstPrivateSymbol(&*mem);
313+
}
314+
} else {
315+
cloneSymbol(sym);
316+
copyFirstPrivateSymbol(sym);
317+
}
318+
}
319+
}
320+
321+
void DataSharingProcessor::copyLastPrivatize(mlir::Operation *op) {
322+
insertLastPrivateCompare(op);
323+
for (const Fortran::semantics::Symbol *sym : privatizedSymbols)
324+
if (const auto *commonDet =
325+
sym->detailsIf<Fortran::semantics::CommonBlockDetails>()) {
326+
for (const auto &mem : commonDet->objects()) {
327+
copyLastPrivateSymbol(&*mem, &lastPrivIP);
328+
}
329+
} else {
330+
copyLastPrivateSymbol(sym, &lastPrivIP);
331+
}
332+
}
333+
334+
void DataSharingProcessor::defaultPrivatize() {
335+
for (const Fortran::semantics::Symbol *sym : defaultSymbols) {
336+
if (!Fortran::semantics::IsProcedure(*sym) &&
337+
!sym->GetUltimate().has<Fortran::semantics::DerivedTypeDetails>() &&
338+
!sym->GetUltimate().has<Fortran::semantics::NamelistDetails>() &&
339+
!symbolsInNestedRegions.contains(sym) &&
340+
!symbolsInParentRegions.contains(sym) &&
341+
!privatizedSymbols.contains(sym)) {
342+
cloneSymbol(sym);
343+
copyFirstPrivateSymbol(sym);
344+
}
345+
}
346+
}
347+
348+
} // namespace omp
349+
} // namespace lower
350+
} // namespace Fortran

0 commit comments

Comments
 (0)