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