[flang-commits] [flang] [flang] Region-based HLFIR operation for conditional expressions lowering (PR #194411)

via flang-commits flang-commits at lists.llvm.org
Mon Apr 27 09:51:57 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: Caroline Newcombe (cenewcombe)

<details>
<summary>Changes</summary>

Introduces `hlfir.conditional`, a region-based HLFIR operation that represents Fortran 2023 conditional expressions (10.1.2.3) with lazy branch evaluation.

## Motivation

The previous lowering emitted `fir.if` directly in ConvertExprToHLFIR with per-category strategies (scalar temp + assign, allocatable temp + realloc, CHARACTER-specific handling). This leaked bufferization concerns into lowering and required four separate code paths.

A naive approach of lowering directly to `fir.if` with the branch values as results doesn't work, because `fir.if` requires both branches to yield results of identical MLIR types. The two branches can produce different runtime representations — for example, a a constant-length CHARACTER variable (`fir.ref<fir.char<1,10>>`) vs. a dynamic-length CHARACTER expression (`fir.ref<fir.char<1,?>>`), or an array with a different descriptor shape. These type mismatches are only resolvable after we know the canonical type for the temporary that will hold the result.

## Approach

`hlfir.conditional` captures each branch in its own region (terminated by `hlfir.yield`), deferring materialization to the bufferization pass. During bufferization, `ConditionalOpConversion`:

- Computes a single canonical temp type from the hlfir.expr result type (computeTempBaseType), ensuring both branches produce an identical MLIR type for the `fir.if` results.
- Clones each region into a `fir.if` branch, creating a temp of that canonical type and assigning the yielded value into it.
- Defers `hlfir.destroy` cloning until after the assign to prevent use-after-free.

Trivial scalar types (INTEGER, REAL, COMPLEX, LOGICAL, UNSIGNED) bypass hlfir.conditional entirely and use fir.if SSA results directly since no temporary is needed.

**AI Usage Disclosure**: AI tools (Claude Sonnet 4.5) were used to assist with implementation of this feature and test code generation. I have reviewed, modified, and tested all AI-generated code.

---

Patch is 35.14 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/194411.diff


5 Files Affected:

- (modified) flang/include/flang/Optimizer/HLFIR/HLFIROps.td (+52-5) 
- (modified) flang/lib/Lower/ConvertExprToHLFIR.cpp (+43-134) 
- (modified) flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp (+35) 
- (modified) flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp (+141-7) 
- (modified) flang/test/Lower/HLFIR/conditional-expr.f90 (+39-76) 


``````````diff
diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
index 69647fe7a2c6d..f05554079816c 100644
--- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
+++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
@@ -1510,11 +1510,14 @@ def hlfir_RegionAssignOp : hlfir_Op<"region_assign", [hlfir_OrderedAssignmentTre
   let hasVerifier = 1;
 }
 
-def hlfir_YieldOp : hlfir_Op<"yield", [Terminator, ParentOneOf<["RegionAssignOp",
-    "ElementalAddrOp", "ForallOp", "ForallMaskOp", "WhereOp", "ElseWhereOp",
-    "ExactlyOnceOp"]>,
-    SingleBlockImplicitTerminator<"fir::FirEndOp">, RecursivelySpeculatable,
-        RecursiveMemoryEffects]> {
+def hlfir_YieldOp
+    : hlfir_Op<"yield", [Terminator,
+                         ParentOneOf<["RegionAssignOp", "ElementalAddrOp",
+                                      "ForallOp", "ForallMaskOp", "WhereOp",
+                                      "ElseWhereOp", "ExactlyOnceOp",
+                                      "ConditionalOp"]>,
+                         SingleBlockImplicitTerminator<"fir::FirEndOp">,
+                         RecursivelySpeculatable, RecursiveMemoryEffects]> {
 
   let summary = "Yield a value or variable inside a forall, where or region assignment";
 
@@ -1988,5 +1991,49 @@ def hlfir_EvaluateInMemoryOp : hlfir_Op<"eval_in_mem", [AttrSizedOperandSegments
   let hasVerifier = 1;
 }
 
+def hlfir_ConditionalOp : hlfir_Op<"conditional", [RecursiveMemoryEffects,
+                                                   MemoryEffects<[MemAlloc]>]> {
+  let summary = "Fortran conditional expression";
+  let description = [{
+    Represent a Fortran conditional expression (F2023 10.1.2.3) that produces a
+    value by lazily evaluating one of two branches based on a condition.
+
+    Only one of the two regions is evaluated at runtime. Each region must
+    be terminated by an hlfir.yield that yields the branch's result value.
+
+    The result is an hlfir.expr. The dynamic type, length type parameters, and
+    shape of the result are determined by the selected branch at runtime (F2023
+    10.1.4.(7)), so they are not operands of this operation.
+
+    Chained conditional expressions are represented by nesting an
+    hlfir.conditional inside the else region.
+
+    Example: ( X ? Y : Z ) where X is logical and Y, Z are real scalars
+    ```
+      %0 = hlfir.conditional %cond : (i1) -> !hlfir.expr<f32> {
+        hlfir.yield %y : f32
+      } else {
+        hlfir.yield %z : f32
+      }
+    ```
+  }];
+
+  let arguments = (ins I1:$condition);
+
+  let results = (outs hlfir_ExprType);
+  let regions = (region SizedRegion<1>:$then_region,
+      SizedRegion<1>:$else_region);
+
+  let assemblyFormat = [{
+    $condition attr-dict `:` functional-type(operands, results)
+    $then_region `else` $else_region
+  }];
+
+  let skipDefaultBuilders = 1;
+  let builders = [OpBuilder<(ins "mlir::Type":$result_type,
+      "mlir::Value":$condition)>];
+
+  let hasVerifier = 1;
+}
 
 #endif // FORTRAN_DIALECT_HLFIR_OPS
diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index a57fce53c0ca5..d9b450d65878f 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -1847,56 +1847,48 @@ class HlfirBuilder {
     llvm_unreachable("unknown descriptor inquiry");
   }
 
-  /// Build nested if-then-else chain by walking the right-skewed
-  /// ConditionalExpr tree. The assignValue callback generates and assigns
-  /// each value to avoid evaluating non-taken branches.
-  template <typename T, typename Callback>
-  void
-  buildConditionalIfChain(const Fortran::evaluate::ConditionalExpr<T> &condExpr,
-                          const Callback &assignValue) {
+  /// Generate a conditional expression as an hlfir.conditional op whose
+  /// regions yield the then/else values. Materialization into memory is
+  /// deferred to the bufferization pass.
+  template <typename T>
+  hlfir::Entity
+  genConditionalOp(const Fortran::evaluate::ConditionalExpr<T> &condExpr,
+                   mlir::Type elementType, bool isPolymorphic) {
     const mlir::Location loc{getLoc()};
     fir::FirOpBuilder &builder{getBuilder()};
+    // Lower the condition to i1.
     getStmtCtx().pushScope();
     const hlfir::EntityWithAttributes condEntity{gen(condExpr.condition())};
     mlir::Value condition{hlfir::loadTrivialScalar(loc, builder, condEntity)};
     condition = builder.createConvert(loc, builder.getI1Type(), condition);
-    builder.genIfOp(loc, {}, condition, /*withElseRegion=*/true)
-        .genThen([&]() {
-          getStmtCtx().pushScope();
-          assignValue(condExpr.thenValue());
-          getStmtCtx().finalizeAndPop();
-        })
-        .genElse([&]() {
-          getStmtCtx().pushScope();
-          assignValue(condExpr.elseValue());
-          getStmtCtx().finalizeAndPop();
-        })
-        .end();
     getStmtCtx().finalizeAndPop();
-  }
-
-  /// Generate scalar conditional with lazy evaluation using assignment.
-  /// Creates a temporary and assigns the selected branch value to it.
-  template <typename T>
-  hlfir::Entity
-  genScalarConditional(const Fortran::evaluate::ConditionalExpr<T> &condExpr,
-                       mlir::Type elementType,
-                       const llvm::SmallVector<mlir::Value, 1> &typeParams) {
-    const mlir::Location loc{getLoc()};
-    fir::FirOpBuilder &builder{getBuilder()};
-    const mlir::Value tempStorage{builder.createTemporary(
-        loc, elementType, ".cond.scalar",
-        /*shape=*/mlir::ValueRange{}, /*typeParams=*/typeParams)};
-    const hlfir::DeclareOp tempDecl{hlfir::DeclareOp::create(
-        builder, loc, tempStorage, ".cond.result",
-        /*shape=*/mlir::Value{}, /*typeParams=*/typeParams)};
-    const hlfir::Entity temp{tempDecl};
-    buildConditionalIfChain(
-        condExpr, [&](const Fortran::evaluate::Expr<T> &expr) {
-          hlfir::Entity entity{gen(expr)};
-          hlfir::AssignOp::create(builder, loc, entity, temp);
-        });
-    return temp;
+    // Build the hlfir.expr result type.
+    const hlfir::ExprType::Shape shape(condExpr.Rank(),
+                                       hlfir::ExprType::getUnknownExtent());
+    const mlir::Type exprType{hlfir::ExprType::get(builder.getContext(), shape,
+                                                   elementType, isPolymorphic)};
+    auto condOp =
+        hlfir::ConditionalOp::create(builder, loc, exprType, condition);
+    // Populate each region inside a scope so that any cleanups
+    // (hlfir.destroy) emitted by gen() stay inside the region, avoiding
+    // dominance violations. The ConditionalOpConversion in bufferization
+    // defers these destroy ops until after the assign into the temp.
+    builder.setInsertionPointToStart(&condOp.getThenRegion().front());
+    getStmtCtx().pushScope();
+    const hlfir::Entity thenEntity{gen(condExpr.thenValue())};
+    getStmtCtx().finalizeAndPop();
+    hlfir::YieldOp::create(builder, loc, thenEntity);
+    builder.setInsertionPointToStart(&condOp.getElseRegion().front());
+    getStmtCtx().pushScope();
+    const hlfir::Entity elseEntity{gen(condExpr.elseValue())};
+    getStmtCtx().finalizeAndPop();
+    hlfir::YieldOp::create(builder, loc, elseEntity);
+    builder.setInsertionPointAfter(condOp);
+    fir::FirOpBuilder *const bldr{&builder};
+    mlir::Value result{condOp.getResult()};
+    getStmtCtx().attachCleanup(
+        [=]() { hlfir::DestroyOp::create(*bldr, loc, result); });
+    return hlfir::Entity{result};
   }
 
   /// Generate scalar conditional for trivial scalar types using fir.if SSA
@@ -1940,110 +1932,27 @@ class HlfirBuilder {
     return hlfir::Entity{results[0]};
   }
 
-  /// Generate conditional expression using an allocatable temporary with lazy
-  /// evaluation. Creates an unallocated allocatable, then uses assignment to
-  /// set the value from the chosen branch (allocation/reallocation handled by
-  /// runtime).
-  template <typename T>
-  hlfir::Entity genAllocatableConditional(
-      const Fortran::evaluate::ConditionalExpr<T> &condExpr,
-      mlir::Type resultType, llvm::StringRef debugName) {
-    const mlir::Location loc{getLoc()};
-    fir::FirOpBuilder &builder{getBuilder()};
-    // Polymorphic types need fir.class (not fir.box) to carry dynamic type
-    // info. Both scalar and array polymorphic types reach here.
-    const bool isPolymorphic{fir::isPolymorphicType(resultType)};
-    const mlir::Type allocType{
-        hlfir::getFortranElementOrSequenceType(resultType)};
-    const mlir::Type heapType{fir::HeapType::get(allocType)};
-    const mlir::Type boxHeapType{isPolymorphic
-                                     ? mlir::Type{fir::ClassType::get(heapType)}
-                                     : mlir::Type{fir::BoxType::get(heapType)}};
-    const mlir::Value tempStorage{
-        builder.createTemporary(loc, boxHeapType, debugName)};
-    const mlir::Value unallocBox{fir::factory::createUnallocatedBox(
-        builder, loc, boxHeapType, /*nonDeferredParams=*/{})};
-    builder.createStoreWithConvert(loc, unallocBox, tempStorage);
-    const hlfir::DeclareOp tempDecl{
-        hlfir::DeclareOp::create(builder, loc, tempStorage, ".cond.result")};
-    const hlfir::Entity temp{tempDecl};
-    // Lazy evaluation: only the selected branch is evaluated and assigned.
-    buildConditionalIfChain(
-        condExpr, [&](const Fortran::evaluate::Expr<T> &expr) {
-          const hlfir::Entity entity{gen(expr)};
-          hlfir::AssignOp::create(builder, loc, entity, temp,
-                                  /*isWholeAllocatableAssignment=*/true,
-                                  /*keepLhsLengthIfRealloc=*/false,
-                                  /*temporary_lhs=*/true);
-        });
-    fir::FirOpBuilder *const bldr{&builder};
-    getStmtCtx().attachCleanup([=]() {
-      fir::factory::genFreememIfAllocated(
-          *bldr, loc,
-          fir::MutableBoxValue{tempStorage, /*lenParams=*/{},
-                               fir::MutableProperties{}});
-    });
-    return temp;
-  }
-
-  /// Generate scalar CHARACTER conditional with proper length handling.
-  template <typename T>
-  std::optional<hlfir::EntityWithAttributes> genCharacterConditional(
-      const Fortran::evaluate::ConditionalExpr<T> &condExpr) {
-    const mlir::Location loc{getLoc()};
-    fir::FirOpBuilder &builder{getBuilder()};
-    const mlir::Type resultType{Fortran::lower::translateSomeExprToFIRType(
-        converter, toEvExpr(condExpr))};
-    const mlir::Type elementType{hlfir::getFortranElementType(resultType)};
-    if (auto charType = mlir::dyn_cast<fir::CharacterType>(elementType)) {
-      if (charType.hasConstantLen()) {
-        llvm::SmallVector<mlir::Value, 1> typeParams;
-        const mlir::Value len{builder.createIntegerConstant(
-            loc, builder.getCharacterLengthType(), charType.getLen())};
-        typeParams.push_back(len);
-        return hlfir::EntityWithAttributes{
-            genScalarConditional(condExpr, elementType, typeParams)};
-      }
-      // Non-constant/varying length: use allocatable conditional to get length
-      // from selected branch.
-      return hlfir::EntityWithAttributes{
-          genAllocatableConditional(condExpr, elementType, ".cond.char")};
-    }
-    return std::nullopt;
-  }
-
   /// Conditional expression (Fortran 2023)
   template <typename T>
   hlfir::EntityWithAttributes
   gen(const Fortran::evaluate::ConditionalExpr<T> &condExpr) {
-    const int rank{condExpr.Rank()};
     mlir::Type resultType{Fortran::lower::translateSomeExprToFIRType(
         converter, toEvExpr(condExpr))};
     if (fir::isRecordWithTypeParameters(
             hlfir::getFortranElementType(resultType)))
       TODO(getLoc(), "conditional expression with length-parameterized "
                      "derived type");
-    // Arrays: handle early to avoid unnecessary type checks.
-    // Per F2023 10.1.4(7), the shape is determined by the chosen branch.
-    if (rank != 0) {
-      return hlfir::EntityWithAttributes{
-          genAllocatableConditional(condExpr, resultType, ".cond.array")};
-    }
-    // CHARACTER scalars require special handling for type parameters.
-    if constexpr (T::category == Fortran::common::TypeCategory::Character) {
-      if (auto result = genCharacterConditional(condExpr))
-        return *result;
-    }
-    // Scalar types (INTEGER, REAL, COMPLEX, LOGICAL, UNSIGNED, Derived).
+    // Trivial scalar types (INTEGER, REAL, COMPLEX, LOGICAL, UNSIGNED)
+    // use fir.if SSA results directly — no temporary needed.
     const mlir::Type elementType{hlfir::getFortranElementType(resultType)};
-    if (fir::isPolymorphicType(resultType))
-      return hlfir::EntityWithAttributes{
-          genAllocatableConditional(condExpr, resultType, ".cond.polymorphic")};
-    if (fir::isa_trivial(elementType))
+    if (condExpr.Rank() == 0 && !fir::isPolymorphicType(resultType) &&
+        fir::isa_trivial(elementType))
       return hlfir::EntityWithAttributes{
           genTrivialScalarConditional(condExpr, elementType)};
-    return hlfir::EntityWithAttributes{
-        genScalarConditional(condExpr, elementType, {})};
+    // All other cases: arrays, CHARACTER, polymorphic, non-trivial derived.
+    // Emit hlfir.conditional to delay materialization to bufferization.
+    return hlfir::EntityWithAttributes{genConditionalOp(
+        condExpr, elementType, fir::isPolymorphicType(resultType))};
   }
 
   hlfir::EntityWithAttributes
diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index e42c064794176..3230066f2305f 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -2461,6 +2461,41 @@ llvm::LogicalResult hlfir::EvaluateInMemoryOp::verify() {
   return mlir::success();
 }
 
+//===----------------------------------------------------------------------===//
+// ConditionalOp
+//===----------------------------------------------------------------------===//
+
+void hlfir::ConditionalOp::build(mlir::OpBuilder &builder,
+                                 mlir::OperationState &odsState,
+                                 mlir::Type resultType, mlir::Value condition) {
+  odsState.addTypes(resultType);
+  odsState.addOperands(condition);
+  // Create the then and else regions, each with one empty block.
+  odsState.addRegion()->push_back(new mlir::Block{});
+  odsState.addRegion()->push_back(new mlir::Block{});
+}
+
+llvm::LogicalResult hlfir::ConditionalOp::verify() {
+  if (!mlir::isa<hlfir::ExprType>(getResult().getType()))
+    return emitOpError("result must be an hlfir.expr type");
+  const auto checkRegion = [&](mlir::Region &region,
+                               llvm::StringRef name) -> llvm::LogicalResult {
+    if (region.empty())
+      return emitOpError(name) << " region must not be empty";
+    if (!region.hasOneBlock())
+      return emitOpError(name) << " region must have exactly one block";
+    if (!mlir::isa_and_nonnull<hlfir::YieldOp>(getTerminator(region)))
+      return emitOpError(name)
+             << " region must be terminated by an hlfir.yield";
+    return mlir::success();
+  };
+  if (const auto res = checkRegion(getThenRegion(), "then"); failed(res))
+    return res;
+  if (const auto res = checkRegion(getElseRegion(), "else"); failed(res))
+    return res;
+  return mlir::success();
+}
+
 #include "flang/Optimizer/HLFIR/HLFIROpInterfaces.cpp.inc"
 #define GET_OP_CLASSES
 #include "flang/Optimizer/HLFIR/HLFIREnums.cpp.inc"
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp
index 35cbdd59cf5d8..24e4099ec98b7 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp
@@ -29,6 +29,7 @@
 #include "flang/Optimizer/OpenMP/Passes.h"
 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
 #include "mlir/IR/Dominance.h"
+#include "mlir/IR/IRMapping.h"
 #include "mlir/IR/PatternMatch.h"
 #include "mlir/Pass/Pass.h"
 #include "mlir/Pass/PassManager.h"
@@ -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())};
+      // Dereference allocatable/pointer values.
+      const hlfir::Entity val{hlfir::derefPointersAndAllocatables(
+          loc, builder,
+          hlfir::Entity{mapper.lookupOrDefault(yield.get...
[truncated]

``````````

</details>


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


More information about the flang-commits mailing list