1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
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();
}
|