Skip to content

Commit f3851f7

Browse files
committed
Optimizer: implement load simplification in Swift
1 parent 61857f3 commit f3851f7

File tree

9 files changed

+458
-232
lines changed

9 files changed

+458
-232
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift

Lines changed: 194 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,97 @@
1212

1313
import SIL
1414

15-
extension LoadInst : OnoneSimplifyable {
15+
extension LoadInst : OnoneSimplifyable, SILCombineSimplifyable {
1616
func simplify(_ context: SimplifyContext) {
17-
replaceLoadOfGlobalLet(context)
17+
if optimizeLoadOfAddrUpcast(context) {
18+
return
19+
}
20+
if optimizeLoadFromStringLiteral(context) {
21+
return
22+
}
23+
if optmizeLoadFromEmptyCollection(context) {
24+
return
25+
}
26+
if replaceLoadOfGlobalLet(context) {
27+
return
28+
}
29+
removeIfDead(context)
30+
}
31+
32+
/// ```
33+
/// %1 = unchecked_addr_cast %0 : $*DerivedClass to $*BaseClass
34+
/// %2 = load %1
35+
/// ```
36+
/// is transformed to
37+
/// ```
38+
/// %1 = load %0 : $*BaseClass
39+
/// %2 = upcast %1 : $DerivedClass to $BaseClass
40+
/// ```
41+
private func optimizeLoadOfAddrUpcast(_ context: SimplifyContext) -> Bool {
42+
if let uac = address as? UncheckedAddrCastInst,
43+
uac.type.isExactSuperclass(of: uac.fromAddress.type),
44+
uac.type != uac.fromAddress.type {
45+
46+
operand.set(to: uac.fromAddress, context)
47+
let builder = Builder(before: self, context)
48+
let newLoad = builder.createLoad(fromAddress: uac.fromAddress, ownership: ownership)
49+
let cast = builder.createUpcast(from: newLoad, to: type)
50+
uses.replaceAll(with: cast, context)
51+
context.erase(instruction: self)
52+
return true
53+
}
54+
return false
1855
}
1956

20-
private func replaceLoadOfGlobalLet(_ context: SimplifyContext) {
57+
/// The load from a string literal element, e.g.
58+
/// ```
59+
/// %0 = string_literal utf8 "abc"
60+
/// %1 = integer_literal $Builtin.Word, 1
61+
/// %2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*UInt8
62+
/// %3 = index_addr %2 : $*UInt8, %1 : $Builtin.Word
63+
/// %4 = struct_element_addr %3 : $*UInt8, #UInt8._value
64+
/// %5 = load %4 : $*Builtin.Int8
65+
/// ```
66+
/// is replaced by the character value, e.g. `98` in this example.
67+
///
68+
private func optimizeLoadFromStringLiteral(_ context: SimplifyContext) -> Bool {
69+
if self.type.isBuiltinInteger(withFixedWidth: 8),
70+
let sea = self.address as? StructElementAddrInst,
71+
let (baseAddr, index) = sea.struct.getBaseAddressAndOffset(),
72+
let pta = baseAddr as? PointerToAddressInst,
73+
let stringLiteral = pta.pointer as? StringLiteralInst,
74+
stringLiteral.encoding == .UTF8,
75+
index < stringLiteral.value.count {
76+
77+
let builder = Builder(before: self, context)
78+
let charLiteral = builder.createIntegerLiteral(Int(stringLiteral.value[index]), type: type)
79+
uses.replaceAll(with: charLiteral, context)
80+
context.erase(instruction: self)
81+
return true
82+
}
83+
return false
84+
}
85+
86+
/// Loading `count` or `capacity` from the empty `Array`, `Set` or `Dictionary` singleton
87+
/// is replaced by a 0 literal.
88+
private func optmizeLoadFromEmptyCollection(_ context: SimplifyContext) -> Bool {
89+
if self.isZeroLoadFromEmptyCollection() {
90+
let builder = Builder(before: self, context)
91+
let zeroLiteral = builder.createIntegerLiteral(0, type: type)
92+
uses.replaceAll(with: zeroLiteral, context)
93+
context.erase(instruction: self)
94+
return true
95+
}
96+
return false
97+
}
98+
99+
/// The load of a global let variable is replaced by its static initializer value.
100+
private func replaceLoadOfGlobalLet(_ context: SimplifyContext) -> Bool {
21101
guard let globalInitVal = getGlobalInitValue(address: address) else {
22-
return
102+
return false
23103
}
24104
if !globalInitVal.canBeCopied(into: parentFunction, context) {
25-
return
105+
return false
26106
}
27107
var cloner = StaticInitCloner(cloneBefore: self, context)
28108
defer { cloner.deinitialize() }
@@ -31,6 +111,94 @@ extension LoadInst : OnoneSimplifyable {
31111

32112
uses.replaceAll(with: initVal, context)
33113
transitivelyErase(load: self, context)
114+
return true
115+
}
116+
117+
private func isZeroLoadFromEmptyCollection() -> Bool {
118+
if !type.isBuiltinInteger {
119+
return false
120+
}
121+
var addr = address
122+
123+
// Find the root object of the load-address.
124+
while true {
125+
switch addr {
126+
case let ga as GlobalAddrInst:
127+
switch ga.global.name {
128+
case "_swiftEmptyArrayStorage",
129+
"_swiftEmptyDictionarySingleton",
130+
"_swiftEmptySetSingleton":
131+
return true
132+
default:
133+
return false
134+
}
135+
case let sea as StructElementAddrInst:
136+
let structType = sea.struct.type
137+
if structType.nominal.name == "_SwiftArrayBodyStorage" {
138+
switch structType.getNominalFields(in: parentFunction).getNameOfField(withIndex: sea.fieldIndex) {
139+
case "count":
140+
break
141+
case "_capacityAndFlags":
142+
if uses.contains(where: { !$0.instruction.isShiftRightByAtLeastOne }) {
143+
return false
144+
}
145+
default:
146+
return false
147+
}
148+
}
149+
addr = sea.struct
150+
case let rea as RefElementAddrInst:
151+
let classType = rea.instance.type
152+
switch classType.nominal.name {
153+
case "__RawDictionaryStorage",
154+
"__RawSetStorage":
155+
// For Dictionary and Set we support "count" and "capacity".
156+
switch classType.getNominalFields(in: parentFunction).getNameOfField(withIndex: rea.fieldIndex) {
157+
case "_count", "_capacity":
158+
break
159+
default:
160+
return false
161+
}
162+
default:
163+
break
164+
}
165+
addr = rea.instance
166+
case is UncheckedRefCastInst,
167+
is UpcastInst,
168+
is RawPointerToRefInst,
169+
is AddressToPointerInst,
170+
is BeginBorrowInst,
171+
is CopyValueInst,
172+
is EndCOWMutationInst:
173+
addr = (addr as! UnaryInstruction).operand.value
174+
case let mviResult as MultipleValueInstructionResult:
175+
guard let bci = mviResult.parentInstruction as? BeginCOWMutationInst else {
176+
return false
177+
}
178+
addr = bci.instance
179+
default:
180+
return false
181+
}
182+
}
183+
}
184+
185+
/// Removes the `load [copy]` if the loaded value is just destroyed.
186+
private func removeIfDead(_ context: SimplifyContext) {
187+
if ownership == .copy,
188+
loadedValueIsDead(context) {
189+
for use in uses {
190+
context.erase(instruction: use.instruction)
191+
}
192+
context.erase(instruction: self)
193+
}
194+
}
195+
196+
private func loadedValueIsDead(_ context: SimplifyContext) -> Bool {
197+
if context.preserveDebugInfo {
198+
return !uses.contains { !($0.instruction is DestroyValueInst) }
199+
} else {
200+
return !nonDebugUses.contains { !($0.instruction is DestroyValueInst) }
201+
}
34202
}
35203
}
36204

@@ -112,5 +280,26 @@ private extension Value {
112280
}
113281
return true
114282
}
283+
284+
func getBaseAddressAndOffset() -> (baseAddress: Value, offset: Int)? {
285+
if let indexAddr = self as? IndexAddrInst {
286+
guard let indexLiteral = indexAddr.index as? IntegerLiteralInst,
287+
indexLiteral.value.getActiveBits() <= 32 else {
288+
return nil
289+
}
290+
return (baseAddress: indexAddr.base, offset: Int(indexLiteral.value.getZExtValue()))
291+
}
292+
return (baseAddress: self, offset: 0)
293+
}
115294
}
116295

296+
private extension Instruction {
297+
var isShiftRightByAtLeastOne: Bool {
298+
guard let bi = self as? BuiltinInst,
299+
bi.id == .LShr,
300+
let shiftLiteral = bi.operands[1].value as? IntegerLiteralInst else {
301+
return false
302+
}
303+
return shiftLiteral.value.isStrictlyPositive()
304+
}
305+
}

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ private func registerSwiftPasses() {
8686
registerForSILCombine(GlobalValueInst.self, { run(GlobalValueInst.self, $0) })
8787
registerForSILCombine(StrongRetainInst.self, { run(StrongRetainInst.self, $0) })
8888
registerForSILCombine(StrongReleaseInst.self, { run(StrongReleaseInst.self, $0) })
89+
registerForSILCombine(LoadInst.self, { run(LoadInst.self, $0) })
8990

9091
// Test passes
9192
registerPass(functionUsesDumper, { functionUsesDumper.run($0) })

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ SWIFT_SILCOMBINE_PASS(BeginCOWMutationInst)
480480
SWIFT_SILCOMBINE_PASS(GlobalValueInst)
481481
SWIFT_SILCOMBINE_PASS(StrongRetainInst)
482482
SWIFT_SILCOMBINE_PASS(StrongReleaseInst)
483+
SWIFT_SILCOMBINE_PASS(LoadInst)
483484

484485
#undef IRGEN_PASS
485486
#undef SWIFT_MODULE_PASS

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ class SILCombiner :
251251
// NOTE: The load optimized in this method is a load [trivial].
252252
SILInstruction *optimizeLoadFromStringLiteral(LoadInst *li);
253253

254-
SILInstruction *visitLoadInst(LoadInst *LI);
255254
SILInstruction *visitLoadBorrowInst(LoadBorrowInst *LI);
256255
SILInstruction *visitIndexAddrInst(IndexAddrInst *IA);
257256
bool optimizeStackAllocatedEnum(AllocStackInst *AS);

0 commit comments

Comments
 (0)