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){
|
||||
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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 : $()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user