Skip to content

Commit 992f645

Browse files
committed
Remove type-punning in LazyOffsetPtr.
1 parent 90767bc commit 992f645

File tree

1 file changed

+63
-24
lines changed

1 file changed

+63
-24
lines changed

clang/include/clang/AST/ExternalASTSource.h

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "clang/AST/DeclBase.h"
1919
#include "clang/Basic/LLVM.h"
2020
#include "llvm/ADT/ArrayRef.h"
21+
#include "llvm/ADT/bit.h"
2122
#include "llvm/ADT/DenseMap.h"
2223
#include "llvm/ADT/IntrusiveRefCntPtr.h"
2324
#include "llvm/ADT/PointerUnion.h"
@@ -321,50 +322,87 @@ class ExternalASTSource : public RefCountedBase<ExternalASTSource> {
321322
/// external AST source itself.
322323
template<typename T, typename OffsT, T* (ExternalASTSource::*Get)(OffsT Offset)>
323324
struct LazyOffsetPtr {
324-
/// Either a pointer to an AST node or the offset within the
325-
/// external AST source where the AST node can be found.
326-
///
327-
/// If the low bit is clear, a pointer to the AST node. If the low
328-
/// bit is set, the upper 63 bits are the offset.
329-
mutable uint64_t Ptr = 0;
325+
/// Storage for a pointer or an offset, for the case where pointers are 64
326+
/// bits wide. The least-significant bit is used as the discriminator. If the
327+
/// bit is clear, a pointer to the AST node. If the bit is set, the upper 63
328+
/// bits are the offset.
329+
union StorageType64 {
330+
StorageType64(uint64_t Offset) : ShiftedOffset((Offset << 1) | 1) {
331+
assert(isOffset());
332+
}
333+
StorageType64(T *Ptr) : Pointer(Ptr) { assert(!isOffset()); }
334+
335+
bool isOffset() { return llvm::bit_cast<uint64_t>(*this) & 1; }
336+
uint64_t offset() { return ShiftedOffset >> 1; }
337+
T *&pointer() { return Pointer; }
338+
339+
uint64_t ShiftedOffset;
340+
T *Pointer;
341+
};
342+
343+
/// Storage for a pointer or an offset, for the case where pointers are not 64
344+
/// bits wide.
345+
union StorageType32 {
346+
StorageType32(uint64_t Off) : Offset{true, Off} {}
347+
StorageType32(T *Ptr) : Pointer{false, Ptr} {}
348+
349+
bool isOffset() { return Offset.IsOffset; }
350+
uint64_t offset() { return Offset.Offset; }
351+
T *&pointer() { return Pointer.Pointer; }
352+
353+
struct OffsetType {
354+
uint64_t IsOffset : 1;
355+
uint64_t Offset : 63;
356+
} Offset;
357+
struct PointerType {
358+
uint64_t IsOffset : 1;
359+
T *Pointer;
360+
} Pointer;
361+
};
362+
363+
mutable std::conditional_t<sizeof(uint64_t) == sizeof(T *), StorageType64,
364+
StorageType32>
365+
Storage;
330366

331367
public:
332-
LazyOffsetPtr() = default;
333-
explicit LazyOffsetPtr(T *Ptr) : Ptr(reinterpret_cast<uint64_t>(Ptr)) {}
368+
LazyOffsetPtr() : LazyOffsetPtr(nullptr) {}
369+
explicit LazyOffsetPtr(T *Ptr) : Storage(Ptr) {}
334370

335-
explicit LazyOffsetPtr(uint64_t Offset) : Ptr((Offset << 1) | 0x01) {
336-
assert((Offset << 1 >> 1) == Offset && "Offsets must require < 63 bits");
371+
explicit LazyOffsetPtr(uint64_t Offset) : Storage(Offset) {
372+
assert(Offset == Storage.offset() && "Offsets must require < 63 bits");
337373
if (Offset == 0)
338-
Ptr = 0;
374+
Storage = nullptr;
339375
}
340376

341377
LazyOffsetPtr &operator=(T *Ptr) {
342-
this->Ptr = reinterpret_cast<uint64_t>(Ptr);
378+
Storage = Ptr;
343379
return *this;
344380
}
345381

346382
LazyOffsetPtr &operator=(uint64_t Offset) {
347-
assert((Offset << 1 >> 1) == Offset && "Offsets must require < 63 bits");
348383
if (Offset == 0)
349-
Ptr = 0;
350-
else
351-
Ptr = (Offset << 1) | 0x01;
352-
384+
Storage = nullptr;
385+
else {
386+
Storage = Offset;
387+
assert(Offset == Storage.offset() && "Offsets must require < 63 bits");
388+
}
353389
return *this;
354390
}
355391

356392
/// Whether this pointer is non-NULL.
357393
///
358394
/// This operation does not require the AST node to be deserialized.
359-
explicit operator bool() const { return Ptr != 0; }
395+
explicit operator bool() const {
396+
return Storage.isOffset() || Storage.pointer();
397+
}
360398

361399
/// Whether this pointer is non-NULL.
362400
///
363401
/// This operation does not require the AST node to be deserialized.
364-
bool isValid() const { return Ptr != 0; }
402+
bool isValid() const { return operator bool(); }
365403

366404
/// Whether this pointer is currently stored as an offset.
367-
bool isOffset() const { return Ptr & 0x01; }
405+
bool isOffset() const { return Storage.isOffset(); }
368406

369407
/// Retrieve the pointer to the AST node that this lazy pointer points to.
370408
///
@@ -375,17 +413,18 @@ struct LazyOffsetPtr {
375413
if (isOffset()) {
376414
assert(Source &&
377415
"Cannot deserialize a lazy pointer without an AST source");
378-
Ptr = reinterpret_cast<uint64_t>((Source->*Get)(OffsT(Ptr >> 1)));
416+
Storage = (Source->*Get)(static_cast<OffsT>(Storage.offset()));
379417
}
380-
return reinterpret_cast<T*>(Ptr);
418+
return Storage.pointer();
381419
}
382420

383421
/// Retrieve the address of the AST node pointer. Deserializes the pointee if
384-
/// necessary.
422+
/// necessary. This function should not exist, and we would like to remove it.
423+
/// Do not add new calls to it.
385424
T **getAddressOfPointer(ExternalASTSource *Source) const {
386425
// Ensure the integer is in pointer form.
387426
(void)get(Source);
388-
return reinterpret_cast<T**>(&Ptr);
427+
return &Storage.pointer();
389428
}
390429
};
391430

0 commit comments

Comments
 (0)