Skip to content

Commit 5096345

Browse files
committed
DI: Handle instructions that initialize multiple tuple elements
DI had trouble with this pattern: %s = struct_element_addr ... %t0 = tuple_element_addr %s, 0 %t1 = tuple_element_addr %s, 1 %f = function_ref ... apply %f(%t0, %t1) This is because the NonLoadUses map only stored a single use of a tuple element per instruction, preventing instructions such as 'apply' from being able to use multiple tuple elements. In other cases where this comes up, such as with 'assign' and 'copy_addr', DI scalarizes the tuple operation by projecting each component, however clearly this can't be easily done with an 'apply'. Instead, we can get DI out of the business of scalarization, at least for instructions which are known to perform an unconditional initialization. We do this by changing the NonLoadUses map to store a vector of DIMemoryUse IDs instead of a single value.
1 parent 7bc5c5e commit 5096345

File tree

2 files changed

+58
-27
lines changed

2 files changed

+58
-27
lines changed

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ namespace {
394394

395395
/// This is a map of uses that are not loads (i.e., they are Stores,
396396
/// InOutUses, and Escapes), to their entry in Uses.
397-
llvm::SmallDenseMap<SILInstruction*, unsigned, 16> NonLoadUses;
397+
llvm::SmallDenseMap<SILInstruction*, SmallVector<unsigned, 1>, 16> NonLoadUses;
398398

399399
/// This is true when there is an ambiguous store, which may be an init or
400400
/// assign, depending on the CFG path.
@@ -472,7 +472,7 @@ namespace {
472472

473473
void handleSelfInitUse(unsigned UseID);
474474

475-
void updateInstructionForInitState(DIMemoryUse &Use);
475+
void updateInstructionForInitState(unsigned UseID);
476476

477477

478478
void processUninitializedRelease(SILInstruction *Release,
@@ -544,7 +544,7 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory,
544544
break;
545545
}
546546

547-
NonLoadUses[Use.Inst] = ui;
547+
NonLoadUses[Use.Inst].push_back(ui);
548548

549549
auto &BBInfo = getBlockInfo(Use.Inst->getParent());
550550
BBInfo.HasNonLoadUse = true;
@@ -562,9 +562,8 @@ LifetimeChecker::LifetimeChecker(const DIMemoryObjectInfo &TheMemory,
562562
getBlockInfo(bb).markStoreToSelf();
563563
}
564564

565-
// If isn't really a use, but we account for the mark_uninitialized or
565+
// It isn't really a use, but we account for the mark_uninitialized or
566566
// project_box as a use so we see it in our dataflow walks.
567-
NonLoadUses[TheMemory.getUninitializedValue()] = ~0U;
568567
auto &MemBBInfo = getBlockInfo(TheMemory.getParentBlock());
569568
MemBBInfo.HasNonLoadUse = true;
570569

@@ -826,7 +825,7 @@ void LifetimeChecker::doIt() {
826825
// postpone lowering of assignment instructions to avoid deleting
827826
// instructions that still appear in the Uses list.
828827
for (unsigned UseID : NeedsUpdateForInitState)
829-
updateInstructionForInitState(Uses[UseID]);
828+
updateInstructionForInitState(UseID);
830829
}
831830

832831
void LifetimeChecker::handleLoadUse(const DIMemoryUse &Use) {
@@ -1911,7 +1910,8 @@ void LifetimeChecker::handleSelfInitUse(unsigned UseID) {
19111910
/// from being InitOrAssign to some concrete state, update it for that state.
19121911
/// This includes rewriting them from assign instructions into their composite
19131912
/// operations.
1914-
void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
1913+
void LifetimeChecker::updateInstructionForInitState(unsigned UseID) {
1914+
DIMemoryUse &Use = Uses[UseID];
19151915
SILInstruction *Inst = Use.Inst;
19161916

19171917
IsInitialization_t InitKind;
@@ -1945,10 +1945,11 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
19451945
// If this is an assign, rewrite it based on whether it is an initialization
19461946
// or not.
19471947
if (auto *AI = dyn_cast<AssignInst>(Inst)) {
1948+
19481949
// Remove this instruction from our data structures, since we will be
19491950
// removing it.
19501951
Use.Inst = nullptr;
1951-
NonLoadUses.erase(Inst);
1952+
llvm::erase_if(NonLoadUses[Inst], [&](unsigned id) { return id == UseID; });
19521953

19531954
if (TheMemory.isClassInitSelf() &&
19541955
Use.Kind == DIUseKind::SelfInit) {
@@ -1966,7 +1967,7 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &Use) {
19661967
// Remove this instruction from our data structures, since we will be
19671968
// removing it.
19681969
Use.Inst = nullptr;
1969-
NonLoadUses.erase(Inst);
1970+
llvm::erase_if(NonLoadUses[Inst], [&](unsigned id) { return id == UseID; });
19701971

19711972
switch (Use.Kind) {
19721973
case DIUseKind::Initialization:
@@ -2819,17 +2820,17 @@ LifetimeChecker::getLivenessAtNonTupleInst(swift::SILInstruction *Inst,
28192820
--BBI;
28202821
SILInstruction *TheInst = &*BBI;
28212822

2822-
// If this instruction is unrelated to the memory, ignore it.
2823-
if (!NonLoadUses.count(TheInst))
2824-
continue;
2823+
if (TheInst == TheMemory.getUninitializedValue()) {
2824+
Result.set(0, DIKind::No);
2825+
return Result;
2826+
}
28252827

2826-
// If we found the allocation itself, then we are loading something that
2827-
// is not defined at all yet. Otherwise, we've found a definition, or
2828-
// something else that will require that the memory is initialized at
2829-
// this point.
2830-
Result.set(0, TheInst == TheMemory.getUninitializedValue() ? DIKind::No
2831-
: DIKind::Yes);
2832-
return Result;
2828+
if (NonLoadUses.count(TheInst)) {
2829+
// We've found a definition, or something else that will require that
2830+
// the memory is initialized at this point.
2831+
Result.set(0, DIKind::Yes);
2832+
return Result;
2833+
}
28332834
}
28342835
}
28352836

@@ -2882,11 +2883,6 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst,
28822883
--BBI;
28832884
SILInstruction *TheInst = &*BBI;
28842885

2885-
// If this instruction is unrelated to the memory, ignore it.
2886-
auto It = NonLoadUses.find(TheInst);
2887-
if (It == NonLoadUses.end())
2888-
continue;
2889-
28902886
// If we found the allocation itself, then we are loading something that
28912887
// is not defined at all yet. Scan no further.
28922888
if (TheInst == TheMemory.getUninitializedValue()) {
@@ -2896,11 +2892,19 @@ AvailabilitySet LifetimeChecker::getLivenessAtInst(SILInstruction *Inst,
28962892
return Result;
28972893
}
28982894

2895+
// If this instruction is unrelated to the memory, ignore it.
2896+
auto It = NonLoadUses.find(TheInst);
2897+
if (It == NonLoadUses.end())
2898+
continue;
2899+
28992900
// Check to see which tuple elements this instruction defines. Clear them
29002901
// from the set we're scanning from.
2901-
auto &TheInstUse = Uses[It->second];
2902-
NeededElements.reset(TheInstUse.FirstElement,
2903-
TheInstUse.FirstElement+TheInstUse.NumElements);
2902+
for (unsigned TheUse : It->second) {
2903+
auto &TheInstUse = Uses[TheUse];
2904+
NeededElements.reset(TheInstUse.FirstElement,
2905+
TheInstUse.FirstElement+TheInstUse.NumElements);
2906+
}
2907+
29042908
// If that satisfied all of the elements we're looking for, then we're
29052909
// done. Otherwise, keep going.
29062910
if (NeededElements.none()) {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -definite-init -raw-sil-inst-lowering
2+
3+
import Builtin
4+
import Swift
5+
import SwiftShims
6+
7+
struct S<T> {
8+
var x: (T, T)
9+
}
10+
11+
sil [ossa] @$s19definite_init_tuple1SV1xx_xtvpfi : $@convention(thin) <T> () -> (@out T, @out T)
12+
13+
sil [ossa] @$s19definite_init_tuple1SVACyxGycfC : $@convention(method) <T> (@thin S<T>.Type) -> @out S<T> {
14+
bb0(%0 : $*S<T>, %1 : $@thin S<T>.Type):
15+
%2 = alloc_box $<τ_0_0> { var S<τ_0_0> } <T>, var, name "self"
16+
%3 = mark_uninitialized [rootself] %2 : $<τ_0_0> { var S<τ_0_0> } <T>
17+
%4 = project_box %3 : $<τ_0_0> { var S<τ_0_0> } <T>, 0
18+
%5 = struct_element_addr %4 : $*S<T>, #S.x
19+
%6 = function_ref @$s19definite_init_tuple1SV1xx_xtvpfi : $@convention(thin) <τ_0_0> () -> (@out τ_0_0, @out τ_0_0)
20+
%7 = tuple_element_addr %5 : $*(T, T), 0
21+
%8 = tuple_element_addr %5 : $*(T, T), 1
22+
%9 = apply %6<T>(%7, %8) : $@convention(thin) <τ_0_0> () -> (@out τ_0_0, @out τ_0_0)
23+
copy_addr %4 to [initialization] %0 : $*S<T>
24+
destroy_value %3 : $<τ_0_0> { var S<τ_0_0> } <T>
25+
%12 = tuple ()
26+
return %12 : $()
27+
}

0 commit comments

Comments
 (0)