今天来介绍一下PointerIntPair这个类。
template IntType = unsigned,
typename PtrTraits = PointerLikeTypeTraits,
typename Info =
PointerIntPairInfo>
class PointerIntPair {
// Used by MSVC visualizer and generally helpful for
debugging/visualizing.
using InfoTy = Info;
intptr_t Value = 0;
它的作用是把一个指针和一个Int合在一起存。
它涉及其他两个模板类,PointerLikeTypeTraits和PointerIntPairInfo
PointerLikeTypeTraits用来决定NumLowBitsAvailable这个值。
以及定义getAsVoidPointer和getFromVoidPointer这两个函数,其实就是相当于把从一个合体对像中得到指针。
它把一个指针中低位部分D用来存整数,上面用来存指针,利用了指针的低位为0,来减小内存占用。
0xFFFFFFFFDDD
用多少位来存Int的逻辑就是由NumLowBitsAvailable和PointerIntPairInfo来决定。
template
struct PointerIntPairInfo {
static_assert(PtrTraits::NumLowBitsAvailable
<</p>
std::numeric_limits::digits,
"cannot use a pointer type that has all bits
free");
static_assert(IntBits <=
PtrTraits::NumLowBitsAvailable,
"PointerIntPair with integer size too large for
pointer");
enum MaskAndShiftConstants : uintptr_t {
/// PointerBitMask - The bits that come from the pointer.
PointerBitMask =
~(uintptr_t)(((intptr_t)1 <<
PtrTraits::NumLowBitsAvailable) - 1),
/// IntShift - The number of low bits that we reserve for other
uses, and
/// keep zero.
IntShift =
(uintptr_t)PtrTraits::NumLowBitsAvailable - IntBits,
/// IntMask - This is the unshifted mask for valid bits of the
int type.
IntMask = (uintptr_t)(((intptr_t)1
<< IntBits) - 1),
// ShiftedIntMask - This is the bits for the integer shifted in
place.
ShiftedIntMask = (uintptr_t)(IntMask
<< IntShift)
};
static PointerT getPointer(intptr_t Value) {
return PtrTraits::getFromVoidPointer(
reinterpret_cast(Value &
PointerBitMask));
}
static intptr_t getInt(intptr_t Value) {
return (Value >> IntShift)
& IntMask;
}
static intptr_t updatePointer(intptr_t OrigValue, PointerT Ptr)
{
intptr_t PtrWord =
reinterpret_cast(PtrTraits::getAsVoidPointer(Ptr));
assert((PtrWord & ~PointerBitMask) == 0
&&
"Pointer is not sufficiently
aligned");
// Preserve all low bits, just update the pointer.
return PtrWord | (OrigValue &
~PointerBitMask);
}
static intptr_t updateInt(intptr_t OrigValue, intptr_t Int)
{
intptr_t IntWord =
static_cast(Int);
assert((IntWord & ~IntMask) == 0
&& "Integer too large for
field");
// Preserve all bits other than the ones we are updating.
return (OrigValue & ~ShiftedIntMask) | IntWord
<< IntShift;
}
};
这里的结构。NumLowBitsAvailable决定低位有多少可用。IntBits是类的第二个参数,也是PointerIntPair的第二个参数。它的意思是现在用几位。
enum MaskAndShiftConstants : uintptr_t {
/// PointerBitMask - The bits that come from the pointer.
PointerBitMask =
~(uintptr_t)(((intptr_t)1 <<
PtrTraits::NumLowBitsAvailable) - 1),
/// IntShift - The number of low bits that we reserve for other
uses, and
/// keep zero.
IntShift =
(uintptr_t)PtrTraits::NumLowBitsAvailable - IntBits,
/// IntMask - This is the unshifted mask for valid bits of the
int type.
IntMask = (uintptr_t)(((intptr_t)1
<< IntBits) - 1),
// ShiftedIntMask - This is the bits for the integer shifted in
place.
ShiftedIntMask = (uintptr_t)(IntMask
<< IntShift)
};
说明了低位整数用几位。
struct S {
int i;
};
S s;
PointerIntPair Pair(&s,
1U);
这段是ADTTests里的代码,用了google test 测llvm代码。
这就是说低位保留两位可用。这时IntShift = 0,也就是没有保留空间存别的值。
PointerIntPair Pair(&s,
3U);
这行代码就报错了,因为只有1位存整数,只能是0或者1.
PointerIntPair Pair(&s,
1U);
这时,本来可以有两位空间。而int只用1位。这时并不会用最低位,而是用#1位。
template <> struct
PointerLikeTypeTraits {
static inline void *getAsVoidPointer(void *P) { return P;
}
static inline void *getFromVoidPointer(void *P) { return
P; }
/// Note, we assume here that void* is related to raw
malloc'ed memory and
/// that malloc returns objects at least 4-byte aligned.
However, this may be
/// wrong, or pointers may be from something other than
malloc. In this case,
/// you should specify a real typed pointer or avoid this
template.
///
/// All clients should use assertions to do a run-time
check to ensure that
/// this is actually true.
static constexpr int NumLowBitsAvailable = 2;
};
因为还要处理
PointerIntPair, 1, bool>
这时候两个bool各用一位,如果每次用最低位,就重合了。
所以这时,巧妙得利用了
0xFFFFFFFFED
0xFFFFFFFFEE
第一次设置在E位
Value intptr_t 0x0000000305d16ea2 PointerIntPair
Pair(&s, 1U);
Value intptr_t 0x0000000305d16ea1 PointerIntPair
Pair(&s, 1U);
要实现PointerIntPair, 1, bool>
构造的时候就要用PointerLikeTypeTraits>
这时就把模板内层参数映射到外层。
// Teach SmallPtrSet that PointerIntPair is "basically a
pointer".
template
typename PtrTraits>
struct
PointerLikeTypeTraits<</span>
PointerIntPair> {
static inline void *
getAsVoidPointer(const PointerIntPair &P)
{
return P.getOpaqueValue();
}
static inline PointerIntPair
getFromVoidPointer(void *P) {
return PointerIntPair::getFromOpaqueValue(P);
}
static inline PointerIntPair
getFromVoidPointer(const void *P) {
return PointerIntPair::getFromOpaqueValue(P);
}
static constexpr int NumLowBitsAvailable =
PtrTraits::NumLowBitsAvailable - IntBits;
};
这个是在/Users/conanchen/llvm-project/llvm/include/llvm/ADT/PointerIntPair.h中实现的特例化。
它实现了PointerIntPair的嵌套,并且能自动通过里面一层的NumLowBitsAvailable来合并计算还有多少位可用。
不过这地方没提示报错,哪怕你多用了,比如最多2位可用,而你用了4位。这样外层NumLowBitsAvailable直接变负数。
但是这个模板类实现成这样,已经太强了。我研究了半天才看明白。
LLVM源码阅读之二---PointerIntPair
今天来介绍一下PointerIntPair这个类。
template IntType = unsigned,
typename PtrTraits = PointerLikeTypeTraits,
typename Info = PointerIntPairInfo>
class PointerIntPair {
// Used by MSVC visualizer and generally helpful for debugging/visualizing.
using InfoTy = Info;
intptr_t Value = 0;
它的作用是把一个指针和一个Int合在一起存。
它涉及其他两个模板类,PointerLikeTypeTraits和PointerIntPairInfo
PointerLikeTypeTraits用来决定NumLowBitsAvailable这个值。
以及定义getAsVoidPointer和getFromVoidPointer这两个函数,其实就是相当于把从一个合体对像中得到指针。
它把一个指针中低位部分D用来存整数,上面用来存指针,利用了指针的低位为0,来减小内存占用。
0xFFFFFFFFDDD
用多少位来存Int的逻辑就是由NumLowBitsAvailable和PointerIntPairInfo来决定。
template
struct PointerIntPairInfo {
static_assert(PtrTraits::NumLowBitsAvailable <</p>
std::numeric_limits::digits,
"cannot use a pointer type that has all bits free");
static_assert(IntBits <= PtrTraits::NumLowBitsAvailable,
"PointerIntPair with integer size too large for pointer");
enum MaskAndShiftConstants : uintptr_t {
/// PointerBitMask - The bits that come from the pointer.
PointerBitMask =
~(uintptr_t)(((intptr_t)1 << PtrTraits::NumLowBitsAvailable) - 1),
/// IntShift - The number of low bits that we reserve for other uses, and
/// keep zero.
IntShift = (uintptr_t)PtrTraits::NumLowBitsAvailable - IntBits,
/// IntMask - This is the unshifted mask for valid bits of the int type.
IntMask = (uintptr_t)(((intptr_t)1 << IntBits) - 1),
// ShiftedIntMask - This is the bits for the integer shifted in place.
ShiftedIntMask = (uintptr_t)(IntMask << IntShift)
};
static PointerT getPointer(intptr_t Value) {
return PtrTraits::getFromVoidPointer(
reinterpret_cast(Value & PointerBitMask));
}
static intptr_t getInt(intptr_t Value) {
return (Value >> IntShift) & IntMask;
}
static intptr_t updatePointer(intptr_t OrigValue, PointerT Ptr) {
intptr_t PtrWord =
reinterpret_cast(PtrTraits::getAsVoidPointer(Ptr));
assert((PtrWord & ~PointerBitMask) == 0 &&
"Pointer is not sufficiently aligned");
// Preserve all low bits, just update the pointer.
return PtrWord | (OrigValue & ~PointerBitMask);
}
static intptr_t updateInt(intptr_t OrigValue, intptr_t Int) {
intptr_t IntWord = static_cast(Int);
assert((IntWord & ~IntMask) == 0 && "Integer too large for field");
// Preserve all bits other than the ones we are updating.
return (OrigValue & ~ShiftedIntMask) | IntWord << IntShift;
}
};
这里的结构。NumLowBitsAvailable决定低位有多少可用。IntBits是类的第二个参数,也是PointerIntPair的第二个参数。它的意思是现在用几位。
enum MaskAndShiftConstants : uintptr_t {
/// PointerBitMask - The bits that come from the pointer.
PointerBitMask =
~(uintptr_t)(((intptr_t)1 << PtrTraits::NumLowBitsAvailable) - 1),
/// IntShift - The number of low bits that we reserve for other uses, and
/// keep zero.
IntShift = (uintptr_t)PtrTraits::NumLowBitsAvailable - IntBits,
/// IntMask - This is the unshifted mask for valid bits of the int type.
IntMask = (uintptr_t)(((intptr_t)1 << IntBits) - 1),
// ShiftedIntMask - This is the bits for the integer shifted in place.
ShiftedIntMask = (uintptr_t)(IntMask << IntShift)
};
说明了低位整数用几位。
struct S {
int i;
};
S s;
PointerIntPair
Pair(&s, 1U);这段是ADTTests里的代码,用了google test 测llvm代码。
这就是说低位保留两位可用。这时IntShift = 0,也就是没有保留空间存别的值。
PointerIntPair
Pair(&s, 3U);这行代码就报错了,因为只有1位存整数,只能是0或者1.
PointerIntPair
Pair(&s, 1U);这时,本来可以有两位空间。而int只用1位。这时并不会用最低位,而是用#1位。
template <> struct PointerLikeTypeTraits {
static inline void *getAsVoidPointer(void *P) { return P; }
static inline void *getFromVoidPointer(void *P) { return P; }
/// Note, we assume here that void* is related to raw malloc'ed memory and
/// that malloc returns objects at least 4-byte aligned. However, this may be
/// wrong, or pointers may be from something other than malloc. In this case,
/// you should specify a real typed pointer or avoid this template.
///
/// All clients should use assertions to do a run-time check to ensure that
/// this is actually true.
static constexpr int NumLowBitsAvailable = 2;
};
因为还要处理
PointerIntPair, 1, bool>
这时候两个bool各用一位,如果每次用最低位,就重合了。
所以这时,巧妙得利用了
0xFFFFFFFFED
0xFFFFFFFFEE
第一次设置在E位
Value intptr_t 0x0000000305d16ea2 PointerIntPair
Pair(&s, 1U);Value intptr_t 0x0000000305d16ea1 PointerIntPair
Pair(&s, 1U);要实现PointerIntPair, 1, bool>
构造的时候就要用PointerLikeTypeTraits> 这时就把模板内层参数映射到外层。
// Teach SmallPtrSet that PointerIntPair is "basically a pointer".
template
typename PtrTraits>
struct PointerLikeTypeTraits<</span>
PointerIntPair> {
static inline void *
getAsVoidPointer(const PointerIntPair &P) {
return P.getOpaqueValue();
}
static inline PointerIntPair
getFromVoidPointer(void *P) {
return PointerIntPair::getFromOpaqueValue(P);
}
static inline PointerIntPair
getFromVoidPointer(const void *P) {
return PointerIntPair::getFromOpaqueValue(P);
}
static constexpr int NumLowBitsAvailable =
PtrTraits::NumLowBitsAvailable - IntBits;
};
这个是在/Users/conanchen/llvm-project/llvm/include/llvm/ADT/PointerIntPair.h中实现的特例化。
它实现了PointerIntPair的嵌套,并且能自动通过里面一层的NumLowBitsAvailable来合并计算还有多少位可用。
不过这地方没提示报错,哪怕你多用了,比如最多2位可用,而你用了4位。这样外层NumLowBitsAvailable直接变负数。
但是这个模板类实现成这样,已经太强了。我研究了半天才看明白。