2016年1月7日 星期四

[Quick Note] Type instances in LLVM IR

The story starts while I was writing a LLVM module pass few days ago.
I was trying to add null pointer initializer for a global variable, the ConstantPointerNull class need a PointerType as parameter, but I accidentally assigned a wrong address space (we're target on CL language) while creating PointerType instance. Then I found the error message pretty interesting:
opt: /my/llvm/path/lib/IR/Globals.cpp:209: void llvm::GlobalVariable::setInitializer(llvm::Constant*): Assertion `InitVal->getType() == getType()->getElementType() && "Initializer type must match GlobalVariable type"' failed.
The question is: Why comparing two pointers?  Because when we're talking about representation for Type, the straightforward way would be a simple Type(or its derived class) class instance. So why it compares two pointers instead of instances?

The first possibility came to my mind was operator overloading on "==" for Type*. But sadly, there isn't such overloading for Type and its derived classes. Finally, it turns out that LLVM "caches" all of the Type instances in the LLVMContext class(actually LLVMContextImpl, which is not exported). One of the evidences is the static factory methods for creating Type instances, for example, getInt8PtrTy:
static PointerType * getInt8PtrTy (LLVMContext &C, unsigned AS=0)
We need to pass a LLVMContext instance as a parameter. So when creating Type instances, it would grab from LLVMContext instead of creating a new one. For example, the FunctionType factory method(/lib/IR/Type.cpp: 360~379):
 
// FunctionType::get - The factory function for the FunctionType class.
FunctionType *FunctionType::get(Type *ReturnType,
                                ArrayRef<Type*> Params, bool isVarArg) {
  LLVMContextImpl *pImpl = ReturnType->getContext().pImpl;
  FunctionTypeKeyInfo::KeyTy Key(ReturnType, Params, isVarArg);
  auto I = pImpl->FunctionTypes.find_as(Key);
  FunctionType *FT;

  if (I == pImpl->FunctionTypes.end()) {
    FT = (FunctionType*) pImpl->TypeAllocator.
      Allocate(sizeof(FunctionType) + sizeof(Type*) * (Params.size() + 1),
               AlignOf<FunctionType>::Alignment);
    new (FT) FunctionType(ReturnType, Params, isVarArg);
    pImpl->FunctionTypes.insert(FT);
  } else {
    FT = *I;
  }

  return FT;
}
It first get the LLVMContextImpl instance, pImpl, and retrieves the storage for FunctionType, FunctionTypes, which declaration is here(/lib/IR/LLVMContextImpl.h: 1003~1004):
 
typedef DenseSet<FunctionType *, FunctionTypeKeyInfo> FunctionTypeSet;
FunctionTypeSet FunctionTypes;
I'm not pretty sure why LLVM want to do this. I guess they want to reduce the memory footprint due to the heavy usages of Type class in IR operations.


沒有留言:

張貼留言