Added unfinished inliner that uses type feedback info to inline instance methods.

Made class method inliner also inline message sends to super.

Various updates caused by clang now attaching more sensible metadata to the IR.
This commit is contained in:
theraven 2010-05-01 13:56:59 +00:00
parent 160d1ce829
commit 6a58d3d49d
6 changed files with 221 additions and 56 deletions

View File

@ -1,9 +1,11 @@
#include "llvm/Pass.h"
#include "llvm/Function.h"
#include "llvm/Module.h"
#include "llvm/LLVMContext.h"
#include "llvm/Instructions.h"
#include "llvm/Constants.h"
#include "llvm/GlobalVariable.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Support/IRBuilder.h"
#include "llvm/Analysis/LoopInfo.h"
#include "IMPCacher.h"
@ -28,53 +30,43 @@ namespace
IntTy = Type::getInt32Ty(M.getContext());
bool modified = false;
unsigned MessageSendMDKind = M.getContext().getMDKindID("GNUObjCMessageSend");
for (Module::iterator F=M.begin(), fend=M.end() ;
F != fend ; ++F) {
if (F->isDeclaration()) { continue; }
SmallVector<CallInst*, 16> Lookups;
SmallVector<std::pair<CallSite, bool>, 16> Lookups;
BasicBlock *entry = &F->getEntryBlock();
for (Function::iterator i=F->begin(), end=F->end() ;
i != end ; ++i) {
for (BasicBlock::iterator b=i->begin(), last=i->end() ;
b != last ; ++b) {
if (CallInst *call = dyn_cast<CallInst>(b)) {
Value *callee = call->getCalledValue()->stripPointerCasts();
CallSite call = CallSite::get(b);
if (call.getInstruction()) {
Value *callee = call.getCalledValue()->stripPointerCasts();
if (Function *func = dyn_cast<Function>(callee)) {
if (func->getName() == "objc_msg_lookup_sender") {
// TODO: Move this to a helper
Value *receiverPtr = call->getOperand(1);
Value *receiver = 0;
// Find where the receiver comes from
for (BasicBlock::iterator start=i->begin(),s=b ; s!=start ; s--) {
if (StoreInst *store = dyn_cast<StoreInst>(s)) {
if (store->getOperand(1) == receiverPtr) {
receiver = store->getOperand(0);
break;
}
}
}
if (0 == receiver) { continue; }
if (CallInst *classLookup = dyn_cast<CallInst>(receiver)) {
Value *lookupVal = classLookup->getCalledValue()->stripPointerCasts();
if (Function *lookupFunc = dyn_cast<Function>(lookupVal)) {
if (lookupFunc->getName() == "objc_lookup_class") {
modified = true;
Lookups.push_back(call);
}
}
MDNode *messageType =
call.getInstruction()->getMetadata(MessageSendMDKind);
if (0 == messageType) { continue; }
if (cast<ConstantInt>(messageType->getOperand(2))->isOne()) {
Lookups.push_back(std::pair<CallSite, bool>(call, false));
}
} else if (func->getName() == "objc_slot_lookup_super") {
Lookups.push_back(std::pair<CallSite, bool>(call, true));
}
}
}
}
}
IRBuilder<> B = IRBuilder<>(entry);
for (SmallVectorImpl<CallInst*>::iterator i=Lookups.begin(),
e=Lookups.end() ; e!=i ; i++) {
const Type *SlotPtrTy = (*i)->getType();
for (SmallVectorImpl<std::pair<CallSite, bool> >::iterator
i=Lookups.begin(), e=Lookups.end() ; e!=i ; i++) {
Instruction *call = i->first.getInstruction();
const Type *SlotPtrTy = call->getType();
Value *slot = new GlobalVariable(M, SlotPtrTy, false,
GlobalValue::PrivateLinkage, Constant::getNullValue(SlotPtrTy),
@ -82,7 +74,7 @@ namespace
Value *version = new GlobalVariable(M, IntTy, false,
GlobalValue::PrivateLinkage, Constant::getNullValue(IntTy),
"version");
cacher.CacheLookup(*i, slot, version);
cacher.CacheLookup(call, slot, version, i->second);
}
}
return modified;

View File

@ -2,6 +2,7 @@
#include "llvm/Function.h"
#include "llvm/Module.h"
#include "llvm/Instructions.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Constants.h"
#include "llvm/LLVMContext.h"
#include "llvm/GlobalVariable.h"
@ -47,41 +48,38 @@ namespace
for (Module::iterator F=M.begin(), fend=M.end() ;
F != fend ; ++F) {
SmallVector<CallInst*, 16> messages;
SmallVector<CallSite, 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);
CallSite call = CallSite::get(b);
if (call.getInstruction()) {
MDNode *messageType = call->getMetadata(MessageSendMDKind);
if (0 == messageType) { continue; }
messages.push_back(call);
}
}
}
for (SmallVectorImpl<CallInst*>::iterator i=messages.begin(),
for (SmallVectorImpl<CallSite>::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);
MDNode *messageType = (*i)->getMetadata(MessageSendMDKind);
StringRef sel =
cast<MDString>(messageType->getOperand(0))->getString();
StringRef cls =
cast<MDString>(messageType->getOperand(1))->getString();
bool isClassMethod =
cast<ConstantInt>(messageType->getOperand(2))->isOne();
StringRef functionName = SymbolNameForMethod(cls, "", sel, isClassMethod);
Function *method = M.getFunction(functionName);
if (0 == method || method->isDeclaration()) { continue; }
cacher.SpeculativelyInline(*i, method);
cacher.SpeculativelyInline((*i).getInstruction(), method);
}
}
return modified;

View File

@ -23,8 +23,8 @@ GNUstep::IMPCacher::IMPCacher(LLVMContext &C, Pass *owner) : Context(C),
IMPCacheFlagKind = Context.getMDKindID("IMPCache");
}
void GNUstep::IMPCacher::CacheLookup(CallInst *lookup, Value *slot, Value
*version) {
void GNUstep::IMPCacher::CacheLookup(Instruction *lookup, Value *slot, Value
*version, bool isSuperMessage) {
// If this IMP is already cached, don't cache it again.
if (lookup->getMetadata(IMPCacheFlagKind)) { return; }
@ -44,7 +44,10 @@ void GNUstep::IMPCacher::CacheLookup(CallInst *lookup, Value *slot, Value
Value *slotValue = B.CreateLoad(slot);
Value *versionValue = B.CreateLoad(version);
Value *receiverPtr = lookup->getOperand(1);
Value *receiver = B.CreateLoad(receiverPtr);
Value *receiver = receiverPtr;
if (!isSuperMessage) {
receiver = B.CreateLoad(receiverPtr);
}
Value *isCacheEmpty =
B.CreateOr(versionValue, B.CreatePtrToInt(slotValue, IntTy));
@ -83,7 +86,10 @@ void GNUstep::IMPCacher::CacheLookup(CallInst *lookup, Value *slot, Value
// Perform the real lookup and cache the result
removeTerminator(lookupBB);
B.SetInsertPoint(lookupBB);
Value * newReceiver = B.CreateLoad(receiverPtr);
Value * newReceiver = receiver;
if (!isSuperMessage) {
newReceiver = B.CreateLoad(receiverPtr);
}
BasicBlock *storeCacheBB = BasicBlock::Create(Context, "cache_store",
lookupBB->getParent());
@ -122,19 +128,40 @@ void GNUstep::IMPCacher::SpeculativelyInline(Instruction *call, Function
// function
IRBuilder<> B = IRBuilder<>(beforeCallBB);
Value *callee = call->getOperand(0);
if (callee->getType() != function->getType()) {
const FunctionType *FTy = function->getFunctionType();
const FunctionType *calleeTy = cast<FunctionType>(
cast<PointerType>(callee->getType())->getElementType());
if (calleeTy != FTy) {
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();
Value *inlineResult= inlineCall;
inlineBB->getInstList().push_back(inlineCall);
inlineCall->setOperand(0, function);
B.SetInsertPoint(inlineBB);
if (calleeTy != FTy) {
for (unsigned i=0 ; i<FTy->getNumParams() ; i++) {
const Type *callType = calleeTy->getParamType(i);
const Type *argType = FTy->getParamType(i);
if (callType != argType) {
inlineCall->setOperand(i+1, new
BitCastInst(inlineCall->getOperand(i+1), argType, "", inlineCall));
}
}
if (FTy->getReturnType() != calleeTy->getReturnType()) {
inlineResult = new BitCastInst(inlineCall, calleeTy->getReturnType(), "", inlineBB);
}
}
B.CreateBr(afterCallBB);
// Unify the return values
@ -143,7 +170,7 @@ void GNUstep::IMPCacher::SpeculativelyInline(Instruction *call, Function
PHINode *phi = B.CreatePHI(call->getType());
call->replaceAllUsesWith(phi);
phi->addIncoming(call, callBB);
phi->addIncoming(inlineCall, inlineBB);
phi->addIncoming(inlineResult, inlineBB);
}
// Really do the real inlining

View File

@ -28,7 +28,8 @@ namespace GNUstep
const IntegerType *IntTy;
public:
IMPCacher(LLVMContext &C, Pass *owner);
void CacheLookup(CallInst *lookup, Value *slot, Value *version);
void CacheLookup(Instruction *lookup, Value *slot, Value *version, bool
isSuperMessage=false);
void SpeculativelyInline(Instruction *call, Function *function);
};

View File

@ -75,7 +75,6 @@ namespace {
Zeros[1] = Zeros[0];
moduleName = ConstantExpr::getGetElementPtr(moduleName, Zeros, 2);
moduleName->dump();
functions.push_back(moduleName);;
functions.push_back(moduleName);;
@ -117,8 +116,7 @@ namespace {
ConstantArray *CA = cast<ConstantArray>(GCL->getInitializer());
for (User::op_iterator i = CA->op_begin(), e = CA->op_end(); i != e; ++i) {
ConstantStruct *CS = cast<ConstantStruct>(*i);
ctors.push_back(dyn_cast<Function>(CS->getOperand(1)));
ctors.push_back(cast<ConstantStruct>(*i));
}
// Type of one ctor
@ -139,7 +137,6 @@ namespace {
NGV->takeName(GCL);
GCL->replaceAllUsesWith(NGV);
GCL->eraseFromParent();
M.dump();
return true;
}

View File

@ -0,0 +1,150 @@
#include "llvm/Constants.h"
#include "llvm/Pass.h"
#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/Support/IRBuilder.h"
#include "llvm/Linker.h"
#include <vector>
using namespace llvm;
namespace {
struct GNUObjCTypeFeedbackDrivenInliner : public ModulePass {
typedef std::pair<CallInst*,CallInst*> callPair;
typedef std::vector<callPair > replacementVector;
static char ID;
uint32_t callsiteCount;
const IntegerType *Int32Ty;
GNUObjCTypeFeedbackDrivenInliner() : ModulePass(&ID), callsiteCount(0) {}
void profileFunction(Function &F, Constant *ModuleID) {
for (Function::iterator i=F.begin(), e=F.end() ;
i != e ; ++i) {
replacementVector replacements;
for (BasicBlock::iterator b=i->begin(), last=i->end() ;
b != last ; ++b) {
Module *M = F.getParent();
if (CallInst *call = dyn_cast<CallInst>(b)) {
if (Function *callee = call->getCalledFunction()) {
if (callee->getName() == "objc_msg_lookup_sender") {
llvm::Value *args[] = { call->getOperand(1),
call->getOperand(2), call->getOperand(3),
ModuleID, ConstantInt::get(Int32Ty,
callsiteCount++) };
Function *profile = cast<Function>(
M->getOrInsertFunction("objc_msg_lookup_profile",
callee->getFunctionType()->getReturnType(),
args[0]->getType(), args[1]->getType(),
args[2]->getType(),
ModuleID->getType(), Int32Ty, NULL));
llvm::CallInst *profileCall =
CallInst::Create(profile, args, args+5, "", call);
replacements.push_back(callPair(call, profileCall));
}
}
}
}
for (replacementVector::iterator r=replacements.begin(),
e=replacements.end() ; e!=r ; r++) {
r->first->replaceAllUsesWith(r->second);
r->second->getParent()->getInstList().erase(r->first);
}
}
}
public:
virtual bool runOnModule(Module &M)
{
LLVMContext &VMContext = M.getContext();
Int32Ty = IntegerType::get(VMContext, 32);
const PointerType *PtrTy = Type::getInt8PtrTy(VMContext);
Constant *moduleName =
ConstantArray::get(VMContext, M.getModuleIdentifier(), true);
moduleName = new GlobalVariable(M, moduleName->getType(), true,
GlobalValue::InternalLinkage, moduleName,
".objc_profile_module_name");
std::vector<Constant*> functions;
llvm::Constant *Zeros[2];
Zeros[0] = ConstantInt::get(Type::getInt32Ty(VMContext), 0);
Zeros[1] = Zeros[0];
moduleName = ConstantExpr::getGetElementPtr(moduleName, Zeros, 2);
functions.push_back(moduleName);;
functions.push_back(moduleName);;
for (Module::iterator F=M.begin(), e=M.end() ;
F != e ; ++F) {
if (F->isDeclaration()) { continue; }
functions.push_back(ConstantExpr::getBitCast(F, PtrTy));
Constant * ConstStr =
llvm::ConstantArray::get(VMContext, F->getName());
ConstStr = new GlobalVariable(M, ConstStr->getType(), true,
GlobalValue::PrivateLinkage, ConstStr, "str");
functions.push_back(
ConstantExpr::getGetElementPtr(ConstStr, Zeros, 2));
profileFunction(*F, moduleName);
}
functions.push_back(ConstantPointerNull::get(PtrTy));
Constant *symtab = ConstantArray::get(ArrayType::get(PtrTy,
functions.size()), functions);
Value *symbolTable = new GlobalVariable(M, symtab->getType(), true,
GlobalValue::InternalLinkage, symtab, "symtab");
Function *init =
Function::Create(FunctionType::get(Type::getVoidTy(VMContext), false),
GlobalValue::PrivateLinkage, "load_symbol_table", &M);
BasicBlock * EntryBB = BasicBlock::Create(VMContext, "entry", init);
IRBuilder<> B = IRBuilder<>(EntryBB);
Value *syms = B.CreateStructGEP(symbolTable, 0);
B.CreateCall(M.getOrInsertFunction("objc_profile_write_symbols",
Type::getVoidTy(VMContext), syms->getType(), NULL),
syms);
B.CreateRetVoid();
GlobalVariable *GCL = M.getGlobalVariable("llvm.global_ctors");
std::vector<Constant*> ctors;
ConstantArray *CA = cast<ConstantArray>(GCL->getInitializer());
for (User::op_iterator i = CA->op_begin(), e = CA->op_end(); i != e; ++i) {
ctors.push_back(cast<ConstantStruct>(*i));
}
// Type of one ctor
const Type *ctorTy =
cast<ArrayType>(GCL->getType()->getElementType())->getElementType();
// Add the
std::vector<Constant*> CSVals;
CSVals.push_back(ConstantInt::get(Type::getInt32Ty(VMContext),65535));
CSVals.push_back(init);
ctors.push_back(ConstantStruct::get(GCL->getContext(), CSVals, false));
// Create the array initializer.
CA = cast<ConstantArray>(ConstantArray::get(ArrayType::get(ctorTy,
ctors.size()), ctors));
// Create the new global and replace the old one
GlobalVariable *NGV = new GlobalVariable(CA->getType(),
GCL->isConstant(), GCL->getLinkage(), CA, "", GCL->isThreadLocal());
GCL->getParent()->getGlobalList().insert(GCL, NGV);
NGV->takeName(GCL);
GCL->replaceAllUsesWith(NGV);
GCL->eraseFromParent();
return true;
}
};
char GNUObjCTypeFeedbackDrivenInliner::ID = 0;
RegisterPass<GNUObjCTypeFeedbackDrivenInliner> X("gnu-objc-feedback-driven-inline",
"Objective-C type feedback-driven inliner for the GNU runtime.", false,
true);
}