Skip to content

Reapply [MLIR][LLVM] Support for indirectbr #136378

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

bcardosolopes
Copy link
Member

Fix msan issue that caused revert in #135695

Original message

Now that LLVM dialect has blockaddress support, introduce import/translation for indirectbr instruction.

@llvmbot
Copy link
Member

llvmbot commented Apr 18, 2025

@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-llvm

Author: Bruno Cardoso Lopes (bcardosolopes)

Changes

Fix msan issue that caused revert in #135695

Original message

Now that LLVM dialect has blockaddress support, introduce import/translation for indirectbr instruction.


Patch is 21.47 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/136378.diff

11 Files Affected:

  • (modified) mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td (+61-2)
  • (modified) mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp (+86-17)
  • (modified) mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp (+9)
  • (modified) mlir/lib/Target/LLVMIR/ModuleImport.cpp (+29-2)
  • (modified) mlir/lib/Target/LLVMIR/ModuleTranslation.cpp (+8)
  • (modified) mlir/test/Dialect/LLVMIR/blockaddress-canonicalize.mlir (+1-1)
  • (added) mlir/test/Dialect/LLVMIR/indirectbr.mlir (+83)
  • (modified) mlir/test/Target/LLVMIR/Import/import-failure.ll (-12)
  • (added) mlir/test/Target/LLVMIR/Import/indirectbr.ll (+86)
  • (added) mlir/test/Target/LLVMIR/indirectbr.mlir (+40)
  • (modified) mlir/test/mlir-translate/import-diagnostics.ll (+8-6)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 76a2ec47b3a22..5745d370f7268 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1690,8 +1690,7 @@ def LLVM_BlockTagOp : LLVM_Op<"blocktag"> {
     the target address.
 
     A given function should have at most one `llvm.blocktag` operation with a
-    given `tag`. This operation cannot be used as a terminator but can be
-    placed everywhere else in a block.
+    given `tag`. This operation cannot be used as a terminator.
 
     Example:
 
@@ -1711,6 +1710,66 @@ 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, %arg0 : i32, i32)
+      ]
+    ^head(%r0 : i32):
+      llvm.return %r0 : i32
+    ^tail(%r1 : i32, %r2 : 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 skipDefaultBuilders = 1;
+  let builders = [
+    OpBuilder<(ins "Value":$addr,
+      CArg<"ArrayRef<ValueRange>", "{}">:$succOperands,
+      CArg<"BlockRange", "{}">:$successors
+      )>
+  ];
+}
+
 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 d1d00ca9681ec..0022be84c212e 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -2240,24 +2240,21 @@ static LogicalResult verifyComdat(Operation *op,
 
 static LogicalResult verifyBlockTags(LLVMFuncOp funcOp) {
   llvm::DenseSet<BlockTagAttr> blockTags;
-  BlockTagOp badBlockTagOp;
-  if (funcOp
-          .walk([&](BlockTagOp blockTagOp) {
-            if (blockTags.contains(blockTagOp.getTag())) {
-              badBlockTagOp = blockTagOp;
-              return WalkResult::interrupt();
-            }
-            blockTags.insert(blockTagOp.getTag());
-            return WalkResult::advance();
-          })
-          .wasInterrupted()) {
-    badBlockTagOp.emitError()
-        << "duplicate block tag '" << badBlockTagOp.getTag().getId()
-        << "' in the same function: ";
-    return failure();
-  }
+  // Note that presence of `BlockTagOp`s currently can't prevent an unrecheable
+  // block to be removed by canonicalizer's region simplify pass, which needs to
+  // be dialect aware to allow extra constraints to be described.
+  WalkResult res = funcOp.walk([&](BlockTagOp blockTagOp) {
+    if (blockTags.contains(blockTagOp.getTag())) {
+      blockTagOp.emitError()
+          << "duplicate block tag '" << blockTagOp.getTag().getId()
+          << "' in the same function: ";
+      return WalkResult::interrupt();
+    }
+    blockTags.insert(blockTagOp.getTag());
+    return WalkResult::advance();
+  });
 
-  return success();
+  return failure(res.wasInterrupted());
 }
 
 /// Parse common attributes that might show up in the same order in both
@@ -3818,6 +3815,78 @@ 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]);
+}
+
+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,
+    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 ad48ca2ce8ad3..7038b5d73d266 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -505,6 +505,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 481d719787397..df7c8d6ea3579 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1988,6 +1988,33 @@ 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<SmallVector<Value>> 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);
+    }
+    SmallVector<ValueRange> succBlockArgsRange =
+        llvm::to_vector_of<ValueRange>(succBlockArgs);
+    Location loc = translateLoc(inst->getDebugLoc());
+    auto indBrOp = builder.create<LLVM::IndirectBrOp>(
+        loc, *basePtr, succBlockArgsRange, succBlocks);
+
+    mapNoResultOp(inst, indBrOp);
+    return success();
+  }
+
   // Convert all instructions that have an mlirBuilder.
   if (succeeded(convertInstructionImpl(builder, inst, *this, iface)))
     return success();
@@ -1998,8 +2025,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 d39ec0e801437..44899c4576a20 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -805,6 +805,14 @@ 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())) {
+      if (indBrOp->getSuccessor(i.index()) == current)
+        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/blockaddress-canonicalize.mlir b/mlir/test/Dialect/LLVMIR/blockaddress-canonicalize.mlir
index 11dd6f0d97f78..241a2ea2edc66 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
diff --git a/mlir/test/Dialect/LLVMIR/indirectbr.mlir b/mlir/test/Dialect/LLVMIR/indirectbr.mlir
new file mode 100644
index 0000000000000..63bdfea0c3095
--- /dev/null
+++ b/mlir/test/Dialect/LLVMIR/indirectbr.mlir
@@ -0,0 +1,83 @@
+// 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, %arg0 : i32, i32)
+  ]
+^head(%r0 : i32):
+  llvm.return %r0 : 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:.*]], %[[A0]] : i32, i32)
+// CHECK:   ]
+// CHECK: ^bb1(%[[Op0:.*]]: i32):
+// CHECK:   llvm.return %[[Op0]] : i32
+// CHECK: ^bb2(%[[Op1:.*]]: i32, %[[Op2:.*]]: i32):
+// CHECK:   %[[Op3:.*]] = llvm.add %[[Op1]], %[[Op2]] : i32
+// CHECK:   llvm.return %[[Op3]] : 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()
+@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:.*]] = s...
[truncated]

Copy link

github-actions bot commented Apr 18, 2025

✅ With the latest revision this PR passed the undef deprecator.

Fix msan issues that cause revert in llvm#135695

Original message:

Now that LLVM dialect has `blockaddress` support, introduce import/translation for `indirectbr` instruction.
@bcardosolopes bcardosolopes force-pushed the mlir-llvm-indirect-br-fix-msan branch from bb440d0 to bc4267d Compare April 18, 2025 22:39
Copy link
Contributor

@gysit gysit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@bcardosolopes bcardosolopes merged commit c179847 into llvm:main Apr 21, 2025
11 checks passed
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
Fix msan issue that caused revert in
llvm#135695

### Original message

Now that LLVM dialect has `blockaddress` support, introduce
import/translation for `indirectbr` instruction.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
Fix msan issue that caused revert in
llvm#135695

### Original message

Now that LLVM dialect has `blockaddress` support, introduce
import/translation for `indirectbr` instruction.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
Fix msan issue that caused revert in
llvm#135695

### Original message

Now that LLVM dialect has `blockaddress` support, introduce
import/translation for `indirectbr` instruction.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants