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