From bc3b24269eabf6e9e54598c82ede3c741d8a6e61 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Fri, 7 Mar 2014 19:19:56 +0000 Subject: [PATCH] [ADT] Update PointerIntPair to handle pointer types with more than 31 bits free. Previously, the assertions in PointerIntPair would try to calculate the value (1 << NumLowBitsAvailable); the inferred type here is 'int', so if there were more than 31 bits available we'd get a shift overflow. Also, add a rudimentary unit test file for PointerIntPair. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@203273 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/ADT/PointerIntPair.h | 14 ++++-- unittests/ADT/CMakeLists.txt | 1 + unittests/ADT/PointerIntPairTest.cpp | 74 ++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 unittests/ADT/PointerIntPairTest.cpp diff --git a/include/llvm/ADT/PointerIntPair.h b/include/llvm/ADT/PointerIntPair.h index a5a684744b2..313abf3a14b 100644 --- a/include/llvm/ADT/PointerIntPair.h +++ b/include/llvm/ADT/PointerIntPair.h @@ -17,6 +17,7 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include +#include namespace llvm { @@ -41,6 +42,9 @@ template > class PointerIntPair { intptr_t Value; + static_assert(PtrTraits::NumLowBitsAvailable < + std::numeric_limits::digits, + "cannot use a pointer type that has all bits free"); enum : uintptr_t { /// PointerBitMask - The bits that come from the pointer. PointerBitMask = @@ -79,7 +83,7 @@ public: void setPointer(PointerTy PtrVal) { intptr_t PtrWord = reinterpret_cast(PtrTraits::getAsVoidPointer(PtrVal)); - assert((PtrWord & ((1 << PtrTraits::NumLowBitsAvailable)-1)) == 0 && + assert((PtrWord & ~PointerBitMask) == 0 && "Pointer is not sufficiently aligned"); // Preserve all low bits, just update the pointer. Value = PtrWord | (Value & ~PointerBitMask); @@ -87,7 +91,7 @@ public: void setInt(IntType IntVal) { intptr_t IntWord = static_cast(IntVal); - assert(IntWord < (1 << IntBits) && "Integer too large for field"); + assert((IntWord & ~IntMask) == 0 && "Integer too large for field"); // Preserve all bits other than the ones we are updating. Value &= ~ShiftedIntMask; // Remove integer field. @@ -97,7 +101,7 @@ public: void initWithPointer(PointerTy PtrVal) { intptr_t PtrWord = reinterpret_cast(PtrTraits::getAsVoidPointer(PtrVal)); - assert((PtrWord & ((1 << PtrTraits::NumLowBitsAvailable)-1)) == 0 && + assert((PtrWord & ~PointerBitMask) == 0 && "Pointer is not sufficiently aligned"); Value = PtrWord; } @@ -105,10 +109,10 @@ public: void setPointerAndInt(PointerTy PtrVal, IntType IntVal) { intptr_t PtrWord = reinterpret_cast(PtrTraits::getAsVoidPointer(PtrVal)); - assert((PtrWord & ((1 << PtrTraits::NumLowBitsAvailable)-1)) == 0 && + assert((PtrWord & ~PointerBitMask) == 0 && "Pointer is not sufficiently aligned"); intptr_t IntWord = static_cast(IntVal); - assert(IntWord < (1 << IntBits) && "Integer too large for field"); + assert((IntWord & ~IntMask) == 0 && "Integer too large for field"); Value = PtrWord | (IntWord << IntShift); } diff --git a/unittests/ADT/CMakeLists.txt b/unittests/ADT/CMakeLists.txt index 88e8186f33a..66e2d20ef73 100644 --- a/unittests/ADT/CMakeLists.txt +++ b/unittests/ADT/CMakeLists.txt @@ -24,6 +24,7 @@ set(ADTSources OptionalTest.cpp OwningPtrTest.cpp PackedVectorTest.cpp + PointerIntPairTest.cpp PointerUnionTest.cpp SCCIteratorTest.cpp SmallPtrSetTest.cpp diff --git a/unittests/ADT/PointerIntPairTest.cpp b/unittests/ADT/PointerIntPairTest.cpp new file mode 100644 index 00000000000..3da31e7dfe7 --- /dev/null +++ b/unittests/ADT/PointerIntPairTest.cpp @@ -0,0 +1,74 @@ +//===- llvm/unittest/ADT/PointerIntPairTest.cpp - Unit tests --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "llvm/ADT/PointerIntPair.h" +#include +using namespace llvm; + +namespace { + +// Test fixture +class PointerIntPairTest : public testing::Test { +}; + +TEST_F(PointerIntPairTest, GetSet) { + PointerIntPair Pair{this, 1U}; + EXPECT_EQ(this, Pair.getPointer()); + EXPECT_EQ(1U, Pair.getInt()); + + Pair.setInt(2); + EXPECT_EQ(this, Pair.getPointer()); + EXPECT_EQ(2U, Pair.getInt()); + + Pair.setPointer(nullptr); + EXPECT_EQ(nullptr, Pair.getPointer()); + EXPECT_EQ(2U, Pair.getInt()); + + Pair.setPointerAndInt(this, 3U); + EXPECT_EQ(this, Pair.getPointer()); + EXPECT_EQ(3U, Pair.getInt()); +} + +TEST_F(PointerIntPairTest, DefaultInitialize) { + PointerIntPair Pair; + EXPECT_EQ(nullptr, Pair.getPointer()); + EXPECT_EQ(0U, Pair.getInt()); +} + +TEST_F(PointerIntPairTest, ManyUnusedBits) { + // In real code this would be a word-sized integer limited to 31 bits. + struct Fixnum31 { + uintptr_t Value; + }; + class FixnumPointerTraits { + public: + static inline void *getAsVoidPointer(Fixnum31 Num) { + return reinterpret_cast(Num.Value << NumLowBitsAvailable); + } + static inline Fixnum31 getFromVoidPointer(void *P) { + // In real code this would assert that the value is in range. + return { reinterpret_cast(P) >> NumLowBitsAvailable }; + } + enum { NumLowBitsAvailable = std::numeric_limits::digits - 31 }; + }; + + PointerIntPair pair; + EXPECT_EQ((uintptr_t)0, pair.getPointer().Value); + EXPECT_EQ(false, pair.getInt()); + + pair.setPointerAndInt({ 0x7FFFFFFF }, true ); + EXPECT_EQ((uintptr_t)0x7FFFFFFF, pair.getPointer().Value); + EXPECT_EQ(true, pair.getInt()); + + EXPECT_EQ(FixnumPointerTraits::NumLowBitsAvailable - 1, + PointerLikeTypeTraits::NumLowBitsAvailable); +} + +} // end anonymous namespace -- 2.34.1