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         ValueRange(), ValueRange(),
209         procBindClauseOperand.dyn_cast_or_null<omp::ClauseProcBindKindAttr>());
210     // Handle attribute based clauses.
211     for (const auto &clause : parallelOpClauseList.v) {
212       // TODO: Handle default clause
213       if (const auto &procBindClause =
214               std::get_if<Fortran::parser::OmpClause::ProcBind>(&clause.u)) {
215         const auto &ompProcBindClause{procBindClause->v};
216         omp::ClauseProcBindKind pbKind;
217         switch (ompProcBindClause.v) {
218         case Fortran::parser::OmpProcBindClause::Type::Master:
219           pbKind = omp::ClauseProcBindKind::Master;
220           break;
221         case Fortran::parser::OmpProcBindClause::Type::Close:
222           pbKind = omp::ClauseProcBindKind::Close;
223           break;
224         case Fortran::parser::OmpProcBindClause::Type::Spread:
225           pbKind = omp::ClauseProcBindKind::Spread;
226           break;
227         }
228         parallelOp.proc_bind_valAttr(omp::ClauseProcBindKindAttr::get(
229             firOpBuilder.getContext(), pbKind));
230       }
231     }
232     createBodyOfOp<omp::ParallelOp>(parallelOp, firOpBuilder, currentLocation);
233   } else if (blockDirective.v == llvm::omp::OMPD_master) {
234     auto masterOp =
235         firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy);
236     createBodyOfOp<omp::MasterOp>(masterOp, firOpBuilder, currentLocation);
237 
238     // Single Construct
239   } else if (blockDirective.v == llvm::omp::OMPD_single) {
240     mlir::UnitAttr nowaitAttr;
241     for (const auto &clause :
242          std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t).v) {
243       if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
244         nowaitAttr = firOpBuilder.getUnitAttr();
245       // TODO: Handle allocate clause (D122302)
246     }
247     auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>(
248         currentLocation, /*allocate_vars=*/ValueRange(),
249         /*allocators_vars=*/ValueRange(), nowaitAttr);
250     createBodyOfOp(singleOp, firOpBuilder, currentLocation);
251   }
252 }
253 
254 static void
255 genOMP(Fortran::lower::AbstractConverter &converter,
256        Fortran::lower::pft::Evaluation &eval,
257        const Fortran::parser::OpenMPCriticalConstruct &criticalConstruct) {
258   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
259   mlir::Location currentLocation = converter.getCurrentLocation();
260   std::string name;
261   const Fortran::parser::OmpCriticalDirective &cd =
262       std::get<Fortran::parser::OmpCriticalDirective>(criticalConstruct.t);
263   if (std::get<std::optional<Fortran::parser::Name>>(cd.t).has_value()) {
264     name =
265         std::get<std::optional<Fortran::parser::Name>>(cd.t).value().ToString();
266   }
267 
268   uint64_t hint = 0;
269   const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t);
270   for (const Fortran::parser::OmpClause &clause : clauseList.v)
271     if (auto hintClause =
272             std::get_if<Fortran::parser::OmpClause::Hint>(&clause.u)) {
273       const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
274       hint = *Fortran::evaluate::ToInt64(*expr);
275       break;
276     }
277 
278   mlir::omp::CriticalOp criticalOp = [&]() {
279     if (name.empty()) {
280       return firOpBuilder.create<mlir::omp::CriticalOp>(currentLocation,
281                                                         FlatSymbolRefAttr());
282     } else {
283       mlir::ModuleOp module = firOpBuilder.getModule();
284       mlir::OpBuilder modBuilder(module.getBodyRegion());
285       auto global = module.lookupSymbol<mlir::omp::CriticalDeclareOp>(name);
286       if (!global)
287         global = modBuilder.create<mlir::omp::CriticalDeclareOp>(
288             currentLocation, name, hint);
289       return firOpBuilder.create<mlir::omp::CriticalOp>(
290           currentLocation, mlir::FlatSymbolRefAttr::get(
291                                firOpBuilder.getContext(), global.sym_name()));
292     }
293   }();
294   createBodyOfOp<omp::CriticalOp>(criticalOp, firOpBuilder, currentLocation);
295 }
296 
297 static void
298 genOMP(Fortran::lower::AbstractConverter &converter,
299        Fortran::lower::pft::Evaluation &eval,
300        const Fortran::parser::OpenMPSectionConstruct &sectionConstruct) {
301 
302   auto &firOpBuilder = converter.getFirOpBuilder();
303   auto currentLocation = converter.getCurrentLocation();
304   mlir::omp::SectionOp sectionOp =
305       firOpBuilder.create<mlir::omp::SectionOp>(currentLocation);
306   createBodyOfOp<omp::SectionOp>(sectionOp, firOpBuilder, currentLocation);
307 }
308 
309 // TODO: Add support for reduction
310 static void
311 genOMP(Fortran::lower::AbstractConverter &converter,
312        Fortran::lower::pft::Evaluation &eval,
313        const Fortran::parser::OpenMPSectionsConstruct &sectionsConstruct) {
314   auto &firOpBuilder = converter.getFirOpBuilder();
315   auto currentLocation = converter.getCurrentLocation();
316   SmallVector<Value> reductionVars, allocateOperands, allocatorOperands;
317   mlir::UnitAttr noWaitClauseOperand;
318   const auto &sectionsClauseList = std::get<Fortran::parser::OmpClauseList>(
319       std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t)
320           .t);
321   for (const Fortran::parser::OmpClause &clause : sectionsClauseList.v) {
322     if (std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u)) {
323       TODO(currentLocation, "OMPC_Reduction");
324     } else if (const auto &allocateClause =
325                    std::get_if<Fortran::parser::OmpClause::Allocate>(
326                        &clause.u)) {
327       genAllocateClause(converter, allocateClause->v, allocatorOperands,
328                         allocateOperands);
329     }
330   }
331   const auto &endSectionsClauseList =
332       std::get<Fortran::parser::OmpEndSectionsDirective>(sectionsConstruct.t);
333   const auto &clauseList =
334       std::get<Fortran::parser::OmpClauseList>(endSectionsClauseList.t);
335   for (const auto &clause : clauseList.v) {
336     if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) {
337       noWaitClauseOperand = firOpBuilder.getUnitAttr();
338     }
339   }
340 
341   mlir::omp::SectionsOp sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
342       currentLocation, reductionVars, /*reductions = */ nullptr,
343       allocateOperands, allocatorOperands, noWaitClauseOperand);
344 
345   createBodyOfOp<omp::SectionsOp>(sectionsOp, firOpBuilder, currentLocation);
346 }
347 
348 void Fortran::lower::genOpenMPConstruct(
349     Fortran::lower::AbstractConverter &converter,
350     Fortran::lower::pft::Evaluation &eval,
351     const Fortran::parser::OpenMPConstruct &ompConstruct) {
352 
353   std::visit(
354       common::visitors{
355           [&](const Fortran::parser::OpenMPStandaloneConstruct
356                   &standaloneConstruct) {
357             genOMP(converter, eval, standaloneConstruct);
358           },
359           [&](const Fortran::parser::OpenMPSectionsConstruct
360                   &sectionsConstruct) {
361             genOMP(converter, eval, sectionsConstruct);
362           },
363           [&](const Fortran::parser::OpenMPSectionConstruct &sectionConstruct) {
364             genOMP(converter, eval, sectionConstruct);
365           },
366           [&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
367             TODO(converter.getCurrentLocation(), "OpenMPLoopConstruct");
368           },
369           [&](const Fortran::parser::OpenMPDeclarativeAllocate
370                   &execAllocConstruct) {
371             TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate");
372           },
373           [&](const Fortran::parser::OpenMPExecutableAllocate
374                   &execAllocConstruct) {
375             TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate");
376           },
377           [&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
378             genOMP(converter, eval, blockConstruct);
379           },
380           [&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
381             TODO(converter.getCurrentLocation(), "OpenMPAtomicConstruct");
382           },
383           [&](const Fortran::parser::OpenMPCriticalConstruct
384                   &criticalConstruct) {
385             genOMP(converter, eval, criticalConstruct);
386           },
387       },
388       ompConstruct.u);
389 }
390