Skip to content

Commit 55c8c43

Browse files
committed
SILOptimizer: add the StripObjectHeader optimization pass
It sets the `[bare]` attribute for `alloc_ref` and `global_value` instructions if their header (reference count and metatype) is not used throughout the lifetime of the object.
1 parent 625619e commit 55c8c43

File tree

7 files changed

+145
-1
lines changed

7 files changed

+145
-1
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ swift_compiler_sources(Optimizer
1919
ReleaseDevirtualizer.swift
2020
SimplificationPasses.swift
2121
StackPromotion.swift
22+
StripObjectHeaders.swift
2223
)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//===--- StripObjectHeaders.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+
/// Sets the `[bare]` attribute for `alloc_ref` and `global_value` instructions
16+
/// if their header (reference count and metatype) is not used throughout the
17+
/// lifetime of the object.
18+
///
19+
let stripObjectHeadersPass = FunctionPass(name: "strip-object-headers") {
20+
(function: Function, context: FunctionPassContext) in
21+
22+
for inst in function.instructions {
23+
switch inst {
24+
case let gv as GlobalValueInst:
25+
if !gv.isBare && !gv.needObjectHeader(context) {
26+
gv.setIsBare(context)
27+
}
28+
case let ar as AllocRefInst:
29+
if !ar.isBare && !ar.needObjectHeader(context) {
30+
ar.setIsBare(context)
31+
}
32+
default:
33+
break
34+
}
35+
}
36+
}
37+
38+
private extension Value {
39+
func needObjectHeader(_ context: FunctionPassContext) -> Bool {
40+
var walker = IsBareObjectWalker()
41+
return walker.walkDownUses(ofValue: self, path: SmallProjectionPath()) == .abortWalk
42+
}
43+
}
44+
45+
private struct IsBareObjectWalker : ValueDefUseWalker {
46+
var walkDownCache = WalkerCache<SmallProjectionPath>()
47+
48+
mutating func walkDown(value operand: Operand, path: Path) -> WalkResult {
49+
switch operand.instruction {
50+
case is StructInst, is TupleInst, is EnumInst,
51+
is StructExtractInst, is TupleExtractInst, is UncheckedEnumDataInst,
52+
is DestructureStructInst, is DestructureTupleInst,
53+
is BeginBorrowInst, is MarkDependenceInst,
54+
is BranchInst, is CondBranchInst, is SwitchEnumInst,
55+
is UpcastInst, is UncheckedRefCastInst,
56+
is EndCOWMutationInst:
57+
return walkDownDefault(value: operand, path: path)
58+
default:
59+
return leafUse(value: operand, path: path)
60+
}
61+
}
62+
63+
mutating func leafUse(value operand: Operand, path: SmallProjectionPath) -> WalkResult {
64+
switch operand.instruction {
65+
case is RefElementAddrInst, is RefTailAddrInst,
66+
is DeallocRefInst, is DeallocStackRefInst, is SetDeallocatingInst,
67+
is DebugValueInst, is FixLifetimeInst:
68+
return .continueWalk
69+
default:
70+
return .abortWalk
71+
}
72+
}
73+
}

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ private func registerSwiftPasses() {
8181
registerPass(lateOnoneSimplificationPass, { lateOnoneSimplificationPass.run($0) })
8282
registerPass(cleanupDebugStepsPass, { cleanupDebugStepsPass.run($0) })
8383
registerPass(namedReturnValueOptimization, { namedReturnValueOptimization.run($0) })
84+
registerPass(stripObjectHeadersPass, { stripObjectHeadersPass.run($0) })
8485

8586
// Instruction passes
8687
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
@@ -401,6 +401,8 @@ SWIFT_FUNCTION_PASS(CleanupDebugSteps, "cleanup-debug-steps",
401401
"Cleanup debug_step instructions for Onone")
402402
SWIFT_FUNCTION_PASS(NamedReturnValueOptimization, "named-return-value-optimization",
403403
"Optimize copies to an indirect return value")
404+
SWIFT_FUNCTION_PASS(StripObjectHeaders, "strip-object-headers",
405+
"Sets the bare flag on objects which don't need their headers")
404406
PASS(SimplifyBBArgs, "simplify-bb-args",
405407
"SIL Block Argument Simplification")
406408
PASS(SimplifyCFG, "simplify-cfg",

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,7 @@ static void addLateLoopOptPassPipeline(SILPassPipelinePlan &P) {
807807
P.addDCE();
808808
P.addSILCombine();
809809
P.addSimplifyCFG();
810+
P.addStripObjectHeaders();
810811

811812
// Try to hoist all releases, including epilogue releases. This should be
812813
// after FSO.

test/SILOptimizer/static_arrays.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
public let globalVariable = [ 100, 101, 102 ]
9999

100100
// CHECK-LABEL: sil [noinline] @$s4test11arrayLookupyS2iF
101-
// CHECK: global_value @$s4test11arrayLookupyS2iFTv_
101+
// CHECK: global_value [bare] @$s4test11arrayLookupyS2iFTv_
102102
// CHECK-NOT: retain
103103
// CHECK-NOT: release
104104
// CHECK: } // end sil function '$s4test11arrayLookupyS2iF'
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// RUN: %target-sil-opt %s -strip-object-headers | %FileCheck %s
2+
3+
// REQUIRES: swift_in_compiler
4+
5+
sil_stage canonical
6+
7+
import Builtin
8+
import Swift
9+
import SwiftShims
10+
11+
class B {
12+
}
13+
class C : B {
14+
@_hasStorage var a: Int
15+
}
16+
17+
sil_global @g : $C
18+
19+
sil @unknown_func : $@convention(thin) (@guaranteed C) -> ()
20+
21+
// CHECK-LABEL: sil @test_alloc_ref :
22+
// CHECK: alloc_ref [bare] [stack] $C
23+
// CHECK: } // end sil function 'test_alloc_ref'
24+
sil @test_alloc_ref : $@convention(thin) () -> () {
25+
bb0:
26+
%0 = alloc_ref [stack] $C
27+
debug_value %0 : $C, let, name "x"
28+
%1 = upcast %0 : $C to $B
29+
%2 = end_cow_mutation %1 : $B
30+
%3 = unchecked_ref_cast %2 : $B to $C
31+
%4 = ref_element_addr %3 : $C, #C.a
32+
%5 = ref_tail_addr %3 : $C, $Int
33+
set_deallocating %0 : $C
34+
fix_lifetime %0 : $C
35+
dealloc_ref %0 : $C
36+
dealloc_stack_ref %0 : $C
37+
%6 = tuple ()
38+
return %6 : $()
39+
}
40+
41+
// CHECK-LABEL: sil @test_global_value :
42+
// CHECK: global_value [bare] @g
43+
// CHECK: } // end sil function 'test_global_value'
44+
sil @test_global_value : $@convention(thin) () -> () {
45+
bb0:
46+
%0 = global_value @g : $C
47+
debug_value %0 : $C, let, name "x"
48+
%4 = ref_element_addr %0 : $C, #C.a
49+
%5 = ref_tail_addr %0 : $C, $Int
50+
%6 = tuple ()
51+
return %6 : $()
52+
}
53+
54+
// CHECK-LABEL: sil @test_not_bare :
55+
// CHECK: alloc_ref [stack] $C
56+
// CHECK: } // end sil function 'test_not_bare'
57+
sil @test_not_bare : $@convention(thin) () -> () {
58+
bb0:
59+
%0 = alloc_ref [stack] $C
60+
%1 = function_ref @unknown_func : $@convention(thin) (@guaranteed C) -> ()
61+
%2 = apply %1(%0) : $@convention(thin) (@guaranteed C) -> ()
62+
dealloc_stack_ref %0 : $C
63+
%6 = tuple ()
64+
return %6 : $()
65+
}
66+

0 commit comments

Comments
 (0)