Skip to content

Commit 9f740d3

Browse files
authored
Merge pull request swiftlang#36389 from atrick/poison-debug
[NFC] Poison references at -Onone after potentially shortening object lifetime
2 parents ef84b19 + 8ba6868 commit 9f740d3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1553
-706
lines changed

docs/SIL.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3466,7 +3466,7 @@ debug_value
34663466

34673467
::
34683468

3469-
sil-instruction ::= debug_value sil-operand (',' debug-var-attr)*
3469+
sil-instruction ::= debug_value '[poison]'? sil-operand (',' debug-var-attr)*
34703470

34713471
debug_value %1 : $Int
34723472

@@ -3488,6 +3488,15 @@ variable that is being described, including the name of the
34883488
variable. For function and closure arguments ``argno`` is the number
34893489
of the function argument starting with 1.
34903490

3491+
If the '[poison]' flag is set, then all references within this debug
3492+
value will be overwritten with a sentinel at this point in the
3493+
program. This is used in debug builds when shortening non-trivial
3494+
value lifetimes to ensure the debugger cannot inspect invalid
3495+
memory. `debug_value` instructions with the poison flag are not
3496+
generated until OSSA islowered. They are not expected to be serialized
3497+
within the module, and the pipeline is not expected to do any
3498+
significant code motion after lowering.
3499+
34913500
debug_value_addr
34923501
````````````````
34933502

include/swift/SIL/SILBuilder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,8 @@ class SILBuilder {
956956
}
957957

958958
DebugValueInst *createDebugValue(SILLocation Loc, SILValue src,
959-
SILDebugVariable Var);
959+
SILDebugVariable Var,
960+
bool poisonRefs = false);
960961
DebugValueAddrInst *createDebugValueAddr(SILLocation Loc, SILValue src,
961962
SILDebugVariable Var);
962963

include/swift/SIL/SILCloner.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1280,7 +1280,8 @@ SILCloner<ImplClass>::visitDebugValueInst(DebugValueInst *Inst) {
12801280
recordClonedInstruction(
12811281
Inst, getBuilder().createDebugValue(Inst->getLoc(),
12821282
getOpValue(Inst->getOperand()),
1283-
*Inst->getVarInfo()));
1283+
*Inst->getVarInfo(),
1284+
Inst->poisonRefs()));
12841285
}
12851286
template<typename ImplClass>
12861287
void

include/swift/SIL/SILInstruction.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4524,9 +4524,10 @@ class DebugValueInst final
45244524
TailAllocatedDebugVariable VarInfo;
45254525

45264526
DebugValueInst(SILDebugLocation DebugLoc, SILValue Operand,
4527-
SILDebugVariable Var);
4527+
SILDebugVariable Var, bool poisonRefs);
45284528
static DebugValueInst *create(SILDebugLocation DebugLoc, SILValue Operand,
4529-
SILModule &M, SILDebugVariable Var);
4529+
SILModule &M, SILDebugVariable Var,
4530+
bool poisonRefs);
45304531

45314532
size_t numTrailingObjects(OverloadToken<char>) const { return 1; }
45324533

@@ -4538,6 +4539,21 @@ class DebugValueInst final
45384539
Optional<SILDebugVariable> getVarInfo() const {
45394540
return VarInfo.get(getDecl(), getTrailingObjects<char>());
45404541
}
4542+
4543+
/// True if all references within this debug value will be overwritten with a
4544+
/// poison sentinel at this point in the program. This is used in debug builds
4545+
/// when shortening non-trivial value lifetimes to ensure the debugger cannot
4546+
/// inspect invalid memory. These are not generated until OSSA is
4547+
/// lowered. They are not expected to be serialized within the module, and the
4548+
/// debug pipeline is not expected to do any significant code motion after
4549+
/// OSSA lowering. It should not be necessary to model the poison operation as
4550+
/// a side effect, which would violate the rule that debug_values cannot
4551+
/// affect optimization.
4552+
bool poisonRefs() const { return SILNode::Bits.DebugValueInst.PoisonRefs; }
4553+
4554+
void setPoisonRefs(bool poisonRefs = true) {
4555+
SILNode::Bits.DebugValueInst.PoisonRefs = poisonRefs;
4556+
}
45414557
};
45424558

45434559
/// Define the start or update to a symbolic variable value (for address-only
@@ -7142,6 +7158,14 @@ class DestroyValueInst
71427158
}
71437159

71447160
public:
7161+
/// If true, then all references within the destroyed value will be
7162+
/// overwritten with a sentinel. This is used in debug builds when shortening
7163+
/// non-trivial value lifetimes to ensure the debugger cannot inspect invalid
7164+
/// memory. These semantics are part of the destroy_value instruction to
7165+
/// avoid representing use-after-destroy in OSSA form and so that OSSA
7166+
/// transformations keep the poison operation associated with the destroy
7167+
/// point. After OSSA, these are lowered to 'debug_values [poison]'
7168+
/// instructions, after which the Onone pipeline should avoid code motion.
71457169
bool poisonRefs() const { return SILNode::Bits.DestroyValueInst.PoisonRefs; }
71467170

