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 *
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 
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>
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 
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
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<
113                     std::list<Fortran::parser::OmpMemoryOrderClause>>>(
114                     flushConstruct.t))
115               TODO("Handle OmpMemoryOrderClause");
116             converter.getFirOpBuilder().create<mlir::omp::FlushOp>(
117                 converter.getCurrentLocation(), operandRange);
118           },
119           [&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) {
120             TODO("");
121           },
122           [&](const Fortran::parser::OpenMPCancellationPointConstruct
123                   &cancellationPointConstruct) { TODO(""); },
124       },
125       standaloneConstruct.u);
126 }
127 
128 static void
129 genOMP(Fortran::lower::AbstractConverter &converter,
130        Fortran::lower::pft::Evaluation &eval,
131        const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
132   const auto &beginBlockDirective =
133       std::get<Fortran::parser::OmpBeginBlockDirective>(blockConstruct.t);
134   const auto &blockDirective =
135       std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t);
136 
137   auto &firOpBuilder = converter.getFirOpBuilder();
138   auto currentLocation = converter.getCurrentLocation();
139   llvm::ArrayRef<mlir::Type> argTy;
140   if (blockDirective.v == llvm::omp::OMPD_parallel) {
141 
142     mlir::Value ifClauseOperand, numThreadsClauseOperand;
143     SmallVector<Value, 4> privateClauseOperands, firstprivateClauseOperands,
144         sharedClauseOperands, copyinClauseOperands;
145     Attribute defaultClauseOperand, procBindClauseOperand;
146 
147     const auto &parallelOpClauseList =
148         std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t);
149     for (const auto &clause : parallelOpClauseList.v) {
150       if (const auto &ifClause =
151               std::get_if<Fortran::parser::OmpIfClause>(&clause.u)) {
152         auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->t);
153         ifClauseOperand = fir::getBase(
154             converter.genExprValue(*Fortran::semantics::GetExpr(expr)));
155       } else if (const auto &numThreadsClause =
156                      std::get_if<Fortran::parser::OmpClause::NumThreads>(
157                          &clause.u)) {
158         // OMPIRBuilder expects `NUM_THREAD` clause as a `Value`.
159         numThreadsClauseOperand = fir::getBase(converter.genExprValue(
160             *Fortran::semantics::GetExpr(numThreadsClause->v)));
161       } else if (const auto &privateClause =
162                      std::get_if<Fortran::parser::OmpClause::Private>(
163                          &clause.u)) {
164         const Fortran::parser::OmpObjectList &ompObjectList = privateClause->v;
165         genObjectList(ompObjectList, converter, privateClauseOperands);
166       } else if (const auto &firstprivateClause =
167                      std::get_if<Fortran::parser::OmpClause::Firstprivate>(
168                          &clause.u)) {
169         const Fortran::parser::OmpObjectList &ompObjectList =
170             firstprivateClause->v;
171         genObjectList(ompObjectList, converter, firstprivateClauseOperands);
172       } else if (const auto &sharedClause =
173                      std::get_if<Fortran::parser::OmpClause::Shared>(
174                          &clause.u)) {
175         const Fortran::parser::OmpObjectList &ompObjectList = sharedClause->v;
176         genObjectList(ompObjectList, converter, sharedClauseOperands);
177       } else if (const auto &copyinClause =
178                      std::get_if<Fortran::parser::OmpClause::Copyin>(
179                          &clause.u)) {
180         const Fortran::parser::OmpObjectList &ompObjectList = copyinClause->v;
181         genObjectList(ompObjectList, converter, copyinClauseOperands);
182       }
183     }
184     // Create and insert the operation.
185     auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>(
186         currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand,
187         defaultClauseOperand.dyn_cast_or_null<StringAttr>(),
188         privateClauseOperands, firstprivateClauseOperands, sharedClauseOperands,
189         copyinClauseOperands, ValueRange(), ValueRange(),
190         procBindClauseOperand.dyn_cast_or_null<StringAttr>());
191     // Handle attribute based clauses.
192     for (const auto &clause : parallelOpClauseList.v) {
193       if (const auto &defaultClause =
194               std::get_if<Fortran::parser::OmpClause::Default>(&clause.u)) {
195         const auto &ompDefaultClause{defaultClause->v};
196         switch (ompDefaultClause.v) {
197         case Fortran::parser::OmpDefaultClause::Type::Private:
198           parallelOp.default_valAttr(firOpBuilder.getStringAttr(
199               omp::stringifyClauseDefault(omp::ClauseDefault::defprivate)));
200           break;
201         case Fortran::parser::OmpDefaultClause::Type::Firstprivate:
202           parallelOp.default_valAttr(
203               firOpBuilder.getStringAttr(omp::stringifyClauseDefault(
204                   omp::ClauseDefault::deffirstprivate)));
205           break;
206         case Fortran::parser::OmpDefaultClause::Type::Shared:
207           parallelOp.default_valAttr(firOpBuilder.getStringAttr(
208               omp::stringifyClauseDefault(omp::ClauseDefault::defshared)));
209           break;
210         case Fortran::parser::OmpDefaultClause::Type::None:
211           parallelOp.default_valAttr(firOpBuilder.getStringAttr(
212               omp::stringifyClauseDefault(omp::ClauseDefault::defnone)));
213           break;
214         }
215       }
216       if (const auto &procBindClause =
217               std::get_if<Fortran::parser::OmpClause::ProcBind>(&clause.u)) {
218         const auto &ompProcBindClause{procBindClause->v};
219         switch (ompProcBindClause.v) {
220         case Fortran::parser::OmpProcBindClause::Type::Master:
221           parallelOp.proc_bind_valAttr(
222               firOpBuilder.getStringAttr(omp::stringifyClauseProcBindKind(
223                   omp::ClauseProcBindKind::master)));
224           break;
225         case Fortran::parser::OmpProcBindClause::Type::Close:
226           parallelOp.proc_bind_valAttr(
227               firOpBuilder.getStringAttr(omp::stringifyClauseProcBindKind(
228                   omp::ClauseProcBindKind::close)));
229           break;
230         case Fortran::parser::OmpProcBindClause::Type::Spread:
231           parallelOp.proc_bind_valAttr(
232               firOpBuilder.getStringAttr(omp::stringifyClauseProcBindKind(
233                   omp::ClauseProcBindKind::spread)));
234           break;
235         }
236       }
237     }
238     createBodyOfOp<omp::ParallelOp>(parallelOp, firOpBuilder, currentLocation);
239   } else if (blockDirective.v == llvm::omp::OMPD_master) {
240     auto masterOp =
241         firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy);
242     createBodyOfOp<omp::MasterOp>(masterOp, firOpBuilder, currentLocation);
243   }
244 }
245 
246 void Fortran::lower::genOpenMPConstruct(
247     Fortran::lower::AbstractConverter &converter,
248     Fortran::lower::pft::Evaluation &eval,
249     const Fortran::parser::OpenMPConstruct &ompConstruct) {
250 
251   std::visit(
252       common::visitors{
253           [&](const Fortran::parser::OpenMPStandaloneConstruct
254                   &standaloneConstruct) {
255             genOMP(converter, eval, standaloneConstruct);
256           },
257           [&](const Fortran::parser::OpenMPSectionsConstruct
258                   &sectionsConstruct) { TODO(""); },
259           [&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
260             TODO("");
261           },
262           [&](const Fortran::parser::OpenMPDeclarativeAllocate
263                   &execAllocConstruct) { TODO(""); },
264           [&](const Fortran::parser::OpenMPExecutableAllocate
265                   &execAllocConstruct) { TODO(""); },
266           [&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
267             genOMP(converter, eval, blockConstruct);
268           },
269           [&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
270             TODO("");
271           },
272           [&](const Fortran::parser::OpenMPCriticalConstruct
273                   &criticalConstruct) { TODO(""); },
274       },
275       ompConstruct.u);
276 }
277 
278 void Fortran::lower::genOpenMPEndLoop(
279     Fortran::lower::AbstractConverter &, Fortran::lower::pft::Evaluation &,
280     const Fortran::parser::OmpEndLoopDirective &) {
281   TODO("");
282 }
283