Added speculative inlining of class messages. Currently inlines all that it can, without deciding whether it's sensible.

main
theraven 16 years ago
parent beb8356367
commit 6cac2816cc

@ -61,10 +61,6 @@ namespace
Value *lookupVal = classLookup->getCalledValue()->stripPointerCasts();
if (Function *lookupFunc = dyn_cast<Function>(lookupVal)) {
if (lookupFunc->getName() == "objc_lookup_class") {
GlobalVariable *classNameVar = cast<GlobalVariable>(
classLookup->getOperand(1)->stripPointerCasts());
string className = cast<ConstantArray>(
classNameVar->getInitializer() )->getAsString();
modified = true;
Lookups.push_back(call);
}

@ -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 <string>
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<CallInst*, 16> messages;
if (F->isDeclaration()) { continue; }
SmallVector<CallInst*, 16> 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<CallInst>(b)) {
Instruction *callee =
dyn_cast<Instruction>(call->getCalledValue()->stripPointerCasts());
if (0 == callee) { continue; }
MDNode *messageType = callee->getMetadata(MessageSendMDKind);
if (0 == messageType) { continue; }
messages.push_back(call);
}
}
}
for (SmallVectorImpl<CallInst*>::iterator i=messages.begin(),
e=messages.end() ; e!=i ; i++) {
Instruction *callee =
dyn_cast<Instruction>((*i)->getCalledValue()->stripPointerCasts());
MDNode *messageType = callee->getMetadata(MessageSendMDKind);
StringRef sel = cast<MDString>(messageType->getOperand(0))->getString();
StringRef cls = cast<MDString>(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<ClassMethodInliner> X("gnu-class-method-inline",
"Inline class methods");
}
ModulePass *createClassMethodInliner(void)
{
return new ClassMethodInliner();
}

@ -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<CallInst>(inlineCall)) {
InlineFunction(c, IFI);
} else if (InvokeInst *c = dyn_cast<InvokeInst>(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();
}

@ -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);
}

Loading…
Cancel
Save