Remove the LLVM optimisation passes. They don’t work with a recent LLVM, can be recovered from git if required, and are better off being upstreamed into LLVM if they make sense.
parent
c7c193e4c7
commit
2ea11117a3
@ -1,47 +0,0 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
find_package(LLVM)
|
||||
include(AddLLVM OPTIONAL RESULT_VARIABLE INCLUDED_LLVM)
|
||||
|
||||
if (${INCLUDED_LLVM} STREQUAL "NOTFOUND")
|
||||
message(WARNING "Failed to include AddLLVM CMake module")
|
||||
else()
|
||||
message("Included: '${INCLUDED_LLVM}'")
|
||||
|
||||
add_definitions(${LLVM_DEFINITIONS})
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
link_directories(${LLVM_LIBRARY_DIRS})
|
||||
|
||||
|
||||
add_llvm_loadable_module( libGNUObjCRuntime
|
||||
ClassIMPCache.cpp
|
||||
ClassMethodInliner.cpp
|
||||
IvarPass.cpp
|
||||
ObjectiveCOpts.cpp
|
||||
TypeFeedbackDrivenInliner.cpp
|
||||
ClassLookupCache.cpp
|
||||
IMPCacher.cpp
|
||||
LoopIMPCachePass.cpp
|
||||
TypeFeedback.cpp
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-variadic-macros")
|
||||
|
||||
set(CMAKE_CXX "clang++")
|
||||
|
||||
EXEC_PROGRAM(llvm-config
|
||||
ARGS --src-root
|
||||
OUTPUT_VARIABLE LLVM_SRC)
|
||||
EXEC_PROGRAM(llvm-config
|
||||
ARGS --obj-root
|
||||
OUTPUT_VARIABLE LLVM_OBJ)
|
||||
EXEC_PROGRAM(llvm-config
|
||||
ARGS --version
|
||||
OUTPUT_VARIABLE LLVM_VER)
|
||||
|
||||
string(REGEX REPLACE "([0-9]*).([0-9]*).*" "-DLLVM_MAJOR=\\1 -DLLVM_MINOR=\\2" LLVM_VERSION "${LLVM_VER}")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_VERSION} -fno-rtti")
|
||||
include_directories( ${LLVM_INCLUDE_DIRS} "${LLVM_SRC}/include/" "${LLVM_OBJ}/include/")
|
||||
|
||||
endif() # AddLLVM include failed
|
||||
@ -1,20 +0,0 @@
|
||||
Copyright (c) 2009 David Chisnall
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
@ -1,104 +0,0 @@
|
||||
#include "llvm/Pass.h"
|
||||
#include "llvm/Support/CallSite.h"
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "ObjectiveCOpts.h"
|
||||
#include "IMPCacher.h"
|
||||
#include <string>
|
||||
#include "LLVMCompat.h"
|
||||
|
||||
using namespace GNUstep;
|
||||
using namespace llvm;
|
||||
using std::string;
|
||||
|
||||
namespace
|
||||
{
|
||||
class ClassIMPCachePass : public ModulePass
|
||||
{
|
||||
LLVMIntegerTy *IntTy;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
ClassIMPCachePass() : ModulePass(ID) {}
|
||||
|
||||
virtual bool runOnModule(Module &M) {
|
||||
Function *sendFn = M.getFunction("objc_msgSend");
|
||||
Function *send_stretFn = M.getFunction("objc_msgSend_stret");
|
||||
Function *send_fpretFn = M.getFunction("objc_msgSend_fpret");
|
||||
Function *lookupFn =M.getFunction("objc_msg_lookup_sender");
|
||||
// If this module doesn't contain any message sends, then skip it
|
||||
if ((sendFn == 0) && (send_stretFn == 0) && (send_fpretFn == 0) &&
|
||||
(lookupFn ==0)) { return false; }
|
||||
|
||||
GNUstep::IMPCacher cacher = GNUstep::IMPCacher(M.getContext(), this);
|
||||
IntTy = (sizeof(int) == 4 ) ? Type::getInt32Ty(M.getContext()) :
|
||||
Type::getInt64Ty(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<std::pair<CallSite, bool>, 16> Lookups;
|
||||
SmallVector<CallSite, 16> Sends;
|
||||
|
||||
for (Function::iterator i=F->begin(), end=F->end() ;
|
||||
i != end ; ++i) {
|
||||
for (BasicBlock::iterator b=i->begin(), last=i->end() ;
|
||||
b != last ; ++b) {
|
||||
CallSite call(b);
|
||||
if (call.getInstruction()) {
|
||||
Value *callee = call.getCalledValue()->stripPointerCasts();
|
||||
if (Function *func = dyn_cast<Function>(callee)) {
|
||||
if ((func == lookupFn) || (func == sendFn) ||
|
||||
(func == send_fpretFn) || (func == send_stretFn)) {
|
||||
MDNode *messageType =
|
||||
call.getInstruction()->getMetadata(MessageSendMDKind);
|
||||
if (0 == messageType) { continue; }
|
||||
if (cast<ConstantInt>(messageType->getOperand(2))->isOne()) {
|
||||
if (func == lookupFn) {
|
||||
Lookups.push_back(std::pair<CallSite, bool>(call, false));
|
||||
} else {
|
||||
Sends.push_back(call);
|
||||
}
|
||||
}
|
||||
} else if (func->getName() == "objc_slot_lookup_super") {
|
||||
Lookups.push_back(std::pair<CallSite, bool>(call, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (SmallVectorImpl<CallSite>::iterator i=Sends.begin(),
|
||||
e=Sends.end() ; e!=i ; i++) {
|
||||
Lookups.push_back(std::pair<CallSite, bool>(cacher.SplitSend(*i), false));
|
||||
}
|
||||
for (SmallVectorImpl<std::pair<CallSite, bool> >::iterator
|
||||
i=Lookups.begin(), e=Lookups.end() ; e!=i ; i++) {
|
||||
Instruction *call = i->first.getInstruction();
|
||||
LLVMType *SlotPtrTy = call->getType();
|
||||
|
||||
Value *slot = new GlobalVariable(M, SlotPtrTy, false,
|
||||
GlobalValue::PrivateLinkage, Constant::getNullValue(SlotPtrTy),
|
||||
"slot");
|
||||
Value *version = new GlobalVariable(M, IntTy, false,
|
||||
GlobalValue::PrivateLinkage, Constant::getNullValue(IntTy),
|
||||
"version");
|
||||
cacher.CacheLookup(call, slot, version, i->second);
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
};
|
||||
|
||||
char ClassIMPCachePass::ID = 0;
|
||||
RegisterPass<ClassIMPCachePass> X("gnu-class-imp-cache",
|
||||
"Cache IMPs for class messages");
|
||||
}
|
||||
|
||||
ModulePass *createClassIMPCachePass(void)
|
||||
{
|
||||
return new ClassIMPCachePass();
|
||||
}
|
||||
@ -1,152 +0,0 @@
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include "ObjectiveCOpts.h"
|
||||
#include <string>
|
||||
|
||||
#include "IMPCacher.h"
|
||||
#include "LLVMCompat.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace GNUstep;
|
||||
using std::string;
|
||||
using std::pair;
|
||||
|
||||
namespace
|
||||
{
|
||||
class ClassLookupCachePass : public ModulePass {
|
||||
/// Module that we're currently optimising
|
||||
Module *M;
|
||||
/// Static cache. If we're not using the non-fragile ABI, then we cache
|
||||
/// all class lookups in static variables to avoid the overhead of the
|
||||
/// lookup. With the non-fragile ABI, we don't need to do this.
|
||||
llvm::StringMap<GlobalVariable*> statics;
|
||||
|
||||
typedef std::pair<CallInst*,std::string> ClassLookup;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
ClassLookupCachePass() : ModulePass(ID) {}
|
||||
|
||||
virtual bool doInitialization(Module &Mod) {
|
||||
M = &Mod;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool runOnFunction(Function &F) {
|
||||
bool modified = false;
|
||||
SmallVector<ClassLookup, 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) {
|
||||
if (CallInst *call = dyn_cast<CallInst>(b)) {
|
||||
if (Function *func = dyn_cast<Function>(call->getCalledValue()->stripPointerCasts())) {
|
||||
if (func->getName() == "objc_lookup_class") {
|
||||
ClassLookup lookup;
|
||||
GlobalVariable *classNameVar = dyn_cast<GlobalVariable>(
|
||||
call->getOperand(0)->stripPointerCasts());
|
||||
if (0 == classNameVar) { continue; }
|
||||
#if (LLVM_MAJOR > 3) || ((LLVM_MAJOR == 3) && (LLVM_MINOR > 0))
|
||||
ConstantDataArray *init = dyn_cast<ConstantDataArray>(
|
||||
classNameVar->getInitializer());
|
||||
#else
|
||||
ConstantArray *init = dyn_cast<ConstantArray>(
|
||||
classNameVar->getInitializer());
|
||||
#endif
|
||||
if (0 == init || !init->isCString()) { continue; }
|
||||
lookup.first = call;
|
||||
lookup.second = init->getAsString();
|
||||
modified = true;
|
||||
Lookups.push_back(lookup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (SmallVectorImpl<ClassLookup>::iterator i=Lookups.begin(),
|
||||
e=Lookups.end() ; e!=i ; i++) {
|
||||
llvm::Instruction *lookup = i->first;
|
||||
std::string &cls = i->second;
|
||||
LLVMType *clsTy = lookup->getType();
|
||||
Value *global = M->getGlobalVariable(("_OBJC_CLASS_" + i->second).c_str(), true);
|
||||
// If we can see the class reference for this, then reference it
|
||||
// directly. If not, then do the lookup and cache it.
|
||||
if (global) {
|
||||
// Insert a bitcast of the class to the required type where the
|
||||
// lookup is and then replace all references to the lookup with it.
|
||||
Value *cls = new BitCastInst(global, clsTy, "class", lookup);
|
||||
lookup->replaceAllUsesWith(cls);
|
||||
lookup->removeFromParent();
|
||||
delete lookup;
|
||||
} else {
|
||||
GlobalVariable *cache = statics[cls];
|
||||
if (!cache) {
|
||||
cache = new GlobalVariable(*M, clsTy, false,
|
||||
GlobalVariable::PrivateLinkage, Constant::getNullValue(clsTy),
|
||||
".class_cache");
|
||||
statics[cls] = cache;
|
||||
}
|
||||
BasicBlock *beforeLookupBB = lookup->getParent();
|
||||
BasicBlock *lookupBB = SplitBlock(beforeLookupBB, lookup, this);
|
||||
BasicBlock::iterator iter = lookup;
|
||||
iter++;
|
||||
BasicBlock *afterLookupBB = SplitBlock(iter->getParent(), iter, this);
|
||||
// SplitBlock() adds an unconditional branch, which we don't want.
|
||||
// Remove it.
|
||||
removeTerminator(beforeLookupBB);
|
||||
removeTerminator(lookupBB);
|
||||
|
||||
PHINode *phi = CreatePHI(clsTy, 2, cls, afterLookupBB->begin());
|
||||
// We replace all of the existing uses with the PHI node now, because
|
||||
// we're going to add some more uses later that we don't want
|
||||
// replaced.
|
||||
lookup->replaceAllUsesWith(phi);
|
||||
|
||||
// In the original basic block, we test whether the cache is NULL,
|
||||
// and skip the lookup if it isn't.
|
||||
IRBuilder<> B(beforeLookupBB);
|
||||
llvm::Value *cachedClass =
|
||||
B.CreateBitCast(B.CreateLoad(cache), clsTy);
|
||||
llvm::Value *needsLookup = B.CreateIsNull(cachedClass);
|
||||
B.CreateCondBr(needsLookup, lookupBB, afterLookupBB);
|
||||
// In the lookup basic block, we just do the lookup, store it in the
|
||||
// cache, and then jump to the continue block
|
||||
B.SetInsertPoint(lookupBB);
|
||||
B.CreateStore(lookup, cache);
|
||||
B.CreateBr(afterLookupBB);
|
||||
// Now we just need to set the PHI node to use the cache or the
|
||||
// lookup result
|
||||
phi->addIncoming(cachedClass, beforeLookupBB);
|
||||
phi->addIncoming(lookup, lookupBB);
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
virtual bool runOnModule(Module &Mod) {
|
||||
statics.empty();
|
||||
M = &Mod;
|
||||
bool modified = false;
|
||||
|
||||
for (Module::iterator F=Mod.begin(), fend=Mod.end() ;
|
||||
F != fend ; ++F) {
|
||||
|
||||
if (F->isDeclaration()) { continue; }
|
||||
|
||||
modified |= runOnFunction(*F);
|
||||
}
|
||||
|
||||
return modified;
|
||||
};
|
||||
};
|
||||
|
||||
char ClassLookupCachePass::ID = 0;
|
||||
RegisterPass<ClassLookupCachePass> X("gnu-class-lookup-cache",
|
||||
"Cache class lookups");
|
||||
}
|
||||
|
||||
ModulePass *createClassLookupCachePass(void)
|
||||
{
|
||||
return new ClassLookupCachePass();
|
||||
}
|
||||
@ -1,105 +0,0 @@
|
||||
#include "llvm/Support/CallSite.h"
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/InlineCost.h"
|
||||
#include "ObjectiveCOpts.h"
|
||||
#include "IMPCacher.h"
|
||||
#include <string>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace GNUstep;
|
||||
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(ID) {}
|
||||
|
||||
virtual bool runOnModule(Module &M) {
|
||||
unsigned MessageSendMDKind = M.getContext().getMDKindID("GNUObjCMessageSend");
|
||||
InlineCostAnalyzer CA;
|
||||
SmallPtrSet<const Function *, 16> NeverInline;
|
||||
|
||||
GNUstep::IMPCacher cacher = GNUstep::IMPCacher(M.getContext(), this);
|
||||
IntTy = (sizeof(int) == 4 ) ? Type::getInt32Ty(M.getContext()) :
|
||||
Type::getInt64Ty(M.getContext()) ;
|
||||
bool modified = false;
|
||||
|
||||
for (Module::iterator F=M.begin(), fend=M.end() ;
|
||||
F != fend ; ++F) {
|
||||
|
||||
SmallVector<CallSite, 16> messages;
|
||||
|
||||
if (F->isDeclaration()) { continue; }
|
||||
|
||||
for (Function::iterator i=F->begin(), end=F->end() ;
|
||||
i != end ; ++i) {
|
||||
for (BasicBlock::iterator b=i->begin(), last=i->end() ;
|
||||
b != last ; ++b) {
|
||||
CallSite call(b);
|
||||
if (call.getInstruction() && !call.getCalledFunction()) {
|
||||
MDNode *messageType = call->getMetadata(MessageSendMDKind);
|
||||
if (0 == messageType) { continue; }
|
||||
messages.push_back(call);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (SmallVectorImpl<CallSite>::iterator i=messages.begin(),
|
||||
e=messages.end() ; e!=i ; i++) {
|
||||
|
||||
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();
|
||||
std::string functionName = SymbolNameForMethod(cls, "", sel, isClassMethod);
|
||||
Function *method = M.getFunction(functionName);
|
||||
|
||||
if (0 == method || method->isDeclaration()) { continue; }
|
||||
|
||||
#if (LLVM_MAJOR > 3) || ((LLVM_MAJOR == 3) && (LLVM_MINOR > 0))
|
||||
InlineCost IC = CA.getInlineCost((*i), method, 200);
|
||||
#else
|
||||
InlineCost IC = CA.getInlineCost((*i), method, NeverInline);
|
||||
#define getCost getValue
|
||||
#endif
|
||||
// FIXME: 200 is a random number. Pick a better one!
|
||||
if (IC.isAlways() || (IC.isVariable() && IC.getCost() < 200)) {
|
||||
cacher.SpeculativelyInline((*i).getInstruction(), method);
|
||||
i->getInstruction()->setMetadata(MessageSendMDKind, 0);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
};
|
||||
|
||||
char ClassMethodInliner::ID = 0;
|
||||
RegisterPass<ClassMethodInliner> X("gnu-class-method-inline",
|
||||
"Inline class methods and message sends to super");
|
||||
}
|
||||
|
||||
ModulePass *createClassMethodInliner(void)
|
||||
{
|
||||
return new ClassMethodInliner();
|
||||
}
|
||||
@ -1,295 +0,0 @@
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "IMPCacher.h"
|
||||
#include "llvm/Support/CallSite.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include "llvm/Transforms/Utils/Cloning.h"
|
||||
|
||||
#include "LLVMCompat.h"
|
||||
|
||||
GNUstep::IMPCacher::IMPCacher(LLVMContext &C, Pass *owner) : Context(C),
|
||||
Owner(owner) {
|
||||
|
||||
PtrTy = Type::getInt8PtrTy(Context);
|
||||
IntTy = (sizeof(int) == 4 ) ? Type::getInt32Ty(C) : Type::getInt64Ty(C);
|
||||
IdTy = PointerType::getUnqual(PtrTy);
|
||||
Value *AlreadyCachedFlagValue = MDString::get(C, "IMPCached");
|
||||
AlreadyCachedFlag = CreateMDNode(C, &AlreadyCachedFlagValue);
|
||||
IMPCacheFlagKind = Context.getMDKindID("IMPCache");
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
lookup->setMetadata(IMPCacheFlagKind, AlreadyCachedFlag);
|
||||
bool isInvoke = false;
|
||||
|
||||
BasicBlock *beforeLookupBB = lookup->getParent();
|
||||
BasicBlock *lookupBB = SplitBlock(beforeLookupBB, lookup, Owner);
|
||||
BasicBlock *lookupFinishedBB = lookupBB;
|
||||
BasicBlock *afterLookupBB;
|
||||
|
||||
if (InvokeInst *inv = dyn_cast<InvokeInst>(lookup)) {
|
||||
afterLookupBB = inv->getNormalDest();
|
||||
lookupFinishedBB =
|
||||
BasicBlock::Create(Context, "done_lookup", lookupBB->getParent());
|
||||
CGBuilder B(lookupFinishedBB);
|
||||
B.CreateBr(afterLookupBB);
|
||||
inv->setNormalDest(lookupFinishedBB);
|
||||
isInvoke = true;
|
||||
} else {
|
||||
BasicBlock::iterator iter = lookup;
|
||||
iter++;
|
||||
afterLookupBB = SplitBlock(iter->getParent(), iter, Owner);
|
||||
}
|
||||
|
||||
removeTerminator(beforeLookupBB);
|
||||
|
||||
CGBuilder B = CGBuilder(beforeLookupBB);
|
||||
// Load the slot and check that neither it nor the version is 0.
|
||||
Value *versionValue = B.CreateLoad(version);
|
||||
Value *receiverPtr = lookup->getOperand(0);
|
||||
Value *receiver = receiverPtr;
|
||||
if (!isSuperMessage) {
|
||||
receiver = B.CreateLoad(receiverPtr);
|
||||
}
|
||||
// For small objects, we skip the cache entirely.
|
||||
// FIXME: Class messages are never to small objects...
|
||||
bool is64Bit = llvm::Module::Pointer64 ==
|
||||
B.GetInsertBlock()->getParent()->getParent()->getPointerSize();
|
||||
LLVMType *intPtrTy = is64Bit ? Type::getInt64Ty(Context) :
|
||||
Type::getInt32Ty(Context);
|
||||
|
||||
// Receiver as an integer
|
||||
Value *receiverSmallObject = B.CreatePtrToInt(receiver, intPtrTy);
|
||||
// Receiver is a small object...
|
||||
receiverSmallObject =
|
||||
B.CreateAnd(receiverSmallObject, is64Bit ? 7 : 1);
|
||||
// Receiver is not a small object.
|
||||
receiverSmallObject =
|
||||
B.CreateICmpNE(receiverSmallObject, Constant::getNullValue(intPtrTy));
|
||||
// Ideally, we'd call objc_msgSend() here, but for now just skip the cache
|
||||
// lookup
|
||||
|
||||
Value *isCacheEmpty =
|
||||
B.CreateICmpEQ(versionValue, Constant::getNullValue(IntTy));
|
||||
Value *receiverNil =
|
||||
B.CreateICmpEQ(receiver, Constant::getNullValue(receiver->getType()));
|
||||
|
||||
isCacheEmpty = B.CreateOr(isCacheEmpty, receiverNil);
|
||||
isCacheEmpty = B.CreateOr(isCacheEmpty, receiverSmallObject);
|
||||
|
||||
BasicBlock *cacheLookupBB = BasicBlock::Create(Context, "cache_check",
|
||||
lookupBB->getParent());
|
||||
|
||||
B.CreateCondBr(isCacheEmpty, lookupBB, cacheLookupBB);
|
||||
|
||||
// Check the cache node is current
|
||||
B.SetInsertPoint(cacheLookupBB);
|
||||
Value *slotValue = B.CreateLoad(slot, "slot_value");
|
||||
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);
|
||||
|
||||
// Perform the real lookup and cache the result
|
||||
removeTerminator(lookupFinishedBB);
|
||||
// Replace the looked up slot with the loaded one
|
||||
B.SetInsertPoint(afterLookupBB, afterLookupBB->begin());
|
||||
PHINode *newLookup = IRBuilderCreatePHI(&B, lookup->getType(), 3, "new_lookup");
|
||||
// Not volatile, so a redundant load elimination pass can do some phi
|
||||
// magic with this later.
|
||||
lookup->replaceAllUsesWith(newLookup);
|
||||
|
||||
B.SetInsertPoint(lookupFinishedBB);
|
||||
Value * newReceiver = receiver;
|
||||
if (!isSuperMessage) {
|
||||
newReceiver = B.CreateLoad(receiverPtr);
|
||||
}
|
||||
BasicBlock *storeCacheBB = BasicBlock::Create(Context, "cache_store",
|
||||
lookupBB->getParent());
|
||||
|
||||
// Don't store the cached lookup if we are doing forwarding tricks.
|
||||
// Also skip caching small object messages for now
|
||||
Value *skipCacheWrite =
|
||||
B.CreateOr(B.CreateICmpNE(receiver, newReceiver), receiverSmallObject);
|
||||
skipCacheWrite = B.CreateOr(skipCacheWrite, receiverNil);
|
||||
B.CreateCondBr(skipCacheWrite, afterLookupBB, storeCacheBB);
|
||||
B.SetInsertPoint(storeCacheBB);
|
||||
|
||||
// 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);
|
||||
|
||||
newLookup->addIncoming(lookup, lookupFinishedBB);
|
||||
newLookup->addIncoming(slotValue, cacheLookupBB);
|
||||
newLookup->addIncoming(lookup, storeCacheBB);
|
||||
}
|
||||
|
||||
|
||||
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 = isa<CallInst>(call) ? cast<CallInst>(call)->getCalledValue()
|
||||
: cast<InvokeInst>(call)->getCalledValue();
|
||||
|
||||
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);
|
||||
|
||||
B.SetInsertPoint(inlineBB);
|
||||
|
||||
if (calleeTy != FTy) {
|
||||
for (unsigned i=0 ; i<FTy->getNumParams() ; i++) {
|
||||
LLVMType *callType = calleeTy->getParamType(i);
|
||||
LLVMType *argType = FTy->getParamType(i);
|
||||
if (callType != argType) {
|
||||
inlineCall->setOperand(i, new
|
||||
BitCastInst(inlineCall->getOperand(i), argType, "", inlineCall));
|
||||
}
|
||||
}
|
||||
if (FTy->getReturnType() != calleeTy->getReturnType()) {
|
||||
if (FTy->getReturnType() == Type::getVoidTy(Context)) {
|
||||
inlineResult = Constant::getNullValue(calleeTy->getReturnType());
|
||||
} else {
|
||||
inlineResult =
|
||||
new BitCastInst(inlineCall, calleeTy->getReturnType(), "", inlineBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
B.CreateBr(afterCallBB);
|
||||
|
||||
// Unify the return values
|
||||
if (call->getType() != Type::getVoidTy(Context)) {
|
||||
PHINode *phi = CreatePHI(call->getType(), 2, "", afterCallBB->begin());
|
||||
call->replaceAllUsesWith(phi);
|
||||
phi->addIncoming(call, callBB);
|
||||
phi->addIncoming(inlineResult, inlineBB);
|
||||
}
|
||||
|
||||
// Really do the real inlining
|
||||
InlineFunctionInfo IFI(0, 0);
|
||||
if (CallInst *c = dyn_cast<CallInst>(inlineCall)) {
|
||||
c->setCalledFunction(function);
|
||||
InlineFunction(c, IFI);
|
||||
} else if (InvokeInst *c = dyn_cast<InvokeInst>(inlineCall)) {
|
||||
c->setCalledFunction(function);
|
||||
InlineFunction(c, IFI);
|
||||
}
|
||||
}
|
||||
|
||||
CallSite GNUstep::IMPCacher::SplitSend(CallSite msgSend)
|
||||
{
|
||||
BasicBlock *lookupBB = msgSend->getParent();
|
||||
Function *F = lookupBB->getParent();
|
||||
Module *M = F->getParent();
|
||||
Function *send = M->getFunction("objc_msgSend");
|
||||
Function *send_stret = M->getFunction("objc_msgSend_stret");
|
||||
Function *send_fpret = M->getFunction("objc_msgSend_fpret");
|
||||
Value *self;
|
||||
Value *cmd;
|
||||
int selfIndex = 0;
|
||||
if ((msgSend.getCalledFunction() == send) ||
|
||||
(msgSend.getCalledFunction() == send_fpret)) {
|
||||
self = msgSend.getArgument(0);
|
||||
cmd = msgSend.getArgument(1);
|
||||
} else if (msgSend.getCalledFunction() == send_stret) {
|
||||
selfIndex = 1;
|
||||
self = msgSend.getArgument(1);
|
||||
cmd = msgSend.getArgument(2);
|
||||
} else {
|
||||
abort();
|
||||
return CallSite();
|
||||
}
|
||||
CGBuilder B(&F->getEntryBlock(), F->getEntryBlock().begin());
|
||||
Value *selfPtr = B.CreateAlloca(self->getType());
|
||||
B.SetInsertPoint(msgSend.getInstruction());
|
||||
B.CreateStore(self, selfPtr, true);
|
||||
LLVMType *impTy = msgSend.getCalledValue()->getType();
|
||||
LLVMType *slotTy = PointerType::getUnqual(StructType::get(PtrTy, PtrTy, PtrTy,
|
||||
IntTy, impTy, PtrTy, NULL));
|
||||
Value *slot;
|
||||
Constant *lookupFn = M->getOrInsertFunction("objc_msg_lookup_sender",
|
||||
slotTy, selfPtr->getType(), cmd->getType(), PtrTy, NULL);
|
||||
if (msgSend.isCall()) {
|
||||
slot = B.CreateCall3(lookupFn, selfPtr, cmd, Constant::getNullValue(PtrTy));
|
||||
} else {
|
||||
InvokeInst *inv = cast<InvokeInst>(msgSend.getInstruction());
|
||||
BasicBlock *callBB = SplitBlock(lookupBB, msgSend.getInstruction(), Owner);
|
||||
removeTerminator(lookupBB);
|
||||
B.SetInsertPoint(lookupBB);
|
||||
slot = B.CreateInvoke3(lookupFn, callBB, inv->getUnwindDest(), selfPtr, cmd,
|
||||
Constant::getNullValue(PtrTy));
|
||||
addPredecssor(inv->getUnwindDest(), msgSend->getParent(), lookupBB);
|
||||
B.SetInsertPoint(msgSend.getInstruction());
|
||||
}
|
||||
Value *imp = B.CreateLoad(B.CreateStructGEP(slot, 4));
|
||||
msgSend.setArgument(selfIndex, B.CreateLoad(selfPtr, true));
|
||||
msgSend.setCalledFunction(imp);
|
||||
return CallSite(slot);
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
void GNUstep::addPredecssor(BasicBlock *block, BasicBlock *oldPredecessor,
|
||||
BasicBlock *newPredecessor) {
|
||||
for (BasicBlock::iterator i=block->begin() ; PHINode *phi=dyn_cast<PHINode>(i)
|
||||
; ++i) {
|
||||
Value *v = phi->getIncomingValueForBlock(oldPredecessor);
|
||||
phi->addIncoming(v, newPredecessor);
|
||||
}
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
#include "LLVMCompat.h"
|
||||
#include "llvm/Support/CallSite.h"
|
||||
namespace llvm
|
||||
{
|
||||
class BasicBlock;
|
||||
class CallInst;
|
||||
class Function;
|
||||
class Instruction;
|
||||
class IntegerType;
|
||||
class LLVMContext;
|
||||
class MDNode;
|
||||
class Pass;
|
||||
class PointerType;
|
||||
class Value;
|
||||
}
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace GNUstep
|
||||
{
|
||||
class IMPCacher
|
||||
{
|
||||
private:
|
||||
LLVMContext &Context;
|
||||
MDNode *AlreadyCachedFlag;
|
||||
unsigned IMPCacheFlagKind;
|
||||
Pass *Owner;
|
||||
LLVMPointerTy *PtrTy;
|
||||
LLVMPointerTy *IdTy;
|
||||
LLVMIntegerTy *IntTy;
|
||||
public:
|
||||
IMPCacher(LLVMContext &C, Pass *owner);
|
||||
void CacheLookup(Instruction *lookup, Value *slot, Value *version, bool
|
||||
isSuperMessage=false);
|
||||
void SpeculativelyInline(Instruction *call, Function *function);
|
||||
/**
|
||||
* Turns a call to objc_msgSend*() into a call to
|
||||
* objc_msg_lookup_sender() and a call to the resulting IMP. The call to
|
||||
* the IMP is returned. The single call is faster, but prevents caching.
|
||||
* The split call allows caching, which is faster in the best case and
|
||||
* slower in the worst...
|
||||
*/
|
||||
CallSite SplitSend(CallSite msgSend);
|
||||
};
|
||||
|
||||
void removeTerminator(BasicBlock *BB);
|
||||
void addPredecssor(BasicBlock *block, BasicBlock *oldPredecessor, BasicBlock
|
||||
*newPredecessor);
|
||||
}
|
||||
@ -1,165 +0,0 @@
|
||||
#include "LLVMCompat.h"
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "ObjectiveCOpts.h"
|
||||
#include <string>
|
||||
|
||||
using namespace llvm;
|
||||
using std::string;
|
||||
|
||||
typedef std::pair<Instruction*, Value*> Replacement;
|
||||
|
||||
namespace llvm {
|
||||
template<> struct DenseMapInfo<Replacement> {
|
||||
static inline Replacement getEmptyKey() { return Replacement(0,0); }
|
||||
static inline Replacement getTombstoneKey() { return Replacement(0, (Value*)-1); }
|
||||
static unsigned getHashValue(const Replacement& Val) { return ((uintptr_t)Val.first) * 37U; }
|
||||
static bool isEqual(const Replacement& LHS, const Replacement& RHS) {
|
||||
return LHS.first == RHS.first;
|
||||
}
|
||||
};
|
||||
}
|
||||
namespace {
|
||||
class GNUNonfragileIvarPass : public FunctionPass {
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
GNUNonfragileIvarPass() : FunctionPass(ID) {}
|
||||
|
||||
Module *M;
|
||||
size_t PointerSize;
|
||||
virtual bool doInitialization(Module &Mod) {
|
||||
M = &Mod;
|
||||
PointerSize = 8;
|
||||
if (M->getPointerSize() == Module::Pointer32)
|
||||
PointerSize = 4;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string getSuperName(Constant *ClsStruct) {
|
||||
User *super = cast<User>(ClsStruct->getOperand(1));
|
||||
if (isa<ConstantPointerNull>(super)) return "";
|
||||
GlobalVariable *name = cast<GlobalVariable>(super->getOperand(0));
|
||||
#if (LLVM_MAJOR > 3) || ((LLVM_MAJOR == 3) && (LLVM_MINOR > 0))
|
||||
return cast<ConstantDataArray>(name->getInitializer())->getAsString();
|
||||
#else
|
||||
return cast<ConstantArray>(name->getInitializer())->getAsString();
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t sizeOfClass(const std::string &className) {
|
||||
// This is a root class
|
||||
if ("" == className) { return 0; }
|
||||
// These root classes are assumed to only have one ivar: isa
|
||||
if (className.compare(0, 8, "NSObject") == 0 ||
|
||||
className.compare(0, 6, "Object") == 0) {
|
||||
return PointerSize;
|
||||
}
|
||||
GlobalVariable *Cls = M->getGlobalVariable("_OBJC_CLASS_" + className);
|
||||
if (!Cls) return 0;
|
||||
Constant *ClsStruct = Cls->getInitializer();
|
||||
// Size is initialized to be negative for the non-fragile ABI.
|
||||
ConstantInt *Size = cast<ConstantInt>(ClsStruct->getOperand(5));
|
||||
int s = Size->getSExtValue();
|
||||
// If we find a fragile class in the hierarchy, don't perform the
|
||||
// simplification. This means that we're the mixed ABI, so we need the
|
||||
// extra indirection.
|
||||
if (s > 0) return 0;
|
||||
return sizeOfClass(getSuperName(ClsStruct)) - Size->getSExtValue();
|
||||
}
|
||||
|
||||
size_t hardCodedOffset(const StringRef &className,
|
||||
const StringRef &ivarName) {
|
||||
GlobalVariable *Cls = M->getGlobalVariable(("_OBJC_CLASS_" + className).str(), true);
|
||||
if (!Cls) return 0;
|
||||
Constant *ClsStruct = Cls->getInitializer();
|
||||
size_t superSize = sizeOfClass(getSuperName(ClsStruct));
|
||||
if (!superSize) return 0;
|
||||
ConstantStruct *IvarStruct = cast<ConstantStruct>(
|
||||
cast<GlobalVariable>(ClsStruct->getOperand(6))->getInitializer());
|
||||
int ivarCount = cast<ConstantInt>(IvarStruct->getOperand(0))->getSExtValue();
|
||||
Constant *ivars = IvarStruct->getOperand(1);
|
||||
for (int i=0 ; i<ivarCount ; i++) {
|
||||
Constant *ivar = cast<Constant>(ivars->getOperand(i));
|
||||
GlobalVariable *name =
|
||||
cast<GlobalVariable>(
|
||||
cast<User>(ivar->getOperand(0))->getOperand(0));
|
||||
std::string ivarNameStr =
|
||||
#if (LLVM_MAJOR > 3) || ((LLVM_MAJOR == 3) && (LLVM_MINOR > 0))
|
||||
cast<ConstantDataArray>(name->getInitializer())->getAsString();
|
||||
#else
|
||||
cast<ConstantArray>(name->getInitializer())->getAsString();
|
||||
#endif
|
||||
// Remove the NULL terminator from the metadata string
|
||||
ivarNameStr.resize(ivarNameStr.size() - 1);
|
||||
if (ivarNameStr == ivarName.str())
|
||||
return superSize +
|
||||
cast<ConstantInt>(ivar->getOperand(2))->getSExtValue();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool runOnFunction(Function &F) {
|
||||
bool modified = false;
|
||||
llvm::DenseSet<Replacement> replacements;
|
||||
//llvm::cerr << "IvarPass: " << F.getName() << "\n";
|
||||
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 (LoadInst *indirectload = dyn_cast<LoadInst>(b)) {
|
||||
if (LoadInst *load = dyn_cast<LoadInst>(indirectload->getOperand(0))) {
|
||||
if (GlobalVariable *ivar =
|
||||
dyn_cast<GlobalVariable>(load->getOperand(0))) {
|
||||
StringRef variableName = ivar->getName();
|
||||
|
||||
if (!variableName.startswith("__objc_ivar_offset_")) break;
|
||||
|
||||
static size_t prefixLength = strlen("__objc_ivar_offset_");
|
||||
|
||||
StringRef suffix = variableName.substr(prefixLength,
|
||||
variableName.size()-prefixLength);
|
||||
|
||||
std::pair<StringRef,StringRef> parts = suffix.split('.');
|
||||
StringRef className = parts.first;
|
||||
StringRef ivarName = parts.second;
|
||||
|
||||
// If the class, and all superclasses, are visible in this module
|
||||
// then we can hard-code the ivar offset
|
||||
if (size_t offset = hardCodedOffset(className, ivarName)) {
|
||||
replacements.insert(Replacement(indirectload,
|
||||
ConstantInt::get(indirectload->getType(), offset)));
|
||||
replacements.insert(Replacement(load, 0));
|
||||
modified = true;
|
||||
} else {
|
||||
// If the class was compiled with the new ABI, then we have a
|
||||
// direct offset variable that we can use
|
||||
if (Value *offset = M->getGlobalVariable(
|
||||
("__objc_ivar_offset_value_" + suffix).str())) {
|
||||
replacements.insert(Replacement(load, offset));
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (DenseSet<Replacement>::iterator i=replacements.begin(),
|
||||
end=replacements.end() ; i != end ; ++i) {
|
||||
if (i->second)
|
||||
i->first->replaceAllUsesWith(i->second);
|
||||
}
|
||||
verifyFunction(F);
|
||||
return modified;
|
||||
}
|
||||
};
|
||||
|
||||
char GNUNonfragileIvarPass::ID = 0;
|
||||
RegisterPass<GNUNonfragileIvarPass> X("gnu-nonfragile-ivar", "Ivar fragility pass");
|
||||
}
|
||||
|
||||
FunctionPass *createGNUNonfragileIvarPass(void)
|
||||
{
|
||||
return new GNUNonfragileIvarPass();
|
||||
}
|
||||
@ -1,172 +0,0 @@
|
||||
/**
|
||||
* Compatibility header that wraps LLVM API breakage and lets us compile with
|
||||
* old and new versions of LLVM.
|
||||
*
|
||||
* First LLVM version supported is 2.9.
|
||||
*/
|
||||
|
||||
#ifndef __LANGUAGEKIT_LLVM_HACKS__
|
||||
#define __LANGUAGEKIT_LLVM_HACKS__
|
||||
#if LLVM_MAJOR < 3 || (LLVM_MAJOR >=3 && LLVM_MINOR <= 2)
|
||||
#if LLVM_MAJOR < 3 || (LLVM_MAJOR >=3 && LLVM_MINOR <= 1)
|
||||
#include <llvm/Support/IRBuilder.h>
|
||||
#else
|
||||
#include <llvm/IRBuilder.h>
|
||||
#endif
|
||||
#include <llvm/Function.h>
|
||||
#include <llvm/Module.h>
|
||||
#include <llvm/LLVMContext.h>
|
||||
#include <llvm/Instructions.h>
|
||||
#include <llvm/Metadata.h>
|
||||
#include <llvm/Intrinsics.h>
|
||||
#include <llvm/Constants.h>
|
||||
#include <llvm/GlobalAlias.h>
|
||||
#include <llvm/GlobalVariable.h>
|
||||
#include <llvm/DefaultPasses.h>
|
||||
#else
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include <llvm/IR/Function.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Instructions.h>
|
||||
#include <llvm/IR/Metadata.h>
|
||||
#include <llvm/IR/Intrinsics.h>
|
||||
#include <llvm/IR/Constants.h>
|
||||
#include <llvm/IR/GlobalVariable.h>
|
||||
#include <llvm/IR/GlobalAlias.h>
|
||||
#include <llvm/PassSupport.h>
|
||||
#endif
|
||||
|
||||
#if (LLVM_MAJOR > 3) || ((LLVM_MAJOR == 3) && (LLVM_MINOR >= 3))
|
||||
# define InlineCostAnalyzer InlineCostAnalysis
|
||||
#endif
|
||||
|
||||
// Only preserve names in a debug build. This simplifies the
|
||||
// IR in a release build, but makes it much harder to debug.
|
||||
#ifndef DEBUG
|
||||
typedef llvm::IRBuilder<false> CGBuilder;
|
||||
#else
|
||||
typedef llvm::IRBuilder<> CGBuilder;
|
||||
#endif
|
||||
|
||||
__attribute((unused)) static inline
|
||||
llvm::PHINode* CreatePHI(llvm::Type *Ty,
|
||||
unsigned NumReservedValues,
|
||||
const llvm::Twine &NameStr="",
|
||||
llvm::Instruction *InsertBefore=0) {
|
||||
#if LLVM_MAJOR < 3
|
||||
llvm::PHINode *phi = llvm::PHINode::Create(Ty, NameStr, InsertBefore);
|
||||
phi->reserveOperandSpace(NumReservedValues);
|
||||
return phi;
|
||||
#else
|
||||
return llvm::PHINode::Create(Ty, NumReservedValues, NameStr, InsertBefore);
|
||||
#endif
|
||||
}
|
||||
|
||||
__attribute((unused)) static inline
|
||||
llvm::PHINode* IRBuilderCreatePHI(CGBuilder *Builder,
|
||||
llvm::Type *Ty,
|
||||
unsigned NumReservedValues,
|
||||
const llvm::Twine &NameStr="")
|
||||
{
|
||||
#if LLVM_MAJOR < 3
|
||||
llvm::PHINode *phi = Builder->CreatePHI(Ty, NameStr);
|
||||
phi->reserveOperandSpace(NumReservedValues);
|
||||
return phi;
|
||||
#else
|
||||
return Builder->CreatePHI(Ty, NumReservedValues, NameStr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
__attribute((unused)) static inline
|
||||
llvm::MDNode* CreateMDNode(llvm::LLVMContext &C,
|
||||
llvm::Value **V,
|
||||
unsigned length=1) {
|
||||
#if LLVM_MAJOR < 3
|
||||
return llvm::MDNode::get(C, V, length);
|
||||
#else
|
||||
llvm::ArrayRef<llvm::Value*> val(V, length);
|
||||
return llvm::MDNode::get(C, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline
|
||||
llvm::InvokeInst* IRBuilderCreateInvoke(CGBuilder *Builder,
|
||||
llvm::Value *callee,
|
||||
llvm::BasicBlock *dest,
|
||||
llvm::BasicBlock *dest2,
|
||||
T values,
|
||||
const llvm::Twine &NameStr="")
|
||||
{
|
||||
#if LLVM_MAJOR < 3
|
||||
return Builder->CreateInvoke(callee, dest, dest2, values.begin(), values.end(), NameStr);
|
||||
#else
|
||||
return Builder->CreateInvoke(callee, dest, dest2, values, NameStr);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline
|
||||
llvm::CallInst* IRBuilderCreateCall(CGBuilder *Builder,
|
||||
llvm::Value *callee,
|
||||
T values,
|
||||
const llvm::Twine &NameStr="")
|
||||
{
|
||||
#if LLVM_MAJOR < 3
|
||||
return Builder->CreateCall(callee, values.begin(), values.end(), NameStr);
|
||||
#else
|
||||
return Builder->CreateCall(callee, values, NameStr);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline
|
||||
llvm::CallInst* CreateCall(llvm::Value *callee,
|
||||
T values,
|
||||
const llvm::Twine &NameStr,
|
||||
llvm::Instruction *before)
|
||||
{
|
||||
#if LLVM_MAJOR < 3
|
||||
return llvm::CallInst::Create(callee, values.begin(), values.end(), NameStr, before);
|
||||
#else
|
||||
return llvm::CallInst::Create(callee, values, NameStr, before);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if LLVM_MAJOR < 3
|
||||
#define GetStructType(context, ...) StructType::get(context, __VA_ARGS__)
|
||||
#else
|
||||
#define GetStructType(context, ...) StructType::get(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
__attribute((unused)) static inline
|
||||
llvm::Constant* GetConstantStruct(llvm::LLVMContext &C, const std::vector<llvm::Constant*>
|
||||
&V, bool Packed) {
|
||||
#if LLVM_MAJOR < 3
|
||||
return llvm::ConstantStruct::get(C, V, Packed);
|
||||
#else
|
||||
return llvm::ConstantStruct::getAnon(C, V, Packed);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if LLVM_MAJOR < 3
|
||||
typedef const llvm::Type LLVMType;
|
||||
typedef const llvm::StructType LLVMStructTy;
|
||||
typedef const llvm::ArrayType LLVMArrayTy;
|
||||
typedef const llvm::PointerType LLVMPointerTy;
|
||||
typedef const llvm::IntegerType LLVMIntegerTy;
|
||||
#else
|
||||
typedef llvm::Type LLVMType;
|
||||
typedef llvm::StructType LLVMStructTy;
|
||||
typedef llvm::ArrayType LLVMArrayTy;
|
||||
typedef llvm::PointerType LLVMPointerTy;
|
||||
typedef llvm::IntegerType LLVMIntegerTy;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -1,117 +0,0 @@
|
||||
#include "LLVMCompat.h"
|
||||
#include "llvm/Analysis/LoopInfo.h"
|
||||
#include "llvm/Analysis/Verifier.h"
|
||||
#include "ObjectiveCOpts.h"
|
||||
#include "IMPCacher.h"
|
||||
#include <string>
|
||||
|
||||
using namespace GNUstep;
|
||||
using namespace llvm;
|
||||
using std::string;
|
||||
|
||||
namespace
|
||||
{
|
||||
class GNULoopIMPCachePass : public FunctionPass
|
||||
{
|
||||
GNUstep::IMPCacher *cacher;
|
||||
LLVMIntegerTy *IntTy;
|
||||
Module *M;
|
||||
bool skip;
|
||||
Function *sendFn;
|
||||
Function *lookupFn;
|
||||
Function *send_stretFn;
|
||||
Function *send_fpretFn;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
GNULoopIMPCachePass() : FunctionPass(ID) {}
|
||||
~GNULoopIMPCachePass() { delete cacher; }
|
||||
|
||||
virtual bool doInitialization(Module &Mod) {
|
||||
cacher = new GNUstep::IMPCacher(Mod.getContext(), this);
|
||||
IntTy = (sizeof(int) == 4 ) ? Type::getInt32Ty(Mod.getContext()) :
|
||||
Type::getInt64Ty(Mod.getContext()) ;
|
||||
M = &Mod;
|
||||
skip = false;
|
||||
sendFn = M->getFunction("objc_msgSend");
|
||||
send_stretFn = M->getFunction("objc_msgSend_stret");
|
||||
send_fpretFn = M->getFunction("objc_msgSend_fpret");
|
||||
lookupFn =M->getFunction("objc_msg_lookup_sender");
|
||||
// If this module doesn't contain any message sends, then skip it
|
||||
if ((sendFn == 0) && (send_stretFn == 0) && (send_fpretFn == 0) &&
|
||||
(lookupFn ==0)) {
|
||||
skip = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void getAnalysisUsage(AnalysisUsage &Info) const {
|
||||
Info.addRequired<LoopInfo>();
|
||||
}
|
||||
|
||||
|
||||
virtual bool runOnFunction(Function &F) {
|
||||
if (skip) { return false; }
|
||||
LoopInfo &LI = getAnalysis<LoopInfo>();
|
||||
bool modified = false;
|
||||
SmallVector<CallSite, 16> Lookups;
|
||||
SmallVector<CallSite, 16> Sends;
|
||||
BasicBlock *entry = &F.getEntryBlock();
|
||||
|
||||
for (Function::iterator i=F.begin(), end=F.end() ;
|
||||
i != end ; ++i) {
|
||||
// Ignore basic blocks that are not parts of loops.
|
||||
if (LI.getLoopDepth(i) == 0) { continue; }
|
||||
for (BasicBlock::iterator b=i->begin(), last=i->end() ;
|
||||
b != last ; ++b) {
|
||||
CallSite call = CallSite(b);
|
||||
if (CallSite() != call) {
|
||||
Value *callee = call.getCalledValue()->stripPointerCasts();
|
||||
Function *func = dyn_cast<Function>(callee);
|
||||
if (func) {
|
||||
if (func == lookupFn) {
|
||||
modified = true;
|
||||
Lookups.push_back(call);
|
||||
} else if ((func == sendFn) || (func == send_fpretFn) ||
|
||||
(func == send_stretFn)) {
|
||||
modified = true;
|
||||
Sends.push_back(call);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (SmallVectorImpl<CallSite>::iterator i=Sends.begin(),
|
||||
e=Sends.end() ; e!=i ; i++) {
|
||||
Lookups.push_back(cacher->SplitSend(*i));
|
||||
}
|
||||
IRBuilder<> B = IRBuilder<>(entry);
|
||||
for (SmallVectorImpl<CallSite>::iterator i=Lookups.begin(),
|
||||
e=Lookups.end() ; e!=i ; i++) {
|
||||
LLVMType *SlotPtrTy = (*i)->getType();
|
||||
B.SetInsertPoint(entry, entry->begin());
|
||||
Value *slot = B.CreateAlloca(SlotPtrTy, 0, "slot");
|
||||
Value *version = B.CreateAlloca(IntTy, 0, "slot_version");
|
||||
|
||||
B.CreateStore(Constant::getNullValue(SlotPtrTy), slot);
|
||||
B.CreateStore(Constant::getNullValue(IntTy), version);
|
||||
cacher->CacheLookup(i->getInstruction(), slot, version);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if (modified){
|
||||
verifyFunction(F);
|
||||
}
|
||||
#endif
|
||||
return modified;
|
||||
}
|
||||
};
|
||||
|
||||
char GNULoopIMPCachePass::ID = 0;
|
||||
RegisterPass<GNULoopIMPCachePass> X("gnu-loop-imp-cache",
|
||||
"Cache IMPs in loops pass");
|
||||
}
|
||||
|
||||
FunctionPass *createGNULoopIMPCachePass(void)
|
||||
{
|
||||
return new GNULoopIMPCachePass();
|
||||
}
|
||||
@ -1,91 +0,0 @@
|
||||
#include "LLVMCompat.h"
|
||||
#if LLVM_MAJOR >= 3
|
||||
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
||||
#include <llvm/PassManager.h>
|
||||
#endif
|
||||
|
||||
#include "ObjectiveCOpts.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace
|
||||
{
|
||||
class ObjectiveCOpts : public ModulePass {
|
||||
ModulePass *ClassIMPCachePass;
|
||||
ModulePass *ClassLookupCachePass;
|
||||
ModulePass *ClassMethodInliner;
|
||||
FunctionPass *GNUNonfragileIvarPass;
|
||||
FunctionPass *GNULoopIMPCachePass;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
ObjectiveCOpts() : ModulePass(ID) {
|
||||
ClassIMPCachePass = createClassIMPCachePass();
|
||||
ClassLookupCachePass = createClassLookupCachePass();
|
||||
ClassMethodInliner = createClassMethodInliner();
|
||||
GNUNonfragileIvarPass = createGNUNonfragileIvarPass();
|
||||
GNULoopIMPCachePass = createGNULoopIMPCachePass();
|
||||
}
|
||||
virtual ~ObjectiveCOpts() {
|
||||
delete ClassIMPCachePass;
|
||||
delete ClassMethodInliner;
|
||||
delete ClassLookupCachePass;
|
||||
delete GNULoopIMPCachePass;
|
||||
delete GNUNonfragileIvarPass;
|
||||
}
|
||||
|
||||
virtual bool runOnModule(Module &Mod) {
|
||||
bool modified;
|
||||
modified = ClassIMPCachePass->runOnModule(Mod);
|
||||
modified |= ClassLookupCachePass->runOnModule(Mod);
|
||||
modified |= ClassMethodInliner->runOnModule(Mod);
|
||||
|
||||
for (Module::iterator F=Mod.begin(), fend=Mod.end() ;
|
||||
F != fend ; ++F) {
|
||||
|
||||
if (F->isDeclaration()) { continue; }
|
||||
modified |= GNUNonfragileIvarPass->runOnFunction(*F);
|
||||
modified |= GNULoopIMPCachePass->runOnFunction(*F);
|
||||
}
|
||||
|
||||
return modified;
|
||||
};
|
||||
};
|
||||
|
||||
char ObjectiveCOpts::ID = 0;
|
||||
RegisterPass<ObjectiveCOpts> X("gnu-objc",
|
||||
"Run all of the GNUstep Objective-C runtimm optimisations");
|
||||
|
||||
|
||||
#if LLVM_MAJOR >= 3
|
||||
|
||||
void addObjCPasses(const PassManagerBuilder &Builder, PassManagerBase &PM) {
|
||||
// Always add the ivar simplification pass
|
||||
PM.add(createGNUNonfragileIvarPass());
|
||||
// Only cache IMPs in loops if we're not optimising for size.
|
||||
if (Builder.SizeLevel == 0) {
|
||||
PM.add(createGNULoopIMPCachePass());
|
||||
}
|
||||
// Do the rest of the caching if we're not aggressively optimising for size
|
||||
if (Builder.SizeLevel < 2) {
|
||||
PM.add(createClassIMPCachePass());
|
||||
PM.add(createClassLookupCachePass());
|
||||
}
|
||||
// Definitely don't do extra inlining if we're optimising for size!
|
||||
if (Builder.SizeLevel == 0) {
|
||||
PM.add(createClassMethodInliner());
|
||||
}
|
||||
}
|
||||
/*
|
||||
static struct PluginRegister {
|
||||
PluginRegister() {
|
||||
PassManagerBuilder::addGlobalExtension(PassManagerBuilder::EP_LoopOptimizerEnd,
|
||||
addObjCPasses);
|
||||
}
|
||||
} Register;
|
||||
*/
|
||||
RegisterStandardPasses S(PassManagerBuilder::EP_LoopOptimizerEnd,
|
||||
addObjCPasses);
|
||||
#endif
|
||||
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
llvm::ModulePass *createClassIMPCachePass(void);
|
||||
llvm::ModulePass *createClassLookupCachePass(void);
|
||||
llvm::ModulePass *createClassMethodInliner(void);
|
||||
llvm::FunctionPass *createGNUNonfragileIvarPass(void);
|
||||
llvm::FunctionPass *createGNULoopIMPCachePass(void);
|
||||
llvm::ModulePass *createTypeFeedbackPass(void);
|
||||
llvm::ModulePass *createTypeFeedbackDrivenInlinerPass(void);
|
||||
@ -1,30 +0,0 @@
|
||||
GNUstep Runtime Optimisations
|
||||
=============================
|
||||
|
||||
This directory contains LLVM optimisations specific to libobjc2. To build
|
||||
them, you must copy this directory to llvm/lib/Transforms/GNURuntime (where
|
||||
llvm is the root of your llvm checkout).
|
||||
|
||||
Running GNU make will then create GNUObjCRuntime.so. This library can be
|
||||
passed to opt to run optimisations on bitcode generated with clang or
|
||||
LanguageKit.
|
||||
|
||||
Non-Fragile Ivar Pass
|
||||
---------------------
|
||||
|
||||
Running `opt -gnu-nonfragile-ivar` will invoke the non-fragile instance
|
||||
variable lowering pass. This will turn non-fragile instance variable accesses,
|
||||
which go via one or two indirection layers, into more fragile ones. If a class
|
||||
and all of its superclasses are present in the module then this pass will turn
|
||||
indirect instance variable accesses into hard-coded ones.
|
||||
|
||||
For this pass to be most useful, it should be run as a link-time optimisation.
|
||||
|
||||
Type Feedback
|
||||
-------------
|
||||
|
||||
Running `opt -gnu-objc-type-feedback` enables type feedback. Objective-C
|
||||
message lookups will be replaced by calls to the profiling version in the
|
||||
runtime library. The generated data can then be used for future optimisations
|
||||
(speculative inlining, polymorphic inline caching, and so on), which have not
|
||||
yet been written.
|
||||
@ -1,151 +0,0 @@
|
||||
#include <llvm/Support/CallSite.h>
|
||||
#include <llvm/Linker.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
#include "LLVMCompat.h"
|
||||
|
||||
namespace {
|
||||
struct GNUObjCTypeFeedback : public ModulePass {
|
||||
|
||||
typedef std::pair<CallInst*,CallInst*> callPair;
|
||||
typedef std::vector<callPair > replacementVector;
|
||||
static char ID;
|
||||
uint32_t callsiteCount;
|
||||
LLVMIntegerTy *Int32Ty;
|
||||
GNUObjCTypeFeedback() : ModulePass(ID), callsiteCount(0) {}
|
||||
|
||||
void profileFunction(Function &F, Constant *ModuleID) {
|
||||
for (Function::iterator i=F.begin(), e=F.end() ;
|
||||
i != e ; ++i) {
|
||||
|
||||
Module *M = F.getParent();
|
||||
replacementVector replacements;
|
||||
for (BasicBlock::iterator b=i->begin(), last=i->end() ;
|
||||
b != last ; ++b) {
|
||||
|
||||
CallSite call(b);
|
||||
if (call.getInstruction() && !call.getCalledFunction()) {
|
||||
llvm::SmallVector<llvm::Value*, 4> args;
|
||||
args.push_back(call->getOperand(1));
|
||||
args.push_back(call->getOperand(0)),
|
||||
args.push_back(ModuleID);
|
||||
args.push_back(ConstantInt::get(Int32Ty, callsiteCount++));
|
||||
Constant *profile =
|
||||
M->getOrInsertFunction("objc_msg_profile",
|
||||
Type::getVoidTy(M->getContext()),
|
||||
args[0]->getType(), args[1]->getType(),
|
||||
args[2]->getType(), args[3]->getType(), NULL);
|
||||
CreateCall(profile, args, "", call.getInstruction());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
virtual bool runOnModule(Module &M)
|
||||
{
|
||||
LLVMContext &VMContext = M.getContext();
|
||||
Int32Ty = IntegerType::get(VMContext, 32);
|
||||
LLVMPointerTy *PtrTy = Type::getInt8PtrTy(VMContext);
|
||||
Constant *moduleName =
|
||||
#if (LLVM_MAJOR > 3) || ((LLVM_MAJOR == 3) && (LLVM_MINOR > 0))
|
||||
ConstantDataArray::getString(VMContext, M.getModuleIdentifier(), true);
|
||||
#else
|
||||
ConstantArray::get(VMContext, M.getModuleIdentifier(), true);
|
||||
#endif
|
||||
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 =
|
||||
#if (LLVM_MAJOR > 3) || ((LLVM_MAJOR == 3) && (LLVM_MINOR > 0))
|
||||
llvm::ConstantDataArray::getString(VMContext, F->getName(), true);
|
||||
#else
|
||||
llvm::ConstantArray::get(VMContext, F->getName());
|
||||
#endif
|
||||
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
|
||||
LLVMType *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(GetConstantStruct(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, "",
|
||||
#if LLVM_MAJOR < 3 || (LLVM_MAJOR == 3 && LLVM_MINOR < 2)
|
||||
GCL->isThreadLocal());
|
||||
#else
|
||||
GCL-> getThreadLocalMode());
|
||||
#endif
|
||||
GCL->getParent()->getGlobalList().insert(GCL, NGV);
|
||||
NGV->takeName(GCL);
|
||||
GCL->replaceAllUsesWith(NGV);
|
||||
GCL->eraseFromParent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
char GNUObjCTypeFeedback::ID = 0;
|
||||
RegisterPass<GNUObjCTypeFeedback> X("gnu-objc-type-feedback",
|
||||
"Objective-C type feedback for the GNU runtime.", false, true);
|
||||
}
|
||||
|
||||
ModulePass *createTypeFeedbackPass(void) {
|
||||
return new GNUObjCTypeFeedback();
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
#include "LLVMCompat.h"
|
||||
#include "llvm/Analysis/InlineCost.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/Linker.h"
|
||||
#include <vector>
|
||||
#include "TypeInfoProvider.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace GNUstep;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
GNUObjCTypeFeedbackDrivenInliner() : ModulePass(ID), callsiteCount(0) {}
|
||||
|
||||
virtual bool runOnModule(Module &M)
|
||||
{
|
||||
bool modified = false;
|
||||
LLVMContext &VMContext = M.getContext();
|
||||
Int32Ty = IntegerType::get(VMContext, 32);
|
||||
TypeInfoProvider::CallSiteMap *SiteMap =
|
||||
TypeInfoProvider::SharedTypeInfoProvider()->getCallSitesForModule(M);
|
||||
SmallPtrSet<const Function *, 16> NeverInline;
|
||||
|
||||
//TypeInfoProvider::SharedTypeInfoProvider()->PrintStatistics();
|
||||
GNUstep::IMPCacher cacher = GNUstep::IMPCacher(M.getContext(), this);
|
||||
InlineCostAnalyzer CA;
|
||||
SmallVector<CallSite, 16> messages;
|
||||
|
||||
for (Module::iterator F=M.begin(), fend=M.end() ;
|
||||
F != fend ; ++F) {
|
||||
|
||||
|
||||
if (F->isDeclaration()) { continue; }
|
||||
|
||||
for (Function::iterator i=F->begin(), end=F->end() ;
|
||||
i != end ; ++i) {
|
||||
for (BasicBlock::iterator b=i->begin(), last=i->end() ;
|
||||
b != last ; ++b) {
|
||||
CallSite call(b);
|
||||
if (call.getInstruction() && !call.getCalledFunction()) {
|
||||
messages.push_back(call);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TypeInfoProvider::CallSiteMap::iterator Entry = SiteMap->begin();
|
||||
|
||||
for (SmallVectorImpl<CallSite>::iterator i=messages.begin(),
|
||||
e=messages.end() ; e!=i ; ++i, ++Entry) {
|
||||
|
||||
if (Entry->size() == 1) {
|
||||
|
||||
Function *method = M.getFunction(Entry->begin()->getKey());
|
||||
if (0 == method || method->isDeclaration()) { continue; }
|
||||
|
||||
#if (LLVM_MAJOR > 3) || ((LLVM_MAJOR == 3) && (LLVM_MINOR > 0))
|
||||
InlineCost IC = CA.getInlineCost((*i), method, 200);
|
||||
#else
|
||||
InlineCost IC = CA.getInlineCost((*i), method, NeverInline);
|
||||
#define getCost getValue
|
||||
#endif
|
||||
// FIXME: 200 is a random number. Pick a better one!
|
||||
if (IC.isAlways() || (IC.isVariable() && IC.getCost() < 200)) {
|
||||
cacher.SpeculativelyInline((*i).getInstruction(), method);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
// FIXME: Inline the most popular call if one is much more popular
|
||||
// than the others.
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
char GNUObjCTypeFeedbackDrivenInliner::ID = 0;
|
||||
RegisterPass<GNUObjCTypeFeedbackDrivenInliner> X("gnu-objc-feedback-driven-inline",
|
||||
"Objective-C type feedback-driven inliner for the GNU runtime.", false,
|
||||
true);
|
||||
}
|
||||
|
||||
ModulePass *createTypeFeedbackDrivenInlinerPass(void) {
|
||||
return new GNUObjCTypeFeedbackDrivenInliner();
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
#include "LLVMCompat.h"
|
||||
#include "IMPCacher.h"
|
||||
#include "llvm/Support/CallSite.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include "llvm/Transforms/Utils/Cloning.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
|
||||
using namespace llvm;
|
||||
namespace GNUstep
|
||||
{
|
||||
class TypeInfoProvider
|
||||
{
|
||||
public:
|
||||
typedef StringMap<uintptr_t> CallSiteEntry;
|
||||
//typedef std::vector<CallSiteEntry> CallSiteMap;
|
||||
typedef SmallVector<CallSiteEntry, 16> CallSiteMap;
|
||||
private:
|
||||
struct callsite_info
|
||||
{
|
||||
uintptr_t moduleID;
|
||||
int32_t callsiteID;
|
||||
uintptr_t methodID;
|
||||
};
|
||||
const char *symbol_table;
|
||||
size_t symbol_size;
|
||||
StringMap<CallSiteMap> CallSiteRecords;
|
||||
|
||||
void loadCallsiteRecords(callsite_info *callsite_records, size_t size);
|
||||
TypeInfoProvider(void);
|
||||
|
||||
public:
|
||||
CallSiteMap* getCallSitesForModule(Module &M);
|
||||
void PrintStatistics();
|
||||
static TypeInfoProvider* SharedTypeInfoProvider();
|
||||
~TypeInfoProvider();
|
||||
};
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue