Skip to content

Commit 97ec127

Browse files
committed
Lower and emit debug_value [poison].
OwnershipEliminator lowers destroy_value [poison] to debug_value [poison]. IRGen overwrites all references in shadow copies with a sentinel value in place of debug_value [poison]. Part 2/2: rdar://75012368 (-Onone compiler support for early object deinitialization with sentinel dead references)
1 parent 6ed58c6 commit 97ec127

File tree

9 files changed

+249
-60
lines changed

9 files changed

+249
-60
lines changed

include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -263,11 +263,11 @@ class CanonicalizeOSSALifetime {
263263

264264
/// remnantLiveOutBlocks are part of the original extended lifetime that are
265265
/// not in canonical pruned liveness. There is a path from a PrunedLiveness
266-
/// boundary to an original destroy that passes through this block.
266+
/// boundary to an original destroy that passes through a remnant block.
267267
///
268268
/// These blocks would be equivalent to PrunedLiveness::LiveOut if
269269
/// PrunedLiveness were recomputed using all original destroys as interesting
270-
/// uses, minus blocks already marked PrunedLiveness::LiveOut. (These blocks
270+
/// uses, minus blocks already marked PrunedLiveness::LiveOut. (Remnant blocks
271271
/// may be in PrunedLiveness::LiveWithin).
272272
SmallSetVector<SILBasicBlock *, 8> remnantLiveOutBlocks;
273273

lib/IRGen/GenExistential.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -1958,6 +1958,14 @@ void irgen::emitMetatypeOfMetatype(IRGenFunction &IGF, Explosion &value,
19581958
out.add(tablesAndValue.first);
19591959
}
19601960

1961+
Address
1962+
irgen::emitClassExistentialValueAddress(IRGenFunction &IGF, Address existential,
1963+
SILType baseTy) {
1964+
assert(baseTy.isClassExistentialType());
1965+
auto &baseTI = IGF.getTypeInfo(baseTy).as<ClassExistentialTypeInfo>();
1966+
return baseTI.projectValue(IGF, existential);
1967+
}
1968+
19611969
/// Extract the instance pointer from a class existential value.
19621970
llvm::Value *
19631971
irgen::emitClassExistentialProjection(IRGenFunction &IGF,

lib/IRGen/GenExistential.h

+5
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ namespace irgen {
9999
IRGenFunction &IGF, OpenedExistentialAccess accessKind, Address base,
100100
SILType existentialType, CanArchetypeType openedArchetype);
101101

102+
/// Return the address of the reference values within a class existential.
103+
Address emitClassExistentialValueAddress(IRGenFunction &IGF,
104+
Address existential,
105+
SILType baseTy);
106+
102107
/// Extract the instance pointer from a class existential value.
103108
///
104109
/// \param openedArchetype If non-null, the archetype that will capture the

lib/IRGen/IRGenSIL.cpp

+176-37
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,7 @@ class IRGenSILFunction :
794794
#ifndef NDEBUG
795795
/// Check if \p Val can be stored into \p Alloca, and emit some diagnostic
796796
/// info if it can't.
797-
bool canAllocaStoreValue(Address Alloca, llvm::Value *Val,
797+
bool canAllocaStoreValue(llvm::Value *Alloca, llvm::Value *Val,
798798
SILDebugVariable VarInfo,
799799
const SILDebugScope *Scope) {
800800
bool canStore =
@@ -804,7 +804,7 @@ class IRGenSILFunction :
804804
return true;
805805
llvm::errs() << "Invalid shadow copy:\n"
806806
<< " Value : " << *Val << "\n"
807-
<< " Alloca: " << *Alloca.getAddress() << "\n"
807+
<< " Alloca: " << *Alloca << "\n"
808808
<< "---\n"
809809
<< "Previous shadow copy into " << VarInfo.Name
810810
<< " in the same scope!\n"
@@ -889,13 +889,21 @@ class IRGenSILFunction :
889889
return Addr;
890890
}
891891

892+
// This returns shadow alloca when \p init is false or the shadowed value
893+
// derived from that alloca with \p init is true.
892894
llvm::Value *emitTaskAllocShadowCopy(llvm::Value *Storage,
893-
const SILDebugScope *Scope) {
895+
const SILDebugScope *Scope,
896+
bool Init) {
894897
auto getRec = [&](llvm::Instruction *Orig) {
898+
llvm::Value *Inner =
899+
emitTaskAllocShadowCopy(Orig->getOperand(0), Scope, Init);
900+
if (!Init)
901+
return Inner;
902+
895903
llvm::Instruction *Cloned = Orig->clone();
896-
Cloned->setOperand(0, emitTaskAllocShadowCopy(Orig->getOperand(0), Scope));
904+
Cloned->setOperand(0, Inner);
897905
Cloned->insertBefore(Orig);
898-
return Cloned;
906+
return static_cast<llvm::Value *>(Cloned);
899907
};
900908
if (auto *LdInst = dyn_cast<llvm::LoadInst>(Storage))
901909
return getRec(LdInst);
@@ -909,11 +917,13 @@ class IRGenSILFunction :
909917
auto &Alloca = TaskAllocStackSlots[CallInst];
910918
if (!Alloca.isValid())
911919
Alloca = createAlloca(Storage->getType(), Align, "taskalloc.debug");
912-
zeroInit(cast<llvm::AllocaInst>(Alloca.getAddress()));
913-
ArtificialLocation AutoRestore(Scope, IGM.DebugInfo.get(), Builder);
914-
auto *Store =
920+
if (Init) {
921+
zeroInit(cast<llvm::AllocaInst>(Alloca.getAddress()));
922+
ArtificialLocation AutoRestore(Scope, IGM.DebugInfo.get(), Builder);
923+
auto *Store =
915924
Builder.CreateStore(Storage, Alloca.getAddress(), Align);
916-
Store->moveAfter(CallInst);
925+
Store->moveAfter(CallInst);
926+
}
917927
return Alloca.getAddress();
918928
}
919929
return Storage;
@@ -922,23 +932,39 @@ class IRGenSILFunction :
922932
/// Unconditionally emit a stack shadow copy of an \c llvm::Value.
923933
llvm::Value *emitShadowCopy(llvm::Value *Storage, const SILDebugScope *Scope,
924934
SILDebugVariable VarInfo,
925-
llvm::Optional<Alignment> _Align) {
935+
llvm::Optional<Alignment> _Align, bool Init) {
926936
if (CurSILFn->isAsync())
927937
if (isTaskAlloc(Storage))
928-
return emitTaskAllocShadowCopy(Storage, Scope);
938+
return emitTaskAllocShadowCopy(Storage, Scope, Init);
939+
929940
auto Align = _Align.getValueOr(IGM.getPointerAlignment());
930941
unsigned ArgNo = VarInfo.ArgNo;
931942
auto &Alloca = ShadowStackSlots[{ArgNo, {Scope, VarInfo.Name}}];
932943
if (!Alloca.isValid())
933944
Alloca = createAlloca(Storage->getType(), Align, VarInfo.Name + ".debug");
934-
zeroInit(cast<llvm::AllocaInst>(Alloca.getAddress()));
935-
assert(canAllocaStoreValue(Alloca, Storage, VarInfo, Scope) &&
945+
assert(canAllocaStoreValue(Alloca.getAddress(), Storage, VarInfo, Scope) &&
936946
"bad scope?");
937-
ArtificialLocation AutoRestore(Scope, IGM.DebugInfo.get(), Builder);
938-
Builder.CreateStore(Storage, Alloca.getAddress(), Align);
947+
if (Init) {
948+
zeroInit(cast<llvm::AllocaInst>(Alloca.getAddress()));
949+
ArtificialLocation AutoRestore(Scope, IGM.DebugInfo.get(), Builder);
950+
Builder.CreateStore(Storage, Alloca.getAddress(), Align);
951+
}
939952
return Alloca.getAddress();
940953
}
941954

955+
bool shouldShadowVariable(SILDebugVariable varInfo, bool isAnonymous) {
956+
return !IGM.IRGen.Opts.DisableDebuggerShadowCopies
957+
&& !IGM.IRGen.Opts.shouldOptimize()
958+
&& !isAnonymous
959+
&& !CurSILFn->isAsync();
960+
}
961+
962+
bool shouldShadowStorage(llvm::Value *Storage) {
963+
return !isa<llvm::AllocaInst>(Storage)
964+
&& !isa<llvm::UndefValue>(Storage)
965+
&& needsShadowCopy(Storage);
966+
}
967+
942968
/// At -Onone, emit a shadow copy of an Address in an alloca, so the
943969
/// register allocator doesn't elide the dbg.value intrinsic when
944970
/// register pressure is high. There is a trade-off to this: With
@@ -961,14 +987,14 @@ class IRGenSILFunction :
961987
if (emitLifetimeExtendingUse(Value))
962988
if (ValueVariables.insert(Value).second)
963989
ValueDomPoints.push_back({Value, getActiveDominancePoint()});
964-
if (IGM.IRGen.Opts.DisableDebuggerShadowCopies ||
965-
IGM.IRGen.Opts.shouldOptimize() || IsAnonymous || CurSILFn->isAsync() ||
966-
isa<llvm::AllocaInst>(Storage) || isa<llvm::UndefValue>(Storage) ||
967-
!needsShadowCopy(Storage))
990+
// This condition must be consistent with emitPoisonDebugValueInst to avoid
991+
// generating extra shadow copies for debug_value [poison].
992+
if (!shouldShadowVariable(VarInfo, IsAnonymous)
993+
|| !shouldShadowStorage(Storage)) {
968994
return Storage;
969-
995+
}
970996
// Emit a shadow copy.
971-
return emitShadowCopy(Storage, Scope, VarInfo, Align);
997+
return emitShadowCopy(Storage, Scope, VarInfo, Align, true);
972998
}
973999

9741000
/// Like \c emitShadowCopyIfNeeded() but takes an \c Address instead of an
@@ -988,9 +1014,7 @@ class IRGenSILFunction :
9881014
Explosion e = getLoweredExplosion(SILVal);
9891015

9901016
// Only do this at -O0.
991-
if (IGM.IRGen.Opts.DisableDebuggerShadowCopies ||
992-
IGM.IRGen.Opts.shouldOptimize() || IsAnonymous ||
993-
(CurSILFn->isAsync())) {
1017+
if (!shouldShadowVariable(VarInfo, IsAnonymous)) {
9941018
auto vals = e.claimAll();
9951019
copy.append(vals.begin(), vals.end());
9961020

@@ -1006,21 +1030,29 @@ class IRGenSILFunction :
10061030
}
10071031

10081032
// Single or empty values.
1009-
if (e.size() <= 1) {
1010-
auto vals = e.claimAll();
1011-
for (auto val : vals)
1012-
copy.push_back(
1013-
emitShadowCopyIfNeeded(val, Scope, VarInfo, IsAnonymous));
1033+
if (e.empty())
1034+
return;
1035+
1036+
if (e.size() == 1) {
1037+
copy.push_back(
1038+
emitShadowCopyIfNeeded(e.claimNext(), Scope, VarInfo, IsAnonymous));
10141039
return;
10151040
}
10161041

1017-
SILType Type = SILVal->getType();
1018-
auto &LTI = cast<LoadableTypeInfo>(IGM.getTypeInfo(Type));
1019-
auto Alloca = LTI.allocateStack(*this, Type, VarInfo.Name + ".debug");
1020-
zeroInit(cast<llvm::AllocaInst>(Alloca.getAddress().getAddress()));
1021-
ArtificialLocation AutoRestore(Scope, IGM.DebugInfo.get(), Builder);
1022-
LTI.initialize(*this, e, Alloca.getAddress(), false /* isOutlined */);
1023-
copy.push_back(Alloca.getAddressPointer());
1042+
unsigned ArgNo = VarInfo.ArgNo;
1043+
auto &Alloca = ShadowStackSlots[{ArgNo, {Scope, VarInfo.Name}}];
1044+
if (Alloca.isValid()) {
1045+
(void)e.claimAll();
1046+
} else {
1047+
SILType Type = SILVal->getType();
1048+
auto &LTI = cast<LoadableTypeInfo>(IGM.getTypeInfo(Type));
1049+
Alloca =
1050+
LTI.allocateStack(*this, Type, VarInfo.Name + ".debug").getAddress();
1051+
zeroInit(cast<llvm::AllocaInst>(Alloca.getAddress()));
1052+
ArtificialLocation AutoRestore(Scope, IGM.DebugInfo.get(), Builder);
1053+
LTI.initialize(*this, e, Alloca, false /* isOutlined */);
1054+
}
1055+
copy.push_back(Alloca.getAddress());
10241056
}
10251057

10261058
/// Force all archetypes referenced by the type to be bound by this point.
@@ -1081,6 +1113,7 @@ class IRGenSILFunction :
10811113
void emitErrorResultVar(CanSILFunctionType FnTy,
10821114
SILResultInfo ErrorInfo,
10831115
DebugValueInst *DbgValue);
1116+
void emitPoisonDebugValueInst(DebugValueInst *i);
10841117
void emitDebugInfoForAllocStack(AllocStackInst *i, const TypeInfo &type,
10851118
llvm::Value *addr);
10861119
void visitAllocStackInst(AllocStackInst *i);
@@ -4524,7 +4557,112 @@ void IRGenSILFunction::emitErrorResultVar(CanSILFunctionType FnTy,
45244557
IndirectValue, ArtificialValue);
45254558
}
45264559

4560+
void IRGenSILFunction::emitPoisonDebugValueInst(DebugValueInst *i) {
4561+
auto varInfo = i->getVarInfo();
4562+
assert(varInfo && "debug_value without debug info");
4563+
4564+
bool isAnonymous = false;
4565+
varInfo->Name = getVarName(i, isAnonymous);
4566+
4567+
SILValue silVal = i->getOperand();
4568+
SILType silTy = silVal->getType();
4569+
SILType unwrappedTy = silTy.unwrapOptionalType();
4570+
CanType refTy = unwrappedTy.getASTType();
4571+
// TODO: Handling nontrivial aggregates requires implementing poisonRefs
4572+
// within TypeInfo. However, this could inflate code size for large types.
4573+
assert(refTy->isAnyClassReferenceType() && "type can't handle poison");
4574+
4575+
Explosion e = getLoweredExplosion(silVal);
4576+
llvm::Value *storage = e.claimNext();
4577+
auto storageTy = storage->getType();
4578+
// Safeguard: don't try to poison an non-word sized value. Not sure how this
4579+
// could ever happen.
4580+
if (!storageTy->isPointerTy() && storageTy != IGM.SizeTy)
4581+
return;
4582+
4583+
// Only the first word of the value is poisoned.
4584+
//
4585+
// TODO: This assumes that only class references are poisoned (as guaranteed
4586+
// by MandatoryCopyPropagation). And it assumes the reference is the first
4587+
// value (class existential witnesses are laid out after the class reference).
4588+
bool singleValueExplosion = e.empty();
4589+
(void)e.claimAll();
4590+
4591+
// Only poison shadow references if this storage is purely used as a shadow
4592+
// copy--poison should never affect program behavior. Also filter everything
4593+
// not handled by emitShadowCopyIfNeeded to avoid extra shadow copies.
4594+
if (!shouldShadowVariable(*varInfo, isAnonymous)
4595+
|| !shouldShadowStorage(storage)) {
4596+
return;
4597+
}
4598+
4599+
// The original decl scope.
4600+
const SILDebugScope *scope = i->getDebugScope();
4601+
4602+
// Shadow allocas are pointer aligned.
4603+
auto ptrAlign = IGM.getPointerAlignment();
4604+
4605+
// Emit or recover the unique shadow copy.
4606+
//
4607+
// FIXME: To limit perturbing original source, this follows the strange
4608+
// emitShadowCopyIfNeeded logic that has separate paths for single-value
4609+
// vs. multi-value explosions.
4610+
llvm::Value *shadowAddress;
4611+
if (singleValueExplosion) {
4612+
shadowAddress = emitShadowCopy(storage, scope, *varInfo, ptrAlign, false);
4613+
} else {
4614+
assert(refTy->isClassExistentialType() && "unknown multi-value explosion");
4615+
// FIXME: Handling Optional existentials requires TypeInfo
4616+
// support. Otherwise we would need to assume the layout of the reference
4617+
// and bitcast everything below to scalar integers.
4618+
if (silTy != unwrappedTy)
4619+
return;
4620+
4621+
unsigned argNo = varInfo->ArgNo;
4622+
auto &alloca = ShadowStackSlots[{argNo, {scope, varInfo->Name}}];
4623+
if (!alloca.isValid()) {
4624+
auto &lti = cast<LoadableTypeInfo>(IGM.getTypeInfo(silTy));
4625+
alloca =
4626+
lti.allocateStack(*this, silTy, varInfo->Name + ".debug").getAddress();
4627+
}
4628+
shadowAddress =
4629+
emitClassExistentialValueAddress(*this, alloca, silTy).getAddress();
4630+
}
4631+
Size::int_type poisonInt = IGM.TargetInfo.ReferencePoisonDebugValue;
4632+
assert((poisonInt & IGM.TargetInfo.PointerSpareBits.asAPInt()) == 0);
4633+
llvm::Value *poisonedVal = llvm::ConstantInt::get(IGM.SizeTy, poisonInt);
4634+
4635+
// If the current value is nil (Optional's extra inhabitant), then don't
4636+
// overwrite it with poison. This way, lldb will correctly display
4637+
// Optional.None rather than telling the user that an object was
4638+
// deinitialized, when there was no object to begin with. This could also be
4639+
// done with a spare-bits mask to handle arbitrary enums but extra inhabitants
4640+
// are tricky.
4641+
if (!storageTy->isPointerTy()) {
4642+
assert(storageTy == IGM.SizeTy && "can't handle non-word values");
4643+
llvm::Value *currentBits =
4644+
Builder.CreateBitOrPointerCast(storage, IGM.SizeTy);
4645+
llvm::Value *zeroWord = llvm::ConstantInt::get(IGM.SizeTy, 0);
4646+
llvm::Value *isNil = Builder.CreateICmpEQ(currentBits, zeroWord);
4647+
poisonedVal = Builder.CreateSelect(isNil, currentBits, poisonedVal);
4648+
}
4649+
llvm::Value *newShadowVal =
4650+
Builder.CreateBitOrPointerCast(poisonedVal, storageTy);
4651+
4652+
assert(canAllocaStoreValue(shadowAddress, newShadowVal, *varInfo, scope) &&
4653+
"shadow copy can't handle poison");
4654+
4655+
// The poison stores have an artificial location within the original variable
4656+
// declaration's scope.
4657+
ArtificialLocation autoRestore(scope, IGM.DebugInfo.get(), Builder);
4658+
Builder.CreateStore(newShadowVal, shadowAddress, ptrAlign);
4659+
}
4660+
45274661
void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) {
4662+
if (i->poisonRefs()) {
4663+
emitPoisonDebugValueInst(i);
4664+
return;
4665+
}
45284666
if (i->getDebugScope()->getInlinedFunction()->isTransparent())
45294667
return;
45304668

@@ -4943,7 +5081,8 @@ void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i,
49435081
if (auto *Alloca = dyn_cast<llvm::AllocaInst>(addr))
49445082
if (!Alloca->isStaticAlloca()) {
49455083
// Store the address of the dynamic alloca on the stack.
4946-
addr = emitShadowCopy(addr, DS, *VarInfo, IGM.getPointerAlignment());
5084+
addr = emitShadowCopy(addr, DS, *VarInfo, IGM.getPointerAlignment(),
5085+
/*init*/ true);
49475086
Indirection = IndirectValue;
49485087
}
49495088

lib/SIL/Verifier/SILVerifier.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -2501,8 +2501,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
25012501
"Source value should be an object value");
25022502
require(!I->getOperand()->getType().isTrivial(*I->getFunction()),
25032503
"Source value should be non-trivial");
2504-
require(I->poisonRefs() || !fnConv.useLoweredAddresses()
2505-
|| F.hasOwnership(),
2504+
require(!fnConv.useLoweredAddresses() || F.hasOwnership(),
25062505
"destroy_value is only valid in functions with qualified "
25072506
"ownership");
25082507
}

0 commit comments

Comments
 (0)