Skip to content

Commit a44c88e

Browse files
authored
Emulate TrackingVH using WeakVH (microsoft#6662)
This PR pulls the upstream change, Emulate TrackingVH using WeakVH (llvm/llvm-project@8a62382), into DXC. Here's the summary of the change: > This frees up one slot in the HandleBaseKind enum, which I will use later to add a new kind of value handle. The size of the HandleBaseKind enum is important because we store a HandleBaseKind in > the low two bits of a (in the worst case) 4 byte aligned pointer. > > Reviewers: davide, chandlerc > > Subscribers: mcrosier, llvm-commits > > Differential Revision: https://reviews.llvm.org/D32634 This is part 2 of the fix for microsoft#6659.
1 parent 1c7cb4f commit a44c88e

File tree

3 files changed

+87
-47
lines changed

3 files changed

+87
-47
lines changed

include/llvm/IR/ValueHandle.h

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,7 @@ class ValueHandleBase {
4545
///
4646
/// This is to avoid having a vtable for the light-weight handle pointers. The
4747
/// fully general Callback version does have a vtable.
48-
enum HandleBaseKind {
49-
Assert,
50-
Callback,
51-
Tracking,
52-
Weak
53-
};
48+
enum HandleBaseKind { Assert, Callback, Weak };
5449

5550
private:
5651
PointerIntPair<ValueHandleBase**, 2, HandleBaseKind> PrevPair;
@@ -165,6 +160,10 @@ class WeakVH : public ValueHandleBase {
165160
operator Value*() const {
166161
return getValPtr();
167162
}
163+
164+
bool pointsToAliveValue() const {
165+
return ValueHandleBase::isValid(getValPtr());
166+
}
168167
};
169168

170169
// Specialize simplify_type to allow WeakVH to participate in
@@ -275,46 +274,65 @@ struct isPodLike<AssertingVH<T> > {
275274
#endif
276275
};
277276

278-
279277
/// \brief Value handle that tracks a Value across RAUW.
280278
///
281279
/// TrackingVH is designed for situations where a client needs to hold a handle
282280
/// to a Value (or subclass) across some operations which may move that value,
283281
/// but should never destroy it or replace it with some unacceptable type.
284282
///
285-
/// It is an error to do anything with a TrackingVH whose value has been
286-
/// destroyed, except to destruct it.
287-
///
288283
/// It is an error to attempt to replace a value with one of a type which is
289284
/// incompatible with any of its outstanding TrackingVHs.
290-
template<typename ValueTy>
291-
class TrackingVH : public ValueHandleBase {
292-
void CheckValidity() const {
293-
Value *VP = ValueHandleBase::getValPtr();
294-
295-
// Null is always ok.
296-
if (!VP) return;
285+
///
286+
/// It is an error to read from a TrackingVH that does not point to a valid
287+
/// value. A TrackingVH is said to not point to a valid value if either it
288+
/// hasn't yet been assigned a value yet or because the value it was tracking
289+
/// has since been deleted.
290+
///
291+
/// Assigning a value to a TrackingVH is always allowed, even if said TrackingVH
292+
/// no longer points to a valid value.
293+
template <typename ValueTy> class TrackingVH {
294+
WeakVH InnerHandle;
297295

298-
// Check that this value is valid (i.e., it hasn't been deleted). We
299-
// explicitly delay this check until access to avoid requiring clients to be
300-
// unnecessarily careful w.r.t. destruction.
301-
assert(ValueHandleBase::isValid(VP) && "Tracked Value was deleted!");
296+
public:
297+
ValueTy *getValPtr() const {
298+
// HLSL Change begin
299+
// The original upstream change will assert here when accessing a TrackingVH
300+
// is deleted.
301+
//
302+
// However, the llvm code that DXC forked has the implicit code like:
303+
// TrackingVH V = nullptr;
304+
//
305+
// It will invoke setValPtr(nullptr) and then getValPtr(nullptr). So pull in
306+
// the original upstream change in DXC will always assert here for debug
307+
// build even this code is valid.
308+
//
309+
// The original upstream change works because of another upstream change
310+
// https://github.com/llvm/llvm-project/commit/70a6051ddfd5f04777f2bc42503bb11bc8f1723a
311+
// cleaned up the problematic code in DXC already.
312+
//
313+
// Untill we decide to pull that upstream change into DXC, DXC should follow
314+
// the original TrackingVH implementation. return Null is always ok here
315+
// instead of assert it.
316+
if (InnerHandle.operator llvm::Value *() == nullptr)
317+
return nullptr;
318+
// HLSL Change end.
319+
320+
assert(InnerHandle.pointsToAliveValue() &&
321+
"TrackingVH must be non-null and valid on dereference!");
302322

303323
// Check that the value is a member of the correct subclass. We would like
304324
// to check this property on assignment for better debugging, but we don't
305325
// want to require a virtual interface on this VH. Instead we allow RAUW to
306326
// replace this value with a value of an invalid type, and check it here.
307-
assert(isa<ValueTy>(VP) &&
327+
assert(isa<ValueTy>(InnerHandle) &&
308328
"Tracked Value was replaced by one with an invalid type!");
329+
return cast<ValueTy>(InnerHandle);
309330
}
310331

311-
ValueTy *getValPtr() const {
312-
CheckValidity();
313-
return (ValueTy*)ValueHandleBase::getValPtr();
314-
}
315332
void setValPtr(ValueTy *P) {
316-
CheckValidity();
317-
ValueHandleBase::operator=(GetAsValue(P));
333+
// Assigning to non-valid TrackingVH's are fine so we just unconditionally
334+
// assign here.
335+
InnerHandle = GetAsValue(P);
318336
}
319337

320338
// Convert a ValueTy*, which may be const, to the type the base
@@ -323,9 +341,11 @@ class TrackingVH : public ValueHandleBase {
323341
static Value *GetAsValue(const Value *V) { return const_cast<Value*>(V); }
324342

325343
public:
326-
TrackingVH() : ValueHandleBase(Tracking) {}
327-
TrackingVH(ValueTy *P) : ValueHandleBase(Tracking, GetAsValue(P)) {}
328-
TrackingVH(const TrackingVH &RHS) : ValueHandleBase(Tracking, RHS) {}
344+
TrackingVH() {}
345+
TrackingVH(ValueTy *P) { setValPtr(P); }
346+
TrackingVH(const TrackingVH &RHS) {
347+
setValPtr(RHS.getValPtr());
348+
} // HLSL Change
329349

330350
operator ValueTy*() const {
331351
return getValPtr();

lib/IR/Value.cpp

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -672,11 +672,6 @@ void ValueHandleBase::ValueIsDeleted(Value *V) {
672672
switch (Entry->getKind()) {
673673
case Assert:
674674
break;
675-
case Tracking:
676-
// Mark that this value has been deleted by setting it to an invalid Value
677-
// pointer.
678-
Entry->operator=(DenseMapInfo<Value *>::getTombstoneKey());
679-
break;
680675
case Weak:
681676
// Weak just goes to null, which will unlink it from the list.
682677
Entry->operator=(nullptr);
@@ -729,13 +724,6 @@ void ValueHandleBase::ValueIsRAUWd(Value *Old, Value *New) {
729724
case Assert:
730725
// Asserting handle does not follow RAUW implicitly.
731726
break;
732-
case Tracking:
733-
// Tracking goes to new value like a WeakVH. Note that this may make it
734-
// something incompatible with its templated type. We don't want to have a
735-
// virtual (or inline) interface to handle this though, so instead we make
736-
// the TrackingVH accessors guarantee that a client never sees this value.
737-
738-
LLVM_FALLTHROUGH; // HLSL CHANGE
739727
case Weak:
740728
// Weak goes to the new value, which will unlink it from Old's list.
741729
Entry->operator=(New);
@@ -748,18 +736,17 @@ void ValueHandleBase::ValueIsRAUWd(Value *Old, Value *New) {
748736
}
749737

750738
#ifndef NDEBUG
751-
// If any new tracking or weak value handles were added while processing the
739+
// If any new weak value handles were added while processing the
752740
// list, then complain about it now.
753741
if (Old->HasValueHandle)
754742
for (Entry = pImpl->ValueHandles[Old]; Entry; Entry = Entry->Next)
755743
switch (Entry->getKind()) {
756-
case Tracking:
757744
case Weak:
758745
dbgs() << "After RAUW from " << *Old->getType() << " %"
759746
<< Old->getName() << " to " << *New->getType() << " %"
760747
<< New->getName() << "\n";
761-
llvm_unreachable("A tracking or weak value handle still pointed to the"
762-
" old value!\n");
748+
llvm_unreachable(
749+
"A weak value handle still pointed to the old value!\n");
763750
default:
764751
break;
765752
}

unittests/IR/ValueHandleTest.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,31 @@ TEST_F(ValueHandle, AssertingVH_ReducesToPointer) {
171171

172172
#else // !NDEBUG
173173

174+
TEST_F(ValueHandle, TrackingVH_Tracks) {
175+
{ // HLSL Change
176+
TrackingVH<Value> VH(BitcastV.get());
177+
BitcastV->replaceAllUsesWith(ConstantV);
178+
EXPECT_EQ(VH, ConstantV);
179+
} // HLSL Change
180+
181+
// HLSL Change begin
182+
// This test is a DEATH_TEST in the original upstream change. It will
183+
// assert when accessing a TrackingVH is deleted.
184+
// However, DXC should follow the original TrackingVH implementation.
185+
// return Null is always ok instead of assert it.
186+
// Check the comment in TrackingVH::getValPtr() for more detail.
187+
{
188+
TrackingVH<Value> VH(BitcastV.get());
189+
190+
// The tracking handle shouldn't assert when the value is deleted.
191+
BitcastV.reset(
192+
new BitCastInst(ConstantV, Type::getInt32Ty(getGlobalContext())));
193+
// The handle should be nullptr after it's deleted.
194+
EXPECT_EQ(VH, nullptr);
195+
}
196+
// HLSL Change end
197+
}
198+
174199
#ifdef GTEST_HAS_DEATH_TEST
175200

176201
TEST_F(ValueHandle, AssertingVH_Asserts) {
@@ -185,6 +210,14 @@ TEST_F(ValueHandle, AssertingVH_Asserts) {
185210
BitcastV.reset();
186211
}
187212

213+
TEST_F(ValueHandle, TrackingVH_Asserts) {
214+
TrackingVH<Instruction> VH(BitcastV.get());
215+
216+
BitcastV->replaceAllUsesWith(ConstantV);
217+
EXPECT_DEATH((void)*VH,
218+
"Tracked Value was replaced by one with an invalid type!");
219+
}
220+
188221
#endif // GTEST_HAS_DEATH_TEST
189222

190223
#endif // NDEBUG

0 commit comments

Comments
 (0)