Skip to content

Commit 616ec93

Browse files
authored
Merge pull request #24625 from atrick/canonicalize-stores
2 parents f243484 + 3207592 commit 616ec93

File tree

4 files changed

+134
-74
lines changed

4 files changed

+134
-74
lines changed

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,6 @@ class SILCombiner :
235235
SILInstruction *visitUpcastInst(UpcastInst *UCI);
236236
SILInstruction *optimizeLoadFromStringLiteral(LoadInst *LI);
237237
SILInstruction *visitLoadInst(LoadInst *LI);
238-
SILInstruction *visitStoreInst(StoreInst *si);
239238
SILInstruction *visitIndexAddrInst(IndexAddrInst *IA);
240239
SILInstruction *visitAllocStackInst(AllocStackInst *AS);
241240
SILInstruction *visitAllocRefInst(AllocRefInst *AR);

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -586,79 +586,6 @@ SILInstruction *SILCombiner::optimizeLoadFromStringLiteral(LoadInst *LI) {
586586
return Builder.createIntegerLiteral(LI->getLoc(), LI->getType(), str[index]);
587587
}
588588

589-
SILInstruction *SILCombiner::visitStoreInst(StoreInst *si) {
590-
auto *f = si->getFunction();
591-
assert(f->getConventions().useLoweredAddresses() &&
592-
"These optimizations assume that opaque values are not enabled");
593-
594-
// (store (struct_element_addr addr) object)
595-
// ->
596-
// (store addr (struct object))
597-
598-
// If our store's destination is not a struct_element_addr, bail early.
599-
auto *sea = dyn_cast<StructElementAddrInst>(si->getDest());
600-
if (!sea)
601-
return nullptr;
602-
603-
// Ok, we have at least one struct_element_addr. Canonicalize the underlying
604-
// store.
605-
Builder.setInsertionPoint(si);
606-
SILLocation loc = si->getLoc();
607-
608-
auto &mod = si->getModule();
609-
SILValue result = si->getSrc();
610-
SILValue iterAddr = sea->getOperand();
611-
SILValue storeAddr;
612-
while (true) {
613-
SILType iterAddrType = iterAddr->getType();
614-
615-
// If our aggregate has unreferenced storage then we can never prove if it
616-
// actually has a single field.
617-
if (iterAddrType.aggregateHasUnreferenceableStorage())
618-
break;
619-
620-
auto *decl = iterAddrType.getStructOrBoundGenericStruct();
621-
assert(
622-
!decl->isResilient(mod.getSwiftModule(), f->getResilienceExpansion()) &&
623-
"This code assumes resilient structs can not have fragile fields. If "
624-
"this assert is hit, this has been changed. Please update this code.");
625-
626-
// NOTE: If this is ever changed to support enums, we must check for address
627-
// only types here. For structs we do not have to check since a single
628-
// element struct with a loadable element can never be address only. We
629-
// additionally do not have to worry about our input value being address
630-
// only since we are storing into it.
631-
auto props = decl->getStoredProperties();
632-
if (std::next(props.begin()) != props.end())
633-
break;
634-
635-
// Update the store location now that we know it is safe.
636-
storeAddr = iterAddr;
637-
638-
// Otherwise, create the struct.
639-
result = Builder.createStruct(loc, iterAddrType.getObjectType(), result);
640-
641-
// See if we have another struct_element_addr we can strip off. If we don't
642-
// then this as much as we can promote.
643-
sea = dyn_cast<StructElementAddrInst>(sea->getOperand());
644-
if (!sea)
645-
break;
646-
iterAddr = sea->getOperand();
647-
}
648-
649-
// If we failed to create any structs, bail.
650-
if (result == si->getSrc())
651-
return nullptr;
652-
653-
// Then create a new store, storing the value into the relevant computed
654-
// address.
655-
Builder.createStore(loc, result, storeAddr,
656-
StoreOwnershipQualifier::Unqualified);
657-
658-
// Then eliminate the original store.
659-
return eraseInstFromFunction(*si);
660-
}
661-
662589
SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) {
663590
// (load (upcast-ptr %x)) -> (upcast-ref (load %x))
664591
Builder.setCurrentDebugScope(LI->getDebugScope());

lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,84 @@ splitAggregateLoad(LoadInst *loadInst, CanonicalizeInstruction &pass) {
309309
return killInstAndIncidentalUses(loadInst, nextII, pass);
310310
}
311311

312+
// Given a store within a single property struct, recursively form the parent
313+
// struct values and promote the store to the outer struct type.
314+
//
315+
// (store (struct_element_addr %base) object)
316+
// ->
317+
// (store %base (struct object))
318+
static SILBasicBlock::iterator
319+
broadenSingleElementStores(StoreInst *storeInst,
320+
CanonicalizeInstruction &pass) {
321+
// Keep track of the next iterator after any newly added or to-be-deleted
322+
// instructions. This must be valid regardless of whether the pass immediately
323+
// deletes the instructions or simply records them for later deletion.
324+
auto nextII = std::next(storeInst->getIterator());
325+
326+
// Bail if the store's destination is not a struct_element_addr.
327+
auto *sea = dyn_cast<StructElementAddrInst>(storeInst->getDest());
328+
if (!sea)
329+
return nextII;
330+
331+
auto *f = storeInst->getFunction();
332+
333+
// Continue up the struct_element_addr chain, as long as each struct only has
334+
// a single property, creating StoreInsts along the way.
335+
SILBuilderWithScope builder(storeInst);
336+
337+
SILValue result = storeInst->getSrc();
338+
SILValue baseAddr = sea->getOperand();
339+
SILValue storeAddr;
340+
while (true) {
341+
SILType baseAddrType = baseAddr->getType();
342+
343+
// If our aggregate has unreferenced storage then we can never prove if it
344+
// actually has a single field.
345+
if (baseAddrType.aggregateHasUnreferenceableStorage())
346+
break;
347+
348+
auto *decl = baseAddrType.getStructOrBoundGenericStruct();
349+
assert(
350+
!decl->isResilient(f->getModule().getSwiftModule(),
351+
f->getResilienceExpansion()) &&
352+
"This code assumes resilient structs can not have fragile fields. If "
353+
"this assert is hit, this has been changed. Please update this code.");
354+
355+
// NOTE: If this is ever changed to support enums, we must check for address
356+
// only types here. For structs we do not have to check since a single
357+
// element struct with a loadable element can never be address only. We
358+
// additionally do not have to worry about our input value being address
359+
// only since we are storing into it.
360+
auto props = decl->getStoredProperties();
361+
if (std::next(props.begin()) != props.end())
362+
break;
363+
364+
// Update the store location now that we know it is safe.
365+
storeAddr = baseAddr;
366+
367+
// Otherwise, create the struct.
368+
result = builder.createStruct(storeInst->getLoc(),
369+
baseAddrType.getObjectType(), result);
370+
371+
// See if we have another struct_element_addr we can strip off. If we don't
372+
// then this as much as we can promote.
373+
sea = dyn_cast<StructElementAddrInst>(sea->getOperand());
374+
if (!sea)
375+
break;
376+
baseAddr = sea->getOperand();
377+
}
378+
// If we failed to create any structs, bail.
379+
if (result == storeInst->getSrc())
380+
return nextII;
381+
382+
// Store the new struct-wrapped value into the final base address.
383+
builder.createStore(storeInst->getLoc(), result, storeAddr,
384+
storeInst->getOwnershipQualifier());
385+
386+
// Erase the original store.
387+
return killInstruction(storeInst, nextII, pass);
388+
}
389+
312390
//===----------------------------------------------------------------------===//
313391
// Top-Level Entry Point
314392
//===----------------------------------------------------------------------===//
@@ -321,6 +399,9 @@ CanonicalizeInstruction::canonicalize(SILInstruction *inst) {
321399
if (auto *loadInst = dyn_cast<LoadInst>(inst))
322400
return splitAggregateLoad(loadInst, *this);
323401

402+
if (auto *storeInst = dyn_cast<StoreInst>(inst))
403+
return broadenSingleElementStores(storeInst, *this);
404+
324405
// Skip ahead.
325406
return std::next(inst->getIterator());
326407
}

test/SILOptimizer/silgen_cleanup.sil

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@ import SwiftShims
1111
// CHECK: bb0([[IN:%[0-9]+]] : $*UInt8):
1212
// CHECK-NEXT: [[IN_GEP:%[0-9]+]] = struct_element_addr [[IN]] : $*UInt8, #UInt8._value
1313
// CHECK-NEXT: [[IN_LOADED:%[0-9]+]] = load [trivial] [[IN_GEP]] : $*Builtin.Int8
14+
// CHECK-NEXT: [[LITERAL:%[0-9]+]] = integer_literal $Builtin.Int8, 1
15+
// CHECK-NEXT: [[UINT8:%.*]] = struct $UInt8 ([[LITERAL]] : $Builtin.Int8)
16+
// CHECK-NEXT: store [[UINT8]] to [trivial] [[IN]] : $*UInt8
1417
// CHECK-NEXT: return [[IN_LOADED]] : $Builtin.Int8
1518
sil [ossa] @struct_extract_load_to_load_struct_element_addr : $@convention(thin) (@inout UInt8) -> (Builtin.Int8) {
1619
bb0(%0 : $*UInt8):
1720
%1 = load [trivial] %0 : $*UInt8
21+
%2 = integer_literal $Builtin.Int8, 1
22+
%3 = struct_element_addr %0 : $*UInt8, #UInt8._value
23+
store %2 to [trivial] %3 : $*Builtin.Int8
1824
%5 = struct_extract %1 : $UInt8, #UInt8._value
1925
return %5 : $Builtin.Int8
2026
}
@@ -23,10 +29,16 @@ bb0(%0 : $*UInt8):
2329
// CHECK: bb0([[IN:%[0-9]+]] : $*(Builtin.Int8, Builtin.Int8)):
2430
// CHECK-NEXT: [[IN_GEP:%[0-9]+]] = tuple_element_addr [[IN]] : $*(Builtin.Int8, Builtin.Int8), 0
2531
// CHECK-NEXT: [[IN_LOADED:%[0-9]+]] = load [trivial] [[IN_GEP]] : $*Builtin.Int8
32+
// CHECK-NEXT: [[LITERAL:%[0-9]+]] = integer_literal $Builtin.Int8, 1
33+
// CHECK-NEXT: [[IN_STORE_GEP:%[0-9]+]] = tuple_element_addr %0 : $*(Builtin.Int8, Builtin.Int8), 0
34+
// CHECK-NEXT: store [[LITERAL]] to [trivial] [[IN_STORE_GEP]] : $*Builtin.Int8
2635
// CHECK-NEXT: return [[IN_LOADED]] : $Builtin.Int8
2736
sil [ossa] @tuple_extract_load_to_load_tuple_element_addr : $@convention(thin) (@inout (Builtin.Int8, Builtin.Int8)) -> (Builtin.Int8) {
2837
bb0(%0 : $*(Builtin.Int8, Builtin.Int8)):
2938
%1 = load [trivial] %0 : $*(Builtin.Int8, Builtin.Int8)
39+
%2 = integer_literal $Builtin.Int8, 1
40+
%3 = tuple_element_addr %0 : $*(Builtin.Int8, Builtin.Int8), 0
41+
store %2 to [trivial] %3 : $*Builtin.Int8
3042
%5 = tuple_extract %1 : $(Builtin.Int8, Builtin.Int8), 0
3143
return %5 : $Builtin.Int8
3244
}
@@ -36,12 +48,18 @@ bb0(%0 : $*(Builtin.Int8, Builtin.Int8)):
3648
// CHECK-LABEL: sil [ossa] @multiple_use_struct_extract_load_to_load_struct_element_addr
3749
// CHECK: bb0([[IN:%[0-9]+]] : $*UInt8):
3850
// CHECK-NEXT: load
51+
// CHECK-NEXT: integer_literal
52+
// CHECK-NEXT: struct
53+
// CHECK-NEXT: store
3954
// CHECK-NEXT: struct_extract
4055
// CHECK-NEXT: tuple
4156
// CHECK-NEXT: return
4257
sil [ossa] @multiple_use_struct_extract_load_to_load_struct_element_addr : $@convention(thin) (@inout UInt8) -> (UInt8, Builtin.Int8) {
4358
bb0(%0 : $*UInt8):
4459
%1 = load [trivial] %0 : $*UInt8
60+
%2 = integer_literal $Builtin.Int8, 1
61+
%3 = struct_element_addr %0 : $*UInt8, #UInt8._value
62+
store %2 to [trivial] %3 : $*Builtin.Int8
4563
%5 = struct_extract %1 : $UInt8, #UInt8._value
4664
%6 = tuple (%1 : $UInt8, %5 : $Builtin.Int8)
4765
return %6 : $(UInt8, Builtin.Int8)
@@ -52,12 +70,18 @@ bb0(%0 : $*UInt8):
5270
// CHECK-LABEL: sil [ossa] @multiple_use_tuple_extract_load_to_load_tuple_element_addr
5371
// CHECK: bb0
5472
// CHECK-NEXT: load
73+
// CHECK-NEXT: integer_literal
74+
// CHECK-NEXT: tuple_element_addr
75+
// CHECK-NEXT: store
5576
// CHECK-NEXT: tuple_extract
5677
// CHECK-NEXT: tuple
5778
// CHECK-NEXT: return
5879
sil [ossa] @multiple_use_tuple_extract_load_to_load_tuple_element_addr : $@convention(thin) (@inout (Builtin.Int8, Builtin.Int8)) -> ((Builtin.Int8, Builtin.Int8), Builtin.Int8) {
5980
bb0(%0 : $*(Builtin.Int8, Builtin.Int8)):
6081
%1 = load [trivial] %0 : $*(Builtin.Int8, Builtin.Int8)
82+
%2 = integer_literal $Builtin.Int8, 1
83+
%3 = tuple_element_addr %0 : $*(Builtin.Int8, Builtin.Int8), 0
84+
store %2 to [trivial] %3 : $*Builtin.Int8
6185
%5 = tuple_extract %1 : $(Builtin.Int8, Builtin.Int8), 0
6286
%6 = tuple (%1 : $(Builtin.Int8, Builtin.Int8), %5 : $Builtin.Int8)
6387
return %6 : $((Builtin.Int8, Builtin.Int8), Builtin.Int8)
@@ -115,3 +139,32 @@ bb0(%0 : $*X1):
115139
%result = tuple (%a : $Int, %copy1 : $AnyObject, %copy2 : $AnyObject)
116140
return %result : $(Int, AnyObject, AnyObject)
117141
}
142+
143+
struct X2 {
144+
@_hasStorage @_hasInitialValue var obj: AnyObject { get set }
145+
}
146+
147+
struct X3 {
148+
@_hasStorage @_hasInitialValue var x2: X2 { get set }
149+
}
150+
151+
// CHECK-LABEL: sil private [ossa] @testStoreNontrivial : $@convention(thin) (@inout X3, @guaranteed AnyObject) -> () {
152+
// CHECK-LABEL: bb0(%0 : $*X3, %1 : @guaranteed $AnyObject):
153+
// CHECK: [[CP:%.*]] = copy_value %1 : $AnyObject
154+
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unknown] %0 : $*X3
155+
// CHECK: [[X2:%.*]] = struct $X2 ([[CP]] : $AnyObject)
156+
// CHECK: [[X3:%.*]] = struct $X3 ([[X2]] : $X2)
157+
// CHECK: store [[X3]] to [assign] [[ACCESS]] : $*X3
158+
// CHECK: end_access [[ACCESS]] : $*X3
159+
// CHECK-LABEL: } // end sil function 'testStoreNontrivial'
160+
sil private [ossa] @testStoreNontrivial : $@convention(thin) (@inout X3, @guaranteed AnyObject) -> () {
161+
bb0(%0 : $*X3, %1 : $AnyObject):
162+
%4 = copy_value %1 : $AnyObject
163+
%5 = begin_access [modify] [unknown] %0 : $*X3
164+
%6 = struct_element_addr %5 : $*X3, #X3.x2
165+
%7 = struct_element_addr %6 : $*X2, #X2.obj
166+
store %4 to [assign] %7 : $*AnyObject
167+
end_access %5 : $*X3
168+
%12 = tuple ()
169+
return %12 : $()
170+
}

0 commit comments

Comments
 (0)