[flang][CodeGen] Replace fir.select* FIR-to-LLVM patterns with stubs that emit conversion errors (#212978)

`fir.select`, `fir.select_case`, `fir.select_rank`, and
`fir.select_type` are lowered to cf.* earlier in the pipeline
(`--fir-select-ops-conversion` and `--fir-polymorphic-op`). Their
FIR-to-LLVM conversion patterns are dead in a correct pipeline. Replace
them with a single templated stub
`SelectShouldHaveBeenConvertedStub<OP>` that emits `"'fir.<op>' op
should have already been converted"` and fails legalization, so running
`--fir-to-llvm-ir` standalone on stale IR reports a clear diagnostic
instead of "unable to legalize".

`Fir/convert-to-llvm.fir`'s six select* test blocks are removed (the
lowering no longer runs; CF-level coverage lives in
`Fir/SelectOpsConversion/`). `Fir/convert-to-llvm-invalid.fir` gains a
stub-error test per op. `Fir/Todo/select_case_with_character.fir` is
retargeted to check the equivalent diagnostic now emitted by
`--fir-select-ops-conversion`.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

PR Stack:
* https://github.com/llvm/llvm-project/pull/212977
* ▶️ https://github.com/llvm/llvm-project/pull/212978

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kareem Ergawy 2026-07-31 09:26:26 +02:00 committed by GitHub
parent bcf20310bd
commit 5eb4272b04
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 61 additions and 582 deletions

View file

