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
27namespace hlfir {
28#define GEN_PASS_DEF_CONVERTHLFIRTOFIR
29#include "flang/Optimizer/HLFIR/Passes.h.inc"
30} // namespace hlfir
31
32using namespace mlir;
33
34static 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
56namespace {
57/// May \p lhs alias with \p rhs?
58/// TODO: implement HLFIR alias analysis.
59class AssignOpConversion : public mlir::OpRewritePattern<hlfir::AssignOp> {
60public:
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
186class CopyInOpConversion : public mlir::OpRewritePattern<hlfir::CopyInOp> {
187public:
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
263class CopyOutOpConversion : public mlir::OpRewritePattern<hlfir::CopyOutOp> {
264public:
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
312class DeclareOpConversion : public mlir::OpRewritePattern<hlfir::DeclareOp> {
313public:
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
420class 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
447public:
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
644private:
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
663class ParentComponentOpConversion
664 : public mlir::OpRewritePattern<hlfir::ParentComponentOp> {
665public:
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
711class NoReassocOpConversion
712 : public mlir::OpRewritePattern<hlfir::NoReassocOp> {
713public:
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
726class NullOpConversion : public mlir::OpRewritePattern<hlfir::NullOp> {
727public:
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
738class GetExtentOpConversion
739 : public mlir::OpRewritePattern<hlfir::GetExtentOp> {
740public:
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
762class ConvertHLFIRtoFIR
763 : public hlfir::impl::ConvertHLFIRtoFIRBase<ConvertHLFIRtoFIR> {
764public:
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
792std::unique_ptr<mlir::Pass> hlfir::createConvertHLFIRtoFIRPass() {
793 return std::make_unique<ConvertHLFIRtoFIR>();
794}
795

source code of flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp