Skip to content

Commit 7322ccb

Browse files
committed
[FieldSensitivePL] NFC: Added unit tests.
Ported versions of the unit tests that Andy added to pruned liveness to FieldSensitivePL. rdar://110862719
1 parent 189dfc8 commit 7322ccb

File tree

2 files changed

+283
-0
lines changed

2 files changed

+283
-0
lines changed

lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
#include "swift/SIL/OwnershipLiveness.h"
7474
#include "swift/SIL/OwnershipUtils.h"
7575
#include "swift/SIL/PrunedLiveness.h"
76+
#include "swift/SIL/FieldSensitivePrunedLiveness.h"
7677
#include "swift/SIL/SILArgumentArrayRef.h"
7778
#include "swift/SIL/SILBasicBlock.h"
7879
#include "swift/SIL/SILBridging.h"
@@ -494,6 +495,85 @@ struct MultiDefUseLivenessTest : UnitTest {
494495
}
495496
};
496497

498+
// Arguments:
499+
// - value: entity whose fields' livenesses are being computed
500+
// - string: "defs:"
501+
// - variadic list of triples consisting of
502+
// - value: a live-range defining value
503+
// - int: the beginning of the range of fields defined by the value
504+
// - int: the end of the range of the fields defined by the value
505+
// - the string "uses:"
506+
// - variadic list of quadruples consisting of
507+
// - instruction: a live-range user
508+
// - bool: whether the user is lifetime-ending
509+
// - int: the beginning of the range of fields used by the instruction
510+
// - int: the end of the range of fields used by the instruction
511+
// Dumps:
512+
// - the liveness result and boundary
513+
//
514+
// Computes liveness for the specified def nodes by considering the
515+
// specified uses. The actual uses of the def nodes are ignored.
516+
//
517+
// This is useful for testing non-ssa liveness, for example, of memory
518+
// locations. In that case, the def nodes may be stores and the uses may be
519+
// destroy_addrs.
520+
struct FieldSensitiveMultiDefUseLiveRangeTest : UnitTest {
521+
FieldSensitiveMultiDefUseLiveRangeTest(UnitTestRunner *pass) : UnitTest(pass) {}
522+
523+
void invoke(Arguments &arguments) override {
524+
SmallVector<SILBasicBlock *, 8> discoveredBlocks;
525+
auto value = arguments.takeValue();
526+
FieldSensitiveMultiDefPrunedLiveRange liveness(getFunction(), value, &discoveredBlocks);
527+
528+
llvm::outs() << "FieldSensitive MultiDef lifetime analysis:\n";
529+
if (arguments.takeString() != "defs:") {
530+
llvm::report_fatal_error(
531+
"test specification expects the 'defs:' label\n");
532+
}
533+
while (true) {
534+
auto argument = arguments.takeArgument();
535+
if (isa<StringArgument>(argument)) {
536+
if(cast<StringArgument>(argument).getValue() != "uses:") {
537+
llvm::report_fatal_error(
538+
"test specification expects the 'uses:' label\n");
539+
}
540+
break;
541+
}
542+
auto begin = arguments.takeUInt();
543+
auto end = arguments.takeUInt();
544+
TypeTreeLeafTypeRange range(begin, end);
545+
if (isa<InstructionArgument>(argument)) {
546+
auto *instruction = cast<InstructionArgument>(argument).getValue();
547+
llvm::outs() << " def in range [" << begin << ", " << end << ") instruction: " << *instruction;
548+
liveness.initializeDef(instruction, range);
549+
continue;
550+
}
551+
if (isa<ValueArgument>(argument)) {
552+
SILValue value = cast<ValueArgument>(argument).getValue();
553+
llvm::outs() << " def in range [" << begin << ", " << end << ") value: " << value;
554+
liveness.initializeDef(value, range);
555+
continue;
556+
}
557+
llvm::report_fatal_error(
558+
"test specification expects the 'uses:' label\n");
559+
}
560+
liveness.finishedInitializationOfDefs();
561+
while (arguments.hasUntaken()) {
562+
auto *inst = arguments.takeInstruction();
563+
auto lifetimeEnding = arguments.takeBool();
564+
auto begin = arguments.takeUInt();
565+
auto end = arguments.takeUInt();
566+
TypeTreeLeafTypeRange range(begin, end);
567+
liveness.updateForUse(inst, range, lifetimeEnding);
568+
}
569+
liveness.print(llvm::errs());
570+
571+
FieldSensitivePrunedLivenessBoundary boundary(liveness.getNumSubElements());
572+
liveness.computeBoundary(boundary);
573+
boundary.print(llvm::errs());
574+
}
575+
};
576+
497577
// Arguments:
498578
// - bool: pruneDebug
499579
// - bool: maximizeLifetimes
@@ -921,6 +1001,7 @@ void UnitTestRunner::withTest(StringRef name, Doit doit) {
9211001
ADD_UNIT_TEST_SUBCLASS("linear-liveness", LinearLivenessTest)
9221002
ADD_UNIT_TEST_SUBCLASS("multidef-liveness", MultiDefLivenessTest)
9231003
ADD_UNIT_TEST_SUBCLASS("multidefuse-liveness", MultiDefUseLivenessTest)
1004+
ADD_UNIT_TEST_SUBCLASS("fieldsensitive-multidefuse-liverange", FieldSensitiveMultiDefUseLiveRangeTest)
9241005
ADD_UNIT_TEST_SUBCLASS("ossa-lifetime-completion", OSSALifetimeCompletionTest)
9251006
ADD_UNIT_TEST_SUBCLASS("pruned-liveness-boundary-with-list-of-last-users-insertion-points", PrunedLivenessBoundaryWithListOfLastUsersInsertionPointsTest)
9261007
ADD_UNIT_TEST_SUBCLASS("shrink-borrow-scope", ShrinkBorrowScopeTest)
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// RUN: %target-sil-opt -unit-test-runner %s -o /dev/null 2>&1 | %FileCheck %s
2+
3+
class C {}
4+
sil @getC : $@convention(thin) () -> (@owned C)
5+
6+
// Test a live range that is extended through reborrows,
7+
// considering them new defs.
8+
// (e.g. BorrowedValue::visitTransitiveLifetimeEndingUses)
9+
//
10+
// This live range is not dominated by the original borrow.
11+
//
12+
// CHECK-LABEL: testReborrow: fieldsensitive-multidefuse-liverange
13+
// CHECK: FieldSensitive MultiDef lifetime analysis:
14+
// CHECK: def in range [0, 1) value: [[B:%.*]] = load_borrow %0 : $*C
15+
// CHECK: def in range [0, 1) value: [[RB:%.*]] = argument of bb3 : $C
16+
// CHECK-NEXT: bb2: LiveWithin
17+
// CHECK-NEXT: bb3: LiveWithin
18+
// CHECK-NEXT: last user: br bb3([[B]] : $C)
19+
// CHECK-NEXT: at 1
20+
// CHECK-NEXT: last user: end_borrow [[RB]] : $C
21+
// CHECK-NEXT: at 1
22+
sil [ossa] @testReborrow : $@convention(thin) () -> () {
23+
bb0:
24+
test_specification """
25+
fieldsensitive-multidefuse-liverange
26+
@instruction
27+
defs:
28+
@trace[0] 0 1
29+
@trace[1] 0 1
30+
uses:
31+
@block[2].instruction[3] true 0 1
32+
@block[3].instruction[0] true 0 1
33+
"""
34+
%stack = alloc_stack $C
35+
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
36+
cond_br undef, bb1, bb2
37+
38+
bb1:
39+
%c1 = apply %getC() : $@convention(thin) () -> (@owned C)
40+
store %c1 to [init] %stack : $*C
41+
%borrow1 = load_borrow %stack : $*C
42+
br bb3(%borrow1 : $C)
43+
44+
bb2:
45+
%c2 = apply %getC() : $@convention(thin) () -> (@owned C)
46+
store %c2 to [init] %stack : $*C
47+
%borrow2 = load_borrow %stack : $*C
48+
debug_value [trace] %borrow2 : $C
49+
br bb3(%borrow2 : $C)
50+
51+
bb3(%reborrow : @guaranteed $C):
52+
debug_value [trace] %reborrow : $C
53+
end_borrow %reborrow : $C
54+
br bb4
55+
56+
bb4:
57+
destroy_addr %stack : $*C
58+
dealloc_stack %stack : $*C
59+
%99 = tuple()
60+
return %99 : $()
61+
}
62+
63+
// CHECK-LABEL: begin running test 1 of 1 on testMultiDefUseAddressReinit
64+
// CHECK: MultiDef lifetime analysis:
65+
// CHECK: def in range [0, 1) instruction: store %{{.*}} to [init] [[ADDR:%.*]] : $*C
66+
// CHECK: def in range [0, 1) instruction: store %0 to [init] [[ADDR]] : $*C
67+
68+
// FIXME: rdar://111118843 : bb0 is live-out, but FieldSensitivePL incorrectly
69+
// determines it to be LiveWithin because of its mishandling of
70+
// uses-before-defs.
71+
//
72+
// The following HECK lines are the correct CHECK lines. The subsequent
73+
// CHECK lines capture the current erroneous behavior.
74+
// HECK: bb0: LiveOut
75+
// HECK: bb1: LiveWithin
76+
// HECK: last user: %{{.*}} = load [copy] [[ADDR]] : $*C
77+
// HECK: boundary edge: bb2
78+
// HECK: dead def: store %0 to [init] %1 : $*C
79+
80+
// FIXME: bb0 is not LiveWithin, it is LiveOut
81+
// CHECK: bb0: LiveWithin,
82+
// CHECK: bb1: LiveWithin,
83+
// CHECK: last user: %{{.*}} = load [copy] [[ADDR]] : $*C
84+
// FIXME: the store of the copy is not a dead-def, it is used in bb1, by the
85+
// only user added.
86+
// CHECK: dead def: store %2 to [init] %1 : $*C
87+
// CHECK: dead def: store %0 to [init] %1 : $*C
88+
89+
// CHECK-LABEL: end running test 1 of 1 on testMultiDefUseAddressReinit
90+
sil [ossa] @testMultiDefUseAddressReinit : $@convention(thin) (@owned C) -> () {
91+
bb0(%0: @owned $C):
92+
test_specification """
93+
fieldsensitive-multidefuse-liverange
94+
@instruction
95+
defs:
96+
@instruction[+2] 0 1
97+
@block[1].instruction[2] 0 1
98+
uses:
99+
@block[1].instruction[0] false 0 1
100+
"""
101+
%1 = alloc_stack $C
102+
%2 = copy_value %0 : $C
103+
store %2 to [init] %1 : $*C
104+
cond_br undef, bb1, bb2
105+
106+
bb1:
107+
%5 = load [copy] %1 : $*C
108+
destroy_addr %1 : $*C
109+
store %0 to [init] %1 : $*C
110+
destroy_value %5 : $C
111+
br bb3
112+
113+
bb2:
114+
destroy_value %0 : $C
115+
br bb3
116+
117+
bb3:
118+
destroy_addr %1 : $*C
119+
dealloc_stack %1 : $*C
120+
%9999 = tuple ()
121+
return %9999 : $()
122+
}
123+
124+
// A single instruction occurs twice on the same liverange
125+
// boundary. Once as a last use, and once as a dead def.
126+
// This is a particularly problematic corner case.
127+
//
128+
// CHECK-LABEL: testDeadSelfKill: fieldsensitive-multidefuse-liverange
129+
// CHECK: FieldSensitive MultiDef lifetime analysis:
130+
// CHECK: def in range [0, 1) instruction: store {{%[^,]+}} to [init] [[STACK:%[^,]+]] :
131+
// CHECK: def in range [0, 1) instruction: store {{%[^,]+}} to [assign] [[STACK]]
132+
// CHECK: bb1: LiveWithin
133+
// CHECK: last user: store {{%[^,]+}} to [assign] [[STACK]]
134+
// CHECK: dead def: store {{%[^,]+}} to [assign] [[STACK]]
135+
sil [ossa] @testDeadSelfKill : $@convention(thin) () -> () {
136+
bb0:
137+
br bb3
138+
139+
bb1(%1 : @owned $C, %2 : @owned $C):
140+
test_specification """
141+
fieldsensitive-multidefuse-liverange
142+
@instruction
143+
defs:
144+
@instruction[+1] 0 1
145+
@instruction[+2] 0 1
146+
uses:
147+
@instruction[+2] true 0 1
148+
"""
149+
%stack = alloc_stack $C
150+
store %1 to [init] %stack : $*C
151+
store %2 to [assign] %stack : $*C
152+
unreachable
153+
154+
bb3:
155+
%99 = tuple()
156+
return %99 : $()
157+
}
158+
159+
// A dead-end block with a def can still be a boundary edge. This can
160+
// only happen in OSSA with incomplete lifetimes.
161+
//
162+
// CHECK-LABEL: testMultiDefDeadDefBoundaryEdge: fieldsensitive-multidefuse-liverange
163+
// CHECK: FieldSensitive MultiDef lifetime analysis:
164+
// CHECK: def in range [0, 1) instruction: store {{%[^,]+}} to [init] [[STACK:%[^,]+]] :
165+
// CHECK: def in range [0, 1) instruction: store {{%[^,]+}} to [assign] [[STACK]]
166+
// CHECK: bb0: LiveOut
167+
// CHECK: bb1: LiveWithin
168+
// CHECK: bb2: LiveWithin
169+
// CHECK: last user: destroy_addr [[STACK]]
170+
// CHECK-NEXT: at 1
171+
// CHECK-NEXT: boundary edge: bb1
172+
// CHECK-NEXT: at 1
173+
// CHECK-NEXT: dead def: store {{%[^,]+}} to [assign] [[STACK]]
174+
// CHECK-NEXT: at 1
175+
sil [ossa] @testMultiDefDeadDefBoundaryEdge : $@convention(thin) () -> () {
176+
bb0:
177+
%getC = function_ref @getC : $@convention(thin) () -> (@owned C)
178+
%stack = alloc_stack $C
179+
%c = apply %getC() : $@convention(thin) () -> (@owned C)
180+
store %c to [init] %stack : $*C
181+
test_specification """
182+
fieldsensitive-multidefuse-liverange
183+
@instruction[-3]
184+
defs:
185+
@instruction[-1] 0 1
186+
@block[1].instruction[1] 0 1
187+
uses:
188+
@block[2].instruction[0] true 0 1
189+
"""
190+
cond_br undef, bb1, bb3
191+
192+
bb1:
193+
%c2 = apply %getC() : $@convention(thin) () -> (@owned C)
194+
store %c2 to [assign] %stack : $*C
195+
unreachable
196+
197+
bb3:
198+
destroy_addr %stack : $*C
199+
dealloc_stack %stack : $*C
200+
%99 = tuple()
201+
return %99 : $()
202+
}

0 commit comments

Comments
 (0)