Skip to content

Commit 9b62f0d

Browse files
authored
Merge pull request #19120 from eeckstein/let-property-opt
SILOptimizer: handle copies in LetPropertiesOpt.
2 parents 190c7d6 + 6ae0b22 commit 9b62f0d

File tree

6 files changed

+74
-6
lines changed

6 files changed

+74
-6
lines changed

lib/SILOptimizer/IPO/LetPropertiesOpts.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,16 @@ bool LetPropertiesOpt::isConstantLetProperty(VarDecl *Property) {
388388
return true;
389389
}
390390

391+
static bool isProjectionOfProperty(SILValue addr, VarDecl *Property) {
392+
if (auto *REA = dyn_cast<RefElementAddrInst>(addr)) {
393+
return REA->getField() == Property;
394+
}
395+
if (auto *SEA = dyn_cast<StructElementAddrInst>(addr)) {
396+
return SEA->getField() == Property;
397+
}
398+
return false;
399+
}
400+
391401
// Analyze the init value being stored by the instruction into a property.
392402
bool
393403
LetPropertiesOpt::analyzeInitValue(SILInstruction *I, VarDecl *Property) {
@@ -398,15 +408,19 @@ LetPropertiesOpt::analyzeInitValue(SILInstruction *I, VarDecl *Property) {
398408
} else if (auto SI = dyn_cast<StoreInst>(I)) {
399409
auto Dest = stripAddressAccess(SI->getDest());
400410

401-
assert(((isa<RefElementAddrInst>(Dest) &&
402-
cast<RefElementAddrInst>(Dest)->getField() == Property) ||
403-
(isa<StructElementAddrInst>(Dest) &&
404-
cast<StructElementAddrInst>(Dest)->getField() == Property)) &&
405-
"Store instruction should store into a proper let property");
411+
assert(isProjectionOfProperty(stripAddressAccess(SI->getDest()), Property)
412+
&& "Store instruction should store into a proper let property");
406413
(void) Dest;
407414
value = SI->getSrc();
408415
}
409416

417+
// Check if it's just a copy from another instance of the struct.
418+
if (auto *LI = dyn_cast<LoadInst>(value)) {
419+
SILValue addr = LI->getOperand();
420+
if (isProjectionOfProperty(addr, Property))
421+
return true;
422+
}
423+
410424
// Bail if a value of a property is not a statically known constant init.
411425
if (!analyzeStaticInitializer(value, ReverseInsns))
412426
return false;

lib/SILOptimizer/Transforms/SILSROA.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ bool SROAMemoryUseAnalyzer::analyze() {
138138
return false;
139139
}
140140

141+
bool hasBenefit = false;
142+
141143
// Go through uses of the memory allocation of AI...
142144
for (auto *Operand : getNonDebugUses(SILValue(AI))) {
143145
SILInstruction *User = Operand->getUser();
@@ -150,6 +152,9 @@ bool SROAMemoryUseAnalyzer::analyze() {
150152
LLVM_DEBUG(llvm::dbgs() << " Found a store into the "
151153
"projection.\n");
152154
Stores.push_back(SI);
155+
SILValue Src = SI->getSrc();
156+
if (isa<StructInst>(Src) || isa<TupleInst>(Src))
157+
hasBenefit = true;
153158
continue;
154159
} else {
155160
LLVM_DEBUG(llvm::dbgs() << " Found a store of the "
@@ -163,6 +168,11 @@ bool SROAMemoryUseAnalyzer::analyze() {
163168
if (auto *LI = dyn_cast<LoadInst>(User)) {
164169
LLVM_DEBUG(llvm::dbgs() << " Found a load of the projection.\n");
165170
Loads.push_back(LI);
171+
for (auto useIter = LI->use_begin(), End = LI->use_end();
172+
!hasBenefit && useIter != End; useIter++) {
173+
hasBenefit = (isa<StructExtractInst>(useIter->get()) ||
174+
isa<TupleExtractInst>(useIter->get()));
175+
}
166176
continue;
167177
}
168178

@@ -171,6 +181,7 @@ bool SROAMemoryUseAnalyzer::analyze() {
171181
if (auto *ASI = dyn_cast<StructElementAddrInst>(User)) {
172182
LLVM_DEBUG(llvm::dbgs() << " Found a struct subprojection!\n");
173183
ExtractInsts.push_back(ASI);
184+
hasBenefit = true;
174185
continue;
175186
}
176187

@@ -179,6 +190,7 @@ bool SROAMemoryUseAnalyzer::analyze() {
179190
if (auto *TSI = dyn_cast<TupleElementAddrInst>(User)) {
180191
LLVM_DEBUG(llvm::dbgs() << " Found a tuple subprojection!\n");
181192
ExtractInsts.push_back(TSI);
193+
hasBenefit = true;
182194
continue;
183195
}
184196

@@ -195,7 +207,7 @@ bool SROAMemoryUseAnalyzer::analyze() {
195207

196208
// Analysis was successful. We can break up this allocation!
197209
++NumChoppedAllocas;
198-
return true;
210+
return hasBenefit;
199211
}
200212

201213
void

test/SILOptimizer/let_properties_opts.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,3 +354,25 @@ public func useInitializers() -> StructWithPublicAndInternalLetProperties {
354354
public func useInitializers() -> StructWithPublicAndInternalAndPrivateLetProperties {
355355
return StructWithPublicAndInternalAndPrivateLetProperties(1, 1)
356356
}
357+
358+
struct RACStruct {
359+
private let end = 27
360+
361+
var startIndex: Int { return 0 }
362+
363+
364+
// CHECK-LABEL: RACStruct.endIndex.getter
365+
// CHECK-NEXT: sil hidden @{{.*}}endIndexSivg
366+
// CHECK-NEXT: bb0
367+
// CHECK-NEXT: %1 = integer_literal $Builtin.Int{{.*}}, 27
368+
// CHECK-NEXT: %2 = struct $Int (%1 : $Builtin.Int{{.*}})
369+
// CHECK-NEXT: return %2 : $Int
370+
var endIndex: Int { return end }
371+
372+
subscript(_ bitIndex: Int) -> Bool {
373+
get { return false }
374+
set { }
375+
}
376+
}
377+
378+
extension RACStruct : RandomAccessCollection {}

test/SILOptimizer/optionset.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public struct TestOptions: OptionSet {
1616
// CHECK-NEXT: bb0:
1717
// CHECK-NEXT: integer_literal {{.*}}, 15
1818
// CHECK-NEXT: struct $Int
19+
// CHECK-NEXT: debug_value
1920
// CHECK-NEXT: struct $TestOptions
2021
// CHECK-NEXT: return
2122
public func returnTestOptions() -> TestOptions {
@@ -26,6 +27,7 @@ public func returnTestOptions() -> TestOptions {
2627
// CHECK-NEXT: global_addr
2728
// CHECK-NEXT: integer_literal {{.*}}, 15
2829
// CHECK-NEXT: struct $Int
30+
// CHECK-NEXT: debug_value
2931
// CHECK-NEXT: struct $TestOptions
3032
// CHECK-NEXT: store
3133
// CHECK-NEXT: tuple

test/SILOptimizer/specialize_unconditional_checked_cast.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ ArchetypeToConcreteConvertUInt8(t: f)
100100
// CHECK-LABEL: sil shared [noinline] @$S37specialize_unconditional_checked_cast31ArchetypeToConcreteConvertUInt8{{[_0-9a-zA-Z]*}}3Not{{.*}}Tg5 : $@convention(thin) (NotUInt8) -> NotUInt8 {
101101
// CHECK: bb0
102102
// CHECK-NEXT: debug_value %0
103+
// CHECK-NEXT: debug_value %0
103104
// CHECK-NEXT: return %0
104105

105106
// x -> y where y is a class but x is not.

test/SILOptimizer/sroa.sil

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,23 @@ bb0(%0 : $S1):
5656
return %6 : $()
5757
}
5858

59+
// CHECK-LABEL: sil @dont_sroa_without_benefit : $@convention(thin) (S1) -> S1
60+
// CHECK: bb0
61+
// CHECK-NEXT: alloc_stack $S1
62+
// CHECK-NEXT: store %0 to %1 : $*S1
63+
// CHECK-NEXT: load %1 : $*S1
64+
// CHECK-NEXT: dealloc_stack %1 : $*S1
65+
// CHECK-NEXT: return
66+
sil @dont_sroa_without_benefit : $@convention(thin) (S1) -> S1 {
67+
bb0(%0 : $S1):
68+
%1 = alloc_stack $S1
69+
store %0 to %1 : $*S1
70+
%3 = load %1 : $*S1
71+
dealloc_stack %1 : $*S1
72+
return %3 : $S1
73+
}
74+
75+
5976
///////////////////////////////
6077
// Struct With Struct Fields //
6178
///////////////////////////////

0 commit comments

Comments
 (0)