diff options
author | Eli Bendersky <eliben@chromium.org> | 2013-06-25 15:14:49 -0700 |
---|---|---|
committer | Eli Bendersky <eliben@chromium.org> | 2013-06-25 15:14:49 -0700 |
commit | 838d8e9753498212ca9587dd0d5b3ddebe824068 (patch) | |
tree | 1d8e1a079f7a8878d37f3434cb311dae79b03855 | |
parent | 35d0901ac239469021a36f21e488cf20484f2095 (diff) |
Support for mem* library functions in stable bitcode via intrinsics.
* Don't preserve external linking for mem{cpy,move,cmp} during LTO.
* In the RewritePNaClLibraryCalls pass - add rewriting of mem* calls to
appropriate intrinsics, similarly to the way it was done for longjmp.
BUG=https://code.google.com/p/nativeclient/issues/detail?id=3493
R=mseaborn@chromium.org
Review URL: https://codereview.chromium.org/17622003
-rw-r--r-- | lib/CodeGen/IntrinsicLowering.cpp | 1 | ||||
-rw-r--r-- | lib/Transforms/NaCl/RewritePNaClLibraryCalls.cpp | 479 | ||||
-rw-r--r-- | test/Transforms/NaCl/rewrite-memfuncs-no-store.ll | 40 | ||||
-rw-r--r-- | test/Transforms/NaCl/rewrite-memfuncs-noncall-uses.ll | 30 |
4 files changed, 442 insertions, 108 deletions
diff --git a/lib/CodeGen/IntrinsicLowering.cpp b/lib/CodeGen/IntrinsicLowering.cpp index 0c035718a4..f16e5978d9 100644 --- a/lib/CodeGen/IntrinsicLowering.cpp +++ b/lib/CodeGen/IntrinsicLowering.cpp @@ -108,7 +108,6 @@ static CallInst *ReplaceCallWith(const char *NewFn, CallInst *CI, // Bitcode can then internalize symbols. static const char *IntrinsicNames[] = { "abort", - "memcpy", "memset", "memmove", "powf", "pow", "powl", NULL }; diff --git a/lib/Transforms/NaCl/RewritePNaClLibraryCalls.cpp b/lib/Transforms/NaCl/RewritePNaClLibraryCalls.cpp index ea50457070..726c3157c1 100644 --- a/lib/Transforms/NaCl/RewritePNaClLibraryCalls.cpp +++ b/lib/Transforms/NaCl/RewritePNaClLibraryCalls.cpp @@ -19,6 +19,7 @@ #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include "llvm/Transforms/NaCl.h" +#include <cstdarg> using namespace llvm; @@ -27,7 +28,9 @@ namespace { public: static char ID; RewritePNaClLibraryCalls() : - ModulePass(ID), TheModule(NULL) { + ModulePass(ID), TheModule(NULL), Context(NULL), + LongjmpIntrinsic(NULL), MemcpyIntrinsic(NULL), + MemmoveIntrinsic(NULL), MemsetIntrinsic(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()); @@ -35,23 +38,79 @@ namespace { 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); + typedef void (RewritePNaClLibraryCalls::*SanityCheckFunc)(Function *); + typedef void (RewritePNaClLibraryCalls::*RewriteCallFunc)(CallInst *); + typedef void (RewritePNaClLibraryCalls::*PopulateWrapperFunc)(Function *); + + /// Handles a certain pattern of library function -> intrinsic rewrites. + /// Currently all library functions this pass knows how to rewrite fall into + /// this pattern. + /// RewriteLibraryCall performs the rewrite for a single library function + /// and is customized by a number of method pointers that collectively + /// handle one of the supported library functions. + /// + /// \p LibraryFunctionName Name of the library function to look for. + /// \p SanityChecker Method that makes sure the library function has the + /// signature we expect it to have. + /// \p CallRewriter Method that rewrites the library function call into an + /// intrinsic call. + /// \p OnlyCallsAllowed True iff only calls are allowed to this library + /// function. + /// \p WrapperPopulator If not only calls are allowed, this method is + /// called to populate the body of the library function with a wrapped + /// intrinsic call. If only calls are allowed, this parameter may be set + /// to NULL. + bool RewriteLibraryCall( + const char *LibraryFunctionName, + SanityCheckFunc SanityChecker, + RewriteCallFunc CallRewriter, + bool OnlyCallsAllowed, + PopulateWrapperFunc WrapperPopulator); - /// 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); + void sanityCheckMemcpyFunc(Function *MemcpyFunc); + void sanityCheckMemmoveFunc(Function *MemmoveFunc); + void sanityCheckMemsetFunc(Function *MemsetFunc); + + void rewriteSetjmpCall(CallInst *Call); + void rewriteLongjmpCall(CallInst *Call); + void rewriteMemcpyCall(CallInst *Call); + void rewriteMemmoveCall(CallInst *Call); + void rewriteMemsetCall(CallInst *Call); - /// The module this pass runs on. + void populateLongjmpWrapper(Function *LongjmpFunc); + void populateMemcpyWrapper(Function *MemcpyFunc); + void populateMemmoveWrapper(Function *MemmoveFunc); + void populateMemsetWrapper(Function *MemsetFunc); + + /// Generic implementation of populating a wrapper function. + /// Initially, the function exists in the module as a declaration with + /// unnamed arguments. This method is called with a NULL-terminated list + /// of argument names that get assigned in the generated IR for + /// readability. + void populateWrapperCommon( + Function *Func, + StringRef FuncName, + RewriteCallFunc CallRewriter, + bool CallCannotReturn, + ...); + + /// Find and cache known intrinsics. + Function *findLongjmpIntrinsic(); + Function *findMemcpyIntrinsic(); + Function *findMemmoveIntrinsic(); + Function *findMemsetIntrinsic(); + + /// Cached data that remains the same throughout a module run. Module *TheModule; + LLVMContext *Context; + + /// These are cached but computed lazily. + Function *LongjmpIntrinsic; + Function *MemcpyIntrinsic; + Function *MemmoveIntrinsic; + Function *MemsetIntrinsic; }; } @@ -60,60 +119,46 @@ INITIALIZE_PASS(RewritePNaClLibraryCalls, "rewrite-pnacl-library-calls", "Rewrite PNaCl library calls to stable intrinsics", false, false) -bool RewritePNaClLibraryCalls::runOnModule(Module &M) { - TheModule = &M; +bool RewritePNaClLibraryCalls::RewriteLibraryCall( + const char *LibraryFunctionName, + SanityCheckFunc SanityChecker, + RewriteCallFunc CallRewriter, + bool OnlyCallsAllowed, + PopulateWrapperFunc WrapperPopulator) { 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"); - } - } - SetjmpFunc->eraseFromParent(); - } - - // 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); + Function *LibFunc = TheModule->getFunction(LibraryFunctionName); - for (Value::use_iterator UI = LongjmpFunc->use_begin(), - UE = LongjmpFunc->use_end(); UI != UE;) { + // Iterate over all uses of this function, 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 private function with the same name + // and doesn't actually include the standard libc header declaring it. + // In such a case we leave the code as it is. + if (LibFunc && LibFunc->hasExternalLinkage()) { + (this->*(SanityChecker))(LibFunc); + + // Handle all uses that are calls. These are simply replaced with + // equivalent intrinsic calls. + for (Value::use_iterator UI = LibFunc->use_begin(), + UE = LibFunc->use_end(); UI != UE;) { Value *Use = *UI++; if (CallInst *Call = dyn_cast<CallInst>(Use)) { - rewriteLongjmpCall(Call); + (this->*(CallRewriter))(Call); Changed = true; } } - // If additional uses remain, these aren't calls; populate the wrapper. - if (LongjmpFunc->use_empty()) { - LongjmpFunc->eraseFromParent(); + if (LibFunc->use_empty()) { + LibFunc->eraseFromParent(); + } else if (OnlyCallsAllowed) { + // If additional uses remain, these aren't calls. + report_fatal_error(Twine("Taking the address of ") + + LibraryFunctionName + " is invalid"); } else { - populateLongjmpWrapper(LongjmpFunc); + // If non-call uses remain and allowed for this function, populate it + // with a wrapper. + (this->*(WrapperPopulator))(LibFunc); + LibFunc->setLinkage(Function::InternalLinkage); Changed = true; } } @@ -121,27 +166,43 @@ bool RewritePNaClLibraryCalls::runOnModule(Module &M) { 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); +bool RewritePNaClLibraryCalls::runOnModule(Module &M) { + TheModule = &M; + Context = &TheModule->getContext(); + bool Changed = false; - // 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); + Changed |= RewriteLibraryCall( + "setjmp", + &RewritePNaClLibraryCalls::sanityCheckSetjmpFunc, + &RewritePNaClLibraryCalls::rewriteSetjmpCall, + true, + NULL); + Changed |= RewriteLibraryCall( + "longjmp", + &RewritePNaClLibraryCalls::sanityCheckLongjmpFunc, + &RewritePNaClLibraryCalls::rewriteLongjmpCall, + false, + &RewritePNaClLibraryCalls::populateLongjmpWrapper); + Changed |= RewriteLibraryCall( + "memset", + &RewritePNaClLibraryCalls::sanityCheckMemsetFunc, + &RewritePNaClLibraryCalls::rewriteMemsetCall, + false, + &RewritePNaClLibraryCalls::populateMemsetWrapper); + Changed |= RewriteLibraryCall( + "memcpy", + &RewritePNaClLibraryCalls::sanityCheckMemcpyFunc, + &RewritePNaClLibraryCalls::rewriteMemcpyCall, + false, + &RewritePNaClLibraryCalls::populateMemcpyWrapper); + Changed |= RewriteLibraryCall( + "memmove", + &RewritePNaClLibraryCalls::sanityCheckMemmoveFunc, + &RewritePNaClLibraryCalls::rewriteMemmoveCall, + false, + &RewritePNaClLibraryCalls::populateMemmoveWrapper); - // Replace the original call. - Call->replaceAllUsesWith(NaClSetjmpCall); - Call->eraseFromParent(); + return Changed; } void RewritePNaClLibraryCalls::sanityCheckLongjmpFunc(Function *LongjmpFunc) { @@ -163,10 +224,64 @@ void RewritePNaClLibraryCalls::sanityCheckSetjmpFunc(Function *SetjmpFunc) { } } +void RewritePNaClLibraryCalls::sanityCheckMemsetFunc(Function *MemsetFunc) { + FunctionType *FTy = MemsetFunc->getFunctionType(); + if (!(FTy->getNumParams() == 3 && + FTy->getReturnType()->isPointerTy() && + FTy->getParamType(0)->isPointerTy() && + FTy->getParamType(1)->isIntegerTy() && + FTy->getParamType(2)->isIntegerTy())) { + report_fatal_error("Wrong signature of memset"); + } +} + +void RewritePNaClLibraryCalls::sanityCheckMemcpyFunc(Function *MemcpyFunc) { + FunctionType *FTy = MemcpyFunc->getFunctionType(); + if (!(FTy->getNumParams() == 3 && + FTy->getReturnType()->isPointerTy() && + FTy->getParamType(0)->isPointerTy() && + FTy->getParamType(1)->isPointerTy() && + FTy->getParamType(2)->isIntegerTy())) { + report_fatal_error("Wrong signature of memcpy"); + } +} + +void RewritePNaClLibraryCalls::sanityCheckMemmoveFunc(Function *MemmoveFunc) { + FunctionType *FTy = MemmoveFunc->getFunctionType(); + if (!(FTy->getNumParams() == 3 && + FTy->getReturnType()->isPointerTy() && + FTy->getParamType(0)->isPointerTy() && + FTy->getParamType(1)->isPointerTy() && + FTy->getParamType(2)->isIntegerTy())) { + report_fatal_error("Wrong signature of memmove"); + } +} + +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. + Value *Args[] = { 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::rewriteLongjmpCall(CallInst *Call) { // Find the intrinsic function. - Function *NaClLongjmpFunc = Intrinsic::getDeclaration( - TheModule, Intrinsic::nacl_longjmp); + Function *NaClLongjmpFunc = findLongjmpIntrinsic(); // Cast the jmp_buf argument to the type NaClLongjmpCall expects. Type *PtrTy = NaClLongjmpFunc->getFunctionType()->getParamType(0); BitCastInst *JmpBufCast = new BitCastInst(Call->getArgOperand(0), PtrTy, @@ -175,9 +290,7 @@ void RewritePNaClLibraryCalls::rewriteLongjmpCall(CallInst *Call) { JmpBufCast->setDebugLoc(DLoc); // Emit the call. - SmallVector<Value *, 2> Args; - Args.push_back(JmpBufCast); - Args.push_back(Call->getArgOperand(1)); + Value *Args[] = { JmpBufCast, 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 @@ -188,39 +301,191 @@ void RewritePNaClLibraryCalls::rewriteLongjmpCall(CallInst *Call) { Call->eraseFromParent(); } +void RewritePNaClLibraryCalls::rewriteMemcpyCall(CallInst *Call) { + Function *MemcpyIntrinsic = findMemcpyIntrinsic(); + // dest, src, len, align, isvolatile + Value *Args[] = { Call->getArgOperand(0), + Call->getArgOperand(1), + Call->getArgOperand(2), + ConstantInt::get(Type::getInt32Ty(*Context), 1), + ConstantInt::get(Type::getInt1Ty(*Context), 0) }; + CallInst *MemcpyIntrinsicCall = CallInst::Create(MemcpyIntrinsic, + Args, "", Call); + MemcpyIntrinsicCall->setDebugLoc(Call->getDebugLoc()); + + // libc memcpy returns the source pointer, but the LLVM intrinsic doesn't; if + // the return value has actual uses, just replace them with the dest + // argument itself. + Call->replaceAllUsesWith(Call->getArgOperand(0)); + Call->eraseFromParent(); +} + +void RewritePNaClLibraryCalls::rewriteMemmoveCall(CallInst *Call) { + Function *MemmoveIntrinsic = findMemmoveIntrinsic(); + // dest, src, len, align, isvolatile + Value *Args[] = { Call->getArgOperand(0), + Call->getArgOperand(1), + Call->getArgOperand(2), + ConstantInt::get(Type::getInt32Ty(*Context), 1), + ConstantInt::get(Type::getInt1Ty(*Context), 0) }; + CallInst *MemmoveIntrinsicCall = CallInst::Create(MemmoveIntrinsic, + Args, "", Call); + MemmoveIntrinsicCall->setDebugLoc(Call->getDebugLoc()); + + // libc memmove returns the source pointer, but the LLVM intrinsic doesn't; if + // the return value has actual uses, just replace them with the dest + // argument itself. + Call->replaceAllUsesWith(Call->getArgOperand(0)); + Call->eraseFromParent(); +} + +void RewritePNaClLibraryCalls::rewriteMemsetCall(CallInst *Call) { + Function *MemsetIntrinsic = findMemsetIntrinsic(); + // libc memset has 'int c' for the filler byte, but the LLVM intrinsic uses + // a i8; truncation is required. + TruncInst *ByteTrunc = new TruncInst(Call->getArgOperand(1), + Type::getInt8Ty(*Context), + "trunc_byte", Call); + + const DebugLoc &DLoc = Call->getDebugLoc(); + ByteTrunc->setDebugLoc(DLoc); + + // dest, val, len, align, isvolatile + Value *Args[] = { Call->getArgOperand(0), + ByteTrunc, + Call->getArgOperand(2), + ConstantInt::get(Type::getInt32Ty(*Context), 1), + ConstantInt::get(Type::getInt1Ty(*Context), 0) }; + CallInst *MemsetIntrinsicCall = CallInst::Create(MemsetIntrinsic, + Args, "", Call); + MemsetIntrinsicCall->setDebugLoc(DLoc); + + // libc memset returns the source pointer, but the LLVM intrinsic doesn't; if + // the return value has actual uses, just replace them with the dest + // argument itself. + Call->replaceAllUsesWith(Call->getArgOperand(0)); + Call->eraseFromParent(); +} + +void RewritePNaClLibraryCalls::populateWrapperCommon( + Function *Func, + StringRef FuncName, + RewriteCallFunc CallRewriter, + bool CallCannotReturn, + ...) { + if (!Func->isDeclaration()) { + report_fatal_error(Twine("Expected ") + FuncName + + " to be declared, not defined"); + } + + // Populate the function body with code. + BasicBlock *BB = BasicBlock::Create(*Context, "entry", Func); + + // Collect and name the function arguments. + Function::arg_iterator FuncArgs = Func->arg_begin(); + SmallVector<Value *, 4> Args; + va_list ap; + va_start(ap, CallCannotReturn); + while (true) { + // Iterate over the varargs until a terminated NULL is encountered. + const char *ArgName = va_arg(ap, const char *); + if (!ArgName) + break; + Value *Arg = FuncArgs++; + Arg->setName(ArgName); + Args.push_back(Arg); + } + va_end(ap); + + // Emit a call to self, and then call CallRewriter to rewrite it to the + // intrinsic. This is done in order to keep the call rewriting logic in a + // single place. + CallInst *SelfCall = CallInst::Create(Func, Args, "", BB); + + if (CallCannotReturn) { + new UnreachableInst(*Context, BB); + } else if (Func->getReturnType()->isVoidTy()) { + ReturnInst::Create(*Context, BB); + } else { + ReturnInst::Create(*Context, SelfCall, BB); + } + + (this->*(CallRewriter))(SelfCall); +} + void RewritePNaClLibraryCalls::populateLongjmpWrapper(Function *LongjmpFunc) { - assert(LongjmpFunc->size() == 0 && - "Expected to be called when longjmp has an empty body"); + populateWrapperCommon( + /* Func */ LongjmpFunc, + /* FuncName */ "longjmp", + /* CallRewriter */ &RewritePNaClLibraryCalls::rewriteLongjmpCall, + /* CallCannotReturn */ true, + /* ... */ "env", "val", NULL); +} - // Populate longjmp with code. - LLVMContext &Context = TheModule->getContext(); - BasicBlock *BB = BasicBlock::Create(Context, "entry", LongjmpFunc); +void RewritePNaClLibraryCalls::populateMemcpyWrapper(Function *MemcpyFunc) { + populateWrapperCommon( + /* Func */ MemcpyFunc, + /* FuncName */ "memcpy", + /* CallRewriter */ &RewritePNaClLibraryCalls::rewriteMemcpyCall, + /* CallCannotReturn */ false, + /* ... */ "dest", "src", "len", NULL); +} - Function::arg_iterator LongjmpArgs = LongjmpFunc->arg_begin(); - Value *EnvArg = LongjmpArgs++; - EnvArg->setName("env"); - Value *ValArg = LongjmpArgs++; - ValArg->setName("val"); +void RewritePNaClLibraryCalls::populateMemmoveWrapper(Function *MemmoveFunc) { + populateWrapperCommon( + /* Func */ MemmoveFunc, + /* FuncName */ "memmove", + /* CallRewriter */ &RewritePNaClLibraryCalls::rewriteMemmoveCall, + /* CallCannotReturn */ false, + /* ... */ "dest", "src", "len", NULL); +} - // 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); +void RewritePNaClLibraryCalls::populateMemsetWrapper(Function *MemsetFunc) { + populateWrapperCommon( + /* Func */ MemsetFunc, + /* FuncName */ "memset", + /* CallRewriter */ &RewritePNaClLibraryCalls::rewriteMemsetCall, + /* CallCannotReturn */ false, + /* ... */ "dest", "val", "len", NULL); +} - // Emit the call. - SmallVector<Value *, 2> Args; - Args.push_back(JmpBufCast); - Args.push_back(ValArg); - CallInst::Create(NaClLongjmpFunc, Args, "", BB); +Function *RewritePNaClLibraryCalls::findLongjmpIntrinsic() { + if (!LongjmpIntrinsic) { + LongjmpIntrinsic = Intrinsic::getDeclaration( + TheModule, Intrinsic::nacl_longjmp); + } + return LongjmpIntrinsic; +} - // Insert an unreachable instruction to terminate this function since longjmp - // does not return. - new UnreachableInst(Context, BB); +Function *RewritePNaClLibraryCalls::findMemcpyIntrinsic() { + if (!MemcpyIntrinsic) { + Type *Tys[] = { Type::getInt8PtrTy(*Context), + Type::getInt8PtrTy(*Context), + Type::getInt32Ty(*Context) }; + MemcpyIntrinsic = Intrinsic::getDeclaration( + TheModule, Intrinsic::memcpy, Tys); + } + return MemcpyIntrinsic; +} + +Function *RewritePNaClLibraryCalls::findMemmoveIntrinsic() { + if (!MemmoveIntrinsic) { + Type *Tys[] = { Type::getInt8PtrTy(*Context), + Type::getInt8PtrTy(*Context), + Type::getInt32Ty(*Context) }; + MemmoveIntrinsic = Intrinsic::getDeclaration( + TheModule, Intrinsic::memmove, Tys); + } + return MemmoveIntrinsic; +} - // Finally, set the linkage to internal - LongjmpFunc->setLinkage(Function::InternalLinkage); +Function *RewritePNaClLibraryCalls::findMemsetIntrinsic() { + if (!MemsetIntrinsic) { + Type *Tys[] = { Type::getInt8PtrTy(*Context), Type::getInt32Ty(*Context) }; + MemsetIntrinsic = Intrinsic::getDeclaration( + TheModule, Intrinsic::memset, Tys); + } + return MemsetIntrinsic; } ModulePass *llvm::createRewritePNaClLibraryCallsPass() { diff --git a/test/Transforms/NaCl/rewrite-memfuncs-no-store.ll b/test/Transforms/NaCl/rewrite-memfuncs-no-store.ll new file mode 100644 index 0000000000..4d91774c34 --- /dev/null +++ b/test/Transforms/NaCl/rewrite-memfuncs-no-store.ll @@ -0,0 +1,40 @@ +; RUN: opt < %s -rewrite-pnacl-library-calls -S | FileCheck %s +; RUN: opt < %s -rewrite-pnacl-library-calls -S | FileCheck %s -check-prefix=CLEANED + +declare i8* @memcpy(i8*, i8*, i32) +declare i8* @memmove(i8*, i8*, i32) +declare i8* @memset(i8*, i32, i32) + +; No declaration or definition of the library functions should remain, since +; the only uses of mem* functions are calls. +; CLEANED-NOT: @memcpy +; CLEANED-NOT: @memmove +; CLEANED-NOT: @memset + +define i8* @call_memcpy(i8* %dest, i8* %src, i32 %len) { + %result = call i8* @memcpy(i8* %dest, i8* %src, i32 %len) + ret i8* %result +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i32 1, i1 false) +; CHECK: ret i8* %dest +} + +define i8* @call_memmove(i8* %dest, i8* %src, i32 %len) { + %result = call i8* @memmove(i8* %dest, i8* %src, i32 %len) + ret i8* %result +; CHECK: call void @llvm.memmove.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i32 1, i1 false) +; CHECK: ret i8* %dest +} + +define i8* @call_memset(i8* %dest, i32 %c, i32 %len) { + %result = call i8* @memset(i8* %dest, i32 %c, i32 %len) + ret i8* %result +; CHECK: %trunc_byte = trunc i32 %c to i8 +; CHECK: call void @llvm.memset.p0i8.i32(i8* %dest, i8 %trunc_byte, i32 %len, i32 1, i1 false) +; CHECK: ret i8* %dest +} + +; CHECK: declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) + +; CHECK: declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture, i8* nocapture, i32, i32, i1) + +; CHECK: declare void @llvm.memmove.p0i8.p0i8.i32(i8* nocapture, i8* nocapture, i32, i32, i1) diff --git a/test/Transforms/NaCl/rewrite-memfuncs-noncall-uses.ll b/test/Transforms/NaCl/rewrite-memfuncs-noncall-uses.ll new file mode 100644 index 0000000000..5c6bdfdcb5 --- /dev/null +++ b/test/Transforms/NaCl/rewrite-memfuncs-noncall-uses.ll @@ -0,0 +1,30 @@ +; RUN: opt < %s -rewrite-pnacl-library-calls -S | FileCheck %s +; Check that the rewrite pass behaves correctly in the presence +; of various uses of mem* that are not calls. + +@fpcpy = global i8* (i8*, i8*, i32)* @memcpy +; CHECK: @fpcpy = global i8* (i8*, i8*, i32)* @memcpy +@fpmove = global i8* (i8*, i8*, i32)* @memmove +; CHECK: @fpmove = global i8* (i8*, i8*, i32)* @memmove +@fpset = global i8* (i8*, i32, i32)* @memset +; CHECK: @fpset = global i8* (i8*, i32, i32)* @memset + +; CHECK: define internal i8* @memcpy(i8* %dest, i8* %src, i32 %len) { +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i32 1, i1 false) +; CHECK: ret i8* %dest +; CHECK: } + +; CHECK: define internal i8* @memmove(i8* %dest, i8* %src, i32 %len) { +; CHECK: call void @llvm.memmove.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i32 1, i1 false) +; CHECK: ret i8* %dest +; CHECK: } + +; CHECK: define internal i8* @memset(i8* %dest, i32 %val, i32 %len) { +; CHECK: %trunc_byte = trunc i32 %val to i8 +; CHECK: call void @llvm.memset.p0i8.i32(i8* %dest, i8 %trunc_byte, i32 %len, i32 1, i1 false) +; CHECK: ret i8* %dest +; CHECK: } + +declare i8* @memcpy(i8*, i8*, i32) +declare i8* @memmove(i8*, i8*, i32) +declare i8* @memset(i8*, i32, i32) |