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                           std::int32_t &objectsCount,
34                           SmallVector<Value, 8> &operands) {
35   for (const auto &accObject : objectList.v) {
36     std::visit(
37         Fortran::common::visitors{
38             [&](const Fortran::parser::Designator &designator) {
39               if (const auto *name = getDesignatorNameIfDataRef(designator)) {
40                 ++objectsCount;
41                 const auto variable = converter.getSymbolAddress(*name->symbol);
42                 operands.push_back(variable);
43               }
44             },
45             [&](const Fortran::parser::Name &name) {
46               ++objectsCount;
47               const auto variable = converter.getSymbolAddress(*name.symbol);
48               operands.push_back(variable);
49             }},
50         accObject.u);
51   }
52 }
53 
54 static void genACC(Fortran::lower::AbstractConverter &converter,
55                    Fortran::lower::pft::Evaluation &eval,
56                    const Fortran::parser::OpenACCLoopConstruct &loopConstruct) {
57 
58   const auto &beginLoopDirective =
59       std::get<Fortran::parser::AccBeginLoopDirective>(loopConstruct.t);
60   const auto &loopDirective =
61       std::get<Fortran::parser::AccLoopDirective>(beginLoopDirective.t);
62 
63   if (loopDirective.v == llvm::acc::ACCD_loop) {
64     auto &firOpBuilder = converter.getFirOpBuilder();
65     auto currentLocation = converter.getCurrentLocation();
66     llvm::ArrayRef<mlir::Type> argTy;
67 
68     // Add attribute extracted from clauses.
69     const auto &accClauseList =
70         std::get<Fortran::parser::AccClauseList>(beginLoopDirective.t);
71 
72     mlir::Value workerNum;
73     mlir::Value vectorLength;
74     mlir::Value gangNum;
75     mlir::Value gangStatic;
76     std::int32_t tileOperands = 0;
77     std::int32_t privateOperands = 0;
78     std::int32_t reductionOperands = 0;
79     std::int64_t executionMapping = mlir::acc::OpenACCExecMapping::NONE;
80     SmallVector<Value, 8> operands;
81 
82     // Lower clauses values mapped to operands.
83     for (const auto &clause : accClauseList.v) {
84       if (const auto *gangClause =
85               std::get_if<Fortran::parser::AccClause::Gang>(&clause.u)) {
86         if (gangClause->v) {
87           const Fortran::parser::AccGangArgument &x = *gangClause->v;
88           if (const auto &gangNumValue =
89                   std::get<std::optional<Fortran::parser::ScalarIntExpr>>(
90                       x.t)) {
91             gangNum = converter.genExprValue(
92                 *Fortran::semantics::GetExpr(gangNumValue.value()));
93             operands.push_back(gangNum);
94           }
95           if (const auto &gangStaticValue =
96                   std::get<std::optional<Fortran::parser::AccSizeExpr>>(x.t)) {
97             const auto &expr =
98                 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(
99                     gangStaticValue.value().t);
100             if (expr) {
101               gangStatic =
102                   converter.genExprValue(*Fortran::semantics::GetExpr(*expr));
103             } else {
104               // * was passed as value and will be represented as a -1 constant
105               // integer.
106               gangStatic = firOpBuilder.createIntegerConstant(
107                   currentLocation, firOpBuilder.getIntegerType(32),
108                   /* STAR */ -1);
109             }
110             operands.push_back(gangStatic);
111           }
112         }
113         executionMapping |= mlir::acc::OpenACCExecMapping::GANG;
114       } else if (const auto *workerClause =
115                      std::get_if<Fortran::parser::AccClause::Worker>(
116                          &clause.u)) {
117         if (workerClause->v) {
118           workerNum = converter.genExprValue(
119               *Fortran::semantics::GetExpr(*workerClause->v));
120           operands.push_back(workerNum);
121         }
122         executionMapping |= mlir::acc::OpenACCExecMapping::WORKER;
123       } else if (const auto *vectorClause =
124                      std::get_if<Fortran::parser::AccClause::Vector>(
125                          &clause.u)) {
126         if (vectorClause->v) {
127           vectorLength = converter.genExprValue(
128               *Fortran::semantics::GetExpr(*vectorClause->v));
129           operands.push_back(vectorLength);
130         }
131         executionMapping |= mlir::acc::OpenACCExecMapping::VECTOR;
132       } else if (const auto *tileClause =
133                      std::get_if<Fortran::parser::AccClause::Tile>(&clause.u)) {
134         const Fortran::parser::AccTileExprList &accTileExprList = tileClause->v;
135         for (const auto &accTileExpr : accTileExprList.v) {
136           const auto &expr =
137               std::get<std::optional<Fortran::parser::ScalarIntConstantExpr>>(
138                   accTileExpr.t);
139           ++tileOperands;
140           if (expr) {
141             operands.push_back(
142                 converter.genExprValue(*Fortran::semantics::GetExpr(*expr)));
143           } else {
144             // * was passed as value and will be represented as a -1 constant
145             // integer.
146             mlir::Value tileStar = firOpBuilder.createIntegerConstant(
147                 currentLocation, firOpBuilder.getIntegerType(32),
148                 /* STAR */ -1);
149             operands.push_back(tileStar);
150           }
151         }
152       } else if (const auto *privateClause =
153                      std::get_if<Fortran::parser::AccClause::Private>(
154                          &clause.u)) {
155         const Fortran::parser::AccObjectList &accObjectList = privateClause->v;
156         genObjectList(accObjectList, converter, privateOperands, operands);
157       }
158       // Reduction clause is left out for the moment as the clause will probably
159       // end up having its own operation.
160     }
161 
162     auto loopOp = firOpBuilder.create<mlir::acc::LoopOp>(currentLocation, argTy,
163                                                          operands);
164 
165     firOpBuilder.createBlock(&loopOp.getRegion());
166     auto &block = loopOp.getRegion().back();
167     firOpBuilder.setInsertionPointToStart(&block);
168     // ensure the block is well-formed.
169     firOpBuilder.create<mlir::acc::YieldOp>(currentLocation);
170 
171     loopOp.setAttr(mlir::acc::LoopOp::getOperandSegmentSizeAttr(),
172                    firOpBuilder.getI32VectorAttr(
173                        {gangNum ? 1 : 0, gangStatic ? 1 : 0, workerNum ? 1 : 0,
174                         vectorLength ? 1 : 0, tileOperands, privateOperands,
175                         reductionOperands}));
176 
177     loopOp.setAttr(mlir::acc::LoopOp::getExecutionMappingAttrName(),
178                    firOpBuilder.getI64IntegerAttr(executionMapping));
179 
180     // Lower clauses mapped to attributes
181     for (const auto &clause : accClauseList.v) {
182       if (const auto *collapseClause =
183               std::get_if<Fortran::parser::AccClause::Collapse>(&clause.u)) {
184         const auto *expr = Fortran::semantics::GetExpr(collapseClause->v);
185         const auto collapseValue = Fortran::evaluate::ToInt64(*expr);
186         if (collapseValue) {
187           loopOp.setAttr(mlir::acc::LoopOp::getCollapseAttrName(),
188                          firOpBuilder.getI64IntegerAttr(*collapseValue));
189         }
190       } else if (std::get_if<Fortran::parser::AccClause::Seq>(&clause.u)) {
191         loopOp.setAttr(mlir::acc::LoopOp::getSeqAttrName(),
192                        firOpBuilder.getUnitAttr());
193       } else if (std::get_if<Fortran::parser::AccClause::Independent>(
194                      &clause.u)) {
195         loopOp.setAttr(mlir::acc::LoopOp::getIndependentAttrName(),
196                        firOpBuilder.getUnitAttr());
197       } else if (std::get_if<Fortran::parser::AccClause::Auto>(&clause.u)) {
198         loopOp.setAttr(mlir::acc::LoopOp::getAutoAttrName(),
199                        firOpBuilder.getUnitAttr());
200       }
201     }
202 
203     // Place the insertion point to the start of the first block.
204     firOpBuilder.setInsertionPointToStart(&block);
205   }
206 }
207 
208 void Fortran::lower::genOpenACCConstruct(
209     Fortran::lower::AbstractConverter &converter,
210     Fortran::lower::pft::Evaluation &eval,
211     const Fortran::parser::OpenACCConstruct &accConstruct) {
212 
213   std::visit(
214       common::visitors{
215           [&](const Fortran::parser::OpenACCBlockConstruct &blockConstruct) {
216             TODO();
217           },
218           [&](const Fortran::parser::OpenACCCombinedConstruct
219                   &combinedConstruct) { TODO(); },
220           [&](const Fortran::parser::OpenACCLoopConstruct &loopConstruct) {
221             genACC(converter, eval, loopConstruct);
222           },
223           [&](const Fortran::parser::OpenACCStandaloneConstruct
224                   &standaloneConstruct) { TODO(); },
225           [&](const Fortran::parser::OpenACCRoutineConstruct
226                   &routineConstruct) { TODO(); },
227           [&](const Fortran::parser::OpenACCCacheConstruct &cacheConstruct) {
228             TODO();
229           },
230           [&](const Fortran::parser::OpenACCWaitConstruct &waitConstruct) {
231             TODO();
232           },
233           [&](const Fortran::parser::OpenACCAtomicConstruct &atomicConstruct) {
234             TODO();
235           },
236       },
237       accConstruct.u);
238 }
239