[flang-commits] [flang] a6df7eb - [flang] allow rebox/embox of OPTIONAL (#194319)
via flang-commits
flang-commits at lists.llvm.org
Tue Apr 28 06:00:51 PDT 2026
Author: jeanPerier
Date: 2026-04-28T15:00:46+02:00
New Revision: a6df7eb0638a452c55fcd44b90d95270e9ebf533
URL: https://github.com/llvm/llvm-project/commit/a6df7eb0638a452c55fcd44b90d95270e9ebf533
DIFF: https://github.com/llvm/llvm-project/commit/a6df7eb0638a452c55fcd44b90d95270e9ebf533.diff
LOG: [flang] allow rebox/embox of OPTIONAL (#194319)
Delay materialization of branches when building local temporary
descriptor for OPTIONAL from hlfir-to-fir until pre-cg-rewrite.
This makes the IR easier to analyze with OPTIONAL (for instance alias
analysis does not need to handle the branches to find the source).
This is done by adding an "optional" attribute to fir.embox, fir.rebox,
and fir.rebox_assumed_rank to indicate that their cogeneration must be
conditional.
The conditional aspect is implemented in pre-cg-rewrite to avoid
complexifying codegen and the fir.cg dialect.
Assisted by: Claude
Added:
flang/test/Fir/rebox-embox-optional-codegen.fir
Modified:
flang/include/flang/Optimizer/CodeGen/CGPasses.td
flang/include/flang/Optimizer/Dialect/FIROps.td
flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp
flang/lib/Optimizer/Dialect/FIROps.cpp
flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
flang/lib/Optimizer/Transforms/AssumedRankOpConversion.cpp
flang/test/Fir/fir-ops.fir
flang/test/Fir/rebox_assumed_rank_codegen.fir
flang/test/HLFIR/declare-codegen.fir
Removed:
################################################################################
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..78f788e2a8ff7 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,34 @@ 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);
+ });
+ 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 +292,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 +445,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..286e004e7bc94 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::Speculatable;
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::Speculatable;
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}>>) -> ()
More information about the flang-commits
mailing list