[Mlir-commits] [mlir] Reapply [MLIR][LLVM] Support for indirectbr (PR #136378)

Bruno Cardoso Lopes llvmlistbot at llvm.org
Fri Apr 18 15:39:13 PDT 2025


https://github.com/bcardosolopes updated https://github.com/llvm/llvm-project/pull/136378

>From bc4267d2b20d2cd3ee9ed6c292706a2ab92ea3f9 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Mon, 14 Apr 2025 14:11:30 -0700
Subject: [PATCH] Reapply [MLIR][LLVM] Support for indirectbr

Fix msan issues that cause revert in https://github.com/llvm/llvm-project/pull/135695

Original message:

Now that LLVM dialect has `blockaddress` support, introduce import/translation for `indirectbr` instruction.
---
 mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td   |  63 ++++++++++-
 mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp    | 103 +++++++++++++++---
 .../LLVMIR/LLVMToLLVMIRTranslation.cpp        |   9 ++
 mlir/lib/Target/LLVMIR/ModuleImport.cpp       |  31 +++++-
 mlir/lib/Target/LLVMIR/ModuleTranslation.cpp  |   8 ++
 .../LLVMIR/blockaddress-canonicalize.mlir     |   2 +-
 mlir/test/Dialect/LLVMIR/indirectbr.mlir      |  83 ++++++++++++++
 .../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 ++-
 11 files changed, 411 insertions(+), 40 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 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..7c197a5281ddd
--- /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 poison, 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 poison, 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)



More information about the Mlir-commits mailing list