Skip to content

Commit f0157b0

Browse files
authored
Merge pull request #28473 from atrick/arrayproperty-opt
2 parents 31370df + 4da33e1 commit f0157b0

File tree

7 files changed

+988
-935
lines changed

7 files changed

+988
-935
lines changed

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ PASS(StackPromotion, "stack-promotion",
295295
"Stack Promotion of Class Objects")
296296
PASS(StripDebugInfo, "strip-debug-info",
297297
"Strip Debug Information")
298-
PASS(SwiftArrayOpts, "array-specialize",
298+
PASS(SwiftArrayPropertyOpt, "array-property-opt",
299299
"Loop Specialization for Array Properties")
300300
PASS(UnsafeGuaranteedPeephole, "unsafe-guaranteed-peephole",
301301
"SIL retain/release Peephole Removal for Builtin.unsafeGuaranteed")
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
//===--- ArrayOpt.h ---------------------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 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+
/// Array optimization utilities.
14+
///
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "swift/SIL/InstructionUtils.h"
18+
#include "swift/SIL/Projection.h"
19+
#include "swift/SIL/SILInstruction.h"
20+
#include "llvm/ADT/SmallPtrSet.h"
21+
22+
namespace swift {
23+
24+
/// Collect all uses of a struct given an aggregate value that contains the
25+
/// struct and access path describing the projection of the aggregate
26+
/// that accesses the struct.
27+
///
28+
/// AggregateAddressUsers records uses of the aggregate value's address. These
29+
/// may indirectly access the struct's elements.
30+
///
31+
/// Projections over the aggregate that do not access the struct are ignored.
32+
///
33+
/// StructLoads records loads of the struct value.
34+
/// StructAddressUsers records other uses of the struct address.
35+
/// StructValueUsers records direct uses of the loaded struct.
36+
///
37+
/// Projections of the struct over its elements are all similarly recorded in
38+
/// ElementAddressUsers, ElementLoads, and ElementValueUsers.
39+
///
40+
/// bb0(%arg : $*S)
41+
/// apply %f(%arg) // <--- Aggregate Address User
42+
/// %struct_addr = struct_element_addr %arg : $*S, #S.element
43+
/// apply %g(%struct_addr) // <--- Struct Address User
44+
/// %val = load %struct_addr // <--- Struct Load
45+
/// apply %h(%val) // <--- Struct Value User
46+
/// %elt_addr = struct_element_addr %struct_addr : $*A, #A.element
47+
/// apply %i(%elt_addr) // <--- Element Address User
48+
/// %elt = load %elt_addr // <--- Element Load
49+
/// apply %j(%elt) // <--- Element Value User
50+
class StructUseCollector {
51+
public:
52+
typedef SmallPtrSet<Operand*, 16> VisitedSet;
53+
typedef SmallVector<SILInstruction*, 16> UserList;
54+
55+
/// Record the users of a value or an element within that value along with the
56+
/// operand that directly uses the value. Multiple levels of struct_extract
57+
/// may exist between the operand and the user instruction.
58+
typedef SmallVector<std::pair<SILInstruction*, Operand*>, 16> UserOperList;
59+
60+
/// \return a sequence of integers representing the access path of this
61+
/// element within a Struct/Ref/Tuple.
62+
///
63+
/// Do not form a path with an IndexAddrInst because we have no way to
64+
/// distinguish between indexing and subelement access. The same index could
65+
/// either refer to the next element (indexed) or a subelement.
66+
static SILValue getAccessPath(SILValue V, SmallVectorImpl<unsigned>& Path) {
67+
V = stripCasts(V);
68+
if (auto *IA = dyn_cast<IndexAddrInst>(V)) {
69+
// Don't include index_addr projections in the access path. We could if
70+
// the index is constant. For simplicity we just ignore them.
71+
V = stripCasts(IA->getBase());
72+
}
73+
ProjectionIndex PI(V);
74+
if (!PI.isValid())
75+
return V;
76+
77+
SILValue UnderlyingObject = getAccessPath(PI.Aggregate, Path);
78+
Path.push_back(PI.Index);
79+
return UnderlyingObject;
80+
}
81+
82+
UserList AggregateAddressUsers;
83+
UserList StructAddressUsers;
84+
SmallVector<LoadInst*, 16> StructLoads;
85+
UserList StructValueUsers;
86+
UserOperList ElementAddressUsers;
87+
SmallVector<std::pair<LoadInst*, Operand*>, 16> ElementLoads;
88+
UserOperList ElementValueUsers;
89+
VisitedSet Visited;
90+
91+
/// Collect all uses of the value at the given address.
92+
void collectUses(ValueBase *V, ArrayRef<unsigned> AccessPath) {
93+
// Save our old indent and increment.
94+
// Collect all users of the address and loads.
95+
collectAddressUses(V, AccessPath, nullptr);
96+
97+
// Collect all uses of the Struct value.
98+
for (auto *DefInst : StructLoads) {
99+
for (auto *DefUI : DefInst->getUses()) {
100+
if (!Visited.insert(&*DefUI).second) {
101+
continue;
102+
}
103+
104+
StructValueUsers.push_back(DefUI->getUser());
105+
}
106+
}
107+
108+
// Collect all users of element values.
109+
for (auto &Pair : ElementLoads) {
110+
for (auto *DefUI : Pair.first->getUses()) {
111+
if (!Visited.insert(&*DefUI).second) {
112+
continue;
113+
}
114+
115+
ElementValueUsers.push_back(
116+
std::make_pair(DefUI->getUser(), Pair.second));
117+
}
118+
}
119+
}
120+
121+
/// Returns true if there is a single address user of the value.
122+
bool hasSingleAddressUse(SILInstruction *SingleAddressUser) {
123+
if (!AggregateAddressUsers.empty())
124+
return false;
125+
if (!ElementAddressUsers.empty())
126+
return false;
127+
if (StructAddressUsers.size() != 1)
128+
return false;
129+
return StructAddressUsers[0] == SingleAddressUser;
130+
}
131+
132+
protected:
133+
134+
static bool definesSingleObjectType(ValueBase *V) {
135+
return V->getType().isObject();
136+
}
137+
138+
/// If AccessPathSuffix is non-empty, then the value is the address of an
139+
/// aggregate containing the Struct. If AccessPathSuffix is empty and
140+
/// StructVal is invalid, then the value is the address of the Struct. If
141+
/// StructVal is valid, the value is the address of an element within the
142+
/// Struct.
143+
void collectAddressUses(ValueBase *V, ArrayRef<unsigned> AccessPathSuffix,
144+
Operand *StructVal) {
145+
for (auto *UI : V->getUses()) {
146+
// Keep the operand, not the instruction in the visited set. The same
147+
// instruction may theoretically have different types of uses.
148+
if (!Visited.insert(&*UI).second) {
149+
continue;
150+
}
151+
152+
SILInstruction *UseInst = UI->getUser();
153+
154+
if (UseInst->isDebugInstruction())
155+
continue;
156+
157+
if (StructVal) {
158+
// Found a use of an element.
159+
assert(AccessPathSuffix.empty() && "should have accessed struct");
160+
if (auto *LoadI = dyn_cast<LoadInst>(UseInst)) {
161+
ElementLoads.push_back(std::make_pair(LoadI, StructVal));
162+
continue;
163+
}
164+
165+
if (auto proj = dyn_cast<StructElementAddrInst>(UseInst)) {
166+
collectAddressUses(proj, AccessPathSuffix, StructVal);
167+
continue;
168+
}
169+
170+
ElementAddressUsers.push_back(std::make_pair(UseInst,StructVal));
171+
continue;
172+
}
173+
174+
if (isa<UncheckedRefCastInst>(UseInst) || isa<IndexAddrInst>(UseInst)) {
175+
// Skip over unchecked_ref_cast and index_addr.
176+
collectAddressUses(cast<SingleValueInstruction>(UseInst),
177+
AccessPathSuffix, nullptr);
178+
continue;
179+
}
180+
181+
if (AccessPathSuffix.empty()) {
182+
// Found a use of the struct at the given access path.
183+
if (auto *LoadI = dyn_cast<LoadInst>(UseInst)) {
184+
StructLoads.push_back(LoadI);
185+
continue;
186+
}
187+
188+
if (auto proj = dyn_cast<StructElementAddrInst>(UseInst)) {
189+
collectAddressUses(proj, AccessPathSuffix, &*UI);
190+
continue;
191+
}
192+
193+
// Value users - this happens if we start with a value object in V.
194+
if (definesSingleObjectType(V)) {
195+
StructValueUsers.push_back(UseInst);
196+
continue;
197+
}
198+
199+
StructAddressUsers.push_back(UseInst);
200+
continue;
201+
}
202+
203+
// Check for uses of projections.
204+
205+
// These are all single-value instructions.
206+
auto *ProjInst = dyn_cast<SingleValueInstruction>(UseInst);
207+
if (!ProjInst) {
208+
AggregateAddressUsers.push_back(UseInst);
209+
continue;
210+
}
211+
ProjectionIndex PI(ProjInst);
212+
// Do not form a path from an IndexAddrInst without otherwise
213+
// distinguishing it from subelement addressing.
214+
if (!PI.isValid()) {
215+
// Found a use of an aggregate containing the given element.
216+
AggregateAddressUsers.push_back(UseInst);
217+
continue;
218+
}
219+
220+
if (PI.Index != AccessPathSuffix[0]) {
221+
// Ignore uses of disjoint elements.
222+
continue;
223+
}
224+
225+
// An alloc_box returns its address as the second value.
226+
assert(PI.Aggregate && "Expected unary element addr inst.");
227+
228+
// Recursively check for users after stripping this component from the
229+
// access path.
230+
collectAddressUses(ProjInst, AccessPathSuffix.slice(1), nullptr);
231+
}
232+
}
233+
};
234+
} // namespace swift

0 commit comments

Comments
 (0)