Skip to content

Commit e541aa5

Browse files
jrtc27tru
authored andcommitted
[clang] Make LazyOffsetPtr more portable (llvm#112927)
LazyOffsetPtr currently relies on uint64_t being able to store a pointer and, unless sizeof(uint64_t) == sizeof(void *), little endianness, since getAddressOfPointer reinterprets the memory as a pointer. This also doesn't properly respect the C++ object model. As removing getAddressOfPointer would have wide-reaching implications, improve the implementation to account for these problems by using placement new and a suitably sized-and-aligned buffer, "right"-aligning the objects on big-endian platforms so the LSBs are in the same place for use as the discriminator. Fixes: bc73ef0 Fixes: llvm#111993 (cherry picked from commit 7619699)
1 parent 936710a commit e541aa5

File tree

1 file changed

+35
-13
lines changed

1 file changed

+35
-13
lines changed

clang/include/clang/AST/ExternalASTSource.h

+35-13
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@
2525
#include "llvm/ADT/SmallVector.h"
2626
#include "llvm/ADT/iterator.h"
2727
#include "llvm/Support/PointerLikeTypeTraits.h"
28+
#include <algorithm>
2829
#include <cassert>
2930
#include <cstddef>
3031
#include <cstdint>
3132
#include <iterator>
33+
#include <new>
3234
#include <optional>
3335
#include <utility>
3436

@@ -326,45 +328,65 @@ struct LazyOffsetPtr {
326328
///
327329
/// If the low bit is clear, a pointer to the AST node. If the low
328330
/// bit is set, the upper 63 bits are the offset.
329-
mutable uint64_t Ptr = 0;
331+
static constexpr size_t DataSize = std::max(sizeof(uint64_t), sizeof(T *));
332+
alignas(uint64_t) alignas(T *) mutable unsigned char Data[DataSize] = {};
333+
334+
unsigned char GetLSB() const {
335+
return Data[llvm::sys::IsBigEndianHost ? DataSize - 1 : 0];
336+
}
337+
338+
template <typename U> U &As(bool New) const {
339+
unsigned char *Obj =
340+
Data + (llvm::sys::IsBigEndianHost ? DataSize - sizeof(U) : 0);
341+
if (New)
342+
return *new (Obj) U;
343+
return *std::launder(reinterpret_cast<U *>(Obj));
344+
}
345+
346+
T *&GetPtr() const { return As<T *>(false); }
347+
uint64_t &GetU64() const { return As<uint64_t>(false); }
348+
void SetPtr(T *Ptr) const { As<T *>(true) = Ptr; }
349+
void SetU64(uint64_t U64) const { As<uint64_t>(true) = U64; }
330350

331351
public:
332352
LazyOffsetPtr() = default;
333-
explicit LazyOffsetPtr(T *Ptr) : Ptr(reinterpret_cast<uint64_t>(Ptr)) {}
353+
explicit LazyOffsetPtr(T *Ptr) : Data() { SetPtr(Ptr); }
334354

335-
explicit LazyOffsetPtr(uint64_t Offset) : Ptr((Offset << 1) | 0x01) {
355+
explicit LazyOffsetPtr(uint64_t Offset) : Data() {
336356
assert((Offset << 1 >> 1) == Offset && "Offsets must require < 63 bits");
337357
if (Offset == 0)
338-
Ptr = 0;
358+
SetPtr(nullptr);
359+
else
360+
SetU64((Offset << 1) | 0x01);
339361
}
340362

341363
LazyOffsetPtr &operator=(T *Ptr) {
342-
this->Ptr = reinterpret_cast<uint64_t>(Ptr);
364+
SetPtr(Ptr);
343365
return *this;
344366
}
345367

346368
LazyOffsetPtr &operator=(uint64_t Offset) {
347369
assert((Offset << 1 >> 1) == Offset && "Offsets must require < 63 bits");
348370
if (Offset == 0)
349-
Ptr = 0;
371+
SetPtr(nullptr);
350372
else
351-
Ptr = (Offset << 1) | 0x01;
373+
SetU64((Offset << 1) | 0x01);
352374

353375
return *this;
354376
}
355377

356378
/// Whether this pointer is non-NULL.
357379
///
358380
/// This operation does not require the AST node to be deserialized.
359-
explicit operator bool() const { return Ptr != 0; }
381+
explicit operator bool() const { return isOffset() || GetPtr() != nullptr; }
360382

361383
/// Whether this pointer is non-NULL.
362384
///
363385
/// This operation does not require the AST node to be deserialized.
364-
bool isValid() const { return Ptr != 0; }
386+
bool isValid() const { return isOffset() || GetPtr() != nullptr; }
365387

366388
/// Whether this pointer is currently stored as an offset.
367-
bool isOffset() const { return Ptr & 0x01; }
389+
bool isOffset() const { return GetLSB() & 0x01; }
368390

369391
/// Retrieve the pointer to the AST node that this lazy pointer points to.
370392
///
@@ -375,17 +397,17 @@ struct LazyOffsetPtr {
375397
if (isOffset()) {
376398
assert(Source &&
377399
"Cannot deserialize a lazy pointer without an AST source");
378-
Ptr = reinterpret_cast<uint64_t>((Source->*Get)(OffsT(Ptr >> 1)));
400+
SetPtr((Source->*Get)(OffsT(GetU64() >> 1)));
379401
}
380-
return reinterpret_cast<T*>(Ptr);
402+
return GetPtr();
381403
}
382404

383405
/// Retrieve the address of the AST node pointer. Deserializes the pointee if
384406
/// necessary.
385407
T **getAddressOfPointer(ExternalASTSource *Source) const {
386408
// Ensure the integer is in pointer form.
387409
(void)get(Source);
388-
return reinterpret_cast<T**>(&Ptr);
410+
return &GetPtr();
389411
}
390412
};
391413

0 commit comments

Comments
 (0)