[flang-commits] [flang] [flang][Transforms] Add `SelectOpsConversion` pass (PR #212977)
Kareem Ergawy via flang-commits
flang-commits at lists.llvm.org
Thu Jul 30 02:50:32 PDT 2026
https://github.com/ergawy created https://github.com/llvm/llvm-project/pull/212977
Introduces `--fir-select-ops-conversion`, which lowers `fir.select`, `fir.select_case`, and `fir.select_rank` to the control-flow dialect (`cf.switch` / `cf.cond_br` / `cf.br`) while preserving the CFG shape. `fir.select_case` becomes an if-then-else ladder of `arith.cmpi` + `cf.cond_br`; Fortran `UNSIGNED` selectors use `ule`. Signed / unsigned FIR integer values are normalized to signless via `fir.convert` first.
`fir.select_type` is not handled here — it is already lowered by `--fir-polymorphic-op` (`PolymorphicOpConversion`).
The pass runs in the default FIR optimizer pipeline right after `PolymorphicOpConversion`. Pipeline-check tests are updated to expect `SelectOpsConversion` in the sequence; `Fir/select.fir` and `Lower/volatile3.f90` are relaxed to accept the newly-canonicalized form of the lowered output.
The main purpose of moving these conversion pattern earlier in the MLIR pipeline and target `cf` instead of directly `llvm` is to be able to later on use control-flow to structured-control-flow lifting: https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Conversion/Passes.td#L402.
>From 679cc014983191ba012f3f28288b60692fdfb712 Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at gmail.com>
Date: Thu, 30 Jul 2026 02:02:01 -0700
Subject: [PATCH] [flang][Transforms] Add SelectOpsConversion pass
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Introduces `--fir-select-ops-conversion`, which lowers `fir.select`,
`fir.select_case`, and `fir.select_rank` to the control-flow dialect
(`cf.switch` / `cf.cond_br` / `cf.br`) while preserving the CFG shape.
`fir.select_case` becomes an if-then-else ladder of `arith.cmpi` +
`cf.cond_br`; Fortran `UNSIGNED` selectors use `ule`. Signed / unsigned
FIR integer values are normalized to signless via `fir.convert` first.
`fir.select_type` is not handled here — it is already lowered by
`--fir-polymorphic-op` (`PolymorphicOpConversion`).
The pass runs in the default FIR optimizer pipeline right after
`PolymorphicOpConversion`. Pipeline-check tests are updated to expect
`SelectOpsConversion` in the sequence; `Fir/select.fir` and
`Lower/volatile3.f90` are relaxed to accept the newly-canonicalized form
of the lowered output.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply at anthropic.com>
---
.../flang/Optimizer/Transforms/Passes.td | 20 +-
flang/lib/Optimizer/Passes/Pipelines.cpp | 1 +
flang/lib/Optimizer/Transforms/CMakeLists.txt | 1 +
.../Transforms/SelectOpsConversion.cpp | 284 ++++++++++++++++++
flang/test/Driver/bbc-mlir-pass-pipeline.f90 | 1 +
.../test/Driver/mlir-debug-pass-pipeline.f90 | 1 +
flang/test/Driver/mlir-pass-pipeline.f90 | 1 +
flang/test/Fir/SelectOpsConversion/select.fir | 113 +++++++
.../Fir/SelectOpsConversion/select_case.fir | 192 ++++++++++++
.../Fir/SelectOpsConversion/select_rank.fir | 46 +++
flang/test/Fir/basic-program.fir | 1 +
flang/test/Fir/select.fir | 18 +-
flang/test/Lower/volatile3.f90 | 18 +-
13 files changed, 683 insertions(+), 14 deletions(-)
create mode 100644 flang/lib/Optimizer/Transforms/SelectOpsConversion.cpp
create mode 100644 flang/test/Fir/SelectOpsConversion/select.fir
create mode 100644 flang/test/Fir/SelectOpsConversion/select_case.fir
create mode 100644 flang/test/Fir/SelectOpsConversion/select_rank.fir
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index ba29cd6df2eac..7a868852ce5f3 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -76,6 +76,22 @@ def AffineDialectDemotion : Pass<"demote-affine", "::mlir::func::FuncOp"> {
];
}
+def SelectOpsConversion : Pass<"fir-select-ops-conversion"> {
+ let summary = "Lower fir.select / fir.select_case / fir.select_rank to cf.*";
+
+ let description = [{
+ Lowers FIR multi-way branch terminators to control-flow dialect ops
+ (`cf.switch`, `cf.cond_br`, `cf.br`), keeping the same CFG shape (block
+ count, branch directions, destination operand forwarding).
+
+ `fir.select_type` is intentionally NOT handled here since it's already
+ handled by `PolymorphicOpConversion`.
+ }];
+
+ let dependentDialects = ["mlir::arith::ArithDialect",
+ "mlir::cf::ControlFlowDialect", "fir::FIROpsDialect"];
+}
+
def FIRToSCFPass : Pass<"fir-to-scf"> {
let summary = "Convert FIR structured control flow ops to SCF dialect.";
let description = [{
@@ -411,8 +427,8 @@ def PolymorphicOpConversion : Pass<"fir-polymorphic-op", "mlir::ModuleOp"> {
let summary =
"Simplify operations on polymorphic types";
let description = [{
- This pass breaks up the lowering of operations on polymorphic types by
- introducing an intermediate FIR level that simplifies code geneation.
+ This pass breaks up the lowering of operations on polymorphic types by
+ introducing an intermediate FIR level that simplifies code generation.
}];
let dependentDialects = [
"fir::FIROpsDialect", "mlir::func::FuncDialect"
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index f40d99aa5a66c..500730ee44f44 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -242,6 +242,7 @@ void createDefaultFIROptimizerPassPipeline(mlir::PassManager &pm,
// Polymorphic types
pm.addPass(fir::createPolymorphicOpConversion());
+ pm.addPass(fir::createSelectOpsConversion());
pm.addPass(fir::createAssumedRankOpConversion());
// Optimize redundant array repacking operations,
diff --git a/flang/lib/Optimizer/Transforms/CMakeLists.txt b/flang/lib/Optimizer/Transforms/CMakeLists.txt
index 997dc22063138..3bc2df4c94248 100644
--- a/flang/lib/Optimizer/Transforms/CMakeLists.txt
+++ b/flang/lib/Optimizer/Transforms/CMakeLists.txt
@@ -46,6 +46,7 @@ add_flang_library(FIRTransforms
GenRuntimeCallsForTest.cpp
LoopInvariantCodeMotion.cpp
LoopVersioning.cpp
+ SelectOpsConversion.cpp
MIFOpConversion.cpp
MemRefDataFlowOpt.cpp
MemoryAllocation.cpp
diff --git a/flang/lib/Optimizer/Transforms/SelectOpsConversion.cpp b/flang/lib/Optimizer/Transforms/SelectOpsConversion.cpp
new file mode 100644
index 0000000000000..184961c81c544
--- /dev/null
+++ b/flang/lib/Optimizer/Transforms/SelectOpsConversion.cpp
@@ -0,0 +1,284 @@
+//===- SelectOpsConversion.cpp - Lower fir.select* to cf.* ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Builder/Todo.h"
+#include "flang/Optimizer/Dialect/FIRDialect.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Optimizer/Transforms/Passes.h"
+#include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
+#include "mlir/IR/Builders.h"
+
+namespace fir {
+#define GEN_PASS_DEF_SELECTOPSCONVERSION
+#include "flang/Optimizer/Transforms/Passes.h.inc"
+} // namespace fir
+
+#define DEBUG_TYPE "fir-select-ops-conversion"
+
+namespace {
+using namespace mlir;
+
+template <typename SwitchLike>
+static ValueRange successorOperands(SwitchLike op, unsigned successorIdx) {
+ return op.getSuccessorOperands(successorIdx).getForwardedOperands();
+}
+
+// Bit-cast a signed or unsigned FIR integer value to its signless-integer
+// equivalent. The arith / cf dialects only accept signless integers, so any
+// `ui*` / `si*` value must be normalized before use.
+static Value toSignlessInteger(OpBuilder &b, Location loc, Value v) {
+ auto intTy = dyn_cast<IntegerType>(v.getType());
+ if (!intTy || intTy.isSignless())
+ return v;
+ auto signlessTy = IntegerType::get(v.getContext(), intTy.getWidth());
+ return fir::ConvertOp::create(b, loc, signlessTy, v);
+}
+
+// Widen `v` to a signless i64, the type used for `cf.switch` selectors in
+// this pass. `v` must be integer- or index-typed. Wider-than-64-bit
+// selectors are truncated to i64, matching the behaviour of the FIR-to-LLVM
+// `integerCast` helper this pass replaces.
+static Value toSignlessI64(OpBuilder &b, Location loc, Value v) {
+ v = toSignlessInteger(b, loc, v);
+ auto i64 = IntegerType::get(v.getContext(), 64);
+ Type srcTy = v.getType();
+ if (srcTy == i64)
+ return v;
+ if (isa<IndexType>(srcTy))
+ return arith::IndexCastOp::create(b, loc, i64, v);
+ unsigned srcW = cast<IntegerType>(srcTy).getWidth();
+ if (srcW < 64)
+ return arith::ExtSIOp::create(b, loc, i64, v);
+ return arith::TruncIOp::create(b, loc, i64, v);
+}
+
+//===----------------------------------------------------------------------===//
+// fir.select / fir.select_rank → cf.switch
+//===----------------------------------------------------------------------===//
+
+template <typename SwitchLike>
+static LogicalResult lowerToSwitch(SwitchLike op) {
+ Location loc = op.getLoc();
+ OpBuilder builder(op);
+
+ // The fir.select* selector must be integer or index typed. (fir.select_rank
+ // in particular reaches this pass with an integer selector: flang emits a
+ // `fir.box_rank` earlier that reads the rank field from the CFI descriptor
+ // as an `i8`, and the `fir.select_rank` then dispatches on that.)
+ Type selectorTy = op.getSelector().getType();
+ if (!isa<IntegerType, IndexType>(selectorTy))
+ return op.emitOpError("selector is not an integer/index type");
+
+ // Widen the selector to i64. `cf.switch`'s case-value APInts are also
+ // constructed at 64 bits below, so the case-value / selector element-type
+ // constraint is satisfied. Widening to i64 additionally accommodates
+ // Fortran computed GOTOs whose label values may not fit in the source
+ // selector's width.
+ Value selector = toSignlessI64(builder, loc, op.getSelector());
+
+ SmallVector<int64_t> caseValues;
+ SmallVector<Block *> caseDests;
+ SmallVector<ValueRange> caseOperands;
+ Block *defaultDest = nullptr;
+ ValueRange defaultOperands;
+
+ unsigned numConds = op.getNumConditions();
+ ArrayRef<Attribute> cases = op.getCases().getValue();
+ for (unsigned i = 0; i != numConds; ++i) {
+ Block *dest = op.getSuccessor(i);
+ ValueRange ops = successorOperands(op, i);
+ Attribute attr = cases[i];
+
+ if (auto intAttr = dyn_cast<IntegerAttr>(attr)) {
+ caseValues.push_back(intAttr.getInt());
+ caseDests.push_back(dest);
+ caseOperands.push_back(ops);
+ continue;
+ }
+
+ assert(isa<UnitAttr>(attr) && "unexpected case attribute kind");
+ assert(!defaultDest && "multiple unit (default) entries in fir.select*");
+ defaultDest = dest;
+ defaultOperands = ops;
+ }
+
+ if (!defaultDest)
+ return op.emitOpError("fir.select* verifier requires a unit default, "
+ "but none was found");
+
+ if (caseValues.empty()) {
+ cf::BranchOp::create(builder, loc, defaultDest, defaultOperands);
+ } else {
+ SmallVector<APInt> caseAPInts;
+ caseAPInts.reserve(caseValues.size());
+ for (int64_t v : caseValues)
+ caseAPInts.emplace_back(64, v, /*isSigned=*/true);
+ cf::SwitchOp::create(builder, loc, selector, defaultDest, defaultOperands,
+ caseAPInts, caseDests, caseOperands);
+ }
+
+ op.erase();
+ return success();
+}
+
+//===----------------------------------------------------------------------===//
+// fir.select_case → if-then-else ladder of cf.cond_br
+//===----------------------------------------------------------------------===//
+
+// Emit one rung of the fir.select_case if-then-else ladder:
+//
+// thisBlock: ...; cf.cond_br %cmp, dest(destOps), nextBlock
+// nextBlock: <insertion point at end>
+//
+// Returns the freshly-created nextBlock.
+static Block *genCaseLadderStep(OpBuilder &builder, Location loc, Value cmp,
+ Block *dest, ValueRange destOps) {
+ Block *thisBlock = builder.getInsertionBlock();
+ Region *region = thisBlock->getParent();
+ Block *nextBlock =
+ builder.createBlock(region, std::next(thisBlock->getIterator()));
+ builder.setInsertionPointToEnd(thisBlock);
+ cf::CondBranchOp::create(builder, loc, cmp, dest, destOps, nextBlock,
+ ValueRange());
+ builder.setInsertionPointToEnd(nextBlock);
+ return nextBlock;
+}
+
+static LogicalResult lowerSelectCase(fir::SelectCaseOp op) {
+ Location loc = op.getLoc();
+ Value selector = op.getSelector();
+
+ // CHARACTER selectors are not yet supported.
+ if (isa<fir::CharacterType>(selector.getType())) {
+ TODO(op.getLoc(), "fir.select_case codegen with character type");
+ return failure();
+ }
+
+ if (!isa<IntegerType, IndexType>(selector.getType()))
+ return op.emitOpError("non-integer/character selector not supported");
+
+ OpBuilder builder(op);
+ // Fortran `UNSIGNED` selectors have `ui*` type; convert to signless up
+ // front and pick the unsigned compare predicate for range checks. Signed
+ // and signless selectors use the signed predicate.
+ auto origSelTy = dyn_cast<IntegerType>(selector.getType());
+ bool isUnsigned = origSelTy && origSelTy.isUnsigned();
+ arith::CmpIPredicate lePred =
+ isUnsigned ? arith::CmpIPredicate::ule : arith::CmpIPredicate::sle;
+ selector = toSignlessInteger(builder, loc, selector);
+ unsigned numConds = op.getNumConditions();
+ ArrayRef<Attribute> cases = op.getCases().getValue();
+
+ for (unsigned i = 0; i != numConds; ++i) {
+ Block *dest = op.getSuccessor(i);
+ ValueRange destOps = successorOperands(op, i);
+ Attribute attr = cases[i];
+
+ if (isa<UnitAttr>(attr)) {
+ // Default branch — unconditional jump. Must be the last entry.
+ assert(i + 1 == numConds && "fir.select_case unit attr must be last");
+ cf::BranchOp::create(builder, loc, dest, destOps);
+ op.erase();
+ return success();
+ }
+
+ std::optional<OperandRange> cmpOpsOpt = op.getCompareOperands(i);
+ if (!cmpOpsOpt)
+ return op.emitOpError("missing compare operands for case ") << i;
+ OperandRange cmpOps = *cmpOpsOpt;
+
+ if (isa<fir::PointIntervalAttr>(attr)) {
+ Value cmpOp = toSignlessInteger(builder, loc, cmpOps.front());
+ Value cmp = arith::CmpIOp::create(
+ builder, loc, arith::CmpIPredicate::eq, selector, cmpOp);
+ genCaseLadderStep(builder, loc, cmp, dest, destOps);
+ continue;
+ }
+ if (isa<fir::LowerBoundAttr>(attr)) {
+ // case(c:): match when c <= selector.
+ Value cmpOp = toSignlessInteger(builder, loc, cmpOps.front());
+ Value cmp = arith::CmpIOp::create(builder, loc, lePred, cmpOp, selector);
+ genCaseLadderStep(builder, loc, cmp, dest, destOps);
+ continue;
+ }
+ if (isa<fir::UpperBoundAttr>(attr)) {
+ // case(:c): match when selector <= c.
+ Value cmpOp = toSignlessInteger(builder, loc, cmpOps.front());
+ Value cmp = arith::CmpIOp::create(builder, loc, lePred, selector, cmpOp);
+ genCaseLadderStep(builder, loc, cmp, dest, destOps);
+ continue;
+ }
+ if (isa<fir::ClosedIntervalAttr>(attr)) {
+ // case(lo:hi): two-step short-circuit. First check lo <= selector;
+ // if true, branch to a hi-check block; if false, fall through.
+ Value lo = toSignlessInteger(builder, loc, cmpOps[0]);
+ Value hi = toSignlessInteger(builder, loc, cmpOps[1]);
+ Value cmpLo = arith::CmpIOp::create(builder, loc, lePred, lo, selector);
+ // Block layout (in source order):
+ // thisBlock -> cond_br cmpLo, hiCheck, fallThrough
+ // hiCheck -> cond_br cmpHi, dest, fallThrough
+ // fallThrough -> next case
+ Block *thisBlock = builder.getInsertionBlock();
+ Region *region = thisBlock->getParent();
+ auto insertIt = std::next(thisBlock->getIterator());
+ Block *hiCheck = builder.createBlock(region, insertIt);
+ Block *fallThrough =
+ builder.createBlock(region, std::next(hiCheck->getIterator()));
+ builder.setInsertionPointToEnd(thisBlock);
+ cf::CondBranchOp::create(builder, loc, cmpLo, hiCheck, ValueRange(),
+ fallThrough, ValueRange());
+ builder.setInsertionPointToEnd(hiCheck);
+ Value cmpHi = arith::CmpIOp::create(builder, loc, lePred, selector, hi);
+ cf::CondBranchOp::create(builder, loc, cmpHi, dest, destOps, fallThrough,
+ ValueRange());
+ builder.setInsertionPointToEnd(fallThrough);
+ continue;
+ }
+ return op.emitOpError("unknown case attribute kind");
+ }
+
+ // The FIR verifier requires a `unit` entry, so we should not reach here.
+ return op.emitOpError("fir.select_case has no unit (default) entry");
+}
+
+//===----------------------------------------------------------------------===//
+// Pass
+//===----------------------------------------------------------------------===//
+
+class SelectOpsConversion
+ : public fir::impl::SelectOpsConversionBase<SelectOpsConversion> {
+public:
+ using SelectOpsConversionBase<SelectOpsConversion>::SelectOpsConversionBase;
+
+ void runOnOperation() override {
+ // Collect first; rewriting mutates blocks and would invalidate a live walk.
+ SmallVector<Operation *> worklist;
+ getOperation()->walk([&](Operation *op) {
+ if (isa<fir::SelectOp, fir::SelectCaseOp, fir::SelectRankOp>(op))
+ worklist.push_back(op);
+ });
+
+ for (Operation *op : worklist) {
+ LogicalResult r = success();
+ if (auto s = dyn_cast<fir::SelectOp>(op))
+ r = lowerToSwitch(s);
+ else if (auto sr = dyn_cast<fir::SelectRankOp>(op))
+ r = lowerToSwitch(sr);
+ else if (auto sc = dyn_cast<fir::SelectCaseOp>(op))
+ r = lowerSelectCase(sc);
+ if (failed(r)) {
+ signalPassFailure();
+ return;
+ }
+ }
+ }
+};
+
+} // namespace
diff --git a/flang/test/Driver/bbc-mlir-pass-pipeline.f90 b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
index 4c98c22b90e33..ad293ac42ef64 100644
--- a/flang/test/Driver/bbc-mlir-pass-pipeline.f90
+++ b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
@@ -47,6 +47,7 @@
! CHECK-NEXT: (S) 0 num-dce'd - Number of operations DCE'd
! CHECK-NEXT: PolymorphicOpConversion
+! CHECK-NEXT: SelectOpsConversion
! CHECK-NEXT: AssumedRankOpConversion
! CHECK-NEXT: 'func.func' Pipeline
! CHECK-NEXT: OptimizeArrayRepacking
diff --git a/flang/test/Driver/mlir-debug-pass-pipeline.f90 b/flang/test/Driver/mlir-debug-pass-pipeline.f90
index 93c227ebfde74..b1d0b01037fa4 100644
--- a/flang/test/Driver/mlir-debug-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-debug-pass-pipeline.f90
@@ -84,6 +84,7 @@
! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'd
! ALL-NEXT: PolymorphicOpConversion
+! ALL-NEXT: SelectOpsConversion
! ALL-NEXT: AssumedRankOpConversion
! ALL-NEXT: LowerRepackArraysPass
! ALL-NEXT: SimplifyFIROperations
diff --git a/flang/test/Driver/mlir-pass-pipeline.f90 b/flang/test/Driver/mlir-pass-pipeline.f90
index dadf3ce28c66f..837d7154c7105 100644
--- a/flang/test/Driver/mlir-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-pass-pipeline.f90
@@ -136,6 +136,7 @@
! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'd
! ALL-NEXT: PolymorphicOpConversion
+! ALL-NEXT: SelectOpsConversion
! ALL-NEXT: AssumedRankOpConversion
! O2-NEXT: 'func.func' Pipeline
! O2-NEXT: OptimizeArrayRepacking
diff --git a/flang/test/Fir/SelectOpsConversion/select.fir b/flang/test/Fir/SelectOpsConversion/select.fir
new file mode 100644
index 0000000000000..b85c69742fc4c
--- /dev/null
+++ b/flang/test/Fir/SelectOpsConversion/select.fir
@@ -0,0 +1,113 @@
+// RUN: fir-opt %s --fir-select-ops-conversion | FileCheck %s
+
+// Exercises fir.select with an `index` selector and a mix of successors
+// that forward one, two, or three block arguments, plus a unit default.
+
+func.func @select(%arg : index, %arg2 : i32) -> i32 {
+ %c1 = arith.constant 1 : i32
+ %c2 = arith.constant 2 : i32
+ %c3 = arith.constant 3 : i32
+ %c4 = arith.constant 4 : i32
+ fir.select %arg:index [ 1, ^bb1(%c1:i32),
+ 2, ^bb2(%c3,%arg,%arg2:i32,index,i32),
+ 3, ^bb3(%arg2,%c3:i32,i32),
+ 4, ^bb4(%c2:i32),
+ unit, ^bb5 ]
+ ^bb1(%a : i32) :
+ return %a : i32
+ ^bb2(%b : i32, %b2 : index, %b3:i32) :
+ %castidx = arith.index_cast %b2 : index to i32
+ %4 = arith.addi %b, %castidx : i32
+ %5 = arith.addi %4, %b3 : i32
+ return %5 : i32
+ ^bb3(%c:i32, %c2b:i32) :
+ %6 = arith.addi %c, %c2b : i32
+ return %6 : i32
+ ^bb4(%d : i32) :
+ return %d : i32
+ ^bb5 :
+ %zero = arith.constant 0 : i32
+ return %zero : i32
+}
+
+// CHECK-LABEL: func.func @select(
+// CHECK-SAME: %[[SELECTVALUE:.*]]: index,
+// CHECK-SAME: %[[ARG1:.*]]: i32)
+// CHECK-DAG: %[[C1:.*]] = arith.constant 1 : i32
+// CHECK-DAG: %[[C2:.*]] = arith.constant 2 : i32
+// CHECK-DAG: %[[C3:.*]] = arith.constant 3 : i32
+// CHECK: %[[SEL:.*]] = arith.index_cast %[[SELECTVALUE]] : index to i64
+// CHECK: cf.switch %[[SEL]] : i64, [
+// CHECK: default: ^[[BB5:.*]],
+// CHECK: 1: ^[[BB1:.*]](%[[C1]] : i32),
+// CHECK: 2: ^[[BB2:.*]](%[[C3]], %[[SELECTVALUE]], %[[ARG1]] : i32, index, i32),
+// CHECK: 3: ^[[BB3:.*]](%[[ARG1]], %[[C3]] : i32, i32),
+// CHECK: 4: ^[[BB4:.*]](%[[C2]] : i32)
+// CHECK: ]
+// CHECK: ^[[BB1]](%{{.*}}: i32):
+// CHECK: return
+// CHECK: ^[[BB2]](%{{.*}}: i32, %{{.*}}: index, %{{.*}}: i32):
+// CHECK: return
+// CHECK: ^[[BB3]](%{{.*}}: i32, %{{.*}}: i32):
+// CHECK: return
+// CHECK: ^[[BB4]](%{{.*}}: i32):
+// CHECK: return
+// CHECK: ^[[BB5]]:
+// CHECK: %[[CST0:.*]] = arith.constant 0 : i32
+// CHECK: return %[[CST0]] : i32
+
+// -----
+
+// Exercises the selector-widening code path across the four permitted
+// selector types (i8, i16, i64, index) plus a case value that doesn't fit
+// in i32. i8/i16 selectors are extended with `arith.extsi`, i64 is used
+// as-is, and an `index` selector is bridged via `arith.index_cast`.
+
+func.func @select_with_cast(%arg1 : i8, %arg2 : i16, %arg3: i64, %arg4: index) -> () {
+ fir.select %arg1 : i8 [ 1, ^bb1, unit, ^bb1 ]
+ ^bb1:
+ fir.select %arg2 : i16 [ 1, ^bb2, unit, ^bb2 ]
+ ^bb2:
+ fir.select %arg3 : i64 [ 1, ^bb3, unit, ^bb3 ]
+ ^bb3:
+ fir.select %arg4 : index [ 1, ^bb4, unit, ^bb4 ]
+ ^bb4:
+ fir.select %arg3 : i64 [ 4294967296, ^bb5, unit, ^bb5 ]
+ ^bb5:
+ return
+}
+
+// CHECK-LABEL: func.func @select_with_cast(
+// CHECK-SAME: %[[ARG0:.*]]: i8,
+// CHECK-SAME: %[[ARG1:.*]]: i16,
+// CHECK-SAME: %[[ARG2:.*]]: i64,
+// CHECK-SAME: %[[ARG3:.*]]: index)
+// CHECK: %[[V0:.*]] = arith.extsi %[[ARG0]] : i8 to i64
+// CHECK: cf.switch %[[V0]] : i64, [
+// CHECK: default: ^bb1,
+// CHECK: 1: ^bb1
+// CHECK: ]
+// CHECK: ^bb1:
+// CHECK: %[[V1:.*]] = arith.extsi %[[ARG1]] : i16 to i64
+// CHECK: cf.switch %[[V1]] : i64, [
+// CHECK: default: ^bb2,
+// CHECK: 1: ^bb2
+// CHECK: ]
+// CHECK: ^bb2:
+// CHECK: cf.switch %[[ARG2]] : i64, [
+// CHECK: default: ^bb3,
+// CHECK: 1: ^bb3
+// CHECK: ]
+// CHECK: ^bb3:
+// CHECK: %[[V3:.*]] = arith.index_cast %[[ARG3]] : index to i64
+// CHECK: cf.switch %[[V3]] : i64, [
+// CHECK: default: ^bb4,
+// CHECK: 1: ^bb4
+// CHECK: ]
+// CHECK: ^bb4:
+// CHECK: cf.switch %[[ARG2]] : i64, [
+// CHECK: default: ^bb5,
+// CHECK: 4294967296: ^bb5
+// CHECK: ]
+// CHECK: ^bb5:
+// CHECK: return
diff --git a/flang/test/Fir/SelectOpsConversion/select_case.fir b/flang/test/Fir/SelectOpsConversion/select_case.fir
new file mode 100644
index 0000000000000..b1068801edfb5
--- /dev/null
+++ b/flang/test/Fir/SelectOpsConversion/select_case.fir
@@ -0,0 +1,192 @@
+// RUN: fir-opt %s --fir-select-ops-conversion | FileCheck %s
+
+// Exercises all five case-attribute kinds for fir.select_case with an i32
+// selector: #fir.upper, #fir.point, #fir.interval, #fir.lower, and unit.
+
+func.func @select_case_integer(%arg0: !fir.ref<i32>) -> i32 {
+ %2 = fir.load %arg0 : !fir.ref<i32>
+ %c1_i32 = arith.constant 1 : i32
+ %c2_i32 = arith.constant 2 : i32
+ %c4_i32 = arith.constant 4 : i32
+ %c5_i32 = arith.constant 5 : i32
+ %c7_i32 = arith.constant 7 : i32
+ %c8_i32 = arith.constant 8 : i32
+ %c15_i32 = arith.constant 15 : i32
+ %c21_i32 = arith.constant 21 : i32
+ fir.select_case %2 : i32 [#fir.upper, %c1_i32, ^bb1,
+ #fir.point, %c2_i32, ^bb2,
+ #fir.interval, %c4_i32, %c5_i32, ^bb4,
+ #fir.point, %c7_i32, ^bb5,
+ #fir.interval, %c8_i32, %c15_i32, ^bb5,
+ #fir.lower, %c21_i32, ^bb5,
+ unit, ^bb3]
+^bb1: // pred: ^bb0
+ %c1_i32_0 = arith.constant 1 : i32
+ fir.store %c1_i32_0 to %arg0 : !fir.ref<i32>
+ cf.br ^bb6
+^bb2: // pred: ^bb0
+ %c2_i32_1 = arith.constant 2 : i32
+ fir.store %c2_i32_1 to %arg0 : !fir.ref<i32>
+ cf.br ^bb6
+^bb3: // pred: ^bb0
+ %c0_i32 = arith.constant 0 : i32
+ fir.store %c0_i32 to %arg0 : !fir.ref<i32>
+ cf.br ^bb6
+^bb4: // pred: ^bb0
+ %c4_i32_2 = arith.constant 4 : i32
+ fir.store %c4_i32_2 to %arg0 : !fir.ref<i32>
+ cf.br ^bb6
+^bb5: // 3 preds: ^bb0, ^bb0, ^bb0
+ %c7_i32_3 = arith.constant 7 : i32
+ fir.store %c7_i32_3 to %arg0 : !fir.ref<i32>
+ cf.br ^bb6
+^bb6: // 5 preds: ^bb1, ^bb2, ^bb3, ^bb4, ^bb5
+ %3 = fir.load %arg0 : !fir.ref<i32>
+ return %3 : i32
+}
+
+// CHECK-LABEL: func.func @select_case_integer(
+// CHECK-SAME: %[[ARG0:.*]]: !fir.ref<i32>) -> i32 {
+// CHECK: %[[SELECT_VALUE:.*]] = fir.load %[[ARG0]] : !fir.ref<i32>
+// CHECK: %[[CST1:.*]] = arith.constant 1 : i32
+// CHECK: %[[CST2:.*]] = arith.constant 2 : i32
+// CHECK: %[[CST4:.*]] = arith.constant 4 : i32
+// CHECK: %[[CST5:.*]] = arith.constant 5 : i32
+// CHECK: %[[CST7:.*]] = arith.constant 7 : i32
+// CHECK: %[[CST8:.*]] = arith.constant 8 : i32
+// CHECK: %[[CST15:.*]] = arith.constant 15 : i32
+// CHECK: %[[CST21:.*]] = arith.constant 21 : i32
+// Check for upper bound `case (:1)`
+// CHECK: %[[CMP_SLE:.*]] = arith.cmpi sle, %[[SELECT_VALUE]], %[[CST1]] : i32
+// CHECK: cf.cond_br %[[CMP_SLE]], ^[[BB_UPPER:bb[0-9]+]], ^bb1
+// CHECK-LABEL: ^bb1:
+// Check for point value `case (2)`
+// CHECK: %[[CMP_EQ:.*]] = arith.cmpi eq, %[[SELECT_VALUE]], %[[CST2]] : i32
+// CHECK: cf.cond_br %[[CMP_EQ]], ^[[BB_POINT2:bb[0-9]+]], ^bb2
+// CHECK-LABEL: ^bb2:
+// Check for the lower bound for the interval `case (4:5)`
+// CHECK: %[[CMP_SLE:.*]] = arith.cmpi sle, %[[CST4]], %[[SELECT_VALUE]] : i32
+// CHECK: cf.cond_br %[[CMP_SLE]], ^[[BB_UPPERBOUND5:bb[0-9]+]], ^bb4
+// CHECK: ^[[BB_UPPERBOUND5]]:
+// Check for the upper bound for the interval `case (4:5)`
+// CHECK: %[[CMP_SLE:.*]] = arith.cmpi sle, %[[SELECT_VALUE]], %[[CST5]] : i32
+// CHECK: cf.cond_br %[[CMP_SLE]], ^[[BB_INTERVAL45:bb[0-9]+]], ^bb4
+// CHECK-LABEL: ^bb4:
+// Check for the point value 7 in `case (7,8:15,21:)`
+// CHECK: %[[CMP_EQ:.*]] = arith.cmpi eq, %[[SELECT_VALUE]], %[[CST7]] : i32
+// CHECK: cf.cond_br %[[CMP_EQ]], ^[[BB_7_8_15_21:bb[0-9]+]], ^bb5
+// CHECK-LABEL: ^bb5:
+// Check for lower bound 8 in `case (7,8:15,21:)`
+// CHECK: %[[CMP_SLE:.*]] = arith.cmpi sle, %[[CST8]], %[[SELECT_VALUE]] : i32
+// CHECK: cf.cond_br %[[CMP_SLE]], ^[[BB_INTERVAL8_15:bb[0-9]+]], ^bb7
+// CHECK: ^[[BB_INTERVAL8_15]]:
+// Check for upper bound 15 in `case (7,8:15,21:)`
+// CHECK: %[[CMP_SLE:.*]] = arith.cmpi sle, %[[SELECT_VALUE]], %[[CST15]] : i32
+// CHECK: cf.cond_br %[[CMP_SLE]], ^[[BB_7_8_15_21]], ^bb7
+// CHECK-LABEL: ^bb7:
+// Check for lower bound 21 in `case (7,8:15,21:)`
+// CHECK: %[[CMP_SLE:.*]] = arith.cmpi sle, %[[CST21]], %[[SELECT_VALUE]] : i32
+// CHECK: cf.cond_br %[[CMP_SLE]], ^[[BB_7_8_15_21]], ^bb8
+// CHECK-LABEL: ^bb8:
+// Default branch (unit case) falls through to the CLASS DEFAULT successor.
+// CHECK: cf.br ^[[BB_DEFAULT:bb[0-9]+]]
+// Each original case successor is preserved in source order after the
+// ladder; each stores its case's constant then falls into the shared
+// join block. The block names captured above are reused so the CFG
+// topology is checked, not just the shape.
+// CHECK: ^[[BB_UPPER]]:
+// CHECK: %[[STORE_UPPER:.*]] = arith.constant 1 : i32
+// CHECK: fir.store %[[STORE_UPPER]] to %[[ARG0]] : !fir.ref<i32>
+// CHECK: cf.br ^[[BB_JOIN:bb[0-9]+]]
+// CHECK: ^[[BB_POINT2]]:
+// CHECK: %[[STORE_POINT2:.*]] = arith.constant 2 : i32
+// CHECK: fir.store %[[STORE_POINT2]] to %[[ARG0]] : !fir.ref<i32>
+// CHECK: cf.br ^[[BB_JOIN]]
+// CHECK: ^[[BB_DEFAULT]]:
+// CHECK: %[[STORE_DEFAULT:.*]] = arith.constant 0 : i32
+// CHECK: fir.store %[[STORE_DEFAULT]] to %[[ARG0]] : !fir.ref<i32>
+// CHECK: cf.br ^[[BB_JOIN]]
+// CHECK: ^[[BB_INTERVAL45]]:
+// CHECK: %[[STORE_INTERVAL45:.*]] = arith.constant 4 : i32
+// CHECK: fir.store %[[STORE_INTERVAL45]] to %[[ARG0]] : !fir.ref<i32>
+// CHECK: cf.br ^[[BB_JOIN]]
+// CHECK: ^[[BB_7_8_15_21]]:
+// CHECK: %[[STORE_7_8_15_21:.*]] = arith.constant 7 : i32
+// CHECK: fir.store %[[STORE_7_8_15_21]] to %[[ARG0]] : !fir.ref<i32>
+// CHECK: cf.br ^[[BB_JOIN]]
+// Join block reads the ref and returns it.
+// CHECK: ^[[BB_JOIN]]:
+// CHECK: %[[RET:.*]] = fir.load %[[ARG0]] : !fir.ref<i32>
+// CHECK: return %[[RET]] : i32
+
+// -----
+
+// Exercises fir.select_case with an i1 selector (converted from a Fortran
+// LOGICAL) and two #fir.point cases plus a unit default.
+
+func.func @select_case_logical(%arg0: !fir.ref<!fir.logical<4>>) {
+ %1 = fir.load %arg0 : !fir.ref<!fir.logical<4>>
+ %2 = fir.convert %1 : (!fir.logical<4>) -> i1
+ %false = arith.constant false
+ %true = arith.constant true
+ fir.select_case %2 : i1 [#fir.point, %false, ^bb1,
+ #fir.point, %true, ^bb2,
+ unit, ^bb3]
+^bb1:
+ %c1_i32 = arith.constant 1 : i32
+ cf.br ^bb3
+^bb2:
+ %c2_i32 = arith.constant 2 : i32
+ cf.br ^bb3
+^bb3:
+ return
+}
+
+// CHECK-LABEL: func.func @select_case_logical(
+// CHECK-SAME: %[[ARG0:.*]]: !fir.ref<!fir.logical<4>>)
+// CHECK: %[[LD:.*]] = fir.load %[[ARG0]] : !fir.ref<!fir.logical<4>>
+// CHECK: %[[SEL:.*]] = fir.convert %[[LD]] : (!fir.logical<4>) -> i1
+// CHECK-DAG: %[[FALSE:.*]] = arith.constant false
+// CHECK-DAG: %[[TRUE:.*]] = arith.constant true
+// CHECK: %[[C0:.*]] = arith.cmpi eq, %[[SEL]], %[[FALSE]] : i1
+// CHECK: cf.cond_br %[[C0]], ^[[BB_FALSE:bb[0-9]+]], ^bb1
+// CHECK-LABEL: ^bb1:
+// CHECK: %[[C1:.*]] = arith.cmpi eq, %[[SEL]], %[[TRUE]] : i1
+// CHECK: cf.cond_br %[[C1]], ^[[BB_TRUE:bb[0-9]+]], ^bb2
+// CHECK-LABEL: ^bb2:
+// Unit-default fall-through.
+// CHECK: cf.br ^[[BB_JOIN:bb[0-9]+]]
+// Two #fir.point successor bodies (each declares its constant then
+// falls into the shared join block), and the join block returns.
+// CHECK: ^[[BB_FALSE]]:
+// CHECK: arith.constant 1 : i32
+// CHECK: cf.br ^[[BB_JOIN]]
+// CHECK: ^[[BB_TRUE]]:
+// CHECK: arith.constant 2 : i32
+// CHECK: cf.br ^[[BB_JOIN]]
+// CHECK: ^[[BB_JOIN]]:
+// CHECK: return
+
+// -----
+
+// Exercises fir.select_case with per-successor block operands: two
+// #fir.point cases pass different `!fir.ref<!fir.array<2xi32>>` values to
+// the same target block. The pass propagates the operand forwarding
+// verbatim; block-argument types are preserved (no type conversion runs).
+
+func.func @select_case_block_args(%arg0: !fir.ref<i32>, %arg1: !fir.ref<!fir.array<2xi32>>, %arg2: !fir.ref<!fir.array<2xi32>>) {
+ %0 = fir.load %arg0 : !fir.ref<i32>
+ %c1 = arith.constant 1 : i32
+ %c2 = arith.constant 2 : i32
+ fir.select_case %0 : i32 [#fir.point, %c1, ^bb1(%arg1 : !fir.ref<!fir.array<2xi32>>),
+ #fir.point, %c2, ^bb1(%arg2 : !fir.ref<!fir.array<2xi32>>),
+ unit, ^bb2]
+^bb1(%1: !fir.ref<!fir.array<2xi32>>):
+ cf.br ^bb2
+^bb2:
+ return
+}
+
+// CHECK-LABEL: func.func @select_case_block_args
+// CHECK: cf.cond_br %{{.*}}, ^{{.*}}(%{{.*}} : !fir.ref<!fir.array<2xi32>>), ^{{.*}}
+// CHECK: ^{{.*}}(%{{.*}}: !fir.ref<!fir.array<2xi32>>):
diff --git a/flang/test/Fir/SelectOpsConversion/select_rank.fir b/flang/test/Fir/SelectOpsConversion/select_rank.fir
new file mode 100644
index 0000000000000..84db7b7efea37
--- /dev/null
+++ b/flang/test/Fir/SelectOpsConversion/select_rank.fir
@@ -0,0 +1,46 @@
+// RUN: fir-opt %s --fir-select-ops-conversion | FileCheck %s
+
+// Exercises fir.select_rank with an i32 selector and successors that
+// forward one, two, or three block arguments, plus a unit default. The
+// rank selector is widened to i64 with `arith.extsi` before `cf.switch`.
+
+func.func @select_rank(%arg : i32, %arg2 : i32) -> i32 {
+ %c1 = arith.constant 1 : i32
+ %c2 = arith.constant 2 : i32
+ %c3 = arith.constant 3 : i32
+ %c4 = arith.constant 4 : i32
+ fir.select_rank %arg:i32 [ 1, ^bb1(%c1:i32),
+ 2, ^bb2(%c3,%arg,%arg2:i32,i32,i32),
+ 3, ^bb3(%arg2,%c3:i32,i32),
+ 4, ^bb4(%c2:i32),
+ unit, ^bb5 ]
+ ^bb1(%a : i32) :
+ return %a : i32
+ ^bb2(%b : i32, %b2 : i32, %b3:i32) :
+ %4 = arith.addi %b, %b2 : i32
+ %5 = arith.addi %4, %b3 : i32
+ return %5 : i32
+ ^bb3(%c:i32, %c2b:i32) :
+ %6 = arith.addi %c, %c2b : i32
+ return %6 : i32
+ ^bb4(%d : i32) :
+ return %d : i32
+ ^bb5 :
+ %zero = arith.constant 0 : i32
+ return %zero : i32
+}
+
+// CHECK-LABEL: func.func @select_rank(
+// CHECK-SAME: %[[SELECTVALUE:.*]]: i32,
+// CHECK-SAME: %[[ARG1:.*]]: i32)
+// CHECK-DAG: %[[C1:.*]] = arith.constant 1 : i32
+// CHECK-DAG: %[[C2:.*]] = arith.constant 2 : i32
+// CHECK-DAG: %[[C3:.*]] = arith.constant 3 : i32
+// CHECK: %[[SELECTOR:.*]] = arith.extsi %[[SELECTVALUE]] : i32 to i64
+// CHECK: cf.switch %[[SELECTOR]] : i64, [
+// CHECK: default: ^{{.*}},
+// CHECK: 1: ^{{.*}}(%[[C1]] : i32),
+// CHECK: 2: ^{{.*}}(%[[C3]], %[[SELECTVALUE]], %[[ARG1]] : i32, i32, i32),
+// CHECK: 3: ^{{.*}}(%[[ARG1]], %[[C3]] : i32, i32),
+// CHECK: 4: ^{{.*}}(%[[C2]] : i32)
+// CHECK: ]
diff --git a/flang/test/Fir/basic-program.fir b/flang/test/Fir/basic-program.fir
index d1e7cdb44d0c7..344379f624f3e 100644
--- a/flang/test/Fir/basic-program.fir
+++ b/flang/test/Fir/basic-program.fir
@@ -119,6 +119,7 @@ func.func @_QQmain() {
// PASSES-NEXT: (S) 0 num-dce'd - Number of operations DCE'd
// PASSES-NEXT: PolymorphicOpConversion
+// PASSES-NEXT: SelectOpsConversion
// PASSES-NEXT: AssumedRankOpConversion
// PASSES-NEXT: 'func.func' Pipeline
// PASSES-NEXT: OptimizeArrayRepacking
diff --git a/flang/test/Fir/select.fir b/flang/test/Fir/select.fir
index b342b7cff1c6a..cde09a78bc788 100644
--- a/flang/test/Fir/select.fir
+++ b/flang/test/Fir/select.fir
@@ -47,10 +47,13 @@ func.func @h(%a : i32) -> i32 {
%b2 = arith.constant 14 : i32
%b3 = arith.constant 82 : i32
%b4 = arith.constant 96 : i32
+ // `sle const, sel` and `sge sel, const` are semantically equivalent; the
+ // full flang pipeline canonicalizes to the latter while tco keeps the
+ // former, so accept both forms for the lower-bound / interval-lo checks.
// CHECK-DAG: icmp eq i32 %{{.*}}, 1
- // CHECK-DAG: icmp sle i32 4, %{{.*}}
+ // CHECK-DAG: icmp {{sle i32 4, %.*|sge i32 %.*, 4}}
// CHECK-DAG: icmp sle i32 %{{.*}}, 14
- // CHECK-DAG: icmp sle i32 82, %{{.*}}
+ // CHECK-DAG: icmp {{sle i32 82, %.*|sge i32 %.*, 82}}
// CHECK-DAG: icmp sle i32 %{{.*}}, 96
fir.select_case %a : i32 [#fir.point, %1, ^bb2(%1:i32), #fir.lower, %b1, ^bb4, #fir.upper, %b2, ^bb6, #fir.interval, %b3, %b4, ^bb5, unit, ^bb3(%2:i32)]
^bb2(%3 : i32) :
@@ -76,8 +79,11 @@ func.func @empty_cases(%arg0 : i32) {
}
func.func private @crash()
+// The empty-cases fir.select has only a `unit` default: no switch is emitted.
+// The `tco` path preserves the intermediate `br label %B; B:` block, whereas
+// the full flang pipeline canonicalizes the trivial cf.br away and drops
+// straight into `call @crash; unreachable`. Either shape is acceptable.
//CHECK-LABEL: @empty_cases(i32 %0) {
-//CHECK: br label %[[BLOCK:.*]]
-//CHECK: [[BLOCK]]:
-//CHECK: call void @crash()
-//CHECK: unreachable
+//CHECK-NOT: switch
+//CHECK: call void @crash()
+//CHECK-NEXT: unreachable
diff --git a/flang/test/Lower/volatile3.f90 b/flang/test/Lower/volatile3.f90
index 06bcbfeee42fa..8afb8a70af3a9 100644
--- a/flang/test/Lower/volatile3.f90
+++ b/flang/test/Lower/volatile3.f90
@@ -247,22 +247,28 @@ subroutine sub_select_rank(arr)
! CHECK: %[[VOLATILE_CAST_1:.*]] = fir.volatile_cast %[[DECLARE_0]]#0 : (!fir.box<!fir.array<*:i32>, volatile>) -> !fir.box<!fir.array<*:i32>>
! CHECK: %[[CONVERT_0:.*]] = fir.convert %[[VOLATILE_CAST_1]] : (!fir.box<!fir.array<*:i32>>) -> !fir.box<none>
! CHECK: %[[CALL_0:.*]] = fir.call @_FortranAIsAssumedSize(%[[CONVERT_0]]) : (!fir.box<none>) -> i1
-! CHECK: cf.cond_br %[[CALL_0]], ^bb4, ^bb1
+! CHECK: cf.cond_br %[[CALL_0]], ^bb5, ^bb1
! CHECK: ^bb1:
! CHECK: %[[BOX_RANK_0:.*]] = fir.box_rank %[[DECLARE_0]]#0 : (!fir.box<!fir.array<*:i32>, volatile>) -> i8
-! CHECK: fir.select_case %[[BOX_RANK_0]] : i8 [#fir.point, %[[CONSTANT_3]], ^bb2, #fir.point, %[[CONSTANT_2]], ^bb3, unit, ^bb4]
+! `fir.select_case` for rank dispatch is now expanded into an arith.cmpi +
+! cf.cond_br ladder by --fir-select-ops-conversion during the pipeline.
+! CHECK: %[[CMP_R1:.*]] = arith.cmpi eq, %[[BOX_RANK_0]], %[[CONSTANT_3]] : i8
+! CHECK: cf.cond_br %[[CMP_R1]], ^bb3, ^bb2
! CHECK: ^bb2:
+! CHECK: %[[CMP_R4:.*]] = arith.cmpi eq, %[[BOX_RANK_0]], %[[CONSTANT_2]] : i8
+! CHECK: cf.cond_br %[[CMP_R4]], ^bb4, ^bb5
+! CHECK: ^bb3:
! CHECK: %[[CONVERT_1:.*]] = fir.convert %[[DECLARE_0]]#0 : (!fir.box<!fir.array<*:i32>, volatile>) -> !fir.box<!fir.array<?xi32>, volatile>
! CHECK: %[[DECLARE_1:.*]]:2 = hlfir.declare %[[CONVERT_1]] {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "_QFFsub_select_rankEarr"} : (!fir.box<!fir.array<?xi32>, volatile>) -> (!fir.box<!fir.array<?xi32>, volatile>, !fir.box<!fir.array<?xi32>, volatile>)
! CHECK: %[[DESIGNATE_0:.*]] = hlfir.designate %[[DECLARE_1]]#0 (%[[CONSTANT_0]]) : (!fir.box<!fir.array<?xi32>, volatile>, index) -> !fir.ref<i32, volatile>
! CHECK: hlfir.assign %[[CONSTANT_1]] to %[[DESIGNATE_0]] : i32, !fir.ref<i32, volatile>
-! CHECK: cf.br ^bb4
-! CHECK: ^bb3:
+! CHECK: cf.br ^bb5
+! CHECK: ^bb4:
! CHECK: %[[CONVERT_2:.*]] = fir.convert %[[DECLARE_0]]#0 : (!fir.box<!fir.array<*:i32>, volatile>) -> !fir.box<!fir.array<?x?x?x?xi32>, volatile>
! CHECK: %[[DECLARE_2:.*]]:2 = hlfir.declare %[[CONVERT_2]] {fortran_attrs = #fir.var_attrs<volatile>, uniq_name = "_QFFsub_select_rankEarr"} : (!fir.box<!fir.array<?x?x?x?xi32>, volatile>) -> (!fir.box<!fir.array<?x?x?x?xi32>, volatile>, !fir.box<!fir.array<?x?x?x?xi32>, volatile>)
! CHECK: %[[DESIGNATE_1:.*]] = hlfir.designate %[[DECLARE_2]]#0 (%[[CONSTANT_0]], %[[CONSTANT_0]], %[[CONSTANT_0]], %[[CONSTANT_0]]) : (!fir.box<!fir.array<?x?x?x?xi32>, volatile>, index, index, index, index) -> !fir.ref<i32, volatile>
! CHECK: hlfir.assign %[[CONSTANT_1]] to %[[DESIGNATE_1]] : i32, !fir.ref<i32, volatile>
-! CHECK: cf.br ^bb4
-! CHECK: ^bb4:
+! CHECK: cf.br ^bb5
+! CHECK: ^bb5:
! CHECK: return
! CHECK: }
More information about the flang-commits
mailing list