[flang-commits] [flang] [flang] allow rebox/embox of OPTIONAL (PR #194319)

via flang-commits flang-commits at lists.llvm.org
Tue Apr 28 03:28:26 PDT 2026


https://github.com/jeanPerier updated https://github.com/llvm/llvm-project/pull/194319

>From d6258d4306ef93be1292f415920c76164ec53ced Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Mon, 27 Apr 2026 02:01:16 -0700
Subject: [PATCH 1/3] [flang] allow rebox/embox of OPTIONAL

---
 .../flang/Optimizer/CodeGen/CGPasses.td       |   3 +-
 .../include/flang/Optimizer/Dialect/FIROps.td |  32 +++-
 flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp  | 145 ++++++++++++++----
 flang/lib/Optimizer/Dialect/FIROps.cpp        |  22 ++-
 .../HLFIR/Transforms/ConvertToFIR.cpp         |  53 +++----
 .../Transforms/AssumedRankOpConversion.cpp    |  37 ++++-
 flang/test/Fir/fir-ops.fir                    |  24 +++
 .../test/Fir/rebox-embox-optional-codegen.fir |  89 +++++++++++
 flang/test/Fir/rebox_assumed_rank_codegen.fir |  20 +++
 flang/test/HLFIR/declare-codegen.fir          |  37 ++++-
 10 files changed, 374 insertions(+), 88 deletions(-)
 create mode 100644 flang/test/Fir/rebox-embox-optional-codegen.fir

diff --git a/flang/include/flang/Optimizer/CodeGen/CGPasses.td b/flang/include/flang/Optimizer/CodeGen/CGPasses.td
index 22025c19121f3..5c39de9041bc3 100644
--- a/flang/include/flang/Optimizer/CodeGen/CGPasses.td
+++ b/flang/include/flang/Optimizer/CodeGen/CGPasses.td
@@ -50,7 +50,8 @@ def CodeGenRewrite : Pass<"cg-rewrite", "mlir::ModuleOp"> {
     Fuse specific subgraphs into single Ops for code generation.
   }];
   let dependentDialects = [
-    "fir::FIROpsDialect", "fir::FIRCodeGenDialect"
+    "fir::FIROpsDialect", "fir::FIRCodeGenDialect",
+    "mlir::cf::ControlFlowDialect"
   ];
   let options = [
     Option<"preserveDeclare", "preserve-declare", "bool", /*default=*/"false",
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 1af30684db4f6..453379b010075 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -894,13 +894,16 @@ def fir_EmboxOp : fir_Op<"embox", [NoMemoryEffect, AttrSizedOperandSegments,
           type descriptor or element size to populate the new descriptor.
         - accessMap: unused/experimental.
         - allocator_idx: specify special allocator to use.
+        - optional: indicates that a null pointer `memref` must produce a absent
+          fir.box as opposed to a fir.box containing a nullptr.
   }];
 
   let arguments = (ins AnyReferenceLike:$memref, Optional<AnyShapeType>:$shape,
       Optional<fir_SliceType>:$slice, Variadic<AnyIntegerType>:$typeparams,
       Optional<BoxOrClassType>:$sourceBox,
       OptionalAttr<AffineMapAttr>:$accessMap,
-      OptionalAttr<I32Attr>:$allocator_idx);
+      OptionalAttr<I32Attr>:$allocator_idx,
+      UnitAttr:$optional);
 
   let results = (outs BoxOrClassType);
 
@@ -913,12 +916,13 @@ def fir_EmboxOp : fir_Op<"embox", [NoMemoryEffect, AttrSizedOperandSegments,
       CArg<"mlir::IntegerAttr", "{}">:$allocator_idx),
     [{ return build($_builder, $_state, resultTypes, memref, shape, slice,
                     typeparams, sourceBox, mlir::AffineMapAttr{},
-                    allocator_idx); }]>
+                    allocator_idx, /*optional=*/mlir::UnitAttr{}); }]>
   ];
 
   let assemblyFormat = [{
     $memref (`(` $shape^ `)`)? (`[` $slice^ `]`)? (`typeparams` $typeparams^)?
-      (`source_box` $sourceBox^)? (`map` $accessMap^)? attr-dict `:`
+      (`source_box` $sourceBox^)? (`map` $accessMap^)?
+      (`optional` $optional^)? attr-dict `:`
       functional-type(operands, results)
   }];
 
@@ -976,18 +980,24 @@ def fir_ReboxOp : fir_Op<"rebox", [NoMemoryEffect, AttrSizedOperandSegments,
       %3 = fir.shape %c3, %c4 : (index, index) -> !fir.shape<2>
       %4 = fir.rebox %2(%3) : (!fir.box<!fir.array<?xf32>>, !fir.shape<2>) -> !fir.box<!fir.array<?x?xf32>>
     ```
+
+    When the `optional` unit attribute is set, `$box` may be an absent
+    (null-descriptor) OPTIONAL. In such case, the produced fir.box will be
+    absent when the input one is absent. Without this flag, it is illegal
+    to execute a fir.rebox where the input fir.box is absent.
   }];
 
   let arguments = (ins
     BoxOrClassType:$box,
     Optional<AnyShapeOrShiftType>:$shape,
-    Optional<fir_SliceType>:$slice
+    Optional<fir_SliceType>:$slice,
+    UnitAttr:$optional
   );
 
   let results = (outs BoxOrClassType);
 
   let assemblyFormat = [{
-    $box (`(` $shape^ `)`)? (`[` $slice^ `]`)?
+    $box (`(` $shape^ `)`)? (`[` $slice^ `]`)? (`optional` $optional^)?
         attr-dict `:` functional-type(operands, results)
   }];
 
@@ -1022,18 +1032,24 @@ def fir_ReboxAssumedRankOp : fir_Op<"rebox_assumed_rank",
     Example:
     ```
       fir.rebox_assumed_rank %1 lbs zeroes : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
-    ```    
+    ```
+
+    When the `optional` unit attribute is set, `$box` may be an absent
+    (null-descriptor) OPTIONAL. In such case, the produced fir.box will be
+    absent when the input one is absent. Without this flag, it is illegal
+    to execute a fir.rebox where the input fir.box is absent.
   }];
 
   let arguments = (ins
     AnyRefOrBoxType:$box,
-    fir_LowerBoundModifierAttribute:$lbs_modifier
+    fir_LowerBoundModifierAttribute:$lbs_modifier,
+    UnitAttr:$optional
   );
 
   let results = (outs BoxOrClassType);
 
   let assemblyFormat = [{
-    $box `lbs` $lbs_modifier
+    $box `lbs` $lbs_modifier (`optional` $optional^)?
         attr-dict `:` functional-type(operands, results)
   }];
 
