[flang-commits] [flang] [flang][Transforms] Add `SelectOpsConversion` pass (PR #212977)
via flang-commits
flang-commits at lists.llvm.org
Thu Jul 30 02:51:04 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-driver
Author: Kareem Ergawy (ergawy)
<details>
<summary>Changes</summary>
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.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@<!-- -->anthropic.com>
---
Patch is 36.70 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/212977.diff
13 Files Affected:
- (modified) flang/include/flang/Optimizer/Transforms/Passes.td (+18-2)
- (modified) flang/lib/Optimizer/Passes/Pipelines.cpp (+1)
- (modified) flang/lib/Optimizer/Transforms/CMakeLists.txt (+1)
- (added) flang/lib/Optimizer/Transforms/SelectOpsConversion.cpp (+284)
- (modified) flang/test/Driver/bbc-mlir-pass-pipeline.f90 (+1)
- (modified) flang/test/Driver/mlir-debug-pass-pipeline.f90 (+1)
- (modified) flang/test/Driver/mlir-pass-pipeline.f90 (+1)
- (added) flang/test/Fir/SelectOpsConversion/select.fir (+113)
- (added) flang/test/Fir/SelectOpsConversion/select_case.fir (+192)
- (added) flang/test/Fir/SelectOpsConversion/select_rank.fir (+46)
- (modified) flang/test/Fir/basic-program.fir (+1)
- (modified) flang/test/Fir/select.fir (+12-6)
- (modified) flang/test/Lower/volatile3.f90 (+12-6)
``````````diff
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, [
+// CHEC...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/212977
More information about the flang-commits
mailing list