mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
SILGen: Explicitly mark uninitialized locals with mark_uninitialized, and have DI only consider mark_uninitialized storage.
Have SILGen mark all variables bound from pattern bindings without initializers (and *only* ones without initializers) with mark_uninitialized [var] pseudo instructions. On the DI end, *only* consider mark_uninitialized instructions for DI analysis. This has many benefits: - DI doesn't waste time analyzing locals that are trivially initialized in the original source code. - DI doesn't try to mangle canonical SIL that has been inlined from transparent functions, which may have been optimized into a form DI isn't written to understand. While we're here, fix an issue with DCE where it would try to kill unused MarkUninitialized instructions. Although MarkUninitialized has no side effects, it still is semantically important to raw SIL, and can't be killed. Chris did most of the work here; I just finished updating tests and fixing bugs. Swift SVN r13247
This commit is contained in:
@@ -279,9 +279,9 @@ public:
|
|||||||
MarkUninitializedInst::Kind k){
|
MarkUninitializedInst::Kind k){
|
||||||
return insert(new (F.getModule()) MarkUninitializedInst(loc, src, k));
|
return insert(new (F.getModule()) MarkUninitializedInst(loc, src, k));
|
||||||
}
|
}
|
||||||
MarkUninitializedInst *createMarkUninitializedGlobalVar(SILLocation loc,
|
MarkUninitializedInst *createMarkUninitializedVar(SILLocation loc,
|
||||||
SILValue src) {
|
SILValue src) {
|
||||||
return createMarkUninitialized(loc, src, MarkUninitializedInst::GlobalVar);
|
return createMarkUninitialized(loc, src, MarkUninitializedInst::Var);
|
||||||
}
|
}
|
||||||
MarkUninitializedInst *createMarkUninitializedRootSelf(SILLocation loc,
|
MarkUninitializedInst *createMarkUninitializedRootSelf(SILLocation loc,
|
||||||
SILValue src) {
|
SILValue src) {
|
||||||
|
|||||||
@@ -818,9 +818,8 @@ class MarkUninitializedInst
|
|||||||
public:
|
public:
|
||||||
// This enum captures what the mark_uninitialized instruction is designating.
|
// This enum captures what the mark_uninitialized instruction is designating.
|
||||||
enum Kind {
|
enum Kind {
|
||||||
/// GlobalVar designates the start of a global variable live range in a
|
/// Var designates the start of a normal variable live range.
|
||||||
/// top level code declaration.
|
Var,
|
||||||
GlobalVar,
|
|
||||||
|
|
||||||
/// RootSelf designates "self" in a struct, enum, or root class.
|
/// RootSelf designates "self" in a struct, enum, or root class.
|
||||||
RootSelf,
|
RootSelf,
|
||||||
@@ -846,7 +845,7 @@ public:
|
|||||||
|
|
||||||
Kind getKind() const { return ThisKind; }
|
Kind getKind() const { return ThisKind; }
|
||||||
|
|
||||||
bool isGlobalVar() const { return ThisKind == GlobalVar; }
|
bool isVar() const { return ThisKind == Var; }
|
||||||
bool isRootSelf() const {
|
bool isRootSelf() const {
|
||||||
return ThisKind == RootSelf;
|
return ThisKind == RootSelf;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1471,17 +1471,23 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case ValueKind::MarkUninitializedInst: {
|
case ValueKind::MarkUninitializedInst: {
|
||||||
|
if (P.parseToken(tok::l_square, diag::expected_tok_in_sil_instr, "["))
|
||||||
|
return true;
|
||||||
|
|
||||||
Identifier KindId;
|
Identifier KindId;
|
||||||
SourceLoc KindLoc;
|
SourceLoc KindLoc = P.Tok.getLoc();
|
||||||
if (P.parseToken(tok::l_square, diag::expected_tok_in_sil_instr, "[") ||
|
if (P.consumeIf(tok::kw_var))
|
||||||
P.parseIdentifier(KindId, KindLoc, diag::expected_tok_in_sil_instr,
|
KindId = P.Context.getIdentifier("var");
|
||||||
"globalvar or rootself") ||
|
else if (P.parseIdentifier(KindId, KindLoc,
|
||||||
P.parseToken(tok::r_square, diag::expected_tok_in_sil_instr, "]"))
|
diag::expected_tok_in_sil_instr, "kind"))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (P.parseToken(tok::r_square, diag::expected_tok_in_sil_instr, "]"))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
MarkUninitializedInst::Kind Kind;
|
MarkUninitializedInst::Kind Kind;
|
||||||
if (KindId.str() == "globalvar")
|
if (KindId.str() == "var")
|
||||||
Kind = MarkUninitializedInst::GlobalVar;
|
Kind = MarkUninitializedInst::Var;
|
||||||
else if (KindId.str() == "rootself")
|
else if (KindId.str() == "rootself")
|
||||||
Kind = MarkUninitializedInst::RootSelf;
|
Kind = MarkUninitializedInst::RootSelf;
|
||||||
else if (KindId.str() == "derivedself")
|
else if (KindId.str() == "derivedself")
|
||||||
@@ -1492,7 +1498,7 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB) {
|
|||||||
Kind = MarkUninitializedInst::DelegatingSelf;
|
Kind = MarkUninitializedInst::DelegatingSelf;
|
||||||
else {
|
else {
|
||||||
P.diagnose(KindLoc, diag::expected_tok_in_sil_instr,
|
P.diagnose(KindLoc, diag::expected_tok_in_sil_instr,
|
||||||
"globalvar, rootself, derivedself, derivedselfonly, "
|
"var, rootself, derivedself, derivedselfonly, "
|
||||||
"or delegatingself");
|
"or delegatingself");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -619,7 +619,7 @@ public:
|
|||||||
void visitMarkUninitializedInst(MarkUninitializedInst *MU) {
|
void visitMarkUninitializedInst(MarkUninitializedInst *MU) {
|
||||||
OS << "mark_uninitialized ";
|
OS << "mark_uninitialized ";
|
||||||
switch (MU->getKind()) {
|
switch (MU->getKind()) {
|
||||||
case MarkUninitializedInst::GlobalVar: OS << "[globalvar] "; break;
|
case MarkUninitializedInst::Var: OS << "[var] "; break;
|
||||||
case MarkUninitializedInst::RootSelf: OS << "[rootself] "; break;
|
case MarkUninitializedInst::RootSelf: OS << "[rootself] "; break;
|
||||||
case MarkUninitializedInst::DerivedSelf: OS << "[derivedself] "; break;
|
case MarkUninitializedInst::DerivedSelf: OS << "[derivedself] "; break;
|
||||||
case MarkUninitializedInst::DerivedSelfOnly:
|
case MarkUninitializedInst::DerivedSelfOnly:
|
||||||
|
|||||||
@@ -511,6 +511,60 @@ struct InitializationForPattern
|
|||||||
#include "swift/AST/PatternNodes.def"
|
#include "swift/AST/PatternNodes.def"
|
||||||
#undef INVALID_PATTERN
|
#undef INVALID_PATTERN
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A visitor that marks local variables uninitialized in a pattern binding
|
||||||
|
/// without an initializer.
|
||||||
|
struct MarkPatternUninitialized
|
||||||
|
: public PatternVisitor<MarkPatternUninitialized>
|
||||||
|
{
|
||||||
|
SILGenFunction &Gen;
|
||||||
|
MarkPatternUninitialized(SILGenFunction &Gen) : Gen(Gen) {}
|
||||||
|
|
||||||
|
// Paren, Typed, and Var patterns are noops, just look through them.
|
||||||
|
void visitParenPattern(ParenPattern *P) {
|
||||||
|
return visit(P->getSubPattern());
|
||||||
|
}
|
||||||
|
void visitTypedPattern(TypedPattern *P) {
|
||||||
|
return visit(P->getSubPattern());
|
||||||
|
}
|
||||||
|
void visitVarPattern(VarPattern *P) {
|
||||||
|
return visit(P->getSubPattern());
|
||||||
|
}
|
||||||
|
|
||||||
|
void visitAnyPattern(AnyPattern *P) {}
|
||||||
|
|
||||||
|
void visitTuplePattern(TuplePattern *P) {
|
||||||
|
for (auto elt : P->getFields())
|
||||||
|
visit(elt.getPattern());
|
||||||
|
}
|
||||||
|
|
||||||
|
void visitNamedPattern(NamedPattern *P) {
|
||||||
|
VarDecl *var = P->getDecl();
|
||||||
|
if (!var->hasStorage())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Emit a mark_uninitialized for the variable's storage, and fix up the
|
||||||
|
// loc to refer to the marker.
|
||||||
|
assert(Gen.VarLocs.count(var)
|
||||||
|
&& "no varloc for uninitialized var?!");
|
||||||
|
auto &varLoc = Gen.VarLocs[var];
|
||||||
|
assert(varLoc.isAddress()
|
||||||
|
&& "uninitialized var doesn't have an address?!");
|
||||||
|
auto mu
|
||||||
|
= Gen.B.createMarkUninitializedVar(P->getDecl(), varLoc.getAddress());
|
||||||
|
Gen.VarLocs[var] = SILGenFunction::VarLoc::getAddress(mu, varLoc.box);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Handle bindings from 'case' labels and match expressions.
|
||||||
|
#define INVALID_PATTERN(Id, Parent) \
|
||||||
|
void visit##Id##Pattern(Id##Pattern *) { \
|
||||||
|
llvm_unreachable("pattern not valid in argument or var binding"); \
|
||||||
|
}
|
||||||
|
#define PATTERN(Id, Parent)
|
||||||
|
#define REFUTABLE_PATTERN(Id, Parent) INVALID_PATTERN(Id, Parent)
|
||||||
|
#include "swift/AST/PatternNodes.def"
|
||||||
|
#undef INVALID_PATTERN
|
||||||
|
};
|
||||||
|
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
@@ -528,9 +582,6 @@ SILGenFunction::emitInitializationForVarDecl(VarDecl *vd, bool isArgument,
|
|||||||
SILValue addr = B.createGlobalAddr(vd, vd,
|
SILValue addr = B.createGlobalAddr(vd, vd,
|
||||||
getLoweredType(vd->getType()).getAddressType());
|
getLoweredType(vd->getType()).getAddressType());
|
||||||
|
|
||||||
// In a top level context, all global variables must be initialized.
|
|
||||||
addr = B.createMarkUninitializedGlobalVar(vd, addr);
|
|
||||||
|
|
||||||
VarLocs[vd] = SILGenFunction::VarLoc::getAddress(addr);
|
VarLocs[vd] = SILGenFunction::VarLoc::getAddress(addr);
|
||||||
return InitializationPtr(new GlobalInitialization(addr));
|
return InitializationPtr(new GlobalInitialization(addr));
|
||||||
}
|
}
|
||||||
@@ -543,10 +594,9 @@ SILGenFunction::emitInitializationForVarDecl(VarDecl *vd, bool isArgument,
|
|||||||
|
|
||||||
// If this is a 'let' initialization for a non-address-only type, set up a
|
// If this is a 'let' initialization for a non-address-only type, set up a
|
||||||
// let binding, which stores the initialization value into VarLocs directly.
|
// let binding, which stores the initialization value into VarLocs directly.
|
||||||
if (vd->isLet()) {
|
if (vd->isLet())
|
||||||
return InitializationPtr(new LetValueInitialization(vd, isArgument, *this));
|
return InitializationPtr(new LetValueInitialization(vd, isArgument, *this));
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we have a normal local-variable initialization.
|
// Otherwise, we have a normal local-variable initialization.
|
||||||
auto varInit = emitLocalVariableWithCleanup(vd);
|
auto varInit = emitLocalVariableWithCleanup(vd);
|
||||||
|
|
||||||
@@ -569,12 +619,13 @@ void SILGenFunction::visitPatternBindingDecl(PatternBindingDecl *D) {
|
|||||||
.visit(D->getPattern());
|
.visit(D->getPattern());
|
||||||
|
|
||||||
// If an initial value expression was specified by the decl, emit it into
|
// If an initial value expression was specified by the decl, emit it into
|
||||||
// the initialization. Otherwise, leave it uninitialized for DI to resolve.
|
// the initialization. Otherwise, mark it uninitialized for DI to resolve.
|
||||||
if (D->getInit()) {
|
if (D->getInit()) {
|
||||||
FullExpr Scope(Cleanups, CleanupLocation(D->getInit()));
|
FullExpr Scope(Cleanups, CleanupLocation(D->getInit()));
|
||||||
emitExprInto(D->getInit(), initialization.get());
|
emitExprInto(D->getInit(), initialization.get());
|
||||||
} else {
|
} else {
|
||||||
initialization->finishInitialization(*this);
|
initialization->finishInitialization(*this);
|
||||||
|
MarkPatternUninitialized(*this).visit(D->getPattern());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -190,10 +190,15 @@ static bool checkAllocBoxUses(AllocBoxInst *ABI, SILValue V,
|
|||||||
auto *User = UI->getUser();
|
auto *User = UI->getUser();
|
||||||
|
|
||||||
// These instructions do not cause the box's address to escape.
|
// These instructions do not cause the box's address to escape.
|
||||||
if (!useCaptured(UI)) {
|
if (!useCaptured(UI)) {
|
||||||
Users.push_back(User);
|
Users.push_back(User);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto *MUI = dyn_cast<MarkUninitializedInst>(User)) {
|
||||||
|
Users.push_back(MUI);
|
||||||
|
return checkAllocBoxUses(ABI, SILValue(MUI, 0), Users);
|
||||||
|
}
|
||||||
|
|
||||||
// These instructions only cause the alloc_box to escape if they are used in
|
// These instructions only cause the alloc_box to escape if they are used in
|
||||||
// a way that escapes. Recursively check that the uses of the instruction
|
// a way that escapes. Recursively check that the uses of the instruction
|
||||||
|
|||||||
@@ -592,19 +592,18 @@ static bool
|
|||||||
examineAllocBoxInst(AllocBoxInst *ABI, ReachabilityInfo &RI,
|
examineAllocBoxInst(AllocBoxInst *ABI, ReachabilityInfo &RI,
|
||||||
PartialApplyIndexMap &IM) {
|
PartialApplyIndexMap &IM) {
|
||||||
SmallVector<SILInstruction*, 32> Mutations;
|
SmallVector<SILInstruction*, 32> Mutations;
|
||||||
|
|
||||||
for (auto *O : ABI->getUses()) {
|
// If the AllocBox is used by a mark_uninitialized, scan the MUI for
|
||||||
|
// interesting uses.
|
||||||
|
SILValue Addr(ABI, 1);
|
||||||
|
if (Addr.hasOneUse())
|
||||||
|
if (auto MUI = dyn_cast<MarkUninitializedInst>(Addr.use_begin()->getUser()))
|
||||||
|
Addr = SILValue(MUI, 0);
|
||||||
|
|
||||||
|
for (auto *O : Addr.getUses()) {
|
||||||
if (auto *PAI = dyn_cast<PartialApplyInst>(O->getUser())) {
|
if (auto *PAI = dyn_cast<PartialApplyInst>(O->getUser())) {
|
||||||
unsigned OpNo = O->getOperandNumber();
|
unsigned OpNo = O->getOperandNumber();
|
||||||
assert(OpNo != 0 && "Alloc box used as callee of partial apply?");
|
assert(OpNo != 0 && "Alloc box used as callee of partial apply?");
|
||||||
if (O->get().getResultNumber() == 1) {
|
|
||||||
if (OpNo < 2 ||
|
|
||||||
PAI->getOperand(OpNo - 1) != SILValue(ABI, 0))
|
|
||||||
return false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
assert(O->get().getResultNumber() == 0 &&
|
|
||||||
"Unexpected result number of alloc box instruction used?");
|
|
||||||
|
|
||||||
// If we've already seen this partial apply, then it means the same alloc
|
// If we've already seen this partial apply, then it means the same alloc
|
||||||
// box is being captured twice by the same closure, which is odd and
|
// box is being captured twice by the same closure, which is odd and
|
||||||
@@ -612,10 +611,9 @@ examineAllocBoxInst(AllocBoxInst *ABI, ReachabilityInfo &RI,
|
|||||||
if (IM.count(PAI))
|
if (IM.count(PAI))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Verify that the next operand of the partial apply is the second result
|
// Verify that the previous operand of the partial apply is the refcount
|
||||||
// of the alloc_box.
|
// result of the alloc_box.
|
||||||
if (OpNo + 1 >= PAI->getNumOperands() ||
|
if (PAI->getOperand(OpNo - 1) != SILValue(ABI, 0))
|
||||||
PAI->getOperand(OpNo + 1) != SILValue(ABI, 1))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto closureType = PAI->getType().castTo<SILFunctionType>();
|
auto closureType = PAI->getType().castTo<SILFunctionType>();
|
||||||
@@ -627,7 +625,7 @@ examineAllocBoxInst(AllocBoxInst *ABI, ReachabilityInfo &RI,
|
|||||||
// Calculate the index into the closure's argument list of the captured
|
// Calculate the index into the closure's argument list of the captured
|
||||||
// box pointer (the captured address is always the immediately following
|
// box pointer (the captured address is always the immediately following
|
||||||
// index so is not stored separately);
|
// index so is not stored separately);
|
||||||
unsigned Index = OpNo - 1 + closureType->getInterfaceParameters().size();
|
unsigned Index = OpNo - 2 + closureType->getInterfaceParameters().size();
|
||||||
|
|
||||||
// Verify that this closure is known not to mutate the captured value; if
|
// Verify that this closure is known not to mutate the captured value; if
|
||||||
// it does, then conservatively refuse to promote any captures of this
|
// it does, then conservatively refuse to promote any captures of this
|
||||||
@@ -722,9 +720,12 @@ processPartialApplyInst(PartialApplyInst *PAI, IndicesSet &PromotableIndices,
|
|||||||
if (PromotableIndices.count(Index)) {
|
if (PromotableIndices.count(Index)) {
|
||||||
SILValue BoxValue = PAI->getOperand(OpNo);
|
SILValue BoxValue = PAI->getOperand(OpNo);
|
||||||
SILValue AddrValue = PAI->getOperand(OpNo + 1);
|
SILValue AddrValue = PAI->getOperand(OpNo + 1);
|
||||||
assert(BoxValue.getDef() == AddrValue.getDef() &&
|
SILValue UnderlyingAddrValue = AddrValue;
|
||||||
|
if (auto *MUI = dyn_cast<MarkUninitializedInst>(AddrValue))
|
||||||
|
UnderlyingAddrValue = MUI->getOperand();
|
||||||
|
assert(BoxValue.getDef() == UnderlyingAddrValue.getDef() &&
|
||||||
BoxValue.getResultNumber() == 0 &&
|
BoxValue.getResultNumber() == 0 &&
|
||||||
AddrValue.getResultNumber() == 1);
|
UnderlyingAddrValue.getResultNumber() == 1);
|
||||||
|
|
||||||
// Emit a strong release, zapping a retain if we can.
|
// Emit a strong release, zapping a retain if we can.
|
||||||
B.emitStrongRelease(PAI->getLoc(), BoxValue);
|
B.emitStrongRelease(PAI->getLoc(), BoxValue);
|
||||||
|
|||||||
@@ -65,10 +65,8 @@ DIMemoryObjectInfo::DIMemoryObjectInfo(SILInstruction *MI) {
|
|||||||
|
|
||||||
// If this is a 'self' decl in an init method for a non-enum value, then we
|
// If this is a 'self' decl in an init method for a non-enum value, then we
|
||||||
// want to process the stored members of the struct/class elementwise.
|
// want to process the stored members of the struct/class elementwise.
|
||||||
if (MUI->getKind() != MarkUninitializedInst::GlobalVar) {
|
if (isAnyInitSelf() && !isEnumSelf() && !isDelegatingInit())
|
||||||
if (!isEnumSelf() && !isDelegatingInit())
|
IsSelfOfNonDelegatingInitializer = true;
|
||||||
IsSelfOfNonDelegatingInitializer = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the number of elements to track in this memory object.
|
// Compute the number of elements to track in this memory object.
|
||||||
@@ -807,7 +805,8 @@ void ElementUseCollector::collectClassSelfUses() {
|
|||||||
assert(MUI->hasOneUse());
|
assert(MUI->hasOneUse());
|
||||||
auto *TheStore = cast<StoreInst>((*MUI->use_begin())->getUser());
|
auto *TheStore = cast<StoreInst>((*MUI->use_begin())->getUser());
|
||||||
SILValue SelfBox = TheStore->getOperand(1);
|
SILValue SelfBox = TheStore->getOperand(1);
|
||||||
assert(isa<AllocBoxInst>(SelfBox) || isa<AllocStackInst>(SelfBox));
|
assert(isa<MarkUninitializedInst>(SelfBox) || isa<AllocBoxInst>(SelfBox) ||
|
||||||
|
isa<AllocStackInst>(SelfBox));
|
||||||
|
|
||||||
// Okay, given that we have a proper setup, we walk the use chains of the self
|
// Okay, given that we have a proper setup, we walk the use chains of the self
|
||||||
// box to find any accesses to it. The possible uses are one of:
|
// box to find any accesses to it. The possible uses are one of:
|
||||||
@@ -959,7 +958,8 @@ void ElementUseCollector::collectDelegatingClassInitSelfUses() {
|
|||||||
assert(MUI->hasOneUse());
|
assert(MUI->hasOneUse());
|
||||||
auto *TheStore = cast<StoreInst>((*MUI->use_begin())->getUser());
|
auto *TheStore = cast<StoreInst>((*MUI->use_begin())->getUser());
|
||||||
SILValue SelfBox = TheStore->getOperand(1);
|
SILValue SelfBox = TheStore->getOperand(1);
|
||||||
assert(isa<AllocBoxInst>(SelfBox) || isa<AllocStackInst>(SelfBox));
|
assert(isa<MarkUninitializedInst>(SelfBox) || isa<AllocBoxInst>(SelfBox) ||
|
||||||
|
isa<AllocStackInst>(SelfBox));
|
||||||
|
|
||||||
// Okay, given that we have a proper setup, we walk the use chains of the self
|
// Okay, given that we have a proper setup, we walk the use chains of the self
|
||||||
// box to find any accesses to it. The possible uses are one of:
|
// box to find any accesses to it. The possible uses are one of:
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public:
|
|||||||
/// isAnyInitSelf - Return true if this is 'self' in any kind of initializer.
|
/// isAnyInitSelf - Return true if this is 'self' in any kind of initializer.
|
||||||
bool isAnyInitSelf() const {
|
bool isAnyInitSelf() const {
|
||||||
if (auto *MUI = dyn_cast<MarkUninitializedInst>(MemoryInst))
|
if (auto *MUI = dyn_cast<MarkUninitializedInst>(MemoryInst))
|
||||||
return !MUI->isGlobalVar();
|
return !MUI->isVar();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1484,13 +1484,6 @@ bool LifetimeChecker::isInitializedAtUse(const DIMemoryUse &Use,
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
static void processMemoryObject(SILInstruction *I) {
|
static void processMemoryObject(SILInstruction *I) {
|
||||||
// If the allocation's address has a mark_uninitialized use, then we'll
|
|
||||||
// analyze it when we look at the mark_uninitialized instruction itself.
|
|
||||||
if (!isa<MarkUninitializedInst>(I))
|
|
||||||
for (auto UI : SILValue(I, 1).getUses())
|
|
||||||
if (isa<MarkUninitializedInst>(UI->getUser()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
DEBUG(llvm::dbgs() << "*** Definite Init looking at: " << *I << "\n");
|
DEBUG(llvm::dbgs() << "*** Definite Init looking at: " << *I << "\n");
|
||||||
DIMemoryObjectInfo MemInfo(I);
|
DIMemoryObjectInfo MemInfo(I);
|
||||||
|
|
||||||
@@ -1514,8 +1507,7 @@ static void checkDefiniteInitialization(SILFunction &Fn) {
|
|||||||
for (auto &BB : Fn) {
|
for (auto &BB : Fn) {
|
||||||
for (auto I = BB.begin(), E = BB.end(); I != E; ++I) {
|
for (auto I = BB.begin(), E = BB.end(); I != E; ++I) {
|
||||||
SILInstruction *Inst = I;
|
SILInstruction *Inst = I;
|
||||||
if (isa<AllocBoxInst>(Inst) || isa<AllocStackInst>(Inst) ||
|
if (isa<MarkUninitializedInst>(Inst))
|
||||||
isa<MarkUninitializedInst>(Inst))
|
|
||||||
processMemoryObject(Inst);
|
processMemoryObject(Inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,9 +93,14 @@ static AllocStackInst *isCopyToOrFromStack(Operand *UI) {
|
|||||||
|
|
||||||
// If this is an explicit copy_addr, return the other operand if it is a stack
|
// If this is an explicit copy_addr, return the other operand if it is a stack
|
||||||
// allocation.
|
// allocation.
|
||||||
if (UI->getOperandNumber() == 0)
|
SILValue OtherOp =
|
||||||
return dyn_cast<AllocStackInst>(CAI->getDest());
|
UI->getOperandNumber() == 0 ? CAI->getDest() : CAI->getSrc();
|
||||||
return dyn_cast<AllocStackInst>(CAI->getSrc());
|
|
||||||
|
// Look through mark_uninitialized.
|
||||||
|
if (auto *MUI = dyn_cast<MarkUninitializedInst>(OtherOp))
|
||||||
|
OtherOp = MUI->getOperand();
|
||||||
|
|
||||||
|
return dyn_cast<AllocStackInst>(OtherOp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ swift::isInstructionTriviallyDead(SILInstruction *I) {
|
|||||||
if (!ILI->getValue())
|
if (!ILI->getValue())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// mark_uninitialized is never dead.
|
||||||
|
if (isa<MarkUninitializedInst>(I))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!I->mayHaveSideEffects())
|
if (!I->mayHaveSideEffects())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|||||||
@@ -1005,7 +1005,7 @@ var staticProp: Int = 0
|
|||||||
sil private @globalinit_func0 : $@thin () -> () {
|
sil private @globalinit_func0 : $@thin () -> () {
|
||||||
bb0:
|
bb0:
|
||||||
%0 = global_addr #staticProp : $*Int
|
%0 = global_addr #staticProp : $*Int
|
||||||
%1 = mark_uninitialized [globalvar] %0 : $*Int
|
%1 = mark_uninitialized [var] %0 : $*Int
|
||||||
%7 = tuple ()
|
%7 = tuple ()
|
||||||
return %7 : $()
|
return %7 : $()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ sil @makesInt : $@thin () -> Int
|
|||||||
// CHECK-LABEL: sil @use_before_init
|
// CHECK-LABEL: sil @use_before_init
|
||||||
sil @use_before_init : $@thin () -> Int {
|
sil @use_before_init : $@thin () -> Int {
|
||||||
bb0:
|
bb0:
|
||||||
%1 = alloc_box $Int // expected-note {{variable defined here}}
|
%0 = alloc_box $Int
|
||||||
%4 = load %1#1 : $*Int // expected-error {{variable '<unknown>' used before being initialized}}
|
%1 = mark_uninitialized [var] %0#1 : $*Int // expected-note {{variable defined here}}
|
||||||
strong_release %1#0 : $Builtin.ObjectPointer
|
%4 = load %1 : $*Int // expected-error {{variable '<unknown>' used before being initialized}}
|
||||||
|
strong_release %0#0 : $Builtin.ObjectPointer
|
||||||
%9 = return %4 : $Int
|
%9 = return %4 : $Int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,14 +23,15 @@ bb0:
|
|||||||
// CHECK-LABEL: @inout_uninit
|
// CHECK-LABEL: @inout_uninit
|
||||||
sil @inout_uninit : $@thin () -> () {
|
sil @inout_uninit : $@thin () -> () {
|
||||||
bb0:
|
bb0:
|
||||||
%1 = alloc_box $Int // expected-note {{variable defined here}}
|
%0 = alloc_box $Int
|
||||||
|
%1 = mark_uninitialized [var] %0#1 : $*Int // expected-note {{variable defined here}}
|
||||||
|
|
||||||
%5 = function_ref @takes_Int_inout : $@thin (@inout Int) -> ()
|
%5 = function_ref @takes_Int_inout : $@thin (@inout Int) -> ()
|
||||||
%6 = apply %5(%1#1) : $@thin (@inout Int) -> () // expected-error {{variable '<unknown>' passed by reference before being initialized}}
|
%6 = apply %5(%1) : $@thin (@inout Int) -> () // expected-error {{variable '<unknown>' passed by reference before being initialized}}
|
||||||
|
|
||||||
%0 = tuple ()
|
%t = tuple ()
|
||||||
strong_release %1#0 : $Builtin.ObjectPointer
|
strong_release %0#0 : $Builtin.ObjectPointer
|
||||||
return %0 : $()
|
return %t : $()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -45,14 +47,16 @@ bb0:
|
|||||||
// CHECK-LABEL: sil @used_by_inout
|
// CHECK-LABEL: sil @used_by_inout
|
||||||
sil @used_by_inout : $@thin (Int) -> (Int, Int) {
|
sil @used_by_inout : $@thin (Int) -> (Int, Int) {
|
||||||
bb0(%0 : $Int):
|
bb0(%0 : $Int):
|
||||||
%1 = alloc_box $Int
|
%91 = alloc_box $Int
|
||||||
%2 = store %0 to %1#1 : $*Int
|
%1 = mark_uninitialized [var] %91#1 : $*Int
|
||||||
%3 = load %1#1 : $*Int
|
|
||||||
|
%2 = store %0 to %1 : $*Int
|
||||||
|
%3 = load %1 : $*Int
|
||||||
%5 = function_ref @takes_Int_inout : $@thin (@inout Int) -> ()
|
%5 = function_ref @takes_Int_inout : $@thin (@inout Int) -> ()
|
||||||
%6 = apply %5(%1#1) : $@thin (@inout Int) -> ()
|
%6 = apply %5(%1) : $@thin (@inout Int) -> ()
|
||||||
%7 = load %1#1 : $*Int
|
%7 = load %1 : $*Int
|
||||||
%8 = tuple (%3 : $Int, %7 : $Int)
|
%8 = tuple (%3 : $Int, %7 : $Int)
|
||||||
strong_release %1#0 : $Builtin.ObjectPointer
|
strong_release %91#0 : $Builtin.ObjectPointer
|
||||||
%11 = return %8 : $(Int, Int)
|
%11 = return %8 : $(Int, Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,12 +73,14 @@ sil @returns_generic_struct : $@thin (@out AddressOnlyStruct) -> ()
|
|||||||
// CHECK-LABEL: sil @call_struct_return_function
|
// CHECK-LABEL: sil @call_struct_return_function
|
||||||
sil @call_struct_return_function : $@thin () -> Int {
|
sil @call_struct_return_function : $@thin () -> Int {
|
||||||
bb0:
|
bb0:
|
||||||
%1 = alloc_box $AddressOnlyStruct
|
%0 = alloc_box $AddressOnlyStruct
|
||||||
|
%1 = mark_uninitialized [var] %0#1 : $*AddressOnlyStruct
|
||||||
|
|
||||||
%2 = function_ref @returns_generic_struct : $@thin (@out AddressOnlyStruct) -> ()
|
%2 = function_ref @returns_generic_struct : $@thin (@out AddressOnlyStruct) -> ()
|
||||||
%3 = apply %2(%1#1) : $@thin (@out AddressOnlyStruct) -> ()
|
%3 = apply %2(%1) : $@thin (@out AddressOnlyStruct) -> ()
|
||||||
%4 = struct_element_addr %1#1 : $*AddressOnlyStruct, #b
|
%4 = struct_element_addr %1 : $*AddressOnlyStruct, #b
|
||||||
%5 = load %4 : $*Int
|
%5 = load %4 : $*Int
|
||||||
strong_release %1#0 : $Builtin.ObjectPointer
|
strong_release %0#0 : $Builtin.ObjectPointer
|
||||||
return %5 : $Int
|
return %5 : $Int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,14 +88,15 @@ bb0:
|
|||||||
// CHECK-LABEL: sil @tuple_elements1
|
// CHECK-LABEL: sil @tuple_elements1
|
||||||
sil @tuple_elements1 : $@thin (Int) -> () {
|
sil @tuple_elements1 : $@thin (Int) -> () {
|
||||||
bb0(%0 : $Int):
|
bb0(%0 : $Int):
|
||||||
%3 = alloc_box $(Int, Int) // expected-note 1 {{variable defined here}}
|
%2 = alloc_box $(Int, Int)
|
||||||
%4 = tuple_element_addr %3#1 : $*(Int, Int), 0
|
%3 = mark_uninitialized [var] %2#1 : $*(Int, Int) // expected-note {{variable defined here}}
|
||||||
%5 = tuple_element_addr %3#1 : $*(Int, Int), 1
|
%4 = tuple_element_addr %3 : $*(Int, Int), 0
|
||||||
|
%5 = tuple_element_addr %3 : $*(Int, Int), 1
|
||||||
%14 = function_ref @takes_Int_inout : $@thin (@inout Int) -> ()
|
%14 = function_ref @takes_Int_inout : $@thin (@inout Int) -> ()
|
||||||
%15 = tuple_element_addr %3#1 : $*(Int, Int), 1
|
%15 = tuple_element_addr %3 : $*(Int, Int), 1
|
||||||
%16 = apply %14(%15) : $@thin (@inout Int) -> () // expected-error {{variable '<unknown>.1' passed by reference before being initialized}}
|
%16 = apply %14(%15) : $@thin (@inout Int) -> () // expected-error {{variable '<unknown>.1' passed by reference before being initialized}}
|
||||||
|
|
||||||
strong_release %3#0 : $Builtin.ObjectPointer
|
strong_release %2#0 : $Builtin.ObjectPointer
|
||||||
%99 = tuple ()
|
%99 = tuple ()
|
||||||
return %99 : $()
|
return %99 : $()
|
||||||
}
|
}
|
||||||
@@ -97,14 +104,15 @@ bb0(%0 : $Int):
|
|||||||
// CHECK-LABEL: sil @tuple_elements2
|
// CHECK-LABEL: sil @tuple_elements2
|
||||||
sil @tuple_elements2 : $@thin (Int) -> (Int, Int) {
|
sil @tuple_elements2 : $@thin (Int) -> (Int, Int) {
|
||||||
bb0(%0 : $Int):
|
bb0(%0 : $Int):
|
||||||
%3 = alloc_box $(Int, Int) // expected-note {{variable defined here}}
|
%2 = alloc_box $(Int, Int)
|
||||||
%18 = tuple_element_addr %3#1 : $*(Int, Int), 0
|
%3 = mark_uninitialized [var] %2#1 : $*(Int, Int) // expected-note {{variable defined here}}
|
||||||
|
%18 = tuple_element_addr %3 : $*(Int, Int), 0
|
||||||
store %0 to %18 : $*Int
|
store %0 to %18 : $*Int
|
||||||
%20 = load %3#1 : $*(Int, Int) // expected-error {{variable '<unknown>.1' used before being initialized}}
|
%20 = load %3 : $*(Int, Int) // expected-error {{variable '<unknown>.1' used before being initialized}}
|
||||||
%21 = tuple_extract %20 : $(Int, Int), 0
|
%21 = tuple_extract %20 : $(Int, Int), 0
|
||||||
%22 = tuple_extract %20 : $(Int, Int), 1
|
%22 = tuple_extract %20 : $(Int, Int), 1
|
||||||
%23 = tuple (%21 : $Int, %22 : $Int)
|
%23 = tuple (%21 : $Int, %22 : $Int)
|
||||||
strong_release %3#0 : $Builtin.ObjectPointer
|
strong_release %2#0 : $Builtin.ObjectPointer
|
||||||
return %23 : $(Int, Int)
|
return %23 : $(Int, Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,10 +121,11 @@ bb0(%0 : $Int):
|
|||||||
// CHECK-LABEL: sil @copy_addr1
|
// CHECK-LABEL: sil @copy_addr1
|
||||||
sil @copy_addr1 : $@thin <T> (@out T, @in T) -> () {
|
sil @copy_addr1 : $@thin <T> (@out T, @in T) -> () {
|
||||||
bb0(%0 : $*T, %1 : $*T):
|
bb0(%0 : $*T, %1 : $*T):
|
||||||
%4 = alloc_box $T
|
%3 = alloc_box $T
|
||||||
copy_addr [take] %1 to [initialization] %4#1 : $*T
|
%4 = mark_uninitialized [var] %3#1 : $*T
|
||||||
copy_addr %4#1 to [initialization] %0 : $*T
|
copy_addr [take] %1 to [initialization] %4 : $*T
|
||||||
strong_release %4#0 : $Builtin.ObjectPointer
|
copy_addr %4 to [initialization] %0 : $*T
|
||||||
|
strong_release %3#0 : $Builtin.ObjectPointer
|
||||||
%9 = tuple ()
|
%9 = tuple ()
|
||||||
return %9 : $()
|
return %9 : $()
|
||||||
}
|
}
|
||||||
@@ -124,9 +133,10 @@ bb0(%0 : $*T, %1 : $*T):
|
|||||||
// CHECK-LABEL: sil @copy_addr2
|
// CHECK-LABEL: sil @copy_addr2
|
||||||
sil @copy_addr2 : $@thin <T> (@out T, @in T) -> () {
|
sil @copy_addr2 : $@thin <T> (@out T, @in T) -> () {
|
||||||
bb0(%0 : $*T, %1 : $*T):
|
bb0(%0 : $*T, %1 : $*T):
|
||||||
%4 = alloc_box $T // expected-note {{variable defined here}}
|
%3 = alloc_box $T
|
||||||
copy_addr %4#1 to [initialization] %0 : $*T // expected-error {{variable '<unknown>' used before being initialized}}
|
%4 = mark_uninitialized [var] %3#1 : $*T // expected-note {{variable defined here}}
|
||||||
strong_release %4#0 : $Builtin.ObjectPointer
|
copy_addr %4 to [initialization] %0 : $*T // expected-error {{variable '<unknown>' used before being initialized}}
|
||||||
|
strong_release %3#0 : $Builtin.ObjectPointer
|
||||||
%9 = tuple ()
|
%9 = tuple ()
|
||||||
return %9 : $()
|
return %9 : $()
|
||||||
}
|
}
|
||||||
@@ -138,14 +148,15 @@ sil @closure0 : $@thin (@owned Builtin.ObjectPointer, @inout Int) -> ()
|
|||||||
// CHECK-LABEL: sil @closure_test
|
// CHECK-LABEL: sil @closure_test
|
||||||
sil @closure_test : $@thin () -> () {
|
sil @closure_test : $@thin () -> () {
|
||||||
bb0:
|
bb0:
|
||||||
%0 = alloc_box $Int // expected-note {{variable defined here}}
|
%1 = alloc_box $Int
|
||||||
|
%0 = mark_uninitialized [var] %1#1 : $*Int // expected-note {{variable defined here}}
|
||||||
|
|
||||||
%5 = function_ref @takes_closure : $@thin (@callee_owned () -> ()) -> ()
|
%5 = function_ref @takes_closure : $@thin (@callee_owned () -> ()) -> ()
|
||||||
%6 = function_ref @closure0 : $@thin (@owned Builtin.ObjectPointer, @inout Int) -> ()
|
%6 = function_ref @closure0 : $@thin (@owned Builtin.ObjectPointer, @inout Int) -> ()
|
||||||
strong_retain %0#0 : $Builtin.ObjectPointer
|
strong_retain %1#0 : $Builtin.ObjectPointer
|
||||||
%8 = partial_apply %6(%0#0, %0#1) : $@thin (@owned Builtin.ObjectPointer, @inout Int) -> () // expected-error {{variable '<unknown>' captured by a closure before being initialized}}
|
%8 = partial_apply %6(%1#0, %0) : $@thin (@owned Builtin.ObjectPointer, @inout Int) -> () // expected-error {{variable '<unknown>' captured by a closure before being initialized}}
|
||||||
%9 = apply %5(%8) : $@thin (@callee_owned () -> ()) -> ()
|
%9 = apply %5(%8) : $@thin (@callee_owned () -> ()) -> ()
|
||||||
strong_release %0#0 : $Builtin.ObjectPointer
|
strong_release %1#0 : $Builtin.ObjectPointer
|
||||||
|
|
||||||
%11 = tuple ()
|
%11 = tuple ()
|
||||||
return %11 : $()
|
return %11 : $()
|
||||||
@@ -160,16 +171,17 @@ sil @getSomeClass : $@thin () -> @owned SomeClass
|
|||||||
// CHECK-LABEL: sil @assign_test_trivial
|
// CHECK-LABEL: sil @assign_test_trivial
|
||||||
sil @assign_test_trivial : $@thin (Int) -> Int {
|
sil @assign_test_trivial : $@thin (Int) -> Int {
|
||||||
bb0(%0 : $Int):
|
bb0(%0 : $Int):
|
||||||
%1 = alloc_box $Int
|
%7 = alloc_box $Int
|
||||||
|
%1 = mark_uninitialized [var] %7#1 : $*Int
|
||||||
|
|
||||||
// These assigns are a mix of init + store forms, but because Int is trivial,
|
// These assigns are a mix of init + store forms, but because Int is trivial,
|
||||||
// they all turn into stores.
|
// they all turn into stores.
|
||||||
assign %0 to %1#1 : $*Int
|
assign %0 to %1 : $*Int
|
||||||
assign %0 to %1#1 : $*Int
|
assign %0 to %1 : $*Int
|
||||||
assign %0 to %1#1 : $*Int
|
assign %0 to %1 : $*Int
|
||||||
|
|
||||||
%2 = load %1#1 : $*Int
|
%2 = load %1 : $*Int
|
||||||
strong_release %1#0 : $Builtin.ObjectPointer
|
strong_release %7#0 : $Builtin.ObjectPointer
|
||||||
|
|
||||||
return %2 : $Int
|
return %2 : $Int
|
||||||
}
|
}
|
||||||
@@ -180,26 +192,28 @@ bb0:
|
|||||||
// Assignments of nontrivial types. The first becomes an initialize (i.e.,
|
// Assignments of nontrivial types. The first becomes an initialize (i.e.,
|
||||||
// lone store), the second becomes an assignment (retain/release dance).
|
// lone store), the second becomes an assignment (retain/release dance).
|
||||||
|
|
||||||
%c = alloc_box $SomeClass
|
%b = alloc_box $SomeClass
|
||||||
|
%c = mark_uninitialized [var] %b#1 : $*SomeClass
|
||||||
|
|
||||||
%f = function_ref @getSomeClass : $@thin () -> @owned SomeClass
|
%f = function_ref @getSomeClass : $@thin () -> @owned SomeClass
|
||||||
|
|
||||||
%4 = apply %f() : $@thin () -> @owned SomeClass
|
%4 = apply %f() : $@thin () -> @owned SomeClass
|
||||||
// CHECK: [[C1:%[0-9]+]] = apply
|
// CHECK: [[C1:%[0-9]+]] = apply
|
||||||
|
|
||||||
assign %4 to %c#1 : $*SomeClass
|
assign %4 to %c : $*SomeClass
|
||||||
// CHECK-NEXT: store
|
// CHECK-NEXT: store
|
||||||
|
|
||||||
%8 = apply %f() : $@thin () -> @owned SomeClass
|
%8 = apply %f() : $@thin () -> @owned SomeClass
|
||||||
// CHECK-NEXT: [[C2:%[0-9]+]] = apply
|
// CHECK-NEXT: [[C2:%[0-9]+]] = apply
|
||||||
|
|
||||||
assign %8 to %c#1 : $*SomeClass
|
assign %8 to %c : $*SomeClass
|
||||||
// CHECK-NEXT: load
|
// CHECK-NEXT: load
|
||||||
// CHECK-NEXT: store [[C2]]
|
// CHECK-NEXT: store [[C2]]
|
||||||
// CHECK-NEXT: strong_release
|
// CHECK-NEXT: strong_release
|
||||||
|
|
||||||
destroy_addr %c#1 : $*SomeClass
|
destroy_addr %c : $*SomeClass
|
||||||
// CHECK-NEXT: destroy_addr
|
// CHECK-NEXT: destroy_addr
|
||||||
dealloc_box $SomeClass, %c#0 : $Builtin.ObjectPointer
|
dealloc_box $SomeClass, %b#0 : $Builtin.ObjectPointer
|
||||||
|
|
||||||
%11 = tuple ()
|
%11 = tuple ()
|
||||||
return %11 : $()
|
return %11 : $()
|
||||||
@@ -209,22 +223,24 @@ bb0:
|
|||||||
// CHECK-LABEL: sil @assign_test_addressonly
|
// CHECK-LABEL: sil @assign_test_addressonly
|
||||||
sil @assign_test_addressonly : $@thin <T> (@out T, @in T) -> () {
|
sil @assign_test_addressonly : $@thin <T> (@out T, @in T) -> () {
|
||||||
bb0(%0 : $*T, %1 : $*T):
|
bb0(%0 : $*T, %1 : $*T):
|
||||||
%2 = alloc_box $T
|
%b = alloc_box $T
|
||||||
|
%2 = mark_uninitialized [var] %b#1 : $*T
|
||||||
|
|
||||||
// CHECK: alloc_box
|
// CHECK: alloc_box
|
||||||
|
|
||||||
// This should become an initialization of %4
|
// This should become an initialization of %4
|
||||||
copy_addr %1 to %2#1 : $*T
|
copy_addr %1 to %2 : $*T
|
||||||
// CHECK-NEXT: copy_addr %1 to [initialization] %2#1 : $*T
|
// CHECK-NEXT: copy_addr %1 to [initialization] %2#1 : $*T
|
||||||
|
|
||||||
// This should stay an assignment of %4
|
// This should stay an assignment of %4
|
||||||
copy_addr [take] %1 to %2#1 : $*T
|
copy_addr [take] %1 to %2 : $*T
|
||||||
// CHECK-NEXT: copy_addr [take] %1 to %2#1 : $*T
|
// CHECK-NEXT: copy_addr [take] %1 to %2#1 : $*T
|
||||||
|
|
||||||
// This is a load, and shouldn't be changed.
|
// This is a load, and shouldn't be changed.
|
||||||
copy_addr %2#1 to [initialization] %0 : $*T
|
copy_addr %2 to [initialization] %0 : $*T
|
||||||
// CHECK-NEXT: copy_addr %2#1 to [initialization] %0 : $*T
|
// CHECK-NEXT: copy_addr %2#1 to [initialization] %0 : $*T
|
||||||
|
|
||||||
strong_release %2#0 : $Builtin.ObjectPointer
|
strong_release %b#0 : $Builtin.ObjectPointer
|
||||||
// CHECK-NEXT: strong_release %2#0
|
// CHECK-NEXT: strong_release %2#0
|
||||||
%9 = tuple ()
|
%9 = tuple ()
|
||||||
return %9 : $()
|
return %9 : $()
|
||||||
@@ -236,18 +252,19 @@ bb0:
|
|||||||
// Assignments of weak pointer. The first becomes an initialize, and the
|
// Assignments of weak pointer. The first becomes an initialize, and the
|
||||||
// second becomes an assignment.
|
// second becomes an assignment.
|
||||||
|
|
||||||
%c = alloc_box $@sil_weak SomeClass // expected-note {{variable defined here}}
|
%b = alloc_box $@sil_weak SomeClass
|
||||||
|
%c = mark_uninitialized [var] %b#1 : $*@sil_weak SomeClass // expected-note {{variable defined here}}
|
||||||
|
|
||||||
// Invalid load to keep the alloc_box around so we can check init semantics.
|
// Invalid load to keep the alloc_box around so we can check init semantics.
|
||||||
load %c#1 : $*@sil_weak SomeClass // expected-error {{used before being initialized}}
|
load %c : $*@sil_weak SomeClass // expected-error {{used before being initialized}}
|
||||||
|
|
||||||
%f = function_ref @getSomeClass : $@thin () -> @owned SomeClass
|
%f = function_ref @getSomeClass : $@thin () -> @owned SomeClass
|
||||||
|
|
||||||
%4 = apply %f() : $@thin () -> @owned SomeClass
|
%4 = apply %f() : $@thin () -> @owned SomeClass
|
||||||
// CHECK: [[C1:%[0-9]+]] = apply
|
// CHECK: [[C1:%[0-9]+]] = apply
|
||||||
|
|
||||||
// THis should become an initialization.
|
// This should become an initialization.
|
||||||
store_weak %4 to %c#1 : $*@sil_weak SomeClass
|
store_weak %4 to %c : $*@sil_weak SomeClass
|
||||||
// CHECK-NEXT: store_weak [[C1]] to [initialization] %0#1
|
// CHECK-NEXT: store_weak [[C1]] to [initialization] %0#1
|
||||||
|
|
||||||
strong_release %4 : $SomeClass
|
strong_release %4 : $SomeClass
|
||||||
@@ -256,13 +273,13 @@ bb0:
|
|||||||
%8 = apply %f() : $@thin () -> @owned SomeClass
|
%8 = apply %f() : $@thin () -> @owned SomeClass
|
||||||
// CHECK-NEXT: [[C2:%[0-9]+]] = apply
|
// CHECK-NEXT: [[C2:%[0-9]+]] = apply
|
||||||
|
|
||||||
store_weak %8 to %c#1 : $*@sil_weak SomeClass
|
store_weak %8 to %c : $*@sil_weak SomeClass
|
||||||
// CHECK-NEXT: store_weak [[C2]] to %0#1
|
// CHECK-NEXT: store_weak [[C2]] to %0#1
|
||||||
|
|
||||||
strong_release %8 : $SomeClass
|
strong_release %8 : $SomeClass
|
||||||
// CHECK-NEXT: release [[C2]]
|
// CHECK-NEXT: release [[C2]]
|
||||||
|
|
||||||
strong_release %c#0 : $Builtin.ObjectPointer
|
strong_release %b#0 : $Builtin.ObjectPointer
|
||||||
|
|
||||||
%11 = tuple ()
|
%11 = tuple ()
|
||||||
return %11 : $()
|
return %11 : $()
|
||||||
@@ -274,7 +291,8 @@ bb0:
|
|||||||
// Assignments of unowned pointer. The first becomes an initialize, and the
|
// Assignments of unowned pointer. The first becomes an initialize, and the
|
||||||
// second becomes an assignment.
|
// second becomes an assignment.
|
||||||
|
|
||||||
%c = alloc_box $@sil_unowned SomeClass
|
%b = alloc_box $@sil_unowned SomeClass
|
||||||
|
%c = mark_uninitialized [var] %b#1 : $*@sil_unowned SomeClass
|
||||||
|
|
||||||
%f = function_ref @getSomeClass : $@thin () -> @owned SomeClass
|
%f = function_ref @getSomeClass : $@thin () -> @owned SomeClass
|
||||||
|
|
||||||
@@ -286,7 +304,7 @@ bb0:
|
|||||||
// CHECK-NEXT: [[C1u:%[0-9]+]] = ref_to_unowned [[C1]]
|
// CHECK-NEXT: [[C1u:%[0-9]+]] = ref_to_unowned [[C1]]
|
||||||
unowned_retain %5 : $@sil_unowned SomeClass
|
unowned_retain %5 : $@sil_unowned SomeClass
|
||||||
// CHECK-NEXT: unowned_retain [[C1u]]
|
// CHECK-NEXT: unowned_retain [[C1u]]
|
||||||
assign %5 to %c#1 : $*@sil_unowned SomeClass
|
assign %5 to %c : $*@sil_unowned SomeClass
|
||||||
// CHECK-NEXT: store [[C1u]]
|
// CHECK-NEXT: store [[C1u]]
|
||||||
strong_release %4 : $SomeClass
|
strong_release %4 : $SomeClass
|
||||||
// CHECK-NEXT: strong_release [[C1]]
|
// CHECK-NEXT: strong_release [[C1]]
|
||||||
@@ -300,7 +318,7 @@ bb0:
|
|||||||
unowned_retain %9 : $@sil_unowned SomeClass
|
unowned_retain %9 : $@sil_unowned SomeClass
|
||||||
// CHECK-NEXT: unowned_retain [[C2u]]
|
// CHECK-NEXT: unowned_retain [[C2u]]
|
||||||
|
|
||||||
assign %9 to %c#1 : $*@sil_unowned SomeClass
|
assign %9 to %c : $*@sil_unowned SomeClass
|
||||||
// CHECK-NEXT: load
|
// CHECK-NEXT: load
|
||||||
// CHECK-NEXT: store
|
// CHECK-NEXT: store
|
||||||
// CHECK-NEXT: unowned_release
|
// CHECK-NEXT: unowned_release
|
||||||
@@ -308,8 +326,8 @@ bb0:
|
|||||||
strong_release %8 : $SomeClass
|
strong_release %8 : $SomeClass
|
||||||
// CHECK-NEXT: strong_release [[C2]]
|
// CHECK-NEXT: strong_release [[C2]]
|
||||||
|
|
||||||
destroy_addr %c#1 : $*@sil_unowned SomeClass
|
destroy_addr %c : $*@sil_unowned SomeClass
|
||||||
dealloc_box $SomeClass, %c#0 : $Builtin.ObjectPointer
|
dealloc_box $SomeClass, %b#0 : $Builtin.ObjectPointer
|
||||||
|
|
||||||
%11 = tuple ()
|
%11 = tuple ()
|
||||||
return %11 : $()
|
return %11 : $()
|
||||||
@@ -325,8 +343,9 @@ struct ContainsObjectPointer {
|
|||||||
sil @test_struct : $@thin (@inout ContainsObjectPointer) -> () {
|
sil @test_struct : $@thin (@inout ContainsObjectPointer) -> () {
|
||||||
bb0(%0 : $*ContainsObjectPointer):
|
bb0(%0 : $*ContainsObjectPointer):
|
||||||
%b = alloc_box $ContainsObjectPointer
|
%b = alloc_box $ContainsObjectPointer
|
||||||
|
%c = mark_uninitialized [var] %b#1 : $*ContainsObjectPointer
|
||||||
%1 = load %0 : $*ContainsObjectPointer
|
%1 = load %0 : $*ContainsObjectPointer
|
||||||
assign %1 to %b#1 : $*ContainsObjectPointer
|
assign %1 to %c : $*ContainsObjectPointer
|
||||||
|
|
||||||
strong_release %b#0 : $Builtin.ObjectPointer
|
strong_release %b#0 : $Builtin.ObjectPointer
|
||||||
%x = tuple ()
|
%x = tuple ()
|
||||||
@@ -363,7 +382,7 @@ var int_global : Int
|
|||||||
// CHECK: return
|
// CHECK: return
|
||||||
sil @test_tlc : $() -> () {
|
sil @test_tlc : $() -> () {
|
||||||
%0 = global_addr #int_global : $*Int
|
%0 = global_addr #int_global : $*Int
|
||||||
%1 = mark_uninitialized [globalvar] %0 : $*Int
|
%1 = mark_uninitialized [var] %0 : $*Int
|
||||||
|
|
||||||
%9 = tuple ()
|
%9 = tuple ()
|
||||||
return %9 : $()
|
return %9 : $()
|
||||||
@@ -386,9 +405,11 @@ sil @release_not_constructed : $@thin () -> () {
|
|||||||
bb0: // CHECK: bb0:
|
bb0: // CHECK: bb0:
|
||||||
%3 = alloc_stack $SomeClass
|
%3 = alloc_stack $SomeClass
|
||||||
// CHECK-NEXT: alloc_stack
|
// CHECK-NEXT: alloc_stack
|
||||||
|
%c = mark_uninitialized [var] %3#1 : $*SomeClass
|
||||||
|
|
||||||
|
|
||||||
// This should get removed.
|
// This should get removed.
|
||||||
destroy_addr %3#1 : $*SomeClass
|
destroy_addr %c : $*SomeClass
|
||||||
|
|
||||||
dealloc_stack %3#0 : $*@local_storage SomeClass
|
dealloc_stack %3#0 : $*@local_storage SomeClass
|
||||||
// CHECK-NEXT: dealloc_stack
|
// CHECK-NEXT: dealloc_stack
|
||||||
@@ -402,10 +423,12 @@ bb0: // CHECK: bb0:
|
|||||||
sil @release_some_constructed : $@thin () -> () {
|
sil @release_some_constructed : $@thin () -> () {
|
||||||
bb0:
|
bb0:
|
||||||
%0 = tuple ()
|
%0 = tuple ()
|
||||||
%1 = alloc_stack $(SomeClass, SomeClass)
|
%b = alloc_stack $(SomeClass, SomeClass)
|
||||||
|
%1 = mark_uninitialized [var] %b#1 : $*(SomeClass, SomeClass)
|
||||||
|
|
||||||
%2 = function_ref @getSomeClass : $@thin () -> @owned SomeClass
|
%2 = function_ref @getSomeClass : $@thin () -> @owned SomeClass
|
||||||
%3 = apply %2() : $@thin () -> @owned SomeClass
|
%3 = apply %2() : $@thin () -> @owned SomeClass
|
||||||
%4 = tuple_element_addr %1#1 : $*(SomeClass, SomeClass), 0
|
%4 = tuple_element_addr %1 : $*(SomeClass, SomeClass), 0
|
||||||
store %3 to %4 : $*SomeClass
|
store %3 to %4 : $*SomeClass
|
||||||
// CHECK: store
|
// CHECK: store
|
||||||
|
|
||||||
@@ -413,10 +436,10 @@ bb0:
|
|||||||
|
|
||||||
// CHECK-NEXT: [[ELT0:%[0-9]+]] = tuple_element_addr {{.*}}, 0
|
// CHECK-NEXT: [[ELT0:%[0-9]+]] = tuple_element_addr {{.*}}, 0
|
||||||
// CHECK-NEXT: destroy_addr [[ELT0]]
|
// CHECK-NEXT: destroy_addr [[ELT0]]
|
||||||
destroy_addr %1#1 : $*(SomeClass, SomeClass)
|
destroy_addr %1 : $*(SomeClass, SomeClass)
|
||||||
|
|
||||||
// CHECK-NEXT: dealloc_stack
|
// CHECK-NEXT: dealloc_stack
|
||||||
dealloc_stack %1#0 : $*@local_storage (SomeClass, SomeClass)
|
dealloc_stack %b#0 : $*@local_storage (SomeClass, SomeClass)
|
||||||
%8 = tuple ()
|
%8 = tuple ()
|
||||||
return %8 : $()
|
return %8 : $()
|
||||||
}
|
}
|
||||||
@@ -428,12 +451,14 @@ bb0:
|
|||||||
sil @init_existential_with_class : $@thin (@inout C) -> () {
|
sil @init_existential_with_class : $@thin (@inout C) -> () {
|
||||||
entry(%a : $*C):
|
entry(%a : $*C):
|
||||||
%p = alloc_stack $P
|
%p = alloc_stack $P
|
||||||
%q = init_existential %p#1 : $*P, $*C
|
%b = mark_uninitialized [var] %p#1 : $*P
|
||||||
|
|
||||||
|
%q = init_existential %b : $*P, $*C
|
||||||
|
|
||||||
// CHECK: copy_addr {{.*}} to [initialization] %2 : $*C
|
// CHECK: copy_addr {{.*}} to [initialization] %2 : $*C
|
||||||
copy_addr %a to [initialization] %q : $*C
|
copy_addr %a to [initialization] %q : $*C
|
||||||
%u = function_ref @use : $@thin (@in P) -> ()
|
%u = function_ref @use : $@thin (@in P) -> ()
|
||||||
%v = apply %u(%p#1) : $@thin (@in P) -> ()
|
%v = apply %u(%b) : $@thin (@in P) -> ()
|
||||||
dealloc_stack %p#0 : $*@local_storage P
|
dealloc_stack %p#0 : $*@local_storage P
|
||||||
%z = tuple ()
|
%z = tuple ()
|
||||||
return %z : $()
|
return %z : $()
|
||||||
@@ -446,7 +471,9 @@ entry(%a : $*C):
|
|||||||
// actually initialized.
|
// actually initialized.
|
||||||
sil @conditional_init : $@thin (Bool) -> () {
|
sil @conditional_init : $@thin (Bool) -> () {
|
||||||
bb0(%0 : $Bool):
|
bb0(%0 : $Bool):
|
||||||
%3 = alloc_stack $SomeClass
|
%2 = alloc_stack $SomeClass
|
||||||
|
%3 = mark_uninitialized [var] %2#1 : $*SomeClass
|
||||||
|
|
||||||
// CHECK: [[CONTROL:%[0-9]+]] = alloc_stack $Builtin.Int1
|
// CHECK: [[CONTROL:%[0-9]+]] = alloc_stack $Builtin.Int1
|
||||||
// CHECK: [[ZERO:%[0-9]+]] = integer_literal $Builtin.Int1, 0
|
// CHECK: [[ZERO:%[0-9]+]] = integer_literal $Builtin.Int1, 0
|
||||||
// CHECK: store [[ZERO]] to [[CONTROL]]#1 : $*Builtin.Int1
|
// CHECK: store [[ZERO]] to [[CONTROL]]#1 : $*Builtin.Int1
|
||||||
@@ -460,12 +487,12 @@ bb1:
|
|||||||
// CHECK: store [[ONE]] to [[CONTROL]]#1 : $*Builtin.Int1
|
// CHECK: store [[ONE]] to [[CONTROL]]#1 : $*Builtin.Int1
|
||||||
%f = function_ref @getSomeClass : $@thin () -> @owned SomeClass
|
%f = function_ref @getSomeClass : $@thin () -> @owned SomeClass
|
||||||
%6 = apply %f() : $@thin () -> @owned SomeClass
|
%6 = apply %f() : $@thin () -> @owned SomeClass
|
||||||
assign %6 to %3#1 : $*SomeClass
|
assign %6 to %3 : $*SomeClass
|
||||||
br bb2
|
br bb2
|
||||||
|
|
||||||
bb2:
|
bb2:
|
||||||
destroy_addr %3#1 : $*SomeClass
|
destroy_addr %3 : $*SomeClass
|
||||||
dealloc_stack %3#0 : $*@local_storage SomeClass
|
dealloc_stack %2#0 : $*@local_storage SomeClass
|
||||||
%14 = tuple ()
|
%14 = tuple ()
|
||||||
return %14 : $()
|
return %14 : $()
|
||||||
}
|
}
|
||||||
@@ -477,7 +504,8 @@ bb0(%0 : $Builtin.Int1):
|
|||||||
// CHECK: [[CLASSVAL:%[0-9]+]] = alloc_stack $SomeClass
|
// CHECK: [[CLASSVAL:%[0-9]+]] = alloc_stack $SomeClass
|
||||||
// CHECK: integer_literal $Builtin.Int1, 0
|
// CHECK: integer_literal $Builtin.Int1, 0
|
||||||
// CHECK: store
|
// CHECK: store
|
||||||
%6 = alloc_stack $SomeClass
|
%5 = alloc_stack $SomeClass
|
||||||
|
%6 = mark_uninitialized [var] %5#1 : $*SomeClass
|
||||||
cond_br %0, bb1, bb2
|
cond_br %0, bb1, bb2
|
||||||
|
|
||||||
bb1:
|
bb1:
|
||||||
@@ -491,7 +519,7 @@ bb1:
|
|||||||
// CHECK: integer_literal $Builtin.Int1, -1
|
// CHECK: integer_literal $Builtin.Int1, -1
|
||||||
// CHECK: store {{.*}} to [[CONTROL]]
|
// CHECK: store {{.*}} to [[CONTROL]]
|
||||||
// CHECK: store [[V1]] to [[CLASSVAL]]
|
// CHECK: store [[V1]] to [[CLASSVAL]]
|
||||||
assign %12 to %6#1 : $*SomeClass // id: %13
|
assign %12 to %6 : $*SomeClass // id: %13
|
||||||
br bb2 // id: %14
|
br bb2 // id: %14
|
||||||
|
|
||||||
bb2:
|
bb2:
|
||||||
@@ -510,9 +538,9 @@ bb2:
|
|||||||
// CHECK: bb4:
|
// CHECK: bb4:
|
||||||
// CHECK: store [[V2]] to [[CLASSVAL]]
|
// CHECK: store [[V2]] to [[CLASSVAL]]
|
||||||
|
|
||||||
assign %17 to %6#1 : $*SomeClass // id: %18
|
assign %17 to %6 : $*SomeClass // id: %18
|
||||||
destroy_addr %6#1 : $*SomeClass // id: %19
|
destroy_addr %6 : $*SomeClass // id: %19
|
||||||
dealloc_stack %6#0 : $*@local_storage SomeClass // id: %20
|
dealloc_stack %5#0 : $*@local_storage SomeClass // id: %20
|
||||||
%23 = tuple () // user: %24
|
%23 = tuple () // user: %24
|
||||||
return %23 : $() // id: %24
|
return %23 : $() // id: %24
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user