[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