[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 11 07:04:11 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:

This is not exactly what I had in mind. It is fine to limit it to dual use (yield + destroy) for safety as it is the form that should reach the pass, but it should be possible to forward any hlfir.expr impl value as another hlfir.expr implementation (with care only to catch and move the related hlfir.destroy), not only the ones produced by hlfir.as_expr.

For instance, the result of an elemental expression does not have to be copied again.

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


More information about the flang-commits mailing list