Skip to content

Commit 82b08c5

Browse files
committed
[ownership] Add new pass OwnershipVerifierTextualErrorDumper and use it in ownership verifier FileCheck tests instead of SILVerifier.
This will make it easier for me with a few further refactors to make the ownership verifier testing mode emit per function error numbers instead of the global error number that it is emitting now. The reason why this is necessary is that today, the verification by -sil-verify-all causes the errors to be emitted. That verification is done on a per value level, rather than a per function level, so it is hard to get per function error numbers without doing unprincipled things like propagating around state saying what the current function being verified is. This pass instead will let me make the error counter be per ErrorBuilder which are created per function. One thing to be aware of is that this /will/ cause SILValue::verifyOwnership to not emit any output when the testing flag is enabled. This is to ensure I only do not get duplicate textual error messages from the SILVerifier.
1 parent a4b8255 commit 82b08c5

12 files changed

+135
-27
lines changed

include/swift/SIL/SILFunction.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,13 @@ class SILFunction
10521052
/// invariants.
10531053
void verify(bool SingleFunction = true) const;
10541054

1055+
/// Run the SIL ownership verifier to check for ownership invariant failures.
1056+
///
1057+
/// NOTE: The ownership verifier is always run when performing normal IR
1058+
/// verification, so this verification can be viewed as a subset of
1059+
/// SILFunction::verify.
1060+
void verifyOwnership(DeadEndBlocks *deadEndBlocks = nullptr) const;
1061+
10551062
/// Verify that all non-cond-br critical edges have been split.
10561063
///
10571064
/// This is a fast subset of the checks performed in the SILVerifier.

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ PASS(UsePrespecialized, "use-prespecialized",
314314
"Use Pre-Specialized Functions")
315315
PASS(OwnershipDumper, "ownership-dumper",
316316
"Print Ownership information for Testing")
317+
PASS(OwnershipVerifierTextualErrorDumper, "ownership-verifier-textual-error-dumper",
318+
"Run ownership verification on all functions, emitting FileCheck-able textual errors instead of asserting")
317319
PASS(SemanticARCOpts, "semantic-arc-opts",
318320
"Semantic ARC Optimization")
319321
PASS(SimplifyUnreachableContainingBlocks, "simplify-unreachable-containing-blocks",

lib/SIL/Verifier/SILOwnershipVerifier.cpp

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -882,12 +882,41 @@ void SILInstruction::verifyOperandOwnership() const {
882882
}
883883
}
884884

