diff options
| author | Eli Bendersky <eliben@chromium.org> | 2013-05-14 10:43:34 -0700 |
|---|---|---|
| committer | Eli Bendersky <eliben@chromium.org> | 2013-05-14 10:43:34 -0700 |
| commit | 4a71e626a34a257c1da0257cf580f26d12729b5d (patch) | |
| tree | 44e8924210103e4c06dc252d7d24dbd8d533ca75 /lib/Transforms/NaCl/RewritePNaClLibraryCalls.cpp | |
| parent | 442f3114c0226f026a5f278becc6554eaa48209a (diff) | |
Adding a pass - RewritePNaClLibraryCalls, that replaces known library calls with stable bitcode intrinsics.
Starting with setjmp and longjmp.
BUG=https://code.google.com/p/nativeclient/issues/detail?id=3429
R=jvoung@chromium.org, mseaborn@chromium.org
Review URL: https://codereview.chromium.org/14617017
Diffstat (limited to 'lib/Transforms/NaCl/RewritePNaClLibraryCalls.cpp')
| -rw-r--r-- | lib/Transforms/NaCl/RewritePNaClLibraryCalls.cpp | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/lib/Transforms/NaCl/RewritePNaClLibraryCalls.cpp b/lib/Transforms/NaCl/RewritePNaClLibraryCalls.cpp new file mode 100644 index 0000000000..2373343f59 --- /dev/null +++ b/lib/Transforms/NaCl/RewritePNaClLibraryCalls.cpp @@ -0,0 +1,225 @@ +//===- RewritePNaClLibraryCalls.cpp - PNaCl library calls to intrinsics ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass replaces calls to known library functions with calls to intrinsics +// that are part of the PNaCl stable bitcode ABI. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/NaCl.h" + +using namespace llvm; + +namespace { + class RewritePNaClLibraryCalls : public ModulePass { + public: + static char ID; + RewritePNaClLibraryCalls() : + ModulePass(ID), TheModule(NULL) { + // This is a module pass because it may have to introduce + // intrinsic declarations into the module and modify a global function. + initializeRewritePNaClLibraryCallsPass(*PassRegistry::getPassRegistry()); + } + + virtual bool runOnModule(Module &M); + private: + /// Rewrites the given \p Call of setjmp to a direct intrinsic call. + void rewriteSetjmpCall(CallInst *Call); + + /// Rewrites the given \p Call of longjmp to a direct intrinsic call. + void rewriteLongjmpCall(CallInst *Call); + + /// Populates the body of longjmp as a wrapper of the intrinsic call. + /// Should only be called once. Modifies the given \p LongjmpFunc. + void populateLongjmpWrapper(Function *LongjmpFunc); + + /// Sanity check that the types of these functions are what we expect them + /// to be. + void sanityCheckSetjmpFunc(Function *SetjmpFunc); + void sanityCheckLongjmpFunc(Function *LongjmpFunc); + + /// The module this pass runs on. + Module *TheModule; + }; +} + +char RewritePNaClLibraryCalls::ID = 0; +INITIALIZE_PASS(RewritePNaClLibraryCalls, "rewrite-pnacl-library-calls", + "Rewrite PNaCl library calls to stable intrinsics", + false, false) + +bool RewritePNaClLibraryCalls::runOnModule(Module &M) { + TheModule = &M; + bool Changed = false; + + // Iterate over all uses of the setjmp, if it exists in the module with + // external linkage. If it exists but the linkage is not external, this may + // come from code that defines its own function named setjmp and doesn't + // include <setjmp.h>. In such a case we leave the code as it is. + // + // The calls are replaced with intrinsics. All other uses of setjmp are + // disallowed (taking the address of setjmp is disallowed in C and C++). + Function *SetjmpFunc = TheModule->getFunction("setjmp"); + + if (SetjmpFunc && SetjmpFunc->hasExternalLinkage()) { + sanityCheckSetjmpFunc(SetjmpFunc); + + for (Value::use_iterator UI = SetjmpFunc->use_begin(), + UE = SetjmpFunc->use_end(); UI != UE;) { + Value *Use = *UI++; + if (CallInst *Call = dyn_cast<CallInst>(Use)) { + rewriteSetjmpCall(Call); + Changed = true; + } else { + report_fatal_error("Taking the address of setjmp is invalid"); + } + } + } + + // For longjmp things are a little more complicated, since longjmp's address + // can be taken. Therefore, longjmp can appear in a variety of Uses. The + // common case is still a direct call and we want that to be as efficient as + // possible, so we rewrite it into a direct intrinsic call. If there are other + // uses, the actual body of longjmp is populated with a wrapper that calls + // the intrinsic. + Function *LongjmpFunc = TheModule->getFunction("longjmp"); + + if (LongjmpFunc && LongjmpFunc->hasExternalLinkage()) { + sanityCheckLongjmpFunc(LongjmpFunc); + + for (Value::use_iterator UI = LongjmpFunc->use_begin(), + UE = LongjmpFunc->use_end(); UI != UE;) { + Value *Use = *UI++; + if (CallInst *Call = dyn_cast<CallInst>(Use)) { + rewriteLongjmpCall(Call); + Changed = true; + } + } + + // If additional uses remain, these aren't calls; populate the wrapper. + if (!LongjmpFunc->use_empty()) { + populateLongjmpWrapper(LongjmpFunc); + Changed = true; + } + } + + return Changed; +} + +void RewritePNaClLibraryCalls::rewriteSetjmpCall(CallInst *Call) { + // Find the intrinsic function. + Function *NaClSetjmpFunc = Intrinsic::getDeclaration(TheModule, + Intrinsic::nacl_setjmp); + // Cast the jmp_buf argument to the type NaClSetjmpCall expects. + Type *PtrTy = NaClSetjmpFunc->getFunctionType()->getParamType(0); + BitCastInst *JmpBufCast = new BitCastInst(Call->getArgOperand(0), PtrTy, + "jmp_buf_i8", Call); + const DebugLoc &DLoc = Call->getDebugLoc(); + JmpBufCast->setDebugLoc(DLoc); + + // Emit the updated call. + SmallVector<Value *, 1> Args; + Args.push_back(JmpBufCast); + CallInst *NaClSetjmpCall = CallInst::Create(NaClSetjmpFunc, Args, "", Call); + NaClSetjmpCall->setDebugLoc(DLoc); + NaClSetjmpCall->takeName(Call); + + // Replace the original call. + Call->replaceAllUsesWith(NaClSetjmpCall); + Call->eraseFromParent(); +} + +void RewritePNaClLibraryCalls::sanityCheckLongjmpFunc(Function *LongjmpFunc) { + FunctionType *FTy = LongjmpFunc->getFunctionType(); + if (!(FTy->getNumParams() == 2 && + FTy->getReturnType()->isVoidTy() && + FTy->getParamType(0)->isPointerTy() && + FTy->getParamType(1)->isIntegerTy())) { + report_fatal_error("Wrong signature of longjmp"); + } +} + +void RewritePNaClLibraryCalls::sanityCheckSetjmpFunc(Function *SetjmpFunc) { + FunctionType *FTy = SetjmpFunc->getFunctionType(); + if (!(FTy->getNumParams() == 1 && + FTy->getReturnType()->isIntegerTy() && + FTy->getParamType(0)->isPointerTy())) { + report_fatal_error("Wrong signature of setjmp"); + } +} + +void RewritePNaClLibraryCalls::rewriteLongjmpCall(CallInst *Call) { + // Find the intrinsic function. + Function *NaClLongjmpFunc = Intrinsic::getDeclaration( + TheModule, Intrinsic::nacl_longjmp); + // Cast the jmp_buf argument to the type NaClLongjmpCall expects. + Type *PtrTy = NaClLongjmpFunc->getFunctionType()->getParamType(0); + BitCastInst *JmpBufCast = new BitCastInst(Call->getArgOperand(0), PtrTy, + "jmp_buf_i8", Call); + const DebugLoc &DLoc = Call->getDebugLoc(); + JmpBufCast->setDebugLoc(DLoc); + + // Emit the call. + SmallVector<Value *, 2> Args; + Args.push_back(JmpBufCast); + Args.push_back(Call->getArgOperand(1)); + CallInst *NaClLongjmpCall = CallInst::Create(NaClLongjmpFunc, Args, "", Call); + NaClLongjmpCall->setDebugLoc(DLoc); + // No takeName here since longjmp is a void call that does not get assigned to + // a value. + + // Remove the original call. There's no need for RAUW because longjmp + // returns void. + Call->eraseFromParent(); +} + +void RewritePNaClLibraryCalls::populateLongjmpWrapper(Function *LongjmpFunc) { + assert(LongjmpFunc->size() == 0 && + "Expected to be called when longjmp has an empty body"); + + // Populate longjmp with code. + LLVMContext &Context = TheModule->getContext(); + BasicBlock *BB = BasicBlock::Create(Context, "entry", LongjmpFunc); + + Function::arg_iterator LongjmpArgs = LongjmpFunc->arg_begin(); + Value *EnvArg = LongjmpArgs++; + EnvArg->setName("env"); + Value *ValArg = LongjmpArgs++; + ValArg->setName("val"); + + // Find the intrinsic function. + Function *NaClLongjmpFunc = Intrinsic::getDeclaration( + TheModule, Intrinsic::nacl_longjmp); + // Cast the jmp_buf argument to the type NaClLongjmpCall expects. + Type *PtrTy = NaClLongjmpFunc->getFunctionType()->getParamType(0); + BitCastInst *JmpBufCast = new BitCastInst(EnvArg, PtrTy, "jmp_buf_i8", BB); + + // Emit the call. + SmallVector<Value *, 2> Args; + Args.push_back(JmpBufCast); + Args.push_back(ValArg); + CallInst::Create(NaClLongjmpFunc, Args, "", BB); + + // Insert an unreachable instruction to terminate this function since longjmp + // does not return. + new UnreachableInst(Context, BB); + + // Finally, set the linkage to internal + LongjmpFunc->setLinkage(Function::InternalLinkage); +} + +ModulePass *llvm::createRewritePNaClLibraryCallsPass() { + return new RewritePNaClLibraryCalls(); +} |
