LLVM源码阅读之二---PointerIntPair

2022-04-07 16:29:00
标签: llvm pointerintpair template pointerliketypetrait

今天来介绍一下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直接变负数。

但是这个模板类实现成这样,已经太强了。我研究了半天才看明白。


阅读(0) 收藏(0) 转载(0) 举报/Report
相关阅读

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有