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