Skip to content

Commit 81ac311

Browse files
committed
Swift Optimizer: add the DeadEndBlocks utility for finding dead-end blocks.
Dead-end blocks are blocks from which there is no path to the function exit (`return`, `throw` or unwind). These are blocks which end with an unreachable instruction and blocks from which all paths end in "unreachable" blocks.
1 parent aea26bb commit 81ac311

File tree

10 files changed

+175
-1
lines changed

10 files changed

+175
-1
lines changed

SwiftCompilerSources/Sources/Optimizer/DataStructures/BasicBlockWorklist.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ struct BasicBlockWorklist : CustomStringConvertible, NoReflectionChildren {
5656
"""
5757
}
5858

59+
var function: Function { pushedBlocks.function }
60+
5961
/// TODO: once we have move-only types, make this a real deinit.
6062
mutating func deinitialize() {
6163
pushedBlocks.deinitialize()

SwiftCompilerSources/Sources/Optimizer/DataStructures/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
swift_compiler_sources(Optimizer
1010
BasicBlockRange.swift
1111
BasicBlockWorklist.swift
12+
DeadEndBlocks.swift
1213
FunctionUses.swift
1314
InstructionRange.swift
1415
Set.swift
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===--- DeadEndBlocks.swift ----------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
/// A utility for finding dead-end blocks.
16+
///
17+
/// Dead-end blocks are blocks from which there is no path to the function exit
18+
/// (`return`, `throw` or unwind). These are blocks which end with an unreachable
19+
/// instruction and blocks from which all paths end in "unreachable" blocks.
20+
struct DeadEndBlocks : CustomStringConvertible, NoReflectionChildren {
21+
private var worklist: BasicBlockWorklist
22+
23+
init(function: Function, _ context: PassContext) {
24+
self.worklist = BasicBlockWorklist(context)
25+
26+
// Initialize the worklist with all function-exiting blocks.
27+
for block in function.blocks where block.terminator.isFunctionExiting {
28+
worklist.pushIfNotVisited(block)
29+
}
30+
31+
// Propagate lifeness up the control flow.
32+
while let block = worklist.pop() {
33+
worklist.pushIfNotVisited(contentsOf: block.predecessors)
34+
}
35+
}
36+
37+
/// Returns true if `block` is a dead-end block.
38+
func isDeadEnd(block: BasicBlock) -> Bool {
39+
return !worklist.hasBeenPushed(block)
40+
}
41+
42+
var description: String {
43+
let blockNames = worklist.function.blocks.filter(isDeadEnd).map(\.name)
44+
return "[" + blockNames.joined(separator: ",") + "]"
45+
}
46+
47+
/// TODO: once we have move-only types, make this a real deinit.
48+
mutating func deinitialize() {
49+
worklist.deinitialize()
50+
}
51+
}

SwiftCompilerSources/Sources/Optimizer/DataStructures/Set.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ struct BasicBlockSet : CustomStringConvertible, NoReflectionChildren {
4646
}
4747

4848
var description: String {
49-
let function = BasicBlockSet_getFunction(bridged).function
5049
let blockNames = function.blocks.enumerated().filter { contains($0.1) }
5150
.map { "bb\($0.0)"}
5251
return "{" + blockNames.joined(separator: ", ") + "}"
5352
}
5453

54+
var function: Function { BasicBlockSet_getFunction(bridged).function }
5555

5656
/// TODO: once we have move-only types, make this a real deinit.
5757
mutating func deinitialize() {

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ private func registerSwiftPasses() {
7070
registerPass(escapeInfoDumper, { escapeInfoDumper.run($0) })
7171
registerPass(addressEscapeInfoDumper, { addressEscapeInfoDumper.run($0) })
7272
registerPass(accessDumper, { accessDumper.run($0) })
73+
registerPass(deadEndBlockDumper, { deadEndBlockDumper.run($0) })
7374
registerPass(rangeDumper, { rangeDumper.run($0) })
7475
registerPass(runUnitTests, { runUnitTests.run($0) })
7576
}

SwiftCompilerSources/Sources/Optimizer/TestPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
swift_compiler_sources(Optimizer
1010
FunctionUsesDumper.swift
1111
AccessDumper.swift
12+
DeadEndBlockDumper.swift
1213
EscapeInfoDumper.swift
1314
SILPrinter.swift
1415
RangeDumper.swift
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//===--- DeadEndBlockDumper.swift -----------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
let deadEndBlockDumper = FunctionPass(name: "dump-deadendblocks", {
16+
(function: Function, context: PassContext) in
17+
18+
19+
print("Function \(function.name)")
20+
21+
var deadEndBlocks = DeadEndBlocks(function: function, context)
22+
print(deadEndBlocks)
23+
defer { deadEndBlocks.deinitialize() }
24+
25+
print("end function \(function.name)")
26+
})

docs/SIL-Utilities.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ A set of utilities for analyzing memory accesses. It defines the following conce
9999

100100
## Control- and Dataflow
101101

102+
#### `DeadEndBlocks`
103+
A utility for finding dead-end blocks.
104+
Dead-end blocks are blocks from which there is no path to the function exit (`return`, `throw` or unwind).
105+
These are blocks which end with an unreachable instruction and blocks from which all paths end in "unreachable" blocks.
106+
107+
**Uses:** `BasicBlockWorklist`
108+
**Related C++ utilities:** `DeadEndBlocks`
109+
**Status:** done
110+
102111
#### `BasicBlockRange`
103112
Defines a range from a dominating "begin" block to one or more "end" blocks. To be used for all kind of backward block reachability analysis.
104113

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ PASS(EmitDFDiagnostics, "dataflow-diagnostics",
235235
"Emit SIL Diagnostics")
236236
PASS(EscapeAnalysisDumper, "escapes-dump",
237237
"Dump Escape Analysis Results")
238+
SWIFT_FUNCTION_PASS(DeadEndBlockDumper, "dump-deadendblocks",
239+
"Tests the DeadEndBlocks utility")
238240
SWIFT_FUNCTION_PASS(EscapeInfoDumper, "dump-escape-info",
239241
"Dumps escape information")
240242
SWIFT_FUNCTION_PASS(AddressEscapeInfoDumper, "dump-addr-escape-info",

test/SILOptimizer/deadendblocks.sil

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// RUN: %target-sil-opt %s -dump-deadendblocks -o /dev/null | %FileCheck %s
2+
3+
// REQUIRES: swift_in_compiler
4+
5+
sil_stage canonical
6+
7+
import Builtin
8+
import Swift
9+
10+
// CHECK-LABEL: Function simple_test
11+
// CHECK: [bb2]
12+
// CHECK: end function simple_test
13+
sil @simple_test : $@convention(thin) () -> () {
14+
bb0:
15+
cond_br undef, bb1, bb2
16+
bb1:
17+
br bb3
18+
bb2:
19+
unreachable
20+
bb3:
21+
%r = tuple ()
22+
return %r : $()
23+
}
24+
25+
sil @throwing_fun : $@convention(thin) () -> ((), @error any Error)
26+
27+
// CHECK-LABEL: Function test_throw
28+
// CHECK: [bb1]
29+
// CHECK: end function test_throw
30+
sil @test_throw : $@convention(thin) () -> ((), @error any Error) {
31+
bb0:
32+
%0 = function_ref @throwing_fun : $@convention(thin) () -> ((), @error any Error)
33+
try_apply %0() : $@convention(thin) () -> ((), @error any Error), normal bb1, error bb2
34+
bb1(%2: $()):
35+
unreachable
36+
bb2(%3 : $Error):
37+
throw %3 : $Error
38+
}
39+
40+
41+
// CHECK-LABEL: Function test_unwind
42+
// CHECK: [bb1]
43+
// CHECK: end function test_unwind
44+
sil @test_unwind : $@yield_once @convention(thin) () -> @yields () {
45+
bb0:
46+
%t = tuple ()
47+
yield %t : $(), resume bb1, unwind bb2
48+
bb1:
49+
unreachable
50+
bb2:
51+
unwind
52+
}
53+
54+
// CHECK-LABEL: Function complex_cfg
55+
// CHECK: [bb7,bb8,bb9]
56+
// CHECK: end function complex_cfg
57+
sil @complex_cfg : $@convention(thin) () -> ((), @error any Error) {
58+
bb0:
59+
cond_br undef, bb1, bb7
60+
bb1:
61+
%0 = function_ref @throwing_fun : $@convention(thin) () -> ((), @error any Error)
62+
try_apply %0() : $@convention(thin) () -> ((), @error any Error), normal bb2, error bb3
63+
bb2(%2: $()):
64+
br bb4
65+
bb3(%3 : $Error):
66+
throw %3 : $Error
67+
bb4:
68+
cond_br undef, bb5, bb6
69+
bb5:
70+
br bb4
71+
bb6:
72+
%r = tuple ()
73+
return %r : $()
74+
bb7:
75+
br bb8
76+
bb8:
77+
cond_br undef, bb8, bb9
78+
bb9:
79+
unreachable
80+
}
81+

0 commit comments

Comments
 (0)