1 //===-- Lower/DirectivesCommon.h --------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
11 //===----------------------------------------------------------------------===//
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"
47 #include <type_traits>
52 /// Information gathered to generate bounds operation and data entry/exit
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
>(
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
) {
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
,
158 if (rightHandClauseList
)
159 genOmpAtomicHintAndMemoryOrderClauses(converter
, *rightHandClauseList
,
161 firOpBuilder
.create
<mlir::omp::AtomicReadOp
>(
162 loc
, fromAddress
, toAddress
, mlir::TypeAttr::get(elementType
), hint
,
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
,
199 if (rightHandClauseList
)
200 genOmpAtomicHintAndMemoryOrderClauses(converter
, *rightHandClauseList
,
202 firOpBuilder
.create
<mlir::omp::AtomicWriteOp
>(loc
, lhsAddr
, rhsExpr
, hint
,
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> {
231 // %3 = arith.addi %arg0, %2 : i32
232 // omp.yield(%3 : i32)
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
)};
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
)
255 const auto &args
{std::get
<std::list
<parser::ActualArgSpec
>>(
256 funcRef
.value().v
.t
)};
257 std::list
<parser::ActualArgSpec
>::const_iterator beginIt
=
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
266 // Add everything except the last
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
);
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
,
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
));
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();
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
);
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
,
320 if (rightHandClauseList
)
321 genOmpAtomicHintAndMemoryOrderClauses(converter
, *rightHandClauseList
,
323 atomicUpdateOp
= firOpBuilder
.create
<mlir::omp::AtomicUpdateOp
>(
324 currentLocation
, lhsAddr
, hint
, memoryOrder
);
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
);
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
);
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
>>(
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
>>(
404 const auto &assignmentStmtVariable
= std::get
<Fortran::parser::Variable
>(
405 std::get
<Fortran::parser::Statement
<Fortran::parser::AssignmentStmt
>>(
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
,
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
>>(
439 const auto &assignmentStmtVariable
= std::get
<Fortran::parser::Variable
>(
440 std::get
<Fortran::parser::Statement
<Fortran::parser::AssignmentStmt
>>(
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
>>(
463 const auto &assignmentStmtVariable
= std::get
<Fortran::parser::Variable
>(
464 std::get
<Fortran::parser::Statement
<Fortran::parser::AssignmentStmt
>>(
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
,
521 genOmpAtomicHintAndMemoryOrderClauses(converter
, rightHandClauseList
, hint
,
524 firOpBuilder
.create
<mlir::omp::AtomicCaptureOp
>(loc
, hint
, memoryOrder
);
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
);
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
);
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
);
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
) {
597 if (eval
.block
->empty()) {
599 eval
.block
= builder
.createBlock(region
);
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
;
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
626 if (const auto *details
=
627 sym
->detailsIf
<Fortran::semantics::HostAssocDetails
>()) {
628 symAddr
= converter
.getSymbolAddress(details
->symbol());
634 llvm::report_fatal_error("could not retrieve symbol address");
636 mlir::Value isPresent
;
637 if (Fortran::semantics::IsOptional(sym
))
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
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
);
676 fir::factory::readLowerBound(builder
, loc
, dataExv
, dim
, one
);
678 builder
.create
<fir::BoxDimsOp
>(loc
, idxTy
, idxTy
, idxTy
, box
, d
);
679 mlir::Value lb
= builder
.createIntegerConstant(loc
, idxTy
, 0);
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
);
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());
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)
727 !fir::isBoxAddress(info
.addr
.getType())
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
);
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
);
751 // Create the bound operations outside the if-then-else with the if op
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],
757 bounds
.push_back(bound
);
760 mlir::Value box
= !fir::isBoxAddress(info
.addr
.getType())
762 : builder
.create
<fir::LoadOp
>(loc
, info
.addr
);
763 bounds
= gatherBoundsOrBoundValues
<BoundsOp
, BoundsType
>(builder
, loc
,
769 /// Generate bounds operation for base array without any subscripts
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)
782 mlir::Value one
= builder
.createIntegerConstant(loc
, idxTy
, 1);
783 const unsigned rank
= dataExv
.rank();
784 for (unsigned dim
= 0; dim
< rank
; ++dim
) {
786 fir::factory::readLowerBound(builder
, loc
, dataExv
, dim
, one
);
787 mlir::Value zero
= builder
.createIntegerConstant(loc
, idxTy
, 0);
789 mlir::Value lb
= zero
;
790 mlir::Value ext
= fir::factory::readExtent(builder
, loc
, dataExv
, dim
);
791 if (isAssumedSize
&& dim
+ 1 == rank
) {
796 ub
= builder
.create
<mlir::arith::SubIOp
>(loc
, ext
, one
);
800 builder
.create
<BoundsOp
>(loc
, boundTy
, lb
, ub
, ext
, one
, false, baseLb
);
801 bounds
.push_back(bound
);
807 template <typename T
> //
808 static T
&&AsRvalueRef(T
&&t
) {
811 template <typename T
> //
812 static T
AsRvalueRef(T
&t
) {
815 template <typename T
> //
816 static T
AsRvalueRef(const T
&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)".
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
>>
831 return Fortran::common::visit(
832 [](auto &&s
) { return visit_with_category
<Category
, Kind
>(s
); },
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
>,
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
>>
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
&) {
867 static inline Fortran::semantics::SomeExpr
868 peelOuterConvert(Fortran::semantics::SomeExpr
&expr
) {
869 if (auto peeled
= PeelConvert::visit(expr
))
873 } // namespace detail
875 /// Generate bounds operations for an array section when subscripts are
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) {
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
) {
899 mlir::Value lbound
, ubound
, extent
;
900 std::optional
<std::int64_t> lval
, uval
;
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
) {
912 .genIfOp(loc
, idxTy
, info
.isPresent
, /*withElseRegion=*/true)
915 !fir::isBoxAddress(info
.addr
.getType())
917 : builder
.create
<fir::LoadOp
>(loc
, info
.addr
);
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());
926 builder
.createIntegerConstant(loc
, idxTy
, 0);
927 builder
.create
<fir::ResultOp
>(loc
, zero
);
931 mlir::Value box
= !fir::isBoxAddress(info
.addr
.getType())
933 : builder
.create
<fir::LoadOp
>(loc
, info
.addr
);
934 mlir::Value d
= builder
.createIntegerConstant(loc
, idxTy
, dimension
);
936 builder
.create
<fir::BoxDimsOp
>(loc
, idxTy
, idxTy
, idxTy
, box
, d
);
937 stride
= dimInfo
.getByteStride();
939 strideInBytes
= true;
942 Fortran::semantics::MaybeExpr lower
;
944 lower
= Fortran::evaluate::AsGenericExpr(triplet
->lower());
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) {
955 loc
, "vector subscript cannot be used for an array section");
960 lval
= Fortran::evaluate::ToInt64(*lower
);
963 lbound
= builder
.createIntegerConstant(loc
, idxTy
, *lval
- 1);
965 mlir::Value lb
= builder
.createIntegerConstant(loc
, idxTy
, *lval
);
966 lbound
= builder
.create
<mlir::arith::SubIOp
>(loc
, lb
, baseLb
);
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();
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.
984 // If it is a scalar subscript, then the upper bound
985 // is equal to the lower bound, and the extent is one.
990 Fortran::semantics::MaybeExpr upper
=
991 Fortran::evaluate::AsGenericExpr(triplet
->upper());
994 uval
= Fortran::evaluate::ToInt64(*upper
);
997 ubound
= builder
.createIntegerConstant(loc
, idxTy
, *uval
- 1);
999 mlir::Value ub
= builder
.createIntegerConstant(loc
, idxTy
, *uval
);
1000 ubound
= builder
.create
<mlir::arith::SubIOp
>(loc
, ub
, baseLb
);
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");
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");
1026 if (info
.isPresent
&& mlir::isa
<fir::BaseBoxType
>(
1027 fir::unwrapRefType(info
.addr
.getType()))) {
1030 .genIfOp(loc
, idxTy
, info
.isPresent
, /*withElseRegion=*/true)
1032 mlir::Value ext
= fir::factory::readExtent(
1033 builder
, loc
, dataExv
, dimension
);
1034 builder
.create
<fir::ResultOp
>(loc
, ext
);
1038 builder
.createIntegerConstant(loc
, idxTy
, 0);
1039 builder
.create
<fir::ResultOp
>(loc
, zero
);
1043 extent
= fir::factory::readExtent(builder
, loc
, dataExv
, dimension
);
1046 if (dataExvIsAssumedSize
&& dimension
+ 1 == dataExvRank
) {
1048 if (ubound
&& lbound
) {
1050 builder
.create
<mlir::arith::SubIOp
>(loc
, ubound
, lbound
);
1051 extent
= builder
.create
<mlir::arith::AddIOp
>(loc
, diff
, one
);
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
);
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
))
1078 return std::nullopt
;
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();
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");
1129 static_assert(std::is_same_v
<semantics::SymbolRef
, BaseType
>);
1130 return ea
.Designate(evaluate::DataRef
{base
});
1134 auto arrayBase
= toMaybeExpr(arrayRef
->base());
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();
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()) {
1153 bounds
= genBoundsOps
<BoundsOp
, BoundsType
>(
1154 builder
, operandLocation
, converter
, stmtCtx
, arrayRef
->subscript(),
1155 asFortran
, dataExv
, dataExvIsAssumedSize
, info
, treatIndexAsSection
);
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
,
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
);
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
);
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
);
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");
1227 } // namespace lower
1228 } // namespace Fortran
1230 #endif // FORTRAN_LOWER_DIRECTIVES_COMMON_H