#define DEBUG_TYPE "lowersetjmp"
#include "llvm/Transforms/IPO.h"
#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Instructions.h"
#include "llvm/Intrinsics.h"
#include "llvm/Module.h"
#include "llvm/Pass.h"
#include "llvm/Support/CFG.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/InstVisitor.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/VectorExtras.h"
#include "llvm/ADT/SmallVector.h"
#include <map>
using namespace llvm;
STATISTIC(LongJmpsTransformed, "Number of longjmps transformed");
STATISTIC(SetJmpsTransformed , "Number of setjmps transformed");
STATISTIC(CallsTransformed , "Number of calls invokified");
STATISTIC(InvokesTransformed , "Number of invokes modified");
namespace {
class VISIBILITY_HIDDEN LowerSetJmp : public ModulePass,
public InstVisitor<LowerSetJmp> {
Constant *InitSJMap; Constant *DestroySJMap; Constant *AddSJToMap; Constant *ThrowLongJmp; Constant *TryCatchLJ; Constant *IsLJException; Constant *GetLJValue;
typedef std::pair<SwitchInst*, CallInst*> SwitchValuePair;
std::set<BasicBlock*> DFSBlocks;
std::map<Function*, AllocaInst*> SJMap;
std::map<const Function*, BasicBlock*> RethrowBBMap;
std::map<const Function*, BasicBlock*> PrelimBBMap;
std::map<const Function*, SwitchValuePair> SwitchValMap;
std::map<const Function*, unsigned> SetJmpIDMap;
AllocaInst* GetSetJmpMap(Function* Func);
BasicBlock* GetRethrowBB(Function* Func);
SwitchValuePair GetSJSwitch(Function* Func, BasicBlock* Rethrow);
void TransformLongJmpCall(CallInst* Inst);
void TransformSetJmpCall(CallInst* Inst);
bool IsTransformableFunction(const std::string& Name);
public:
static char ID; LowerSetJmp() : ModulePass(&ID) {}
void visitCallInst(CallInst& CI);
void visitInvokeInst(InvokeInst& II);
void visitReturnInst(ReturnInst& RI);
void visitUnwindInst(UnwindInst& UI);
bool runOnModule(Module& M);
bool doInitialization(Module& M);
};
}
char LowerSetJmp::ID = 0;
static RegisterPass<LowerSetJmp> X("lowersetjmp", "Lower Set Jump");
bool LowerSetJmp::runOnModule(Module& M) {
bool Changed = false;
Function* SetJmp = M.getFunction("llvm.setjmp");
Function* LongJmp = M.getFunction("llvm.longjmp");
if ((!LongJmp || LongJmp->use_empty()) &&
(!SetJmp || SetJmp->use_empty())) return false;
doInitialization(M);
if (SetJmp) {
for (Value::use_iterator B = SetJmp->use_begin(), E = SetJmp->use_end();
B != E; ++B) {
BasicBlock* BB = cast<Instruction>(*B)->getParent();
for (df_ext_iterator<BasicBlock*> I = df_ext_begin(BB, DFSBlocks),
E = df_ext_end(BB, DFSBlocks); I != E; ++I)
;
}
while (!SetJmp->use_empty()) {
assert(isa<CallInst>(SetJmp->use_back()) &&
"User of setjmp intrinsic not a call?");
TransformSetJmpCall(cast<CallInst>(SetJmp->use_back()));
Changed = true;
}
}
if (LongJmp)
while (!LongJmp->use_empty()) {
assert(isa<CallInst>(LongJmp->use_back()) &&
"User of longjmp intrinsic not a call?");
TransformLongJmpCall(cast<CallInst>(LongJmp->use_back()));
Changed = true;
}
for (std::map<Function*, AllocaInst*>::iterator
B = SJMap.begin(), E = SJMap.end(); B != E; ++B) {
Function* F = B->first;
for (Function::iterator BB = F->begin(), BE = F->end(); BB != BE; ++BB)
for (BasicBlock::iterator IB = BB->begin(), IE = BB->end(); IB != IE; ) {
visit(*IB++);
if (IB != BB->end() && IB->getParent() != BB)
break; }
}
DFSBlocks.clear();
SJMap.clear();
RethrowBBMap.clear();
PrelimBBMap.clear();
SwitchValMap.clear();
SetJmpIDMap.clear();
return Changed;
}
bool LowerSetJmp::doInitialization(Module& M)
{
const Type *SBPTy = PointerType::getUnqual(Type::Int8Ty);
const Type *SBPPTy = PointerType::getUnqual(SBPTy);
InitSJMap = M.getOrInsertFunction("__llvm_sjljeh_init_setjmpmap",
Type::VoidTy, SBPPTy, (Type *)0);
DestroySJMap = M.getOrInsertFunction("__llvm_sjljeh_destroy_setjmpmap",
Type::VoidTy, SBPPTy, (Type *)0);
AddSJToMap = M.getOrInsertFunction("__llvm_sjljeh_add_setjmp_to_map",
Type::VoidTy, SBPPTy, SBPTy,
Type::Int32Ty, (Type *)0);
ThrowLongJmp = M.getOrInsertFunction("__llvm_sjljeh_throw_longjmp",
Type::VoidTy, SBPTy, Type::Int32Ty,
(Type *)0);
TryCatchLJ =
M.getOrInsertFunction("__llvm_sjljeh_try_catching_longjmp_exception",
Type::Int32Ty, SBPPTy, (Type *)0);
IsLJException = M.getOrInsertFunction("__llvm_sjljeh_is_longjmp_exception",
Type::Int1Ty, (Type *)0);
GetLJValue = M.getOrInsertFunction("__llvm_sjljeh_get_longjmp_value",
Type::Int32Ty, (Type *)0);
return true;
}
bool LowerSetJmp::IsTransformableFunction(const std::string& Name) {
std::string SJLJEh("__llvm_sjljeh");
if (Name.size() > SJLJEh.size())
return std::string(Name.begin(), Name.begin() + SJLJEh.size()) != SJLJEh;
return true;
}
void LowerSetJmp::TransformLongJmpCall(CallInst* Inst)
{
const Type* SBPTy = PointerType::getUnqual(Type::Int8Ty);
CastInst* CI =
new BitCastInst(Inst->getOperand(1), SBPTy, "LJBuf", Inst);
SmallVector<Value *, 2> Args;
Args.push_back(CI);
Args.push_back(Inst->getOperand(2));
CallInst::Create(ThrowLongJmp, Args.begin(), Args.end(), "", Inst);
SwitchValuePair& SVP = SwitchValMap[Inst->getParent()->getParent()];
if (SVP.first)
BranchInst::Create(SVP.first->getParent(), Inst);
else
new UnwindInst(Inst);
BasicBlock *BB = Inst->getParent();
Instruction *Removed;
do {
Removed = &BB->back();
if (!Removed->use_empty())
Removed->replaceAllUsesWith(UndefValue::get(Removed->getType()));
Removed->eraseFromParent();
} while (Removed != Inst);
++LongJmpsTransformed;
}
AllocaInst* LowerSetJmp::GetSetJmpMap(Function* Func)
{
if (SJMap[Func]) return SJMap[Func];
Instruction* Inst = Func->getEntryBlock().begin();
assert(Inst && "Couldn't find even ONE instruction in entry block!");
const Type *SBPTy = PointerType::getUnqual(Type::Int8Ty);
AllocaInst* Map = new AllocaInst(SBPTy, 0, "SJMap", Inst);
CallInst::Create(InitSJMap, Map, "", Inst);
return SJMap[Func] = Map;
}
BasicBlock* LowerSetJmp::GetRethrowBB(Function* Func)
{
if (RethrowBBMap[Func]) return RethrowBBMap[Func];
BasicBlock* Rethrow = BasicBlock::Create("RethrowExcept", Func);
new UnwindInst(Rethrow);
return RethrowBBMap[Func] = Rethrow;
}
LowerSetJmp::SwitchValuePair LowerSetJmp::GetSJSwitch(Function* Func,
BasicBlock* Rethrow)
{
if (SwitchValMap[Func].first) return SwitchValMap[Func];
BasicBlock* LongJmpPre = BasicBlock::Create("LongJmpBlkPre", Func);
PrelimBBMap[Func] = LongJmpPre;
CallInst* Cond = CallInst::Create(IsLJException, "IsLJExcept", LongJmpPre);
BasicBlock* DecisionBB = BasicBlock::Create("LJDecisionBB", Func);
BranchInst::Create(DecisionBB, Rethrow, Cond, LongJmpPre);
CallInst* LJVal = CallInst::Create(GetLJValue, "LJVal", DecisionBB);
CallInst* SJNum = CallInst::Create(TryCatchLJ, GetSetJmpMap(Func), "SJNum",
DecisionBB);
SwitchInst* SI = SwitchInst::Create(SJNum, Rethrow, 0, DecisionBB);
return SwitchValMap[Func] = SwitchValuePair(SI, LJVal);
}
void LowerSetJmp::TransformSetJmpCall(CallInst* Inst)
{
BasicBlock* ABlock = Inst->getParent();
Function* Func = ABlock->getParent();
const Type* SBPTy = PointerType::getUnqual(Type::Int8Ty);
CastInst* BufPtr =
new BitCastInst(Inst->getOperand(1), SBPTy, "SBJmpBuf", Inst);
std::vector<Value*> Args =
make_vector<Value*>(GetSetJmpMap(Func), BufPtr,
ConstantInt::get(Type::Int32Ty,
SetJmpIDMap[Func]++), 0);
CallInst::Create(AddSJToMap, Args.begin(), Args.end(), "", Inst);
std::set<Instruction*> InstrsAfterCall;
for (BasicBlock::iterator I = ++BasicBlock::iterator(Inst), E = ABlock->end();
I != E; ++I)
InstrsAfterCall.insert(I);
for (BasicBlock::iterator II = ABlock->begin();
II != BasicBlock::iterator(Inst); ++II)
for (Value::use_iterator UI = II->use_begin(), E = II->use_end();
UI != E; ++UI)
if (cast<Instruction>(*UI)->getParent() != ABlock ||
InstrsAfterCall.count(cast<Instruction>(*UI))) {
DemoteRegToStack(*II);
break;
}
InstrsAfterCall.clear();
BasicBlock* SetJmpContBlock = ABlock->splitBasicBlock(Inst);
assert(SetJmpContBlock && "Couldn't split setjmp BB!!");
SetJmpContBlock->setName(ABlock->getName()+"SetJmpCont");
DFSBlocks.insert(SetJmpContBlock);
PHINode* PHI = PHINode::Create(Type::Int32Ty, "SetJmpReturn", Inst);
PHI->addIncoming(ConstantInt::getNullValue(Type::Int32Ty), ABlock);
SwitchValuePair SVP = GetSJSwitch(Func, GetRethrowBB(Func));
SVP.first->addCase(ConstantInt::get(Type::Int32Ty, SetJmpIDMap[Func] - 1),
SetJmpContBlock);
PHI->addIncoming(SVP.second, SVP.second->getParent());
Inst->replaceAllUsesWith(PHI);
Inst->eraseFromParent();
++SetJmpsTransformed;
}
void LowerSetJmp::visitCallInst(CallInst& CI)
{
if (CI.getCalledFunction())
if (!IsTransformableFunction(CI.getCalledFunction()->getName()) ||
CI.getCalledFunction()->isIntrinsic()) return;
BasicBlock* OldBB = CI.getParent();
if (!DFSBlocks.count(OldBB)) return;
BasicBlock* NewBB = OldBB->splitBasicBlock(CI);
assert(NewBB && "Couldn't split BB of \"call\" instruction!!");
DFSBlocks.insert(NewBB);
NewBB->setName("Call2Invoke");
Function* Func = OldBB->getParent();
TerminatorInst* Term = OldBB->getTerminator();
std::vector<Value*> Params(CI.op_begin() + 1, CI.op_end());
InvokeInst* II =
InvokeInst::Create(CI.getCalledValue(), NewBB, PrelimBBMap[Func],
Params.begin(), Params.end(), CI.getName(), Term);
II->setCallingConv(CI.getCallingConv());
II->setAttributes(CI.getAttributes());
CI.replaceAllUsesWith(II);
CI.eraseFromParent();
Term->eraseFromParent();
++CallsTransformed;
}
void LowerSetJmp::visitInvokeInst(InvokeInst& II)
{
if (II.getCalledFunction())
if (!IsTransformableFunction(II.getCalledFunction()->getName()) ||
II.getCalledFunction()->isIntrinsic()) return;
BasicBlock* BB = II.getParent();
if (!DFSBlocks.count(BB)) return;
BasicBlock* ExceptBB = II.getUnwindDest();
Function* Func = BB->getParent();
BasicBlock* NewExceptBB = BasicBlock::Create("InvokeExcept", Func);
CallInst* IsLJExcept = CallInst::Create(IsLJException, "IsLJExcept",
NewExceptBB);
BranchInst::Create(PrelimBBMap[Func], ExceptBB, IsLJExcept, NewExceptBB);
II.setUnwindDest(NewExceptBB);
++InvokesTransformed;
}
void LowerSetJmp::visitReturnInst(ReturnInst &RI) {
Function* Func = RI.getParent()->getParent();
CallInst::Create(DestroySJMap, GetSetJmpMap(Func), "", &RI);
}
void LowerSetJmp::visitUnwindInst(UnwindInst &UI) {
Function* Func = UI.getParent()->getParent();
CallInst::Create(DestroySJMap, GetSetJmpMap(Func), "", &UI);
}
ModulePass *llvm::createLowerSetJmpPass() {
return new LowerSetJmp();
}