1 //===-- OpenACC.cpp -- OpenACC 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/OpenACC.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/Parser/parse-tree.h"
19 #include "flang/Semantics/tools.h"
20 #include "mlir/Dialect/OpenACC/OpenACC.h"
21 #include "llvm/Frontend/OpenACC/ACC.h.inc"
22 
23 #define TODO() llvm_unreachable("not yet implemented")
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::AccObjectList &objectList,
32                           Fortran::lower::AbstractConverter &converter,
33                           SmallVectorImpl<Value> &operands) {
34   for (const auto &accObject : 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         accObject.u);
48   }
49 }
50 
51 static void addOperands(SmallVectorImpl<Value> &operands,
52                         SmallVectorImpl<int32_t> &operandSegments,
53                         const SmallVectorImpl<Value> &clauseOperands) {
54   operands.append(clauseOperands.begin(), clauseOperands.end());
55   operandSegments.push_back(clauseOperands.size());
56 }
57 
58 static void addOperand(SmallVectorImpl<Value> &operands,
59                        SmallVectorImpl<int32_t> &operandSegments,
60                        const Value &clauseOperand) {
61   if (clauseOperand) {
62     operands.push_back(clauseOperand);
63     operandSegments.push_back(1);
64   } else {
65     operandSegments.push_back(0);
66   }
67 }
68 
69 template <typename Op, typename Terminator>
70 static Op createRegionOp(Fortran::lower::FirOpBuilder &builder,
71                          mlir::Location loc,
72                          const SmallVectorImpl<Value> &operands,
73                          const SmallVectorImpl<int32_t> &operandSegments) {
74   llvm::ArrayRef<mlir::Type> argTy;
75   Op op = builder.create<Op>(loc, argTy, operands);
76   builder.createBlock(&op.getRegion());
77   auto &block = op.getRegion().back();
78   builder.setInsertionPointToStart(&block);
79   builder.create<Terminator>(loc);
80 
81   op.setAttr(Op::getOperandSegmentSizeAttr(),
82              builder.getI32VectorAttr(operandSegments));
83 
84   // Place the insertion point to the start of the first block.
85   builder.setInsertionPointToStart(&block);
86 
87   return op;
88 }
89 
90 static void genACC(Fortran::lower::AbstractConverter &converter,
91                    Fortran::lower::pft::Evaluation &eval,
92                    const Fortran::parser::OpenACCLoopConstruct &loopConstruct) {
93 
94   const auto &beginLoopDirective =
95       std::get<Fortran::parser::AccBeginLoopDirective>(loopConstruct.t);
96   const auto &loopDirective =
97       std::get<Fortran::parser::AccLoopDirective>(beginLoopDirective.t);
98 
99   if (loopDirective.v == llvm::acc::ACCD_loop) {
100     auto &firOpBuilder = converter.getFirOpBuilder();
101     auto currentLocation = converter.getCurrentLocation();
102 
103     // Add attribute extracted from clauses.
104     const auto &accClauseList =
105         std::get<Fortran::parser::AccClauseList>(beginLoopDirective.t);
106 
107     mlir::Value workerNum;
108     mlir::Value vectorLength;
109     mlir::Value gangNum;
110     mlir::Value gangStatic;
111     SmallVector<Value, 2> tileOperands, privateOperands, reductionOperands;
112     std::int64_t executionMapping = mlir::acc::OpenACCExecMapping::NONE;
113 
114     // Lower clauses values mapped to operands.
115     for (const auto &clause : accClauseList.v) {
116       if (const auto *gangClause =
117               std::get_if<Fortran::parser::AccClause::Gang>(&clause.u)) {
118         if (gangClause->v) {
119           const Fortran::parser::AccGangArgument &x = *gangClause->v;
120           if (const auto &gangNumValue =
121                   std::get<std::optional<Fortran::parser::ScalarIntExpr>>(
122                       x.t)) {
123             gangNum = converter.genExprValue(
124                 *Fortran::semantics::GetExpr(gangNumValue.value()));
125           }
126           if (const auto &gangStaticValue =
127                   std::get<std::optional<Fortran::parser::AccSizeExpr>>(x.t)) {
128             const auto &expr =
129                 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(
130                     gangStaticValue.value().t);
131             if (expr) {
132               gangStatic =
133                   converter.genExprValue(*Fortran::semantics::GetExpr(*expr));
134             } else {
135               // * was passed as value and will be represented as a -1 constant
136               // integer.
137               gangStatic = firOpBuilder.createIntegerConstant(
138                   currentLocation, firOpBuilder.getIntegerType(32),
139                   /* STAR */ -1);
140             }
141           }
142         }
143         executionMapping |= mlir::acc::OpenACCExecMapping::GANG;
144       } else if (const auto *workerClause =
145                      std::get_if<Fortran::parser::AccClause::Worker>(
146                          &clause.u)) {
147         if (workerClause->v) {
148           workerNum = converter.genExprValue(
149               *Fortran::semantics::GetExpr(*workerClause->v));
150         }
151         executionMapping |= mlir::acc::OpenACCExecMapping::WORKER;
152       } else if (const auto *vectorClause =
153                      std::get_if<Fortran::parser::AccClause::Vector>(
154                          &clause.u)) {
155         if (vectorClause->v) {
156           vectorLength = converter.genExprValue(
157               *Fortran::semantics::GetExpr(*vectorClause->v));
158         }
159         executionMapping |= mlir::acc::OpenACCExecMapping::VECTOR;
160       } else if (const auto *tileClause =
161                      std::get_if<Fortran::parser::AccClause::Tile>(&clause.u)) {
162         const Fortran::parser::AccTileExprList &accTileExprList = tileClause->v;
163         for (const auto &accTileExpr : accTileExprList.v) {
164           const auto &expr =
165               std::get<std::optional<Fortran::parser::ScalarIntConstantExpr>>(
166                   accTileExpr.t);
167           if (expr) {
168             tileOperands.push_back(
169                 converter.genExprValue(*Fortran::semantics::GetExpr(*expr)));
170           } else {
171             // * was passed as value and will be represented as a -1 constant
172             // integer.
173             mlir::Value tileStar = firOpBuilder.createIntegerConstant(
174                 currentLocation, firOpBuilder.getIntegerType(32),
175                 /* STAR */ -1);
176             tileOperands.push_back(tileStar);
177           }
178         }
179       } else if (const auto *privateClause =
180                      std::get_if<Fortran::parser::AccClause::Private>(
181                          &clause.u)) {
182         genObjectList(privateClause->v, converter, privateOperands);
183       }
184       // Reduction clause is left out for the moment as the clause will probably
185       // end up having its own operation.
186     }
187 
188     // Prepare the operand segement size attribute and the operands value range.
189     SmallVector<Value, 8> operands;
190     SmallVector<int32_t, 8> operandSegments;
191     addOperand(operands, operandSegments, gangNum);
192     addOperand(operands, operandSegments, gangStatic);
193     addOperand(operands, operandSegments, workerNum);
194     addOperand(operands, operandSegments, vectorLength);
195     addOperands(operands, operandSegments, tileOperands);
196     addOperands(operands, operandSegments, privateOperands);
197     addOperands(operands, operandSegments, reductionOperands);
198 
199     auto loopOp = createRegionOp<mlir::acc::LoopOp, mlir::acc::YieldOp>(
200         firOpBuilder, currentLocation, operands, operandSegments);
201 
202     loopOp.setAttr(mlir::acc::LoopOp::getExecutionMappingAttrName(),
203                    firOpBuilder.getI64IntegerAttr(executionMapping));
204 
205     // Lower clauses mapped to attributes
206     for (const auto &clause : accClauseList.v) {
207       if (const auto *collapseClause =
208               std::get_if<Fortran::parser::AccClause::Collapse>(&clause.u)) {
209         const auto *expr = Fortran::semantics::GetExpr(collapseClause->v);
210         const auto collapseValue = Fortran::evaluate::ToInt64(*expr);
211         if (collapseValue) {
212           loopOp.setAttr(mlir::acc::LoopOp::getCollapseAttrName(),
213                          firOpBuilder.getI64IntegerAttr(*collapseValue));
214         }
215       } else if (std::get_if<Fortran::parser::AccClause::Seq>(&clause.u)) {
216         loopOp.setAttr(mlir::acc::LoopOp::getSeqAttrName(),
217                        firOpBuilder.getUnitAttr());
218       } else if (std::get_if<Fortran::parser::AccClause::Independent>(
219                      &clause.u)) {
220         loopOp.setAttr(mlir::acc::LoopOp::getIndependentAttrName(),
221                        firOpBuilder.getUnitAttr());
222       } else if (std::get_if<Fortran::parser::AccClause::Auto>(&clause.u)) {
223         loopOp.setAttr(mlir::acc::LoopOp::getAutoAttrName(),
224                        firOpBuilder.getUnitAttr());
225       }
226     }
227   }
228 }
229 
230 void Fortran::lower::genOpenACCConstruct(
231     Fortran::lower::AbstractConverter &converter,
232     Fortran::lower::pft::Evaluation &eval,
233     const Fortran::parser::OpenACCConstruct &accConstruct) {
234 
235   std::visit(
236       common::visitors{
237           [&](const Fortran::parser::OpenACCBlockConstruct &blockConstruct) {
238             TODO();
239           },
240           [&](const Fortran::parser::OpenACCCombinedConstruct
241                   &combinedConstruct) { TODO(); },
242           [&](const Fortran::parser::OpenACCLoopConstruct &loopConstruct) {
243             genACC(converter, eval, loopConstruct);
244           },
245           [&](const Fortran::parser::OpenACCStandaloneConstruct
246                   &standaloneConstruct) { TODO(); },
247           [&](const Fortran::parser::OpenACCRoutineConstruct
248                   &routineConstruct) { TODO(); },
249           [&](const Fortran::parser::OpenACCCacheConstruct &cacheConstruct) {
250             TODO();
251           },
252           [&](const Fortran::parser::OpenACCWaitConstruct &waitConstruct) {
253             TODO();
254           },
255           [&](const Fortran::parser::OpenACCAtomicConstruct &atomicConstruct) {
256             TODO();
257           },
258       },
259       accConstruct.u);
260 }
261