[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:23 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 &region) {
+      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())};
----------------
jeanPerier wrote:

when the yield is itself an hlfir.expr<> that was produced inside the region, you could just forwards its storage and must free as the conditional expr results to avoid the extra temp (you may want to only do it if the `hlfir.expr<>` type matches to start with to avoid issues with different type, especially when the result is polymorphic but some branch is not).

https://github.com/llvm/llvm-project/pull/194411


More information about the flang-commits mailing list