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:
Joe Groff
2014-01-31 22:50:21 +00:00
parent a610743064
commit 71379f5bad
14 changed files with 236 additions and 145 deletions

View File

@@ -279,9 +279,9 @@ public:
MarkUninitializedInst::Kind k){
return insert(new (F.getModule()) MarkUninitializedInst(loc, src, k));
}
MarkUninitializedInst *createMarkUninitializedGlobalVar(SILLocation loc,
SILValue src) {
return createMarkUninitialized(loc, src, MarkUninitializedInst::GlobalVar);
MarkUninitializedInst *createMarkUninitializedVar(SILLocation loc,
SILValue src) {
return createMarkUninitialized(loc, src, MarkUninitializedInst::Var);
}
MarkUninitializedInst *createMarkUninitializedRootSelf(SILLocation loc,
SILValue src) {

View File

@@ -818,9 +818,8 @@ class MarkUninitializedInst
public:
// This enum captures what the mark_uninitialized instruction is designating.
enum Kind {
/// GlobalVar designates the start of a global variable live range in a
/// top level code declaration.
GlobalVar,
/// Var designates the start of a normal variable live range.
Var,
/// RootSelf designates "self" in a struct, enum, or root class.
RootSelf,
@@ -846,7 +845,7 @@ public:
Kind getKind() const { return ThisKind; }
bool isGlobalVar() const { return ThisKind == GlobalVar; }
bool isVar() const { return ThisKind == Var; }
bool isRootSelf() const {
return ThisKind == RootSelf;
}

View File

@@ -1471,17 +1471,23 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB) {
}
case ValueKind::MarkUninitializedInst: {
if (P.parseToken(tok::l_square, diag::expected_tok_in_sil_instr, "["))
return true;
Identifier KindId;
SourceLoc KindLoc;
if (P.parseToken(tok::l_square, diag::expected_tok_in_sil_instr, "[") ||
P.parseIdentifier(KindId, KindLoc, diag::expected_tok_in_sil_instr,
"globalvar or rootself") ||
P.parseToken(tok::r_square, diag::expected_tok_in_sil_instr, "]"))
SourceLoc KindLoc = P.Tok.getLoc();
if (P.consumeIf(tok::kw_var))
KindId = P.Context.getIdentifier("var");
else if (P.parseIdentifier(KindId, KindLoc,
diag::expected_tok_in_sil_instr, "kind"))
return true;
if (P.parseToken(tok::r_square, diag::expected_tok_in_sil_instr, "]"))
return true;
MarkUninitializedInst::Kind Kind;
if (KindId.str() == "globalvar")
Kind = MarkUninitializedInst::GlobalVar;
if (KindId.str() == "var")
Kind = MarkUninitializedInst::Var;
else if (KindId.str() == "rootself")
Kind = MarkUninitializedInst::RootSelf;
else if (KindId.str() == "derivedself")
@@ -1492,7 +1498,7 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB) {
Kind = MarkUninitializedInst::DelegatingSelf;
else {
P.diagnose(KindLoc, diag::expected_tok_in_sil_instr,
"globalvar, rootself, derivedself, derivedselfonly, "
"var, rootself, derivedself, derivedselfonly, "
"or delegatingself");
return true;
}

View File

@@ -619,7 +619,7 @@ public:
void visitMarkUninitializedInst(MarkUninitializedInst *MU) {
OS << "mark_uninitialized ";
switch (MU->getKind()) {
case MarkUninitializedInst::GlobalVar: OS << "[globalvar] "; break;
case MarkUninitializedInst::Var: OS << "[var] "; break;
case MarkUninitializedInst::RootSelf: OS << "[rootself] "; break;
case MarkUninitializedInst::DerivedSelf: OS << "[derivedself] "; break;
case MarkUninitializedInst::DerivedSelfOnly:

View File

@@ -512,6 +512,60 @@ struct InitializationForPattern
#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
InitializationPtr
@@ -528,9 +582,6 @@ SILGenFunction::emitInitializationForVarDecl(VarDecl *vd, bool isArgument,
SILValue addr = B.createGlobalAddr(vd, vd,
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);
return InitializationPtr(new GlobalInitialization(addr));
}
@@ -543,9 +594,8 @@ SILGenFunction::emitInitializationForVarDecl(VarDecl *vd, bool isArgument,
// 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.
if (vd->isLet()) {
if (vd->isLet())
return InitializationPtr(new LetValueInitialization(vd, isArgument, *this));
}
// Otherwise, we have a normal local-variable initialization.
auto varInit = emitLocalVariableWithCleanup(vd);
@@ -569,12 +619,13 @@ void SILGenFunction::visitPatternBindingDecl(PatternBindingDecl *D) {
.visit(D->getPattern());
// 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()) {
FullExpr Scope(Cleanups, CleanupLocation(D->getInit()));
emitExprInto(D->getInit(), initialization.get());
} else {
initialization->finishInitialization(*this);
MarkPatternUninitialized(*this).visit(D->getPattern());
}
}

View File

@@ -190,11 +190,16 @@ static bool checkAllocBoxUses(AllocBoxInst *ABI, SILValue V,
auto *User = UI->getUser();
// These instructions do not cause the box's address to escape.
if (!useCaptured(UI)) {
if (!useCaptured(UI)) {
Users.push_back(User);
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
// a way that escapes. Recursively check that the uses of the instruction
// don't escape and collect all of the uses of the value.

View File

@@ -593,18 +593,17 @@ examineAllocBoxInst(AllocBoxInst *ABI, ReachabilityInfo &RI,
PartialApplyIndexMap &IM) {
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())) {
unsigned OpNo = O->getOperandNumber();
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
// 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))
return false;
// Verify that the next operand of the partial apply is the second result
// of the alloc_box.
if (OpNo + 1 >= PAI->getNumOperands() ||
PAI->getOperand(OpNo + 1) != SILValue(ABI, 1))
// Verify that the previous operand of the partial apply is the refcount
// result of the alloc_box.
if (PAI->getOperand(OpNo - 1) != SILValue(ABI, 0))
return false;
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
// box pointer (the captured address is always the immediately following
// 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
// it does, then conservatively refuse to promote any captures of this
@@ -722,9 +720,12 @@ processPartialApplyInst(PartialApplyInst *PAI, IndicesSet &PromotableIndices,
if (PromotableIndices.count(Index)) {
SILValue BoxValue = PAI->getOperand(OpNo);
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 &&
AddrValue.getResultNumber() == 1);
UnderlyingAddrValue.getResultNumber() == 1);
// Emit a strong release, zapping a retain if we can.
B.emitStrongRelease(PAI->getLoc(), BoxValue);

View File

@@ -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
// want to process the stored members of the struct/class elementwise.
if (MUI->getKind() != MarkUninitializedInst::GlobalVar) {
if (!isEnumSelf() && !isDelegatingInit())
IsSelfOfNonDelegatingInitializer = true;
}
if (isAnyInitSelf() && !isEnumSelf() && !isDelegatingInit())
IsSelfOfNonDelegatingInitializer = true;
}
// Compute the number of elements to track in this memory object.
@@ -807,7 +805,8 @@ void ElementUseCollector::collectClassSelfUses() {
assert(MUI->hasOneUse());
auto *TheStore = cast<StoreInst>((*MUI->use_begin())->getUser());
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
// box to find any accesses to it. The possible uses are one of:
@@ -959,7 +958,8 @@ void ElementUseCollector::collectDelegatingClassInitSelfUses() {
assert(MUI->hasOneUse());
auto *TheStore = cast<StoreInst>((*MUI->use_begin())->getUser());
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
// box to find any accesses to it. The possible uses are one of:

View File

@@ -91,7 +91,7 @@ public:
/// isAnyInitSelf - Return true if this is 'self' in any kind of initializer.
bool isAnyInitSelf() const {
if (auto *MUI = dyn_cast<MarkUninitializedInst>(MemoryInst))
return !MUI->isGlobalVar();
return !MUI->isVar();
return false;
}

View File

@@ -1484,13 +1484,6 @@ bool LifetimeChecker::isInitializedAtUse(const DIMemoryUse &Use,
//===----------------------------------------------------------------------===//
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");
DIMemoryObjectInfo MemInfo(I);
@@ -1514,8 +1507,7 @@ static void checkDefiniteInitialization(SILFunction &Fn) {
for (auto &BB : Fn) {
for (auto I = BB.begin(), E = BB.end(); I != E; ++I) {
SILInstruction *Inst = I;
if (isa<AllocBoxInst>(Inst) || isa<AllocStackInst>(Inst) ||
isa<MarkUninitializedInst>(Inst))
if (isa<MarkUninitializedInst>(Inst))
processMemoryObject(Inst);
}
}

View File

@@ -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
// allocation.
if (UI->getOperandNumber() == 0)
return dyn_cast<AllocStackInst>(CAI->getDest());
return dyn_cast<AllocStackInst>(CAI->getSrc());
SILValue OtherOp =
UI->getOperandNumber() == 0 ? CAI->getDest() : CAI->getSrc();
// Look through mark_uninitialized.
if (auto *MUI = dyn_cast<MarkUninitializedInst>(OtherOp))
OtherOp = MUI->getOperand();
return dyn_cast<AllocStackInst>(OtherOp);
}

View File

@@ -60,6 +60,10 @@ swift::isInstructionTriviallyDead(SILInstruction *I) {
if (!ILI->getValue())
return true;
// mark_uninitialized is never dead.
if (isa<MarkUninitializedInst>(I))
return false;
if (!I->mayHaveSideEffects())
return true;

View File

@@ -1005,7 +1005,7 @@ var staticProp: Int = 0
sil private @globalinit_func0 : $@thin () -> () {
bb0:
%0 = global_addr #staticProp : $*Int
%1 = mark_uninitialized [globalvar] %0 : $*Int
%1 = mark_uninitialized [var] %0 : $*Int
%7 = tuple ()
return %7 : $()
}

View File

@@ -12,9 +12,10 @@ sil @makesInt : $@thin () -> Int
// CHECK-LABEL: sil @use_before_init
sil @use_before_init : $@thin () -> Int {
bb0:
%1 = alloc_box $Int // expected-note {{variable defined here}}
%4 = load %1#1 : $*Int // expected-error {{variable '<unknown>' used before being initialized}}
strong_release %1#0 : $Builtin.ObjectPointer
%0 = alloc_box $Int
%1 = mark_uninitialized [var] %0#1 : $*Int // expected-note {{variable defined here}}
%4 = load %1 : $*Int // expected-error {{variable '<unknown>' used before being initialized}}
strong_release %0#0 : $Builtin.ObjectPointer
%9 = return %4 : $Int
}
@@ -22,14 +23,15 @@ bb0:
// CHECK-LABEL: @inout_uninit
sil @inout_uninit : $@thin () -> () {
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) -> ()
%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 ()
strong_release %1#0 : $Builtin.ObjectPointer
return %0 : $()
%t = tuple ()
strong_release %0#0 : $Builtin.ObjectPointer
return %t : $()
}
@@ -45,14 +47,16 @@ bb0:
// CHECK-LABEL: sil @used_by_inout
sil @used_by_inout : $@thin (Int) -> (Int, Int) {
bb0(%0 : $Int):
%1 = alloc_box $Int
%2 = store %0 to %1#1 : $*Int
%3 = load %1#1 : $*Int
%91 = alloc_box $Int
%1 = mark_uninitialized [var] %91#1 : $*Int
%2 = store %0 to %1 : $*Int
%3 = load %1 : $*Int
%5 = function_ref @takes_Int_inout : $@thin (@inout Int) -> ()
%6 = apply %5(%1#1) : $@thin (@inout Int) -> ()
%7 = load %1#1 : $*Int
%6 = apply %5(%1) : $@thin (@inout Int) -> ()
%7 = load %1 : $*Int
%8 = tuple (%3 : $Int, %7 : $Int)
strong_release %1#0 : $Builtin.ObjectPointer
strong_release %91#0 : $Builtin.ObjectPointer
%11 = return %8 : $(Int, Int)
}
@@ -69,12 +73,14 @@ sil @returns_generic_struct : $@thin (@out AddressOnlyStruct) -> ()
// CHECK-LABEL: sil @call_struct_return_function
sil @call_struct_return_function : $@thin () -> Int {
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) -> ()
%3 = apply %2(%1#1) : $@thin (@out AddressOnlyStruct) -> ()
%4 = struct_element_addr %1#1 : $*AddressOnlyStruct, #b
%3 = apply %2(%1) : $@thin (@out AddressOnlyStruct) -> ()
%4 = struct_element_addr %1 : $*AddressOnlyStruct, #b
%5 = load %4 : $*Int
strong_release %1#0 : $Builtin.ObjectPointer
strong_release %0#0 : $Builtin.ObjectPointer
return %5 : $Int
}
@@ -82,14 +88,15 @@ bb0:
// CHECK-LABEL: sil @tuple_elements1
sil @tuple_elements1 : $@thin (Int) -> () {
bb0(%0 : $Int):
%3 = alloc_box $(Int, Int) // expected-note 1 {{variable defined here}}
%4 = tuple_element_addr %3#1 : $*(Int, Int), 0
%5 = tuple_element_addr %3#1 : $*(Int, Int), 1
%2 = alloc_box $(Int, Int)
%3 = mark_uninitialized [var] %2#1 : $*(Int, Int) // expected-note {{variable defined here}}
%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) -> ()
%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}}
strong_release %3#0 : $Builtin.ObjectPointer
strong_release %2#0 : $Builtin.ObjectPointer
%99 = tuple ()
return %99 : $()
}
@@ -97,14 +104,15 @@ bb0(%0 : $Int):
// CHECK-LABEL: sil @tuple_elements2
sil @tuple_elements2 : $@thin (Int) -> (Int, Int) {
bb0(%0 : $Int):
%3 = alloc_box $(Int, Int) // expected-note {{variable defined here}}
%18 = tuple_element_addr %3#1 : $*(Int, Int), 0
%2 = alloc_box $(Int, Int)
%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
%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
%22 = tuple_extract %20 : $(Int, Int), 1
%23 = tuple (%21 : $Int, %22 : $Int)
strong_release %3#0 : $Builtin.ObjectPointer
strong_release %2#0 : $Builtin.ObjectPointer
return %23 : $(Int, Int)
}
@@ -113,10 +121,11 @@ bb0(%0 : $Int):
// CHECK-LABEL: sil @copy_addr1
sil @copy_addr1 : $@thin <T> (@out T, @in T) -> () {
bb0(%0 : $*T, %1 : $*T):
%4 = alloc_box $T
copy_addr [take] %1 to [initialization] %4#1 : $*T
copy_addr %4#1 to [initialization] %0 : $*T
strong_release %4#0 : $Builtin.ObjectPointer
%3 = alloc_box $T
%4 = mark_uninitialized [var] %3#1 : $*T
copy_addr [take] %1 to [initialization] %4 : $*T
copy_addr %4 to [initialization] %0 : $*T
strong_release %3#0 : $Builtin.ObjectPointer
%9 = tuple ()
return %9 : $()
}
@@ -124,9 +133,10 @@ bb0(%0 : $*T, %1 : $*T):
// CHECK-LABEL: sil @copy_addr2
sil @copy_addr2 : $@thin <T> (@out T, @in T) -> () {
bb0(%0 : $*T, %1 : $*T):
%4 = alloc_box $T // expected-note {{variable defined here}}
copy_addr %4#1 to [initialization] %0 : $*T // expected-error {{variable '<unknown>' used before being initialized}}
strong_release %4#0 : $Builtin.ObjectPointer
%3 = alloc_box $T
%4 = mark_uninitialized [var] %3#1 : $*T // expected-note {{variable defined here}}
copy_addr %4 to [initialization] %0 : $*T // expected-error {{variable '<unknown>' used before being initialized}}
strong_release %3#0 : $Builtin.ObjectPointer
%9 = tuple ()
return %9 : $()
}
@@ -138,14 +148,15 @@ sil @closure0 : $@thin (@owned Builtin.ObjectPointer, @inout Int) -> ()
// CHECK-LABEL: sil @closure_test
sil @closure_test : $@thin () -> () {
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 () -> ()) -> ()
%6 = function_ref @closure0 : $@thin (@owned Builtin.ObjectPointer, @inout Int) -> ()
strong_retain %0#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}}
strong_retain %1#0 : $Builtin.ObjectPointer
%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 () -> ()) -> ()
strong_release %0#0 : $Builtin.ObjectPointer
strong_release %1#0 : $Builtin.ObjectPointer
%11 = tuple ()
return %11 : $()
@@ -160,16 +171,17 @@ sil @getSomeClass : $@thin () -> @owned SomeClass
// CHECK-LABEL: sil @assign_test_trivial
sil @assign_test_trivial : $@thin (Int) -> 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,
// they all turn into stores.
assign %0 to %1#1 : $*Int
assign %0 to %1#1 : $*Int
assign %0 to %1#1 : $*Int
assign %0 to %1 : $*Int
assign %0 to %1 : $*Int
assign %0 to %1 : $*Int
%2 = load %1#1 : $*Int
strong_release %1#0 : $Builtin.ObjectPointer
%2 = load %1 : $*Int
strong_release %7#0 : $Builtin.ObjectPointer
return %2 : $Int
}
@@ -180,26 +192,28 @@ bb0:
// Assignments of nontrivial types. The first becomes an initialize (i.e.,
// 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
%4 = apply %f() : $@thin () -> @owned SomeClass
// CHECK: [[C1:%[0-9]+]] = apply
assign %4 to %c#1 : $*SomeClass
assign %4 to %c : $*SomeClass
// CHECK-NEXT: store
%8 = apply %f() : $@thin () -> @owned SomeClass
// CHECK-NEXT: [[C2:%[0-9]+]] = apply
assign %8 to %c#1 : $*SomeClass
assign %8 to %c : $*SomeClass
// CHECK-NEXT: load
// CHECK-NEXT: store [[C2]]
// CHECK-NEXT: strong_release
destroy_addr %c#1 : $*SomeClass
destroy_addr %c : $*SomeClass
// CHECK-NEXT: destroy_addr
dealloc_box $SomeClass, %c#0 : $Builtin.ObjectPointer
dealloc_box $SomeClass, %b#0 : $Builtin.ObjectPointer
%11 = tuple ()
return %11 : $()
@@ -209,22 +223,24 @@ bb0:
// CHECK-LABEL: sil @assign_test_addressonly
sil @assign_test_addressonly : $@thin <T> (@out T, @in T) -> () {
bb0(%0 : $*T, %1 : $*T):
%2 = alloc_box $T
%b = alloc_box $T
%2 = mark_uninitialized [var] %b#1 : $*T
// CHECK: alloc_box
// 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
// 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
// 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
strong_release %2#0 : $Builtin.ObjectPointer
strong_release %b#0 : $Builtin.ObjectPointer
// CHECK-NEXT: strong_release %2#0
%9 = tuple ()
return %9 : $()
@@ -236,18 +252,19 @@ bb0:
// Assignments of weak pointer. The first becomes an initialize, and the
// 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.
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
%4 = apply %f() : $@thin () -> @owned SomeClass
// CHECK: [[C1:%[0-9]+]] = apply
// THis should become an initialization.
store_weak %4 to %c#1 : $*@sil_weak SomeClass
// This should become an initialization.
store_weak %4 to %c : $*@sil_weak SomeClass
// CHECK-NEXT: store_weak [[C1]] to [initialization] %0#1
strong_release %4 : $SomeClass
@@ -256,13 +273,13 @@ bb0:
%8 = apply %f() : $@thin () -> @owned SomeClass
// 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
strong_release %8 : $SomeClass
// CHECK-NEXT: release [[C2]]
strong_release %c#0 : $Builtin.ObjectPointer
strong_release %b#0 : $Builtin.ObjectPointer
%11 = tuple ()
return %11 : $()
@@ -274,7 +291,8 @@ bb0:
// Assignments of unowned pointer. The first becomes an initialize, and the
// 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
@@ -286,7 +304,7 @@ bb0:
// CHECK-NEXT: [[C1u:%[0-9]+]] = ref_to_unowned [[C1]]
unowned_retain %5 : $@sil_unowned SomeClass
// CHECK-NEXT: unowned_retain [[C1u]]
assign %5 to %c#1 : $*@sil_unowned SomeClass
assign %5 to %c : $*@sil_unowned SomeClass
// CHECK-NEXT: store [[C1u]]
strong_release %4 : $SomeClass
// CHECK-NEXT: strong_release [[C1]]
@@ -300,7 +318,7 @@ bb0:
unowned_retain %9 : $@sil_unowned SomeClass
// 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: store
// CHECK-NEXT: unowned_release
@@ -308,8 +326,8 @@ bb0:
strong_release %8 : $SomeClass
// CHECK-NEXT: strong_release [[C2]]
destroy_addr %c#1 : $*@sil_unowned SomeClass
dealloc_box $SomeClass, %c#0 : $Builtin.ObjectPointer
destroy_addr %c : $*@sil_unowned SomeClass
dealloc_box $SomeClass, %b#0 : $Builtin.ObjectPointer
%11 = tuple ()
return %11 : $()
@@ -325,8 +343,9 @@ struct ContainsObjectPointer {
sil @test_struct : $@thin (@inout ContainsObjectPointer) -> () {
bb0(%0 : $*ContainsObjectPointer):
%b = alloc_box $ContainsObjectPointer
%c = mark_uninitialized [var] %b#1 : $*ContainsObjectPointer
%1 = load %0 : $*ContainsObjectPointer
assign %1 to %b#1 : $*ContainsObjectPointer
assign %1 to %c : $*ContainsObjectPointer
strong_release %b#0 : $Builtin.ObjectPointer
%x = tuple ()
@@ -363,7 +382,7 @@ var int_global : Int
// CHECK: return
sil @test_tlc : $() -> () {
%0 = global_addr #int_global : $*Int
%1 = mark_uninitialized [globalvar] %0 : $*Int
%1 = mark_uninitialized [var] %0 : $*Int
%9 = tuple ()
return %9 : $()
@@ -386,9 +405,11 @@ sil @release_not_constructed : $@thin () -> () {
bb0: // CHECK: bb0:
%3 = alloc_stack $SomeClass
// CHECK-NEXT: alloc_stack
%c = mark_uninitialized [var] %3#1 : $*SomeClass
// This should get removed.
destroy_addr %3#1 : $*SomeClass
destroy_addr %c : $*SomeClass
dealloc_stack %3#0 : $*@local_storage SomeClass
// CHECK-NEXT: dealloc_stack
@@ -402,10 +423,12 @@ bb0: // CHECK: bb0:
sil @release_some_constructed : $@thin () -> () {
bb0:
%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
%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
// CHECK: store
@@ -413,10 +436,10 @@ bb0:
// CHECK-NEXT: [[ELT0:%[0-9]+]] = tuple_element_addr {{.*}}, 0
// CHECK-NEXT: destroy_addr [[ELT0]]
destroy_addr %1#1 : $*(SomeClass, SomeClass)
destroy_addr %1 : $*(SomeClass, SomeClass)
// CHECK-NEXT: dealloc_stack
dealloc_stack %1#0 : $*@local_storage (SomeClass, SomeClass)
dealloc_stack %b#0 : $*@local_storage (SomeClass, SomeClass)
%8 = tuple ()
return %8 : $()
}
@@ -428,12 +451,14 @@ bb0:
sil @init_existential_with_class : $@thin (@inout C) -> () {
entry(%a : $*C):
%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
copy_addr %a to [initialization] %q : $*C
%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
%z = tuple ()
return %z : $()
@@ -446,7 +471,9 @@ entry(%a : $*C):
// actually initialized.
sil @conditional_init : $@thin (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: [[ZERO:%[0-9]+]] = integer_literal $Builtin.Int1, 0
// CHECK: store [[ZERO]] to [[CONTROL]]#1 : $*Builtin.Int1
@@ -460,12 +487,12 @@ bb1:
// CHECK: store [[ONE]] to [[CONTROL]]#1 : $*Builtin.Int1
%f = function_ref @getSomeClass : $@thin () -> @owned SomeClass
%6 = apply %f() : $@thin () -> @owned SomeClass
assign %6 to %3#1 : $*SomeClass
assign %6 to %3 : $*SomeClass
br bb2
bb2:
destroy_addr %3#1 : $*SomeClass
dealloc_stack %3#0 : $*@local_storage SomeClass
destroy_addr %3 : $*SomeClass
dealloc_stack %2#0 : $*@local_storage SomeClass
%14 = tuple ()
return %14 : $()
}
@@ -477,7 +504,8 @@ bb0(%0 : $Builtin.Int1):
// CHECK: [[CLASSVAL:%[0-9]+]] = alloc_stack $SomeClass
// CHECK: integer_literal $Builtin.Int1, 0
// CHECK: store
%6 = alloc_stack $SomeClass
%5 = alloc_stack $SomeClass
%6 = mark_uninitialized [var] %5#1 : $*SomeClass
cond_br %0, bb1, bb2
bb1:
@@ -491,7 +519,7 @@ bb1:
// CHECK: integer_literal $Builtin.Int1, -1
// CHECK: store {{.*}} to [[CONTROL]]
// CHECK: store [[V1]] to [[CLASSVAL]]
assign %12 to %6#1 : $*SomeClass // id: %13
assign %12 to %6 : $*SomeClass // id: %13
br bb2 // id: %14
bb2:
@@ -510,9 +538,9 @@ bb2:
// CHECK: bb4:
// CHECK: store [[V2]] to [[CLASSVAL]]
assign %17 to %6#1 : $*SomeClass // id: %18
destroy_addr %6#1 : $*SomeClass // id: %19
dealloc_stack %6#0 : $*@local_storage SomeClass // id: %20
assign %17 to %6 : $*SomeClass // id: %18
destroy_addr %6 : $*SomeClass // id: %19
dealloc_stack %5#0 : $*@local_storage SomeClass // id: %20
%23 = tuple () // user: %24
return %23 : $() // id: %24
}