@ -101,13 +101,6 @@ static inline mlir::Type getI8Type(mlir::MLIRContext *context) {
return mlir::IntegerType::get(context, 8);
}
static mlir::Block *createBlock(mlir::ConversionPatternRewriter &rewriter,
mlir::Block *insertBefore) {
assert(insertBefore && "expected valid insertion block");
return rewriter.createBlock(insertBefore->getParent(),
mlir::Region::iterator(insertBefore));
}
/// Extract constant from a value that must be the result of one of the
/// ConstantOp operations.
static int64_t getConstantIntValue(mlir::Value val) {
@ -3972,272 +3965,32 @@ struct ModuleDebugImportsOpConversion
}
};
static void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
std::optional<mlir::ValueRange> destOps,
mlir::ConversionPatternRewriter &rewriter,
mlir::Block *newBlock) {
if (destOps)
mlir::LLVM::CondBrOp::create(rewriter, loc, cmp, dest, *destOps, newBlock,
mlir::ValueRange());
else
mlir::LLVM::CondBrOp::create(rewriter, loc, cmp, dest, newBlock);
}
template <typename A, typename B>
static void genBrOp(A caseOp, mlir::Block *dest, std::optional<B> destOps,
mlir::ConversionPatternRewriter &rewriter) {
if (destOps)
rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, *destOps, dest);
else
rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, B{}, dest);
}
static void genCaseLadderStep(mlir::Location loc, mlir::Value cmp,
mlir::Block *dest,
std::optional<mlir::ValueRange> destOps,
mlir::ConversionPatternRewriter &rewriter) {
auto *thisBlock = rewriter.getInsertionBlock();
auto *newBlock = createBlock(rewriter, dest);
rewriter.setInsertionPointToEnd(thisBlock);
genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
rewriter.setInsertionPointToEnd(newBlock);
}
/// Conversion of `fir.select_case`
///
/// The `fir.select_case` operation is converted to a if-then-else ladder.
/// Depending on the case condition type, one or several comparison and
/// conditional branching can be generated.
///
/// A point value case such as `case(4)`, a lower bound case such as
/// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
/// simple comparison between the selector value and the constant value in the
/// case. The block associated with the case condition is then executed if
/// the comparison succeed otherwise it branch to the next block with the
/// comparison for the next case conditon.
///
/// A closed interval case condition such as `case(7:10)` is converted with a
/// first comparison and conditional branching for the lower bound. If
/// successful, it branch to a second block with the comparison for the
/// upper bound in the same case condition.
///
/// TODO: lowering of CHARACTER type cases is not handled yet.
struct SelectCaseOpConversion : public fir::FIROpConversion<fir::SelectCaseOp> {
using FIROpConversion::FIROpConversion;
llvm::LogicalResult
matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
unsigned conds = caseOp.getNumConditions();
llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
// Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
auto ty = caseOp.getSelector().getType();
if (mlir::isa<fir::CharacterType>(ty)) {
TODO(caseOp.getLoc(), "fir.select_case codegen with character type");
return mlir::failure();
}
mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
auto loc = caseOp.getLoc();
for (unsigned t = 0; t != conds; ++t) {
mlir::Block *dest = caseOp.getSuccessor(t);
std::optional<mlir::ValueRange> destOps =
caseOp.getSuccessorOperands(adaptor.getOperands(), t);
// Convert block signature if needed
if (destOps && !destOps->empty())
if (auto conversion = getTypeConverter()->convertBlockSignature(dest))
dest = rewriter.applySignatureConversion(dest, *conversion,
getTypeConverter());
std::optional<mlir::ValueRange> cmpOps =
*caseOp.getCompareOperands(adaptor.getOperands(), t);
mlir::Attribute attr = cases[t];
assert(mlir::isa<mlir::UnitAttr>(attr) || cmpOps.has_value());
if (mlir::isa<fir::PointIntervalAttr>(attr)) {
auto cmp = mlir::LLVM::ICmpOp::create(rewriter, loc,
mlir::LLVM::ICmpPredicate::eq,
selector, cmpOps->front());
genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
continue;
}
if (mlir::isa<fir::LowerBoundAttr>(attr)) {
auto cmp = mlir::LLVM::ICmpOp::create(rewriter, loc,
mlir::LLVM::ICmpPredicate::sle,
cmpOps->front(), selector);
genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
continue;
}
if (mlir::isa<fir::UpperBoundAttr>(attr)) {
auto cmp = mlir::LLVM::ICmpOp::create(rewriter, loc,
mlir::LLVM::ICmpPredicate::sle,
selector, cmpOps->front());
genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
continue;
}
if (mlir::isa<fir::ClosedIntervalAttr>(attr)) {
mlir::Value caseArg0 = *cmpOps->begin();
auto cmp0 = mlir::LLVM::ICmpOp::create(
rewriter, loc, mlir::LLVM::ICmpPredicate::sle, caseArg0, selector);
auto *thisBlock = rewriter.getInsertionBlock();
auto *newBlock1 = createBlock(rewriter, dest);
auto *newBlock2 = createBlock(rewriter, dest);
rewriter.setInsertionPointToEnd(thisBlock);
mlir::LLVM::CondBrOp::create(rewriter, loc, cmp0, newBlock1, newBlock2);
rewriter.setInsertionPointToEnd(newBlock1);
mlir::Value caseArg1 = *(cmpOps->begin() + 1);
auto cmp1 = mlir::LLVM::ICmpOp::create(
rewriter, loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg1);
genCondBrOp(loc, cmp1, dest, destOps, rewriter, newBlock2);
rewriter.setInsertionPointToEnd(newBlock2);
continue;
}
assert(mlir::isa<mlir::UnitAttr>(attr));
assert((t + 1 == conds) && "unit must be last");
genBrOp(caseOp, dest, destOps, rewriter);
}
return mlir::success();
}
};
/// Base class for SelectOpConversion and SelectRankOpConversion.
/// Defensive stub. `fir.select`, `fir.select_case`, and `fir.select_rank` are
/// lowered to `cf.switch` / `cf.cond_br` by `--fir-select-ops-conversion`
/// earlier in the pipeline; `fir.select_type` is lowered to a cf-based
/// if-then-else ladder by `--fir-polymorphic-op`. If any of these ops
/// reaches FIR-to-LLVM, we emit a clear diagnostic rather than an opaque
/// "unable to legalize" error.
template <typename OP>
struct SelectOpConversionBase : public fir::FIROpConversion<OP> {
struct SelectShouldHaveBeenConvertedStub : public fir::FIROpConversion<OP> {
using fir::FIROpConversion<OP>::FIROpConversion;
private:
/// Helper function for converting select ops. This function converts the
/// signature of the given block. If the new block signature is different from
/// `expectedTypes`, returns "failure".
llvm::FailureOr<mlir::Block *>
getConvertedBlock(mlir::ConversionPatternRewriter &rewriter,
mlir::Operation *branchOp, mlir::Block *block,
mlir::TypeRange expectedTypes) const {
const mlir::TypeConverter *converter = this->getTypeConverter();
assert(converter && "expected non-null type converter");
assert(!block->isEntryBlock() && "entry blocks have no predecessors");
// There is nothing to do if the types already match.
if (block->getArgumentTypes() == expectedTypes)
return block;
// Compute the new block argument types and convert the block.
std::optional<mlir::TypeConverter::SignatureConversion> conversion =
converter->convertBlockSignature(block);
if (!conversion)
return rewriter.notifyMatchFailure(branchOp,
"could not compute block signature");
if (expectedTypes != conversion->getConvertedTypes())
return rewriter.notifyMatchFailure(branchOp,
"mismatch between adaptor operand "
"types and computed block signature");
return rewriter.applySignatureConversion(block, *conversion, converter);
}
protected:
llvm::LogicalResult
selectMatchAndRewrite(OP select, typename OP::Adaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
unsigned conds = select.getNumConditions();
auto cases = select.getCases().getValue();
mlir::Value selector = adaptor.getSelector();
auto loc = select.getLoc();
assert(conds > 0 && "select must have cases");
llvm::SmallVector<mlir::Block *> destinations;
llvm::SmallVector<mlir::ValueRange> destinationsOperands;
mlir::Block *defaultDestination;
mlir::ValueRange defaultOperands;
// LLVM::SwitchOp selector type and the case values types
// must have the same bit width, so cast the selector to i64,
// and use i64 for the case values. It is hard to imagine
// a computed GO TO with the number of labels in the label-list
// bigger than INT_MAX, but let's use i64 to be on the safe side.
// Moreover, fir.select operation is more relaxed than
// a Fortran computed GO TO, so it may specify such a case value
// even if there is just a single label/case.
llvm::SmallVector<int64_t> caseValues;
for (unsigned t = 0; t != conds; ++t) {
mlir::Block *dest = select.getSuccessor(t);
auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
const mlir::Attribute &attr = cases[t];
if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(attr)) {
destinationsOperands.push_back(destOps ? *destOps : mlir::ValueRange{});
auto convertedBlock =
getConvertedBlock(rewriter, select, dest,
mlir::TypeRange(destinationsOperands.back()));
if (mlir::failed(convertedBlock))
return mlir::failure();
destinations.push_back(*convertedBlock);
caseValues.push_back(intAttr.getInt());
continue;
}
assert(mlir::dyn_cast_or_null<mlir::UnitAttr>(attr));
assert((t + 1 == conds) && "unit must be last");
defaultOperands = destOps ? *destOps : mlir::ValueRange{};
auto convertedBlock = getConvertedBlock(rewriter, select, dest,
mlir::TypeRange(defaultOperands));
if (mlir::failed(convertedBlock))
return mlir::failure();
defaultDestination = *convertedBlock;
}
// Deal with the case where there is only a default destination. Handle it
// now because emitting empty case values is not legal.
if (caseValues.empty()) {
rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(select, defaultOperands,
defaultDestination);
return mlir::success();
}
selector =
this->integerCast(loc, rewriter, rewriter.getI64Type(), selector);
rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
select, selector,
/*defaultDestination=*/defaultDestination,
/*defaultOperands=*/defaultOperands,
/*caseValues=*/rewriter.getI64VectorAttr(caseValues),
/*caseDestinations=*/destinations,
/*caseOperands=*/destinationsOperands,
/*branchWeights=*/llvm::ArrayRef<std::int32_t>());
return mlir::success();
}
};
/// conversion of fir::SelectOp to an if-then-else ladder
struct SelectOpConversion : public SelectOpConversionBase<fir::SelectOp> {
using SelectOpConversionBase::SelectOpConversionBase;
llvm::LogicalResult
matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
return this->selectMatchAndRewrite(op, adaptor, rewriter);
}
};
/// conversion of fir::SelectRankOp to an if-then-else ladder
struct SelectRankOpConversion
: public SelectOpConversionBase<fir::SelectRankOp> {
using SelectOpConversionBase::SelectOpConversionBase;
llvm::LogicalResult
matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
return this->selectMatchAndRewrite(op, adaptor, rewriter);
}
};
/// Lower `fir.select_type` to LLVM IR dialect.
struct SelectTypeOpConversion : public fir::FIROpConversion<fir::SelectTypeOp> {
using FIROpConversion::FIROpConversion;
llvm::LogicalResult
matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
mlir::emitError(select.getLoc(),
"fir.select_type should have already been converted");
llvm::LogicalResult matchAndRewrite(
OP op, typename OP::Adaptor /*adaptor*/,
mlir::ConversionPatternRewriter & /*rewriter*/) const override {
op.emitOpError("should have already been converted");
return mlir::failure();
}
};
using SelectOpConversion = SelectShouldHaveBeenConvertedStub<fir::SelectOp>;
using SelectCaseOpConversion =
SelectShouldHaveBeenConvertedStub<fir::SelectCaseOp>;
using SelectRankOpConversion =
SelectShouldHaveBeenConvertedStub<fir::SelectRankOp>;
using SelectTypeOpConversion =
SelectShouldHaveBeenConvertedStub<fir::SelectTypeOp>;
/// `fir.store` --> `llvm.store`
struct StoreOpConversion : public fir::FIROpConversion<fir::StoreOp> {
using FIROpConversion::FIROpConversion;

View file

@ -1,4 +1,4 @@
// RUN: %not_todo_cmd fir-opt --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu" %s 2>&1 | FileCheck %s
// RUN: %not_todo_cmd fir-opt --fir-select-ops-conversion %s 2>&1 | FileCheck %s
// Test `fir.select_case` conversion to llvm with character type.
// Not implemented yet.

View file

@ -27,13 +27,50 @@ func.func @shift_not_dead(%arg0: !fir.box<!fir.array<?xf32>>, %i: index) {
// -----
// Test `fir.select_type` conversion to llvm.
// Should have been converted.
// The four fir.select* terminators are lowered to cf.* earlier in the
// pipeline (--fir-select-ops-conversion for select/select_case/select_rank,
// --fir-polymorphic-op for select_type). Any that reach FIR-to-LLVM hit a
// defensive stub that emits a clear "should have already been converted"
// diagnostic.
func.func @bar_select_type(%arg : !fir.class<!fir.type<derivedst{a:f32}>>) -> i32 {
func.func @select_should_have_been_converted(%arg: i32) -> i32 {
%0 = arith.constant 1 : i32
// expected-error@+2{{'fir.select' op should have already been converted}}
// expected-error@+1{{failed to legalize operation 'fir.select'}}
fir.select %arg : i32 [1, ^bb1(%0:i32), unit, ^bb1(%0:i32)]
^bb1(%a: i32):
return %a : i32
}
// -----
func.func @select_case_should_have_been_converted(%arg: !fir.ref<i32>) -> i32 {
%v = fir.load %arg : !fir.ref<i32>
%c1 = arith.constant 1 : i32
// expected-error@+2{{'fir.select_case' op should have already been converted}}
// expected-error@+1{{failed to legalize operation 'fir.select_case'}}
fir.select_case %v : i32 [#fir.point, %c1, ^bb1, unit, ^bb1]
^bb1:
return %v : i32
}
// -----
func.func @select_rank_should_have_been_converted(%arg: i32) -> i32 {
%0 = arith.constant 1 : i32
// expected-error@+2{{'fir.select_rank' op should have already been converted}}
// expected-error@+1{{failed to legalize operation 'fir.select_rank'}}
fir.select_rank %arg : i32 [1, ^bb1(%0:i32), unit, ^bb1(%0:i32)]
^bb1(%a: i32):
return %a : i32
}
// -----
func.func @select_type_should_have_been_converted(%arg: !fir.class<!fir.type<derivedst{a:f32}>>) -> i32 {
%0 = arith.constant 1 : i32
%2 = arith.constant 3 : i32
// expected-error@+2{{fir.select_type should have already been converted}}
// expected-error@+2{{'fir.select_type' op should have already been converted}}
// expected-error@+1{{failed to legalize operation 'fir.select_type'}}
fir.select_type %arg : !fir.class<!fir.type<derivedst{a:f32}>> [
#fir.type_is<!fir.int<4>>,^bb1(%0:i32),

View file

@ -326,98 +326,6 @@ func.func @test_unreachable() {
// -----
// Test `fir.select` operation conversion pattern.
// Check that the if-then-else ladder is correctly constructed and that we
// branch to the correct block.
func.func @select(%arg : index, %arg2 : i32) -> i32 {
%0 = arith.constant 1 : i32
%1 = arith.constant 2 : i32
%2 = arith.constant 3 : i32
%3 = arith.constant 4 : i32
fir.select %arg:index [ 1, ^bb1(%0:i32),
2, ^bb2(%2,%arg,%arg2:i32,index,i32),
3, ^bb3(%arg2,%2:i32,i32),
4, ^bb4(%1: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, %c2:i32) :
%6 = arith.addi %c, %c2 : i32
return %6 : i32
^bb4(%d : i32) :
return %d : i32
^bb5 :
%zero = arith.constant 0 : i32
return %zero : i32
}
// CHECK-LABEL: func @select(
// CHECK-SAME: %[[SELECTVALUE:.*]]: [[IDX:.*]],
// CHECK-SAME: %[[ARG1:.*]]: i32)
// CHECK: %[[C0:.*]] = llvm.mlir.constant(1 : i32) : i32
// CHECK: %[[C1:.*]] = llvm.mlir.constant(2 : i32) : i32
// CHECK: %[[C2:.*]] = llvm.mlir.constant(3 : i32) : i32
// CHECK: llvm.switch %[[SELECTVALUE]] : i64, ^bb5 [
// CHECK: 1: ^bb1(%[[C0]] : i32),
// CHECK: 2: ^bb2(%[[C2]], %[[SELECTVALUE]], %[[ARG1]] : i32, [[IDX]], i32),
// CHECK: 3: ^bb3(%[[ARG1]], %[[C2]] : i32, i32),
// CHECK: 4: ^bb4(%[[C1]] : i32)
// CHECK: ]
// -----
// Test `fir.select_rank` operation conversion pattern.
// Check that the if-then-else ladder is correctly constructed and that we
// branch to the correct block.
func.func @select_rank(%arg : i32, %arg2 : i32) -> i32 {
%0 = arith.constant 1 : i32
%1 = arith.constant 2 : i32
%2 = arith.constant 3 : i32
%3 = arith.constant 4 : i32
fir.select_rank %arg:i32 [ 1, ^bb1(%0:i32),
2, ^bb2(%2,%arg,%arg2:i32,i32,i32),
3, ^bb3(%arg2,%2:i32,i32),
4, ^bb4(%1: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, %c2:i32) :
%6 = arith.addi %c, %c2 : i32
return %6 : i32
^bb4(%d : i32) :
return %d : i32
^bb5 :
%zero = arith.constant 0 : i32
return %zero : i32
}
// CHECK-LABEL: func @select_rank(
// CHECK-SAME: %[[SELECTVALUE:.*]]: i32,
// CHECK-SAME: %[[ARG1:.*]]: i32)
// CHECK: %[[C0:.*]] = llvm.mlir.constant(1 : i32) : i32
// CHECK: %[[C1:.*]] = llvm.mlir.constant(2 : i32) : i32
// CHECK: %[[C2:.*]] = llvm.mlir.constant(3 : i32) : i32
// CHECK: %[[SELECTOR:.*]] = llvm.sext %[[SELECTVALUE]] : i{{.*}} to i64
// CHECK: llvm.switch %[[SELECTOR]] : i64, ^bb5 [
// CHECK: 1: ^bb1(%[[C0]] : i32),
// CHECK: 2: ^bb2(%[[C2]], %[[SELECTVALUE]], %[[ARG1]] : i32, i32, i32),
// CHECK: 3: ^bb3(%[[ARG1]], %[[C2]] : i32, i32),
// CHECK: 4: ^bb4(%[[C1]] : i32)
// CHECK: ]
// -----
// Test fir.extract_value operation conversion with derived type.
func.func @extract_derived_type() -> f32 {
@ -1366,159 +1274,6 @@ func.func @alloca_assumed_rank_box() -> !fir.ref<!fir.box<!fir.array<*:f32>>> {
// -----
// Test `fir.select_case` operation conversion with INTEGER.
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: llvm.func @select_case_integer(
// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr) -> i32 {
// CHECK: %[[SELECT_VALUE:.*]] = llvm.load %[[ARG0]] : !llvm.ptr -> i32
// CHECK: %[[CST1:.*]] = llvm.mlir.constant(1 : i32) : i32
// CHECK: %[[CST2:.*]] = llvm.mlir.constant(2 : i32) : i32
// CHECK: %[[CST4:.*]] = llvm.mlir.constant(4 : i32) : i32
// CHECK: %[[CST5:.*]] = llvm.mlir.constant(5 : i32) : i32
// CHECK: %[[CST7:.*]] = llvm.mlir.constant(7 : i32) : i32
// CHECK: %[[CST8:.*]] = llvm.mlir.constant(8 : i32) : i32
// CHECK: %[[CST15:.*]] = llvm.mlir.constant(15 : i32) : i32
// CHECK: %[[CST21:.*]] = llvm.mlir.constant(21 : i32) : i32
// Check for upper bound `case (:1)`
// CHECK: %[[CMP_SLE:.*]] = llvm.icmp "sle" %[[SELECT_VALUE]], %[[CST1]] : i32
// CHECK: llvm.cond_br %[[CMP_SLE]], ^bb2, ^bb1
// CHECK-LABEL: ^bb1:
// Check for point value `case (2)`
// CHECK: %[[CMP_EQ:.*]] = llvm.icmp "eq" %[[SELECT_VALUE]], %[[CST2]] : i32
// CHECK: llvm.cond_br %[[CMP_EQ]], ^bb4, ^bb3
// Block ^bb1 in original FIR code.
// CHECK-LABEL: ^bb2:
// CHECK: llvm.br ^bb{{.*}}
// CHECK-LABEL: ^bb3:
// Check for the lower bound for the interval `case (4:5)`
// CHECK: %[[CMP_SLE:.*]] = llvm.icmp "sle" %[[CST4]], %[[SELECT_VALUE]] : i32
// CHECK: llvm.cond_br %[[CMP_SLE]], ^bb[[UPPERBOUND5:.*]], ^bb7
// Block ^bb2 in original FIR code.
// CHECK-LABEL: ^bb4:
// CHECK: llvm.br ^bb{{.*}}
// Block ^bb3 in original FIR code.
// CHECK-LABEL: ^bb5:
// CHECK: llvm.br ^bb{{.*}}
// CHECK: ^bb[[UPPERBOUND5]]:
// Check for the upper bound for the interval `case (4:5)`
// CHECK: %[[CMP_SLE:.*]] = llvm.icmp "sle" %[[SELECT_VALUE]], %[[CST5]] : i32
// CHECK: llvm.cond_br %[[CMP_SLE]], ^bb8, ^bb7
// CHECK-LABEL: ^bb7:
// Check for the point value 7 in `case (7,8:15,21:)`
// CHECK: %[[CMP_EQ:.*]] = llvm.icmp "eq" %[[SELECT_VALUE]], %[[CST7]] : i32
// CHECK: llvm.cond_br %[[CMP_EQ]], ^bb13, ^bb9
// Block ^bb4 in original FIR code.
// CHECK-LABEL: ^bb8:
// CHECK: llvm.br ^bb{{.*}}
// CHECK-LABEL: ^bb9:
// Check for lower bound 8 in `case (7,8:15,21:)`
// CHECK: %[[CMP_SLE:.*]] = llvm.icmp "sle" %[[CST8]], %[[SELECT_VALUE]] : i32
// CHECK: llvm.cond_br %[[CMP_SLE]], ^bb[[INTERVAL8_15:.*]], ^bb11
// CHECK: ^bb[[INTERVAL8_15]]:
// Check for upper bound 15 in `case (7,8:15,21:)`
// CHECK: %[[CMP_SLE:.*]] = llvm.icmp "sle" %[[SELECT_VALUE]], %[[CST15]] : i32
// CHECK: llvm.cond_br %[[CMP_SLE]], ^bb13, ^bb11
// CHECK-LABEL: ^bb11:
// Check for lower bound 21 in `case (7,8:15,21:)`
// CHECK: %[[CMP_SLE:.*]] = llvm.icmp "sle" %[[CST21]], %[[SELECT_VALUE]] : i32
// CHECK: llvm.cond_br %[[CMP_SLE]], ^bb13, ^bb12
// CHECK-LABEL: ^bb12:
// CHECK: llvm.br ^bb5
// Block ^bb5 in original FIR code.
// CHECK-LABEL: ^bb13:
// CHECK: llvm.br ^bb14
// Block ^bb6 in original FIR code.
// CHECK-LABEL: ^bb14:
// CHECK: %[[RET:.*]] = llvm.load %[[ARG0:.*]] : !llvm.ptr -> i32
// CHECK: llvm.return %[[RET]] : i32
// -----
// Test `fir.select_case` operation conversion with LOGICAL.
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: llvm.func @select_case_logical(
// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr
// CHECK: %[[LOAD_ARG0:.*]] = llvm.load %[[ARG0]] : !llvm.ptr -> i32
// CHECK: %[[CST_ZERO:.*]] = llvm.mlir.constant(0 : i32) : i32
// CHECK: %[[SELECT_VALUE:.*]] = llvm.icmp "ne" %[[LOAD_ARG0]], %[[CST_ZERO]] : i32
// CHECK: %[[CST_FALSE:.*]] = llvm.mlir.constant(false) : i1
// CHECK: %[[CST_TRUE:.*]] = llvm.mlir.constant(true) : i1
// CHECK: %[[CMPEQ:.*]] = llvm.icmp "eq" %[[SELECT_VALUE]], %[[CST_FALSE]] : i1
// CHECK: llvm.cond_br %[[CMPEQ]], ^bb2, ^bb1
// CHECK-LABEL: ^bb1:
// CHECK: %[[CMPEQ:.*]] = llvm.icmp "eq" %[[SELECT_VALUE]], %[[CST_TRUE]] : i1
// CHECK: llvm.cond_br %[[CMPEQ]], ^bb4, ^bb3
// CHECK-LABEL: ^bb2:
// CHECK: llvm.br ^bb5
// CHECK-LABEL: ^bb3:
// CHECK: llvm.br ^bb5
// CHECK-LABEL: ^bb4:
// CHECK: llvm.br ^bb5
// CHECK-LABEL: ^bb5:
// CHECK: llvm.return
// -----
// Test `fir.is_present`
func.func @test_is_present_i64(%arg0: !fir.ref<i64>) -> () {
@ -2929,72 +2684,6 @@ func.func @test_sret(%arg0: (!fir.ref<!fir.type<t{a:!fir.array<5xf64>}>>, f64) -
// -----
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: llvm.func @select_with_cast(
// CHECK-SAME: %[[ARG0:.*]]: i8,
// CHECK-SAME: %[[ARG1:.*]]: i16,
// CHECK-SAME: %[[ARG2:.*]]: i64,
// CHECK-SAME: %[[ARG3:.*]]: i64) {
// CHECK: %[[VAL_0:.*]] = llvm.sext %[[ARG0]] : i8 to i64
// CHECK: llvm.switch %[[VAL_0]] : i64, ^bb1 [
// CHECK: 1: ^bb1
// CHECK: ]
// CHECK: ^bb1:
// CHECK: %[[VAL_1:.*]] = llvm.sext %[[ARG1]] : i16 to i64
// CHECK: llvm.switch %[[VAL_1]] : i64, ^bb2 [
// CHECK: 1: ^bb2
// CHECK: ]
// CHECK: ^bb2:
// CHECK: llvm.switch %[[ARG2]] : i64, ^bb3 [
// CHECK: 1: ^bb3
// CHECK: ]
// CHECK: ^bb3:
// CHECK: llvm.switch %[[ARG3]] : i64, ^bb4 [
// CHECK: 1: ^bb4
// CHECK: ]
// CHECK: ^bb4:
// CHECK: llvm.switch %[[ARG2]] : i64, ^bb5 [
// CHECK: 4294967296: ^bb5
// CHECK: ]
// CHECK: ^bb5:
// CHECK: llvm.return
// CHECK: }
// -----
// Test `fir.select_case` with block arguments requiring type conversion.
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: llvm.func @select_case_block_args
// CHECK: llvm.cond_br %{{.*}}, ^{{.*}}(%{{.*}} : !llvm.ptr), ^{{.*}}
// CHECK: ^{{.*}}(%{{.*}}: !llvm.ptr):
// -----
// Check memcpy alignment
func.func @test_load_box_alignment(%addr : !fir.ref<!fir.box<!fir.array<10xf32>>>) {