1 //===-- OpenMP.cpp -- Open MP directive lowering --------------------------===//
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 "flang/Lower/OpenMP.h"
14 #include "flang/Common/idioms.h"
15 #include "flang/Lower/Bridge.h"
16 #include "flang/Lower/FIRBuilder.h"
17 #include "flang/Lower/PFTBuilder.h"
18 #include "flang/Lower/Support/BoxValue.h"
19 #include "flang/Lower/Todo.h"
20 #include "flang/Parser/parse-tree.h"
21 #include "flang/Semantics/tools.h"
22 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
23 #include "llvm/Frontend/OpenMP/OMPConstants.h"
24
25 static const Fortran::parser::Name *
getDesignatorNameIfDataRef(const Fortran::parser::Designator & designator)26 getDesignatorNameIfDataRef(const Fortran::parser::Designator &designator) {
27 const auto *dataRef = std::get_if<Fortran::parser::DataRef>(&designator.u);
28 return dataRef ? std::get_if<Fortran::parser::Name>(&dataRef->u) : nullptr;
29 }
30
genObjectList(const Fortran::parser::OmpObjectList & objectList,Fortran::lower::AbstractConverter & converter,SmallVectorImpl<Value> & operands)31 static void genObjectList(const Fortran::parser::OmpObjectList &objectList,
32 Fortran::lower::AbstractConverter &converter,
33 SmallVectorImpl<Value> &operands) {
34 for (const auto &ompObject : objectList.v) {
35 std::visit(
36 Fortran::common::visitors{
37 [&](const Fortran::parser::Designator &designator) {
38 if (const auto *name = getDesignatorNameIfDataRef(designator)) {
39 const auto variable = converter.getSymbolAddress(*name->symbol);
40 operands.push_back(variable);
41 }
42 },
43 [&](const Fortran::parser::Name &name) {
44 const auto variable = converter.getSymbolAddress(*name.symbol);
45 operands.push_back(variable);
46 }},
47 ompObject.u);
48 }
49 }
50
51 template <typename Op>
createBodyOfOp(Op & op,Fortran::lower::FirOpBuilder & firOpBuilder,mlir::Location & loc)52 static void createBodyOfOp(Op &op, Fortran::lower::FirOpBuilder &firOpBuilder,
53 mlir::Location &loc) {
54 firOpBuilder.createBlock(&op.getRegion());
55 auto &block = op.getRegion().back();
56 firOpBuilder.setInsertionPointToStart(&block);
57 // Ensure the block is well-formed.
58 firOpBuilder.create<mlir::omp::TerminatorOp>(loc);
59 // Reset the insertion point to the start of the first block.
60 firOpBuilder.setInsertionPointToStart(&block);
61 }
62
genOMP(Fortran::lower::AbstractConverter & converter,Fortran::lower::pft::Evaluation & eval,const Fortran::parser::OpenMPSimpleStandaloneConstruct & simpleStandaloneConstruct)63 static void genOMP(Fortran::lower::AbstractConverter &converter,
64 Fortran::lower::pft::Evaluation &eval,
65 const Fortran::parser::OpenMPSimpleStandaloneConstruct
66 &simpleStandaloneConstruct) {
67 const auto &directive =
68 std::get<Fortran::parser::OmpSimpleStandaloneDirective>(
69 simpleStandaloneConstruct.t);
70 switch (directive.v) {
71 default:
72 break;
73 case llvm::omp::Directive::OMPD_barrier:
74 converter.getFirOpBuilder().create<mlir::omp::BarrierOp>(
75 converter.getCurrentLocation());
76 break;
77 case llvm::omp::Directive::OMPD_taskwait:
78 converter.getFirOpBuilder().create<mlir::omp::TaskwaitOp>(
79 converter.getCurrentLocation());
80 break;
81 case llvm::omp::Directive::OMPD_taskyield:
82 converter.getFirOpBuilder().create<mlir::omp::TaskyieldOp>(
83 converter.getCurrentLocation());
84 break;
85 case llvm::omp::Directive::OMPD_target_enter_data:
86 TODO("");
87 case llvm::omp::Directive::OMPD_target_exit_data:
88 TODO("");
89 case llvm::omp::Directive::OMPD_target_update:
90 TODO("");
91 case llvm::omp::Directive::OMPD_ordered:
92 TODO("");
93 }
94 }
95
96 static void
genOMP(Fortran::lower::AbstractConverter & converter,Fortran::lower::pft::Evaluation & eval,const Fortran::parser::OpenMPStandaloneConstruct & standaloneConstruct)97 genOMP(Fortran::lower::AbstractConverter &converter,
98 Fortran::lower::pft::Evaluation &eval,
99 const Fortran::parser::OpenMPStandaloneConstruct &standaloneConstruct) {
100 std::visit(
101 Fortran::common::visitors{
102 [&](const Fortran::parser::OpenMPSimpleStandaloneConstruct
103 &simpleStandaloneConstruct) {
104 genOMP(converter, eval, simpleStandaloneConstruct);
105 },
106 [&](const Fortran::parser::OpenMPFlushConstruct &flushConstruct) {
107 SmallVector<Value, 4> operandRange;
108 if (const auto &ompObjectList =
109 std::get<std::optional<Fortran::parser::OmpObjectList>>(
110 flushConstruct.t))
111 genObjectList(*ompObjectList, converter, operandRange);
112 if (std::get<std::optional<Fortran::parser::OmpMemoryOrderClause>>(
113 flushConstruct.t))
114 TODO("Handle OmpMemoryOrderClause");
115 converter.getFirOpBuilder().create<mlir::omp::FlushOp>(
116 converter.getCurrentLocation(), operandRange);
117 },
118 [&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) {
119 TODO("");
120 },
121 [&](const Fortran::parser::OpenMPCancellationPointConstruct
122 &cancellationPointConstruct) { TODO(""); },
123 },
124 standaloneConstruct.u);
125 }
126
127 static void
genOMP(Fortran::lower::AbstractConverter & converter,Fortran::lower::pft::Evaluation & eval,const Fortran::parser::OpenMPBlockConstruct & blockConstruct)128 genOMP(Fortran::lower::AbstractConverter &converter,
129 Fortran::lower::pft::Evaluation &eval,
130 const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
131 const auto &beginBlockDirective =
132 std::get<Fortran::parser::OmpBeginBlockDirective>(blockConstruct.t);
133 const auto &blockDirective =
134 std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t);
135
136 auto &firOpBuilder = converter.getFirOpBuilder();
137 auto currentLocation = converter.getCurrentLocation();
138 llvm::ArrayRef<mlir::Type> argTy;
139 if (blockDirective.v == llvm::omp::OMPD_parallel) {
140
141 mlir::Value ifClauseOperand, numThreadsClauseOperand;
142 SmallVector<Value, 4> privateClauseOperands, firstprivateClauseOperands,
143 sharedClauseOperands, copyinClauseOperands;
144 Attribute defaultClauseOperand, procBindClauseOperand;
145
146 const auto ¶llelOpClauseList =
147 std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t);
148 for (const auto &clause : parallelOpClauseList.v) {
149 if (const auto &ifClause =
150 std::get_if<Fortran::parser::OmpIfClause>(&clause.u)) {
151 auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->t);
152 ifClauseOperand = fir::getBase(
153 converter.genExprValue(*Fortran::semantics::GetExpr(expr)));
154 } else if (const auto &numThreadsClause =
155 std::get_if<Fortran::parser::OmpClause::NumThreads>(
156 &clause.u)) {
157 // OMPIRBuilder expects `NUM_THREAD` clause as a `Value`.
158 numThreadsClauseOperand = fir::getBase(converter.genExprValue(
159 *Fortran::semantics::GetExpr(numThreadsClause->v)));
160 } else if (const auto &privateClause =
161 std::get_if<Fortran::parser::OmpClause::Private>(
162 &clause.u)) {
163 const Fortran::parser::OmpObjectList &ompObjectList = privateClause->v;
164 genObjectList(ompObjectList, converter, privateClauseOperands);
165 } else if (const auto &firstprivateClause =
166 std::get_if<Fortran::parser::OmpClause::Firstprivate>(
167 &clause.u)) {
168 const Fortran::parser::OmpObjectList &ompObjectList =
169 firstprivateClause->v;
170 genObjectList(ompObjectList, converter, firstprivateClauseOperands);
171 } else if (const auto &sharedClause =
172 std::get_if<Fortran::parser::OmpClause::Shared>(
173 &clause.u)) {
174 const Fortran::parser::OmpObjectList &ompObjectList = sharedClause->v;
175 genObjectList(ompObjectList, converter, sharedClauseOperands);
176 } else if (const auto ©inClause =
177 std::get_if<Fortran::parser::OmpClause::Copyin>(
178 &clause.u)) {
179 const Fortran::parser::OmpObjectList &ompObjectList = copyinClause->v;
180 genObjectList(ompObjectList, converter, copyinClauseOperands);
181 }
182 }
183 // Create and insert the operation.
184 auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>(
185 currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand,
186 defaultClauseOperand.dyn_cast_or_null<StringAttr>(),
187 privateClauseOperands, firstprivateClauseOperands, sharedClauseOperands,
188 copyinClauseOperands, ValueRange(), ValueRange(),
189 procBindClauseOperand.dyn_cast_or_null<StringAttr>());
190 // Handle attribute based clauses.
191 for (const auto &clause : parallelOpClauseList.v) {
192 if (const auto &defaultClause =
193 std::get_if<Fortran::parser::OmpDefaultClause>(&clause.u)) {
194 switch (defaultClause->v) {
195 case Fortran::parser::OmpDefaultClause::Type::Private:
196 parallelOp.default_valAttr(firOpBuilder.getStringAttr(
197 omp::stringifyClauseDefault(omp::ClauseDefault::defprivate)));
198 break;
199 case Fortran::parser::OmpDefaultClause::Type::Firstprivate:
200 parallelOp.default_valAttr(
201 firOpBuilder.getStringAttr(omp::stringifyClauseDefault(
202 omp::ClauseDefault::deffirstprivate)));
203 break;
204 case Fortran::parser::OmpDefaultClause::Type::Shared:
205 parallelOp.default_valAttr(firOpBuilder.getStringAttr(
206 omp::stringifyClauseDefault(omp::ClauseDefault::defshared)));
207 break;
208 case Fortran::parser::OmpDefaultClause::Type::None:
209 parallelOp.default_valAttr(firOpBuilder.getStringAttr(
210 omp::stringifyClauseDefault(omp::ClauseDefault::defnone)));
211 break;
212 }
213 }
214 if (const auto &procBindClause =
215 std::get_if<Fortran::parser::OmpProcBindClause>(&clause.u)) {
216 switch (procBindClause->v) {
217 case Fortran::parser::OmpProcBindClause::Type::Master:
218 parallelOp.proc_bind_valAttr(
219 firOpBuilder.getStringAttr(omp::stringifyClauseProcBindKind(
220 omp::ClauseProcBindKind::master)));
221 break;
222 case Fortran::parser::OmpProcBindClause::Type::Close:
223 parallelOp.proc_bind_valAttr(
224 firOpBuilder.getStringAttr(omp::stringifyClauseProcBindKind(
225 omp::ClauseProcBindKind::close)));
226 break;
227 case Fortran::parser::OmpProcBindClause::Type::Spread:
228 parallelOp.proc_bind_valAttr(
229 firOpBuilder.getStringAttr(omp::stringifyClauseProcBindKind(
230 omp::ClauseProcBindKind::spread)));
231 break;
232 }
233 }
234 }
235 createBodyOfOp<omp::ParallelOp>(parallelOp, firOpBuilder, currentLocation);
236 } else if (blockDirective.v == llvm::omp::OMPD_master) {
237 auto masterOp =
238 firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy);
239 createBodyOfOp<omp::MasterOp>(masterOp, firOpBuilder, currentLocation);
240 }
241 }
242
genOpenMPConstruct(Fortran::lower::AbstractConverter & converter,Fortran::lower::pft::Evaluation & eval,const Fortran::parser::OpenMPConstruct & ompConstruct)243 void Fortran::lower::genOpenMPConstruct(
244 Fortran::lower::AbstractConverter &converter,
245 Fortran::lower::pft::Evaluation &eval,
246 const Fortran::parser::OpenMPConstruct &ompConstruct) {
247
248 std::visit(
249 common::visitors{
250 [&](const Fortran::parser::OpenMPStandaloneConstruct
251 &standaloneConstruct) {
252 genOMP(converter, eval, standaloneConstruct);
253 },
254 [&](const Fortran::parser::OpenMPSectionsConstruct
255 §ionsConstruct) { TODO(""); },
256 [&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
257 TODO("");
258 },
259 [&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
260 genOMP(converter, eval, blockConstruct);
261 },
262 [&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
263 TODO("");
264 },
265 [&](const Fortran::parser::OpenMPCriticalConstruct
266 &criticalConstruct) { TODO(""); },
267 },
268 ompConstruct.u);
269 }
270
genOpenMPEndLoop(Fortran::lower::AbstractConverter &,Fortran::lower::pft::Evaluation &,const Fortran::parser::OmpEndLoopDirective &)271 void Fortran::lower::genOpenMPEndLoop(
272 Fortran::lower::AbstractConverter &, Fortran::lower::pft::Evaluation &,
273 const Fortran::parser::OmpEndLoopDirective &) {
274 TODO("");
275 }
276