Skip to content

Commit 7786596

Browse files
committed
RedundantLoadElimination: support redundant loads of array elements with non-constant index
For example: ``` func test3(_ arr: Array<Int>, index: Int) -> Int { return arr[index] + arr[index] } ``` rdar://138519664
1 parent f0633d5 commit 7786596

File tree

3 files changed

+80
-6
lines changed

3 files changed

+80
-6
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/RedundantLoadElimination.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ private extension LoadInst {
142142
}
143143

144144
func isRedundant(complexityBudget: inout Int, _ context: FunctionPassContext) -> DataflowResult {
145-
return isRedundant(at: address.accessPath, complexityBudget: &complexityBudget, context)
145+
return isRedundant(at: address.constantAccessPath, complexityBudget: &complexityBudget, context)
146146
}
147147

148148
func isRedundant(at accessPath: AccessPath, complexityBudget: inout Int, _ context: FunctionPassContext) -> DataflowResult {
@@ -282,7 +282,7 @@ private func provideValue(
282282
from availableValue: AvailableValue,
283283
_ context: FunctionPassContext
284284
) -> Value {
285-
let projectionPath = availableValue.address.accessPath.getMaterializableProjection(to: load.address.accessPath)!
285+
let projectionPath = availableValue.address.constantAccessPath.getMaterializableProjection(to: load.address.constantAccessPath)!
286286

287287
switch load.loadOwnership {
288288
case .unqualified:
@@ -483,7 +483,7 @@ private struct InstructionScanner {
483483
// This happens if the load is in a loop.
484484
return .available
485485
}
486-
let precedingLoadPath = precedingLoad.address.accessPath
486+
let precedingLoadPath = precedingLoad.address.constantAccessPath
487487
if precedingLoadPath.getMaterializableProjection(to: accessPath) != nil {
488488
availableValues.append(.viaLoad(precedingLoad))
489489
return .available
@@ -500,7 +500,7 @@ private struct InstructionScanner {
500500
if precedingStore.source is Undef {
501501
return .overwritten
502502
}
503-
let precedingStorePath = precedingStore.destination.accessPath
503+
let precedingStorePath = precedingStore.destination.constantAccessPath
504504
if precedingStorePath.getMaterializableProjection(to: accessPath) != nil {
505505
availableValues.append(.viaStore(precedingStore))
506506
return .available

test/SILOptimizer/redundant_load_elim_ossa.sil

+32
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,38 @@ bb0(%0 : $Builtin.RawPointer):
12881288
return %99 : $(Int, Int, Int, Int)
12891289
}
12901290

1291+
// CHECK-LABEL: sil [ossa] @non_const_index :
1292+
// CHECK: [[L:%.*]] = load
1293+
// CHECK-NOT: load
1294+
// CHECK: tuple ([[L]] : $Int, [[L]] : $Int)
1295+
// CHECK-LABEL: } // end sil function 'non_const_index'
1296+
sil [ossa] @non_const_index : $@convention(thin) (@guaranteed B, Builtin.Word) -> (Int, Int) {
1297+
bb0(%0 : @guaranteed $B, %1 : $Builtin.Word):
1298+
%2 = ref_tail_addr %0 : $B, $Int
1299+
%3 = index_addr %2 : $*Int, %1 : $Builtin.Word
1300+
%4 = load [trivial] %3 : $*Int
1301+
%5 = load [trivial] %3 : $*Int
1302+
%6 = tuple (%4 : $Int, %5 : $Int)
1303+
return %6 : $(Int, Int)
1304+
}
1305+
1306+
// CHECK-LABEL: sil [ossa] @non_const_index_aliasing :
1307+
// CHECK: [[L1:%.*]] = load
1308+
// CHECK: store
1309+
// CHECK: [[L2:%.*]] = load
1310+
// CHECK: tuple ([[L1]] : $Int, [[L2]] : $Int)
1311+
// CHECK-LABEL: } // end sil function 'non_const_index_aliasing'
1312+
sil [ossa] @non_const_index_aliasing : $@convention(thin) (@guaranteed B, Builtin.Word, Int) -> (Int, Int) {
1313+
bb0(%0 : @guaranteed $B, %1 : $Builtin.Word, %2 : $Int):
1314+
%3 = ref_tail_addr %0 : $B, $Int
1315+
%4 = index_addr %3 : $*Int, %1 : $Builtin.Word
1316+
%5 = load [trivial] %3 : $*Int
1317+
store %2 to [trivial] %4: $*Int
1318+
%7 = load [trivial] %3 : $*Int
1319+
%8 = tuple (%5 : $Int, %7 : $Int)
1320+
return %8 : $(Int, Int)
1321+
}
1322+
12911323
sil [ossa] @overwrite_int : $@convention(thin) (@inout Int, Int) -> ()
12921324

12931325
// Make sure that the store is forwarded to the load, ie. the load is

test/SILOptimizer/redundant_load_elimination.swift

+44-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// RUN: %target-swift-frontend -parse-as-library -module-name test %s -O -emit-sil | %FileCheck %s
22

3-
// REQUIRES: swift_in_compiler
4-
3+
// REQUIRES: swift_stdlib_no_asserts,optimized_stdlib
54

65
public final class C {
76
let i: Int
@@ -25,3 +24,46 @@ public func testLetField(_ c: C, f: () -> ()) -> (Int, Int) {
2524
return (a, b)
2625
}
2726

27+
let globalLetArray = [1, 2, 3, 4]
28+
29+
// CHECK-LABEL: sil @$s4test5test15indexS2i_tF :
30+
// CHECK: load
31+
// CHECK: [[L:%.*]] = load
32+
// CHECK-NOT: load
33+
// CHECK: builtin "sadd_with_overflow{{.*}}"([[L]] : {{.*}}, [[L]] :
34+
// CHECK: } // end sil function '$s4test5test15indexS2i_tF'
35+
public func test1(index: Int) -> Int {
36+
let elem1 = globalLetArray[index]
37+
let elem2 = globalLetArray[index]
38+
return elem1 + elem2
39+
}
40+
41+
struct Wrapper {
42+
let arr: Array<Int>
43+
init() {
44+
arr = [1, 2, 3, 4]
45+
}
46+
}
47+
48+
// CHECK-LABEL: sil @$s4test5test25indexS2i_tF :
49+
// CHECK: [[L:%.*]] = load
50+
// CHECK-NOT: load
51+
// CHECK: builtin "sadd_with_overflow{{.*}}"([[L]] : {{.*}}, [[L]] :
52+
// CHECK: } // end sil function '$s4test5test25indexS2i_tF'
53+
public func test2(index: Int) -> Int {
54+
let w = Wrapper()
55+
let elem1 = w.arr[index]
56+
let elem2 = w.arr[index]
57+
return elem1 + elem2
58+
}
59+
60+
// CHECK-LABEL: sil @$s4test5test3_5indexSiSaySiG_SitF :
61+
// CHECK: load
62+
// CHECK: [[L:%.*]] = load
63+
// CHECK-NOT: load
64+
// CHECK: builtin "sadd_with_overflow{{.*}}"([[L]] : {{.*}}, [[L]] :
65+
// CHECK: } // end sil function '$s4test5test3_5indexSiSaySiG_SitF'
66+
public func test3(_ arr: Array<Int>, index: Int) -> Int {
67+
return arr[index] + arr[index]
68+
}
69+

0 commit comments

Comments
 (0)