diff --git a/opts/ClassIMPCache.cpp b/opts/ClassIMPCache.cpp index fc7cf33..354a526 100644 --- a/opts/ClassIMPCache.cpp +++ b/opts/ClassIMPCache.cpp @@ -61,10 +61,6 @@ namespace Value *lookupVal = classLookup->getCalledValue()->stripPointerCasts(); if (Function *lookupFunc = dyn_cast(lookupVal)) { if (lookupFunc->getName() == "objc_lookup_class") { - GlobalVariable *classNameVar = cast( - classLookup->getOperand(1)->stripPointerCasts()); - string className = cast( - classNameVar->getInitializer() )->getAsString(); modified = true; Lookups.push_back(call); } diff --git a/opts/ClassMethodInliner.cpp b/opts/ClassMethodInliner.cpp new file mode 100644 index 0000000..f3a6786 --- /dev/null +++ b/opts/ClassMethodInliner.cpp @@ -0,0 +1,99 @@ +#include "llvm/Pass.h" +#include "llvm/Function.h" +#include "llvm/Module.h" +#include "llvm/Instructions.h" +#include "llvm/Constants.h" +#include "llvm/LLVMContext.h" +#include "llvm/GlobalVariable.h" +#include "llvm/Support/IRBuilder.h" +#include "llvm/Analysis/LoopInfo.h" +#include "IMPCacher.h" +#include + +using namespace llvm; +using std::string; + +// Mangle a method name +// +// From clang: +static std::string SymbolNameForMethod(const std::string &ClassName, const + std::string &CategoryName, const std::string &MethodName, bool isClassMethod) +{ + std::string MethodNameColonStripped = MethodName; + std::replace(MethodNameColonStripped.begin(), MethodNameColonStripped.end(), + ':', '_'); + return std::string(isClassMethod ? "_c_" : "_i_") + ClassName + "_" + + CategoryName + "_" + MethodNameColonStripped; +} + +namespace +{ + class ClassMethodInliner : public ModulePass + { + const IntegerType *IntTy; + + public: + static char ID; + ClassMethodInliner() : ModulePass((intptr_t)&ID) {} + + virtual bool runOnModule(Module &M) { + unsigned MessageSendMDKind = M.getContext().getMDKindID("GNUObjCMessageSend"); + + GNUstep::IMPCacher cacher = GNUstep::IMPCacher(M.getContext(), this); + // FIXME: ILP64 + IntTy = Type::getInt32Ty(M.getContext()); + bool modified = false; + + for (Module::iterator F=M.begin(), fend=M.end() ; + F != fend ; ++F) { + + SmallVector messages; + + if (F->isDeclaration()) { continue; } + + SmallVector Lookups; + + for (Function::iterator i=F->begin(), end=F->end() ; + i != end ; ++i) { + for (BasicBlock::iterator b=i->begin(), last=i->end() ; + b != last ; ++b) { + // FIXME: InvokeInst + if (CallInst *call = dyn_cast(b)) { + Instruction *callee = + dyn_cast(call->getCalledValue()->stripPointerCasts()); + if (0 == callee) { continue; } + MDNode *messageType = callee->getMetadata(MessageSendMDKind); + if (0 == messageType) { continue; } + messages.push_back(call); + } + } + } + for (SmallVectorImpl::iterator i=messages.begin(), + e=messages.end() ; e!=i ; i++) { + + Instruction *callee = + dyn_cast((*i)->getCalledValue()->stripPointerCasts()); + MDNode *messageType = callee->getMetadata(MessageSendMDKind); + StringRef sel = cast(messageType->getOperand(0))->getString(); + StringRef cls = cast(messageType->getOperand(1))->getString(); + StringRef functionName = SymbolNameForMethod(cls, "", sel, true); + Function *method = M.getFunction(functionName); + + if (0 == method || method->isDeclaration()) { continue; } + + cacher.SpeculativelyInline(*i, method); + } + } + return modified; + } + }; + + char ClassMethodInliner::ID = 0; + RegisterPass X("gnu-class-method-inline", + "Inline class methods"); +} + +ModulePass *createClassMethodInliner(void) +{ + return new ClassMethodInliner(); +} diff --git a/opts/IMPCacher.cpp b/opts/IMPCacher.cpp index 2400f07..724e857 100644 --- a/opts/IMPCacher.cpp +++ b/opts/IMPCacher.cpp @@ -1,12 +1,15 @@ #include "IMPCacher.h" #include "llvm/Pass.h" #include "llvm/Function.h" +#include "llvm/Module.h" #include "llvm/Instructions.h" #include "llvm/Constants.h" #include "llvm/LLVMContext.h" #include "llvm/Metadata.h" #include "llvm/Support/IRBuilder.h" +#include "llvm/Support/CallSite.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Cloning.h" GNUstep::IMPCacher::IMPCacher(LLVMContext &C, Pass *owner) : Context(C), Owner(owner) { @@ -27,13 +30,14 @@ void GNUstep::IMPCacher::CacheLookup(CallInst *lookup, Value *slot, Value if (lookup->getMetadata(IMPCacheFlagKind)) { return; } lookup->setMetadata(IMPCacheFlagKind, AlreadyCachedFlag); + 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(); + removeTerminator(beforeLookupBB); IRBuilder<> B = IRBuilder<>(beforeLookupBB); // Load the slot and check that neither it nor the version is 0. @@ -77,7 +81,7 @@ void GNUstep::IMPCacher::CacheLookup(CallInst *lookup, Value *slot, Value lookup->replaceAllUsesWith(B.CreateLoad(slot)); // Perform the real lookup and cache the result - lookupBB->getTerminator()->removeFromParent(); + removeTerminator(lookupBB); B.SetInsertPoint(lookupBB); Value * newReceiver = B.CreateLoad(receiverPtr); BasicBlock *storeCacheBB = BasicBlock::Create(Context, "cache_store", @@ -97,3 +101,70 @@ void GNUstep::IMPCacher::CacheLookup(CallInst *lookup, Value *slot, Value B.CreateStore(cls, B.CreateStructGEP(lookup, 1)); B.CreateBr(afterLookupBB); } + + +void GNUstep::IMPCacher::SpeculativelyInline(Instruction *call, Function + *function) { + BasicBlock *beforeCallBB = call->getParent(); + BasicBlock *callBB = SplitBlock(beforeCallBB, call, Owner); + BasicBlock *inlineBB = BasicBlock::Create(Context, "inline", + callBB->getParent()); + + + BasicBlock::iterator iter = call; + iter++; + + BasicBlock *afterCallBB = SplitBlock(iter->getParent(), iter, Owner); + + removeTerminator(beforeCallBB); + + // Put a branch before the call, testing whether the callee really is the + // function + IRBuilder<> B = IRBuilder<>(beforeCallBB); + Value *callee = call->getOperand(0); + if (callee->getType() != function->getType()) { + callee = B.CreateBitCast(callee, function->getType()); + } + Value *isInlineValid = B.CreateICmpEQ(callee, function); + B.CreateCondBr(isInlineValid, inlineBB, callBB); + + // In the inline BB, add a copy of the call, but this time calling the real + // version. + Instruction *inlineCall = call->clone(); + inlineBB->getInstList().push_back(inlineCall); + inlineCall->setOperand(0, function); + + B.SetInsertPoint(inlineBB); + B.CreateBr(afterCallBB); + + // Unify the return values + if (call->getType() != Type::getVoidTy(Context)) { + B.SetInsertPoint(afterCallBB, afterCallBB->begin()); + PHINode *phi = B.CreatePHI(call->getType()); + call->replaceAllUsesWith(phi); + phi->addIncoming(call, callBB); + phi->addIncoming(inlineCall, inlineBB); + } + + // Really do the real inlining + InlineFunctionInfo IFI(0, 0); + if (CallInst *c = dyn_cast(inlineCall)) { + InlineFunction(c, IFI); + } else if (InvokeInst *c = dyn_cast(inlineCall)) { + InlineFunction(c, IFI); + } +} + +// Cleanly removes a terminator instruction. +void GNUstep::removeTerminator(BasicBlock *BB) { + TerminatorInst *BBTerm = BB->getTerminator(); + + // Remove the BB as a predecessor from all of successors + for (unsigned i = 0, e = BBTerm->getNumSuccessors(); i != e; ++i) { + BBTerm->getSuccessor(i)->removePredecessor(BB); + } + + BBTerm->replaceAllUsesWith(UndefValue::get(BBTerm->getType())); + // Remove the terminator instruction itself. + BBTerm->eraseFromParent(); +} diff --git a/opts/IMPCacher.h b/opts/IMPCacher.h index 5403e70..028a116 100644 --- a/opts/IMPCacher.h +++ b/opts/IMPCacher.h @@ -1,6 +1,9 @@ namespace llvm { + class BasicBlock; class CallInst; + class Function; + class Instruction; class IntegerType; class LLVMContext; class MDNode; @@ -26,5 +29,8 @@ namespace GNUstep public: IMPCacher(LLVMContext &C, Pass *owner); void CacheLookup(CallInst *lookup, Value *slot, Value *version); + void SpeculativelyInline(Instruction *call, Function *function); }; + + void removeTerminator(BasicBlock *BB); }