aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/llvm/InitializePasses.h1
-rw-r--r--include/llvm/Transforms/NaCl.h10
-rw-r--r--lib/Transforms/NaCl/CMakeLists.txt1
-rw-r--r--lib/Transforms/NaCl/ExpandSmallArguments.cpp217
-rw-r--r--lib/Transforms/NaCl/ExpandUtils.cpp15
-rw-r--r--lib/Transforms/NaCl/ExpandVarArgs.cpp12
-rw-r--r--lib/Transforms/NaCl/PNaClABISimplify.cpp6
-rw-r--r--lib/Transforms/NaCl/ReplacePtrsWithInts.cpp16
-rw-r--r--test/Transforms/NaCl/expand-small-arguments.ll97
-rw-r--r--tools/opt/opt.cpp1
10 files changed, 352 insertions, 24 deletions
diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h
index 968503d28e..53357cca0a 100644
--- a/include/llvm/InitializePasses.h
+++ b/include/llvm/InitializePasses.h
@@ -283,6 +283,7 @@ void initializeExpandByValPass(PassRegistry&);
void initializeExpandConstantExprPass(PassRegistry&);
void initializeExpandCtorsPass(PassRegistry&);
void initializeExpandGetElementPtrPass(PassRegistry&);
+void initializeExpandSmallArgumentsPass(PassRegistry&);
void initializeExpandTlsConstantExprPass(PassRegistry&);
void initializeExpandTlsPass(PassRegistry&);
void initializeExpandVarArgsPass(PassRegistry&);
diff --git a/include/llvm/Transforms/NaCl.h b/include/llvm/Transforms/NaCl.h
index 92838e5fc7..cfd4c6e51d 100644
--- a/include/llvm/Transforms/NaCl.h
+++ b/include/llvm/Transforms/NaCl.h
@@ -13,7 +13,9 @@
namespace llvm {
class BasicBlockPass;
+class Function;
class FunctionPass;
+class FunctionType;
class Instruction;
class ModulePass;
class PassManager;
@@ -26,6 +28,7 @@ ModulePass *createExpandByValPass();
FunctionPass *createExpandConstantExprPass();
ModulePass *createExpandCtorsPass();
BasicBlockPass *createExpandGetElementPtrPass();
+ModulePass *createExpandSmallArgumentsPass();
ModulePass *createExpandTlsPass();
ModulePass *createExpandTlsConstantExprPass();
ModulePass *createExpandVarArgsPass();
@@ -48,6 +51,13 @@ void PhiSafeReplaceUses(Use *U, Value *NewVal);
// Copy debug information from Original to NewInst, and return NewInst.
Instruction *CopyDebug(Instruction *NewInst, Instruction *Original);
+// In order to change a function's type, the function must be
+// recreated. RecreateFunction() recreates Func with type NewType.
+// It copies or moves across everything except the argument values,
+// which the caller must update because the argument types might be
+// different.
+Function *RecreateFunction(Function *Func, FunctionType *NewType);
+
}
#endif
diff --git a/lib/Transforms/NaCl/CMakeLists.txt b/lib/Transforms/NaCl/CMakeLists.txt
index ab6b5fba8a..027bb46a14 100644
--- a/lib/Transforms/NaCl/CMakeLists.txt
+++ b/lib/Transforms/NaCl/CMakeLists.txt
@@ -5,6 +5,7 @@ add_llvm_library(LLVMNaClTransforms
ExpandConstantExpr.cpp
ExpandCtors.cpp
ExpandGetElementPtr.cpp
+ ExpandSmallArguments.cpp
ExpandTls.cpp
ExpandTlsConstantExpr.cpp
ExpandUtils.cpp
diff --git a/lib/Transforms/NaCl/ExpandSmallArguments.cpp b/lib/Transforms/NaCl/ExpandSmallArguments.cpp
new file mode 100644
index 0000000000..c8a321edb9
--- /dev/null
+++ b/lib/Transforms/NaCl/ExpandSmallArguments.cpp
@@ -0,0 +1,217 @@
+//===- ExpandSmallArguments.cpp - Expand out arguments smaller than i32----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// LLVM IR allows function return types and argument types such as
+// "zeroext i8" and "signext i8". The Language Reference says that
+// zeroext "indicates to the code generator that the parameter or
+// return value should be zero-extended to the extent required by the
+// target's ABI (which is usually 32-bits, but is 8-bits for a i1 on
+// x86-64) by the caller (for a parameter) or the callee (for a return
+// value)".
+//
+// This can lead to non-portable behaviour when calling functions
+// without C prototypes or with wrong C prototypes.
+//
+// In order to remove this non-portability from PNaCl, and to simplify
+// the language that the PNaCl translator accepts, the
+// ExpandSmallArguments pass widens integer arguments and return types
+// to be at least 32 bits. The pass inserts explicit cast
+// instructions (ZExtInst/SExtInst/TruncInst) as needed.
+//
+// The pass chooses between ZExtInst and SExtInst widening based on
+// whether a "signext" attribute is present. However, in principle
+// the pass could always use zero-extension, because the extent to
+// which either zero-extension or sign-extension is done is up to the
+// target ABI, which is up to PNaCl to specify.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Transforms/NaCl.h"
+
+using namespace llvm;
+
+namespace {
+ // This is a ModulePass because the pass recreates functions in
+ // order to change their arguments' types.
+ class ExpandSmallArguments : public ModulePass {
+ public:
+ static char ID; // Pass identification, replacement for typeid
+ ExpandSmallArguments() : ModulePass(ID) {
+ initializeExpandSmallArgumentsPass(*PassRegistry::getPassRegistry());
+ }
+
+ virtual bool runOnModule(Module &M);
+ };
+}
+
+char ExpandSmallArguments::ID = 0;
+INITIALIZE_PASS(ExpandSmallArguments, "expand-small-arguments",
+ "Expand function arguments to be at least 32 bits in size",
+ false, false)
+
+// Returns the normalized version of the given argument/return type.
+static Type *NormalizeType(Type *Ty) {
+ if (IntegerType *IntTy = dyn_cast<IntegerType>(Ty)) {
+ if (IntTy->getBitWidth() < 32) {
+ return IntegerType::get(Ty->getContext(), 32);
+ }
+ }
+ return Ty;
+}
+
+// Returns the normalized version of the given function type.
+static FunctionType *NormalizeFunctionType(FunctionType *FTy) {
+ if (FTy->isVarArg()) {
+ report_fatal_error(
+ "ExpandSmallArguments does not handle varargs functions");
+ }
+ SmallVector<Type *, 8> ArgTypes;
+ for (unsigned I = 0; I < FTy->getNumParams(); ++I) {
+ ArgTypes.push_back(NormalizeType(FTy->getParamType(I)));
+ }
+ return FunctionType::get(NormalizeType(FTy->getReturnType()),
+ ArgTypes, false);
+}
+
+// Convert the given function to use normalized argument/return types.
+static bool ConvertFunction(Function *Func) {
+ FunctionType *FTy = Func->getFunctionType();
+ FunctionType *NFTy = NormalizeFunctionType(FTy);
+ if (NFTy == FTy)
+ return false; // No change needed.
+ Function *NewFunc = RecreateFunction(Func, NFTy);
+
+ // Move the arguments across to the new function.
+ for (Function::arg_iterator Arg = Func->arg_begin(), E = Func->arg_end(),
+ NewArg = NewFunc->arg_begin();
+ Arg != E; ++Arg, ++NewArg) {
+ NewArg->takeName(Arg);
+ if (Arg->getType() == NewArg->getType()) {
+ Arg->replaceAllUsesWith(NewArg);
+ } else {
+ Instruction *Trunc = new TruncInst(
+ NewArg, Arg->getType(), NewArg->getName() + ".arg_trunc",
+ NewFunc->getEntryBlock().getFirstInsertionPt());
+ Arg->replaceAllUsesWith(Trunc);
+ }
+ }
+
+ if (FTy->getReturnType() != NFTy->getReturnType()) {
+ // Fix up return instructions.
+ Instruction::CastOps CastType =
+ Func->getAttributes().hasAttribute(0, Attribute::SExt) ?
+ Instruction::SExt : Instruction::ZExt;
+ for (Function::iterator BB = NewFunc->begin(), E = NewFunc->end();
+ BB != E;
+ ++BB) {
+ for (BasicBlock::iterator Iter = BB->begin(), E = BB->end();
+ Iter != E; ) {
+ Instruction *Inst = Iter++;
+ if (ReturnInst *Ret = dyn_cast<ReturnInst>(Inst)) {
+ Value *Ext = CopyDebug(
+ CastInst::Create(CastType, Ret->getReturnValue(),
+ NFTy->getReturnType(),
+ Ret->getReturnValue()->getName() + ".ret_ext",
+ Ret),
+ Ret);
+ CopyDebug(ReturnInst::Create(Ret->getContext(), Ext, Ret), Ret);
+ Ret->eraseFromParent();
+ }
+ }
+ }
+ }
+
+ Func->eraseFromParent();
+ return true;
+}
+
+// Convert the given call to use normalized argument/return types.
+static bool ConvertCall(CallInst *Call) {
+ // Don't try to change calls to intrinsics.
+ if (isa<IntrinsicInst>(Call))
+ return false;
+ FunctionType *FTy = cast<FunctionType>(
+ Call->getCalledValue()->getType()->getPointerElementType());
+ FunctionType *NFTy = NormalizeFunctionType(FTy);
+ if (NFTy == FTy)
+ return false; // No change needed.
+
+ // Convert arguments.
+ SmallVector<Value *, 8> Args;
+ for (unsigned I = 0; I < Call->getNumArgOperands(); ++I) {
+ Value *Arg = Call->getArgOperand(I);
+ if (NFTy->getParamType(I) != FTy->getParamType(I)) {
+ Instruction::CastOps CastType =
+ Call->getAttributes().hasAttribute(I + 1, Attribute::SExt) ?
+ Instruction::SExt : Instruction::ZExt;
+ Arg = CopyDebug(CastInst::Create(CastType, Arg, NFTy->getParamType(I),
+ "arg_ext", Call), Call);
+ }
+ Args.push_back(Arg);
+ }
+ Value *CastFunc =
+ CopyDebug(new BitCastInst(Call->getCalledValue(), NFTy->getPointerTo(),
+ Call->getName() + ".arg_cast", Call), Call);
+ CallInst *NewCall = CallInst::Create(CastFunc, Args, "", Call);
+ CopyDebug(NewCall, Call);
+ NewCall->takeName(Call);
+ NewCall->setAttributes(Call->getAttributes());
+ NewCall->setCallingConv(Call->getCallingConv());
+ NewCall->setTailCall(Call->isTailCall());
+ Value *Result = NewCall;
+ if (FTy->getReturnType() != NFTy->getReturnType()) {
+ Result = CopyDebug(new TruncInst(NewCall, FTy->getReturnType(),
+ NewCall->getName() + ".ret_trunc",
+ Call), Call);
+ }
+ Call->replaceAllUsesWith(Result);
+ Call->eraseFromParent();
+ return true;
+}
+
+bool ExpandSmallArguments::runOnModule(Module &M) {
+ bool Changed = false;
+ for (Module::iterator Iter = M.begin(), E = M.end(); Iter != E; ) {
+ Function *Func = Iter++;
+ // Don't try to change intrinsic declarations because intrinsics
+ // will continue to have non-normalized argument types. For
+ // example, memset() takes an i8 argument. It shouldn't matter
+ // whether we modify the types of other function declarations, but
+ // we don't expect to see non-intrinsic function declarations in a
+ // PNaCl pexe.
+ if (Func->empty())
+ continue;
+
+ for (Function::iterator BB = Func->begin(), E = Func->end();
+ BB != E; ++BB) {
+ for (BasicBlock::iterator Iter = BB->begin(), E = BB->end();
+ Iter != E; ) {
+ Instruction *Inst = Iter++;
+ if (CallInst *Call = dyn_cast<CallInst>(Inst)) {
+ Changed |= ConvertCall(Call);
+ } else if (isa<InvokeInst>(Inst)) {
+ report_fatal_error(
+ "ExpandSmallArguments does not handle invoke instructions");
+ }
+ }
+ }
+
+ Changed |= ConvertFunction(Func);
+ }
+ return Changed;
+}
+
+ModulePass *llvm::createExpandSmallArgumentsPass() {
+ return new ExpandSmallArguments();
+}
diff --git a/lib/Transforms/NaCl/ExpandUtils.cpp b/lib/Transforms/NaCl/ExpandUtils.cpp
index 507760603f..913e0d6607 100644
--- a/lib/Transforms/NaCl/ExpandUtils.cpp
+++ b/lib/Transforms/NaCl/ExpandUtils.cpp
@@ -8,7 +8,9 @@
//===----------------------------------------------------------------------===//
#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
#include "llvm/Transforms/NaCl.h"
using namespace llvm;
@@ -43,3 +45,16 @@ Instruction *llvm::CopyDebug(Instruction *NewInst, Instruction *Original) {
NewInst->setDebugLoc(Original->getDebugLoc());
return NewInst;
}
+
+Function *llvm::RecreateFunction(Function *Func, FunctionType *NewType) {
+ Function *NewFunc = Function::Create(NewType, Func->getLinkage());
+ NewFunc->copyAttributesFrom(Func);
+ Func->getParent()->getFunctionList().insert(Func, NewFunc);
+ NewFunc->takeName(Func);
+ NewFunc->getBasicBlockList().splice(NewFunc->begin(),
+ Func->getBasicBlockList());
+ Func->replaceAllUsesWith(
+ ConstantExpr::getBitCast(NewFunc,
+ Func->getFunctionType()->getPointerTo()));
+ return NewFunc;
+}
diff --git a/lib/Transforms/NaCl/ExpandVarArgs.cpp b/lib/Transforms/NaCl/ExpandVarArgs.cpp
index 3ea093f8f8..36f58416e2 100644
--- a/lib/Transforms/NaCl/ExpandVarArgs.cpp
+++ b/lib/Transforms/NaCl/ExpandVarArgs.cpp
@@ -72,15 +72,7 @@ static void ExpandVarArgFunc(Function *Func) {
SmallVector<Type *, 8> Params(FTy->param_begin(), FTy->param_end());
Params.push_back(PtrType);
FunctionType *NFTy = FunctionType::get(FTy->getReturnType(), Params, false);
-
- // In order to change the function's arguments, we have to recreate
- // the function.
- Function *NewFunc = Function::Create(NFTy, Func->getLinkage());
- NewFunc->copyAttributesFrom(Func);
- Func->getParent()->getFunctionList().insert(Func, NewFunc);
- NewFunc->takeName(Func);
- NewFunc->getBasicBlockList().splice(NewFunc->begin(),
- Func->getBasicBlockList());
+ Function *NewFunc = RecreateFunction(Func, NFTy);
// Declare the new argument as "noalias".
NewFunc->setAttributes(
@@ -95,8 +87,6 @@ static void ExpandVarArgFunc(Function *Func) {
NewArg->takeName(Arg);
}
- Func->replaceAllUsesWith(
- ConstantExpr::getBitCast(NewFunc, FTy->getPointerTo()));
Func->eraseFromParent();
Value *VarArgsArg = --NewFunc->arg_end();
diff --git a/lib/Transforms/NaCl/PNaClABISimplify.cpp b/lib/Transforms/NaCl/PNaClABISimplify.cpp
index 47e5fb67e6..3c59546275 100644
--- a/lib/Transforms/NaCl/PNaClABISimplify.cpp
+++ b/lib/Transforms/NaCl/PNaClABISimplify.cpp
@@ -49,6 +49,12 @@ void llvm::PNaClABISimplifyAddPostOptPasses(PassManager &PM) {
// ExpandByVal expands it to.
PM.add(createExpandByValPass());
+ // We place ExpandSmallArguments after optimization passes because
+ // some optimizations undo its changes. Note that
+ // ExpandSmallArguments requires that ExpandVarArgs has already been
+ // run.
+ PM.add(createExpandSmallArgumentsPass());
+
// We place StripMetadata after optimization passes because
// optimizations depend on the metadata.
PM.add(createStripMetadataPass());
diff --git a/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp b/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp
index 619ae04826..00d374dc7b 100644
--- a/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp
+++ b/lib/Transforms/NaCl/ReplacePtrsWithInts.cpp
@@ -552,17 +552,9 @@ bool ReplacePtrsWithInts::runOnModule(Module &M) {
FunctionConverter FC(IntPtrType);
FunctionType *NFTy = FC.convertFuncType(OldFunc->getFunctionType());
-
- // In order to change the function's argument types, we have to
- // recreate the function.
- Function *NewFunc = Function::Create(NFTy, OldFunc->getLinkage());
- NewFunc->copyAttributesFrom(OldFunc);
- NewFunc->setAttributes(RemovePointerAttrs(M.getContext(),
- NewFunc->getAttributes()));
- M.getFunctionList().insert(OldFunc, NewFunc);
- NewFunc->takeName(OldFunc);
- NewFunc->getBasicBlockList().splice(NewFunc->begin(),
- OldFunc->getBasicBlockList());
+ OldFunc->setAttributes(RemovePointerAttrs(M.getContext(),
+ OldFunc->getAttributes()));
+ Function *NewFunc = RecreateFunction(OldFunc, NFTy);
// Move the arguments across to the new function.
for (Function::arg_iterator Arg = OldFunc->arg_begin(),
@@ -594,8 +586,6 @@ bool ReplacePtrsWithInts::runOnModule(Module &M) {
}
}
FC.eraseReplacedInstructions();
- OldFunc->replaceAllUsesWith(ConstantExpr::getBitCast(NewFunc,
- OldFunc->getType()));
OldFunc->eraseFromParent();
}
// Now that all functions have their normalized types, we can remove
diff --git a/test/Transforms/NaCl/expand-small-arguments.ll b/test/Transforms/NaCl/expand-small-arguments.ll
new file mode 100644
index 0000000000..48a62d80d7
--- /dev/null
+++ b/test/Transforms/NaCl/expand-small-arguments.ll
@@ -0,0 +1,97 @@
+; RUN: opt %s -expand-small-arguments -S | FileCheck %s
+
+@var = global i8 0
+
+
+define void @small_arg(i8 %val) {
+ store i8 %val, i8* @var
+ ret void
+}
+; CHECK: define void @small_arg(i32 %val) {
+; CHECK-NEXT: %val.arg_trunc = trunc i32 %val to i8
+; CHECK-NEXT: store i8 %val.arg_trunc, i8* @var
+
+
+define i8 @small_result() {
+ %val = load i8* @var
+ ret i8 %val
+}
+; CHECK: define i32 @small_result() {
+; CHECK-NEXT: %val = load i8* @var
+; CHECK-NEXT: %val.ret_ext = zext i8 %val to i32
+; CHECK-NEXT: ret i32 %val.ret_ext
+
+define signext i8 @small_result_signext() {
+ %val = load i8* @var
+ ret i8 %val
+}
+; CHECK: define signext i32 @small_result_signext() {
+; CHECK-NEXT: %val = load i8* @var
+; CHECK-NEXT: %val.ret_ext = sext i8 %val to i32
+; CHECK-NEXT: ret i32 %val.ret_ext
+
+
+define void @call_small_arg() {
+ call void @small_arg(i8 100)
+ ret void
+}
+; CHECK: define void @call_small_arg() {
+; CHECK-NEXT: %arg_ext = zext i8 100 to i32
+; CHECK-NEXT: %.arg_cast = bitcast {{.*}} @small_arg
+; CHECK-NEXT: call void %.arg_cast(i32 %arg_ext)
+
+define void @call_small_arg_signext() {
+ call void @small_arg(i8 signext 100)
+ ret void
+}
+; CHECK: define void @call_small_arg_signext() {
+; CHECK-NEXT: %arg_ext = sext i8 100 to i32
+; CHECK-NEXT: %.arg_cast = bitcast {{.*}} @small_arg
+; CHECK-NEXT: call void %.arg_cast(i32 signext %arg_ext)
+
+
+define void @call_small_result() {
+ %r = call i8 @small_result()
+ store i8 %r, i8* @var
+ ret void
+}
+; CHECK: define void @call_small_result() {
+; CHECK-NEXT: %r.arg_cast = bitcast {{.*}} @small_result
+; CHECK-NEXT: %r = call i32 %r.arg_cast()
+; CHECK-NEXT: %r.ret_trunc = trunc i32 %r to i8
+; CHECK-NEXT: store i8 %r.ret_trunc, i8* @var
+
+
+; Check that various attributes are preserved.
+define i1 @attributes(i8 %arg) nounwind {
+ %r = tail call fastcc i1 @attributes(i8 %arg) nounwind
+ ret i1 %r
+}
+; CHECK: define i32 @attributes(i32 %arg) [[NOUNWIND:#[0-9]+]] {
+; CHECK: tail call fastcc i32 {{.*}} [[NOUNWIND]]
+
+
+; These arguments and results should be left alone.
+define i64 @larger_arguments(i32 %a, i64 %b, i8* %ptr, double %d) {
+ %r = call i64 @larger_arguments(i32 %a, i64 %b, i8* %ptr, double %d)
+ ret i64 %r
+}
+; CHECK: define i64 @larger_arguments(i32 %a, i64 %b, i8* %ptr, double %d) {
+; CHECK-NEXT: %r = call i64 @larger_arguments(i32 %a, i64 %b, i8* %ptr, double %d)
+; CHECK-NEXT: ret i64 %r
+
+
+; Intrinsics must be left alone since the pass cannot change their types.
+
+declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1)
+; CHECK: declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1)
+
+define void @intrinsic_call(i8* %ptr) {
+ call void @llvm.memset.p0i8.i32(i8* %ptr, i8 99, i32 256, i32 1, i1 0)
+ ret void
+}
+; CHECK: define void @intrinsic_call
+; CHECK-NEXT: call void @llvm.memset.p0i8.i32(i8* %ptr, i8 99,
+
+
+; CHECK: attributes [[NOUNWIND]] = { nounwind }
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index e44cfe06f4..bec40a97d9 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -604,6 +604,7 @@ int main(int argc, char **argv) {
initializeExpandConstantExprPass(Registry);
initializeExpandCtorsPass(Registry);
initializeExpandGetElementPtrPass(Registry);
+ initializeExpandSmallArgumentsPass(Registry);
initializeExpandTlsPass(Registry);
initializeExpandTlsConstantExprPass(Registry);
initializeExpandVarArgsPass(Registry);