Skip to content

Commit a9c6da6

Browse files
authored
Merge pull request #80819 from eeckstein/copy-block-optimization
Optimizer: remove redundant `copy_block` instructions
2 parents 7ce06dd + 199eb1d commit a9c6da6

File tree

7 files changed

+172
-1
lines changed

7 files changed

+172
-1
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ swift_compiler_sources(Optimizer
1919
SimplifyCondBranch.swift
2020
SimplifyCondFail.swift
2121
SimplifyConvertEscapeToNoEscape.swift
22+
SimplifyCopyBlock.swift
2223
SimplifyCopyValue.swift
2324
SimplifyDebugStep.swift
2425
SimplifyDestroyValue.swift
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===--- SimplifyCopyBlock.swift ------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2025 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+
extension CopyBlockInst : Simplifiable, SILCombineSimplifiable {
16+
17+
/// Removes a `copy_block` if its only uses, beside ownership instructions, are callees of function calls
18+
/// ```
19+
/// %2 = copy_block %0
20+
/// %3 = begin_borrow [lexical] %2
21+
/// %4 = apply %3() : $@convention(block) @noescape () -> ()
22+
/// end_borrow %3
23+
/// destroy_value %2
24+
/// ```
25+
/// ->
26+
/// ```
27+
/// %4 = apply %0() : $@convention(block) @noescape () -> ()
28+
/// ```
29+
///
30+
func simplify(_ context: SimplifyContext) {
31+
if hasValidUses(block: self) {
32+
replaceBlock( self, with: operand.value, context)
33+
context.erase(instruction: self)
34+
}
35+
}
36+
}
37+
38+
private func hasValidUses(block: Value) -> Bool {
39+
for use in block.uses {
40+
switch use.instruction {
41+
case let beginBorrow as BeginBorrowInst:
42+
if !hasValidUses(block: beginBorrow) {
43+
return false
44+
}
45+
case let apply as FullApplySite where apply.isCallee(operand: use):
46+
break
47+
case is EndBorrowInst, is DestroyValueInst:
48+
break
49+
default:
50+
return false
51+
}
52+
}
53+
return true
54+
}
55+
56+
private func replaceBlock(_ block: Value, with original: Value, _ context: SimplifyContext) {
57+
for use in block.uses {
58+
switch use.instruction {
59+
case let beginBorrow as BeginBorrowInst:
60+
replaceBlock(beginBorrow, with: original, context)
61+
context.erase(instruction: beginBorrow)
62+
case is FullApplySite:
63+
use.set(to: original, context)
64+
case is EndBorrowInst, is DestroyValueInst:
65+
context.erase(instruction: use.instruction)
66+
default:
67+
fatalError("unhandled use")
68+
}
69+
}
70+
}

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ private func registerSwiftPasses() {
114114
registerForSILCombine(LoadInst.self, { run(LoadInst.self, $0) })
115115
registerForSILCombine(LoadBorrowInst.self, { run(LoadBorrowInst.self, $0) })
116116
registerForSILCombine(CopyValueInst.self, { run(CopyValueInst.self, $0) })
117+
registerForSILCombine(CopyBlockInst.self, { run(CopyBlockInst.self, $0) })
117118
registerForSILCombine(DestroyValueInst.self, { run(DestroyValueInst.self, $0) })
118119
registerForSILCombine(DestructureStructInst.self, { run(DestructureStructInst.self, $0) })
119120
registerForSILCombine(DestructureTupleInst.self, { run(DestructureTupleInst.self, $0) })

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,7 @@ class ThinToThickFunctionInst : SingleValueInstruction, UnaryInstruction {
10711071
final public class ThickToObjCMetatypeInst : SingleValueInstruction {}
10721072
final public class ObjCToThickMetatypeInst : SingleValueInstruction {}
10731073

1074-
final public class CopyBlockInst : SingleValueInstruction {}
1074+
final public class CopyBlockInst : SingleValueInstruction, UnaryInstruction {}
10751075
final public class CopyBlockWithoutEscapingInst : SingleValueInstruction {}
10761076

10771077
final public

lib/SILOptimizer/SILCombiner/Simplifications.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ INSTRUCTION_SIMPLIFICATION(ReleaseValueInst)
4141
INSTRUCTION_SIMPLIFICATION(LoadInst)
4242
INSTRUCTION_SIMPLIFICATION(LoadBorrowInst)
4343
INSTRUCTION_SIMPLIFICATION(CopyValueInst)
44+
INSTRUCTION_SIMPLIFICATION(CopyBlockInst)
4445
INSTRUCTION_SIMPLIFICATION(DestroyValueInst)
4546
INSTRUCTION_SIMPLIFICATION(DestructureStructInst)
4647
INSTRUCTION_SIMPLIFICATION(DestructureTupleInst)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
// RUN: %target-swift-frontend -I %t %t/test.swift -O -emit-sil | %FileCheck %s
4+
5+
// REQUIRES: objc_interop
6+
7+
//--- module.modulemap
8+
9+
module CModule {
10+
header "c-header.h"
11+
export *
12+
}
13+
14+
15+
//--- c-header.h
16+
17+
@import Foundation;
18+
19+
@interface TestClass : NSObject
20+
- (void)callHandlerInline: (NS_NOESCAPE _Nonnull dispatch_block_t)block;
21+
@end
22+
23+
24+
//--- test.swift
25+
26+
import CModule
27+
28+
@objc @implementation
29+
extension TestClass {
30+
// CHECK-LABEL: sil private [thunk] @$sSo9TestClassC4testE17callHandlerInlineyyyyXEFTo :
31+
// CHECK-NOT: copy_block
32+
// CHECK: apply %0
33+
// CHECK-NOT: destroy_value
34+
// CHECK: } // end sil function '$sSo9TestClassC4testE17callHandlerInlineyyyyXEFTo'
35+
func callHandlerInline(_ handler: () -> Void) {
36+
handler()
37+
}
38+
}
39+
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %target-sil-opt %s -simplification -simplify-instruction=copy_block | %FileCheck %s
2+
3+
// REQUIRES: objc_interop
4+
5+
sil_stage canonical
6+
7+
import Swift
8+
import SwiftShims
9+
import Builtin
10+
11+
// CHECK-LABEL: sil [ossa] @remove_copy_block :
12+
// CHECK-NOT: copy_block
13+
// CHECK: apply %0
14+
// CHECK-NOT: destroy_value
15+
// CHECK: } // end sil function 'remove_copy_block'
16+
sil [ossa] @remove_copy_block : $@convention(thin) (@convention(block) @noescape () -> ()) -> () {
17+
bb0(%0 : @unowned $@convention(block) @noescape () -> ()):
18+
%2 = copy_block %0
19+
%3 = begin_borrow [lexical] %2
20+
%4 = apply %3() : $@convention(block) @noescape () -> ()
21+
end_borrow %3
22+
destroy_value %2
23+
%7 = tuple ()
24+
return %7
25+
}
26+
27+
// CHECK-LABEL: sil [ossa] @dont_remove_copied_block :
28+
// CHECK: copy_block
29+
// CHECK: } // end sil function 'dont_remove_copied_block'
30+
sil [ossa] @dont_remove_copied_block : $@convention(thin) (@convention(block) @noescape () -> ()) -> () {
31+
bb0(%0 : @unowned $@convention(block) @noescape () -> ()):
32+
%2 = copy_block %0
33+
%3 = begin_borrow [lexical] %2
34+
%4 = apply %3() : $@convention(block) @noescape () -> ()
35+
%5 = copy_value %3
36+
fix_lifetime %5
37+
destroy_value %5
38+
end_borrow %3
39+
destroy_value %2
40+
%7 = tuple ()
41+
return %7
42+
}
43+
44+
sil [ossa] @use_block : $@convention(thin) (@convention(block) @noescape () -> ()) -> ()
45+
46+
// CHECK-LABEL: sil [ossa] @dont_remove_escaping_block :
47+
// CHECK: copy_block
48+
// CHECK: } // end sil function 'dont_remove_escaping_block'
49+
sil [ossa] @dont_remove_escaping_block : $@convention(thin) (@convention(block) @noescape () -> ()) -> () {
50+
bb0(%0 : @unowned $@convention(block) @noescape () -> ()):
51+
%2 = copy_block %0
52+
%3 = begin_borrow [lexical] %2
53+
%4 = function_ref @use_block : $@convention(thin) (@convention(block) @noescape () -> ()) -> ()
54+
%5 = apply %4(%3) : $@convention(thin) (@convention(block) @noescape () -> ()) -> ()
55+
end_borrow %3
56+
destroy_value %2
57+
%7 = tuple ()
58+
return %7
59+
}

0 commit comments

Comments
 (0)