Skip to content

Commit 16804e6

Browse files
authored
Merge pull request #80849 from eeckstein/copy-block-optimization-6.2
[6.2] Optimizer: remove redundant copy_block instructions
2 parents 65f78f3 + 9f99428 commit 16804e6

File tree

9 files changed

+185
-1
lines changed

9 files changed

+185
-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: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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+
// Temporarily guarded with an experimental feature flag.
32+
if !context.options.hasFeature(.CopyBlockOptimization) {
33+
return
34+
}
35+
36+
if hasValidUses(block: self) {
37+
replaceBlock( self, with: operand.value, context)
38+
context.erase(instruction: self)
39+
}
40+
}
41+
}
42+
43+
private func hasValidUses(block: Value) -> Bool {
44+
for use in block.uses {
45+
switch use.instruction {
46+
case let beginBorrow as BeginBorrowInst:
47+
if !hasValidUses(block: beginBorrow) {
48+
return false
49+
}
50+
case let apply as FullApplySite where apply.isCallee(operand: use):
51+
break
52+
case is EndBorrowInst, is DestroyValueInst:
53+
break
54+
default:
55+
return false
56+
}
57+
}
58+
return true
59+
}
60+
61+
private func replaceBlock(_ block: Value, with original: Value, _ context: SimplifyContext) {
62+
for use in block.uses {
63+
switch use.instruction {
64+
case let beginBorrow as BeginBorrowInst:
65+
replaceBlock(beginBorrow, with: original, context)
66+
context.erase(instruction: beginBorrow)
67+
case is FullApplySite:
68+
use.set(to: original, context)
69+
case is EndBorrowInst, is DestroyValueInst:
70+
context.erase(instruction: use.instruction)
71+
default:
72+
fatalError("unhandled use")
73+
}
74+
}
75+
}

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

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,9 @@ EXPERIMENTAL_FEATURE(ClosureBodyMacro, true)
517517
/// Allow declarations of Swift runtime symbols using @_silgen_name.
518518
EXPERIMENTAL_FEATURE(AllowRuntimeSymbolDeclarations, true)
519519

520+
/// Optimize copies of ObjectiveC blocks.
521+
EXPERIMENTAL_FEATURE(CopyBlockOptimization, true)
522+
520523
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
521524
#undef EXPERIMENTAL_FEATURE
522525
#undef UPCOMING_FEATURE

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ UNINTERESTING_FEATURE(ImportNonPublicCxxMembers)
429429
UNINTERESTING_FEATURE(SuppressCXXForeignReferenceTypeInitializers)
430430
UNINTERESTING_FEATURE(CoroutineAccessorsUnwindOnCallerError)
431431
UNINTERESTING_FEATURE(AllowRuntimeSymbolDeclarations)
432+
UNINTERESTING_FEATURE(CopyBlockOptimization)
432433

433434
static bool usesFeatureSwiftSettings(const Decl *decl) {
434435
// We just need to guard `#SwiftSettings`.

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

0 commit comments

Comments
 (0)