[TargetVersion] Only enable on RISC-V and AArch64 (#115991)
[llvm-project.git] / flang / lib / Lower / DirectivesCommon.h
blob88514b167432784fb8bb5a89a04742b75db56f36
1 //===-- Lower/DirectivesCommon.h --------------------------------*- C++ -*-===//
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/
11 //===----------------------------------------------------------------------===//
12 ///
13 /// A location to place directive utilities shared across multiple lowering
14 /// files, e.g. utilities shared in OpenMP and OpenACC. The header file can
15 /// be used for both declarations and templated/inline implementations
16 //===----------------------------------------------------------------------===//
18 #ifndef FORTRAN_LOWER_DIRECTIVES_COMMON_H
19 #define FORTRAN_LOWER_DIRECTIVES_COMMON_H
21 #include "flang/Common/idioms.h"
22 #include "flang/Evaluate/tools.h"
23 #include "flang/Lower/AbstractConverter.h"
24 #include "flang/Lower/Bridge.h"
25 #include "flang/Lower/ConvertExpr.h"
26 #include "flang/Lower/ConvertVariable.h"
27 #include "flang/Lower/OpenACC.h"
28 #include "flang/Lower/OpenMP.h"
29 #include "flang/Lower/PFTBuilder.h"
30 #include "flang/Lower/StatementContext.h"
31 #include "flang/Lower/Support/Utils.h"
32 #include "flang/Optimizer/Builder/BoxValue.h"
33 #include "flang/Optimizer/Builder/FIRBuilder.h"
34 #include "flang/Optimizer/Builder/HLFIRTools.h"
35 #include "flang/Optimizer/Builder/Todo.h"
36 #include "flang/Optimizer/Dialect/FIRType.h"
37 #include "flang/Optimizer/HLFIR/HLFIROps.h"
38 #include "flang/Parser/parse-tree.h"
39 #include "flang/Semantics/openmp-directive-sets.h"
40 #include "flang/Semantics/tools.h"
41 #include "mlir/Dialect/OpenACC/OpenACC.h"
42 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
43 #include "mlir/Dialect/SCF/IR/SCF.h"
44 #include "mlir/IR/Value.h"
45 #include "llvm/Frontend/OpenMP/OMPConstants.h"
46 #include <list>
47 #include <type_traits>
49 namespace Fortran {
50 namespace lower {
52 /// Information gathered to generate bounds operation and data entry/exit
53 /// operations.
54 struct AddrAndBoundsInfo {
55 explicit AddrAndBoundsInfo() {}
56 explicit AddrAndBoundsInfo(mlir::Value addr, mlir::Value rawInput)
57 : addr(addr), rawInput(rawInput) {}
58 explicit AddrAndBoundsInfo(mlir::Value addr, mlir::Value rawInput,
59 mlir::Value isPresent)
60 : addr(addr), rawInput(rawInput), isPresent(isPresent) {}
61 explicit AddrAndBoundsInfo(mlir::Value addr, mlir::Value rawInput,
62 mlir::Value isPresent, mlir::Type boxType)
63 : addr(addr), rawInput(rawInput), isPresent(isPresent), boxType(boxType) {
65 mlir::Value addr = nullptr;
66 mlir::Value rawInput = nullptr;
67 mlir::Value isPresent = nullptr;
68 mlir::Type boxType = nullptr;
69 void dump(llvm::raw_ostream &os) {
70 os << "AddrAndBoundsInfo addr: " << addr << "\n";
71 os << "AddrAndBoundsInfo rawInput: " << rawInput << "\n";
72 os << "AddrAndBoundsInfo isPresent: " << isPresent << "\n";
73 os << "AddrAndBoundsInfo boxType: " << boxType << "\n";
77 /// Populates \p hint and \p memoryOrder with appropriate clause information
78 /// if present on atomic construct.
79 static inline void genOmpAtomicHintAndMemoryOrderClauses(
80 Fortran::lower::AbstractConverter &converter,
81 const Fortran::parser::OmpAtomicClauseList &clauseList,
82 mlir::IntegerAttr &hint,
83 mlir::omp::ClauseMemoryOrderKindAttr &memoryOrder) {
84 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
85 for (const Fortran::parser::OmpAtomicClause &clause : clauseList.v) {
86 if (const auto *ompClause =
87 std::get_if<Fortran::parser::OmpClause>(&clause.u)) {
88 if (const auto *hintClause =
89 std::get_if<Fortran::parser::OmpClause::Hint>(&ompClause->u)) {
90 const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
91 uint64_t hintExprValue = *Fortran::evaluate::ToInt64(*expr);
92 hint = firOpBuilder.getI64IntegerAttr(hintExprValue);
94 } else if (const auto *ompMemoryOrderClause =
95 std::get_if<Fortran::parser::OmpMemoryOrderClause>(
96 &clause.u)) {
97 if (std::get_if<Fortran::parser::OmpClause::Acquire>(
98 &ompMemoryOrderClause->v.u)) {
99 memoryOrder = mlir::omp::ClauseMemoryOrderKindAttr::get(
100 firOpBuilder.getContext(),
101 mlir::omp::ClauseMemoryOrderKind::Acquire);
102 } else if (std::get_if<Fortran::parser::OmpClause::Relaxed>(
103 &ompMemoryOrderClause->v.u)) {
104 memoryOrder = mlir::omp::ClauseMemoryOrderKindAttr::get(
105 firOpBuilder.getContext(),
106 mlir::omp::ClauseMemoryOrderKind::Relaxed);
107 } else if (std::get_if<Fortran::parser::OmpClause::SeqCst>(
108 &ompMemoryOrderClause->v.u)) {
109 memoryOrder = mlir::omp::ClauseMemoryOrderKindAttr::get(
110 firOpBuilder.getContext(),
111 mlir::omp::ClauseMemoryOrderKind::Seq_cst);
112 } else if (std::get_if<Fortran::parser::OmpClause::Release>(
113 &ompMemoryOrderClause->v.u)) {
114 memoryOrder = mlir::omp::ClauseMemoryOrderKindAttr::get(
115 firOpBuilder.getContext(),
116 mlir::omp::ClauseMemoryOrderKind::Release);
122 template <typename AtomicListT>
123 static void processOmpAtomicTODO(mlir::Type elementType,
124 [[maybe_unused]] mlir::Location loc) {
125 if (!elementType)
126 return;
127 if constexpr (std::is_same<AtomicListT,
128 Fortran::parser::OmpAtomicClauseList>()) {
129 assert(fir::isa_trivial(fir::unwrapRefType(elementType)) &&
130 "is supported type for omp atomic");
134 /// Used to generate atomic.read operation which is created in existing
135 /// location set by builder.
136 template <typename AtomicListT>
137 static inline void genOmpAccAtomicCaptureStatement(
138 Fortran::lower::AbstractConverter &converter, mlir::Value fromAddress,
139 mlir::Value toAddress,
140 [[maybe_unused]] const AtomicListT *leftHandClauseList,
141 [[maybe_unused]] const AtomicListT *rightHandClauseList,
142 mlir::Type elementType, mlir::Location loc) {
143 // Generate `atomic.read` operation for atomic assigment statements
144 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
146 processOmpAtomicTODO<AtomicListT>(elementType, loc);
148 if constexpr (std::is_same<AtomicListT,
149 Fortran::parser::OmpAtomicClauseList>()) {
150 // If no hint clause is specified, the effect is as if
151 // hint(omp_sync_hint_none) had been specified.
152 mlir::IntegerAttr hint = nullptr;
154 mlir::omp::ClauseMemoryOrderKindAttr memoryOrder = nullptr;
155 if (leftHandClauseList)
156 genOmpAtomicHintAndMemoryOrderClauses(converter, *leftHandClauseList,
157 hint, memoryOrder);
158 if (rightHandClauseList)
159 genOmpAtomicHintAndMemoryOrderClauses(converter, *rightHandClauseList,
160 hint, memoryOrder);
161 firOpBuilder.create<mlir::omp::AtomicReadOp>(
162 loc, fromAddress, toAddress, mlir::TypeAttr::get(elementType), hint,
163 memoryOrder);
164 } else {
165 firOpBuilder.create<mlir::acc::AtomicReadOp>(
166 loc, fromAddress, toAddress, mlir::TypeAttr::get(elementType));
170 /// Used to generate atomic.write operation which is created in existing
171 /// location set by builder.
172 template <typename AtomicListT>
173 static inline void genOmpAccAtomicWriteStatement(
174 Fortran::lower::AbstractConverter &converter, mlir::Value lhsAddr,
175 mlir::Value rhsExpr, [[maybe_unused]] const AtomicListT *leftHandClauseList,
176 [[maybe_unused]] const AtomicListT *rightHandClauseList, mlir::Location loc,
177 mlir::Value *evaluatedExprValue = nullptr) {
178 // Generate `atomic.write` operation for atomic assignment statements
179 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
181 mlir::Type varType = fir::unwrapRefType(lhsAddr.getType());
182 // Create a conversion outside the capture block.
183 auto insertionPoint = firOpBuilder.saveInsertionPoint();
184 firOpBuilder.setInsertionPointAfter(rhsExpr.getDefiningOp());
185 rhsExpr = firOpBuilder.createConvert(loc, varType, rhsExpr);
186 firOpBuilder.restoreInsertionPoint(insertionPoint);
188 processOmpAtomicTODO<AtomicListT>(varType, loc);
190 if constexpr (std::is_same<AtomicListT,
191 Fortran::parser::OmpAtomicClauseList>()) {
192 // If no hint clause is specified, the effect is as if
193 // hint(omp_sync_hint_none) had been specified.
194 mlir::IntegerAttr hint = nullptr;
195 mlir::omp::ClauseMemoryOrderKindAttr memoryOrder = nullptr;
196 if (leftHandClauseList)
197 genOmpAtomicHintAndMemoryOrderClauses(converter, *leftHandClauseList,
198 hint, memoryOrder);
199 if (rightHandClauseList)
200 genOmpAtomicHintAndMemoryOrderClauses(converter, *rightHandClauseList,
201 hint, memoryOrder);
202 firOpBuilder.create<mlir::omp::AtomicWriteOp>(loc, lhsAddr, rhsExpr, hint,
203 memoryOrder);
204 } else {
205 firOpBuilder.create<mlir::acc::AtomicWriteOp>(loc, lhsAddr, rhsExpr);
209 /// Used to generate atomic.update operation which is created in existing
210 /// location set by builder.
211 template <typename AtomicListT>
212 static inline void genOmpAccAtomicUpdateStatement(
213 Fortran::lower::AbstractConverter &converter, mlir::Value lhsAddr,
214 mlir::Type varType, const Fortran::parser::Variable &assignmentStmtVariable,
215 const Fortran::parser::Expr &assignmentStmtExpr,
216 [[maybe_unused]] const AtomicListT *leftHandClauseList,
217 [[maybe_unused]] const AtomicListT *rightHandClauseList, mlir::Location loc,
218 mlir::Operation *atomicCaptureOp = nullptr) {
219 // Generate `atomic.update` operation for atomic assignment statements
220 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
221 mlir::Location currentLocation = converter.getCurrentLocation();
223 // Create the omp.atomic.update or acc.atomic.update operation
225 // func.func @_QPsb() {
226 // %0 = fir.alloca i32 {bindc_name = "a", uniq_name = "_QFsbEa"}
227 // %1 = fir.alloca i32 {bindc_name = "b", uniq_name = "_QFsbEb"}
228 // %2 = fir.load %1 : !fir.ref<i32>
229 // omp.atomic.update %0 : !fir.ref<i32> {
230 // ^bb0(%arg0: i32):
231 // %3 = arith.addi %arg0, %2 : i32
232 // omp.yield(%3 : i32)
233 // }
234 // return
235 // }
237 auto getArgExpression =
238 [](std::list<parser::ActualArgSpec>::const_iterator it) {
239 const auto &arg{std::get<parser::ActualArg>((*it).t)};
240 const auto *parserExpr{
241 std::get_if<common::Indirection<parser::Expr>>(&arg.u)};
242 return parserExpr;
245 // Lower any non atomic sub-expression before the atomic operation, and
246 // map its lowered value to the semantic representation.
247 Fortran::lower::ExprToValueMap exprValueOverrides;
248 // Max and min intrinsics can have a list of Args. Hence we need a list
249 // of nonAtomicSubExprs to hoist. Currently, only the load is hoisted.
250 llvm::SmallVector<const Fortran::lower::SomeExpr *> nonAtomicSubExprs;
251 Fortran::common::visit(
252 Fortran::common::visitors{
253 [&](const common::Indirection<parser::FunctionReference> &funcRef)
254 -> void {
255 const auto &args{std::get<std::list<parser::ActualArgSpec>>(
256 funcRef.value().v.t)};
257 std::list<parser::ActualArgSpec>::const_iterator beginIt =
258 args.begin();
259 std::list<parser::ActualArgSpec>::const_iterator endIt = args.end();
260 const auto *exprFirst{getArgExpression(beginIt)};
261 if (exprFirst && exprFirst->value().source ==
262 assignmentStmtVariable.GetSource()) {
263 // Add everything except the first
264 beginIt++;
265 } else {
266 // Add everything except the last
267 endIt--;
269 std::list<parser::ActualArgSpec>::const_iterator it;
270 for (it = beginIt; it != endIt; it++) {
271 const common::Indirection<parser::Expr> *expr =
272 getArgExpression(it);
273 if (expr)
274 nonAtomicSubExprs.push_back(Fortran::semantics::GetExpr(*expr));
277 [&](const auto &op) -> void {
278 using T = std::decay_t<decltype(op)>;
279 if constexpr (std::is_base_of<
280 Fortran::parser::Expr::IntrinsicBinary,
281 T>::value) {
282 const auto &exprLeft{std::get<0>(op.t)};
283 const auto &exprRight{std::get<1>(op.t)};
284 if (exprLeft.value().source == assignmentStmtVariable.GetSource())
285 nonAtomicSubExprs.push_back(
286 Fortran::semantics::GetExpr(exprRight));
287 else
288 nonAtomicSubExprs.push_back(
289 Fortran::semantics::GetExpr(exprLeft));
293 assignmentStmtExpr.u);
294 StatementContext nonAtomicStmtCtx;
295 if (!nonAtomicSubExprs.empty()) {
296 // Generate non atomic part before all the atomic operations.
297 auto insertionPoint = firOpBuilder.saveInsertionPoint();
298 if (atomicCaptureOp)
299 firOpBuilder.setInsertionPoint(atomicCaptureOp);
300 mlir::Value nonAtomicVal;
301 for (auto *nonAtomicSubExpr : nonAtomicSubExprs) {
302 nonAtomicVal = fir::getBase(converter.genExprValue(
303 currentLocation, *nonAtomicSubExpr, nonAtomicStmtCtx));
304 exprValueOverrides.try_emplace(nonAtomicSubExpr, nonAtomicVal);
306 if (atomicCaptureOp)
307 firOpBuilder.restoreInsertionPoint(insertionPoint);
310 mlir::Operation *atomicUpdateOp = nullptr;
311 if constexpr (std::is_same<AtomicListT,
312 Fortran::parser::OmpAtomicClauseList>()) {
313 // If no hint clause is specified, the effect is as if
314 // hint(omp_sync_hint_none) had been specified.
315 mlir::IntegerAttr hint = nullptr;
316 mlir::omp::ClauseMemoryOrderKindAttr memoryOrder = nullptr;
317 if (leftHandClauseList)
318 genOmpAtomicHintAndMemoryOrderClauses(converter, *leftHandClauseList,
319 hint, memoryOrder);
320 if (rightHandClauseList)
321 genOmpAtomicHintAndMemoryOrderClauses(converter, *rightHandClauseList,
322 hint, memoryOrder);
323 atomicUpdateOp = firOpBuilder.create<mlir::omp::AtomicUpdateOp>(
324 currentLocation, lhsAddr, hint, memoryOrder);
325 } else {
326 atomicUpdateOp = firOpBuilder.create<mlir::acc::AtomicUpdateOp>(
327 currentLocation, lhsAddr);
330 processOmpAtomicTODO<AtomicListT>(varType, loc);
332 llvm::SmallVector<mlir::Type> varTys = {varType};
333 llvm::SmallVector<mlir::Location> locs = {currentLocation};
334 firOpBuilder.createBlock(&atomicUpdateOp->getRegion(0), {}, varTys, locs);
335 mlir::Value val =
336 fir::getBase(atomicUpdateOp->getRegion(0).front().getArgument(0));
338 exprValueOverrides.try_emplace(
339 Fortran::semantics::GetExpr(assignmentStmtVariable), val);
341 // statement context inside the atomic block.
342 converter.overrideExprValues(&exprValueOverrides);
343 Fortran::lower::StatementContext atomicStmtCtx;
344 mlir::Value rhsExpr = fir::getBase(converter.genExprValue(
345 *Fortran::semantics::GetExpr(assignmentStmtExpr), atomicStmtCtx));
346 mlir::Value convertResult =
347 firOpBuilder.createConvert(currentLocation, varType, rhsExpr);
348 if constexpr (std::is_same<AtomicListT,
349 Fortran::parser::OmpAtomicClauseList>()) {
350 firOpBuilder.create<mlir::omp::YieldOp>(currentLocation, convertResult);
351 } else {
352 firOpBuilder.create<mlir::acc::YieldOp>(currentLocation, convertResult);
354 converter.resetExprOverrides();
356 firOpBuilder.setInsertionPointAfter(atomicUpdateOp);
359 /// Processes an atomic construct with write clause.
360 template <typename AtomicT, typename AtomicListT>
361 void genOmpAccAtomicWrite(Fortran::lower::AbstractConverter &converter,
362 const AtomicT &atomicWrite, mlir::Location loc) {
363 const AtomicListT *rightHandClauseList = nullptr;
364 const AtomicListT *leftHandClauseList = nullptr;
365 if constexpr (std::is_same<AtomicListT,
366 Fortran::parser::OmpAtomicClauseList>()) {
367 // Get the address of atomic read operands.
368 rightHandClauseList = &std::get<2>(atomicWrite.t);
369 leftHandClauseList = &std::get<0>(atomicWrite.t);
372 const Fortran::parser::AssignmentStmt &stmt =
373 std::get<Fortran::parser::Statement<Fortran::parser::AssignmentStmt>>(
374 atomicWrite.t)
375 .statement;
376 const Fortran::evaluate::Assignment &assign = *stmt.typedAssignment->v;
377 Fortran::lower::StatementContext stmtCtx;
378 // Get the value and address of atomic write operands.
379 mlir::Value rhsExpr =
380 fir::getBase(converter.genExprValue(assign.rhs, stmtCtx));
381 mlir::Value lhsAddr =
382 fir::getBase(converter.genExprAddr(assign.lhs, stmtCtx));
383 genOmpAccAtomicWriteStatement(converter, lhsAddr, rhsExpr, leftHandClauseList,
384 rightHandClauseList, loc);
387 /// Processes an atomic construct with read clause.
388 template <typename AtomicT, typename AtomicListT>
389 void genOmpAccAtomicRead(Fortran::lower::AbstractConverter &converter,
390 const AtomicT &atomicRead, mlir::Location loc) {
391 const AtomicListT *rightHandClauseList = nullptr;
392 const AtomicListT *leftHandClauseList = nullptr;
393 if constexpr (std::is_same<AtomicListT,
394 Fortran::parser::OmpAtomicClauseList>()) {
395 // Get the address of atomic read operands.
396 rightHandClauseList = &std::get<2>(atomicRead.t);
397 leftHandClauseList = &std::get<0>(atomicRead.t);
400 const auto &assignmentStmtExpr = std::get<Fortran::parser::Expr>(
401 std::get<Fortran::parser::Statement<Fortran::parser::AssignmentStmt>>(
402 atomicRead.t)
403 .statement.t);
404 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>(
405 std::get<Fortran::parser::Statement<Fortran::parser::AssignmentStmt>>(
406 atomicRead.t)
407 .statement.t);
409 Fortran::lower::StatementContext stmtCtx;
410 const Fortran::semantics::SomeExpr &fromExpr =
411 *Fortran::semantics::GetExpr(assignmentStmtExpr);
412 mlir::Type elementType = converter.genType(fromExpr);
413 mlir::Value fromAddress =
414 fir::getBase(converter.genExprAddr(fromExpr, stmtCtx));
415 mlir::Value toAddress = fir::getBase(converter.genExprAddr(
416 *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx));
417 genOmpAccAtomicCaptureStatement(converter, fromAddress, toAddress,
418 leftHandClauseList, rightHandClauseList,
419 elementType, loc);
422 /// Processes an atomic construct with update clause.
423 template <typename AtomicT, typename AtomicListT>
424 void genOmpAccAtomicUpdate(Fortran::lower::AbstractConverter &converter,
425 const AtomicT &atomicUpdate, mlir::Location loc) {
426 const AtomicListT *rightHandClauseList = nullptr;
427 const AtomicListT *leftHandClauseList = nullptr;
428 if constexpr (std::is_same<AtomicListT,
429 Fortran::parser::OmpAtomicClauseList>()) {
430 // Get the address of atomic read operands.
431 rightHandClauseList = &std::get<2>(atomicUpdate.t);
432 leftHandClauseList = &std::get<0>(atomicUpdate.t);
435 const auto &assignmentStmtExpr = std::get<Fortran::parser::Expr>(
436 std::get<Fortran::parser::Statement<Fortran::parser::AssignmentStmt>>(
437 atomicUpdate.t)
438 .statement.t);
439 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>(
440 std::get<Fortran::parser::Statement<Fortran::parser::AssignmentStmt>>(
441 atomicUpdate.t)
442 .statement.t);
444 Fortran::lower::StatementContext stmtCtx;
445 mlir::Value lhsAddr = fir::getBase(converter.genExprAddr(
446 *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx));
447 mlir::Type varType = fir::unwrapRefType(lhsAddr.getType());
448 genOmpAccAtomicUpdateStatement<AtomicListT>(
449 converter, lhsAddr, varType, assignmentStmtVariable, assignmentStmtExpr,
450 leftHandClauseList, rightHandClauseList, loc);
453 /// Processes an atomic construct with no clause - which implies update clause.
454 template <typename AtomicT, typename AtomicListT>
455 void genOmpAtomic(Fortran::lower::AbstractConverter &converter,
456 const AtomicT &atomicConstruct, mlir::Location loc) {
457 const AtomicListT &atomicClauseList =
458 std::get<AtomicListT>(atomicConstruct.t);
459 const auto &assignmentStmtExpr = std::get<Fortran::parser::Expr>(
460 std::get<Fortran::parser::Statement<Fortran::parser::AssignmentStmt>>(
461 atomicConstruct.t)
462 .statement.t);
463 const auto &assignmentStmtVariable = std::get<Fortran::parser::Variable>(
464 std::get<Fortran::parser::Statement<Fortran::parser::AssignmentStmt>>(
465 atomicConstruct.t)
466 .statement.t);
467 Fortran::lower::StatementContext stmtCtx;
468 mlir::Value lhsAddr = fir::getBase(converter.genExprAddr(
469 *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx));
470 mlir::Type varType = fir::unwrapRefType(lhsAddr.getType());
471 // If atomic-clause is not present on the construct, the behaviour is as if
472 // the update clause is specified (for both OpenMP and OpenACC).
473 genOmpAccAtomicUpdateStatement<AtomicListT>(
474 converter, lhsAddr, varType, assignmentStmtVariable, assignmentStmtExpr,
475 &atomicClauseList, nullptr, loc);
478 /// Processes an atomic construct with capture clause.
479 template <typename AtomicT, typename AtomicListT>
480 void genOmpAccAtomicCapture(Fortran::lower::AbstractConverter &converter,
481 const AtomicT &atomicCapture, mlir::Location loc) {
482 fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
484 const Fortran::parser::AssignmentStmt &stmt1 =
485 std::get<typename AtomicT::Stmt1>(atomicCapture.t).v.statement;
486 const Fortran::evaluate::Assignment &assign1 = *stmt1.typedAssignment->v;
487 const auto &stmt1Var{std::get<Fortran::parser::Variable>(stmt1.t)};
488 const auto &stmt1Expr{std::get<Fortran::parser::Expr>(stmt1.t)};
489 const Fortran::parser::AssignmentStmt &stmt2 =
490 std::get<typename AtomicT::Stmt2>(atomicCapture.t).v.statement;
491 const Fortran::evaluate::Assignment &assign2 = *stmt2.typedAssignment->v;
492 const auto &stmt2Var{std::get<Fortran::parser::Variable>(stmt2.t)};
493 const auto &stmt2Expr{std::get<Fortran::parser::Expr>(stmt2.t)};
495 // Pre-evaluate expressions to be used in the various operations inside
496 // `atomic.capture` since it is not desirable to have anything other than
497 // a `atomic.read`, `atomic.write`, or `atomic.update` operation
498 // inside `atomic.capture`
499 Fortran::lower::StatementContext stmtCtx;
500 // LHS evaluations are common to all combinations of `atomic.capture`
501 mlir::Value stmt1LHSArg =
502 fir::getBase(converter.genExprAddr(assign1.lhs, stmtCtx));
503 mlir::Value stmt2LHSArg =
504 fir::getBase(converter.genExprAddr(assign2.lhs, stmtCtx));
506 // Type information used in generation of `atomic.update` operation
507 mlir::Type stmt1VarType =
508 fir::getBase(converter.genExprValue(assign1.lhs, stmtCtx)).getType();
509 mlir::Type stmt2VarType =
510 fir::getBase(converter.genExprValue(assign2.lhs, stmtCtx)).getType();
512 mlir::Operation *atomicCaptureOp = nullptr;
513 if constexpr (std::is_same<AtomicListT,
514 Fortran::parser::OmpAtomicClauseList>()) {
515 mlir::IntegerAttr hint = nullptr;
516 mlir::omp::ClauseMemoryOrderKindAttr memoryOrder = nullptr;
517 const AtomicListT &rightHandClauseList = std::get<2>(atomicCapture.t);
518 const AtomicListT &leftHandClauseList = std::get<0>(atomicCapture.t);
519 genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint,
520 memoryOrder);
521 genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint,
522 memoryOrder);
523 atomicCaptureOp =
524 firOpBuilder.create<mlir::omp::AtomicCaptureOp>(loc, hint, memoryOrder);
525 } else {
526 atomicCaptureOp = firOpBuilder.create<mlir::acc::AtomicCaptureOp>(loc);
529 firOpBuilder.createBlock(&(atomicCaptureOp->getRegion(0)));
530 mlir::Block &block = atomicCaptureOp->getRegion(0).back();
531 firOpBuilder.setInsertionPointToStart(&block);
532 if (Fortran::semantics::checkForSingleVariableOnRHS(stmt1)) {
533 if (Fortran::semantics::checkForSymbolMatch(stmt2)) {
534 // Atomic capture construct is of the form [capture-stmt, update-stmt]
535 const Fortran::semantics::SomeExpr &fromExpr =
536 *Fortran::semantics::GetExpr(stmt1Expr);
537 mlir::Type elementType = converter.genType(fromExpr);
538 genOmpAccAtomicCaptureStatement<AtomicListT>(
539 converter, stmt2LHSArg, stmt1LHSArg,
540 /*leftHandClauseList=*/nullptr,
541 /*rightHandClauseList=*/nullptr, elementType, loc);
542 genOmpAccAtomicUpdateStatement<AtomicListT>(
543 converter, stmt2LHSArg, stmt2VarType, stmt2Var, stmt2Expr,
544 /*leftHandClauseList=*/nullptr,
545 /*rightHandClauseList=*/nullptr, loc, atomicCaptureOp);
546 } else {
547 // Atomic capture construct is of the form [capture-stmt, write-stmt]
548 firOpBuilder.setInsertionPoint(atomicCaptureOp);
549 mlir::Value stmt2RHSArg =
550 fir::getBase(converter.genExprValue(assign2.rhs, stmtCtx));
551 firOpBuilder.setInsertionPointToStart(&block);
552 const Fortran::semantics::SomeExpr &fromExpr =
553 *Fortran::semantics::GetExpr(stmt1Expr);
554 mlir::Type elementType = converter.genType(fromExpr);
555 genOmpAccAtomicCaptureStatement<AtomicListT>(
556 converter, stmt2LHSArg, stmt1LHSArg,
557 /*leftHandClauseList=*/nullptr,
558 /*rightHandClauseList=*/nullptr, elementType, loc);
559 genOmpAccAtomicWriteStatement<AtomicListT>(
560 converter, stmt2LHSArg, stmt2RHSArg,
561 /*leftHandClauseList=*/nullptr,
562 /*rightHandClauseList=*/nullptr, loc);
564 } else {
565 // Atomic capture construct is of the form [update-stmt, capture-stmt]
566 const Fortran::semantics::SomeExpr &fromExpr =
567 *Fortran::semantics::GetExpr(stmt2Expr);
568 mlir::Type elementType = converter.genType(fromExpr);
569 genOmpAccAtomicUpdateStatement<AtomicListT>(
570 converter, stmt1LHSArg, stmt1VarType, stmt1Var, stmt1Expr,
571 /*leftHandClauseList=*/nullptr,
572 /*rightHandClauseList=*/nullptr, loc, atomicCaptureOp);
573 genOmpAccAtomicCaptureStatement<AtomicListT>(
574 converter, stmt1LHSArg, stmt2LHSArg,
575 /*leftHandClauseList=*/nullptr,
576 /*rightHandClauseList=*/nullptr, elementType, loc);
578 firOpBuilder.setInsertionPointToEnd(&block);
579 if constexpr (std::is_same<AtomicListT,
580 Fortran::parser::OmpAtomicClauseList>()) {
581 firOpBuilder.create<mlir::omp::TerminatorOp>(loc);
582 } else {
583 firOpBuilder.create<mlir::acc::TerminatorOp>(loc);
585 firOpBuilder.setInsertionPointToStart(&block);
588 /// Create empty blocks for the current region.
589 /// These blocks replace blocks parented to an enclosing region.
590 template <typename... TerminatorOps>
591 void createEmptyRegionBlocks(
592 fir::FirOpBuilder &builder,
593 std::list<Fortran::lower::pft::Evaluation> &evaluationList) {
594 mlir::Region *region = &builder.getRegion();
595 for (Fortran::lower::pft::Evaluation &eval : evaluationList) {
596 if (eval.block) {
597 if (eval.block->empty()) {
598 eval.block->erase();
599 eval.block = builder.createBlock(region);
600 } else {
601 [[maybe_unused]] mlir::Operation &terminatorOp = eval.block->back();
602 assert(mlir::isa<TerminatorOps...>(terminatorOp) &&
603 "expected terminator op");
606 if (!eval.isDirective() && eval.hasNestedEvaluations())
607 createEmptyRegionBlocks<TerminatorOps...>(builder,
608 eval.getNestedEvaluations());
612 inline AddrAndBoundsInfo
613 getDataOperandBaseAddr(Fortran::lower::AbstractConverter &converter,
614 fir::FirOpBuilder &builder,
615 Fortran::lower::SymbolRef sym, mlir::Location loc) {
616 mlir::Value symAddr = converter.getSymbolAddress(sym);
617 mlir::Value rawInput = symAddr;
618 if (auto declareOp =
619 mlir::dyn_cast_or_null<hlfir::DeclareOp>(symAddr.getDefiningOp())) {
620 symAddr = declareOp.getResults()[0];
621 rawInput = declareOp.getResults()[1];
624 // TODO: Might need revisiting to handle for non-shared clauses
625 if (!symAddr) {
626 if (const auto *details =
627 sym->detailsIf<Fortran::semantics::HostAssocDetails>()) {
628 symAddr = converter.getSymbolAddress(details->symbol());
629 rawInput = symAddr;
633 if (!symAddr)
634 llvm::report_fatal_error("could not retrieve symbol address");
636 mlir::Value isPresent;
637 if (Fortran::semantics::IsOptional(sym))
638 isPresent =
639 builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), rawInput);
641 if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(
642 fir::unwrapRefType(symAddr.getType()))) {
643 if (mlir::isa<fir::RecordType>(boxTy.getEleTy()))
644 TODO(loc, "derived type");
646 // In case of a box reference, load it here to get the box value.
647 // This is preferrable because then the same box value can then be used for
648 // all address/dimension retrievals. For Fortran optional though, leave
649 // the load generation for later so it can be done in the appropriate
650 // if branches.
651 if (mlir::isa<fir::ReferenceType>(symAddr.getType()) &&
652 !Fortran::semantics::IsOptional(sym)) {
653 mlir::Value addr = builder.create<fir::LoadOp>(loc, symAddr);
654 return AddrAndBoundsInfo(addr, rawInput, isPresent, boxTy);
657 return AddrAndBoundsInfo(symAddr, rawInput, isPresent, boxTy);
659 return AddrAndBoundsInfo(symAddr, rawInput, isPresent);
662 template <typename BoundsOp, typename BoundsType>
663 llvm::SmallVector<mlir::Value>
664 gatherBoundsOrBoundValues(fir::FirOpBuilder &builder, mlir::Location loc,
665 fir::ExtendedValue dataExv, mlir::Value box,
666 bool collectValuesOnly = false) {
667 assert(box && "box must exist");
668 llvm::SmallVector<mlir::Value> values;
669 mlir::Value byteStride;
670 mlir::Type idxTy = builder.getIndexType();
671 mlir::Type boundTy = builder.getType<BoundsType>();
672 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
673 for (unsigned dim = 0; dim < dataExv.rank(); ++dim) {
674 mlir::Value d = builder.createIntegerConstant(loc, idxTy, dim);
675 mlir::Value baseLb =
676 fir::factory::readLowerBound(builder, loc, dataExv, dim, one);
677 auto dimInfo =
678 builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, box, d);
679 mlir::Value lb = builder.createIntegerConstant(loc, idxTy, 0);
680 mlir::Value ub =
681 builder.create<mlir::arith::SubIOp>(loc, dimInfo.getExtent(), one);
682 if (dim == 0) // First stride is the element size.
683 byteStride = dimInfo.getByteStride();
684 if (collectValuesOnly) {
685 values.push_back(lb);
686 values.push_back(ub);
687 values.push_back(dimInfo.getExtent());
688 values.push_back(byteStride);
689 values.push_back(baseLb);
690 } else {
691 mlir::Value bound = builder.create<BoundsOp>(
692 loc, boundTy, lb, ub, dimInfo.getExtent(), byteStride, true, baseLb);
693 values.push_back(bound);
695 // Compute the stride for the next dimension.
696 byteStride = builder.create<mlir::arith::MulIOp>(loc, byteStride,
697 dimInfo.getExtent());
699 return values;
702 /// Generate the bounds operation from the descriptor information.
703 template <typename BoundsOp, typename BoundsType>
704 llvm::SmallVector<mlir::Value>
705 genBoundsOpsFromBox(fir::FirOpBuilder &builder, mlir::Location loc,
706 fir::ExtendedValue dataExv,
707 Fortran::lower::AddrAndBoundsInfo &info) {
708 llvm::SmallVector<mlir::Value> bounds;
709 mlir::Type idxTy = builder.getIndexType();
710 mlir::Type boundTy = builder.getType<BoundsType>();
712 assert(mlir::isa<fir::BaseBoxType>(info.boxType) &&
713 "expect fir.box or fir.class");
714 assert(fir::unwrapRefType(info.addr.getType()) == info.boxType &&
715 "expected box type consistency");
717 if (info.isPresent) {
718 llvm::SmallVector<mlir::Type> resTypes;
719 constexpr unsigned nbValuesPerBound = 5;
720 for (unsigned dim = 0; dim < dataExv.rank() * nbValuesPerBound; ++dim)
721 resTypes.push_back(idxTy);
723 mlir::Operation::result_range ifRes =
724 builder.genIfOp(loc, resTypes, info.isPresent, /*withElseRegion=*/true)
725 .genThen([&]() {
726 mlir::Value box =
727 !fir::isBoxAddress(info.addr.getType())
728 ? info.addr
729 : builder.create<fir::LoadOp>(loc, info.addr);
730 llvm::SmallVector<mlir::Value> boundValues =
731 gatherBoundsOrBoundValues<BoundsOp, BoundsType>(
732 builder, loc, dataExv, box,
733 /*collectValuesOnly=*/true);
734 builder.create<fir::ResultOp>(loc, boundValues);
736 .genElse([&] {
737 // Box is not present. Populate bound values with default values.
738 llvm::SmallVector<mlir::Value> boundValues;
739 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
740 mlir::Value mOne = builder.createMinusOneInteger(loc, idxTy);
741 for (unsigned dim = 0; dim < dataExv.rank(); ++dim) {
742 boundValues.push_back(zero); // lb
743 boundValues.push_back(mOne); // ub
744 boundValues.push_back(zero); // extent
745 boundValues.push_back(zero); // byteStride
746 boundValues.push_back(zero); // baseLb
748 builder.create<fir::ResultOp>(loc, boundValues);
750 .getResults();
751 // Create the bound operations outside the if-then-else with the if op
752 // results.
753 for (unsigned i = 0; i < ifRes.size(); i += nbValuesPerBound) {
754 mlir::Value bound = builder.create<BoundsOp>(
755 loc, boundTy, ifRes[i], ifRes[i + 1], ifRes[i + 2], ifRes[i + 3],
756 true, ifRes[i + 4]);
757 bounds.push_back(bound);
759 } else {
760 mlir::Value box = !fir::isBoxAddress(info.addr.getType())
761 ? info.addr
762 : builder.create<fir::LoadOp>(loc, info.addr);
763 bounds = gatherBoundsOrBoundValues<BoundsOp, BoundsType>(builder, loc,
764 dataExv, box);
766 return bounds;
769 /// Generate bounds operation for base array without any subscripts
770 /// provided.
771 template <typename BoundsOp, typename BoundsType>
772 llvm::SmallVector<mlir::Value>
773 genBaseBoundsOps(fir::FirOpBuilder &builder, mlir::Location loc,
774 fir::ExtendedValue dataExv, bool isAssumedSize) {
775 mlir::Type idxTy = builder.getIndexType();
776 mlir::Type boundTy = builder.getType<BoundsType>();
777 llvm::SmallVector<mlir::Value> bounds;
779 if (dataExv.rank() == 0)
780 return bounds;
782 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
783 const unsigned rank = dataExv.rank();
784 for (unsigned dim = 0; dim < rank; ++dim) {
785 mlir::Value baseLb =
786 fir::factory::readLowerBound(builder, loc, dataExv, dim, one);
787 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
788 mlir::Value ub;
789 mlir::Value lb = zero;
790 mlir::Value ext = fir::factory::readExtent(builder, loc, dataExv, dim);
791 if (isAssumedSize && dim + 1 == rank) {
792 ext = zero;
793 ub = lb;
794 } else {
795 // ub = extent - 1
796 ub = builder.create<mlir::arith::SubIOp>(loc, ext, one);
799 mlir::Value bound =
800 builder.create<BoundsOp>(loc, boundTy, lb, ub, ext, one, false, baseLb);
801 bounds.push_back(bound);
803 return bounds;
806 namespace detail {
807 template <typename T> //
808 static T &&AsRvalueRef(T &&t) {
809 return std::move(t);
811 template <typename T> //
812 static T AsRvalueRef(T &t) {
813 return t;
815 template <typename T> //
816 static T AsRvalueRef(const T &t) {
817 return t;
820 // Helper class for stripping enclosing parentheses and a conversion that
821 // preserves type category. This is used for triplet elements, which are
822 // always of type integer(kind=8). The lower/upper bounds are converted to
823 // an "index" type, which is 64-bit, so the explicit conversion to kind=8
824 // (if present) is not needed. When it's present, though, it causes generated
825 // names to contain "int(..., kind=8)".
826 struct PeelConvert {
827 template <Fortran::common::TypeCategory Category, int Kind>
828 static Fortran::semantics::MaybeExpr visit_with_category(
829 const Fortran::evaluate::Expr<Fortran::evaluate::Type<Category, Kind>>
830 &expr) {
831 return Fortran::common::visit(
832 [](auto &&s) { return visit_with_category<Category, Kind>(s); },
833 expr.u);
835 template <Fortran::common::TypeCategory Category, int Kind>
836 static Fortran::semantics::MaybeExpr visit_with_category(
837 const Fortran::evaluate::Convert<Fortran::evaluate::Type<Category, Kind>,
838 Category> &expr) {
839 return AsGenericExpr(AsRvalueRef(expr.left()));
841 template <Fortran::common::TypeCategory Category, int Kind, typename T>
842 static Fortran::semantics::MaybeExpr visit_with_category(const T &) {
843 return std::nullopt; //
845 template <Fortran::common::TypeCategory Category, typename T>
846 static Fortran::semantics::MaybeExpr visit_with_category(const T &) {
847 return std::nullopt; //
850 template <Fortran::common::TypeCategory Category>
851 static Fortran::semantics::MaybeExpr
852 visit(const Fortran::evaluate::Expr<Fortran::evaluate::SomeKind<Category>>
853 &expr) {
854 return Fortran::common::visit(
855 [](auto &&s) { return visit_with_category<Category>(s); }, expr.u);
857 static Fortran::semantics::MaybeExpr
858 visit(const Fortran::evaluate::Expr<Fortran::evaluate::SomeType> &expr) {
859 return Fortran::common::visit([](auto &&s) { return visit(s); }, expr.u);
861 template <typename T> //
862 static Fortran::semantics::MaybeExpr visit(const T &) {
863 return std::nullopt;
867 static inline Fortran::semantics::SomeExpr
868 peelOuterConvert(Fortran::semantics::SomeExpr &expr) {
869 if (auto peeled = PeelConvert::visit(expr))
870 return *peeled;
871 return expr;
873 } // namespace detail
875 /// Generate bounds operations for an array section when subscripts are
876 /// provided.
877 template <typename BoundsOp, typename BoundsType>
878 llvm::SmallVector<mlir::Value>
879 genBoundsOps(fir::FirOpBuilder &builder, mlir::Location loc,
880 Fortran::lower::AbstractConverter &converter,
881 Fortran::lower::StatementContext &stmtCtx,
882 const std::vector<Fortran::evaluate::Subscript> &subscripts,
883 std::stringstream &asFortran, fir::ExtendedValue &dataExv,
884 bool dataExvIsAssumedSize, AddrAndBoundsInfo &info,
885 bool treatIndexAsSection = false) {
886 int dimension = 0;
887 mlir::Type idxTy = builder.getIndexType();
888 mlir::Type boundTy = builder.getType<BoundsType>();
889 llvm::SmallVector<mlir::Value> bounds;
891 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
892 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
893 const int dataExvRank = static_cast<int>(dataExv.rank());
894 for (const auto &subscript : subscripts) {
895 const auto *triplet{std::get_if<Fortran::evaluate::Triplet>(&subscript.u)};
896 if (triplet || treatIndexAsSection) {
897 if (dimension != 0)
898 asFortran << ',';
899 mlir::Value lbound, ubound, extent;
900 std::optional<std::int64_t> lval, uval;
901 mlir::Value baseLb =
902 fir::factory::readLowerBound(builder, loc, dataExv, dimension, one);
903 bool defaultLb = baseLb == one;
904 mlir::Value stride = one;
905 bool strideInBytes = false;
907 if (mlir::isa<fir::BaseBoxType>(
908 fir::unwrapRefType(info.addr.getType()))) {
909 if (info.isPresent) {
910 stride =
911 builder
912 .genIfOp(loc, idxTy, info.isPresent, /*withElseRegion=*/true)
913 .genThen([&]() {
914 mlir::Value box =
915 !fir::isBoxAddress(info.addr.getType())
916 ? info.addr
917 : builder.create<fir::LoadOp>(loc, info.addr);
918 mlir::Value d =
919 builder.createIntegerConstant(loc, idxTy, dimension);
920 auto dimInfo = builder.create<fir::BoxDimsOp>(
921 loc, idxTy, idxTy, idxTy, box, d);
922 builder.create<fir::ResultOp>(loc, dimInfo.getByteStride());
924 .genElse([&] {
925 mlir::Value zero =
926 builder.createIntegerConstant(loc, idxTy, 0);
927 builder.create<fir::ResultOp>(loc, zero);
929 .getResults()[0];
930 } else {
931 mlir::Value box = !fir::isBoxAddress(info.addr.getType())
932 ? info.addr
933 : builder.create<fir::LoadOp>(loc, info.addr);
934 mlir::Value d = builder.createIntegerConstant(loc, idxTy, dimension);
935 auto dimInfo =
936 builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, box, d);
937 stride = dimInfo.getByteStride();
939 strideInBytes = true;
942 Fortran::semantics::MaybeExpr lower;
943 if (triplet) {
944 lower = Fortran::evaluate::AsGenericExpr(triplet->lower());
945 } else {
946 // Case of IndirectSubscriptIntegerExpr
947 using IndirectSubscriptIntegerExpr =
948 Fortran::evaluate::IndirectSubscriptIntegerExpr;
949 using SubscriptInteger = Fortran::evaluate::SubscriptInteger;
950 Fortran::evaluate::Expr<SubscriptInteger> oneInt =
951 std::get<IndirectSubscriptIntegerExpr>(subscript.u).value();
952 lower = Fortran::evaluate::AsGenericExpr(std::move(oneInt));
953 if (lower->Rank() > 0) {
954 mlir::emitError(
955 loc, "vector subscript cannot be used for an array section");
956 break;
959 if (lower) {
960 lval = Fortran::evaluate::ToInt64(*lower);
961 if (lval) {
962 if (defaultLb) {
963 lbound = builder.createIntegerConstant(loc, idxTy, *lval - 1);
964 } else {
965 mlir::Value lb = builder.createIntegerConstant(loc, idxTy, *lval);
966 lbound = builder.create<mlir::arith::SubIOp>(loc, lb, baseLb);
968 asFortran << *lval;
969 } else {
970 mlir::Value lb =
971 fir::getBase(converter.genExprValue(loc, *lower, stmtCtx));
972 lb = builder.createConvert(loc, baseLb.getType(), lb);
973 lbound = builder.create<mlir::arith::SubIOp>(loc, lb, baseLb);
974 asFortran << detail::peelOuterConvert(*lower).AsFortran();
976 } else {
977 // If the lower bound is not specified, then the section
978 // starts from offset 0 of the dimension.
979 // Note that the lowerbound in the BoundsOp is always 0-based.
980 lbound = zero;
983 if (!triplet) {
984 // If it is a scalar subscript, then the upper bound
985 // is equal to the lower bound, and the extent is one.
986 ubound = lbound;
987 extent = one;
988 } else {
989 asFortran << ':';
990 Fortran::semantics::MaybeExpr upper =
991 Fortran::evaluate::AsGenericExpr(triplet->upper());
993 if (upper) {
994 uval = Fortran::evaluate::ToInt64(*upper);
995 if (uval) {
996 if (defaultLb) {
997 ubound = builder.createIntegerConstant(loc, idxTy, *uval - 1);
998 } else {
999 mlir::Value ub = builder.createIntegerConstant(loc, idxTy, *uval);
1000 ubound = builder.create<mlir::arith::SubIOp>(loc, ub, baseLb);
1002 asFortran << *uval;
1003 } else {
1004 mlir::Value ub =
1005 fir::getBase(converter.genExprValue(loc, *upper, stmtCtx));
1006 ub = builder.createConvert(loc, baseLb.getType(), ub);
1007 ubound = builder.create<mlir::arith::SubIOp>(loc, ub, baseLb);
1008 asFortran << detail::peelOuterConvert(*upper).AsFortran();
1011 if (lower && upper) {
1012 if (lval && uval && *uval < *lval) {
1013 mlir::emitError(loc, "zero sized array section");
1014 break;
1015 } else {
1016 // Stride is mandatory in evaluate::Triplet. Make sure it's 1.
1017 auto val = Fortran::evaluate::ToInt64(triplet->GetStride());
1018 if (!val || *val != 1) {
1019 mlir::emitError(loc, "stride cannot be specified on "
1020 "an array section");
1021 break;
1026 if (info.isPresent && mlir::isa<fir::BaseBoxType>(
1027 fir::unwrapRefType(info.addr.getType()))) {
1028 extent =
1029 builder
1030 .genIfOp(loc, idxTy, info.isPresent, /*withElseRegion=*/true)
1031 .genThen([&]() {
1032 mlir::Value ext = fir::factory::readExtent(
1033 builder, loc, dataExv, dimension);
1034 builder.create<fir::ResultOp>(loc, ext);
1036 .genElse([&] {
1037 mlir::Value zero =
1038 builder.createIntegerConstant(loc, idxTy, 0);
1039 builder.create<fir::ResultOp>(loc, zero);
1041 .getResults()[0];
1042 } else {
1043 extent = fir::factory::readExtent(builder, loc, dataExv, dimension);
1046 if (dataExvIsAssumedSize && dimension + 1 == dataExvRank) {
1047 extent = zero;
1048 if (ubound && lbound) {
1049 mlir::Value diff =
1050 builder.create<mlir::arith::SubIOp>(loc, ubound, lbound);
1051 extent = builder.create<mlir::arith::AddIOp>(loc, diff, one);
1053 if (!ubound)
1054 ubound = lbound;
1057 if (!ubound) {
1058 // ub = extent - 1
1059 ubound = builder.create<mlir::arith::SubIOp>(loc, extent, one);
1062 mlir::Value bound = builder.create<BoundsOp>(
1063 loc, boundTy, lbound, ubound, extent, stride, strideInBytes, baseLb);
1064 bounds.push_back(bound);
1065 ++dimension;
1068 return bounds;
1071 namespace detail {
1072 template <typename Ref, typename Expr> //
1073 std::optional<Ref> getRef(Expr &&expr) {
1074 if constexpr (std::is_same_v<llvm::remove_cvref_t<Expr>,
1075 Fortran::evaluate::DataRef>) {
1076 if (auto *ref = std::get_if<Ref>(&expr.u))
1077 return *ref;
1078 return std::nullopt;
1079 } else {
1080 auto maybeRef = Fortran::evaluate::ExtractDataRef(expr);
1081 if (!maybeRef || !std::holds_alternative<Ref>(maybeRef->u))
1082 return std::nullopt;
1083 return std::get<Ref>(maybeRef->u);
1086 } // namespace detail
1088 template <typename BoundsOp, typename BoundsType>
1089 AddrAndBoundsInfo gatherDataOperandAddrAndBounds(
1090 Fortran::lower::AbstractConverter &converter, fir::FirOpBuilder &builder,
1091 semantics::SemanticsContext &semaCtx,
1092 Fortran::lower::StatementContext &stmtCtx,
1093 Fortran::semantics::SymbolRef symbol,
1094 const Fortran::semantics::MaybeExpr &maybeDesignator,
1095 mlir::Location operandLocation, std::stringstream &asFortran,
1096 llvm::SmallVector<mlir::Value> &bounds, bool treatIndexAsSection = false) {
1097 using namespace Fortran;
1099 AddrAndBoundsInfo info;
1101 if (!maybeDesignator) {
1102 info = getDataOperandBaseAddr(converter, builder, symbol, operandLocation);
1103 asFortran << symbol->name().ToString();
1104 return info;
1107 semantics::SomeExpr designator = *maybeDesignator;
1109 if ((designator.Rank() > 0 || treatIndexAsSection) &&
1110 IsArrayElement(designator)) {
1111 auto arrayRef = detail::getRef<evaluate::ArrayRef>(designator);
1112 // This shouldn't fail after IsArrayElement(designator).
1113 assert(arrayRef && "Expecting ArrayRef");
1115 fir::ExtendedValue dataExv;
1116 bool dataExvIsAssumedSize = false;
1118 auto toMaybeExpr = [&](auto &&base) {
1119 using BaseType = llvm::remove_cvref_t<decltype(base)>;
1120 evaluate::ExpressionAnalyzer ea{semaCtx};
1122 if constexpr (std::is_same_v<evaluate::NamedEntity, BaseType>) {
1123 if (auto *ref = base.UnwrapSymbolRef())
1124 return ea.Designate(evaluate::DataRef{*ref});
1125 if (auto *ref = base.UnwrapComponent())
1126 return ea.Designate(evaluate::DataRef{*ref});
1127 llvm_unreachable("Unexpected NamedEntity");
1128 } else {
1129 static_assert(std::is_same_v<semantics::SymbolRef, BaseType>);
1130 return ea.Designate(evaluate::DataRef{base});
1134 auto arrayBase = toMaybeExpr(arrayRef->base());
1135 assert(arrayBase);
1137 if (detail::getRef<evaluate::Component>(*arrayBase)) {
1138 dataExv = converter.genExprAddr(operandLocation, *arrayBase, stmtCtx);
1139 info.addr = fir::getBase(dataExv);
1140 info.rawInput = info.addr;
1141 asFortran << arrayBase->AsFortran();
1142 } else {
1143 const semantics::Symbol &sym = arrayRef->GetLastSymbol();
1144 dataExvIsAssumedSize =
1145 Fortran::semantics::IsAssumedSizeArray(sym.GetUltimate());
1146 info = getDataOperandBaseAddr(converter, builder, sym, operandLocation);
1147 dataExv = converter.getSymbolExtendedValue(sym);
1148 asFortran << sym.name().ToString();
1151 if (!arrayRef->subscript().empty()) {
1152 asFortran << '(';
1153 bounds = genBoundsOps<BoundsOp, BoundsType>(
1154 builder, operandLocation, converter, stmtCtx, arrayRef->subscript(),
1155 asFortran, dataExv, dataExvIsAssumedSize, info, treatIndexAsSection);
1157 asFortran << ')';
1158 } else if (auto compRef = detail::getRef<evaluate::Component>(designator)) {
1159 fir::ExtendedValue compExv =
1160 converter.genExprAddr(operandLocation, designator, stmtCtx);
1161 info.addr = fir::getBase(compExv);
1162 info.rawInput = info.addr;
1163 if (mlir::isa<fir::SequenceType>(fir::unwrapRefType(info.addr.getType())))
1164 bounds = genBaseBoundsOps<BoundsOp, BoundsType>(builder, operandLocation,
1165 compExv,
1166 /*isAssumedSize=*/false);
1167 asFortran << designator.AsFortran();
1169 if (semantics::IsOptional(compRef->GetLastSymbol())) {
1170 info.isPresent = builder.create<fir::IsPresentOp>(
1171 operandLocation, builder.getI1Type(), info.rawInput);
1174 if (auto loadOp =
1175 mlir::dyn_cast_or_null<fir::LoadOp>(info.addr.getDefiningOp())) {
1176 if (fir::isAllocatableType(loadOp.getType()) ||
1177 fir::isPointerType(loadOp.getType())) {
1178 info.boxType = info.addr.getType();
1179 info.addr = builder.create<fir::BoxAddrOp>(operandLocation, info.addr);
1181 info.rawInput = info.addr;
1184 // If the component is an allocatable or pointer the result of
1185 // genExprAddr will be the result of a fir.box_addr operation or
1186 // a fir.box_addr has been inserted just before.
1187 // Retrieve the box so we handle it like other descriptor.
1188 if (auto boxAddrOp =
1189 mlir::dyn_cast_or_null<fir::BoxAddrOp>(info.addr.getDefiningOp())) {
1190 info.addr = boxAddrOp.getVal();
1191 info.boxType = info.addr.getType();
1192 info.rawInput = info.addr;
1193 bounds = genBoundsOpsFromBox<BoundsOp, BoundsType>(
1194 builder, operandLocation, compExv, info);
1196 } else {
1197 if (detail::getRef<evaluate::ArrayRef>(designator)) {
1198 fir::ExtendedValue compExv =
1199 converter.genExprAddr(operandLocation, designator, stmtCtx);
1200 info.addr = fir::getBase(compExv);
1201 info.rawInput = info.addr;
1202 asFortran << designator.AsFortran();
1203 } else if (auto symRef = detail::getRef<semantics::SymbolRef>(designator)) {
1204 // Scalar or full array.
1205 fir::ExtendedValue dataExv = converter.getSymbolExtendedValue(*symRef);
1206 info =
1207 getDataOperandBaseAddr(converter, builder, *symRef, operandLocation);
1208 if (mlir::isa<fir::BaseBoxType>(
1209 fir::unwrapRefType(info.addr.getType()))) {
1210 info.boxType = fir::unwrapRefType(info.addr.getType());
1211 bounds = genBoundsOpsFromBox<BoundsOp, BoundsType>(
1212 builder, operandLocation, dataExv, info);
1214 bool dataExvIsAssumedSize =
1215 Fortran::semantics::IsAssumedSizeArray(symRef->get().GetUltimate());
1216 if (mlir::isa<fir::SequenceType>(fir::unwrapRefType(info.addr.getType())))
1217 bounds = genBaseBoundsOps<BoundsOp, BoundsType>(
1218 builder, operandLocation, dataExv, dataExvIsAssumedSize);
1219 asFortran << symRef->get().name().ToString();
1220 } else { // Unsupported
1221 llvm::report_fatal_error("Unsupported type of OpenACC operand");
1225 return info;
1227 } // namespace lower
1228 } // namespace Fortran
1230 #endif // FORTRAN_LOWER_DIRECTIVES_COMMON_H