Skip to content

Commit 9dd0eb9

Browse files
authored
[mlir][llvm] Drop unreachable basic block during import (llvm#78467)
This revision updates the LLVM IR import to support unreachable basic blocks. An unreachable block may dominate itself and a value defined inside the block may thus be used before its definition. The import does not support such dependencies. We thus delete the unreachable basic blocks before the import. This is possible since MLIR does not have basic block labels that can be reached using an indirect call and unreachable blocks can indeed be deleted safely. Additionally, add a small poison constant import test.
1 parent 3d90e1f commit 9dd0eb9

File tree

4 files changed

+97
-35
lines changed

4 files changed

+97
-35
lines changed

mlir/lib/Target/LLVMIR/ModuleImport.cpp

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "mlir/Interfaces/DataLayoutInterfaces.h"
2727
#include "mlir/Tools/mlir-translate/Translation.h"
2828

29+
#include "llvm/ADT/DepthFirstIterator.h"
2930
#include "llvm/ADT/PostOrderIterator.h"
3031
#include "llvm/ADT/ScopeExit.h"
3132
#include "llvm/ADT/StringSet.h"
@@ -132,18 +133,17 @@ static LogicalResult convertInstructionImpl(OpBuilder &odsBuilder,
132133
return failure();
133134
}
134135

135-
/// Get a topologically sorted list of blocks for the given function.
136+
/// Get a topologically sorted list of blocks for the given basic blocks.
136137
static SetVector<llvm::BasicBlock *>
137-
getTopologicallySortedBlocks(llvm::Function *func) {
138+
getTopologicallySortedBlocks(ArrayRef<llvm::BasicBlock *> basicBlocks) {
138139
SetVector<llvm::BasicBlock *> blocks;
139-
for (llvm::BasicBlock &bb : *func) {
140-
if (!blocks.contains(&bb)) {
141-
llvm::ReversePostOrderTraversal<llvm::BasicBlock *> traversal(&bb);
140+
for (llvm::BasicBlock *basicBlock : basicBlocks) {
141+
if (!blocks.contains(basicBlock)) {
142+
llvm::ReversePostOrderTraversal<llvm::BasicBlock *> traversal(basicBlock);
142143
blocks.insert(traversal.begin(), traversal.end());
143144
}
144145
}
145-
assert(blocks.size() == func->size() && "some blocks are not sorted");
146-
146+
assert(blocks.size() == basicBlocks.size() && "some blocks are not sorted");
147147
return blocks;
148148
}
149149

@@ -1859,11 +1859,26 @@ LogicalResult ModuleImport::processFunction(llvm::Function *func) {
18591859
if (func->isDeclaration())
18601860
return success();
18611861

1862-
// Eagerly create all blocks.
1863-
for (llvm::BasicBlock &bb : *func) {
1864-
Block *block =
1865-
builder.createBlock(&funcOp.getBody(), funcOp.getBody().end());
1866-
mapBlock(&bb, block);
1862+
// Collect the set of basic blocks reachable from the function's entry block.
1863+
// This step is crucial as LLVM IR can contain unreachable blocks that
1864+
// self-dominate. As a result, an operation might utilize a variable it
1865+
// defines, which the import does not support. Given that MLIR lacks block
1866+
// label support, we can safely remove unreachable blocks, as there are no
1867+
// indirect branch instructions that could potentially target these blocks.
1868+
llvm::df_iterator_default_set<llvm::BasicBlock *> reachable;
1869+
for (llvm::BasicBlock *basicBlock : llvm::depth_first_ext(func, reachable))
1870+
(void)basicBlock;
1871+
1872+
// Eagerly create all reachable blocks.
1873+
SmallVector<llvm::BasicBlock *> reachableBasicBlocks;
1874+
for (llvm::BasicBlock &basicBlock : *func) {
1875+
// Skip unreachable blocks.
1876+
if (!reachable.contains(&basicBlock))
1877+
continue;
1878+
Region &body = funcOp.getBody();
1879+
Block *block = builder.createBlock(&body, body.end());
1880+
mapBlock(&basicBlock, block);
1881+
reachableBasicBlocks.push_back(&basicBlock);
18671882
}
18681883

18691884
// Add function arguments to the entry block.
@@ -1876,10 +1891,11 @@ LogicalResult ModuleImport::processFunction(llvm::Function *func) {
18761891
// Process the blocks in topological order. The ordered traversal ensures
18771892
// operands defined in a dominating block have a valid mapping to an MLIR
18781893
// value once a block is translated.
1879-
SetVector<llvm::BasicBlock *> blocks = getTopologicallySortedBlocks(func);
1894+
SetVector<llvm::BasicBlock *> blocks =
1895+
getTopologicallySortedBlocks(reachableBasicBlocks);
18801896
setConstantInsertionPointToStart(lookupBlock(blocks.front()));
1881-
for (llvm::BasicBlock *bb : blocks)
1882-
if (failed(processBasicBlock(bb, lookupBlock(bb))))
1897+
for (llvm::BasicBlock *basicBlock : blocks)
1898+
if (failed(processBasicBlock(basicBlock, lookupBlock(basicBlock))))
18831899
return failure();
18841900

18851901
// Process the debug intrinsics that require a delayed conversion after

mlir/test/Target/LLVMIR/Import/constant.ll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ define void @undef_constant(i32 %arg0) {
4747

4848
; // -----
4949

50+
; CHECK-LABEL: @poison_constant
51+
define void @poison_constant(double %arg0) {
52+
; CHECK: %[[POISON:.+]] = llvm.mlir.poison : f64
53+
; CHECK: llvm.fadd %[[POISON]], %{{.*}} : f64
54+
%1 = fadd double poison, %arg0
55+
ret void
56+
}
57+
58+
; // -----
59+
5060
; CHECK-LABEL: @null_constant
5161
define ptr @null_constant() {
5262
; CHECK: %[[NULL:[0-9]+]] = llvm.mlir.zero : !llvm.ptr

mlir/test/Target/LLVMIR/Import/exception.ll

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,35 @@ define i32 @invokeLandingpad() personality ptr @__gxx_personality_v0 {
1212
; CHECK: %[[a1:[0-9]+]] = llvm.mlir.addressof @_ZTIii : !llvm.ptr
1313
; CHECK: %[[a3:[0-9]+]] = llvm.alloca %{{[0-9]+}} x i8 {alignment = 1 : i64} : (i32) -> !llvm.ptr
1414
%1 = alloca i8
15-
; CHECK: llvm.invoke @foo(%[[a3]]) to ^bb2 unwind ^bb1 : (!llvm.ptr) -> ()
16-
invoke void @foo(ptr %1) to label %4 unwind label %2
15+
; CHECK: llvm.invoke @foo(%[[a3]]) to ^[[bb1:.*]] unwind ^[[bb4:.*]] : (!llvm.ptr) -> ()
16+
invoke void @foo(ptr %1) to label %bb1 unwind label %bb4
1717

18-
; CHECK: ^bb1:
18+
; CHECK: ^[[bb1]]:
19+
bb1:
20+
; CHECK: %{{[0-9]+}} = llvm.invoke @bar(%[[a3]]) to ^[[bb2:.*]] unwind ^[[bb4]] : (!llvm.ptr) -> !llvm.ptr
21+
%2 = invoke ptr @bar(ptr %1) to label %bb2 unwind label %bb4
22+
23+
; CHECK: ^[[bb2]]:
24+
bb2:
25+
; CHECK: llvm.invoke @vararg_foo(%[[a3]], %{{.*}}) to ^[[bb3:.*]] unwind ^[[bb4]] vararg(!llvm.func<void (ptr, ...)>) : (!llvm.ptr, i32) -> ()
26+
invoke void (ptr, ...) @vararg_foo(ptr %1, i32 0) to label %bb3 unwind label %bb4
27+
28+
; CHECK: ^[[bb3]]:
29+
bb3:
30+
; CHECK: llvm.invoke %{{.*}}(%[[a3]], %{{.*}}) to ^[[bb5:.*]] unwind ^[[bb4]] vararg(!llvm.func<void (ptr, ...)>) : !llvm.ptr, (!llvm.ptr, i32) -> ()
31+
invoke void (ptr, ...) undef(ptr %1, i32 0) to label %bb5 unwind label %bb4
32+
33+
; CHECK: ^[[bb4]]:
34+
bb4:
1935
; CHECK: %{{[0-9]+}} = llvm.landingpad (catch %{{[0-9]+}} : !llvm.ptr) (catch %[[a1]] : !llvm.ptr) (filter %{{[0-9]+}} : !llvm.array<1 x i1>) : !llvm.struct<(ptr, i32)>
2036
%3 = landingpad { ptr, i32 } catch ptr @_ZTIi catch ptr @_ZTIii
2137
filter [1 x i1] [i1 1]
2238
resume { ptr, i32 } %3
2339

24-
; CHECK: ^bb2:
40+
; CHECK: ^[[bb5]]:
41+
bb5:
2542
; CHECK: llvm.return %{{[0-9]+}} : i32
2643
ret i32 1
27-
28-
; CHECK: ^bb3:
29-
; CHECK: %{{[0-9]+}} = llvm.invoke @bar(%[[a3]]) to ^bb2 unwind ^bb1 : (!llvm.ptr) -> !llvm.ptr
30-
%6 = invoke ptr @bar(ptr %1) to label %4 unwind label %2
31-
32-
; CHECK: ^bb4:
33-
; CHECK: llvm.invoke @vararg_foo(%[[a3]], %{{.*}}) to ^bb2 unwind ^bb1 vararg(!llvm.func<void (ptr, ...)>) : (!llvm.ptr, i32) -> ()
34-
invoke void (ptr, ...) @vararg_foo(ptr %1, i32 0) to label %4 unwind label %2
35-
36-
; CHECK: ^bb5:
37-
; CHECK: llvm.invoke %{{.*}}(%[[a3]], %{{.*}}) to ^bb2 unwind ^bb1 vararg(!llvm.func<void (ptr, ...)>) : !llvm.ptr, (!llvm.ptr, i32) -> ()
38-
invoke void (ptr, ...) undef(ptr %1, i32 0) to label %4 unwind label %2
39-
40-
; CHECK: ^bb6:
41-
; CHECK: llvm.return %{{[0-9]+}} : i32
42-
ret i32 0
4344
}
4445

4546
declare i32 @foo2()
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
; RUN: mlir-translate -import-llvm %s | FileCheck %s
2+
3+
; Test unreachable blocks are dropped.
4+
5+
; CHECK-LABEL: llvm.func @unreachable_block
6+
define void @unreachable_block(float %0) {
7+
.entry:
8+
; CHECK: llvm.return
9+
ret void
10+
11+
unreachable:
12+
; CHECK-NOT: llvm.fadd
13+
%1 = fadd float %0, %1
14+
br label %unreachable
15+
}
16+
17+
; Test unreachable blocks with back edges are supported.
18+
19+
; CHECK-LABEL: llvm.func @back_edge
20+
define i32 @back_edge(i32 %0) {
21+
.entry:
22+
; CHECK: llvm.br ^[[RET:.*]](%{{.*}})
23+
br label %ret
24+
ret:
25+
; CHECK: ^[[RET]](%{{.*}}: i32)
26+
%1 = phi i32 [ %0, %.entry ], [ %2, %unreachable ]
27+
; CHECK: llvm.return %{{.*}} : i32
28+
ret i32 %1
29+
30+
unreachable:
31+
; CHECK-NOT: add
32+
%2 = add i32 %0, %2
33+
%3 = icmp eq i32 %2, 42
34+
br i1 %3, label %ret, label %unreachable
35+
}

0 commit comments

Comments
 (0)