blob: c3f1e9409a923db2069371254a02953576bf1229 [file] [log] [blame]
//===- 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/Constants.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"
#include <cstdarg>
using namespace llvm;
namespace {
class RewritePNaClLibraryCalls : public ModulePass {
public:
static char ID;
RewritePNaClLibraryCalls() :
ModulePass(ID), TheModule(NULL), Context(NULL), SetjmpIntrinsic(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 globals.
initializeRewritePNaClLibraryCallsPass(*PassRegistry::getPassRegistry());
}
virtual bool runOnModule(Module &M);
private:
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 its arguments.
///
/// \p LibraryFunctionName Name of the library function to look for.
/// \p CorrectFunctionType is the correct type of this library function.
/// \p CallRewriter Method that rewrites the library function call into an
/// intrinsic call.
/// \p OnlyCallsAllowed Only calls to this library function are allowed.
/// \p WrapperPopulator called to populate the body of the library function
/// with a wrapped intrinsic call.
bool RewriteLibraryCall(
const char *LibraryFunctionName,
FunctionType *CorrectFunctionType,
RewriteCallFunc CallRewriter,
bool OnlyCallsAllowed,
PopulateWrapperFunc WrapperPopulator);
/// Two function types are compatible if they have compatible return types
/// and the same number of compatible parameters. Return types and
/// parameters are compatible if they are exactly the same type or both are
/// pointer types.
static bool compatibleFunctionTypes(FunctionType *FTy1, FunctionType *FTy2);
static bool compatibleParamOrRetTypes(Type *Ty1, Type *Ty2);
void rewriteSetjmpCall(CallInst *Call);
void rewriteLongjmpCall(CallInst *Call);
void rewriteMemcpyCall(CallInst *Call);
void rewriteMemmoveCall(CallInst *Call);
void rewriteMemsetCall(CallInst *Call);
void populateSetjmpWrapper(Function *SetjmpFunc);
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 *findSetjmpIntrinsic();
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 *SetjmpIntrinsic;
Function *LongjmpIntrinsic;
Function *MemcpyIntrinsic;
Function *MemmoveIntrinsic;
Function *MemsetIntrinsic;
};
}
char RewritePNaClLibraryCalls::ID = 0;
INITIALIZE_PASS(RewritePNaClLibraryCalls, "rewrite-pnacl-library-calls",
"Rewrite PNaCl library calls to stable intrinsics",
false, false)
bool RewritePNaClLibraryCalls::RewriteLibraryCall(
const char *LibraryFunctionName,
FunctionType *CorrectFunctionType,
RewriteCallFunc CallRewriter,
bool OnlyCallsAllowed,
PopulateWrapperFunc WrapperPopulator) {
bool Changed = false;
Function *LibFunc = TheModule->getFunction(LibraryFunctionName);
// 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.
//
// Another case we need to handle here is this function having the wrong
// prototype (incompatible with the C library function prototype, and hence
// incompatible with the intrinsic). In general, this is undefined behavior,
// but we can't fail compilation because some workflows rely on it
// compiling correctly (for example, autoconf). The solution is:
// When the declared type of the function in the module is not correct, we
// re-create the function with the correct prototype and replace all calls
// to this new function (casted to the old function type). Effectively this
// delays the undefined behavior until run-time.
if (LibFunc && LibFunc->hasExternalLinkage()) {
if (!compatibleFunctionTypes(LibFunc->getFunctionType(),
CorrectFunctionType)) {
// Use the RecreateFunction utility to create a new function with the
// correct prototype. RecreateFunction also RAUWs the function with
// proper bitcasts.
//
// One interesting case that may arise is when the original module had
// calls to both a correct and an incorrect version of the library
// function. Depending on the linking order, either version could be
// selected as the global declaration in the module, so even valid calls
// could end up being bitcast-ed from the incorrect to the correct
// function type. The RecreateFunction call below will eliminate such
// bitcasts (because the new type matches the call type), but dead
// constant expressions may be left behind.
// These are cleaned up with removeDeadConstantUsers.
Function *NewFunc = RecreateFunction(LibFunc, CorrectFunctionType);
LibFunc->eraseFromParent();
NewFunc->setLinkage(Function::InternalLinkage);
Changed = true;
NewFunc->removeDeadConstantUsers();
LibFunc = NewFunc;
}
// Handle all uses that are calls. These are simply replaced with
// equivalent intrinsic calls.
{
SmallVector<CallInst *, 32> Calls;
for (User *U : LibFunc->users())
// users() will also provide call instructions in which the used value
// is an argument, and not the value being called. Make sure we rewrite
// only actual calls to LibFunc here.
if (CallInst *Call = dyn_cast<CallInst>(U))
if (Call->getCalledValue() == LibFunc)
Calls.push_back(Call);
for (auto Call : Calls)
(this->*(CallRewriter))(Call);
Changed |= !Calls.empty();
}
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 {
// If non-call uses remain and allowed for this function, populate it
// with a wrapper.
(this->*(WrapperPopulator))(LibFunc);
LibFunc->setLinkage(Function::InternalLinkage);
Changed = true;
}
}
return Changed;
}
bool RewritePNaClLibraryCalls::runOnModule(Module &M) {
TheModule = &M;
Context = &TheModule->getContext();
bool Changed = false;
Type *Int8PtrTy = Type::getInt8PtrTy(*Context);
Type *Int64PtrTy = Type::getInt64PtrTy(*Context);
Type *Int32Ty = Type::getInt32Ty(*Context);
Type *VoidTy = Type::getVoidTy(*Context);
Type *SetjmpParams[] = { Int64PtrTy };
FunctionType *SetjmpFunctionType = FunctionType::get(Int32Ty, SetjmpParams,
false);
Changed |= RewriteLibraryCall(
"setjmp",
SetjmpFunctionType,
&RewritePNaClLibraryCalls::rewriteSetjmpCall,
true,
&RewritePNaClLibraryCalls::populateSetjmpWrapper);
Type *LongjmpParams[] = { Int64PtrTy, Int32Ty };
FunctionType *LongjmpFunctionType = FunctionType::get(VoidTy, LongjmpParams,
false);
Changed |= RewriteLibraryCall(
"longjmp",
LongjmpFunctionType,
&RewritePNaClLibraryCalls::rewriteLongjmpCall,
false,
&RewritePNaClLibraryCalls::populateLongjmpWrapper);
Type *MemsetParams[] = { Int8PtrTy, Int32Ty, Int32Ty };
FunctionType *MemsetFunctionType = FunctionType::get(Int8PtrTy, MemsetParams,
false);
Changed |= RewriteLibraryCall(
"memset",
MemsetFunctionType,
&RewritePNaClLibraryCalls::rewriteMemsetCall,
false,
&RewritePNaClLibraryCalls::populateMemsetWrapper);
Type *MemcpyParams[] = { Int8PtrTy, Int8PtrTy, Int32Ty };
FunctionType *MemcpyFunctionType = FunctionType::get(Int8PtrTy, MemcpyParams,
false);
Changed |= RewriteLibraryCall(
"memcpy",
MemcpyFunctionType,
&RewritePNaClLibraryCalls::rewriteMemcpyCall,
false,
&RewritePNaClLibraryCalls::populateMemcpyWrapper);
Type *MemmoveParams[] = { Int8PtrTy, Int8PtrTy, Int32Ty };
FunctionType *MemmoveFunctionType = FunctionType::get(Int8PtrTy,
MemmoveParams,
false);
Changed |= RewriteLibraryCall(
"memmove",
MemmoveFunctionType,
&RewritePNaClLibraryCalls::rewriteMemmoveCall,
false,
&RewritePNaClLibraryCalls::populateMemmoveWrapper);
return Changed;
}
bool RewritePNaClLibraryCalls::compatibleFunctionTypes(FunctionType *FTy1,
FunctionType *FTy2) {
if (FTy1->getNumParams() != FTy2->getNumParams()) {
return false;
}
if (!compatibleParamOrRetTypes(FTy1->getReturnType(),
FTy2->getReturnType())) {
return false;
}
for (unsigned I = 0, End = FTy1->getNumParams(); I != End; ++I) {
if (!compatibleParamOrRetTypes(FTy1->getParamType(I),
FTy2->getParamType(I))) {
return false;
}
}
return true;
}
bool RewritePNaClLibraryCalls::compatibleParamOrRetTypes(Type *Ty1,
Type *Ty2) {
return (Ty1 == Ty2 || (Ty1->isPointerTy() && Ty2->isPointerTy()));
}
void RewritePNaClLibraryCalls::rewriteSetjmpCall(CallInst *Call) {
// Find the intrinsic function.
Function *NaClSetjmpFunc = findSetjmpIntrinsic();
// 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 = 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,
"jmp_buf_i8", Call);
const DebugLoc &DLoc = Call->getDebugLoc();
JmpBufCast->setDebugLoc(DLoc);
// Emit the call.
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
// a value.
// Remove the original call. There's no need for RAUW because longjmp
// returns void.
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::populateSetjmpWrapper(Function *SetjmpFunc) {
populateWrapperCommon(
/* Func */ SetjmpFunc,
/* FuncName */ "setjmp",
/* CallRewriter */ &RewritePNaClLibraryCalls::rewriteSetjmpCall,
/* CallCannotReturn */ false,
/* ... */ "env", NULL);
}
void RewritePNaClLibraryCalls::populateLongjmpWrapper(Function *LongjmpFunc) {
populateWrapperCommon(
/* Func */ LongjmpFunc,
/* FuncName */ "longjmp",
/* CallRewriter */ &RewritePNaClLibraryCalls::rewriteLongjmpCall,
/* CallCannotReturn */ true,
/* ... */ "env", "val", NULL);
}
void RewritePNaClLibraryCalls::populateMemcpyWrapper(Function *MemcpyFunc) {
populateWrapperCommon(
/* Func */ MemcpyFunc,
/* FuncName */ "memcpy",
/* CallRewriter */ &RewritePNaClLibraryCalls::rewriteMemcpyCall,
/* CallCannotReturn */ false,
/* ... */ "dest", "src", "len", NULL);
}
void RewritePNaClLibraryCalls::populateMemmoveWrapper(Function *MemmoveFunc) {
populateWrapperCommon(
/* Func */ MemmoveFunc,
/* FuncName */ "memmove",
/* CallRewriter */ &RewritePNaClLibraryCalls::rewriteMemmoveCall,
/* CallCannotReturn */ false,
/* ... */ "dest", "src", "len", NULL);
}
void RewritePNaClLibraryCalls::populateMemsetWrapper(Function *MemsetFunc) {
populateWrapperCommon(
/* Func */ MemsetFunc,
/* FuncName */ "memset",
/* CallRewriter */ &RewritePNaClLibraryCalls::rewriteMemsetCall,
/* CallCannotReturn */ false,
/* ... */ "dest", "val", "len", NULL);
}
Function *RewritePNaClLibraryCalls::findSetjmpIntrinsic() {
if (!SetjmpIntrinsic) {
SetjmpIntrinsic = Intrinsic::getDeclaration(
TheModule, Intrinsic::nacl_setjmp);
}
return SetjmpIntrinsic;
}
Function *RewritePNaClLibraryCalls::findLongjmpIntrinsic() {
if (!LongjmpIntrinsic) {
LongjmpIntrinsic = Intrinsic::getDeclaration(
TheModule, Intrinsic::nacl_longjmp);
}
return LongjmpIntrinsic;
}
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;
}
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() {
return new RewritePNaClLibraryCalls();
}