Skip to content

Commit 7eb2cb8

Browse files
committed
Swift Optimizer: add a pass to cleanup debug_step instructions
If a `debug_step` has the same debug location as a previous or succeeding instruction it is removed. It's just important that there is at least one instruction for a certain debug location so that single stepping on that location will work.
1 parent cef6ef9 commit 7eb2cb8

File tree

5 files changed

+241
-0
lines changed

5 files changed

+241
-0
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
swift_compiler_sources(Optimizer
1010
AssumeSingleThreaded.swift
11+
CleanupDebugSteps.swift
1112
ComputeEscapeEffects.swift
1213
ComputeSideEffects.swift
1314
ObjCBridgingOptimization.swift
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//===--- SimplificationPasses.swift ----------------------------------------==//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 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+
/// Removes redundant `debug_step` instructions.
16+
/// If a `debug_step` has the same debug location as a previous or succeeding instruction
17+
/// it is removed. It's just important that there is at least one instruction for a
18+
/// certain debug location so that single stepping on that location will work.
19+
let cleanupDebugStepsPass = FunctionPass(name: "cleanup-debug-steps") {
20+
(function: Function, context: FunctionPassContext) in
21+
22+
for block in function.blocks {
23+
cleanupDebugSteps(in: block, context)
24+
}
25+
}
26+
27+
private func cleanupDebugSteps(in block: BasicBlock, _ context: FunctionPassContext) {
28+
var lastInstWithSameLocation: Instruction?
29+
30+
for inst in block.instructions {
31+
if !inst.location.isDebugSteppable {
32+
if inst is DebugStepInst && !inst.location.isDebugSteppable {
33+
// First case: the instruction which is replaced by the debug_step didn't have a valid
34+
// location itself. Then we don't need the debug_step either.
35+
context.erase(instruction: inst)
36+
}
37+
continue
38+
}
39+
40+
if let li = lastInstWithSameLocation,
41+
!inst.location.hasSameSourceLocation(as: li.location) {
42+
lastInstWithSameLocation = nil
43+
}
44+
45+
// Only instructions which are really compiled down to some machine instructions can be
46+
// single stepped on.
47+
if !inst.producesMachineCode {
48+
continue
49+
}
50+
51+
if let li = lastInstWithSameLocation {
52+
if inst is DebugStepInst {
53+
54+
// Second case:
55+
// %li = some_instruction, loc "l"
56+
// debug_step, loc "l" // current inst -> erase
57+
context.erase(instruction: inst)
58+
continue
59+
} else if li is DebugStepInst {
60+
61+
// Third case:
62+
// debug_step, loc "l" // li -> erase
63+
// %inst = some_instruction, loc "l" // current inst
64+
context.erase(instruction: li)
65+
}
66+
}
67+
lastInstWithSameLocation = inst
68+
}
69+
}
70+
71+
private extension Instruction {
72+
var producesMachineCode: Bool {
73+
switch self {
74+
// We could include more instructions here.
75+
// In worst case a debug_step instruction remains in the code although it's not needed.
76+
// This is harmless.
77+
case is DebugStepInst, is ApplySite, is LoadInst, is StoreInst, is TermInst:
78+
return location.isDebugSteppable
79+
default:
80+
return false
81+
}
82+
}
83+
}

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ private func registerSwiftPasses() {
7575
registerPass(simplificationPass, { simplificationPass.run($0) })
7676
registerPass(ononeSimplificationPass, { ononeSimplificationPass.run($0) })
7777
registerPass(lateOnoneSimplificationPass, { lateOnoneSimplificationPass.run($0) })
78+
registerPass(cleanupDebugStepsPass, { cleanupDebugStepsPass.run($0) })
7879

7980
// Instruction passes
8081
registerForSILCombine(BeginCOWMutationInst.self, { run(BeginCOWMutationInst.self, $0) })

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,8 @@ SWIFT_FUNCTION_PASS(OnoneSimplification, "onone-simplification",
395395
"Peephole simplifications which runs at -Onone")
396396
SWIFT_FUNCTION_PASS(LateOnoneSimplification, "late-onone-simplification",
397397
"Peephole simplifications which can only run late in the -Onone pipeline")
398+
SWIFT_FUNCTION_PASS(CleanupDebugSteps, "cleanup-debug-steps",
399+
"Cleanup debug_step instructions for Onone")
398400
PASS(SimplifyBBArgs, "simplify-bb-args",
399401
"SIL Block Argument Simplification")
400402
PASS(SimplifyCFG, "simplify-cfg",
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -cleanup-debug-steps -sil-print-debuginfo | %FileCheck %s
2+
3+
// REQUIRES: swift_in_compiler
4+
5+
import Swift
6+
import Builtin
7+
8+
sil @f : $@convention(thin) () -> ()
9+
10+
// CHECK-LABEL: sil @remove_after_previous_location
11+
// CHECK-NOT: debug_step
12+
// CHECK: } // end sil function 'remove_after_previous_location'
13+
sil @remove_after_previous_location : $@convention(thin) () -> () {
14+
bb0:
15+
%f = function_ref @f : $@convention(thin) () -> ()
16+
%a = apply %f() : $@convention(thin) () -> (), loc "a.swift":1:2
17+
debug_step, loc "a.swift":1:2
18+
%r = tuple ()
19+
return %r : $()
20+
}
21+
22+
// CHECK-LABEL: sil @dont_remove_after_previous_location
23+
// CHECK: debug_step
24+
// CHECK: } // end sil function 'dont_remove_after_previous_location'
25+
sil @dont_remove_after_previous_location : $@convention(thin) () -> () {
26+
bb0:
27+
%f = function_ref @f : $@convention(thin) () -> ()
28+
%a = apply %f() : $@convention(thin) () -> (), loc "a.swift":1:2
29+
%r = tuple (), loc "a.swift":27:2
30+
debug_step, loc "a.swift":1:2
31+
return %r : $()
32+
}
33+
34+
// CHECK-LABEL: sil @remove_two_after_previous_location
35+
// CHECK-NOT: debug_step
36+
// CHECK: } // end sil function 'remove_two_after_previous_location'
37+
sil @remove_two_after_previous_location : $@convention(thin) () -> () {
38+
bb0:
39+
%f = function_ref @f : $@convention(thin) () -> ()
40+
%a = apply %f() : $@convention(thin) () -> (), loc "a.swift":1:2
41+
debug_step, loc "a.swift":1:2
42+
debug_step, loc "a.swift":1:2
43+
%r = tuple ()
44+
return %r : $()
45+
}
46+
47+
// CHECK-LABEL: sil @remove_before_next_location
48+
// CHECK-NOT: debug_step
49+
// CHECK: } // end sil function 'remove_before_next_location'
50+
sil @remove_before_next_location : $@convention(thin) () -> () {
51+
bb0:
52+
%f = function_ref @f : $@convention(thin) () -> ()
53+
%a = apply %f() : $@convention(thin) () -> ()
54+
debug_step, loc "a.swift":1:2
55+
%r = tuple (), loc "<compiler-generated>":0:0
56+
return %r : $(), loc "a.swift":1:2
57+
}
58+
59+
// CHECK-LABEL: sil @dont_remove_before_next_location
60+
// CHECK: debug_step
61+
// CHECK: } // end sil function 'dont_remove_before_next_location'
62+
sil @dont_remove_before_next_location : $@convention(thin) () -> () {
63+
bb0:
64+
%f = function_ref @f : $@convention(thin) () -> ()
65+
%a = apply %f() : $@convention(thin) () -> ()
66+
debug_step, loc "a.swift":1:2
67+
%r = tuple (), loc "a.swift":27:2
68+
return %r : $(), loc "a.swift":1:2
69+
}
70+
71+
// CHECK-LABEL: sil @remove_two_before_next_location
72+
// CHECK-NOT: debug_step
73+
// CHECK: } // end sil function 'remove_two_before_next_location'
74+
sil @remove_two_before_next_location : $@convention(thin) () -> () {
75+
bb0:
76+
%f = function_ref @f : $@convention(thin) () -> ()
77+
%a = apply %f() : $@convention(thin) () -> ()
78+
debug_step, loc "a.swift":1:2
79+
debug_step, loc "a.swift":1:2
80+
%r = tuple (), loc "<compiler-generated>":0:0
81+
return %r : $(), loc "a.swift":1:2
82+
}
83+
84+
// CHECK-LABEL: sil @remove_null_location
85+
// CHECK-NOT: debug_step
86+
// CHECK: } // end sil function 'remove_null_location'
87+
sil @remove_null_location : $@convention(thin) () -> () {
88+
bb0:
89+
%f = function_ref @f : $@convention(thin) () -> ()
90+
%a = apply %f() : $@convention(thin) () -> ()
91+
debug_step, loc "<compiler-generated>":0:0
92+
%r = tuple ()
93+
return %r : $(), loc "a.swift":1:2
94+
}
95+
// CHECK-LABEL: sil @dont_remove_one
96+
// CHECK: debug_step
97+
// CHECK: } // end sil function 'dont_remove_one'
98+
sil @dont_remove_one : $@convention(thin) () -> () {
99+
bb0:
100+
%f = function_ref @f : $@convention(thin) () -> ()
101+
%a = apply %f() : $@convention(thin) () -> ()
102+
debug_step, loc "a.swift":1:2
103+
%r = tuple ()
104+
return %r : $()
105+
}
106+
107+
// CHECK-LABEL: sil @replace_three_with_one
108+
// CHECK: apply
109+
// CHECK-NEXT: debug_step , loc "a.swift":1:2
110+
// CHECK-NEXT: tuple
111+
// CHECK: } // end sil function 'replace_three_with_one'
112+
sil @replace_three_with_one : $@convention(thin) () -> () {
113+
bb0:
114+
%f = function_ref @f : $@convention(thin) () -> ()
115+
%a = apply %f() : $@convention(thin) () -> ()
116+
debug_step, loc "a.swift":1:2
117+
debug_step, loc "a.swift":1:2
118+
debug_step, loc "a.swift":1:2
119+
%r = tuple ()
120+
return %r : $()
121+
}
122+
123+
// CHECK-LABEL: sil @replace_three_with_two
124+
// CHECK: apply
125+
// CHECK-NEXT: debug_step , loc "a.swift":1:2
126+
// CHECK-NEXT: debug_step , loc "a.swift":2:3
127+
// CHECK-NEXT: tuple
128+
// CHECK: } // end sil function 'replace_three_with_two'
129+
sil @replace_three_with_two : $@convention(thin) () -> () {
130+
bb0:
131+
%f = function_ref @f : $@convention(thin) () -> ()
132+
%a = apply %f() : $@convention(thin) () -> ()
133+
debug_step, loc "a.swift":1:2
134+
debug_step, loc "a.swift":2:3
135+
debug_step, loc "a.swift":2:3
136+
%r = tuple ()
137+
return %r : $()
138+
}
139+
140+
141+
// CHECK-LABEL: sil @dont_remove_two
142+
// CHECK: debug_step
143+
// CHECK-NEXT: debug_step
144+
// CHECK: } // end sil function 'dont_remove_two'
145+
sil @dont_remove_two : $@convention(thin) () -> () {
146+
bb0:
147+
%f = function_ref @f : $@convention(thin) () -> ()
148+
%a = apply %f() : $@convention(thin) () -> ()
149+
debug_step, loc "a.swift":1:2
150+
debug_step, loc "a.swift":2:3
151+
%r = tuple ()
152+
return %r : $()
153+
}
154+

0 commit comments

Comments
 (0)