
llvm::PointerIntPair has methods that when used together can invoke undefined behavior by violating strict aliasing. `getPointer()` uses the underlying storage as it's declared: `intptr_t` `getAddrOfPointer()` casts the underlying storage as if it was a `PointerTy` This violates strict aliasing, so depending on how they are used, it's possible to have the compiler to optimize the code in unwanted ways. See the unit test in the patch. We declare a `PointerIntPair` and use the `getAddrOfPointer` method to fill in the a pointer value. Then, when we use `getPointer` the compiler is thrown off, thinking that `intptr_t` storage could not have possibly be changed, and the check fails. Reviewed By: efriedma Differential Revision: https://reviews.llvm.org/D124571
131 lines
3.7 KiB
C++
131 lines
3.7 KiB
C++
//===- llvm/unittest/ADT/PointerIntPairTest.cpp - Unit tests --------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ADT/PointerIntPair.h"
|
|
#include "gtest/gtest.h"
|
|
#include <limits>
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
TEST(PointerIntPairTest, GetSet) {
|
|
struct S {
|
|
int i;
|
|
};
|
|
S s;
|
|
|
|
PointerIntPair<S *, 2> Pair(&s, 1U);
|
|
EXPECT_EQ(&s, Pair.getPointer());
|
|
EXPECT_EQ(1U, Pair.getInt());
|
|
|
|
Pair.setInt(2);
|
|
EXPECT_EQ(&s, Pair.getPointer());
|
|
EXPECT_EQ(2U, Pair.getInt());
|
|
|
|
Pair.setPointer(nullptr);
|
|
EXPECT_EQ(nullptr, Pair.getPointer());
|
|
EXPECT_EQ(2U, Pair.getInt());
|
|
|
|
Pair.setPointerAndInt(&s, 3U);
|
|
EXPECT_EQ(&s, Pair.getPointer());
|
|
EXPECT_EQ(3U, Pair.getInt());
|
|
|
|
// Make sure that we can perform all of our operations on enum classes.
|
|
//
|
|
// The concern is that enum classes are only explicitly convertible to
|
|
// integers. This means that if we assume in PointerIntPair this, a
|
|
// compilation error will result. This group of tests exercises the enum class
|
|
// code to make sure that we do not run into such issues in the future.
|
|
enum class E : unsigned {
|
|
Case1,
|
|
Case2,
|
|
Case3,
|
|
};
|
|
PointerIntPair<S *, 2, E> Pair2(&s, E::Case1);
|
|
EXPECT_EQ(&s, Pair2.getPointer());
|
|
EXPECT_EQ(E::Case1, Pair2.getInt());
|
|
|
|
Pair2.setInt(E::Case2);
|
|
EXPECT_EQ(&s, Pair2.getPointer());
|
|
EXPECT_EQ(E::Case2, Pair2.getInt());
|
|
|
|
Pair2.setPointer(nullptr);
|
|
EXPECT_EQ(nullptr, Pair2.getPointer());
|
|
EXPECT_EQ(E::Case2, Pair2.getInt());
|
|
|
|
Pair2.setPointerAndInt(&s, E::Case3);
|
|
EXPECT_EQ(&s, Pair2.getPointer());
|
|
EXPECT_EQ(E::Case3, Pair2.getInt());
|
|
|
|
auto [Pointer2, Int2] = Pair2;
|
|
EXPECT_EQ(Pair2.getPointer(), Pointer2);
|
|
EXPECT_EQ(Pair2.getInt(), Int2);
|
|
|
|
static_assert(std::is_trivially_copyable_v<PointerIntPair<S *, 2, E>>,
|
|
"trivially copyable");
|
|
}
|
|
|
|
TEST(PointerIntPairTest, DefaultInitialize) {
|
|
PointerIntPair<float *, 2> Pair;
|
|
EXPECT_EQ(nullptr, Pair.getPointer());
|
|
EXPECT_EQ(0U, Pair.getInt());
|
|
}
|
|
|
|
// In real code this would be a word-sized integer limited to 31 bits.
|
|
struct Fixnum31 {
|
|
uintptr_t Value;
|
|
};
|
|
struct FixnumPointerTraits {
|
|
static inline void *getAsVoidPointer(Fixnum31 Num) {
|
|
return reinterpret_cast<void *>(Num.Value << NumLowBitsAvailable);
|
|
}
|
|
static inline Fixnum31 getFromVoidPointer(void *P) {
|
|
// In real code this would assert that the value is in range.
|
|
return {reinterpret_cast<uintptr_t>(P) >> NumLowBitsAvailable};
|
|
}
|
|
static constexpr int NumLowBitsAvailable =
|
|
std::numeric_limits<uintptr_t>::digits - 31;
|
|
};
|
|
TEST(PointerIntPairTest, ManyUnusedBits) {
|
|
|
|
PointerIntPair<Fixnum31, 1, bool, FixnumPointerTraits> pair;
|
|
EXPECT_EQ((uintptr_t)0, pair.getPointer().Value);
|
|
EXPECT_FALSE(pair.getInt());
|
|
|
|
pair.setPointerAndInt({ 0x7FFFFFFF }, true );
|
|
EXPECT_EQ((uintptr_t)0x7FFFFFFF, pair.getPointer().Value);
|
|
EXPECT_TRUE(pair.getInt());
|
|
|
|
EXPECT_EQ(FixnumPointerTraits::NumLowBitsAvailable - 1,
|
|
(int)PointerLikeTypeTraits<decltype(pair)>::NumLowBitsAvailable);
|
|
|
|
static_assert(std::is_trivially_copyable_v<
|
|
PointerIntPair<Fixnum31, 1, bool, FixnumPointerTraits>>,
|
|
"trivially copyable");
|
|
}
|
|
|
|
TEST(PointerIntPairTest, TypePunning) {
|
|
int I = 0;
|
|
int *IntPtr = &I;
|
|
|
|
int **IntPtrBegin = &IntPtr;
|
|
int **IntPtrEnd = IntPtrBegin + 1;
|
|
|
|
PointerIntPair<int *, 1> Pair;
|
|
int **PairAddr = Pair.getAddrOfPointer();
|
|
|
|
while (IntPtrBegin != IntPtrEnd) {
|
|
*PairAddr = *IntPtrBegin;
|
|
++PairAddr;
|
|
++IntPtrBegin;
|
|
}
|
|
EXPECT_EQ(Pair.getPointer(), IntPtr);
|
|
}
|
|
|
|
} // end anonymous namespace
|