[Mlir-commits] [mlir] [MLIR][LLVM] Support for indirectbr (PR #135092)
Bruno Cardoso Lopes
llvmlistbot at llvm.org
Fri Apr 11 18:57:51 PDT 2025
https://github.com/bcardosolopes updated https://github.com/llvm/llvm-project/pull/135092
>From ad1bad2be83cdc4ef45823247deac72b937e107a Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Tue, 8 Apr 2025 11:19:40 -0700
Subject: [PATCH 1/4] [MLIR][LLVM] Support for indirectbr
Now that LLVM dialect has blockaddress support, introduce import/translation
for indirectbr intruction.
---
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 59 +++++++++++++
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 58 +++++++++++++
.../LLVMIR/LLVMToLLVMIRTranslation.cpp | 9 ++
mlir/lib/Target/LLVMIR/ModuleImport.cpp | 29 ++++++-
mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | 6 ++
mlir/test/Dialect/LLVMIR/indirectbr.mlir | 81 +++++++++++++++++
.../Target/LLVMIR/Import/import-failure.ll | 12 ---
mlir/test/Target/LLVMIR/Import/indirectbr.ll | 86 +++++++++++++++++++
mlir/test/Target/LLVMIR/indirectbr.mlir | 40 +++++++++
.../test/mlir-translate/import-diagnostics.ll | 14 +--
10 files changed, 374 insertions(+), 20 deletions(-)
create mode 100644 mlir/test/Dialect/LLVMIR/indirectbr.mlir
create mode 100644 mlir/test/Target/LLVMIR/Import/indirectbr.ll
create mode 100644 mlir/test/Target/LLVMIR/indirectbr.mlir
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 6602318b07b85..154594833906d 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1705,6 +1705,65 @@ def LLVM_BlockTagOp : LLVM_Op<"blocktag"> {
let hasVerifier = 0;
}
+//===----------------------------------------------------------------------===//
+// IndirectBrOp
+//===----------------------------------------------------------------------===//
+
+def LLVM_IndirectBrOp : LLVM_TerminatorOp<"indirectbr",
+ [SameVariadicOperandSize, DeclareOpInterfaceMethods<BranchOpInterface>,
+ Pure]> {
+ let description = [{
+ Transfer control flow to address in `$addr`. A list of possible target
+ blocks in `$successors` can be provided and maybe used as a hint in LLVM:
+
+ ```mlir
+ ...
+ llvm.func @g(...
+ %dest = llvm.blockaddress <function = @g, tag = <id = 0>> : !llvm.ptr
+ llvm.indirectbr %dest : !llvm.ptr, [
+ ^head
+ ]
+ ^head:
+ llvm.blocktag <id = 0>
+ llvm.return %arg0 : i32
+ ...
+ ```
+
+ It also supports a list of operands that can be passed to a target block:
+
+ ```mlir
+ llvm.indirectbr %dest : !llvm.ptr, [
+ ^head(%arg0 : i32),
+ ^tail(%arg1 : i32)
+ ]
+ ^head(%r0 : i32):
+ llvm.return %r0 : i32
+ ^tail(%r1 : i32):
+ llvm.return %r1 : i32
+
+ ```
+ }];
+ let arguments = (ins LLVM_AnyPointer:$addr,
+ VariadicOfVariadic<AnyType, "indbr_operand_segments">:$succOperands,
+ DenseI32ArrayAttr:$indbr_operand_segments
+ );
+ let successors = (successor VariadicSuccessor<AnySuccessor>:$successors);
+ let assemblyFormat = [{
+ $addr `:` type($addr) `,`
+ custom<IndirectBrOpSucessors>(ref(type($addr)),
+ $successors,
+ $succOperands,
+ type($succOperands))
+ attr-dict
+ }];
+
+ let builders = [
+ OpBuilder<(ins "Value":$addr,
+ CArg<"BlockRange", "{}">:$successors,
+ CArg<"ArrayRef<ValueRange>", "{}">:$succOperands)>
+ ];
+}
+
def LLVM_ComdatSelectorOp : LLVM_Op<"comdat_selector", [Symbol]> {
let arguments = (ins
SymbolNameAttr:$sym_name,
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 33a1686541996..d28f97893206c 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -3879,6 +3879,64 @@ LogicalResult BlockAddressOp::verify() {
/// attribute.
OpFoldResult BlockAddressOp::fold(FoldAdaptor) { return getBlockAddr(); }
+//===----------------------------------------------------------------------===//
+// LLVM::IndirectBrOp
+//===----------------------------------------------------------------------===//
+
+SuccessorOperands IndirectBrOp::getSuccessorOperands(unsigned index) {
+ assert(index < getNumSuccessors() && "invalid successor index");
+ return SuccessorOperands(getSuccOperandsMutable()[index]);
+}
+
+static ParseResult parseIndirectBrOpSucessors(
+ OpAsmParser &parser, Type &flagType,
+ SmallVectorImpl<Block *> &succOperandBlocks,
+ SmallVectorImpl<SmallVector<OpAsmParser::UnresolvedOperand>> &succOperands,
+ SmallVectorImpl<SmallVector<Type>> &succOperandsTypes) {
+ if (failed(parser.parseCommaSeparatedList(
+ OpAsmParser::Delimiter::Square,
+ [&]() {
+ Block *destination = nullptr;
+ SmallVector<OpAsmParser::UnresolvedOperand> operands;
+ SmallVector<Type> operandTypes;
+
+ if (parser.parseSuccessor(destination).failed())
+ return failure();
+
+ if (succeeded(parser.parseOptionalLParen())) {
+ if (failed(parser.parseOperandList(
+ operands, OpAsmParser::Delimiter::None)) ||
+ failed(parser.parseColonTypeList(operandTypes)) ||
+ failed(parser.parseRParen()))
+ return failure();
+ }
+ succOperandBlocks.push_back(destination);
+ succOperands.emplace_back(operands);
+ succOperandsTypes.emplace_back(operandTypes);
+ return success();
+ },
+ "successor blocks")))
+ return failure();
+ return success();
+}
+
+static void
+printIndirectBrOpSucessors(OpAsmPrinter &p, IndirectBrOp op, Type flagType,
+ SuccessorRange succs, OperandRangeRange succOperands,
+ const TypeRangeRange &succOperandsTypes) {
+ p << "[";
+ llvm::interleave(
+ llvm::zip(succs, succOperands),
+ [&](auto i) {
+ p.printNewline();
+ p.printSuccessorAndUseList(std::get<0>(i), std::get<1>(i));
+ },
+ [&] { p << ','; });
+ if (!succOperands.empty())
+ p.printNewline();
+ p << "]";
+}
+
//===----------------------------------------------------------------------===//
// AssumeOp (intrinsic)
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 738f036bb376a..5c7f032404010 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -503,6 +503,15 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
moduleTranslation.mapBranch(&opInst, switchInst);
return success();
}
+ if (auto indBrOp = dyn_cast<LLVM::IndirectBrOp>(opInst)) {
+ llvm::IndirectBrInst *indBr = builder.CreateIndirectBr(
+ moduleTranslation.lookupValue(indBrOp.getAddr()),
+ indBrOp->getNumSuccessors());
+ for (auto *succ : indBrOp.getSuccessors())
+ indBr->addDestination(moduleTranslation.lookupBlock(succ));
+ moduleTranslation.mapBranch(&opInst, indBr);
+ return success();
+ }
// Emit addressof. We need to look up the global value referenced by the
// operation and store it in the MLIR-to-LLVM value mapping. This does not
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 187f1bdf7af6e..a1faef695aecb 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1988,6 +1988,31 @@ LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
return success();
}
+ if (inst->getOpcode() == llvm::Instruction::IndirectBr) {
+ auto *indBrInst = cast<llvm::IndirectBrInst>(inst);
+
+ FailureOr<Value> basePtr = convertValue(indBrInst->getAddress());
+ if (failed(basePtr))
+ return failure();
+
+ SmallVector<Block *> succBlocks;
+ SmallVector<ValueRange> succBlockArgs;
+ for (auto i : llvm::seq<unsigned>(0, indBrInst->getNumSuccessors())) {
+ llvm::BasicBlock *succ = indBrInst->getSuccessor(i);
+ SmallVector<Value> blockArgs;
+ if (failed(convertBranchArgs(indBrInst, succ, blockArgs)))
+ return failure();
+ succBlocks.push_back(lookupBlock(succ));
+ succBlockArgs.push_back(blockArgs);
+ }
+ Location loc = translateLoc(inst->getDebugLoc());
+ auto indBrOp = builder.create<LLVM::IndirectBrOp>(
+ loc, TypeRange{}, *basePtr, succBlockArgs, succBlocks);
+
+ mapNoResultOp(inst, indBrOp);
+ return success();
+ }
+
// Convert all instructions that have an mlirBuilder.
if (succeeded(convertInstructionImpl(builder, inst, *this, iface)))
return success();
@@ -1998,8 +2023,8 @@ LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
LogicalResult ModuleImport::processInstruction(llvm::Instruction *inst) {
// FIXME: Support uses of SubtargetData.
// FIXME: Add support for call / operand attributes.
- // FIXME: Add support for the indirectbr, cleanupret, catchret, catchswitch,
- // callbr, vaarg, catchpad, cleanuppad instructions.
+ // FIXME: Add support for the cleanupret, catchret, catchswitch, callbr,
+ // vaarg, catchpad, cleanuppad instructions.
// Convert LLVM intrinsics calls to MLIR intrinsics.
if (auto *intrinsic = dyn_cast<llvm::IntrinsicInst>(inst))
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index ee7dc3a5231f4..54d9e4410714b 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -805,6 +805,12 @@ static Value getPHISourceValue(Block *current, Block *pred,
return switchOp.getCaseOperands(i.index())[index];
}
+ if (auto indBrOp = dyn_cast<LLVM::IndirectBrOp>(terminator)) {
+ // For indirect branches we take operands for each successor.
+ for (const auto &i : llvm::enumerate(indBrOp->getSuccessors()))
+ return indBrOp.getSuccessorOperands(i.index())[index];
+ }
+
if (auto invokeOp = dyn_cast<LLVM::InvokeOp>(terminator)) {
return invokeOp.getNormalDest() == current
? invokeOp.getNormalDestOperands()[index]
diff --git a/mlir/test/Dialect/LLVMIR/indirectbr.mlir b/mlir/test/Dialect/LLVMIR/indirectbr.mlir
new file mode 100644
index 0000000000000..c3c04eacb1a73
--- /dev/null
+++ b/mlir/test/Dialect/LLVMIR/indirectbr.mlir
@@ -0,0 +1,81 @@
+// RUN: mlir-opt -split-input-file --verify-roundtrip %s | FileCheck %s
+
+llvm.func @ib0(%dest : !llvm.ptr, %arg0 : i32, %arg1 : i32) -> i32 {
+ llvm.indirectbr %dest : !llvm.ptr, [
+ ^head(%arg0 : i32),
+ ^tail(%arg1 : i32)
+ ]
+^head(%r0 : i32):
+ llvm.return %r0 : i32
+^tail(%r1 : i32):
+ llvm.return %r1 : i32
+}
+
+// CHECK: llvm.func @ib0(%[[Addr:.*]]: !llvm.ptr, %[[A0:.*]]: i32, %[[A1:.*]]: i32) -> i32 {
+// CHECK: llvm.indirectbr %[[Addr]] : !llvm.ptr, [
+// CHECK: ^bb1(%[[A0:.*]] : i32)
+// CHECK: ^bb2(%[[A1:.*]] : i32)
+// CHECK: ]
+// CHECK: ^bb1(%[[Op0:.*]]: i32):
+// CHECK: llvm.return %[[Op0]] : i32
+// CHECK: ^bb2(%[[Op1:.*]]: i32):
+// CHECK: llvm.return %[[Op1]] : i32
+// CHECK: }
+
+// -----
+
+llvm.func @ib1(%dest : !llvm.ptr) {
+ llvm.indirectbr %dest : !llvm.ptr, []
+}
+
+// CHECK: llvm.func @ib1(%[[Addr:.*]]: !llvm.ptr) {
+// CHECK: llvm.indirectbr %[[Addr]] : !llvm.ptr, []
+// CHECK: }
+
+// -----
+
+// CHECK: llvm.func @test_indirectbr_phi(
+// CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr, %arg3: i32) -> i32 {
+llvm.func @callee(!llvm.ptr, i32, i32) -> i32
+llvm.func @test_indirectbr_phi(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr, %arg3: i32) -> i32 {
+ %0 = llvm.mlir.undef : i1
+ %1 = llvm.mlir.addressof @test_indirectbr_phi : !llvm.ptr
+ %2 = llvm.blockaddress <function = @test_indirectbr_phi, tag = <id = 1>> : !llvm.ptr
+ // CHECK: %[[ONE:.*]] = llvm.mlir.constant(1 : i32) : i32
+ %3 = llvm.mlir.constant(1 : i32) : i32
+ // CHECK: %[[TWO:.*]] = llvm.mlir.constant(2 : i32) : i32
+ %4 = llvm.mlir.constant(2 : i32) : i32
+ %5 = llvm.select %0, %2, %arg0 : i1, !llvm.ptr
+ // CHECK: llvm.indirectbr {{.*}} : !llvm.ptr, [
+ // CHECK: ^[[HEAD_BB:.*]],
+ // CHECK: ^[[TAIL_BB:.*]](%[[ONE]] : i32)
+ // CHECK: ]
+ llvm.indirectbr %5 : !llvm.ptr, [
+ ^bb1,
+ ^bb2(%3 : i32)
+ ]
+^bb1:
+ // CHECK: ^[[HEAD_BB]]:
+ // CHECK: llvm.indirectbr {{.*}} : !llvm.ptr, [
+ // CHECK: ^[[TAIL_BB]](%[[TWO]] : i32),
+ // CHECK: ^[[END_BB:.*]]
+ // CHECK: ]
+ %6 = llvm.select %0, %2, %arg0 : i1, !llvm.ptr
+ llvm.indirectbr %6 : !llvm.ptr, [
+ ^bb2(%4 : i32),
+ ^bb3
+ ]
+^bb2(%7: i32):
+ // CHECK: ^[[TAIL_BB]](%[[BLOCK_ARG:.*]]: i32):
+ // CHECK: {{.*}} = llvm.call @callee({{.*}}, %[[BLOCK_ARG]])
+ // CHECK: llvm.return
+ %8 = llvm.call @callee(%arg1, %arg3, %7) : (!llvm.ptr, i32, i32) -> i32
+ llvm.return %8 : i32
+^bb3:
+ // CHECK: ^[[END_BB]]:
+ // CHECK: llvm.blocktag
+ // CHECK: llvm.return
+ // CHECK: }
+ llvm.blocktag <id = 1>
+ llvm.return %arg3 : i32
+}
diff --git a/mlir/test/Target/LLVMIR/Import/import-failure.ll b/mlir/test/Target/LLVMIR/Import/import-failure.ll
index 4fbf187659a7b..782925a0a938e 100644
--- a/mlir/test/Target/LLVMIR/Import/import-failure.ll
+++ b/mlir/test/Target/LLVMIR/Import/import-failure.ll
@@ -1,17 +1,5 @@
; RUN: not mlir-translate -import-llvm -emit-expensive-warnings -split-input-file %s 2>&1 -o /dev/null | FileCheck %s
-; CHECK: <unknown>
-; CHECK-SAME: error: unhandled instruction: indirectbr ptr %dst, [label %bb1, label %bb2]
-define i32 @unhandled_instruction(ptr %dst) {
- indirectbr ptr %dst, [label %bb1, label %bb2]
-bb1:
- ret i32 0
-bb2:
- ret i32 1
-}
-
-; // -----
-
; Check that debug intrinsics with an unsupported argument are dropped.
declare void @llvm.dbg.value(metadata, metadata, metadata)
diff --git a/mlir/test/Target/LLVMIR/Import/indirectbr.ll b/mlir/test/Target/LLVMIR/Import/indirectbr.ll
new file mode 100644
index 0000000000000..f1da3e3887927
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/Import/indirectbr.ll
@@ -0,0 +1,86 @@
+; RUN: mlir-translate --import-llvm %s -split-input-file | FileCheck %s
+
+; CHECK: llvm.func @basic(%[[arg0:.*]]: !llvm.ptr)
+define i32 @basic(ptr %dst) {
+ ; CHECK: llvm.indirectbr %[[arg0]] : !llvm.ptr, [
+ ; CHECK: ^[[bb1:.*]],
+ ; CHECK: ^[[bb2:.*]]
+ ; CHECK: ]
+ indirectbr ptr %dst, [label %bb1, label %bb2]
+bb1:
+ ; CHECK: ^[[bb1]]:
+ ; CHECK: llvm.return
+ ret i32 0
+bb2:
+ ; CHECK: ^[[bb2]]:
+ ; CHECK: llvm.return
+ ret i32 1
+}
+
+; // -----
+
+; CHECK: llvm.mlir.global external @addr()
+ at addr = global ptr null
+
+; CHECK-LABEL: llvm.func @test_indirectbr() {
+define void @test_indirectbr() {
+ ; CHECK: %[[BA:.*]] = llvm.blockaddress <function = @test_indirectbr, tag = <id = 1>> : !llvm.ptr
+ ; CHECK: {{.*}} = llvm.mlir.addressof @addr : !llvm.ptr
+ ; CHECK: llvm.store %[[BA]], {{.*}} : !llvm.ptr, !llvm.ptr
+ store ptr blockaddress(@test_indirectbr, %block), ptr @addr
+ ; CHECK: %[[TARGET_ADDR:.*]] = llvm.load {{.*}} : !llvm.ptr -> !llvm.ptr
+ %val = load ptr, ptr @addr
+ ; CHECK: llvm.indirectbr %[[TARGET_ADDR]] : !llvm.ptr, [
+ ; CHECK: ^[[TARGET_BB:.*]]
+ ; CHECK: ]
+ indirectbr ptr %val, [label %block]
+ ; CHECK: ^[[TARGET_BB]]:
+ ; CHECK: llvm.blocktag <id = 1>
+ ; CHECK: llvm.return
+ ; CHECK: }
+block:
+ ret void
+}
+
+; // -----
+
+; CHECK: llvm.func @callee(!llvm.ptr, i32, i32) -> i32
+declare i32 @callee(ptr %a, i32 %v, i32 %p)
+
+; CHECK: llvm.func @test_indirectbr_phi(
+; CHECK-SAME: %arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr, %arg3: i32) -> i32 {
+define i32 @test_indirectbr_phi(ptr %address, ptr %a, ptr %b, i32 %v) {
+entry:
+ ; CHECK: %[[ONE:.*]] = llvm.mlir.constant(1 : i32) : i32
+ ; CHECK: %[[TWO:.*]] = llvm.mlir.constant(2 : i32) : i32
+ %dest = select i1 undef, ptr blockaddress(@test_indirectbr_phi, %end), ptr %address
+ ; CHECK: llvm.indirectbr {{.*}} : !llvm.ptr, [
+ ; CHECK: ^[[HEAD_BB:.*]],
+ ; CHECK: ^[[TAIL_BB:.*]](%[[ONE]] : i32)
+ ; CHECK: ]
+ indirectbr ptr %dest, [label %head, label %tail]
+
+head:
+ ; CHECK: ^[[HEAD_BB]]:
+ ; CHECK: llvm.indirectbr {{.*}} : !llvm.ptr, [
+ ; CHECK: ^[[TAIL_BB]](%[[TWO]] : i32),
+ ; CHECK: ^[[END_BB:.*]]
+ ; CHECK: ]
+ %dest2 = select i1 undef, ptr blockaddress(@test_indirectbr_phi, %end), ptr %address
+ indirectbr ptr %dest2, [label %tail, label %end]
+
+tail:
+ ; CHECK: ^[[TAIL_BB]](%[[BLOCK_ARG:.*]]: i32):
+ ; CHECK: {{.*}} = llvm.call @callee({{.*}}, %[[BLOCK_ARG]])
+ ; CHECK: llvm.return
+ %p = phi i32 [1, %entry], [2, %head]
+ %r = call i32 @callee(ptr %a, i32 %v, i32 %p)
+ ret i32 %r
+
+end:
+ ; CHECK: ^[[END_BB]]:
+ ; CHECK: llvm.blocktag
+ ; CHECK: llvm.return
+ ; CHECK: }
+ ret i32 %v
+}
diff --git a/mlir/test/Target/LLVMIR/indirectbr.mlir b/mlir/test/Target/LLVMIR/indirectbr.mlir
new file mode 100644
index 0000000000000..538220721e9eb
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/indirectbr.mlir
@@ -0,0 +1,40 @@
+// RUN: mlir-translate -mlir-to-llvmir %s -split-input-file | FileCheck %s
+
+llvm.func @callee(!llvm.ptr, i32, i32) -> i32
+
+// CHECK: define i32 @test_indirectbr_phi(ptr %[[IN_PTR:.*]], ptr %[[ARG1:.*]], i32 %[[ARG2:.*]]) {
+llvm.func @test_indirectbr_phi(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg3: i32) -> i32 {
+ %0 = llvm.mlir.undef : i1
+ %2 = llvm.blockaddress <function = @test_indirectbr_phi, tag = <id = 1>> : !llvm.ptr
+ %3 = llvm.mlir.constant(1 : i32) : i32
+ %4 = llvm.mlir.constant(2 : i32) : i32
+ %5 = llvm.select %0, %2, %arg0 : i1, !llvm.ptr
+ // CHECK: %[[BA0:.*]] = select i1 undef, ptr blockaddress(@test_indirectbr_phi, %[[RET_BB:.*]]), ptr %[[IN_PTR]]
+ // CHECK: indirectbr ptr %[[BA0]], [label %[[BB1:.*]], label %[[BB2:.*]]]
+ llvm.indirectbr %5 : !llvm.ptr, [
+ ^bb1,
+ ^bb2(%3 : i32)
+ ]
+^bb1:
+ // CHECK: [[BB1]]:
+ // CHECK: %[[BA1:.*]] = select i1 undef, ptr blockaddress(@test_indirectbr_phi, %[[RET_BB]]), ptr %[[IN_PTR]]
+ // CHECK: indirectbr ptr %[[BA1]], [label %[[BB2]], label %[[RET_BB]]]
+ %6 = llvm.select %0, %2, %arg0 : i1, !llvm.ptr
+ llvm.indirectbr %6 : !llvm.ptr, [
+ ^bb2(%4 : i32),
+ ^bb3
+ ]
+^bb2(%7: i32):
+ // CHECK: [[BB2]]:
+ // CHECK: %[[PHI:.*]] = phi i32 [ 2, %[[BB1]] ], [ 1, {{.*}} ]
+ // CHECK: %[[CALL:.*]] = call i32 @callee(ptr %[[ARG1]], i32 %[[ARG2]], i32 %[[PHI]])
+ // CHECK: ret i32 %[[CALL]]
+ %8 = llvm.call @callee(%arg1, %arg3, %7) : (!llvm.ptr, i32, i32) -> i32
+ llvm.return %8 : i32
+^bb3:
+ // CHECK: [[RET_BB]]:
+ // CHECK: ret i32 %[[ARG2]]
+ // CHECK: }
+ llvm.blocktag <id = 1>
+ llvm.return %arg3 : i32
+}
diff --git a/mlir/test/mlir-translate/import-diagnostics.ll b/mlir/test/mlir-translate/import-diagnostics.ll
index acf45eb1c7c5d..6767d948e3bbd 100644
--- a/mlir/test/mlir-translate/import-diagnostics.ll
+++ b/mlir/test/mlir-translate/import-diagnostics.ll
@@ -21,14 +21,16 @@ end:
; ERROR: error:
; DEFAULT: error:
; EXPENSIVE: error:
-define i32 @error(ptr %dst) {
- indirectbr ptr %dst, [label %bb1, label %bb2]
-bb1:
- ret i32 0
-bb2:
- ret i32 1
+define dso_local void @tbaa(ptr %0) {
+ store i32 1, ptr %0, align 4, !tbaa !2
+ ret void
}
+!2 = !{!3, !3, i64 0, i64 4}
+!3 = !{!4, i64 4, !"int"}
+!4 = !{!5, i64 1, !"omnipotent char"}
+!5 = !{!"Simple C++ TBAA"}
+
; // -----
declare void @llvm.dbg.value(metadata, metadata, metadata)
>From 38215a63f7f9cbe34b67afcbedc1e348748bf833 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Thu, 10 Apr 2025 14:36:38 -0700
Subject: [PATCH 2/4] improve tests and doc
---
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 9 ++++-----
mlir/test/Dialect/LLVMIR/indirectbr.mlir | 14 ++++++++------
2 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 154594833906d..f72467244a3cd 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1733,14 +1733,13 @@ def LLVM_IndirectBrOp : LLVM_TerminatorOp<"indirectbr",
```mlir
llvm.indirectbr %dest : !llvm.ptr, [
- ^head(%arg0 : i32),
- ^tail(%arg1 : i32)
+ ^head(%arg0 : i32),
+ ^tail(%arg1, %arg0 : i32, i32)
]
^head(%r0 : i32):
llvm.return %r0 : i32
- ^tail(%r1 : i32):
- llvm.return %r1 : i32
-
+ ^tail(%r1 : i32, %r2 : i32):
+ ...
```
}];
let arguments = (ins LLVM_AnyPointer:$addr,
diff --git a/mlir/test/Dialect/LLVMIR/indirectbr.mlir b/mlir/test/Dialect/LLVMIR/indirectbr.mlir
index c3c04eacb1a73..63bdfea0c3095 100644
--- a/mlir/test/Dialect/LLVMIR/indirectbr.mlir
+++ b/mlir/test/Dialect/LLVMIR/indirectbr.mlir
@@ -3,23 +3,25 @@
llvm.func @ib0(%dest : !llvm.ptr, %arg0 : i32, %arg1 : i32) -> i32 {
llvm.indirectbr %dest : !llvm.ptr, [
^head(%arg0 : i32),
- ^tail(%arg1 : i32)
+ ^tail(%arg1, %arg0 : i32, i32)
]
^head(%r0 : i32):
llvm.return %r0 : i32
-^tail(%r1 : i32):
- llvm.return %r1 : i32
+^tail(%r1 : i32, %r2 : i32):
+ %r = llvm.add %r1, %r2 : i32
+ llvm.return %r : i32
}
// CHECK: llvm.func @ib0(%[[Addr:.*]]: !llvm.ptr, %[[A0:.*]]: i32, %[[A1:.*]]: i32) -> i32 {
// CHECK: llvm.indirectbr %[[Addr]] : !llvm.ptr, [
// CHECK: ^bb1(%[[A0:.*]] : i32)
-// CHECK: ^bb2(%[[A1:.*]] : i32)
+// CHECK: ^bb2(%[[A1:.*]], %[[A0]] : i32, i32)
// CHECK: ]
// CHECK: ^bb1(%[[Op0:.*]]: i32):
// CHECK: llvm.return %[[Op0]] : i32
-// CHECK: ^bb2(%[[Op1:.*]]: i32):
-// CHECK: llvm.return %[[Op1]] : i32
+// CHECK: ^bb2(%[[Op1:.*]]: i32, %[[Op2:.*]]: i32):
+// CHECK: %[[Op3:.*]] = llvm.add %[[Op1]], %[[Op2]] : i32
+// CHECK: llvm.return %[[Op3]] : i32
// CHECK: }
// -----
>From 36d8f98f8ee34011869f5f345166c6931454ec0d Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Fri, 11 Apr 2025 14:50:43 -0700
Subject: [PATCH 3/4] Address unrecheable block concerns
---
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 18 ++++++++++---
.../LLVMIR/blockaddress-canonicalize.mlir | 26 ++++++++++++++++++-
2 files changed, 40 insertions(+), 4 deletions(-)
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index d28f97893206c..1924264e234af 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -2302,19 +2302,31 @@ static LogicalResult verifyComdat(Operation *op,
static LogicalResult verifyBlockTags(LLVMFuncOp funcOp) {
llvm::DenseSet<BlockTagAttr> blockTags;
BlockTagOp badBlockTagOp;
+ enum { DupTag, UnrecheableBlock } errorMsgType;
if (funcOp
.walk([&](BlockTagOp blockTagOp) {
+ mlir::Block *block = blockTagOp->getBlock();
+ if (!block->isEntryBlock() && block->use_empty()) {
+ badBlockTagOp = blockTagOp;
+ errorMsgType = UnrecheableBlock;
+ return WalkResult::interrupt();
+ }
+
if (blockTags.contains(blockTagOp.getTag())) {
badBlockTagOp = blockTagOp;
+ errorMsgType = DupTag;
return WalkResult::interrupt();
}
blockTags.insert(blockTagOp.getTag());
return WalkResult::advance();
})
.wasInterrupted()) {
- badBlockTagOp.emitError()
- << "duplicate block tag '" << badBlockTagOp.getTag().getId()
- << "' in the same function: ";
+ if (errorMsgType == DupTag)
+ badBlockTagOp.emitError()
+ << "duplicate block tag '" << badBlockTagOp.getTag().getId()
+ << "' in the same function: ";
+ else
+ badBlockTagOp.emitError() << "not allowed in unrecheable blocks";
return failure();
}
diff --git a/mlir/test/Dialect/LLVMIR/blockaddress-canonicalize.mlir b/mlir/test/Dialect/LLVMIR/blockaddress-canonicalize.mlir
index 11dd6f0d97f78..5efe6777a0bb7 100644
--- a/mlir/test/Dialect/LLVMIR/blockaddress-canonicalize.mlir
+++ b/mlir/test/Dialect/LLVMIR/blockaddress-canonicalize.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt %s -pass-pipeline='builtin.module(llvm.func(canonicalize{region-simplify=aggressive}))' -split-input-file | FileCheck %s
+// RUN: mlir-opt %s -pass-pipeline='builtin.module(llvm.func(canonicalize{region-simplify=aggressive}))' -verify-diagnostics -split-input-file | FileCheck %s
llvm.mlir.global private @x() {addr_space = 0 : i32, dso_local} : !llvm.ptr {
%0 = llvm.blockaddress <function = @ba, tag = <id = 2>> : !llvm.ptr
@@ -46,3 +46,27 @@ llvm.func @fn(%cond : i1, %arg0 : i32, %arg1 : i32) -> i32 {
llvm.blocktag <id = 1>
llvm.return %arg1 : i32
}
+
+// -----
+
+llvm.mlir.global private @g() {addr_space = 0 : i32, dso_local} : !llvm.ptr {
+ %0 = llvm.blockaddress <function = @fn, tag = <id = 1>> : !llvm.ptr
+ llvm.return %0 : !llvm.ptr
+}
+
+// The Canonicalizer's region simplify pass can be hazardous when dealing
+// with indirect branches, as there is currently no mechanism to convey
+// dialect-specific block constraints.
+
+llvm.func @fn(%dest : !llvm.ptr, %arg0 : i32, %arg1 : i32) -> i32 {
+ llvm.indirectbr %dest : !llvm.ptr, [
+ ^head
+ ]
+^head:
+ llvm.blocktag <id = 0>
+ llvm.return %arg0 : i32
+^tail:
+ // expected-error at +1 {{not allowed in unrecheable blocks}}
+ llvm.blocktag <id = 1>
+ llvm.return %arg1 : i32
+}
>From 79561f03e8c935c1bde187b6cf93cbe2dd283ae4 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Fri, 11 Apr 2025 18:04:15 -0700
Subject: [PATCH 4/4] fix user after free in builder and cleanup.
---
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 6 ++++--
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 14 ++++++++++++++
mlir/lib/Target/LLVMIR/ModuleImport.cpp | 8 ++++++--
3 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index fab979816be89..f021116ebfa91 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1762,10 +1762,12 @@ def LLVM_IndirectBrOp : LLVM_TerminatorOp<"indirectbr",
attr-dict
}];
+ let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins "Value":$addr,
- CArg<"BlockRange", "{}">:$successors,
- CArg<"ArrayRef<ValueRange>", "{}">:$succOperands)>
+ CArg<"ArrayRef<ValueRange>", "{}">:$succOperands,
+ CArg<"BlockRange", "{}">:$successors
+ )>
];
}
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 94fd71ba08935..db3e5649489d8 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -3904,6 +3904,20 @@ SuccessorOperands IndirectBrOp::getSuccessorOperands(unsigned index) {
return SuccessorOperands(getSuccOperandsMutable()[index]);
}
+void IndirectBrOp::build(OpBuilder &odsBuilder, OperationState &odsState,
+ Value addr, ArrayRef<ValueRange> succOperands,
+ BlockRange successors) {
+ odsState.addOperands(addr);
+ for (ValueRange range : succOperands)
+ odsState.addOperands(range);
+ SmallVector<int32_t> rangeSegments;
+ for (ValueRange range : succOperands)
+ rangeSegments.push_back(range.size());
+ odsState.getOrAddProperties<Properties>().indbr_operand_segments =
+ odsBuilder.getDenseI32ArrayAttr(rangeSegments);
+ odsState.addSuccessors(successors);
+}
+
static ParseResult parseIndirectBrOpSucessors(
OpAsmParser &parser, Type &flagType,
SmallVectorImpl<Block *> &succOperandBlocks,
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index e883ef5550e1a..346a0ed738fe5 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1996,7 +1996,10 @@ LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
return failure();
SmallVector<Block *> succBlocks;
- SmallVector<ValueRange> succBlockArgs;
+ // `succBlockArgs` is storage for the block arguments ranges used in
+ // `succBlockArgsRange`, so the later references live data.
+ SmallVector<SmallVector<Value>> succBlockArgs;
+ SmallVector<ValueRange> succBlockArgsRange;
for (auto i : llvm::seq<unsigned>(0, indBrInst->getNumSuccessors())) {
llvm::BasicBlock *succ = indBrInst->getSuccessor(i);
SmallVector<Value> blockArgs;
@@ -2004,10 +2007,11 @@ LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
return failure();
succBlocks.push_back(lookupBlock(succ));
succBlockArgs.push_back(blockArgs);
+ succBlockArgsRange.push_back(succBlockArgs.back());
}
Location loc = translateLoc(inst->getDebugLoc());
auto indBrOp = builder.create<LLVM::IndirectBrOp>(
- loc, TypeRange{}, *basePtr, succBlockArgs, succBlocks);
+ loc, *basePtr, succBlockArgsRange, succBlocks);
mapNoResultOp(inst, indBrOp);
return success();
More information about the Mlir-commits
mailing list