Skip to content

Commit 2e12d6b

Browse files
author
Joe Shajrawi
authored
Merge pull request #18892 from shajrawi/class_paths
[Exclusivity] Teach MemAccessUtils about Projection Paths
2 parents 4fb95a8 + 5e7ea88 commit 2e12d6b

File tree

5 files changed

+118
-16
lines changed

5 files changed

+118
-16
lines changed

benchmark/single-source/ExistentialPerformance.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import TestsUtils
1414

1515
public let ExistentialPerformance = [
16+
BenchmarkInfo(name: "DistinctClassFieldAccesses", runFunction: run_DistinctClassFieldAccesses, tags: [.unstable, .api, .Array]),
1617
BenchmarkInfo(name: "ExistentialTestArrayConditionalShift_ClassValueBuffer1", runFunction: run_ExistentialTestArrayConditionalShift_ClassValueBuffer1, tags: [.unstable, .api, .Array]),
1718
BenchmarkInfo(name: "ExistentialTestArrayConditionalShift_ClassValueBuffer2", runFunction: run_ExistentialTestArrayConditionalShift_ClassValueBuffer2, tags: [.unstable, .api, .Array]),
1819
BenchmarkInfo(name: "ExistentialTestArrayConditionalShift_ClassValueBuffer3", runFunction: run_ExistentialTestArrayConditionalShift_ClassValueBuffer3, tags: [.unstable, .api, .Array]),
@@ -114,6 +115,43 @@ public let ExistentialPerformance = [
114115
BenchmarkInfo(name: "ExistentialTestTwoMethodCalls_IntValueBuffer4", runFunction: run_ExistentialTestTwoMethodCalls_IntValueBuffer4, tags: [.unstable]),
115116
]
116117

118+
class ClassWithArrs {
119+
var N: Int = 0
120+
var A: [Int]
121+
var B: [Int]
122+
123+
init(N: Int) {
124+
self.N = N
125+
126+
A = [Int](repeating: 0, count: N)
127+
B = [Int](repeating: 0, count: N)
128+
}
129+
130+
func readArr() {
131+
for i in 0..<self.N {
132+
for j in 0..<i {
133+
let _ = A[j]
134+
let _ = B[j]
135+
}
136+
}
137+
}
138+
139+
func writeArr() {
140+
for i in 0..<self.N {
141+
A[i] = i
142+
B[i] = i
143+
}
144+
}
145+
}
146+
147+
public func run_DistinctClassFieldAccesses(_ N: Int) {
148+
let workload = ClassWithArrs(N: 100)
149+
for _ in 1...N {
150+
workload.writeArr()
151+
workload.readArr()
152+
}
153+
}
154+
117155
protocol Existential {
118156
init()
119157
func doIt() -> Bool

include/swift/SIL/MemAccessUtils.h

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,44 @@ inline bool accessKindMayConflict(SILAccessKind a, SILAccessKind b) {
2929
}
3030

3131
/// Represents the identity of a stored class property as a combination
32-
/// of a base and a single projection. Eventually the goal is to make this
33-
/// more precise and consider, casts, etc.
32+
/// of a base, projection and projection path.
33+
/// we pre-compute the base and projection path, even though we can
34+
/// lazily do so, because it is more expensive otherwise
35+
/// We lazily compute the projection path,
36+
/// In the rare occasions we need it, because of Its destructor and
37+
/// its non-trivial copy constructor
3438
class ObjectProjection {
3539
SILValue object;
40+
const RefElementAddrInst *REA;
3641
Projection proj;
3742

3843
public:
39-
ObjectProjection(SILValue object, const Projection &proj)
40-
: object(object), proj(proj) {
41-
assert(object->getType().isObject());
42-
}
44+
ObjectProjection(const RefElementAddrInst *REA)
45+
: object(stripBorrow(REA->getOperand())), REA(REA),
46+
proj(Projection(REA)) {}
47+
48+
ObjectProjection(SILValue object, const RefElementAddrInst *REA)
49+
: object(object), REA(REA), proj(Projection(REA)) {}
50+
51+
const RefElementAddrInst *getInstr() const { return REA; }
4352

4453
SILValue getObject() const { return object; }
54+
4555
const Projection &getProjection() const { return proj; }
4656

57+
const ProjectionPath getProjectionPath() const {
58+
return ProjectionPath::getProjectionPath(stripBorrow(REA->getOperand()),
59+
REA)
60+
.getValue();
61+
}
62+
4763
bool operator==(const ObjectProjection &other) const {
48-
return object == other.object && proj == other.proj;
64+
return getObject() == other.getObject() &&
65+
getProjection() == other.getProjection();
4966
}
5067

5168
bool operator!=(const ObjectProjection &other) const {
52-
return object != other.object || proj != other.proj;
69+
return !operator==(other);
5370
}
5471
};
5572

@@ -168,8 +185,8 @@ class AccessedStorage {
168185

169186
AccessedStorage(SILValue base, Kind kind);
170187

171-
AccessedStorage(SILValue object, Projection projection)
172-
: objProj(object, projection) {
188+
AccessedStorage(SILValue object, const RefElementAddrInst *REA)
189+
: objProj(object, REA) {
173190
initKind(Class);
174191
}
175192

@@ -255,8 +272,19 @@ class AccessedStorage {
255272
}
256273

257274
bool isDistinctFrom(const AccessedStorage &other) const {
258-
return isUniquelyIdentified() && other.isUniquelyIdentified()
259-
&& !hasIdenticalBase(other);
275+
if (isUniquelyIdentified() && other.isUniquelyIdentified()) {
276+
return !hasIdenticalBase(other);
277+
}
278+
if (getKind() != Class || other.getKind() != Class) {
279+
return false;
280+
}
281+
if (getObjectProjection().getProjection() ==
282+
other.getObjectProjection().getProjection()) {
283+
return false;
284+
}
285+
auto projPath = getObjectProjection().getProjectionPath();
286+
auto otherProjPath = other.getObjectProjection().getProjectionPath();
287+
return projPath.hasNonEmptySymmetricDifference(otherProjPath);
260288
}
261289

262290
/// Returns the ValueDecl for the underlying storage, if it can be

lib/SIL/MemAccessUtils.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,7 @@ AccessedStorage::AccessedStorage(SILValue base, Kind kind) {
104104
// actually refer the same address) because these will be dynamically
105105
// checked.
106106
auto *REA = cast<RefElementAddrInst>(base);
107-
SILValue Object = stripBorrow(REA->getOperand());
108-
objProj = ObjectProjection(Object, Projection(REA));
107+
objProj = ObjectProjection(REA);
109108
}
110109
}
111110
}

lib/SILOptimizer/Analysis/AccessedStorageAnalysis.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,9 @@ transformCalleeStorage(const StorageAccessInfo &storage,
258258
if (auto *arg = dyn_cast<SILFunctionArgument>(obj)) {
259259
SILValue argVal = getCallerArg(fullApply, arg->getIndex());
260260
if (argVal) {
261-
auto &proj = storage.getObjectProjection().getProjection();
261+
auto *instr = storage.getObjectProjection().getInstr();
262262
// Remap the argument source value and inherit the old storage info.
263-
return StorageAccessInfo(AccessedStorage(argVal, proj), storage);
263+
return StorageAccessInfo(AccessedStorage(argVal, instr), storage);
264264
}
265265
}
266266
// Otherwise, continue to reference the value in the callee because we don't

test/SILOptimizer/licm_exclusivity.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// RUN: %target-swift-frontend -O -enforce-exclusivity=checked -emit-sil -Xllvm -debug-only=sil-licm -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST1
22
// RUN: %target-swift-frontend -O -enforce-exclusivity=checked -emit-sil -Xllvm -debug-only=sil-licm -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST2
33
// RUN: %target-swift-frontend -O -enforce-exclusivity=checked -emit-sil -primary-file %s | %FileCheck %s --check-prefix=TESTSIL
4+
// RUN: %target-swift-frontend -O -enforce-exclusivity=checked -emit-sil -Xllvm -debug-only=sil-licm -whole-module-optimization %s 2>&1 | %FileCheck %s --check-prefix=TEST3
5+
// RUN: %target-swift-frontend -O -enforce-exclusivity=checked -emit-sil -whole-module-optimization %s | %FileCheck %s --check-prefix=TESTSIL2
6+
47
// REQUIRES: optimized_stdlib,asserts
58

69
// TEST1-LABEL: Processing loops in {{.*}}run_ReversedArray{{.*}}
@@ -44,3 +47,37 @@ public func count_unicodeScalars(_ s: String.UnicodeScalarView) {
4447
count += 1
4548
}
4649
}
50+
51+
52+
public class ClassWithArrs {
53+
var N: Int = 0
54+
var A: [Int]
55+
var B: [Int]
56+
57+
init(N: Int) {
58+
self.N = N
59+
60+
A = [Int](repeating: 0, count: N)
61+
B = [Int](repeating: 0, count: N)
62+
}
63+
64+
// TEST3-LABEL: Processing loops in {{.*}}ClassWithArrsC7readArr{{.*}}
65+
// TEST3: Hoist and Sink pairs attempt
66+
// TEST3: Hoisted
67+
// TEST3: Successfully hosited and sank pair
68+
// TEST3: Hoisted
69+
// TEST3: Successfully hosited and sank pair
70+
// TESTSIL2-LABEL: sil @$S16licm_exclusivity13ClassWithArrsC7readArryyF : $@convention(method) (@guaranteed ClassWithArrs) -> () {
71+
// TESTSIL2: [[R1:%.*]] = ref_element_addr %0 : $ClassWithArrs, #ClassWithArrs.A
72+
// TESTSIL2: [[R2:%.*]] = ref_element_addr %0 : $ClassWithArrs, #ClassWithArrs.B
73+
// TESTSIL2: begin_access [read] [static] [no_nested_conflict] [[R1]]
74+
// TESTSIL2: begin_access [read] [static] [no_nested_conflict] [[R2]]
75+
public func readArr() {
76+
for i in 0..<self.N {
77+
for j in 0..<i {
78+
let _ = A[j]
79+
let _ = B[j]
80+
}
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)