Skip to content

Commit 37ff9ad

Browse files
committed
Add a MultiDefUseLivenessTest
This provides a way to test liveness based on non-SSA values. The move checker is now using pruned liveness this way. So we need a way to test that liveness no longer makes any assumptions about SSA values. These test cases need to explicitly specify all the def and use instructions.
1 parent aa46954 commit 37ff9ad

File tree

3 files changed

+127
-14
lines changed

3 files changed

+127
-14
lines changed

lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,12 @@ struct ScopedAddressLivenessTest : UnitTest {
368368
};
369369

370370
// Arguments:
371-
// - variadic list of live-range defining values
371+
// - variadic list of live-range defining values or instructions
372372
// Dumps:
373373
// - the liveness result and boundary
374+
//
375+
// Computes liveness for the specified def nodes by finding all their direct SSA
376+
// uses. If the def is an instruction, then all results are considered.
374377
struct MultiDefLivenessTest : UnitTest {
375378
MultiDefLivenessTest(UnitTestRunner *pass) : UnitTest(pass) {}
376379

@@ -380,9 +383,16 @@ struct MultiDefLivenessTest : UnitTest {
380383

381384
llvm::outs() << "MultiDef lifetime analysis:\n";
382385
while (arguments.hasUntaken()) {
383-
SILValue value = arguments.takeValue();
384-
llvm::outs() << " def: " << value;
385-
liveness.initializeDef(value);
386+
auto argument = arguments.takeArgument();
387+
if (isa<InstructionArgument>(argument)) {
388+
auto *instruction = cast<InstructionArgument>(argument).getValue();
389+
llvm::outs() << " def instruction: " << instruction;
390+
liveness.initializeDef(instruction);
391+
} else {
392+
SILValue value = cast<ValueArgument>(argument).getValue();
393+
llvm::outs() << " def value: " << value;
394+
liveness.initializeDef(value);
395+
}
386396
}
387397
liveness.computeSimple();
388398
liveness.print(llvm::outs());
@@ -393,6 +403,66 @@ struct MultiDefLivenessTest : UnitTest {
393403
}
394404
};
395405

406+
// Arguments:
407+
// - the string "defs:"
408+
// - list of live-range defining values or instructions
409+
// - the string "uses:"
410+
// - variadic list of live-range user instructions
411+
// Dumps:
412+
// - the liveness result and boundary
413+
//
414+
// Computes liveness for the specified def nodes by considering only the
415+
// specified uses. The actual uses of the def nodes are ignored.
416+
//
417+
// This is useful for testing non-ssa liveness, for example, of memory
418+
// locations. In that case, the def nodes may be stores and the uses may be
419+
// destroy_addrs.
420+
struct MultiDefUseLivenessTest : UnitTest {
421+
MultiDefUseLivenessTest(UnitTestRunner *pass) : UnitTest(pass) {}
422+
423+
void invoke(Arguments &arguments) override {
424+
SmallVector<SILBasicBlock *, 8> discoveredBlocks;
425+
MultiDefPrunedLiveness liveness(getFunction(), &discoveredBlocks);
426+
427+
llvm::outs() << "MultiDef lifetime analysis:\n";
428+
if (arguments.takeString() != "defs:") {
429+
llvm::report_fatal_error(
430+
"test specification expects the 'defs:' label\n");
431+
}
432+
while (true) {
433+
auto argument = arguments.takeArgument();
434+
if (isa<InstructionArgument>(argument)) {
435+
auto *instruction = cast<InstructionArgument>(argument).getValue();
436+
llvm::outs() << " def instruction: " << *instruction;
437+
liveness.initializeDef(instruction);
438+
continue;
439+
}
440+
if (isa<ValueArgument>(argument)) {
441+
SILValue value = cast<ValueArgument>(argument).getValue();
442+
llvm::outs() << " def value: " << value;
443+
liveness.initializeDef(value);
444+
continue;
445+
}
446+
if (cast<StringArgument>(argument).getValue() != "uses:") {
447+
llvm::report_fatal_error(
448+
"test specification expects the 'uses:' label\n");
449+
}
450+
break;
451+
}
452+
while (arguments.hasUntaken()) {
453+
auto *inst = arguments.takeInstruction();
454+
// lifetimeEnding has no effects on liveness, it's only a cache for the
455+
// caller.
456+
liveness.updateForUse(inst, /*lifetimeEnding*/false);
457+
}
458+
liveness.print(llvm::outs());
459+
460+
PrunedLivenessBoundary boundary;
461+
liveness.computeBoundary(boundary);
462+
boundary.print(llvm::outs());
463+
}
464+
};
465+
396466
// Arguments:
397467
// - bool: pruneDebug
398468
// - bool: maximizeLifetimes
@@ -815,6 +885,7 @@ void UnitTestRunner::withTest(StringRef name, Doit doit) {
815885
ADD_UNIT_TEST_SUBCLASS("is-lexical", IsLexicalTest)
816886
ADD_UNIT_TEST_SUBCLASS("linear-liveness", LinearLivenessTest)
817887
ADD_UNIT_TEST_SUBCLASS("multidef-liveness", MultiDefLivenessTest)
888+
ADD_UNIT_TEST_SUBCLASS("multidefuse-liveness", MultiDefUseLivenessTest)
818889
ADD_UNIT_TEST_SUBCLASS("ossa-lifetime-completion", OSSALifetimeCompletionTest)
819890
ADD_UNIT_TEST_SUBCLASS("pruned-liveness-boundary-with-list-of-last-users-insertion-points", PrunedLivenessBoundaryWithListOfLastUsersInsertionPointsTest)
820891
ADD_UNIT_TEST_SUBCLASS("shrink-borrow-scope", ShrinkBorrowScopeTest)

test/SILOptimizer/liveness_incomplete_unit.sil

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ bb0(%0 : @guaranteed $C):
7878
//
7979
// CHECK-LABEL: testDeadSelfKill: multidef-liveness
8080
// CHECK: MultiDef lifetime analysis:
81-
// CHECK: def: %1 = argument of bb1 : $C
82-
// CHECK: def: [[V:%.*]] = move_value %1 : $C
81+
// CHECK: def value: %1 = argument of bb1 : $C
82+
// CHECK: def value: [[V:%.*]] = move_value %1 : $C
8383
// CHECK: bb1: LiveWithin
8484
// CHECK: lifetime-ending user: [[V]] = move_value %1 : $C
8585
// CHECK: last user: [[V]] = move_value %1 : $C
@@ -129,8 +129,8 @@ bb0(%0 : @guaranteed $C, %1 : $@thick Never.Type):
129129
//
130130
// CHECK-LABEL: testMultiDefDeadDefBoundaryEdge: multidef-liveness
131131
// CHECK: MultiDef lifetime analysis:
132-
// CHECK: def: [[CP0:%.*]] = copy_value %0 : $C
133-
// CHECK: def: [[CP3:%.*]] = copy_value %0 : $C
132+
// CHECK: def value: [[CP0:%.*]] = copy_value %0 : $C
133+
// CHECK: def value: [[CP3:%.*]] = copy_value %0 : $C
134134
// CHECK: bb0: LiveOut
135135
// CHECK: bb1: LiveWithin
136136
// CHECK: bb2: LiveWithin

test/SILOptimizer/liveness_unit.sil

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ bb3:
7979
//
8080
// CHECK-LABEL: testReborrow: multidef-liveness
8181
// CHECK: MultiDef lifetime analysis:
82-
// CHECK: def: [[B:%.*]] = begin_borrow %0 : $C
83-
// CHECK: def: [[RB:%.*]] = argument of bb3 : $C
82+
// CHECK: def value: [[B:%.*]] = begin_borrow %0 : $C
83+
// CHECK: def value: [[RB:%.*]] = argument of bb3 : $C
8484
// CHECK-NEXT: bb2: LiveWithin
8585
// CHECK-NEXT: bb3: LiveWithin
8686
// CHECK-NEXT: lifetime-ending user: br bb3([[B]] : $C)
@@ -180,10 +180,10 @@ bb0(%0 : @guaranteed $D):
180180
//
181181
// CHECK-LABEL: testMultiDefLiveOutNoBoundary: multidef-liveness
182182
// CHECK: MultiDef lifetime analysis:
183-
// CHECK: def: [[CP0:%.*]] = copy_value %0 : $C
184-
// CHECK: def: %{{.*}} = copy_value %0 : $C
185-
// CHECK: def: %{{.*}} = move_value [[CP0]] : $C
186-
// CHECK: def: %{{.*}} = argument of bb4 : $C
183+
// CHECK: def value: [[CP0:%.*]] = copy_value %0 : $C
184+
// CHECK: def value: %{{.*}} = copy_value %0 : $C
185+
// CHECK: def value: %{{.*}} = move_value [[CP0]] : $C
186+
// CHECK: def value: %{{.*}} = argument of bb4 : $C
187187
// CHECK-NEXT: bb0: LiveOut
188188
// CHECK-NEXT: bb2: LiveWithin
189189
// CHECK-NEXT: bb3: LiveWithin
@@ -503,3 +503,45 @@ bb0(%0 : @guaranteed $C):
503503
%99 = tuple()
504504
return %99 : $()
505505
}
506+
507+
// CHECK-LABEL: begin running test 1 of 1 on testMultiDefUseAddressReinit
508+
// CHECK: MultiDef lifetime analysis:
509+
// CHECK: def instruction: store %{{.*}} to [init] [[ADR:%.*]] : $*C
510+
// CHECK: def instruction: destroy_addr [[ADR]] : $*C
511+
// CHECK: bb0: LiveWithin
512+
// CHECK: bb1: LiveWithin
513+
// CHECK: regular user: %{{.*}} = load [copy] [[ADR]] : $*C
514+
// CHECK: last user: %{{.*}} = load [copy] [[ADR]] : $*C
515+
//
516+
// FIXME: This store is not really a dead def!
517+
// CHECK: dead def: store %2 to [init] %1 : $*C
518+
// CHECK: dead def: destroy_addr %1 : $*C
519+
// CHECK-LABEL: end running test 1 of 1 on testMultiDefUseAddressReinit
520+
sil [ossa] @testMultiDefUseAddressReinit : $@convention(thin) (@owned C) -> () {
521+
bb0(%0: @owned $C):
522+
%1 = alloc_stack $C
523+
%2 = copy_value %0 : $C
524+
test_specification """
525+
multidefuse-liveness defs: @instruction @block[1].instruction[1]
526+
uses: @block[1].instruction[0]
527+
"""
528+
store %2 to [init] %1 : $*C
529+
cond_br undef, bb1, bb2
530+
531+
bb1:
532+
%5 = load [copy] %1 : $*C
533+
destroy_addr %1 : $*C
534+
store %0 to [init] %1 : $*C
535+
destroy_value %5 : $C
536+
br bb3
537+
538+
bb2:
539+
destroy_value %0 : $C
540+
br bb3
541+
542+
bb3:
543+
destroy_addr %1 : $*C
544+
dealloc_stack %1 : $*C
545+
%9999 = tuple ()
546+
return %9999 : $()
547+
}

0 commit comments

Comments
 (0)