FLANG
DirectivesCommon.h
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/
10//
11//===----------------------------------------------------------------------===//
16//===----------------------------------------------------------------------===//
17
18#ifndef FORTRAN_LOWER_DIRECTIVES_COMMON_H
19#define FORTRAN_LOWER_DIRECTIVES_COMMON_H
20
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/DirectivesCommon.h"
33#include "flang/Optimizer/Builder/HLFIRTools.h"
34#include "flang/Optimizer/Dialect/FIRType.h"
35#include "flang/Parser/parse-tree.h"
36#include "flang/Semantics/openmp-directive-sets.h"
37#include "flang/Semantics/tools.h"
38#include "mlir/Dialect/OpenACC/OpenACC.h"
39#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
40#include "mlir/Dialect/SCF/IR/SCF.h"
41#include "mlir/IR/Value.h"
42#include "llvm/Frontend/OpenMP/OMPConstants.h"
43#include <list>
44#include <type_traits>
45
46namespace Fortran {
47namespace lower {
48
51template <typename... TerminatorOps>
53 fir::FirOpBuilder &builder,
54 std::list<Fortran::lower::pft::Evaluation> &evaluationList) {
55 mlir::Region *region = &builder.getRegion();
56 for (Fortran::lower::pft::Evaluation &eval : evaluationList) {
57 if (eval.block) {
58 if (eval.block->empty()) {
59 eval.block->erase();
60 eval.block = builder.createBlock(region);
61 } else {
62 [[maybe_unused]] mlir::Operation &terminatorOp = eval.block->back();
63 assert(mlir::isa<TerminatorOps...>(terminatorOp) &&
64 "expected terminator op");
65 }
66 }
67 if (!eval.isDirective() && eval.hasNestedEvaluations())
68 createEmptyRegionBlocks<TerminatorOps...>(builder,
69 eval.getNestedEvaluations());
70 }
71}
72
74getDataOperandBaseAddr(Fortran::lower::AbstractConverter &converter,
75 fir::FirOpBuilder &builder,
76 Fortran::lower::SymbolRef sym, mlir::Location loc,
77 bool unwrapFirBox = true) {
78 return fir::factory::getDataOperandBaseAddr(
79 builder, converter.getSymbolAddress(sym),
80 Fortran::semantics::IsOptional(sym), loc, unwrapFirBox);
81}
82
83namespace detail {
84template <typename T> //
85static T &&AsRvalueRef(T &&t) {
86 return std::move(t);
87}
88template <typename T> //
89static T AsRvalueRef(T &t) {
90 return t;
91}
92template <typename T> //
93static T AsRvalueRef(const T &t) {
94 return t;
95}
96
97// Helper class for stripping enclosing parentheses and a conversion that
98// preserves type category. This is used for triplet elements, which are
99// always of type integer(kind=8). The lower/upper bounds are converted to
100// an "index" type, which is 64-bit, so the explicit conversion to kind=8
101// (if present) is not needed. When it's present, though, it causes generated
102// names to contain "int(..., kind=8)".
104 template <Fortran::common::TypeCategory Category, int Kind>
105 static Fortran::semantics::MaybeExpr visit_with_category(
107 &expr) {
108 return Fortran::common::visit(
109 [](auto &&s) { return visit_with_category<Category, Kind>(s); },
110 expr.u);
111 }
112 template <Fortran::common::TypeCategory Category, int Kind>
113 static Fortran::semantics::MaybeExpr visit_with_category(
115 Category> &expr) {
116 return AsGenericExpr(AsRvalueRef(expr.left()));
117 }
118 template <Fortran::common::TypeCategory Category, int Kind, typename T>
119 static Fortran::semantics::MaybeExpr visit_with_category(const T &) {
120 return std::nullopt; //
121 }
122 template <Fortran::common::TypeCategory Category, typename T>
123 static Fortran::semantics::MaybeExpr visit_with_category(const T &) {
124 return std::nullopt; //
125 }
126
127 template <Fortran::common::TypeCategory Category>
128 static Fortran::semantics::MaybeExpr
130 &expr) {
131 return Fortran::common::visit(
132 [](auto &&s) { return visit_with_category<Category>(s); }, expr.u);
133 }
134 static Fortran::semantics::MaybeExpr
136 return Fortran::common::visit([](auto &&s) { return visit(s); }, expr.u);
137 }
138 template <typename T> //
139 static Fortran::semantics::MaybeExpr visit(const T &) {
140 return std::nullopt;
141 }
142};
143
144static inline Fortran::semantics::SomeExpr
145peelOuterConvert(Fortran::semantics::SomeExpr &expr) {
146 if (auto peeled = PeelConvert::visit(expr))
147 return *peeled;
148 return expr;
149}
150} // namespace detail
151
154template <typename BoundsOp, typename BoundsType>
156genBoundsOps(fir::FirOpBuilder &builder, mlir::Location loc,
159 const std::vector<Fortran::evaluate::Subscript> &subscripts,
160 std::stringstream &asFortran, fir::ExtendedValue &dataExv,
161 bool dataExvIsAssumedSize, fir::factory::AddrAndBoundsInfo &info,
162 bool treatIndexAsSection = false,
163 bool strideIncludeLowerExtent = false) {
164 int dimension = 0;
165 mlir::Type idxTy = builder.getIndexType();
166 mlir::Type boundTy = builder.getType<BoundsType>();
168
169 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
170 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
171 const int dataExvRank = static_cast<int>(dataExv.rank());
172 mlir::Value cumulativeExtent = one;
173 for (const auto &subscript : subscripts) {
174 const auto *triplet{std::get_if<Fortran::evaluate::Triplet>(&subscript.u)};
175 if (triplet || treatIndexAsSection) {
176 if (dimension != 0)
177 asFortran << ',';
178 mlir::Value lbound, ubound, extent;
179 std::optional<std::int64_t> lval, uval;
180 mlir::Value baseLb =
181 fir::factory::readLowerBound(builder, loc, dataExv, dimension, one);
182 bool defaultLb = baseLb == one;
183 mlir::Value stride = one;
184 bool strideInBytes = false;
185
186 if (mlir::isa<fir::BaseBoxType>(
187 fir::unwrapRefType(info.addr.getType()))) {
188 if (info.isPresent) {
189 stride =
190 builder
191 .genIfOp(loc, idxTy, info.isPresent, /*withElseRegion=*/true)
192 .genThen([&]() {
193 mlir::Value box =
194 !fir::isBoxAddress(info.addr.getType())
195 ? info.addr
196 : fir::LoadOp::create(builder, loc, info.addr);
197 mlir::Value d =
198 builder.createIntegerConstant(loc, idxTy, dimension);
199 auto dimInfo = fir::BoxDimsOp::create(builder, loc, idxTy,
200 idxTy, idxTy, box, d);
201 fir::ResultOp::create(builder, loc,
202 dimInfo.getByteStride());
203 })
204 .genElse([&] {
205 mlir::Value zero =
206 builder.createIntegerConstant(loc, idxTy, 0);
207 fir::ResultOp::create(builder, loc, zero);
208 })
209 .getResults()[0];
210 } else {
211 mlir::Value box = !fir::isBoxAddress(info.addr.getType())
212 ? info.addr
213 : fir::LoadOp::create(builder, loc, info.addr);
214 mlir::Value d = builder.createIntegerConstant(loc, idxTy, dimension);
215 auto dimInfo =
216 fir::BoxDimsOp::create(builder, loc, idxTy, idxTy, idxTy, box, d);
217 stride = dimInfo.getByteStride();
218 }
219 strideInBytes = true;
220 }
221
222 Fortran::semantics::MaybeExpr lower;
223 if (triplet) {
224 lower = Fortran::evaluate::AsGenericExpr(triplet->lower());
225 } else {
226 // Case of IndirectSubscriptIntegerExpr
227 using IndirectSubscriptIntegerExpr =
228 Fortran::evaluate::IndirectSubscriptIntegerExpr;
229 using SubscriptInteger = Fortran::evaluate::SubscriptInteger;
231 std::get<IndirectSubscriptIntegerExpr>(subscript.u).value();
232 lower = Fortran::evaluate::AsGenericExpr(std::move(oneInt));
233 if (lower->Rank() > 0) {
234 mlir::emitError(
235 loc, "vector subscript cannot be used for an array section");
236 break;
237 }
238 }
239 if (lower) {
240 lval = Fortran::evaluate::ToInt64(*lower);
241 if (lval) {
242 if (defaultLb) {
243 lbound = builder.createIntegerConstant(loc, idxTy, *lval - 1);
244 } else {
245 mlir::Value lb = builder.createIntegerConstant(loc, idxTy, *lval);
246 lbound = mlir::arith::SubIOp::create(builder, loc, lb, baseLb);
247 }
248 asFortran << *lval;
249 } else {
250 mlir::Value lb =
251 fir::getBase(converter.genExprValue(loc, *lower, stmtCtx));
252 lb = builder.createConvert(loc, baseLb.getType(), lb);
253 lbound = mlir::arith::SubIOp::create(builder, loc, lb, baseLb);
254 asFortran << detail::peelOuterConvert(*lower).AsFortran();
255 }
256 } else {
257 // If the lower bound is not specified, then the section
258 // starts from offset 0 of the dimension.
259 // Note that the lowerbound in the BoundsOp is always 0-based.
260 lbound = zero;
261 }
262
263 if (!triplet) {
264 // If it is a scalar subscript, then the upper bound
265 // is equal to the lower bound, and the extent is one.
266 ubound = lbound;
267 extent = one;
268 } else {
269 asFortran << ':';
270 Fortran::semantics::MaybeExpr upper =
271 Fortran::evaluate::AsGenericExpr(triplet->upper());
272
273 if (upper) {
274 uval = Fortran::evaluate::ToInt64(*upper);
275 if (uval) {
276 if (defaultLb) {
277 ubound = builder.createIntegerConstant(loc, idxTy, *uval - 1);
278 } else {
279 mlir::Value ub = builder.createIntegerConstant(loc, idxTy, *uval);
280 ubound = mlir::arith::SubIOp::create(builder, loc, ub, baseLb);
281 }
282 asFortran << *uval;
283 } else {
284 mlir::Value ub =
285 fir::getBase(converter.genExprValue(loc, *upper, stmtCtx));
286 ub = builder.createConvert(loc, baseLb.getType(), ub);
287 ubound = mlir::arith::SubIOp::create(builder, loc, ub, baseLb);
288 asFortran << detail::peelOuterConvert(*upper).AsFortran();
289 }
290 }
291 if (lower && upper) {
292 if (lval && uval && *uval < *lval) {
293 mlir::emitError(loc, "zero sized array section");
294 break;
295 } else {
296 // Stride is mandatory in evaluate::Triplet. Make sure it's 1.
297 auto val = Fortran::evaluate::ToInt64(triplet->GetStride());
298 if (!val || *val != 1) {
299 mlir::emitError(loc, "stride cannot be specified on "
300 "an array section");
301 break;
302 }
303 }
304 }
305
306 if (info.isPresent && mlir::isa<fir::BaseBoxType>(
307 fir::unwrapRefType(info.addr.getType()))) {
308 extent =
309 builder
310 .genIfOp(loc, idxTy, info.isPresent, /*withElseRegion=*/true)
311 .genThen([&]() {
312 mlir::Value ext = fir::factory::readExtent(
313 builder, loc, dataExv, dimension);
314 fir::ResultOp::create(builder, loc, ext);
315 })
316 .genElse([&] {
317 mlir::Value zero =
318 builder.createIntegerConstant(loc, idxTy, 0);
319 fir::ResultOp::create(builder, loc, zero);
320 })
321 .getResults()[0];
322 } else {
323 extent = fir::factory::readExtent(builder, loc, dataExv, dimension);
324 }
325
326 if (dataExvIsAssumedSize && dimension + 1 == dataExvRank) {
327 extent = zero;
328 if (ubound && lbound) {
329 mlir::Value diff =
330 mlir::arith::SubIOp::create(builder, loc, ubound, lbound);
331 extent = mlir::arith::AddIOp::create(builder, loc, diff, one);
332 }
333 if (!ubound)
334 ubound = lbound;
335 }
336
337 if (!ubound) {
338 // ub = extent - 1
339 ubound = mlir::arith::SubIOp::create(builder, loc, extent, one);
340 }
341 }
342
343 // When the strideInBytes is true, it means the stride is from descriptor
344 // and this already includes the lower extents.
345 if (strideIncludeLowerExtent && !strideInBytes) {
346 stride = cumulativeExtent;
347 cumulativeExtent = builder.createOrFold<mlir::arith::MulIOp>(
348 loc, cumulativeExtent, extent);
349 }
350
351 mlir::Value bound =
352 BoundsOp::create(builder, loc, boundTy, lbound, ubound, extent,
353 stride, strideInBytes, baseLb);
354 bounds.push_back(bound);
355 ++dimension;
356 }
357 }
358 return bounds;
359}
360
361namespace detail {
362template <typename Ref, typename Expr> //
363std::optional<Ref> getRef(Expr &&expr) {
364 if constexpr (std::is_same_v<llvm::remove_cvref_t<Expr>,
366 if (auto *ref = std::get_if<Ref>(&expr.u))
367 return *ref;
368 return std::nullopt;
369 } else {
370 auto maybeRef = Fortran::evaluate::ExtractDataRef(expr);
371 if (!maybeRef || !std::holds_alternative<Ref>(maybeRef->u))
372 return std::nullopt;
373 return std::get<Ref>(maybeRef->u);
374 }
375}
376} // namespace detail
377
378template <typename BoundsOp, typename BoundsType>
379fir::factory::AddrAndBoundsInfo gatherDataOperandAddrAndBounds(
380 Fortran::lower::AbstractConverter &converter, fir::FirOpBuilder &builder,
381 semantics::SemanticsContext &semaCtx,
382 Fortran::lower::StatementContext &stmtCtx,
383 Fortran::semantics::SymbolRef symbol,
384 const Fortran::semantics::MaybeExpr &maybeDesignator,
385 mlir::Location operandLocation, std::stringstream &asFortran,
386 llvm::SmallVector<mlir::Value> &bounds, bool treatIndexAsSection = false,
387 bool unwrapFirBox = true, bool genDefaultBounds = true,
388 bool strideIncludeLowerExtent = false) {
389 using namespace Fortran;
390
391 fir::factory::AddrAndBoundsInfo info;
392
393 if (!maybeDesignator) {
394 info = getDataOperandBaseAddr(converter, builder, symbol, operandLocation,
395 unwrapFirBox);
396 asFortran << symbol->name().ToString();
397 return info;
398 }
399
400 semantics::SomeExpr designator = *maybeDesignator;
401
402 if ((designator.Rank() > 0 || treatIndexAsSection) &&
403 IsArrayElement(designator)) {
404 auto arrayRef = detail::getRef<evaluate::ArrayRef>(designator);
405 // This shouldn't fail after IsArrayElement(designator).
406 assert(arrayRef && "Expecting ArrayRef");
407
408 fir::ExtendedValue dataExv;
409 bool dataExvIsAssumedSize = false;
410
411 auto toMaybeExpr = [&](auto &&base) {
412 using BaseType = llvm::remove_cvref_t<decltype(base)>;
413 evaluate::ExpressionAnalyzer ea{semaCtx};
414
415 if constexpr (std::is_same_v<evaluate::NamedEntity, BaseType>) {
416 if (auto *ref = base.UnwrapSymbolRef())
417 return ea.Designate(evaluate::DataRef{*ref});
418 if (auto *ref = base.UnwrapComponent())
419 return ea.Designate(evaluate::DataRef{*ref});
420 llvm_unreachable("Unexpected NamedEntity");
421 } else {
422 static_assert(std::is_same_v<semantics::SymbolRef, BaseType>);
423 return ea.Designate(evaluate::DataRef{base});
424 }
425 };
426
427 auto arrayBase = toMaybeExpr(arrayRef->base());
428 assert(arrayBase);
429
430 if (detail::getRef<evaluate::Component>(*arrayBase)) {
431 dataExv = converter.genExprAddr(operandLocation, *arrayBase, stmtCtx);
432 info.addr = fir::getBase(dataExv);
433 info.rawInput = info.addr;
434 asFortran << arrayBase->AsFortran();
435 } else {
436 const semantics::Symbol &sym = arrayRef->GetLastSymbol();
437 dataExvIsAssumedSize =
438 Fortran::semantics::IsAssumedSizeArray(sym.GetUltimate());
439 info = getDataOperandBaseAddr(converter, builder, sym, operandLocation,
440 unwrapFirBox);
441 dataExv = converter.getSymbolExtendedValue(sym);
442 asFortran << sym.name().ToString();
443 }
444
445 if (!arrayRef->subscript().empty()) {
446 asFortran << '(';
448 builder, operandLocation, converter, stmtCtx, arrayRef->subscript(),
449 asFortran, dataExv, dataExvIsAssumedSize, info, treatIndexAsSection,
450 strideIncludeLowerExtent);
451 }
452 asFortran << ')';
453 } else if (auto compRef = detail::getRef<evaluate::Component>(designator)) {
454 fir::ExtendedValue compExv =
455 converter.genExprAddr(operandLocation, designator, stmtCtx);
456 info.addr = fir::getBase(compExv);
457 info.rawInput = info.addr;
458 if (genDefaultBounds &&
459 mlir::isa<fir::SequenceType>(fir::unwrapRefType(info.addr.getType())))
461 builder, operandLocation, compExv,
462 /*isAssumedSize=*/false, strideIncludeLowerExtent);
463 asFortran << designator.AsFortran();
464
465 if (semantics::IsOptional(compRef->GetLastSymbol())) {
466 info.isPresent = fir::IsPresentOp::create(
467 builder, operandLocation, builder.getI1Type(), info.rawInput);
468 }
469
470 if (unwrapFirBox) {
471 if (auto loadOp =
472 mlir::dyn_cast_or_null<fir::LoadOp>(info.addr.getDefiningOp())) {
473 if (fir::isAllocatableType(loadOp.getType()) ||
474 fir::isPointerType(loadOp.getType())) {
475 info.boxType = info.addr.getType();
476 info.addr =
477 fir::BoxAddrOp::create(builder, operandLocation, info.addr);
478 }
479 info.rawInput = info.addr;
480 }
481 }
482
483 // If the component is an allocatable or pointer the result of
484 // genExprAddr will be the result of a fir.box_addr operation or
485 // a fir.box_addr has been inserted just before.
486 // Retrieve the box so we handle it like other descriptor.
487 if (auto boxAddrOp =
488 mlir::dyn_cast_or_null<fir::BoxAddrOp>(info.addr.getDefiningOp())) {
489 info.addr = boxAddrOp.getVal();
490 info.boxType = info.addr.getType();
491 info.rawInput = info.addr;
492 if (genDefaultBounds)
494 builder, operandLocation, compExv, info);
495 }
496 } else {
497 if (detail::getRef<evaluate::ArrayRef>(designator)) {
498 fir::ExtendedValue compExv =
499 converter.genExprAddr(operandLocation, designator, stmtCtx);
500 info.addr = fir::getBase(compExv);
501 info.rawInput = info.addr;
502 asFortran << designator.AsFortran();
503 } else if (auto symRef = detail::getRef<semantics::SymbolRef>(designator)) {
504 // Scalar or full array.
505 fir::ExtendedValue dataExv = converter.getSymbolExtendedValue(*symRef);
506 info = getDataOperandBaseAddr(converter, builder, *symRef,
507 operandLocation, unwrapFirBox);
508 if (genDefaultBounds && mlir::isa<fir::BaseBoxType>(
509 fir::unwrapRefType(info.addr.getType()))) {
510 info.boxType = fir::unwrapRefType(info.addr.getType());
512 builder, operandLocation, dataExv, info);
513 }
514 bool dataExvIsAssumedSize =
515 Fortran::semantics::IsAssumedSizeArray(symRef->get().GetUltimate());
516 if (genDefaultBounds &&
517 mlir::isa<fir::SequenceType>(fir::unwrapRefType(info.addr.getType())))
519 builder, operandLocation, dataExv, dataExvIsAssumedSize,
520 strideIncludeLowerExtent);
521 asFortran << symRef->get().name().ToString();
522 } else { // Unsupported
523 llvm::report_fatal_error("Unsupported type of OpenACC operand");
524 }
525 }
526
527 return info;
528}
529
530} // namespace lower
531} // namespace Fortran
532
533#endif // FORTRAN_LOWER_DIRECTIVES_COMMON_H
Definition common.h:214
Definition type.h:57
Definition AbstractConverter.h:85
virtual mlir::Value getSymbolAddress(SymbolRef sym)=0
Get the mlir instance of a symbol.
virtual fir::ExtendedValue genExprValue(const SomeExpr &expr, StatementContext &context, mlir::Location *locPtr=nullptr)=0
Generate the computations of the expression to produce a value.
virtual fir::ExtendedValue genExprAddr(const SomeExpr &expr, StatementContext &context, mlir::Location *locPtr=nullptr)=0
Definition StatementContext.h:46
Definition BoxValue.h:478
Definition FIRBuilder.h:55
mlir::Value createConvert(mlir::Location loc, mlir::Type toTy, mlir::Value val)
Lazy creation of fir.convert op.
Definition FIRBuilder.cpp:611
IfBuilder genIfOp(mlir::Location loc, mlir::TypeRange results, mlir::Value cdt, bool withElseRegion)
Definition FIRBuilder.h:538
mlir::Region & getRegion()
Get the current Region of the insertion point.
Definition FIRBuilder.h:109
mlir::Value createIntegerConstant(mlir::Location loc, mlir::Type integerType, std::int64_t i)
Definition FIRBuilder.cpp:144
Definition OpenACC.h:20
Definition ParserActions.h:24
llvm::SmallVector< mlir::Value > genBoundsOps(fir::FirOpBuilder &builder, mlir::Location loc, Fortran::lower::AbstractConverter &converter, Fortran::lower::StatementContext &stmtCtx, const std::vector< Fortran::evaluate::Subscript > &subscripts, std::stringstream &asFortran, fir::ExtendedValue &dataExv, bool dataExvIsAssumedSize, fir::factory::AddrAndBoundsInfo &info, bool treatIndexAsSection=false, bool strideIncludeLowerExtent=false)
Definition DirectivesCommon.h:156
void createEmptyRegionBlocks(fir::FirOpBuilder &builder, std::list< Fortran::lower::pft::Evaluation > &evaluationList)
Definition DirectivesCommon.h:52
Definition bit-population-count.h:20
llvm::SmallVector< mlir::Value > genBaseBoundsOps(fir::FirOpBuilder &builder, mlir::Location loc, fir::ExtendedValue dataExv, bool isAssumedSize, bool strideIncludeLowerExtent=false)
Definition DirectivesCommon.h:278
llvm::SmallVector< mlir::Value > genBoundsOpsFromBox(fir::FirOpBuilder &builder, mlir::Location loc, fir::ExtendedValue dataExv, AddrAndBoundsInfo &info)
Generate the bounds operation from the descriptor information.
Definition DirectivesCommon.h:211
mlir::Value readLowerBound(fir::FirOpBuilder &builder, mlir::Location loc, const fir::ExtendedValue &box, unsigned dim, mlir::Value defaultValue)
Definition FIRBuilder.cpp:1008
mlir::Value readExtent(fir::FirOpBuilder &builder, mlir::Location loc, const fir::ExtendedValue &box, unsigned dim)
Read or get the extent in dimension dim of the array described by box.
Definition FIRBuilder.cpp:977
bool isBoxAddress(mlir::Type t)
Is t an address to fir.box or class type?
Definition FIRType.h:506
mlir::Value getBase(const ExtendedValue &exv)
Definition BoxValue.cpp:21
bool isPointerType(mlir::Type ty)
Definition FIRType.cpp:305
bool isAllocatableType(mlir::Type ty)
Return true iff ty is the type of an ALLOCATABLE entity or value.
Definition FIRType.cpp:313
Definition expression.h:211
Definition variable.h:284
Definition type.h:399
Definition DirectivesCommon.h:103
Definition PFTBuilder.h:221
Definition DirectivesCommon.h:33