diff --git a/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp b/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
index 3b137d1e54234..d0b9093eaae65 100644
--- a/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
+++ b/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
@@ -18,6 +18,8 @@
 #include "flang/Optimizer/Dialect/FIROps.h"
 #include "flang/Optimizer/Dialect/FIRType.h"
 #include "flang/Optimizer/Dialect/Support/FIRContext.h"
+#include "mlir/Dialect/ControlFlow/IR/ControlFlow.h"
+#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
 #include "mlir/IR/Iterators.h"
 #include "mlir/Transforms/DialectConversion.h"
 #include "llvm/ADT/STLExtras.h"
@@ -55,6 +57,40 @@ static void populateShift(llvm::SmallVectorImpl<mlir::Value> &vec,
   vec.append(shift.getOrigins().begin(), shift.getOrigins().end());
 }
 
+// Helper to emit embox/rebox for OPTIONAL input inside a block
+// guarded by a runtime presence check and to return an absent
+// box when the input is not present.
+template <typename OP>
+static mlir::Value
+emitOptionalBoxGuard(mlir::PatternRewriter &rewriter, OP op,
+                     llvm::function_ref<mlir::Value()> buildPresent) {
+  mlir::Location loc = op.getLoc();
+  mlir::Type boxType = op.getResult().getType();
+  mlir::Value isPresent = fir::IsPresentOp::create(
+      rewriter, loc, rewriter.getI1Type(), op->getOperand(0));
+  mlir::Block *condBlock = op->getBlock();
+  mlir::Block *mergeBlock = rewriter.splitBlock(condBlock, op->getIterator());
+  mergeBlock->addArgument(boxType, loc);
+
+  mlir::Block *thenBlock = rewriter.createBlock(mergeBlock);
+  rewriter.setInsertionPointToStart(thenBlock);
+  mlir::Value present = buildPresent();
+  mlir::cf::BranchOp::create(rewriter, loc, mergeBlock,
+                             mlir::ValueRange{present});
+
+  mlir::Block *elseBlock = rewriter.createBlock(mergeBlock);
+  rewriter.setInsertionPointToStart(elseBlock);
+  mlir::Value absent = fir::AbsentOp::create(rewriter, loc, boxType);
+  mlir::cf::BranchOp::create(rewriter, loc, mergeBlock,
+                             mlir::ValueRange{absent});
+
+  rewriter.setInsertionPointToEnd(condBlock);
+  mlir::cf::CondBranchOp::create(rewriter, loc, isPresent, thenBlock,
+                                 elseBlock);
+  rewriter.setInsertionPointToStart(mergeBlock);
+  return mergeBlock->getArgument(0);
+}
+
 namespace {
 
 /// Convert fir.embox to the extended form where necessary.
@@ -80,24 +116,60 @@ class EmboxConversion : public mlir::OpRewritePattern<fir::EmboxOp> {
 public:
   using OpRewritePattern::OpRewritePattern;
 
-  llvm::LogicalResult
-  matchAndRewrite(fir::EmboxOp embox,
-                  mlir::PatternRewriter &rewriter) const override {
-    // If the embox does not include a shape, then do not convert it
+  enum class RewriteKind { Dynamic, Static, DropOptional };
+
+  static llvm::FailureOr<RewriteKind> getRewriteKind(fir::EmboxOp embox) {
     if (auto shapeVal = embox.getShape())
-      return rewriteDynamicShape(embox, rewriter, shapeVal);
-    if (mlir::isa<fir::ClassType>(embox.getType()))
-      TODO(embox.getLoc(), "embox conversion for fir.class type");
+      return RewriteKind::Dynamic;
     if (auto boxTy = mlir::dyn_cast<fir::BoxType>(embox.getType()))
       if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(boxTy.getEleTy()))
         if (!seqTy.hasDynamicExtents())
-          return rewriteStaticShape(embox, rewriter, seqTy);
-    return mlir::failure();
+          return RewriteKind::Static;
+    if (embox.getOptional())
+      return RewriteKind::DropOptional;
+    return llvm::failure();
   }
 
-  llvm::LogicalResult rewriteStaticShape(fir::EmboxOp embox,
-                                         mlir::PatternRewriter &rewriter,
-                                         fir::SequenceType seqTy) const {
+  llvm::LogicalResult
+  matchAndRewrite(fir::EmboxOp embox,
+                  mlir::PatternRewriter &rewriter) const override {
+    llvm::FailureOr<RewriteKind> rewriteKind = getRewriteKind(embox);
+    if (llvm::failed(rewriteKind))
+      return llvm::failure();
+    if (embox.getOptional()) {
+      mlir::Value newBox = emitOptionalBoxGuard(rewriter, embox, [&] {
+        return matchAndRewriteImpl(embox, rewriter, *rewriteKind)->getResult(0);
+      });
+      rewriter.replaceOp(embox, newBox);
+      return mlir::success();
+    }
+    mlir::Operation *newOp = matchAndRewriteImpl(embox, rewriter, *rewriteKind);
+    rewriter.replaceOp(embox, newOp);
+    return mlir::success();
+  }
+  mlir::Operation *matchAndRewriteImpl(fir::EmboxOp embox,
+                                       mlir::PatternRewriter &rewriter,
+                                       RewriteKind rewriteKind) const {
+    switch (rewriteKind) {
+    case RewriteKind::Dynamic:
+      return rewriteDynamicShape(embox, rewriter, embox.getShape());
+    case RewriteKind::Static:
+      return rewriteStaticShape(embox, rewriter,
+                                fir::unwrapUntilSeqType(embox.getType()));
+    case RewriteKind::DropOptional: {
+      auto newEmbox =
+          llvm::cast<fir::EmboxOp>(rewriter.clone(*embox.getOperation()));
+      newEmbox.setOptional(false);
+      return newEmbox.getOperation();
+    }
+    }
+    llvm_unreachable("all cases covered");
+    return nullptr;
+  }
+
+  mlir::Operation *rewriteStaticShape(fir::EmboxOp embox,
+                                      mlir::PatternRewriter &rewriter,
+                                      fir::SequenceType seqTy) const {
     auto loc = embox.getLoc();
     llvm::SmallVector<mlir::Value> shapeOpers;
     auto idxTy = rewriter.getIndexType();
@@ -113,13 +185,12 @@ class EmboxConversion : public mlir::OpRewritePattern<fir::EmboxOp> {
         mlir::ValueRange{}, embox.getTypeparams(), embox.getSourceBox(),
         embox.getAllocatorIdxAttr());
     LLVM_DEBUG(llvm::dbgs() << "rewriting " << embox << " to " << xbox << '\n');
-    rewriter.replaceOp(embox, xbox.getOperation()->getResults());
-    return mlir::success();
+    return xbox.getOperation();
   }
 
-  llvm::LogicalResult rewriteDynamicShape(fir::EmboxOp embox,
-                                          mlir::PatternRewriter &rewriter,
-                                          mlir::Value shapeVal) const {
+  mlir::Operation *rewriteDynamicShape(fir::EmboxOp embox,
+                                       mlir::PatternRewriter &rewriter,
+                                       mlir::Value shapeVal) const {
     auto loc = embox.getLoc();
     llvm::SmallVector<mlir::Value> shapeOpers;
     llvm::SmallVector<mlir::Value> shiftOpers;
@@ -150,8 +221,7 @@ class EmboxConversion : public mlir::OpRewritePattern<fir::EmboxOp> {
         embox.getTypeparams(), embox.getSourceBox(),
         embox.getAllocatorIdxAttr());
     LLVM_DEBUG(llvm::dbgs() << "rewriting " << embox << " to " << xbox << '\n');
-    rewriter.replaceOp(embox, xbox.getOperation()->getResults());
-    return mlir::success();
+    return xbox.getOperation();
   }
 };
 
@@ -174,20 +244,35 @@ class ReboxConversion : public mlir::OpRewritePattern<fir::ReboxOp> {
   llvm::LogicalResult
   matchAndRewrite(fir::ReboxOp rebox,
                   mlir::PatternRewriter &rewriter) const override {
+    if (rebox.getOptional()) {
+      mlir::Value newBox = emitOptionalBoxGuard(rewriter, rebox, [&] {
+        return matchAndRewriteImpl(rebox, rewriter)->getResult(0);
+      });
+      llvm::errs() << newBox;
+      rewriter.replaceOp(rebox, newBox);
+      return mlir::success();
+    }
+    mlir::Operation *newOp = matchAndRewriteImpl(rebox, rewriter);
+    rewriter.replaceOp(rebox, newOp);
+    return mlir::success();
+  }
+
+  mlir::Operation *matchAndRewriteImpl(fir::ReboxOp rebox,
+                                       mlir::PatternRewriter &rewriter) const {
     auto loc = rebox.getLoc();
     llvm::SmallVector<mlir::Value> shapeOpers;
     llvm::SmallVector<mlir::Value> shiftOpers;
     if (auto shapeVal = rebox.getShape()) {
       if (auto shapeOp = mlir::dyn_cast<fir::ShapeOp>(shapeVal.getDefiningOp()))
         populateShape(shapeOpers, shapeOp);
-      else if (auto shiftOp =
+      else if (auto shapeShiftOp =
                    mlir::dyn_cast<fir::ShapeShiftOp>(shapeVal.getDefiningOp()))
-        populateShapeAndShift(shapeOpers, shiftOpers, shiftOp);
-      else if (auto shiftOp =
-                   mlir::dyn_cast<fir::ShiftOp>(shapeVal.getDefiningOp()))
+        populateShapeAndShift(shapeOpers, shiftOpers, shapeShiftOp);
+      else {
+        auto shiftOp = mlir::dyn_cast<fir::ShiftOp>(shapeVal.getDefiningOp());
+        assert(shiftOp && "unexpected shape operand type");
         populateShift(shiftOpers, shiftOp);
-      else
-        return mlir::failure();
+      }
     }
     llvm::SmallVector<mlir::Value> sliceOpers;
     llvm::SmallVector<mlir::Value> subcompOpers;
@@ -208,8 +293,7 @@ class ReboxConversion : public mlir::OpRewritePattern<fir::ReboxOp> {
         sliceOpers, subcompOpers, substrOpers);
     LLVM_DEBUG(llvm::dbgs()
                << "rewriting " << rebox << " to " << xRebox << '\n');
-    rewriter.replaceOp(rebox, xRebox.getOperation()->getResults());
-    return mlir::success();
+    return xRebox.getOperation();
   }
 };
 
@@ -362,15 +446,14 @@ class CodeGenRewrite : public fir::impl::CodeGenRewriteBase<CodeGenRewrite> {
     auto &context = getContext();
     mlir::ConversionTarget target(context);
     target.addLegalDialect<mlir::arith::ArithDialect, fir::FIROpsDialect,
-                           fir::FIRCodeGenDialect, mlir::func::FuncDialect>();
+                           fir::FIRCodeGenDialect, mlir::func::FuncDialect,
+                           mlir::cf::ControlFlowDialect>();
     target.addIllegalOp<fir::ArrayCoorOp>();
     target.addIllegalOp<fir::ReboxOp>();
     target.addIllegalOp<fir::DeclareOp>();
     target.addIllegalOp<fir::DummyScopeOp>();
     target.addDynamicallyLegalOp<fir::EmboxOp>([](fir::EmboxOp embox) {
-      return !(embox.getShape() ||
-               mlir::isa<fir::SequenceType>(
-                   mlir::cast<fir::BaseBoxType>(embox.getType()).getEleTy()));
+      return llvm::failed(EmboxConversion::getRewriteKind(embox));
     });
     mlir::RewritePatternSet patterns(&context);
     fir::populatePreCGRewritePatterns(patterns, preserveDeclare);
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 4705033945611..307511988eb8d 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -203,8 +203,16 @@ bool fir::mayBeAbsentBox(mlir::Value val) {
 
     // Check for fir.embox and fir.rebox before checking for
     // FortranObjectViewOpInterface, which they support.
-    // A box created by fir.embox/rebox cannot be absent.
-    if (mlir::isa<fir::ReboxOp, fir::EmboxOp, fir::LoadOp>(defOp))
+    // A box created by fir.embox/fir.rebox/fir.rebox_assumed_rank is only
+    // potentially absent when the operation was explicitly tagged with the
+    // `optional` attribute.
+    if (auto reboxOp = mlir::dyn_cast<fir::ReboxOp>(defOp))
+      return reboxOp.getOptional();
+    if (auto emboxOp = mlir::dyn_cast<fir::EmboxOp>(defOp))
+      return emboxOp.getOptional();
+    if (auto reboxAROp = mlir::dyn_cast<fir::ReboxAssumedRankOp>(defOp))
+      return reboxAROp.getOptional();
+    if (mlir::isa<fir::LoadOp>(defOp))
       return false;
 
     if (auto viewIface =
@@ -2490,6 +2498,11 @@ std::optional<std::int64_t> fir::EmboxOp::getViewOffset(mlir::OpResult) {
 }
 
 mlir::Speculation::Speculatability fir::EmboxOp::getSpeculatability() {
+  // The operation is always safe to evaluate if it has the "optional"
+  // attribute, otherwise it is not safe to evaluate if the input may
+  // be absent.
+  if (getOptional())
+    return mlir::Speculation::NotSpeculatable;
   return (getSourceBox() && mayBeAbsentBox(getSourceBox()))
              ? mlir::Speculation::NotSpeculatable
              : mlir::Speculation::Speculatable;
@@ -3801,6 +3814,11 @@ std::optional<std::int64_t> fir::ReboxOp::getViewOffset(mlir::OpResult) {
 }
 
 mlir::Speculation::Speculatability fir::ReboxOp::getSpeculatability() {
+  // The operation is always safe to evaluate if it has the "optional"
+  // attribute, otherwise it is not safe to evaluate if the input may
+  // be absent.
+  if (getOptional())
+    return mlir::Speculation::NotSpeculatable;
   return mayBeAbsentBox(getBox()) ? mlir::Speculation::NotSpeculatable
                                   : mlir::Speculation::Speculatable;
 }
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
index 25ffb8fe6768b..84c6999ec26f5 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
@@ -365,23 +365,30 @@ class DeclareOpConversion : public mlir::OpRewritePattern<hlfir::DeclareOp> {
     if (mlir::isa<fir::BaseBoxType>(hlfirBaseType)) {
       fir::FirOpBuilder builder(rewriter, declareOp.getOperation());
       // Helper to generate the hlfir fir.box with the local lower bounds and
-      // type parameters.
+      // type parameters and OPTIONAL aspect.
+      const bool isOptional =
+          mlir::cast<fir::FortranVariableOpInterface>(declareOp.getOperation())
+              .isOptional();
       auto genHlfirBox = [&]() -> mlir::Value {
         if (auto baseBoxType =
                 mlir::dyn_cast<fir::BaseBoxType>(firBase.getType())) {
           if (declareOp.getSkipRebox())
             return firBase;
           // Rebox so that lower bounds and attributes are correct.
-          if (baseBoxType.isAssumedRank())
+          if (baseBoxType.isAssumedRank()) {
             return fir::ReboxAssumedRankOp::create(
                 builder, loc, hlfirBaseType, firBase,
-                fir::LowerBoundModifierAttribute::SetToOnes);
+                fir::LowerBoundModifierAttribute::SetToOnes, isOptional);
+          }
           if (!fir::extractSequenceType(baseBoxType.getEleTy()) &&
               baseBoxType == hlfirBaseType)
             return firBase;
-          return fir::ReboxOp::create(builder, loc, hlfirBaseType, firBase,
-                                      declareOp.getShape(),
-                                      /*slice=*/mlir::Value{});
+          auto rebox = fir::ReboxOp::create(builder, loc, hlfirBaseType,
+                                            firBase, declareOp.getShape(),
+                                            /*slice=*/mlir::Value{});
+          if (isOptional)
+            rebox.setOptional(true);
+          return rebox.getResult();
         } else {
           llvm::SmallVector<mlir::Value> typeParams;
           auto maybeCharType = mlir::dyn_cast<fir::CharacterType>(
@@ -389,14 +396,16 @@ class DeclareOpConversion : public mlir::OpRewritePattern<hlfir::DeclareOp> {
           if (!maybeCharType || maybeCharType.hasDynamicLen())
             typeParams.append(declareOp.getTypeparams().begin(),
                               declareOp.getTypeparams().end());
-          return fir::EmboxOp::create(builder, loc, hlfirBaseType, firBase,
-                                      declareOp.getShape(),
-                                      /*slice=*/mlir::Value{}, typeParams);
+          auto embox = fir::EmboxOp::create(
+              builder, loc, hlfirBaseType, firBase, declareOp.getShape(),
+              /*slice=*/mlir::Value{}, typeParams);
+          if (isOptional)
+            embox.setOptional(true);
+          return embox.getResult();
         }
       };
-      if (!mlir::cast<fir::FortranVariableOpInterface>(declareOp.getOperation())
-               .isOptional()) {
-        hlfirBase = genHlfirBox();
+      hlfirBase = genHlfirBox();
+      if (!isOptional) {
         // If the original base is a box too, we could as well
         // use the HLFIR box as the FIR base: otherwise, the two
         // boxes are "alive" at the same time, and the FIR box
@@ -406,26 +415,6 @@ class DeclareOpConversion : public mlir::OpRewritePattern<hlfir::DeclareOp> {
         // the representation a little bit more clear.
         if (hlfirBase.getType() == declareOp.getOriginalBase().getType())
           firBase = hlfirBase;
-      } else {
-        // Need to conditionally rebox/embox the optional: the input fir.box
-        // may be null and the rebox would be illegal. It is also important to
-        // preserve the optional aspect: the hlfir fir.box should be null if
-        // the entity is absent so that later fir.is_present on the hlfir base
-        // are valid.
-        mlir::Value isPresent = fir::IsPresentOp::create(
-            builder, loc, builder.getI1Type(), firBase);
-        hlfirBase =
-            builder
-                .genIfOp(loc, {hlfirBaseType}, isPresent,
-                         /*withElseRegion=*/true)
-                .genThen(
-                    [&] { fir::ResultOp::create(builder, loc, genHlfirBox()); })
-                .genElse([&]() {
-                  mlir::Value absent =
-                      fir::AbsentOp::create(builder, loc, hlfirBaseType);
-                  fir::ResultOp::create(builder, loc, absent);
-                })
-                .getResults()[0];
       }
     } else if (mlir::isa<fir::BoxCharType>(hlfirBaseType)) {
       assert(declareOp.getTypeparams().size() == 1 &&
diff --git a/flang/lib/Optimizer/Transforms/AssumedRankOpConversion.cpp b/flang/lib/Optimizer/Transforms/AssumedRankOpConversion.cpp
index 4c7b228eefeb5..781457ad67a4c 100644
--- a/flang/lib/Optimizer/Transforms/AssumedRankOpConversion.cpp
+++ b/flang/lib/Optimizer/Transforms/AssumedRankOpConversion.cpp
@@ -78,7 +78,6 @@ class ReboxAssumedRankConv
     // get modified.
     if (fir::isBoxAddress(rebox.getBox().getType()))
       TODO(loc, "fir.rebox_assumed_rank codegen with fir.ref<fir.box<>> input");
-    mlir::Value tempDesc = builder.createTemporary(loc, newMaxRankBoxType);
     mlir::Value newDtype;
     mlir::Type newEleType = newBoxType.unwrapInnerType();
     auto oldBoxType = mlir::cast<fir::BaseBoxType>(
@@ -99,12 +98,38 @@ class ReboxAssumedRankConv
         static_cast<int>(getLowerBoundModifier(rebox.getLbsModifier()));
     mlir::Value lowerBoundModifier = builder.createIntegerConstant(
         loc, builder.getIntegerType(32), lbsModifierCode);
-    fir::runtime::genCopyAndUpdateDescriptor(builder, loc, tempDesc,
-                                             rebox.getBox(), newDtype,
-                                             newAttribute, lowerBoundModifier);
 
-    mlir::Value descValue = fir::LoadOp::create(builder, loc, tempDesc);
-    mlir::Value castDesc = builder.createConvert(loc, newBoxType, descValue);
+    auto emitCopyAndConvert = [&]() -> mlir::Value {
+      mlir::Value tempDesc = builder.createTemporary(loc, newMaxRankBoxType);
+      fir::runtime::genCopyAndUpdateDescriptor(
+          builder, loc, tempDesc, rebox.getBox(), newDtype, newAttribute,
+          lowerBoundModifier);
+      mlir::Value descValue = fir::LoadOp::create(builder, loc, tempDesc);
+      return builder.createConvert(loc, newBoxType, descValue);
+    };
+
+    mlir::Value castDesc;
+    if (rebox.getOptional()) {
+      // If the input may be an absent OPTIONAL dummy, guard the runtime
+      // call with a presence check and return a fir.absent box otherwise.
+      mlir::Value isPresent = fir::IsPresentOp::create(
+          builder, loc, builder.getI1Type(), rebox.getBox());
+      castDesc =
+          builder
+              .genIfOp(loc, {newBoxType}, isPresent,
+                       /*withElseRegion=*/true)
+              .genThen([&] {
+                fir::ResultOp::create(builder, loc, emitCopyAndConvert());
+              })
+              .genElse([&]() {
+                mlir::Value absent =
+                    fir::AbsentOp::create(builder, loc, newBoxType);
+                fir::ResultOp::create(builder, loc, absent);
+              })
+              .getResults()[0];
+    } else {
+      castDesc = emitCopyAndConvert();
+    }
     rewriter.replaceOp(rebox, castDesc);
     return mlir::success();
   }
diff --git a/flang/test/Fir/fir-ops.fir b/flang/test/Fir/fir-ops.fir
index 79e25d286fb3f..b552f2b60b7f6 100644
--- a/flang/test/Fir/fir-ops.fir
+++ b/flang/test/Fir/fir-ops.fir
@@ -736,6 +736,28 @@ func.func @test_rebox_char(%arg0: !fir.box<!fir.array<?x!fir.char<1,20>>>) {
   return
 }
 
+// CHECK-LABEL: @test_rebox_optional(
+func.func @test_rebox_optional(%arg0: !fir.box<!fir.array<?xf32>>) {
+  %c0 = arith.constant 0 : index
+  %0 = fir.shift %c0 : (index) -> !fir.shift<1>
+  // CHECK: fir.rebox %{{.*}}(%{{.*}}) optional : (!fir.box<!fir.array<?xf32>>, !fir.shift<1>) -> !fir.box<!fir.array<?xf32>>
+  %1 = fir.rebox %arg0(%0) optional : (!fir.box<!fir.array<?xf32>>, !fir.shift<1>) -> !fir.box<!fir.array<?xf32>>
+  // CHECK: fir.rebox %{{.*}} optional : (!fir.box<!fir.array<?xf32>>) -> !fir.box<!fir.array<?xf32>>
+  %2 = fir.rebox %arg0 optional : (!fir.box<!fir.array<?xf32>>) -> !fir.box<!fir.array<?xf32>>
+  return
+}
+
+// CHECK-LABEL: @test_embox_optional(
+func.func @test_embox_optional(%arg0: !fir.ref<!fir.array<?xi32>>, %arg1: !fir.ref<i32>) {
+  %c10 = arith.constant 10 : index
+  %0 = fir.shape %c10 : (index) -> !fir.shape<1>
+  // CHECK: fir.embox %{{.*}}(%{{.*}}) optional : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.box<!fir.array<?xi32>>
+  %1 = fir.embox %arg0(%0) optional : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.box<!fir.array<?xi32>>
+  // CHECK: fir.embox %{{.*}} optional : (!fir.ref<i32>) -> !fir.box<i32>
+  %2 = fir.embox %arg1 optional : (!fir.ref<i32>) -> !fir.box<i32>
+  return
+}
+
 
 func.func private @array_func() -> !fir.array<?x!fir.char<1,?>>
 // CHECK-LABEL: @test_save_result(
@@ -912,6 +934,7 @@ func.func @test_rebox_assumed_rank(%arg0: !fir.box<!fir.array<*:f32>> ) {
   %1 = fir.rebox_assumed_rank %arg0 lbs ones : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
   %2 = fir.rebox_assumed_rank %arg0 lbs zeroes : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
   %3 = fir.rebox_assumed_rank %arg0 lbs preserve : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+  %4 = fir.rebox_assumed_rank %arg0 lbs ones optional : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
   return
 }
 // CHECK-LABEL: func.func @test_rebox_assumed_rank(
@@ -919,6 +942,7 @@ func.func @test_rebox_assumed_rank(%arg0: !fir.box<!fir.array<*:f32>> ) {
   // CHECK: fir.rebox_assumed_rank %[[A]] lbs ones : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
   // CHECK: fir.rebox_assumed_rank %[[A]] lbs zeroes : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
   // CHECK: fir.rebox_assumed_rank %[[A]] lbs preserve : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+  // CHECK: fir.rebox_assumed_rank %[[A]] lbs ones optional : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
 
 func.func @test_is_assumed_size(%arg0: !fir.class<!fir.array<*:none>>, %arg1 : !fir.box<!fir.array<?xf32>>) {
   %1 = fir.is_assumed_size %arg0 : (!fir.class<!fir.array<*:none>>) -> i1
diff --git a/flang/test/Fir/rebox-embox-optional-codegen.fir b/flang/test/Fir/rebox-embox-optional-codegen.fir
new file mode 100644
index 0000000000000..ae22de199ce4b
--- /dev/null
+++ b/flang/test/Fir/rebox-embox-optional-codegen.fir
@@ -0,0 +1,89 @@
+// RUN: fir-opt --split-input-file --pass-pipeline="builtin.module(cg-rewrite)" %s | FileCheck %s
+
+// Test that the PreCGRewrite pass (cg-rewrite) materializes a cf.cond_br
+// diamond around fircg.ext_rebox / fircg.ext_embox when the original
+// fir.rebox / fir.embox carries the `optional` attribute.
+
+// CHECK-LABEL: func.func @test_rebox_optional(
+// CHECK-SAME:      %[[BOX:.*]]: !fir.box<!fir.array<?xf32>>)
+func.func @test_rebox_optional(%arg0: !fir.box<!fir.array<?xf32>>) -> !fir.box<!fir.array<?xf32>> {
+  %c0 = arith.constant 0 : index
+  %0 = fir.shift %c0 : (index) -> !fir.shift<1>
+  %1 = fir.rebox %arg0(%0) optional : (!fir.box<!fir.array<?xf32>>, !fir.shift<1>) -> !fir.box<!fir.array<?xf32>>
+  return %1 : !fir.box<!fir.array<?xf32>>
+}
+// CHECK:   %[[IS_PRESENT:.*]] = fir.is_present %[[BOX]] : (!fir.box<!fir.array<?xf32>>) -> i1
+// CHECK:   cf.cond_br %[[IS_PRESENT]], ^[[THEN:.*]], ^[[ELSE:.*]]
+// CHECK: ^[[THEN]]:
+// CHECK:   %[[EXTREBOX:.*]] = fircg.ext_rebox %[[BOX]] origin %{{.*}} : (!fir.box<!fir.array<?xf32>>, index) -> !fir.box<!fir.array<?xf32>>
+// CHECK:   cf.br ^[[MERGE:.*]](%[[EXTREBOX]] : !fir.box<!fir.array<?xf32>>)
+// CHECK: ^[[ELSE]]:
+// CHECK:   %[[ABSENT:.*]] = fir.absent !fir.box<!fir.array<?xf32>>
+// CHECK:   cf.br ^[[MERGE]](%[[ABSENT]] : !fir.box<!fir.array<?xf32>>)
+// CHECK: ^[[MERGE]](%[[MERGED:.*]]: !fir.box<!fir.array<?xf32>>):
+// CHECK:   return %[[MERGED]] : !fir.box<!fir.array<?xf32>>
+
+// -----
+
+// CHECK-LABEL: func.func @test_embox_optional_dynamic(
+// CHECK-SAME:      %[[REF:.*]]: !fir.ref<!fir.array<?xi32>>)
+func.func @test_embox_optional_dynamic(%arg0: !fir.ref<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>> {
+  %c10 = arith.constant 10 : index
+  %0 = fir.shape %c10 : (index) -> !fir.shape<1>
+  %1 = fir.embox %arg0(%0) optional : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.box<!fir.array<?xi32>>
+  return %1 : !fir.box<!fir.array<?xi32>>
+}
+// CHECK:   %[[IS_PRESENT:.*]] = fir.is_present %[[REF]] : (!fir.ref<!fir.array<?xi32>>) -> i1
+// CHECK:   cf.cond_br %[[IS_PRESENT]], ^[[THEN:.*]], ^[[ELSE:.*]]
+// CHECK: ^[[THEN]]:
+// CHECK:   %[[EXTEMBOX:.*]] = fircg.ext_embox %[[REF]](%{{.*}}) : (!fir.ref<!fir.array<?xi32>>, index) -> !fir.box<!fir.array<?xi32>>
+// CHECK:   cf.br ^[[MERGE:.*]](%[[EXTEMBOX]] : !fir.box<!fir.array<?xi32>>)
+// CHECK: ^[[ELSE]]:
+// CHECK:   %[[ABSENT:.*]] = fir.absent !fir.box<!fir.array<?xi32>>
+// CHECK:   cf.br ^[[MERGE]](%[[ABSENT]] : !fir.box<!fir.array<?xi32>>)
+// CHECK: ^[[MERGE]](%[[MERGED:.*]]: !fir.box<!fir.array<?xi32>>):
+// CHECK:   return %[[MERGED]] : !fir.box<!fir.array<?xi32>>
+
+// -----
+
+// CHECK-LABEL: func.func @test_embox_optional_static(
+// CHECK-SAME:      %[[REF:.*]]: !fir.ref<!fir.array<10xi32>>)
+func.func @test_embox_optional_static(%arg0: !fir.ref<!fir.array<10xi32>>) -> !fir.box<!fir.array<10xi32>> {
+  %1 = fir.embox %arg0 optional : (!fir.ref<!fir.array<10xi32>>) -> !fir.box<!fir.array<10xi32>>
+  return %1 : !fir.box<!fir.array<10xi32>>
+}
+// CHECK:   %[[IS_PRESENT:.*]] = fir.is_present %[[REF]] : (!fir.ref<!fir.array<10xi32>>) -> i1
+// CHECK:   cf.cond_br %[[IS_PRESENT]], ^[[THEN:.*]], ^[[ELSE:.*]]
+// CHECK: ^[[THEN]]:
+// CHECK:   %[[EXTEMBOX:.*]] = fircg.ext_embox %[[REF]](%{{.*}}) : (!fir.ref<!fir.array<10xi32>>, index) -> !fir.box<!fir.array<10xi32>>
+// CHECK:   cf.br ^[[MERGE:.*]](%[[EXTEMBOX]] : !fir.box<!fir.array<10xi32>>)
+// CHECK: ^[[ELSE]]:
+// CHECK:   %[[ABSENT:.*]] = fir.absent !fir.box<!fir.array<10xi32>>
+// CHECK:   cf.br ^[[MERGE]](%[[ABSENT]] : !fir.box<!fir.array<10xi32>>)
+// CHECK: ^[[MERGE]](%[[MERGED:.*]]: !fir.box<!fir.array<10xi32>>):
+// CHECK:   return %[[MERGED]] : !fir.box<!fir.array<10xi32>>
+
+// -----
+
+// An embox that is "kept as is" by the pass (no shape, scalar element type)
+// is also wrapped in a cf.cond_br guard. The cloned embox in the "present"
+// branch has its `optional` attribute dropped so that LLVM codegen sees a
+// plain fir.embox.
+
+// CHECK-LABEL: func.func @test_embox_optional_scalar_keep(
+// CHECK-SAME:      %[[REF:.*]]: !fir.ref<i32>)
+func.func @test_embox_optional_scalar_keep(%arg0: !fir.ref<i32>) -> !fir.box<i32> {
+  %0 = fir.embox %arg0 optional : (!fir.ref<i32>) -> !fir.box<i32>
+  return %0 : !fir.box<i32>
+}
+// CHECK:   %[[IS_PRESENT:.*]] = fir.is_present %[[REF]] : (!fir.ref<i32>) -> i1
+// CHECK:   cf.cond_br %[[IS_PRESENT]], ^[[THEN:.*]], ^[[ELSE:.*]]
+// CHECK: ^[[THEN]]:
+// CHECK:   %[[EMBOX:.*]] = fir.embox %[[REF]] : (!fir.ref<i32>) -> !fir.box<i32>
+// CHECK-NOT: optional
+// CHECK:   cf.br ^[[MERGE:.*]](%[[EMBOX]] : !fir.box<i32>)
+// CHECK: ^[[ELSE]]:
+// CHECK:   %[[ABSENT:.*]] = fir.absent !fir.box<i32>
+// CHECK:   cf.br ^[[MERGE]](%[[ABSENT]] : !fir.box<i32>)
+// CHECK: ^[[MERGE]](%[[MERGED:.*]]: !fir.box<i32>):
+// CHECK:   return %[[MERGED]] : !fir.box<i32>
diff --git a/flang/test/Fir/rebox_assumed_rank_codegen.fir b/flang/test/Fir/rebox_assumed_rank_codegen.fir
index b4336b9279493..aa7cce5fe1843 100644
--- a/flang/test/Fir/rebox_assumed_rank_codegen.fir
+++ b/flang/test/Fir/rebox_assumed_rank_codegen.fir
@@ -41,6 +41,12 @@ func.func @test_poly_to_nonepoly(%arg0: !fir.class<!fir.array<*:!sometype>>) {
   return
 }
 
+func.func @test_optional(%arg0: !fir.box<!fir.array<*:f32>>) {
+  %1 = fir.rebox_assumed_rank %arg0 lbs ones optional : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+  fir.call @somefunc(%1) : (!fir.box<!fir.array<*:f32>>) -> ()
+  return
+}
+
 func.func private @somefunc(!fir.box<!fir.array<*:f32>>)
 func.func private @somefuncalloc(!fir.box<!fir.heap<!fir.array<*:f32>>>)
 func.func private @somefuncpointer(!fir.box<!fir.ptr<!fir.array<*:f32>>>)
@@ -121,4 +127,18 @@ func.func private @takes_assumed_rank_t(!fir.box<!fir.array<*:!sometype>>)
 // CHECK:           %[[VAL_7:.*]] = fir.convert %[[VAL_4]] : (!fir.tdesc<!fir.type<sometype{i:i32}>>) -> !fir.ref<none>
 // CHECK:           fir.call @_FortranACopyAndUpdateDescriptor(%{{.*}}, %{{.*}}, %[[VAL_7]],
 
+// CHECK-LABEL:   func.func @test_optional(
+// CHECK-SAME:                             %[[VAL_0:.*]]: !fir.box<!fir.array<*:f32>>) {
+// CHECK:           %[[IS_PRESENT:.*]] = fir.is_present %[[VAL_0]] : (!fir.box<!fir.array<*:f32>>) -> i1
+// CHECK:           %[[RES:.*]] = fir.if %[[IS_PRESENT]] -> (!fir.box<!fir.array<*:f32>>) {
+// CHECK:             fir.call @_FortranACopyAndUpdateDescriptor(
+// CHECK:             %[[LOADED:.*]] = fir.load
+// CHECK:             %[[CAST:.*]] = fir.convert %[[LOADED]]
+// CHECK:             fir.result %[[CAST]] : !fir.box<!fir.array<*:f32>>
+// CHECK:           } else {
+// CHECK:             %[[ABSENT:.*]] = fir.absent !fir.box<!fir.array<*:f32>>
+// CHECK:             fir.result %[[ABSENT]] : !fir.box<!fir.array<*:f32>>
+// CHECK:           }
+// CHECK:           fir.call @somefunc(%[[RES]]) : (!fir.box<!fir.array<*:f32>>) -> ()
+
 // CHECK:         func.func private @_FortranACopyAndUpdateDescriptor(!fir.ref<!fir.box<none>> {llvm.nocapture}, !fir.box<none> {llvm.nocapture}, !fir.ref<none>, i8, i32) attributes {fir.runtime}
diff --git a/flang/test/HLFIR/declare-codegen.fir b/flang/test/HLFIR/declare-codegen.fir
index 04c2ddcece4a7..eb2160f13e538 100644
--- a/flang/test/HLFIR/declare-codegen.fir
+++ b/flang/test/HLFIR/declare-codegen.fir
@@ -201,14 +201,24 @@ func.func @test_optional_declare(%arg0: !fir.box<!fir.array<?xi32>>) {
 // CHECK:  %[[VAL_1:.*]] = arith.constant 42 : index
 // CHECK:  %[[VAL_2:.*]] = fir.shift %[[VAL_1]] : (index) -> !fir.shift<1>
 // CHECK:  %[[VAL_3:.*]] = fir.declare %[[VAL_0]](%[[VAL_2]]) {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "x"} : (!fir.box<!fir.array<?xi32>>, !fir.shift<1>) -> !fir.box<!fir.array<?xi32>>
-// CHECK:  %[[VAL_4:.*]] = fir.is_present %[[VAL_3]] : (!fir.box<!fir.array<?xi32>>) -> i1
-// CHECK:  %[[VAL_5:.*]] = fir.if %[[VAL_4]] -> (!fir.box<!fir.array<?xi32>>) {
-// CHECK:    %[[VAL_6:.*]] = fir.rebox %[[VAL_3]](%[[VAL_2]]) : (!fir.box<!fir.array<?xi32>>, !fir.shift<1>) -> !fir.box<!fir.array<?xi32>>
-// CHECK:    fir.result %[[VAL_6]] : !fir.box<!fir.array<?xi32>>
-// CHECK:  } else {
-// CHECK:    %[[VAL_7:.*]] = fir.absent !fir.box<!fir.array<?xi32>>
-// CHECK:    fir.result %[[VAL_7]] : !fir.box<!fir.array<?xi32>>
-// CHECK:  }
+// CHECK:  %[[VAL_4:.*]] = fir.rebox %[[VAL_3]](%[[VAL_2]]) optional : (!fir.box<!fir.array<?xi32>>, !fir.shift<1>) -> !fir.box<!fir.array<?xi32>>
+// CHECK-NOT:  fir.if
+// CHECK-NOT:  fir.is_present
+
+func.func @test_optional_declare_embox(%arg0: !fir.ref<!fir.array<?xi32>>) {
+  %c42 = arith.constant 42 : index
+  %0 = fir.shape %c42 : (index) -> !fir.shape<1>
+  %1:2 = hlfir.declare %arg0(%0) {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "y"} : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> (!fir.box<!fir.array<?xi32>>, !fir.ref<!fir.array<?xi32>>)
+  return
+}
+// CHECK-LABEL:   func.func @test_optional_declare_embox(
+// CHECK-SAME:      %[[VAL_0:.*]]: !fir.ref<!fir.array<?xi32>>) {
+// CHECK:  %[[VAL_1:.*]] = arith.constant 42 : index
+// CHECK:  %[[VAL_2:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
+// CHECK:  %[[VAL_3:.*]] = fir.declare %[[VAL_0]](%[[VAL_2]]) {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "y"} : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.ref<!fir.array<?xi32>>
+// CHECK:  %[[VAL_4:.*]] = fir.embox %[[VAL_3]](%[[VAL_2]]) optional : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.box<!fir.array<?xi32>>
+// CHECK-NOT:  fir.if
+// CHECK-NOT:  fir.is_present
 
 func.func @dummy_scope(%arg0: !fir.ref<f32>) {
   %scope = fir.dummy_scope : !fir.dscope
@@ -229,6 +239,17 @@ func.func @assumed_rank_declare(%arg0: !fir.box<!fir.array<*:f32>>) {
 // CHECK:    %[[VAL_1:.*]] = fir.declare %[[VAL_0]] {uniq_name = "x"} : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
 // CHECK:    %[[VAL_2:.*]] = fir.rebox_assumed_rank %[[VAL_1]] lbs ones : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
 
+func.func @test_optional_assumed_rank_declare(%arg0: !fir.box<!fir.array<*:f32>>) {
+  %0:2 = hlfir.declare %arg0 {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "x"} : (!fir.box<!fir.array<*:f32>>) -> (!fir.box<!fir.array<*:f32>>, !fir.box<!fir.array<*:f32>>)
+  return
+}
+// CHECK-LABEL:  func.func @test_optional_assumed_rank_declare(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<*:f32>>) {
+// CHECK:    %[[VAL_1:.*]] = fir.declare %[[VAL_0]] {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "x"} : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+// CHECK:    %[[VAL_2:.*]] = fir.rebox_assumed_rank %[[VAL_1]] lbs ones optional : (!fir.box<!fir.array<*:f32>>) -> !fir.box<!fir.array<*:f32>>
+// CHECK-NOT:  fir.if
+// CHECK-NOT:  fir.is_present
+
 func.func @no_useless_rebox(%arg0: !fir.class<!fir.type<sometype{i:i32}>>) {
   %0:2 = hlfir.declare %arg0 {uniq_name = "x"} : (!fir.class<!fir.type<sometype{i:i32}>>) -> (!fir.class<!fir.type<sometype{i:i32}>>, !fir.class<!fir.type<sometype{i:i32}>>)
   fir.call @takes_class(%0#0) : (!fir.class<!fir.type<sometype{i:i32}>>) -> ()

>From 77e80c651cfbe65b6ed17160c9d9194184c5ca3e Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Mon, 27 Apr 2026 05:00:33 -0700
Subject: [PATCH 2/3] remove leftover debug print

---
 flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp b/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
index d0b9093eaae65..78f788e2a8ff7 100644
--- a/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
+++ b/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
@@ -248,7 +248,6 @@ class ReboxConversion : public mlir::OpRewritePattern<fir::ReboxOp> {
       mlir::Value newBox = emitOptionalBoxGuard(rewriter, rebox, [&] {
         return matchAndRewriteImpl(rebox, rewriter)->getResult(0);
       });
-      llvm::errs() << newBox;
       rewriter.replaceOp(rebox, newBox);
       return mlir::success();
     }

>From 357e9dbacac9a4235745a32f02bb515e34b909a1 Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Tue, 28 Apr 2026 02:50:26 -0700
Subject: [PATCH 3/3] return Speculatable for fir.rebox/fir.embox

---
 flang/lib/Optimizer/Dialect/FIROps.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 307511988eb8d..286e004e7bc94 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -2502,7 +2502,7 @@ mlir::Speculation::Speculatability fir::EmboxOp::getSpeculatability() {
   // attribute, otherwise it is not safe to evaluate if the input may
   // be absent.
   if (getOptional())
-    return mlir::Speculation::NotSpeculatable;
+    return mlir::Speculation::Speculatable;
   return (getSourceBox() && mayBeAbsentBox(getSourceBox()))
              ? mlir::Speculation::NotSpeculatable
              : mlir::Speculation::Speculatable;
@@ -3818,7 +3818,7 @@ mlir::Speculation::Speculatability fir::ReboxOp::getSpeculatability() {
   // attribute, otherwise it is not safe to evaluate if the input may
   // be absent.
   if (getOptional())
-    return mlir::Speculation::NotSpeculatable;
+    return mlir::Speculation::Speculatable;
   return mayBeAbsentBox(getBox()) ? mlir::Speculation::NotSpeculatable
                                   : mlir::Speculation::Speculatable;
 }



More information about the flang-commits mailing list