FLANG
DirectivesCommon.h
1//===-- 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//===----------------------------------------------------------------------===//
15//===----------------------------------------------------------------------===//
16
17#ifndef FORTRAN_OPTIMIZER_BUILDER_DIRECTIVESCOMMON_H_
18#define FORTRAN_OPTIMIZER_BUILDER_DIRECTIVESCOMMON_H_
19
20#include "BoxValue.h"
21#include "FIRBuilder.h"
22#include "flang/Optimizer/Builder/BoxValue.h"
23#include "flang/Optimizer/Builder/FIRBuilder.h"
24#include "flang/Optimizer/Builder/Todo.h"
25#include "flang/Optimizer/HLFIR/HLFIROps.h"
26#include "mlir/Dialect/OpenACC/OpenACC.h"
27#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
28
29namespace fir::factory {
30
34 explicit AddrAndBoundsInfo() {}
35 explicit AddrAndBoundsInfo(mlir::Value addr, mlir::Value rawInput)
36 : addr(addr), rawInput(rawInput) {}
37 explicit AddrAndBoundsInfo(mlir::Value addr, mlir::Value rawInput,
38 mlir::Value isPresent)
39 : addr(addr), rawInput(rawInput), isPresent(isPresent) {}
40 explicit AddrAndBoundsInfo(mlir::Value addr, mlir::Value rawInput,
41 mlir::Value isPresent, mlir::Type boxType)
42 : addr(addr), rawInput(rawInput), isPresent(isPresent), boxType(boxType) {
43 }
44 mlir::Value addr = nullptr;
45 mlir::Value rawInput = nullptr;
46 mlir::Value isPresent = nullptr;
47 mlir::Type boxType = nullptr;
48 void dump(llvm::raw_ostream &os) {
49 os << "AddrAndBoundsInfo addr: " << addr << "\n";
50 os << "AddrAndBoundsInfo rawInput: " << rawInput << "\n";
51 os << "AddrAndBoundsInfo isPresent: " << isPresent << "\n";
52 os << "AddrAndBoundsInfo boxType: " << boxType << "\n";
53 }
54};
55
56inline AddrAndBoundsInfo getDataOperandBaseAddr(fir::FirOpBuilder &builder,
57 mlir::Value symAddr,
58 bool isOptional,
59 mlir::Location loc,
60 bool unwrapFirBox = true) {
61 mlir::Value rawInput = symAddr;
62 if (auto declareOp =
63 mlir::dyn_cast_or_null<hlfir::DeclareOp>(symAddr.getDefiningOp())) {
64 symAddr = declareOp.getResults()[0];
65 rawInput = declareOp.getResults()[1];
66 }
67
68 if (!symAddr)
69 llvm::report_fatal_error("could not retrieve symbol address");
70
71 mlir::Value isPresent;
72 if (isOptional)
73 isPresent =
74 fir::IsPresentOp::create(builder, loc, builder.getI1Type(), rawInput);
75
76 if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(
77 fir::unwrapRefType(symAddr.getType()))) {
78 // In case of a box reference, load it here to get the box value.
79 // This is preferrable because then the same box value can then be used for
80 // all address/dimension retrievals. For Fortran optional though, leave
81 // the load generation for later so it can be done in the appropriate
82 // if branches.
83 if (unwrapFirBox && mlir::isa<fir::ReferenceType>(symAddr.getType()) &&
84 !isOptional) {
85 mlir::Value addr = fir::LoadOp::create(builder, loc, symAddr);
86 return AddrAndBoundsInfo(addr, rawInput, isPresent, boxTy);
87 }
88
89 return AddrAndBoundsInfo(symAddr, rawInput, isPresent, boxTy);
90 }
91 // For boxchar references, do the same as what is done above for box
92 // references - Load the boxchar so that it is easier to retrieve the length
93 // of the underlying character and the data pointer.
94 if (auto boxCharType = mlir::dyn_cast<fir::BoxCharType>(
95 fir::unwrapRefType((symAddr.getType())))) {
96 if (!isOptional && mlir::isa<fir::ReferenceType>(symAddr.getType())) {
97 mlir::Value boxChar = fir::LoadOp::create(builder, loc, symAddr);
98 return AddrAndBoundsInfo(boxChar, rawInput, isPresent);
99 }
100 }
101 return AddrAndBoundsInfo(symAddr, rawInput, isPresent);
102}
103
104template <typename BoundsOp, typename BoundsType>
106gatherBoundsOrBoundValues(fir::FirOpBuilder &builder, mlir::Location loc,
107 fir::ExtendedValue dataExv, mlir::Value box,
108 bool collectValuesOnly = false) {
109 assert(box && "box must exist");
111 mlir::Value byteStride;
112 mlir::Type idxTy = builder.getIndexType();
113 mlir::Type boundTy = builder.getType<BoundsType>();
114 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
115 for (unsigned dim = 0; dim < dataExv.rank(); ++dim) {
116 mlir::Value d = builder.createIntegerConstant(loc, idxTy, dim);
117 mlir::Value baseLb =
118 fir::factory::readLowerBound(builder, loc, dataExv, dim, one);
119 auto dimInfo =
120 fir::BoxDimsOp::create(builder, loc, idxTy, idxTy, idxTy, box, d);
121 mlir::Value lb = builder.createIntegerConstant(loc, idxTy, 0);
122 mlir::Value ub =
123 mlir::arith::SubIOp::create(builder, loc, dimInfo.getExtent(), one);
124 if (dim == 0) // First stride is the element size.
125 byteStride = dimInfo.getByteStride();
126 if (collectValuesOnly) {
127 values.push_back(lb);
128 values.push_back(ub);
129 values.push_back(dimInfo.getExtent());
130 values.push_back(byteStride);
131 values.push_back(baseLb);
132 } else {
133 mlir::Value bound =
134 BoundsOp::create(builder, loc, boundTy, lb, ub, dimInfo.getExtent(),
135 byteStride, true, baseLb);
136 values.push_back(bound);
137 }
138 // Compute the stride for the next dimension.
139 byteStride = mlir::arith::MulIOp::create(builder, loc, byteStride,
140 dimInfo.getExtent());
141 }
142 return values;
143}
144template <typename BoundsOp, typename BoundsType>
145mlir::Value
146genBoundsOpFromBoxChar(fir::FirOpBuilder &builder, mlir::Location loc,
147 fir::ExtendedValue dataExv, AddrAndBoundsInfo &info) {
148
149 if (!mlir::isa<fir::BoxCharType>(fir::unwrapRefType(info.addr.getType())))
150 return mlir::Value{};
151
152 mlir::Type idxTy = builder.getIndexType();
153 mlir::Type lenType = builder.getCharacterLengthType();
154 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
155 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
156 using ExtentAndStride = std::tuple<mlir::Value, mlir::Value>;
157 auto [extent, stride] = [&]() -> ExtentAndStride {
158 if (info.isPresent) {
159 llvm::SmallVector<mlir::Type> resTypes = {idxTy, idxTy};
160 mlir::Operation::result_range ifRes =
161 builder
162 .genIfOp(loc, resTypes, info.isPresent, /*withElseRegion=*/true)
163 .genThen([&]() {
164 mlir::Value boxChar =
165 fir::isa_ref_type(info.addr.getType())
166 ? fir::LoadOp::create(builder, loc, info.addr)
167 : info.addr;
168 fir::BoxCharType boxCharType =
169 mlir::cast<fir::BoxCharType>(boxChar.getType());
170 mlir::Type refType = builder.getRefType(boxCharType.getEleTy());
171 auto unboxed = fir::UnboxCharOp::create(builder, loc, refType,
172 lenType, boxChar);
173 mlir::SmallVector<mlir::Value> results = {unboxed.getResult(1),
174 one};
175 fir::ResultOp::create(builder, loc, results);
176 })
177 .genElse([&]() {
178 mlir::SmallVector<mlir::Value> results = {zero, zero};
179 fir::ResultOp::create(builder, loc, results);
180 })
181 .getResults();
182 return {ifRes[0], ifRes[1]};
183 }
184 // We have already established that info.addr.getType() is a boxchar
185 // or a boxchar address. If an address, load the boxchar.
186 mlir::Value boxChar = fir::isa_ref_type(info.addr.getType())
187 ? fir::LoadOp::create(builder, loc, info.addr)
188 : info.addr;
189 fir::BoxCharType boxCharType =
190 mlir::cast<fir::BoxCharType>(boxChar.getType());
191 mlir::Type refType = builder.getRefType(boxCharType.getEleTy());
192 auto unboxed =
193 fir::UnboxCharOp::create(builder, loc, refType, lenType, boxChar);
194 return {unboxed.getResult(1), one};
195 }();
196
197 mlir::Value ub = mlir::arith::SubIOp::create(builder, loc, extent, one);
198 mlir::Type boundTy = builder.getType<BoundsType>();
199 return BoundsOp::create(builder, loc, boundTy,
200 /*lower_bound=*/zero,
201 /*upper_bound=*/ub,
202 /*extent=*/extent,
203 /*stride=*/stride,
204 /*stride_in_bytes=*/true,
205 /*start_idx=*/zero);
206}
207
209template <typename BoundsOp, typename BoundsType>
211genBoundsOpsFromBox(fir::FirOpBuilder &builder, mlir::Location loc,
212 fir::ExtendedValue dataExv, AddrAndBoundsInfo &info) {
214 mlir::Type idxTy = builder.getIndexType();
215 mlir::Type boundTy = builder.getType<BoundsType>();
216
217 assert(mlir::isa<fir::BaseBoxType>(info.boxType) &&
218 "expect fir.box or fir.class");
219 assert(fir::unwrapRefType(info.addr.getType()) == info.boxType &&
220 "expected box type consistency");
221
222 if (info.isPresent) {
224 constexpr unsigned nbValuesPerBound = 5;
225 for (unsigned dim = 0; dim < dataExv.rank() * nbValuesPerBound; ++dim)
226 resTypes.push_back(idxTy);
227
228 mlir::Operation::result_range ifRes =
229 builder.genIfOp(loc, resTypes, info.isPresent, /*withElseRegion=*/true)
230 .genThen([&]() {
231 mlir::Value box =
232 !fir::isBoxAddress(info.addr.getType())
233 ? info.addr
234 : fir::LoadOp::create(builder, loc, info.addr);
236 gatherBoundsOrBoundValues<BoundsOp, BoundsType>(
237 builder, loc, dataExv, box,
238 /*collectValuesOnly=*/true);
239 fir::ResultOp::create(builder, loc, boundValues);
240 })
241 .genElse([&] {
242 // Box is not present. Populate bound values with default values.
244 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
245 mlir::Value mOne = builder.createMinusOneInteger(loc, idxTy);
246 for (unsigned dim = 0; dim < dataExv.rank(); ++dim) {
247 boundValues.push_back(zero); // lb
248 boundValues.push_back(mOne); // ub
249 boundValues.push_back(zero); // extent
250 boundValues.push_back(zero); // byteStride
251 boundValues.push_back(zero); // baseLb
252 }
253 fir::ResultOp::create(builder, loc, boundValues);
254 })
255 .getResults();
256 // Create the bound operations outside the if-then-else with the if op
257 // results.
258 for (unsigned i = 0; i < ifRes.size(); i += nbValuesPerBound) {
259 mlir::Value bound =
260 BoundsOp::create(builder, loc, boundTy, ifRes[i], ifRes[i + 1],
261 ifRes[i + 2], ifRes[i + 3], true, ifRes[i + 4]);
262 bounds.push_back(bound);
263 }
264 } else {
265 mlir::Value box = !fir::isBoxAddress(info.addr.getType())
266 ? info.addr
267 : fir::LoadOp::create(builder, loc, info.addr);
268 bounds = gatherBoundsOrBoundValues<BoundsOp, BoundsType>(builder, loc,
269 dataExv, box);
270 }
271 return bounds;
272}
273
276template <typename BoundsOp, typename BoundsType>
278genBaseBoundsOps(fir::FirOpBuilder &builder, mlir::Location loc,
279 fir::ExtendedValue dataExv, bool isAssumedSize,
280 bool strideIncludeLowerExtent = false) {
281 mlir::Type idxTy = builder.getIndexType();
282 mlir::Type boundTy = builder.getType<BoundsType>();
284
285 if (dataExv.rank() == 0)
286 return bounds;
287
288 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
289 const unsigned rank = dataExv.rank();
290 mlir::Value cumulativeExtent = one;
291 for (unsigned dim = 0; dim < rank; ++dim) {
292 mlir::Value baseLb =
293 fir::factory::readLowerBound(builder, loc, dataExv, dim, one);
294 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
295 mlir::Value ub;
296 mlir::Value lb = zero;
297 mlir::Value extent = fir::factory::readExtent(builder, loc, dataExv, dim);
298 if (isAssumedSize && dim + 1 == rank) {
299 extent = zero;
300 ub = lb;
301 } else {
302 // ub = extent - 1
303 ub = mlir::arith::SubIOp::create(builder, loc, extent, one);
304 }
305 mlir::Value stride = one;
306 if (strideIncludeLowerExtent) {
307 stride = cumulativeExtent;
308 cumulativeExtent = builder.createOrFold<mlir::arith::MulIOp>(
309 loc, cumulativeExtent, extent);
310 }
311
312 mlir::Value bound = BoundsOp::create(builder, loc, boundTy, lb, ub, extent,
313 stride, false, baseLb);
314 bounds.push_back(bound);
315 }
316 return bounds;
317}
318
321inline bool isOptionalArgument(mlir::Operation *op) {
322 if (auto declareOp = mlir::dyn_cast_or_null<hlfir::DeclareOp>(op))
323 if (declareOp.getFortranAttrs() &&
324 bitEnumContainsAny(*declareOp.getFortranAttrs(),
325 fir::FortranVariableFlagsEnum::optional))
326 return true;
327 return false;
328}
329
330template <typename BoundsOp, typename BoundsType>
332genImplicitBoundsOps(fir::FirOpBuilder &builder, AddrAndBoundsInfo &info,
333 fir::ExtendedValue dataExv, bool dataExvIsAssumedSize,
334 mlir::Location loc) {
336
337 mlir::Value baseOp = info.rawInput;
338 if (mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(baseOp.getType())))
339 bounds =
340 genBoundsOpsFromBox<BoundsOp, BoundsType>(builder, loc, dataExv, info);
341 if (mlir::isa<fir::SequenceType>(fir::unwrapRefType(baseOp.getType()))) {
342 bounds = genBaseBoundsOps<BoundsOp, BoundsType>(builder, loc, dataExv,
343 dataExvIsAssumedSize);
344 }
345 if (characterWithDynamicLen(fir::unwrapRefType(baseOp.getType())) ||
346 mlir::isa<fir::BoxCharType>(fir::unwrapRefType(info.addr.getType()))) {
347 bounds = {genBoundsOpFromBoxChar<BoundsOp, BoundsType>(builder, loc,
348 dataExv, info)};
349 }
350 return bounds;
351}
352
353} // namespace fir::factory
354#endif // FORTRAN_OPTIMIZER_BUILDER_DIRECTIVESCOMMON_H_
Definition: BoxValue.h:478
Definition: FIRBuilder.h:55
IfBuilder genIfOp(mlir::Location loc, mlir::TypeRange results, mlir::Value cdt, bool withElseRegion)
Definition: FIRBuilder.h:531
mlir::Type getCharacterLengthType()
Get character length type.
Definition: FIRBuilder.h:159
mlir::Type getRefType(mlir::Type eleTy, bool isVolatile=false)
Safely create a reference type to the type eleTy.
Definition: FIRBuilder.cpp:108
mlir::Value createMinusOneInteger(mlir::Location loc, mlir::Type integerType)
Definition: FIRBuilder.h:193
mlir::Value createIntegerConstant(mlir::Location loc, mlir::Type integerType, std::int64_t i)
Definition: FIRBuilder.cpp:144
Definition: OpenACC.h:20
Definition: BoxValue.h:445
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 isOptionalArgument(mlir::Operation *op)
Definition: DirectivesCommon.h:321
bool isa_ref_type(mlir::Type t)
Is t a FIR dialect type that implies a memory (de)reference?
Definition: FIRType.h:118
bool isBoxAddress(mlir::Type t)
Is t an address to fir.box or class type?
Definition: FIRType.h:506
bool characterWithDynamicLen(mlir::Type t)
Returns true iff t is a fir.char type and has an unknown length.
Definition: FIRType.h:238
Definition: DirectivesCommon.h:33