[semantic-arc] As staging detail, add a HasQualifiedOwnership flag on all SILFunctions.

Over the past day or so I have been thinking about how we are going to need to
manage verification of semantic ARC semantics in the pass pipeline. Specifically
the Eliminator pass really needs to be a function pass to ensure that we can
transparently put it at any stage of the optimization pipeline. This means that
just having a flag on the SILVerifier that states whether or not ownership is
enabled is not sufficient for our purposes. Instead, while staging in the SIL
ownership model, we need a bit on all SILFunctions to state whether the function
has been run through the ownership model eliminator so that the verifier can
ensure that we are in a world with "SIL ownership" or in a world without "SIL
ownership", never in a world with only some "SIL ownership" instructions. We
embed this distinction in SIL by creating the concept of a function with
"qualified ownership" and a function with "unqualified ownership".

Define a function with "qualified ownership" as a function that contains no
instructions with "unqualified ownership" (i.e. unqualified load) and a function
with "unqualified ownership" as a function containing such no "ownership
qualified" instructions (i.e. load [copy]) and at least 1 unqualified ownership
instruction.

This commit embeds this distinction into SILFunction in a manner that is
transparently ignored when compiling with SIL ownership disabled. This is done
by representing qualified or unqualified ownership via an optional Boolean on
SILFunction. If the Boolean is None, then SILOwnership is not enabled and the
verifier/passes can work as appropriate. If the Boolean is not None, then it
states whether or not the function has been run through the Ownership Model
Eliminator and thus what invariants the verifier should enforce.

How does this concept flow through the compilation pipeline for functions in a
given module? When SIL Ownership is enabled, all SILFunctions that are produced
in a given module start with "qualified ownership" allowing them to contain SIL
ownership instructions. After the Ownership Model eliminator has run, the
Ownership Model sets the "unqualified" ownership flag on the SILFunction stating
that no more ownership qualified instructions are allowed to be seen in the
given function.

But what about functions that are parsed or are deserialized from another
module? Luckily, given the manner in which we have categories our functions, we
can categorize functions directly without needing to add anything to the parser
or to the deserializer. This is done by enforcing that it is illegal to have a
function with qualified ownership and unqualified ownership instructions and
asserting that functions without either are considered qualified.

rdar://28685236
This commit is contained in:
Michael Gottesman
2016-10-20 18:43:46 -07:00
parent 65641548c1
commit a3e7bd6cad
7 changed files with 209 additions and 37 deletions

View File

@@ -12,9 +12,10 @@
#define DEBUG_TYPE "sil-inst-utils"
#include "swift/SIL/InstructionUtils.h"
#include "swift/Basic/NullablePtr.h"
#include "swift/SIL/Projection.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/Projection.h"
using namespace swift;
@@ -221,3 +222,86 @@ SILValue swift::stripExpectIntrinsic(SILValue V) {
return V;
return BI->getArguments()[0];
}
namespace {
enum class OwnershipQualifiedKind {
NotApplicable,
Qualified,
Unqualified,
};
} // end anonymous namespace
static OwnershipQualifiedKind
getOwnershipQualifiedKind(const SILInstruction &I) {
switch (I.getKind()) {
case ValueKind::LoadInst:
if (cast<LoadInst>(I).getOwnershipQualifier() ==
LoadOwnershipQualifier::Unqualified)
return OwnershipQualifiedKind::Unqualified;
return OwnershipQualifiedKind::Qualified;
case ValueKind::StoreInst:
if (cast<StoreInst>(I).getOwnershipQualifier() ==
StoreOwnershipQualifier::Unqualified)
return OwnershipQualifiedKind::Unqualified;
return OwnershipQualifiedKind::Qualified;
case ValueKind::LoadBorrowInst:
case ValueKind::EndBorrowInst:
return OwnershipQualifiedKind::Qualified;
default:
return OwnershipQualifiedKind::NotApplicable;
}
}
bool FunctionOwnershipEvaluator::evaluate(const SILInstruction &I) {
assert(I.getFunction() == F.get() && "Can not evaluate function ownership "
"implications of an instruction that "
"does not belong to the instruction "
"that we are evaluating");
// If SIL ownership is not enabled in this module, just return true. There is
// no further work to do here.
if (!I.getModule().getOptions().EnableSILOwnership)
return true;
switch (getOwnershipQualifiedKind(I)) {
case OwnershipQualifiedKind::Unqualified: {
// If we already know that the function has unqualified ownership, just
// return early.
if (!F.get()->hasQualifiedOwnership().getValue())
return true;
// Ok, so we know at this point that we have qualified ownership. If we have
// seen any instructions with qualified ownership, we have an error since
// the function mixes qualified and unqualified instructions.
if (HasOwnershipQualifiedInstruction)
return false;
// Otherwise, set the function to have unqualified ownership. This will
// ensure that no more Qualified instructions can be added to the given
// function.
F.get()->setUnqualifiedOwnership();
return true;
}
case OwnershipQualifiedKind::Qualified: {
// First check if our function has unqualified ownership. If we already do
// have unqualified ownership, then we know that we have already seen an
// unqualified ownership instruction. This means the function has both
// qualified and unqualified instructions. =><=.
if (!F.get()->hasQualifiedOwnership().getValue())
return false;
// Ok, at this point we know that we are still qualified. Since functions
// start as qualified, we need to set the HasOwnershipQualifiedInstructions
// so we do not need to look back through the function if we see an
// unqualified instruction later on.
HasOwnershipQualifiedInstruction = true;
return true;
}
case OwnershipQualifiedKind::NotApplicable: {
// Not Applicable instr
return true;
}
}
}