Skip to content

Commit 960ca70

Browse files
committed
Swift Optimizer: add the module pass ReadOnlyGlobalVariables
It marks global `var` variables as `let` if they are never written.
1 parent 88a4a97 commit 960ca70

File tree

5 files changed

+124
-0
lines changed

5 files changed

+124
-0
lines changed

SwiftCompilerSources/Sources/Optimizer/ModulePasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
swift_compiler_sources(Optimizer
10+
ReadOnlyGlobalVariables.swift
1011
StackProtection.swift
1112
)
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===--- ReadOnlyGlobalVariables.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+
/// Marks global `var` variables as `let` if they are never written.
16+
///
17+
/// Note that this pass relies on the initialize-static-globals pass which converts lazily
18+
/// initialized globals to statically initialized globals.
19+
/// This pass does not mark lazily initialized globals as `let`, because such globals _are_
20+
/// written: in their initializers.
21+
///
22+
let readOnlyGlobalVariablesPass = ModulePass(name: "read-only-global-variables") {
23+
(moduleContext: ModulePassContext) in
24+
25+
var writtenGlobals = Set<GlobalVariable>()
26+
27+
for f in moduleContext.functions {
28+
for inst in f.instructions {
29+
if let gAddr = inst as? GlobalAddrInst {
30+
if gAddr.addressHasWrites {
31+
writtenGlobals.insert(gAddr.global)
32+
}
33+
}
34+
}
35+
}
36+
37+
for g in moduleContext.globalVariables {
38+
if !g.isPossiblyUsedExternally,
39+
!g.isLet,
40+
!writtenGlobals.contains(g) {
41+
g.setIsLet(to: true, moduleContext)
42+
}
43+
}
44+
}
45+
46+
private extension Value {
47+
var addressHasWrites: Bool {
48+
var walker = FindWrites()
49+
return walker.walkDownUses(ofAddress: self, path: UnusedWalkingPath()) == .abortWalk
50+
}
51+
}
52+
53+
private struct FindWrites : AddressDefUseWalker {
54+
mutating func leafUse(address: Operand, path: UnusedWalkingPath) -> WalkResult {
55+
switch address.instruction {
56+
case is LoadInst, is LoadBorrowInst:
57+
return .continueWalk
58+
59+
case let ca as CopyAddrInst:
60+
return address == ca.sourceOperand ? .continueWalk : .abortWalk
61+
62+
case let apply as FullApplySite:
63+
if let callerArgIdx = apply.argumentIndex(of: address) {
64+
let calleeArgIdx = apply.calleeArgIndex(callerArgIndex: callerArgIdx)
65+
let convention = apply.getArgumentConvention(calleeArgIndex: calleeArgIdx)
66+
if convention.isIndirectIn {
67+
return .continueWalk
68+
}
69+
}
70+
return .abortWalk
71+
default:
72+
return .abortWalk
73+
}
74+
}
75+
}

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ private func registerForSILCombine<InstType: SILCombineSimplifyable>(
6161

6262
private func registerSwiftPasses() {
6363
// Module passes
64+
registerPass(readOnlyGlobalVariablesPass, { readOnlyGlobalVariablesPass.run($0) })
6465
registerPass(stackProtection, { stackProtection.run($0) })
6566

6667
// Function passes

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,8 @@ SWIFT_FUNCTION_PASS(SILPrinter, "sil-printer",
384384
"Test pass which prints the SIL of a function")
385385
SWIFT_MODULE_PASS(FunctionUsesDumper, "dump-function-uses",
386386
"Dump the results of FunctionUses")
387+
SWIFT_MODULE_PASS(ReadOnlyGlobalVariablesPass, "read-only-global-variables",
388+
"Converts read-only var-globals to let-globals")
387389
SWIFT_MODULE_PASS(StackProtection, "stack-protection",
388390
"Decides which functions need stack protectors")
389391
SWIFT_FUNCTION_PASS(FunctionStackProtection, "function-stack-protection",
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -read-only-global-variables | %FileCheck %s
2+
3+
// REQUIRES: swift_in_compiler
4+
5+
import Builtin
6+
import Swift
7+
8+
// CHECK-LABEL: sil_global private [let] @read_only_var : $Int32 = {
9+
sil_global private @read_only_var : $Int32 = {
10+
%0 = integer_literal $Builtin.Int32, 27
11+
%initval = struct $Int32 (%0 : $Builtin.Int32)
12+
}
13+
14+
// CHECK-LABEL: sil_global private @written_var : $Int32 = {
15+
sil_global private @written_var : $Int32 = {
16+
%0 = integer_literal $Builtin.Int32, 27
17+
%initval = struct $Int32 (%0 : $Builtin.Int32)
18+
}
19+
20+
// CHECK-LABEL: sil_global @public_var : $Int32 = {
21+
sil_global @public_var : $Int32 = {
22+
%0 = integer_literal $Builtin.Int32, 27
23+
%initval = struct $Int32 (%0 : $Builtin.Int32)
24+
}
25+
26+
sil @unknown_read_func : $@convention(thin) (@in Int32) -> ()
27+
28+
sil @read_var : $@convention(thin) (@inout Int32) -> Int32 {
29+
bb0(%0 : $*Int32):
30+
%1 = global_addr @read_only_var : $*Int32
31+
%2 = load %1 : $*Int32
32+
copy_addr %1 to %0 : $*Int32
33+
%4 = function_ref @unknown_read_func : $@convention(thin) (@in Int32) -> ()
34+
%5 = apply %4(%1) : $@convention(thin) (@in Int32) -> ()
35+
return %2 : $Int32
36+
}
37+
38+
sil @write_var : $@convention(thin) (Int32) -> () {
39+
bb0(%0 : $Int32):
40+
%1 = global_addr @written_var : $*Int32
41+
store %0 to %1 : $*Int32
42+
%r = tuple ()
43+
return %r : $()
44+
}
45+

0 commit comments

Comments
 (0)