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/PFTBuilder.h"
17 #include "flang/Lower/StatementContext.h"
18 #include "flang/Lower/Todo.h"
19 #include "flang/Optimizer/Builder/BoxValue.h"
20 #include "flang/Optimizer/Builder/FIRBuilder.h"
21 #include "flang/Parser/parse-tree.h"
22 #include "flang/Semantics/tools.h"
23 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
24 #include "llvm/Frontend/OpenMP/OMPConstants.h"
25 
26 using namespace mlir;
27 
28 static const Fortran::parser::Name *
29 getDesignatorNameIfDataRef(const Fortran::parser::Designator &designator) {
30   const auto *dataRef = std::get_if<Fortran::parser::DataRef>(&designator.u);
31   return dataRef ? std::get_if<Fortran::parser::Name>(&dataRef->u) : nullptr;
32 }
33 
34 static void genObjectList(const Fortran::parser::OmpObjectList &objectList,
35                           Fortran::lower::AbstractConverter &converter,
36                           SmallVectorImpl<Value> &operands) {
37   for (const auto &ompObject : objectList.v) {
38     std::visit(
39         Fortran::common::visitors{
40             [&](const Fortran::parser::Designator &designator) {
41               if (const auto *name = getDesignatorNameIfDataRef(designator)) {
42                 const auto variable = converter.getSymbolAddress(*name->symbol);
43                 operands.push_back(variable);
44               }
45             },
46             [&](const Fortran::parser::Name &name) {
47               const auto variable = converter.getSymbolAddress(*name.symbol);
48               operands.push_back(variable);
49             }},
50         ompObject.u);
51   }
52 }
53 
54 template <typename Op>
55 static void createBodyOfOp(Op &op, fir::FirOpBuilder &firOpBuilder,
56                            mlir::Location &loc) {
57   firOpBuilder.createBlock(&op.getRegion());
58   auto &block = op.getRegion().back();
59   firOpBuilder.setInsertionPointToStart(&block);
60   // Ensure the block is well-formed.
61   firOpBuilder.create<mlir::omp::TerminatorOp>(loc);
62   // Reset the insertion point to the start of the first block.
63   firOpBuilder.setInsertionPointToStart(&block);
64 }
65 
66 static void genOMP(Fortran::lower::AbstractConverter &converter,
67                    Fortran::lower::pft::Evaluation &eval,
68                    const Fortran::parser::OpenMPSimpleStandaloneConstruct
69                        &simpleStandaloneConstruct) {
70   const auto &directive =
71       std::get<Fortran::parser::OmpSimpleStandaloneDirective>(
72           simpleStandaloneConstruct.t);
73   switch (directive.v) {
74   default:
75     break;
76   case llvm::omp::Directive::OMPD_barrier:
77     converter.getFirOpBuilder().create<mlir::omp::BarrierOp>(
78         converter.getCurrentLocation());
79     break;
80   case llvm::omp::Directive::OMPD_taskwait:
81     converter.getFirOpBuilder().create<mlir::omp::TaskwaitOp>(
82         converter.getCurrentLocation());
83     break;
84   case llvm::omp::Directive::OMPD_taskyield:
85     converter.getFirOpBuilder().create<mlir::omp::TaskyieldOp>(
86         converter.getCurrentLocation());
87     break;
88   case llvm::omp::Directive::OMPD_target_enter_data:
89     TODO(converter.getCurrentLocation(), "OMPD_target_enter_data");
90   case llvm::omp::Directive::OMPD_target_exit_data:
91     TODO(converter.getCurrentLocation(), "OMPD_target_exit_data");
92   case llvm::omp::Directive::OMPD_target_update:
93     TODO(converter.getCurrentLocation(), "OMPD_target_update");
94   case llvm::omp::Directive::OMPD_ordered:
95     TODO(converter.getCurrentLocation(), "OMPD_ordered");
96   }
97 }
98 
99 static void
100 genAllocateClause(Fortran::lower::AbstractConverter &converter,
101                   const Fortran::parser::OmpAllocateClause &ompAllocateClause,
102                   SmallVector<Value> &allocatorOperands,
103                   SmallVector<Value> &allocateOperands) {
104   auto &firOpBuilder = converter.getFirOpBuilder();
105   auto currentLocation = converter.getCurrentLocation();
106   Fortran::lower::StatementContext stmtCtx;
107 
108   mlir::Value allocatorOperand;
109   const Fortran::parser::OmpObjectList &ompObjectList =
110       std::get<Fortran::parser::OmpObjectList>(ompAllocateClause.t);
111   const auto &allocatorValue =
112       std::get<std::optional<Fortran::parser::OmpAllocateClause::Allocator>>(
113           ompAllocateClause.t);
114   // Check if allocate clause has allocator specified. If so, add it
115   // to list of allocators, otherwise, add default allocator to
116   // list of allocators.
117   if (allocatorValue) {
118     allocatorOperand = fir::getBase(converter.genExprValue(
119         *Fortran::semantics::GetExpr(allocatorValue->v), stmtCtx));
120     allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(),
121                              allocatorOperand);
122   } else {
123     allocatorOperand = firOpBuilder.createIntegerConstant(
124         currentLocation, firOpBuilder.getI32Type(), 1);
125     allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(),
126                              allocatorOperand);
127   }
128   genObjectList(ompObjectList, converter, allocateOperands);
129 }
130 
131 static void
132 genOMP(Fortran::lower::AbstractConverter &converter,
133        Fortran::lower::pft::Evaluation &eval,
134        const Fortran::parser::OpenMPStandaloneConstruct &standaloneConstruct) {
135   std::visit(
136       Fortran::common::visitors{
137           [&](const Fortran::parser::OpenMPSimpleStandaloneConstruct
138                   &simpleStandaloneConstruct) {
139             genOMP(converter, eval, simpleStandaloneConstruct);
140           },
141           [&](const Fortran::parser::OpenMPFlushConstruct &flushConstruct) {
142             SmallVector<Value, 4> operandRange;
143             if (const auto &ompObjectList =
144                     std::get<std::optional<Fortran::parser::OmpObjectList>>(
145                         flushConstruct.t))
146               genObjectList(*ompObjectList, converter, operandRange);
147             const auto &memOrderClause = std::get<std::optional<
148                 std::list<Fortran::parser::OmpMemoryOrderClause>>>(
149                 flushConstruct.t);
150             if (memOrderClause.has_value() && memOrderClause->size() > 0)
151               TODO(converter.getCurrentLocation(),
152                    "Handle OmpMemoryOrderClause");
153             converter.getFirOpBuilder().create<mlir::omp::FlushOp>(
154                 converter.getCurrentLocation(), operandRange);
155           },
156           [&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) {
157             TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
158           },
159           [&](const Fortran::parser::OpenMPCancellationPointConstruct
160                   &cancellationPointConstruct) {
161             TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
162           },
163       },
164       standaloneConstruct.u);
165 }
166 
167 static void
168 genOMP(Fortran::lower::AbstractConverter &converter,
169        Fortran::lower::pft::Evaluation &eval,
170        const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
171   const auto &beginBlockDirective =
172       std::get<Fortran::parser::OmpBeginBlockDirective>(blockConstruct.t);
173   const auto &blockDirective =
174       std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t);
175   const auto &endBlockDirective =
176       std::get<Fortran::parser::OmpEndBlockDirective>(blockConstruct.t);
177 
178   auto &firOpBuilder = converter.getFirOpBuilder();
179   auto currentLocation = converter.getCurrentLocation();
180   Fortran::lower::StatementContext stmtCtx;
181   llvm::ArrayRef<mlir::Type> argTy;
182   if (blockDirective.v == llvm::omp::OMPD_parallel) {
183 
184     mlir::Value ifClauseOperand, numThreadsClauseOperand;
185     Attribute procBindClauseOperand;
186 
187     const auto &parallelOpClauseList =
188         std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t);
189     for (const auto &clause : parallelOpClauseList.v) {
190       if (const auto &ifClause =
191               std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) {
192         auto &expr =
193             std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t);
194         ifClauseOperand = fir::getBase(converter.genExprValue(
195             *Fortran::semantics::GetExpr(expr), stmtCtx));
196       } else if (const auto &numThreadsClause =
197                      std::get_if<Fortran::parser::OmpClause::NumThreads>(
198                          &clause.u)) {
199         // OMPIRBuilder expects `NUM_THREAD` clause as a `Value`.
200         numThreadsClauseOperand = fir::getBase(converter.genExprValue(
201             *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx));
202       }
203       // TODO: Handle private, firstprivate, shared and copyin
204     }
205     // Create and insert the operation.
206     auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>(
207         currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand,
208         /*allocate_vars=*/ValueRange(), /*allocators_vars=*/ValueRange(),
209         /*reduction_vars=*/ValueRange(), /*reductions=*/nullptr,
210         procBindClauseOperand.dyn_cast_or_null<omp::ClauseProcBindKindAttr>());
211     // Handle attribute based clauses.
212     for (const auto &clause : parallelOpClauseList.v) {
213       // TODO: Handle default clause
214       if (const auto &procBindClause =
215               std::get_if<Fortran::parser::OmpClause::ProcBind>(&clause.u)) {
216         const auto &ompProcBindClause{procBindClause->v};
217         omp::ClauseProcBindKind pbKind;
218         switch (ompProcBindClause.v) {
219         case Fortran::parser::OmpProcBindClause::Type::Master:
220           pbKind = omp::ClauseProcBindKind::Master;
221           break;
222         case Fortran::parser::OmpProcBindClause::Type::Close:
223           pbKind = omp::ClauseProcBindKind::Close;
224           break;
225         case Fortran::parser::OmpProcBindClause::Type::Spread:
226           pbKind = omp::ClauseProcBindKind::Spread;
227           break;
228         }
229         parallelOp.proc_bind_valAttr(omp::ClauseProcBindKindAttr::get(
230             firOpBuilder.getContext(), pbKind));
231       }
232     }
233     createBodyOfOp<omp::ParallelOp>(parallelOp, firOpBuilder, currentLocation);
234   } else if (blockDirective.v == llvm::omp::OMPD_master) {
235     auto masterOp =
236         firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy);
237     createBodyOfOp<omp::MasterOp>(masterOp, firOpBuilder, currentLocation);
238 
239     // Single Construct
240   } else if (blockDirective.v == llvm::omp::OMPD_single) {
241     mlir::UnitAttr nowaitAttr;
242     for (const auto &clause :
243          std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t).v) {
244       if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
245         nowaitAttr = firOpBuilder.getUnitAttr();
246       // TODO: Handle allocate clause (D122302)
247     }
248     auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>(
249         currentLocation, /*allocate_vars=*/ValueRange(),
250         /*allocators_vars=*/ValueRange(), nowaitAttr);
251     createBodyOfOp(singleOp, firOpBuilder, currentLocation);
252   }
253 }
254 
255 static void
256 genOMP(Fortran::lower::AbstractConverter &converter,
257        Fortran::lower::pft::Evaluation &eval,
258        const Fortran::parser::OpenMPCriticalConstruct &criticalConstruct) {
259   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
260   mlir::Location currentLocation = converter.getCurrentLocation();
261   std::string name;
262   const Fortran::parser::OmpCriticalDirective &cd =
263       std::get<Fortran::parser::OmpCriticalDirective>(criticalConstruct.t);
264   if (std::get<std::optional<Fortran::parser::Name>>(cd.t).has_value()) {
265     name =
266         std::get<std::optional<Fortran::parser::Name>>(cd.t).value().ToString();
267   }
268 
269   uint64_t hint = 0;
270   const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t);
271   for (const Fortran::parser::OmpClause &clause : clauseList.v)
272     if (auto hintClause =
273             std::get_if<Fortran::parser::OmpClause::Hint>(&clause.u)) {
274       const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
275       hint = *Fortran::evaluate::ToInt64(*expr);
276       break;
277     }
278 
279   mlir::omp::CriticalOp criticalOp = [&]() {
280     if (name.empty()) {
281       return firOpBuilder.create<mlir::omp::CriticalOp>(currentLocation,
282                                                         FlatSymbolRefAttr());
283     } else {
284       mlir::ModuleOp module = firOpBuilder.getModule();
285       mlir::OpBuilder modBuilder(module.getBodyRegion());
286       auto global = module.lookupSymbol<mlir::omp::CriticalDeclareOp>(name);
287       if (!global)
288         global = modBuilder.create<mlir::omp::CriticalDeclareOp>(
289             currentLocation, name, hint);
290       return firOpBuilder.create<mlir::omp::CriticalOp>(
291           currentLocation, mlir::FlatSymbolRefAttr::get(
292                                firOpBuilder.getContext(), global.sym_name()));
293     }
294   }();
295   createBodyOfOp<omp::CriticalOp>(criticalOp, firOpBuilder, currentLocation);
296 }
297 
298 static void
299 genOMP(Fortran::lower::AbstractConverter &converter,
300        Fortran::lower::pft::Evaluation &eval,
301        const Fortran::parser::OpenMPSectionConstruct &sectionConstruct) {
302 
303   auto &firOpBuilder = converter.getFirOpBuilder();
304   auto currentLocation = converter.getCurrentLocation();
305   mlir::omp::SectionOp sectionOp =
306       firOpBuilder.create<mlir::omp::SectionOp>(currentLocation);
307   createBodyOfOp<omp::SectionOp>(sectionOp, firOpBuilder, currentLocation);
308 }
309 
310 // TODO: Add support for reduction
311 static void
312 genOMP(Fortran::lower::AbstractConverter &converter,
313        Fortran::lower::pft::Evaluation &eval,
314        const Fortran::parser::OpenMPSectionsConstruct &sectionsConstruct) {
315   auto &firOpBuilder = converter.getFirOpBuilder();
316   auto currentLocation = converter.getCurrentLocation();
317   SmallVector<Value> reductionVars, allocateOperands, allocatorOperands;
318   mlir::UnitAttr noWaitClauseOperand;
319   const auto &sectionsClauseList = std::get<Fortran::parser::OmpClauseList>(
320       std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t)
321           .t);
322   for (const Fortran::parser::OmpClause &clause : sectionsClauseList.v) {
323 
324     // Reduction Clause
325     if (std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u)) {
326       TODO(currentLocation, "OMPC_Reduction");
327 
328       // Allocate clause
329     } else if (const auto &allocateClause =
330                    std::get_if<Fortran::parser::OmpClause::Allocate>(
331                        &clause.u)) {
332       genAllocateClause(converter, allocateClause->v, allocatorOperands,
333                         allocateOperands);
334     }
335   }
336   const auto &endSectionsClauseList =
337       std::get<Fortran::parser::OmpEndSectionsDirective>(sectionsConstruct.t);
338   const auto &clauseList =
339       std::get<Fortran::parser::OmpClauseList>(endSectionsClauseList.t);
340   for (const auto &clause : clauseList.v) {
341     // Nowait clause
342     if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) {
343       noWaitClauseOperand = firOpBuilder.getUnitAttr();
344     }
345   }
346 
347   llvm::omp::Directive dir =
348       std::get<Fortran::parser::OmpSectionsDirective>(
349           std::get<Fortran::parser::OmpBeginSectionsDirective>(
350               sectionsConstruct.t)
351               .t)
352           .v;
353 
354   // Parallel Sections Construct
355   if (dir == llvm::omp::Directive::OMPD_parallel_sections) {
356     auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>(
357         currentLocation, /*if_expr_var*/ nullptr, /*num_threads_var*/ nullptr,
358         allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(),
359         /*reductions=*/nullptr, /*proc_bind_val*/ nullptr);
360     createBodyOfOp(parallelOp, firOpBuilder, currentLocation);
361     auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
362         currentLocation, /*reduction_vars*/ ValueRange(),
363         /*reductions=*/nullptr, /*allocate_vars*/ ValueRange(),
364         /*allocators_vars*/ ValueRange(), /*nowait=*/nullptr);
365     createBodyOfOp(sectionsOp, firOpBuilder, currentLocation);
366 
367     // Sections Construct
368   } else if (dir == llvm::omp::Directive::OMPD_sections) {
369     auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
370         currentLocation, reductionVars, /*reductions = */ nullptr,
371         allocateOperands, allocatorOperands, noWaitClauseOperand);
372     createBodyOfOp<omp::SectionsOp>(sectionsOp, firOpBuilder, currentLocation);
373   }
374 }
375 
376 void Fortran::lower::genOpenMPConstruct(
377     Fortran::lower::AbstractConverter &converter,
378     Fortran::lower::pft::Evaluation &eval,
379     const Fortran::parser::OpenMPConstruct &ompConstruct) {
380 
381   std::visit(
382       common::visitors{
383           [&](const Fortran::parser::OpenMPStandaloneConstruct
384                   &standaloneConstruct) {
385             genOMP(converter, eval, standaloneConstruct);
386           },
387           [&](const Fortran::parser::OpenMPSectionsConstruct
388                   &sectionsConstruct) {
389             genOMP(converter, eval, sectionsConstruct);
390           },
391           [&](const Fortran::parser::OpenMPSectionConstruct &sectionConstruct) {
392             genOMP(converter, eval, sectionConstruct);
393           },
394           [&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
395             TODO(converter.getCurrentLocation(), "OpenMPLoopConstruct");
396           },
397           [&](const Fortran::parser::OpenMPDeclarativeAllocate
398                   &execAllocConstruct) {
399             TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate");
400           },
401           [&](const Fortran::parser::OpenMPExecutableAllocate
402                   &execAllocConstruct) {
403             TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate");
404           },
405           [&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
406             genOMP(converter, eval, blockConstruct);
407           },
408           [&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
409             TODO(converter.getCurrentLocation(), "OpenMPAtomicConstruct");
410           },
411           [&](const Fortran::parser::OpenMPCriticalConstruct
412                   &criticalConstruct) {
413             genOMP(converter, eval, criticalConstruct);
414           },
415       },
416       ompConstruct.u);
417 }
418