885+
static void verifySILValueHelper(const SILFunction *f, SILValue value,
886+
DeadEndBlocks *deadEndBlocks) {
887+
assert(!isa<SILUndef>(value) &&
888+
"We assume we are always passed arguments or instruction results");
889+
890+
// If the given function has unqualified ownership or we have been asked by
891+
// the user not to verify this function, there is nothing to verify.
892+
if (!f->hasOwnership() || !f->shouldVerifyOwnership())
893+
return;
894+
895+
using BehaviorKind = LinearLifetimeChecker::ErrorBehaviorKind;
896+
Optional<LinearLifetimeChecker::ErrorBuilder> errorBuilder;
897+
if (IsSILOwnershipVerifierTestingEnabled) {
898+
errorBuilder.emplace(*f, BehaviorKind::PrintMessageAndReturnFalse);
899+
} else {
900+
errorBuilder.emplace(*f, BehaviorKind::PrintMessageAndAssert);
901+
}
902+
903+
SmallPtrSet<SILBasicBlock *, 32> liveBlocks;
904+
if (deadEndBlocks) {
905+
SILValueOwnershipChecker(*deadEndBlocks, value, *errorBuilder, liveBlocks)
906+
.check();
907+
} else {
908+
DeadEndBlocks deadEndBlocks(f);
909+
SILValueOwnershipChecker(deadEndBlocks, value, *errorBuilder, liveBlocks)
910+
.check();
911+
}
912+
}
913+
885914
void SILValue::verifyOwnership(DeadEndBlocks *deadEndBlocks) const {
886915
if (DisableOwnershipVerification)
887916
return;
888917

889918
// Do not validate SILUndef values.
890-
if (isa<SILUndef>(Value))
919+
if (isa<SILUndef>(*this))
891920
return;
892921

893922
#ifdef NDEBUG
@@ -898,8 +927,8 @@ void SILValue::verifyOwnership(DeadEndBlocks *deadEndBlocks) const {
898927
// that if we run into something that we do not understand, we do not assert
899928
// in user code even tohugh we aren't going to actually verify (the default
900929
// behavior when -sil-verify-all is disabled).
901-
auto *Mod = Value->getModule();
902-
if (!Mod || !Mod->getOptions().VerifyAll)
930+
auto *mod = Value->getModule();
931+
if (!mod || !mod->getOptions().VerifyAll)
903932
return;
904933
#endif
905934

@@ -911,31 +940,51 @@ void SILValue::verifyOwnership(DeadEndBlocks *deadEndBlocks) const {
911940
}
912941
}
913942

943+
// If we are testing the verifier, bail so we only print errors once when
944+
// performing a full verification a function at a time by the
945+
// OwnershipVerifierStateDumper pass, instead of additionally in the
946+
// SILBuilder and in the actual SIL verifier that may be run by sil-opt.
947+
if (IsSILOwnershipVerifierTestingEnabled)
948+
return;
949+
914950
// Since we do not have SILUndef, we now know that getFunction() should return
915951
// a real function. Assert in case this assumption is no longer true.
916-
SILFunction *f = (*this)->getFunction();
952+
auto *f = (*this)->getFunction();
917953
assert(f && "Instructions and arguments should have a function");
954+
verifySILValueHelper(f, *this, deadEndBlocks);
955+
}
956+
957+
void SILFunction::verifyOwnership(DeadEndBlocks *deadEndBlocks) const {
958+
if (DisableOwnershipVerification)
959+
return;
960+
961+
#ifdef NDEBUG
962+
// When compiling without asserts enabled, only verify ownership if
963+
// -sil-verify-all is set.
964+
//
965+
// NOTE: We purposely return if we do can not look up a module here to ensure
966+
// that if we run into something that we do not understand, we do not assert
967+
// in user code even tohugh we aren't going to actually verify (the default
968+
// behavior when -sil-verify-all is disabled).
969+
auto *mod = getParent();
970+
if (!mod || !mod->getOptions().VerifyAll)
971+
return;
972+
#endif
918973

919974
// If the given function has unqualified ownership or we have been asked by
920975
// the user not to verify this function, there is nothing to verify.
921-
if (!f->hasOwnership() || !f->shouldVerifyOwnership())
976+
if (!hasOwnership() || !shouldVerifyOwnership())
922977
return;
923978

924-
using BehaviorKind = LinearLifetimeChecker::ErrorBehaviorKind;
925-
Optional<LinearLifetimeChecker::ErrorBuilder> errorBuilder;
926-
if (IsSILOwnershipVerifierTestingEnabled) {
927-
errorBuilder.emplace(*f, BehaviorKind::PrintMessageAndReturnFalse);
928-
} else {
929-
errorBuilder.emplace(*f, BehaviorKind::PrintMessageAndAssert);
930-
}
979+
for (auto &block : *this) {
980+
for (auto *arg : block.getArguments()) {
981+
verifySILValueHelper(this, arg, deadEndBlocks);
982+
}
931983

932-
SmallPtrSet<SILBasicBlock *, 32> liveBlocks;
933-
if (deadEndBlocks) {
934-
SILValueOwnershipChecker(*deadEndBlocks, *this, *errorBuilder, liveBlocks)
935-
.check();
936-
} else {
937-
DeadEndBlocks deadEndBlocks(f);
938-
SILValueOwnershipChecker(deadEndBlocks, *this, *errorBuilder, liveBlocks)
939-
.check();
984+
for (auto &inst : block) {
985+
for (auto result : inst.getResults()) {
986+
verifySILValueHelper(this, result, deadEndBlocks);
987+
}
988+
}
940989
}
941990
}

lib/SILOptimizer/UtilityPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ silopt_register_sources(
3131
SimplifyUnreachableContainingBlocks.cpp
3232
StripDebugInfo.cpp
3333
OwnershipDumper.cpp
34+
OwnershipVerifierTextualErrorDumper.cpp
3435
)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===--- OwnershipVerifierStateDumper.cpp ---------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
///
13+
/// \file
14+
///
15+
/// This is a simple utility pass that verifies the ownership of all SILValue in
16+
/// a module with a special flag set that causes the verifier to emit textual
17+
/// errors instead of asserting. This is done one function at a time so that we
18+
/// can number the errors as we emit them so in FileCheck tests, we can be 100%
19+
/// sure we exactly matched the number of errors.
20+
///
21+
//===----------------------------------------------------------------------===//
22+
23+
#include "swift/SIL/BasicBlockUtils.h"
24+
#include "swift/SIL/SILFunction.h"
25+
#include "swift/SIL/SILInstruction.h"
26+
#include "swift/SILOptimizer/PassManager/Passes.h"
27+
#include "swift/SILOptimizer/PassManager/Transforms.h"
28+
29+
using namespace swift;
30+
31+
//===----------------------------------------------------------------------===//
32+
// Top Level Entrypoint
33+
//===----------------------------------------------------------------------===//
34+
35+
namespace {
36+
37+
class OwnershipVerifierTextualErrorDumper : public SILFunctionTransform {
38+
void run() override {
39+
SILFunction *f = getFunction();
40+
DeadEndBlocks deadEndBlocks(f);
41+
f->verifyOwnership(&deadEndBlocks);
42+
}
43+
};
44+
45+
} // end anonymous namespace
46+
47+
SILTransform *swift::createOwnershipVerifierTextualErrorDumper() {
48+
return new OwnershipVerifierTextualErrorDumper();
49+
}

test/SIL/ownership-verifier/arguments.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt -module-name Swift -sil-ownership-verifier-enable-testing -enable-sil-verify-all=0 %s -o /dev/null 2>&1 | %FileCheck %s
1+
// RUN: %target-sil-opt -module-name Swift -sil-ownership-verifier-enable-testing -ownership-verifier-textual-error-dumper -enable-sil-verify-all=0 %s -o /dev/null 2>&1 | %FileCheck %s
22
// REQUIRES: asserts
33

44
// This is a test that verifies ownership behavior around arguments that should

test/SIL/ownership-verifier/borrow_scope_introducing_operands.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt -sil-ownership-verifier-enable-testing -enable-sil-verify-all=0 -o /dev/null %s 2>&1 | %FileCheck %s
1+
// RUN: %target-sil-opt -sil-ownership-verifier-enable-testing -ownership-verifier-textual-error-dumper -enable-sil-verify-all=0 -o /dev/null %s 2>&1 | %FileCheck %s
22
// REQUIRES: asserts
33

44
// This file tests that when we emit an error, it is a true negative. This is

test/SIL/ownership-verifier/disable_verifier_using_semantic_tag.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt -sil-ownership-verifier-enable-testing -enable-sil-verify-all=0 %s -o /dev/null 2>&1 | %FileCheck %s
1+
// RUN: %target-sil-opt -sil-ownership-verifier-enable-testing -ownership-verifier-textual-error-dumper -enable-sil-verify-all=0 %s -o /dev/null 2>&1 | %FileCheck %s
22
// REQUIRES: asserts
33

44
// This test makes sure that the ownership verifier can be disabled on specific

test/SIL/ownership-verifier/interior_pointer.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt -sil-ownership-verifier-enable-testing -enable-sil-verify-all=0 %s -o /dev/null 2>&1 | %FileCheck %s
1+
// RUN: %target-sil-opt -sil-ownership-verifier-enable-testing -ownership-verifier-textual-error-dumper -enable-sil-verify-all=0 %s -o /dev/null 2>&1 | %FileCheck %s
22
// REQUIRES: asserts
33

44
sil_stage canonical

test/SIL/ownership-verifier/leaks.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt -module-name Swift -sil-ownership-verifier-enable-testing -enable-sil-verify-all=0 %s -o /dev/null 2>&1 | %FileCheck %s
1+
// RUN: %target-sil-opt -module-name Swift -sil-ownership-verifier-enable-testing -ownership-verifier-textual-error-dumper -enable-sil-verify-all=0 %s -o /dev/null 2>&1 | %FileCheck %s
22
// REQUIRES: asserts
33

44
// This file is meant to contain dataflow tests that are true leaks. It is

test/SIL/ownership-verifier/over_consume.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt -sil-ownership-verifier-enable-testing -enable-sil-verify-all=0 -o /dev/null 2>&1 %s | %FileCheck %s
1+
// RUN: %target-sil-opt -sil-ownership-verifier-enable-testing -ownership-verifier-textual-error-dumper -enable-sil-verify-all=0 -o /dev/null %s 2>&1 | %FileCheck %s
22
// REQUIRES: asserts
33

44
sil_stage canonical

test/SIL/ownership-verifier/subobject_borrowing.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt -sil-ownership-verifier-enable-testing -enable-sil-verify-all=0 -o /dev/null 2>&1 %s | %FileCheck %s
1+
// RUN: %target-sil-opt -sil-ownership-verifier-enable-testing -ownership-verifier-textual-error-dumper -enable-sil-verify-all=0 -o /dev/null %s 2>&1 | %FileCheck %s
22
// REQUIRES: asserts
33

44
sil_stage canonical

0 commit comments

Comments
 (0)