[flang-commits] [flang] [flang][Transforms] Add `SelectOpsConversion` pass (PR #212977)

Kareem Ergawy via flang-commits flang-commits at lists.llvm.org
Thu Jul 30 06:57:43 PDT 2026


================
@@ -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
----------------
ergawy wrote:

Done.

https://github.com/llvm/llvm-project/pull/212977


More information about the flang-commits mailing list