[flang-commits] [flang] [flang] Region-based HLFIR operation for conditional expressions lowering (PR #194411)
via flang-commits
flang-commits at lists.llvm.org
Mon May 4 07:42:25 PDT 2026
================
@@ -857,6 +858,137 @@ struct CharExtremumOpConversion
}
};
+struct ConditionalOpConversion
+ : public mlir::OpConversionPattern<hlfir::ConditionalOp> {
+ using mlir::OpConversionPattern<hlfir::ConditionalOp>::OpConversionPattern;
+ explicit ConditionalOpConversion(mlir::MLIRContext *ctx)
+ : mlir::OpConversionPattern<hlfir::ConditionalOp>{ctx} {
+ // This pattern recursively converts nested ConditionalOp's
+ // by cloning and then converting them, so we have to allow
+ // for recursive pattern application. The recursion is bounded
+ // by the nesting level of ConditionalOp's.
+ setHasBoundedRewriteRecursion();
+ }
+ /// Compute the MLIR type of the temp's DeclareOp base result,
+ /// given the hlfir.expr type of the conditional. This must match
+ /// what createAndDeclareTemp + hlfir::DeclareOp would produce.
+ static mlir::Type computeTempBaseType(fir::FirOpBuilder &builder,
+ hlfir::ExprType exprType) {
+ const mlir::Type eleTy{exprType.getEleTy()};
+ const bool isPolymorphic{exprType.isPolymorphic()};
+ const bool isArray{exprType.isArray()};
+ mlir::Type elemOrSeqType{eleTy};
+ if (isArray)
+ elemOrSeqType = fir::SequenceType::get(exprType.getShape(), eleTy);
+ // Polymorphic: runtime allocate produces fir.class<fir.heap<T>>,
+ // DeclareOp strips the heap attribute → fir.class<T>.
+ if (isPolymorphic)
+ return fir::ClassType::get(elemOrSeqType);
+ // Arrays (non-polymorphic): heap alloc → fir.heap<seqTy>,
+ // DeclareOp wraps in box → fir.box<seqTy>.
+ if (isArray)
+ return fir::BoxType::get(elemOrSeqType);
+ // Scalar, non-polymorphic.
+ if (auto charTy{mlir::dyn_cast<fir::CharacterType>(eleTy)})
+ if (charTy.hasDynamicLen())
+ return fir::BoxCharType::get(builder.getContext(), charTy.getFKind());
+ if (fir::isRecordWithTypeParameters(eleTy))
+ return fir::BoxType::get(eleTy);
+ return fir::ReferenceType::get(eleTy);
+ }
+
+ llvm::LogicalResult
+ matchAndRewrite(hlfir::ConditionalOp condOp, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const override {
+ const mlir::Location loc{condOp->getLoc()};
+ fir::FirOpBuilder builder(rewriter, condOp.getOperation());
+ HLFIRListener listener{builder, rewriter};
+ builder.setListener(&listener);
+ // Use ExprType to ensure both branches produce identical MLIR temp types.
+ const auto exprType{
+ mlir::cast<hlfir::ExprType>(condOp.getResult().getType())};
+ const bool isPolymorphic{exprType.isPolymorphic()};
+ const bool isArray{exprType.isArray()};
+ mlir::Type elemOrSeqType{exprType.getEleTy()};
+ if (isArray)
+ elemOrSeqType =
+ fir::SequenceType::get(exprType.getShape(), elemOrSeqType);
+ const bool useStack{!isArray && !isPolymorphic};
+ const mlir::Type tempBaseType{computeTempBaseType(builder, exprType)};
+ // Callback for hlfir::DeclareOp.
+ auto genTempDeclareOp =
+ [](fir::FirOpBuilder &bldr, mlir::Location l, mlir::Value memref,
+ llvm::StringRef name, mlir::Value shape,
+ llvm::ArrayRef<mlir::Value> typeParams,
+ fir::FortranVariableFlagsAttr attrs) -> mlir::Value {
+ auto declareOp =
+ hlfir::DeclareOp::create(bldr, l, memref, name, shape, typeParams,
+ /*dummy_scope=*/nullptr, /*storage=*/nullptr,
+ /*storage_offset=*/0, attrs);
+ return declareOp.getBase();
+ };
+
+ // Emit one branch: clone ops, create temp, assign, run deferred
+ // destroys, yield (temp, mustFree).
+ auto emitBranch = [&](mlir::Region ®ion) {
+ mlir::IRMapping mapper;
+ // Clone all ops except hlfir.destroy and the terminator.
+ for (auto &op : region.front().without_terminator())
+ if (!mlir::isa<hlfir::DestroyOp>(op))
+ builder.clone(op, mapper);
+ auto yield{mlir::cast<hlfir::YieldOp>(region.front().getTerminator())};
+ // Dereference allocatable/pointer values.
+ const hlfir::Entity val{hlfir::derefPointersAndAllocatables(
+ loc, builder,
+ hlfir::Entity{mapper.lookupOrDefault(yield.getEntity())})};
+ // Obtain runtime length/shape from the actual yielded value.
+ llvm::SmallVector<mlir::Value> lenParams;
+ hlfir::genLengthParameters(loc, builder, val, lenParams);
+ mlir::Value shape{};
+ llvm::SmallVector<mlir::Value> extents;
+ if (isArray) {
+ shape = hlfir::genShape(loc, builder, val);
+ extents = hlfir::getExplicitExtentsFromShape(shape, builder);
+ }
+ // Create temp with common MLIR type but runtime params from the yielded
+ // value.
+ const auto [base, isHeapAlloc]{builder.createAndDeclareTemp(
+ loc, elemOrSeqType, shape, extents, lenParams, genTempDeclareOp,
+ isPolymorphic ? val.getBase() : nullptr, useStack, ".tmp.cond")};
+ const hlfir::Entity temp{base};
----------------
jeanPerier wrote:
Is it possible to use `hlfir::createTempFromMold(loc, builder, yield);` instead, or `copyInTempAndPackage` (I guess the slight issue with both is the result type, but maybe it is best to reuse it and cast/embox/rebox as needed).
https://github.com/llvm/llvm-project/pull/194411
More information about the flang-commits
mailing list