From dfc4a0286b8547e3e056021ebc557be4cdba7344 Mon Sep 17 00:00:00 2001 From: theraven Date: Wed, 28 Apr 2010 11:38:31 +0000 Subject: [PATCH] Factored out code that performs IMP caching into a separate class. This can now be used by other passes that cache IMPs outside of loops (for example, always caching class messages because they always have the same receiver). --- opts/IMPCacher.cpp | 76 ++++++++++++++++++++++++++++ opts/IMPCacher.h | 31 ++++++++++++ opts/LoopIMPCachePass.cpp | 104 +++++++------------------------------- 3 files changed, 125 insertions(+), 86 deletions(-) create mode 100644 opts/IMPCacher.cpp create mode 100644 opts/IMPCacher.h diff --git a/opts/IMPCacher.cpp b/opts/IMPCacher.cpp new file mode 100644 index 0000000..8de5dfc --- /dev/null +++ b/opts/IMPCacher.cpp @@ -0,0 +1,76 @@ +#include "llvm/Pass.h" +#include "llvm/Function.h" +#include "llvm/Module.h" +#include "llvm/Instructions.h" +#include "llvm/GlobalAlias.h" +#include "llvm/GlobalVariable.h" +#include "llvm/Constants.h" +#include "llvm/Support/IRBuilder.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "IMPCacher.h" + +void GNUstep::IMPCacher::CacheLookup(CallInst *lookup, Value *slot, Value + *version) { + + BasicBlock *beforeLookupBB = lookup->getParent(); + BasicBlock *lookupBB = SplitBlock(beforeLookupBB, lookup, Owner); + BasicBlock::iterator iter = lookup; + iter++; + BasicBlock *afterLookupBB = SplitBlock(iter->getParent(), iter, Owner); + + beforeLookupBB->getTerminator()->removeFromParent(); + + IRBuilder<> B = IRBuilder<>(beforeLookupBB); + // Load the slot and check that neither it nor the version is 0. + Value *slotValue = B.CreateLoad(slot); + Value *versionValue = B.CreateLoad(version); + Value *receiver = lookup->getOperand(1); + + Value *isCacheEmpty = + B.CreateOr(versionValue, B.CreatePtrToInt(slotValue, IntTy)); + isCacheEmpty = + B.CreateICmpEQ(isCacheEmpty, Constant::getNullValue(IntTy)); + Value *receiverNotNil = + B.CreateICmpNE(receiver, Constant::getNullValue(receiver->getType())); + isCacheEmpty = B.CreateAnd(isCacheEmpty, receiverNotNil); + + BasicBlock *cacheLookupBB = BasicBlock::Create(Context, "cache_check", + lookupBB->getParent()); + + B.CreateCondBr(isCacheEmpty, lookupBB, cacheLookupBB); + + // Check the cache node is current + B.SetInsertPoint(cacheLookupBB); + Value *slotVersion = B.CreateStructGEP(slotValue, 3); + // Note: Volatile load because the slot version might have changed in + // another thread. + slotVersion = B.CreateLoad(slotVersion, true, "slot_version"); + Value *slotCachedFor = B.CreateStructGEP(slotValue, 1); + slotCachedFor = B.CreateLoad(slotCachedFor, true, "slot_owner"); + Value *cls = B.CreateLoad(B.CreateBitCast(receiver, IdTy)); + Value *isVersionCorrect = B.CreateICmpEQ(slotVersion, versionValue); + Value *isOwnerCorrect = B.CreateICmpEQ(slotCachedFor, cls); + Value *isSlotValid = B.CreateAnd(isVersionCorrect, isOwnerCorrect); + // If this slot is still valid, skip the lookup. + B.CreateCondBr(isSlotValid, afterLookupBB, lookupBB); + + // Replace the looked up slot with the loaded one + B.SetInsertPoint(afterLookupBB, afterLookupBB->begin()); + // Not volatile, so a redundant load elimination pass can do some phi + // magic with this later. + lookup->replaceAllUsesWith(B.CreateLoad(slot)); + + // Perform the real lookup and cache the result + lookupBB->getTerminator()->removeFromParent(); + B.SetInsertPoint(lookupBB); + // Store it even if the version is 0, because we always check that the + // version is not 0 at the start and an occasional redundant store is + // probably better than a branch every time. + B.CreateStore(lookup, slot); + B.CreateStore(B.CreateLoad(B.CreateStructGEP(lookup, 3)), version); + cls = B.CreateLoad(B.CreateBitCast(receiver, IdTy)); + B.CreateStore(cls, B.CreateStructGEP(lookup, 1)); + B.CreateBr(afterLookupBB); +} + diff --git a/opts/IMPCacher.h b/opts/IMPCacher.h new file mode 100644 index 0000000..aa4a093 --- /dev/null +++ b/opts/IMPCacher.h @@ -0,0 +1,31 @@ +namespace llvm +{ + class LLVMContext; + class Pass; + class PointerType; + class IntegerType; + class CallInst; +} + +using namespace llvm; + +namespace GNUstep +{ + class IMPCacher + { + private: + LLVMContext &Context; + Pass *Owner; + const PointerType *PtrTy; + const PointerType *IdTy; + const IntegerType *IntTy; + public: + IMPCacher(LLVMContext &C, Pass *owner) : Context(C), Owner(owner) { + PtrTy = Type::getInt8PtrTy(Context); + // FIXME: 64-bit. + IntTy = Type::getInt32Ty(Context); + IdTy = PointerType::getUnqual(PtrTy); + } + void CacheLookup(CallInst *lookup, Value *slot, Value *version); + }; +} diff --git a/opts/LoopIMPCachePass.cpp b/opts/LoopIMPCachePass.cpp index 2324eb2..c931ddd 100644 --- a/opts/LoopIMPCachePass.cpp +++ b/opts/LoopIMPCachePass.cpp @@ -8,6 +8,7 @@ #include "llvm/Support/IRBuilder.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "IMPCacher.h" #include using namespace llvm; @@ -17,104 +18,26 @@ namespace { class GNULoopIMPCachePass : public FunctionPass { + GNUstep::IMPCacher *cacher; + const IntegerType *IntTy; public: static char ID; GNULoopIMPCachePass() : FunctionPass((intptr_t)&ID) {} - - Module *M; - LLVMContext *Context; - const PointerType *PtrTy; - const PointerType *IdTy; - const IntegerType *IntTy; + ~GNULoopIMPCachePass() { delete cacher; } virtual bool doInitialization(Module &Mod) { - M = &Mod; - Context = &M->getContext(); - PtrTy = Type::getInt8PtrTy(*Context); - // FIXME: 64-bit. - IntTy = Type::getInt32Ty(*Context); - IdTy = PointerType::getUnqual(PtrTy); + cacher = new GNUstep::IMPCacher(Mod.getContext(), this); + // FIXME: ILP64 + IntTy = Type::getInt32Ty(Mod.getContext()); return false; } + virtual void getAnalysisUsage(AnalysisUsage &Info) const { Info.addRequired(); } - // TODO: Move this to a helper class. - void CacheLookup(CallInst *lookup, BasicBlock *allocaBlock) { - const Type *SlotPtrTy = lookup->getType(); - - IRBuilder<> B = IRBuilder<>(allocaBlock); - B.SetInsertPoint(allocaBlock, allocaBlock->begin()); - Value *slot = B.CreateAlloca(SlotPtrTy, 0, "slot"); - Value *version = B.CreateAlloca(IntTy, 0, "slot_version"); - - B.SetInsertPoint(allocaBlock, allocaBlock->getTerminator()); - B.CreateStore(Constant::getNullValue(SlotPtrTy), slot); - B.CreateStore(Constant::getNullValue(IntTy), version); - - BasicBlock *beforeLookupBB = lookup->getParent(); - BasicBlock *lookupBB = SplitBlock(beforeLookupBB, lookup, this); - BasicBlock::iterator iter = lookup; - iter++; - BasicBlock *afterLookupBB = SplitBlock(iter->getParent(), iter, this); - - beforeLookupBB->getTerminator()->removeFromParent(); - B.SetInsertPoint(beforeLookupBB); - // Load the slot and check that neither it nor the version is 0. - Value *slotValue = B.CreateLoad(slot); - Value *versionValue = B.CreateLoad(version); - Value *receiver = lookup->getOperand(1); - - Value *isCacheEmpty = - B.CreateOr(versionValue, B.CreatePtrToInt(slotValue, IntTy)); - isCacheEmpty = - B.CreateICmpEQ(isCacheEmpty, Constant::getNullValue(IntTy)); - Value *receiverNotNil = - B.CreateICmpNE(receiver, Constant::getNullValue(receiver->getType())); - isCacheEmpty = B.CreateAnd(isCacheEmpty, receiverNotNil); - - BasicBlock *cacheLookupBB = BasicBlock::Create(*Context, "cache_check", - allocaBlock->getParent()); - - B.CreateCondBr(isCacheEmpty, lookupBB, cacheLookupBB); - - // Check the cache node is current - B.SetInsertPoint(cacheLookupBB); - Value *slotVersion = B.CreateStructGEP(slotValue, 3); - // Note: Volatile load because the slot version might have changed in - // another thread. - slotVersion = B.CreateLoad(slotVersion, true, "slot_version"); - Value *slotCachedFor = B.CreateStructGEP(slotValue, 1); - slotCachedFor = B.CreateLoad(slotCachedFor, true, "slot_owner"); - Value *cls = B.CreateLoad(B.CreateBitCast(receiver, IdTy)); - Value *isVersionCorrect = B.CreateICmpEQ(slotVersion, versionValue); - Value *isOwnerCorrect = B.CreateICmpEQ(slotCachedFor, cls); - Value *isSlotValid = B.CreateAnd(isVersionCorrect, isOwnerCorrect); - // If this slot is still valid, skip the lookup. - B.CreateCondBr(isSlotValid, afterLookupBB, lookupBB); - - // Replace the looked up slot with the loaded one - B.SetInsertPoint(afterLookupBB, afterLookupBB->begin()); - // Not volatile, so a redundant load elimination pass can do some phi - // magic with this later. - lookup->replaceAllUsesWith(B.CreateLoad(slot)); - - // Perform the real lookup and cache the result - lookupBB->getTerminator()->removeFromParent(); - B.SetInsertPoint(lookupBB); - // Store it even if the version is 0, because we always check that the - // version is not 0 at the start and an occasional redundant store is - // probably better than a branch every time. - B.CreateStore(lookup, slot); - B.CreateStore(B.CreateLoad(B.CreateStructGEP(lookup, 3)), version); - cls = B.CreateLoad(B.CreateBitCast(receiver, IdTy)); - B.CreateStore(cls, B.CreateStructGEP(lookup, 1)); - B.CreateBr(afterLookupBB); - } - virtual bool runOnFunction(Function &F) { LoopInfo &LI = getAnalysis(); bool modified = false; @@ -138,9 +61,18 @@ namespace } } } + IRBuilder<> B = IRBuilder<>(entry); for (SmallVectorImpl::iterator i=Lookups.begin(), e=Lookups.end() ; e!=i ; i++) { - CacheLookup(*i, entry); + const Type *SlotPtrTy = (*i)->getType(); + B.SetInsertPoint(entry, entry->begin()); + Value *slot = B.CreateAlloca(SlotPtrTy, 0, "slot"); + Value *version = B.CreateAlloca(IntTy, 0, "slot_version"); + + B.SetInsertPoint(entry, entry->getTerminator()); + B.CreateStore(Constant::getNullValue(SlotPtrTy), slot); + B.CreateStore(Constant::getNullValue(IntTy), version); + cacher->CacheLookup(*i, slot, version); } return modified; }