71477171
void setPoisonRefs(bool poisonRefs = true) {

include/swift/SIL/SILNode.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,9 @@ class alignas(8) SILNode {
341341
SWIFT_INLINE_BITFIELD(DestroyValueInst, NonValueInstruction, 1,
342342
PoisonRefs : 1);
343343

344+
SWIFT_INLINE_BITFIELD(DebugValueInst, NonValueInstruction, 1,
345+
PoisonRefs : 1);
346+
344347
SWIFT_INLINE_BITFIELD(EndCOWMutationInst, NonValueInstruction, 1,
345348
KeepUnique : 1
346349
);

include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
#ifndef SWIFT_SILOPTIMIZER_UTILS_CANONICALOSSALIFETIME_H
9494
#define SWIFT_SILOPTIMIZER_UTILS_CANONICALOSSALIFETIME_H
9595

96+
#include "swift/Basic/SmallPtrSetVector.h"
9697
#include "swift/SIL/SILInstruction.h"
9798
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
9899
#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h"
@@ -123,13 +124,33 @@ class CanonicalOSSAConsumeInfo {
123124
/// that should be canonicalized separately.
124125
llvm::SmallDenseMap<SILBasicBlock *, CopyValueInst *, 4> persistentCopies;
125126

127+
/// The set of non-destroy consumes that need to be poisoned. This is
128+
/// determined in two steps. First findOrInsertDestroyInBlock() checks if the
129+
/// lifetime shrank within the block. Second rewriteCopies() checks if the
130+
/// consume is in remnantLiveOutBlock(). Finally injectPoison() inserts new
131+
/// copies and poison destroys for everything in this set.
132+
SmallPtrSetVector<SILInstruction *, 4> needsPoisonConsumes;
133+
126134
public:
127135
bool hasUnclaimedConsumes() const { return !finalBlockConsumes.empty(); }
128136

129137
void clear() {
130138
finalBlockConsumes.clear();
131139
debugAfterConsume.clear();
132140
persistentCopies.clear();
141+
needsPoisonConsumes.clear();
142+
}
143+
144+
void recordNeedsPoison(SILInstruction *consume) {
145+
needsPoisonConsumes.insert(consume);
146+
}
147+
148+
bool needsPoison(SILInstruction *consume) const {
149+
return needsPoisonConsumes.count(consume);
150+
}
151+
152+
ArrayRef<SILInstruction *> getNeedsPoisonConsumes() const {
153+
return needsPoisonConsumes.getArrayRef();
133154
}
134155

135156
bool hasFinalConsumes() const { return !finalBlockConsumes.empty(); }
@@ -158,6 +179,11 @@ class CanonicalOSSAConsumeInfo {
158179
debugAfterConsume.push_back(dvi);
159180
}
160181

182+
void popDebugAfterConsume(DebugValueInst *dvi) {
183+
if (!debugAfterConsume.empty() && debugAfterConsume.back() == dvi)
184+
debugAfterConsume.pop_back();
185+
}
186+
161187
ArrayRef<DebugValueInst *> getDebugInstsAfterConsume() const {
162188
return debugAfterConsume;
163189
}
@@ -189,6 +215,9 @@ class CanonicalizeOSSALifetime {
189215
/// liveness may be pruned during canonicalization.
190216
bool pruneDebugMode;
191217

218+
/// If true, then new destroy_value instructions will be poison.
219+
bool poisonRefsMode;
220+
192221
NonLocalAccessBlockAnalysis *accessBlockAnalysis;
193222
// Lazily initialize accessBlocks only when
194223
// extendLivenessThroughOverlappingAccess is invoked.
@@ -232,16 +261,26 @@ class CanonicalizeOSSALifetime {
232261
/// ending". end_borrows do not end a liverange that may include owned copies.
233262
PrunedLiveness liveness;
234263

264+
/// remnantLiveOutBlocks are part of the original extended lifetime that are
265+
/// not in canonical pruned liveness. There is a path from a PrunedLiveness
266+
/// boundary to an original destroy that passes through a remnant block.
267+
///
268+
/// These blocks would be equivalent to PrunedLiveness::LiveOut if
269+
/// PrunedLiveness were recomputed using all original destroys as interesting
270+
/// uses, minus blocks already marked PrunedLiveness::LiveOut. (Remnant blocks
271+
/// may be in PrunedLiveness::LiveWithin).
272+
SmallSetVector<SILBasicBlock *, 8> remnantLiveOutBlocks;
273+
235274
/// Information about consuming instructions discovered in this caonical OSSA
236275
/// lifetime.
237276
CanonicalOSSAConsumeInfo consumes;
238277

239278
public:
240-
CanonicalizeOSSALifetime(bool pruneDebugMode,
279+
CanonicalizeOSSALifetime(bool pruneDebugMode, bool poisonRefsMode,
241280
NonLocalAccessBlockAnalysis *accessBlockAnalysis,
242281
DominanceAnalysis *dominanceAnalysis,
243282
DeadEndBlocks *deBlocks)
244-
: pruneDebugMode(pruneDebugMode),
283+
: pruneDebugMode(pruneDebugMode), poisonRefsMode(poisonRefsMode),
245284
accessBlockAnalysis(accessBlockAnalysis),
246285
dominanceAnalysis(dominanceAnalysis), deBlocks(deBlocks) {}
247286

@@ -263,6 +302,7 @@ class CanonicalizeOSSALifetime {
263302
consumingBlocks.clear();
264303
debugValues.clear();
265304
liveness.clear();
305+
remnantLiveOutBlocks.clear();
266306
}
267307

268308
bool hasChanged() const { return changed; }
@@ -309,7 +349,12 @@ class CanonicalizeOSSALifetime {
309349

310350
void findOrInsertDestroys();
311351

352+
void insertDestroyOnCFGEdge(SILBasicBlock *predBB, SILBasicBlock *succBB,
353+
bool needsPoison);
354+
312355
void rewriteCopies();
356+
357+
void injectPoison();
313358
};
314359

315360
} // end namespace swift

lib/IRGen/GenExistential.cpp

Lines changed: 8 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

0 commit comments

Comments
 (0)