[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