1 | //===- ConvertToFIR.cpp - Convert HLFIR to FIR ----------------------------===// |
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 | // This file defines a pass to lower HLFIR to FIR |
9 | //===----------------------------------------------------------------------===// |
10 | |
11 | #include "flang/Optimizer/Builder/Character.h" |
12 | #include "flang/Optimizer/Builder/FIRBuilder.h" |
13 | #include "flang/Optimizer/Builder/HLFIRTools.h" |
14 | #include "flang/Optimizer/Builder/MutableBox.h" |
15 | #include "flang/Optimizer/Builder/Runtime/Assign.h" |
16 | #include "flang/Optimizer/Builder/Runtime/Derived.h" |
17 | #include "flang/Optimizer/Builder/Runtime/Inquiry.h" |
18 | #include "flang/Optimizer/Builder/Todo.h" |
19 | #include "flang/Optimizer/Dialect/FIRDialect.h" |
20 | #include "flang/Optimizer/Dialect/FIROps.h" |
21 | #include "flang/Optimizer/Dialect/FIRType.h" |
22 | #include "flang/Optimizer/Dialect/Support/FIRContext.h" |
23 | #include "flang/Optimizer/HLFIR/HLFIROps.h" |
24 | #include "flang/Optimizer/HLFIR/Passes.h" |
25 | #include "mlir/Transforms/DialectConversion.h" |
26 | |
27 | namespace hlfir { |
28 | #define GEN_PASS_DEF_CONVERTHLFIRTOFIR |
29 | #include "flang/Optimizer/HLFIR/Passes.h.inc" |
30 | } // namespace hlfir |
31 | |
32 | using namespace mlir; |
33 | |
34 | static mlir::Value genAllocatableTempFromSourceBox(mlir::Location loc, |
35 | fir::FirOpBuilder &builder, |
36 | mlir::Value sourceBox) { |
37 | assert(sourceBox.getType().isa<fir::BaseBoxType>() && |
38 | "must be a base box type" ); |
39 | // Use the runtime to make a quick and dirty temp with the rhs value. |
40 | // Overkill for scalar rhs that could be done in much more clever ways. |
41 | // Note that temp descriptor must have the allocatable flag set so that |
42 | // the runtime will allocate it with the shape and type parameters of |
43 | // the RHS. |
44 | // This has the huge benefit of dealing with all cases, including |
45 | // polymorphic entities. |
46 | mlir::Type fromHeapType = fir::HeapType::get(fir::unwrapRefType( |
47 | sourceBox.getType().cast<fir::BaseBoxType>().getEleTy())); |
48 | mlir::Type fromBoxHeapType = fir::BoxType::get(fromHeapType); |
49 | mlir::Value fromMutableBox = |
50 | fir::factory::genNullBoxStorage(builder, loc, fromBoxHeapType); |
51 | fir::runtime::genAssignTemporary(builder, loc, fromMutableBox, sourceBox); |
52 | mlir::Value copy = builder.create<fir::LoadOp>(loc, fromMutableBox); |
53 | return copy; |
54 | } |
55 | |
56 | namespace { |
57 | /// May \p lhs alias with \p rhs? |
58 | /// TODO: implement HLFIR alias analysis. |
59 | class AssignOpConversion : public mlir::OpRewritePattern<hlfir::AssignOp> { |
60 | public: |
61 | explicit AssignOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {} |
62 | |
63 | mlir::LogicalResult |
64 | matchAndRewrite(hlfir::AssignOp assignOp, |
65 | mlir::PatternRewriter &rewriter) const override { |
66 | mlir::Location loc = assignOp->getLoc(); |
67 | hlfir::Entity lhs(assignOp.getLhs()); |
68 | hlfir::Entity rhs(assignOp.getRhs()); |
69 | auto module = assignOp->getParentOfType<mlir::ModuleOp>(); |
70 | fir::FirOpBuilder builder(rewriter, module); |
71 | |
72 | if (rhs.getType().isa<hlfir::ExprType>()) { |
73 | mlir::emitError(loc, "hlfir must be bufferized with --bufferize-hlfir " |
74 | "pass before being converted to FIR" ); |
75 | return mlir::failure(); |
76 | } |
77 | auto [rhsExv, rhsCleanUp] = |
78 | hlfir::translateToExtendedValue(loc, builder, rhs); |
79 | auto [lhsExv, lhsCleanUp] = |
80 | hlfir::translateToExtendedValue(loc, builder, lhs); |
81 | assert(!lhsCleanUp && !rhsCleanUp && |
82 | "variable to fir::ExtendedValue must not require cleanup" ); |
83 | |
84 | auto emboxRHS = [&](fir::ExtendedValue &rhsExv) -> mlir::Value { |
85 | // There may be overlap between lhs and rhs. The runtime is able to detect |
86 | // and to make a copy of the rhs before modifying the lhs if needed. |
87 | // The code below relies on this and does not do any compile time alias |
88 | // analysis. |
89 | const bool rhsIsValue = fir::isa_trivial(fir::getBase(rhsExv).getType()); |
90 | if (rhsIsValue) { |
91 | // createBox can only be called for fir::ExtendedValue that are |
92 | // already in memory. Place the integer/real/complex/logical scalar |
93 | // in memory. |
94 | // The RHS might be i1, which is not supported for emboxing. |
95 | // If LHS is not polymorphic, we may cast the RHS to the LHS type |
96 | // before emboxing. If LHS is polymorphic we have to figure out |
97 | // the data type for RHS emboxing anyway. |
98 | // It is probably a good idea to make sure that the data type |
99 | // of the RHS is always a valid Fortran storage data type. |
100 | // For the time being, just handle i1 explicitly here. |
101 | mlir::Type rhsType = rhs.getFortranElementType(); |
102 | mlir::Value rhsVal = fir::getBase(rhsExv); |
103 | if (rhsType == builder.getI1Type()) { |
104 | rhsType = fir::LogicalType::get(builder.getContext(), 4); |
105 | rhsVal = builder.createConvert(loc, rhsType, rhsVal); |
106 | } |
107 | mlir::Value temp = builder.create<fir::AllocaOp>(loc, rhsType); |
108 | builder.create<fir::StoreOp>(loc, rhsVal, temp); |
109 | rhsExv = temp; |
110 | } |
111 | return fir::getBase(builder.createBox(loc, rhsExv)); |
112 | }; |
113 | |
114 | if (assignOp.isAllocatableAssignment()) { |
115 | // Whole allocatable assignment: use the runtime to deal with the |
116 | // reallocation. |
117 | mlir::Value from = emboxRHS(rhsExv); |
118 | mlir::Value to = fir::getBase(lhsExv); |
119 | if (assignOp.mustKeepLhsLengthInAllocatableAssignment()) { |
120 | // Indicate the runtime that it should not reallocate in case of length |
121 | // mismatch, and that it should use the LHS explicit/assumed length if |
122 | // allocating/reallocation the LHS. |
123 | // Note that AssignExplicitLengthCharacter() must be used |
124 | // when isTemporaryLHS() is true here: the LHS is known to be |
125 | // character allocatable in this case, so finalization will not |
126 | // happen (as implied by temporary_lhs attribute), and LHS |
127 | // must keep its length (as implied by keep_lhs_length_if_realloc). |
128 | fir::runtime::genAssignExplicitLengthCharacter(builder, loc, to, from); |
129 | } else if (assignOp.isTemporaryLHS()) { |
130 | // Use AssignTemporary, when the LHS is a compiler generated temporary. |
131 | // Note that it also works properly for polymorphic LHS (i.e. the LHS |
132 | // will have the RHS dynamic type after the assignment). |
133 | fir::runtime::genAssignTemporary(builder, loc, to, from); |
134 | } else if (lhs.isPolymorphic()) { |
135 | // Indicate the runtime that the LHS must have the RHS dynamic type |
136 | // after the assignment. |
137 | fir::runtime::genAssignPolymorphic(builder, loc, to, from); |
138 | } else { |
139 | fir::runtime::genAssign(builder, loc, to, from); |
140 | } |
141 | } else if (lhs.isArray() || |
142 | // Special case for element-by-element (or scalar) assignments |
143 | // generated for creating polymorphic expressions. |
144 | // The LHS of these assignments is a box describing just |
145 | // a single element, not the whole allocatable temp. |
146 | // They do not have 'realloc' attribute, because reallocation |
147 | // must not happen. The only expected effect of such an |
148 | // assignment is the copy of the contents, because the dynamic |
149 | // types of the LHS and the RHS must match already. We use the |
150 | // runtime in this case so that the polymorphic (including |
151 | // unlimited) content is copied properly. |
152 | (lhs.isPolymorphic() && assignOp.isTemporaryLHS())) { |
153 | // Use the runtime for simplicity. An optimization pass will be added to |
154 | // inline array assignment when profitable. |
155 | mlir::Value from = emboxRHS(rhsExv); |
156 | mlir::Value to = fir::getBase(builder.createBox(loc, lhsExv)); |
157 | // This is not a whole allocatable assignment: the runtime will not |
158 | // reallocate and modify "toMutableBox" even if it is taking it by |
159 | // reference. |
160 | auto toMutableBox = builder.createTemporary(loc, to.getType()); |
161 | builder.create<fir::StoreOp>(loc, to, toMutableBox); |
162 | if (assignOp.isTemporaryLHS()) |
163 | fir::runtime::genAssignTemporary(builder, loc, toMutableBox, from); |
164 | else |
165 | fir::runtime::genAssign(builder, loc, toMutableBox, from); |
166 | } else { |
167 | // TODO: use the type specification to see if IsFinalizable is set, |
168 | // or propagate IsFinalizable attribute from lowering. |
169 | bool needFinalization = |
170 | !assignOp.isTemporaryLHS() && |
171 | mlir::isa<fir::RecordType>(fir::getElementTypeOf(lhsExv)); |
172 | |
173 | // genScalarAssignment() must take care of potential overlap |
174 | // between LHS and RHS. Note that the overlap is possible |
175 | // also for components of LHS/RHS, and the Assign() runtime |
176 | // must take care of it. |
177 | fir::factory::genScalarAssignment(builder, loc, lhsExv, rhsExv, |
178 | needFinalization, |
179 | assignOp.isTemporaryLHS()); |
180 | } |
181 | rewriter.eraseOp(assignOp); |
182 | return mlir::success(); |
183 | } |
184 | }; |
185 | |
186 | class CopyInOpConversion : public mlir::OpRewritePattern<hlfir::CopyInOp> { |
187 | public: |
188 | explicit CopyInOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {} |
189 | |
190 | struct CopyInResult { |
191 | mlir::Value addr; |
192 | mlir::Value wasCopied; |
193 | }; |
194 | |
195 | static CopyInResult genNonOptionalCopyIn(mlir::Location loc, |
196 | fir::FirOpBuilder &builder, |
197 | hlfir::CopyInOp copyInOp) { |
198 | mlir::Value inputVariable = copyInOp.getVar(); |
199 | mlir::Type resultAddrType = copyInOp.getCopiedIn().getType(); |
200 | mlir::Value isContiguous = |
201 | fir::runtime::genIsContiguous(builder, loc, inputVariable); |
202 | mlir::Value addr = |
203 | builder |
204 | .genIfOp(loc, {resultAddrType}, isContiguous, |
205 | /*withElseRegion=*/true) |
206 | .genThen( |
207 | [&]() { builder.create<fir::ResultOp>(loc, inputVariable); }) |
208 | .genElse([&] { |
209 | // Create temporary on the heap. Note that the runtime is used and |
210 | // that is desired: since the data copy happens under a runtime |
211 | // check (for IsContiguous) the copy loops can hardly provide any |
212 | // value to optimizations, instead, the optimizer just wastes |
213 | // compilation time on these loops. |
214 | mlir::Value temp = |
215 | genAllocatableTempFromSourceBox(loc, builder, inputVariable); |
216 | // Get rid of allocatable flag in the fir.box. |
217 | temp = builder.create<fir::ReboxOp>(loc, resultAddrType, temp, |
218 | /*shape=*/mlir::Value{}, |
219 | /*slice=*/mlir::Value{}); |
220 | builder.create<fir::ResultOp>(loc, temp); |
221 | }) |
222 | .getResults()[0]; |
223 | return {addr, builder.genNot(loc, isContiguous)}; |
224 | } |
225 | |
226 | static CopyInResult genOptionalCopyIn(mlir::Location loc, |
227 | fir::FirOpBuilder &builder, |
228 | hlfir::CopyInOp copyInOp) { |
229 | mlir::Type resultAddrType = copyInOp.getCopiedIn().getType(); |
230 | mlir::Value isPresent = copyInOp.getVarIsPresent(); |
231 | auto res = |
232 | builder |
233 | .genIfOp(loc, {resultAddrType, builder.getI1Type()}, isPresent, |
234 | /*withElseRegion=*/true) |
235 | .genThen([&]() { |
236 | CopyInResult res = genNonOptionalCopyIn(loc, builder, copyInOp); |
237 | builder.create<fir::ResultOp>( |
238 | loc, mlir::ValueRange{res.addr, res.wasCopied}); |
239 | }) |
240 | .genElse([&] { |
241 | mlir::Value absent = |
242 | builder.create<fir::AbsentOp>(loc, resultAddrType); |
243 | builder.create<fir::ResultOp>( |
244 | loc, mlir::ValueRange{absent, isPresent}); |
245 | }) |
246 | .getResults(); |
247 | return {res[0], res[1]}; |
248 | } |
249 | |
250 | mlir::LogicalResult |
251 | matchAndRewrite(hlfir::CopyInOp copyInOp, |
252 | mlir::PatternRewriter &rewriter) const override { |
253 | mlir::Location loc = copyInOp.getLoc(); |
254 | fir::FirOpBuilder builder(rewriter, copyInOp.getOperation()); |
255 | CopyInResult result = copyInOp.getVarIsPresent() |
256 | ? genOptionalCopyIn(loc, builder, copyInOp) |
257 | : genNonOptionalCopyIn(loc, builder, copyInOp); |
258 | rewriter.replaceOp(copyInOp, {result.addr, result.wasCopied}); |
259 | return mlir::success(); |
260 | } |
261 | }; |
262 | |
263 | class CopyOutOpConversion : public mlir::OpRewritePattern<hlfir::CopyOutOp> { |
264 | public: |
265 | explicit CopyOutOpConversion(mlir::MLIRContext *ctx) |
266 | : OpRewritePattern{ctx} {} |
267 | |
268 | mlir::LogicalResult |
269 | matchAndRewrite(hlfir::CopyOutOp copyOutOp, |
270 | mlir::PatternRewriter &rewriter) const override { |
271 | mlir::Location loc = copyOutOp.getLoc(); |
272 | fir::FirOpBuilder builder(rewriter, copyOutOp.getOperation()); |
273 | |
274 | builder.genIfThen(loc, copyOutOp.getWasCopied()) |
275 | .genThen([&]() { |
276 | mlir::Value temp = copyOutOp.getTemp(); |
277 | if (mlir::Value var = copyOutOp.getVar()) { |
278 | auto mutableBoxTo = builder.createTemporary(loc, var.getType()); |
279 | builder.create<fir::StoreOp>(loc, var, mutableBoxTo); |
280 | // Generate CopyOutAssign() call to copy data from the temporary |
281 | // to the actualArg. Note that in case the actual argument |
282 | // is ALLOCATABLE/POINTER the CopyOutAssign() implementation |
283 | // should not engage its reallocation, because the temporary |
284 | // is rank, shape and type compatible with it. |
285 | // Moreover, CopyOutAssign() guarantees that there will be no |
286 | // finalization for the LHS even if it is of a derived type |
287 | // with finalization. |
288 | fir::runtime::genCopyOutAssign(builder, loc, mutableBoxTo, temp, |
289 | /*skipToInit=*/true); |
290 | } |
291 | // Destroy components of the temporary (if any). |
292 | fir::runtime::genDerivedTypeDestroyWithoutFinalization(builder, loc, |
293 | temp); |
294 | mlir::Type heapType = |
295 | fir::HeapType::get(fir::dyn_cast_ptrOrBoxEleTy(temp.getType())); |
296 | mlir::Value tempAddr = |
297 | builder.create<fir::BoxAddrOp>(loc, heapType, temp); |
298 | |
299 | // Deallocate the top-level entity of the temporary. |
300 | // |
301 | // Note that this FreeMemOp is coupled with the runtime |
302 | // allocation engaged by the code generated by |
303 | // genAllocatableTempFromSourceBox(). |
304 | builder.create<fir::FreeMemOp>(loc, tempAddr); |
305 | }) |
306 | .end(); |
307 | rewriter.eraseOp(copyOutOp); |
308 | return mlir::success(); |
309 | } |
310 | }; |
311 | |
312 | class DeclareOpConversion : public mlir::OpRewritePattern<hlfir::DeclareOp> { |
313 | public: |
314 | explicit DeclareOpConversion(mlir::MLIRContext *ctx) |
315 | : OpRewritePattern{ctx} {} |
316 | |
317 | mlir::LogicalResult |
318 | matchAndRewrite(hlfir::DeclareOp declareOp, |
319 | mlir::PatternRewriter &rewriter) const override { |
320 | mlir::Location loc = declareOp->getLoc(); |
321 | mlir::Value memref = declareOp.getMemref(); |
322 | fir::FortranVariableFlagsAttr fortranAttrs; |
323 | fir::CUDADataAttributeAttr cudaAttr; |
324 | if (auto attrs = declareOp.getFortranAttrs()) |
325 | fortranAttrs = |
326 | fir::FortranVariableFlagsAttr::get(rewriter.getContext(), *attrs); |
327 | if (auto attr = declareOp.getCudaAttr()) |
328 | cudaAttr = fir::CUDADataAttributeAttr::get(rewriter.getContext(), *attr); |
329 | auto firDeclareOp = rewriter.create<fir::DeclareOp>( |
330 | loc, memref.getType(), memref, declareOp.getShape(), |
331 | declareOp.getTypeparams(), declareOp.getUniqName(), fortranAttrs, |
332 | cudaAttr); |
333 | |
334 | // Propagate other attributes from hlfir.declare to fir.declare. |
335 | // OpenACC's acc.declare is one example. Right now, the propagation |
336 | // is verbatim. |
337 | mlir::NamedAttrList elidedAttrs = |
338 | mlir::NamedAttrList{firDeclareOp->getAttrs()}; |
339 | for (const mlir::NamedAttribute &attr : declareOp->getAttrs()) |
340 | if (!elidedAttrs.get(attr.getName())) |
341 | firDeclareOp->setAttr(attr.getName(), attr.getValue()); |
342 | |
343 | auto firBase = firDeclareOp.getResult(); |
344 | mlir::Value hlfirBase; |
345 | mlir::Type hlfirBaseType = declareOp.getBase().getType(); |
346 | if (hlfirBaseType.isa<fir::BaseBoxType>()) { |
347 | fir::FirOpBuilder builder(rewriter, declareOp.getOperation()); |
348 | // Helper to generate the hlfir fir.box with the local lower bounds and |
349 | // type parameters. |
350 | auto genHlfirBox = [&]() -> mlir::Value { |
351 | if (!firBase.getType().isa<fir::BaseBoxType>()) { |
352 | llvm::SmallVector<mlir::Value> typeParams; |
353 | auto maybeCharType = |
354 | fir::unwrapSequenceType(fir::unwrapPassByRefType(hlfirBaseType)) |
355 | .dyn_cast<fir::CharacterType>(); |
356 | if (!maybeCharType || maybeCharType.hasDynamicLen()) |
357 | typeParams.append(declareOp.getTypeparams().begin(), |
358 | declareOp.getTypeparams().end()); |
359 | return builder.create<fir::EmboxOp>( |
360 | loc, hlfirBaseType, firBase, declareOp.getShape(), |
361 | /*slice=*/mlir::Value{}, typeParams); |
362 | } else { |
363 | // Rebox so that lower bounds are correct. |
364 | return builder.create<fir::ReboxOp>(loc, hlfirBaseType, firBase, |
365 | declareOp.getShape(), |
366 | /*slice=*/mlir::Value{}); |
367 | } |
368 | }; |
369 | if (!mlir::cast<fir::FortranVariableOpInterface>(declareOp.getOperation()) |
370 | .isOptional()) { |
371 | hlfirBase = genHlfirBox(); |
372 | // If the original base is a box too, we could as well |
373 | // use the HLFIR box as the FIR base: otherwise, the two |
374 | // boxes are "alive" at the same time, and the FIR box |
375 | // is used for accessing the base_addr and the HLFIR box |
376 | // is used for accessing the bounds etc. Using the HLFIR box, |
377 | // that holds the same base_addr at this point, makes |
378 | // the representation a little bit more clear. |
379 | if (hlfirBase.getType() == firBase.getType()) |
380 | firBase = hlfirBase; |
381 | } else { |
382 | // Need to conditionally rebox/embox the optional: the input fir.box |
383 | // may be null and the rebox would be illegal. It is also important to |
384 | // preserve the optional aspect: the hlfir fir.box should be null if |
385 | // the entity is absent so that later fir.is_present on the hlfir base |
386 | // are valid. |
387 | mlir::Value isPresent = |
388 | builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), firBase); |
389 | hlfirBase = builder |
390 | .genIfOp(loc, {hlfirBaseType}, isPresent, |
391 | /*withElseRegion=*/true) |
392 | .genThen([&] { |
393 | builder.create<fir::ResultOp>(loc, genHlfirBox()); |
394 | }) |
395 | .genElse([&]() { |
396 | mlir::Value absent = |
397 | builder.create<fir::AbsentOp>(loc, hlfirBaseType); |
398 | builder.create<fir::ResultOp>(loc, absent); |
399 | }) |
400 | .getResults()[0]; |
401 | } |
402 | } else if (hlfirBaseType.isa<fir::BoxCharType>()) { |
403 | assert(declareOp.getTypeparams().size() == 1 && |
404 | "must contain character length" ); |
405 | hlfirBase = rewriter.create<fir::EmboxCharOp>( |
406 | loc, hlfirBaseType, firBase, declareOp.getTypeparams()[0]); |
407 | } else { |
408 | if (hlfirBaseType != firBase.getType()) { |
409 | declareOp.emitOpError() |
410 | << "unhandled HLFIR variable type '" << hlfirBaseType << "'\n" ; |
411 | return mlir::failure(); |
412 | } |
413 | hlfirBase = firBase; |
414 | } |
415 | rewriter.replaceOp(declareOp, {hlfirBase, firBase}); |
416 | return mlir::success(); |
417 | } |
418 | }; |
419 | |
420 | class DesignateOpConversion |
421 | : public mlir::OpRewritePattern<hlfir::DesignateOp> { |
422 | // Helper method to generate the coordinate of the first element |
423 | // of an array section. It is also called for cases of non-section |
424 | // array element addressing. |
425 | static mlir::Value genSubscriptBeginAddr( |
426 | fir::FirOpBuilder &builder, mlir::Location loc, |
427 | hlfir::DesignateOp designate, mlir::Type baseEleTy, mlir::Value base, |
428 | mlir::Value shape, |
429 | const llvm::SmallVector<mlir::Value> &firBaseTypeParameters) { |
430 | assert(!designate.getIndices().empty()); |
431 | llvm::SmallVector<mlir::Value> firstElementIndices; |
432 | auto indices = designate.getIndices(); |
433 | int i = 0; |
434 | for (auto isTriplet : designate.getIsTripletAttr().asArrayRef()) { |
435 | // Coordinate of the first element are the index and triplets lower |
436 | // bounds |
437 | firstElementIndices.push_back(indices[i]); |
438 | i = i + (isTriplet ? 3 : 1); |
439 | } |
440 | mlir::Type arrayCoorType = fir::ReferenceType::get(baseEleTy); |
441 | base = builder.create<fir::ArrayCoorOp>( |
442 | loc, arrayCoorType, base, shape, |
443 | /*slice=*/mlir::Value{}, firstElementIndices, firBaseTypeParameters); |
444 | return base; |
445 | } |
446 | |
447 | public: |
448 | explicit DesignateOpConversion(mlir::MLIRContext *ctx) |
449 | : OpRewritePattern{ctx} {} |
450 | |
451 | mlir::LogicalResult |
452 | matchAndRewrite(hlfir::DesignateOp designate, |
453 | mlir::PatternRewriter &rewriter) const override { |
454 | mlir::Location loc = designate.getLoc(); |
455 | fir::FirOpBuilder builder(rewriter, designate.getOperation()); |
456 | |
457 | hlfir::Entity baseEntity(designate.getMemref()); |
458 | |
459 | if (baseEntity.isMutableBox()) |
460 | TODO(loc, "hlfir::designate load of pointer or allocatable" ); |
461 | |
462 | mlir::Type designateResultType = designate.getResult().getType(); |
463 | llvm::SmallVector<mlir::Value> firBaseTypeParameters; |
464 | auto [base, shape] = hlfir::genVariableFirBaseShapeAndParams( |
465 | loc, builder, baseEntity, firBaseTypeParameters); |
466 | mlir::Type baseEleTy = hlfir::getFortranElementType(base.getType()); |
467 | mlir::Type resultEleTy = hlfir::getFortranElementType(designateResultType); |
468 | |
469 | mlir::Value fieldIndex; |
470 | if (designate.getComponent()) { |
471 | mlir::Type baseRecordType = baseEntity.getFortranElementType(); |
472 | if (fir::isRecordWithTypeParameters(baseRecordType)) |
473 | TODO(loc, "hlfir.designate with a parametrized derived type base" ); |
474 | fieldIndex = builder.create<fir::FieldIndexOp>( |
475 | loc, fir::FieldType::get(builder.getContext()), |
476 | designate.getComponent().value(), baseRecordType, |
477 | /*typeParams=*/mlir::ValueRange{}); |
478 | if (baseEntity.isScalar()) { |
479 | // Component refs of scalar base right away: |
480 | // - scalar%scalar_component [substring|complex_part] or |
481 | // - scalar%static_size_array_comp |
482 | // - scalar%array(indices) [substring| complex part] |
483 | mlir::Type componentType = baseEleTy.cast<fir::RecordType>().getType( |
484 | designate.getComponent().value()); |
485 | mlir::Type coorTy = fir::ReferenceType::get(componentType); |
486 | base = builder.create<fir::CoordinateOp>(loc, coorTy, base, fieldIndex); |
487 | if (componentType.isa<fir::BaseBoxType>()) { |
488 | auto variableInterface = mlir::cast<fir::FortranVariableOpInterface>( |
489 | designate.getOperation()); |
490 | if (variableInterface.isAllocatable() || |
491 | variableInterface.isPointer()) { |
492 | rewriter.replaceOp(designate, base); |
493 | return mlir::success(); |
494 | } |
495 | TODO(loc, |
496 | "addressing parametrized derived type automatic components" ); |
497 | } |
498 | baseEleTy = hlfir::getFortranElementType(componentType); |
499 | shape = designate.getComponentShape(); |
500 | } else { |
501 | // array%component[(indices) substring|complex part] cases. |
502 | // Component ref of array bases are dealt with below in embox/rebox. |
503 | assert(designateResultType.isa<fir::BaseBoxType>()); |
504 | } |
505 | } |
506 | |
507 | if (designateResultType.isa<fir::BaseBoxType>()) { |
508 | // Generate embox or rebox. |
509 | mlir::Type eleTy = fir::unwrapPassByRefType(designateResultType); |
510 | bool isScalarDesignator = !eleTy.isa<fir::SequenceType>(); |
511 | mlir::Value sourceBox; |
512 | if (isScalarDesignator) { |
513 | // The base box will be used for emboxing the scalar element. |
514 | sourceBox = base; |
515 | // Generate the coordinate of the element. |
516 | base = genSubscriptBeginAddr(builder, loc, designate, baseEleTy, base, |
517 | shape, firBaseTypeParameters); |
518 | shape = nullptr; |
519 | // Type information will be taken from the source box, |
520 | // so the type parameters are not needed. |
521 | firBaseTypeParameters.clear(); |
522 | } |
523 | llvm::SmallVector<mlir::Value> triples; |
524 | llvm::SmallVector<mlir::Value> sliceFields; |
525 | mlir::Type idxTy = builder.getIndexType(); |
526 | auto subscripts = designate.getIndices(); |
527 | if (fieldIndex && baseEntity.isArray()) { |
528 | // array%scalar_comp or array%array_comp(indices) |
529 | // Generate triples for array(:, :, ...). |
530 | triples = genFullSliceTriples(builder, loc, baseEntity); |
531 | sliceFields.push_back(fieldIndex); |
532 | // Add indices in the field path for "array%array_comp(indices)" |
533 | // case. The indices of components provided to the sliceOp must |
534 | // be zero based (fir.slice has no knowledge of the component |
535 | // lower bounds). The component lower bounds are applied here. |
536 | if (!subscripts.empty()) { |
537 | llvm::SmallVector<mlir::Value> lbounds = hlfir::genLowerbounds( |
538 | loc, builder, designate.getComponentShape(), subscripts.size()); |
539 | for (auto [i, lb] : llvm::zip(subscripts, lbounds)) { |
540 | mlir::Value iIdx = builder.createConvert(loc, idxTy, i); |
541 | mlir::Value lbIdx = builder.createConvert(loc, idxTy, lb); |
542 | sliceFields.emplace_back( |
543 | builder.create<mlir::arith::SubIOp>(loc, iIdx, lbIdx)); |
544 | } |
545 | } |
546 | } else if (!isScalarDesignator) { |
547 | // Otherwise, this is an array section with triplets. |
548 | auto undef = builder.create<fir::UndefOp>(loc, idxTy); |
549 | unsigned i = 0; |
550 | for (auto isTriplet : designate.getIsTriplet()) { |
551 | triples.push_back(subscripts[i++]); |
552 | if (isTriplet) { |
553 | triples.push_back(subscripts[i++]); |
554 | triples.push_back(subscripts[i++]); |
555 | } else { |
556 | triples.push_back(undef); |
557 | triples.push_back(undef); |
558 | } |
559 | } |
560 | } |
561 | llvm::SmallVector<mlir::Value, 2> substring; |
562 | if (!designate.getSubstring().empty()) { |
563 | substring.push_back(designate.getSubstring()[0]); |
564 | mlir::Type idxTy = builder.getIndexType(); |
565 | // fir.slice op substring expects the zero based lower bound. |
566 | mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); |
567 | substring[0] = builder.createConvert(loc, idxTy, substring[0]); |
568 | substring[0] = |
569 | builder.create<mlir::arith::SubIOp>(loc, substring[0], one); |
570 | substring.push_back(designate.getTypeparams()[0]); |
571 | } |
572 | if (designate.getComplexPart()) { |
573 | if (triples.empty()) |
574 | triples = genFullSliceTriples(builder, loc, baseEntity); |
575 | sliceFields.push_back(builder.createIntegerConstant( |
576 | loc, idxTy, *designate.getComplexPart())); |
577 | } |
578 | mlir::Value slice; |
579 | if (!triples.empty()) |
580 | slice = |
581 | builder.create<fir::SliceOp>(loc, triples, sliceFields, substring); |
582 | else |
583 | assert(sliceFields.empty() && substring.empty()); |
584 | llvm::SmallVector<mlir::Type> resultType{designateResultType}; |
585 | mlir::Value resultBox; |
586 | if (base.getType().isa<fir::BaseBoxType>()) |
587 | resultBox = |
588 | builder.create<fir::ReboxOp>(loc, resultType, base, shape, slice); |
589 | else |
590 | resultBox = |
591 | builder.create<fir::EmboxOp>(loc, resultType, base, shape, slice, |
592 | firBaseTypeParameters, sourceBox); |
593 | rewriter.replaceOp(designate, resultBox); |
594 | return mlir::success(); |
595 | } |
596 | |
597 | // Otherwise, the result is the address of a scalar, or the address of the |
598 | // first element of a contiguous array section with compile time constant |
599 | // shape. The base may be an array, or a scalar. |
600 | mlir::Type resultAddressType = designateResultType; |
601 | if (auto boxCharType = designateResultType.dyn_cast<fir::BoxCharType>()) |
602 | resultAddressType = fir::ReferenceType::get(boxCharType.getEleTy()); |
603 | |
604 | // Array element indexing. |
605 | if (!designate.getIndices().empty()) { |
606 | // - array(indices) [substring|complex_part] or |
607 | // - scalar%array_comp(indices) [substring|complex_part] |
608 | // This may be a ranked contiguous array section in which case |
609 | // The first element address is being computed. |
610 | base = genSubscriptBeginAddr(builder, loc, designate, baseEleTy, base, |
611 | shape, firBaseTypeParameters); |
612 | } |
613 | |
614 | // Scalar substring (potentially on the previously built array element or |
615 | // component reference). |
616 | if (!designate.getSubstring().empty()) |
617 | base = fir::factory::CharacterExprHelper{builder, loc}.genSubstringBase( |
618 | base, designate.getSubstring()[0], resultAddressType); |
619 | |
620 | // Scalar complex part ref |
621 | if (designate.getComplexPart()) { |
622 | // Sequence types should have already been handled by this point |
623 | assert(!designateResultType.isa<fir::SequenceType>()); |
624 | auto index = builder.createIntegerConstant(loc, builder.getIndexType(), |
625 | *designate.getComplexPart()); |
626 | auto coorTy = fir::ReferenceType::get(resultEleTy); |
627 | base = builder.create<fir::CoordinateOp>(loc, coorTy, base, index); |
628 | } |
629 | |
630 | // Cast/embox the computed scalar address if needed. |
631 | if (designateResultType.isa<fir::BoxCharType>()) { |
632 | assert(designate.getTypeparams().size() == 1 && |
633 | "must have character length" ); |
634 | auto emboxChar = builder.create<fir::EmboxCharOp>( |
635 | loc, designateResultType, base, designate.getTypeparams()[0]); |
636 | rewriter.replaceOp(designate, emboxChar.getResult()); |
637 | } else { |
638 | base = builder.createConvert(loc, designateResultType, base); |
639 | rewriter.replaceOp(designate, base); |
640 | } |
641 | return mlir::success(); |
642 | } |
643 | |
644 | private: |
645 | // Generates triple for full slice |
646 | // Used for component and complex part slices when a triple is |
647 | // not specified |
648 | static llvm::SmallVector<mlir::Value> |
649 | genFullSliceTriples(fir::FirOpBuilder &builder, mlir::Location loc, |
650 | hlfir::Entity baseEntity) { |
651 | llvm::SmallVector<mlir::Value> triples; |
652 | mlir::Type idxTy = builder.getIndexType(); |
653 | auto one = builder.createIntegerConstant(loc, idxTy, 1); |
654 | for (auto [lb, ub] : hlfir::genBounds(loc, builder, baseEntity)) { |
655 | triples.push_back(builder.createConvert(loc, idxTy, lb)); |
656 | triples.push_back(builder.createConvert(loc, idxTy, ub)); |
657 | triples.push_back(one); |
658 | } |
659 | return triples; |
660 | } |
661 | }; |
662 | |
663 | class ParentComponentOpConversion |
664 | : public mlir::OpRewritePattern<hlfir::ParentComponentOp> { |
665 | public: |
666 | explicit ParentComponentOpConversion(mlir::MLIRContext *ctx) |
667 | : OpRewritePattern{ctx} {} |
668 | |
669 | mlir::LogicalResult |
670 | matchAndRewrite(hlfir::ParentComponentOp parentComponent, |
671 | mlir::PatternRewriter &rewriter) const override { |
672 | mlir::Location loc = parentComponent.getLoc(); |
673 | mlir::Type resultType = parentComponent.getType(); |
674 | if (!parentComponent.getType().isa<fir::BoxType>()) { |
675 | mlir::Value baseAddr = parentComponent.getMemref(); |
676 | // Scalar parent component ref without any length type parameters. The |
677 | // input may be a fir.class if it is polymorphic, since this is a scalar |
678 | // and the output will be monomorphic, the base address can be extracted |
679 | // from the fir.class. |
680 | if (baseAddr.getType().isa<fir::BaseBoxType>()) |
681 | baseAddr = rewriter.create<fir::BoxAddrOp>(loc, baseAddr); |
682 | rewriter.replaceOpWithNewOp<fir::ConvertOp>(parentComponent, resultType, |
683 | baseAddr); |
684 | return mlir::success(); |
685 | } |
686 | // Array parent component ref or PDTs. |
687 | hlfir::Entity base{parentComponent.getMemref()}; |
688 | mlir::Value baseAddr = base.getBase(); |
689 | if (!baseAddr.getType().isa<fir::BaseBoxType>()) { |
690 | // Embox cannot directly be used to address parent components: it expects |
691 | // the output type to match the input type when there are no slices. When |
692 | // the types have at least one component, a slice to the first element can |
693 | // be built, and the result set to the parent component type. Just create |
694 | // a fir.box with the base for now since this covers all cases. |
695 | mlir::Type baseBoxType = |
696 | fir::BoxType::get(base.getElementOrSequenceType()); |
697 | assert(!base.hasLengthParameters() && |
698 | "base must be a box if it has any type parameters" ); |
699 | baseAddr = rewriter.create<fir::EmboxOp>( |
700 | loc, baseBoxType, baseAddr, parentComponent.getShape(), |
701 | /*slice=*/mlir::Value{}, /*typeParams=*/mlir::ValueRange{}); |
702 | } |
703 | rewriter.replaceOpWithNewOp<fir::ReboxOp>(parentComponent, resultType, |
704 | baseAddr, |
705 | /*shape=*/mlir::Value{}, |
706 | /*slice=*/mlir::Value{}); |
707 | return mlir::success(); |
708 | } |
709 | }; |
710 | |
711 | class NoReassocOpConversion |
712 | : public mlir::OpRewritePattern<hlfir::NoReassocOp> { |
713 | public: |
714 | explicit NoReassocOpConversion(mlir::MLIRContext *ctx) |
715 | : OpRewritePattern{ctx} {} |
716 | |
717 | mlir::LogicalResult |
718 | matchAndRewrite(hlfir::NoReassocOp noreassoc, |
719 | mlir::PatternRewriter &rewriter) const override { |
720 | rewriter.replaceOpWithNewOp<fir::NoReassocOp>(noreassoc, |
721 | noreassoc.getVal()); |
722 | return mlir::success(); |
723 | } |
724 | }; |
725 | |
726 | class NullOpConversion : public mlir::OpRewritePattern<hlfir::NullOp> { |
727 | public: |
728 | explicit NullOpConversion(mlir::MLIRContext *ctx) : OpRewritePattern{ctx} {} |
729 | |
730 | mlir::LogicalResult |
731 | matchAndRewrite(hlfir::NullOp nullop, |
732 | mlir::PatternRewriter &rewriter) const override { |
733 | rewriter.replaceOpWithNewOp<fir::ZeroOp>(nullop, nullop.getType()); |
734 | return mlir::success(); |
735 | } |
736 | }; |
737 | |
738 | class GetExtentOpConversion |
739 | : public mlir::OpRewritePattern<hlfir::GetExtentOp> { |
740 | public: |
741 | using mlir::OpRewritePattern<hlfir::GetExtentOp>::OpRewritePattern; |
742 | |
743 | mlir::LogicalResult |
744 | matchAndRewrite(hlfir::GetExtentOp getExtentOp, |
745 | mlir::PatternRewriter &rewriter) const override { |
746 | mlir::Value shape = getExtentOp.getShape(); |
747 | mlir::Operation *shapeOp = shape.getDefiningOp(); |
748 | // the hlfir.shape_of operation which led to the creation of this get_extent |
749 | // operation should now have been lowered to a fir.shape operation |
750 | if (auto s = mlir::dyn_cast_or_null<fir::ShapeOp>(shapeOp)) { |
751 | fir::ShapeType shapeTy = shape.getType().cast<fir::ShapeType>(); |
752 | llvm::APInt dim = getExtentOp.getDim(); |
753 | uint64_t dimVal = dim.getLimitedValue(shapeTy.getRank()); |
754 | mlir::Value extent = s.getExtents()[dimVal]; |
755 | rewriter.replaceOp(getExtentOp, extent); |
756 | return mlir::success(); |
757 | } |
758 | return mlir::failure(); |
759 | } |
760 | }; |
761 | |
762 | class ConvertHLFIRtoFIR |
763 | : public hlfir::impl::ConvertHLFIRtoFIRBase<ConvertHLFIRtoFIR> { |
764 | public: |
765 | void runOnOperation() override { |
766 | // TODO: like "bufferize-hlfir" pass, runtime signature may be added |
767 | // by this pass. This requires the pass to run on the ModuleOp. It would |
768 | // probably be more optimal to have it run on FuncOp and find a way to |
769 | // generate the signatures in a thread safe way. |
770 | auto module = this->getOperation(); |
771 | auto *context = &getContext(); |
772 | mlir::RewritePatternSet patterns(context); |
773 | patterns.insert<AssignOpConversion, CopyInOpConversion, CopyOutOpConversion, |
774 | DeclareOpConversion, DesignateOpConversion, |
775 | GetExtentOpConversion, NoReassocOpConversion, |
776 | NullOpConversion, ParentComponentOpConversion>(context); |
777 | mlir::ConversionTarget target(*context); |
778 | target.addIllegalDialect<hlfir::hlfirDialect>(); |
779 | target.markUnknownOpDynamicallyLegal( |
780 | [](mlir::Operation *) { return true; }); |
781 | if (mlir::failed(mlir::applyPartialConversion(module, target, |
782 | std::move(patterns)))) { |
783 | mlir::emitError(mlir::UnknownLoc::get(context), |
784 | "failure in HLFIR to FIR conversion pass" ); |
785 | signalPassFailure(); |
786 | } |
787 | } |
788 | }; |
789 | |
790 | } // namespace |
791 | |
792 | std::unique_ptr<mlir::Pass> hlfir::createConvertHLFIRtoFIRPass() { |
793 | return std::make_unique<ConvertHLFIRtoFIR>(); |
794 | } |
795 | |