Skip to content

Commit 03db62b

Browse files
author
Mark Seaborn
committed
Varargs: Add a pass to expand out varargs functions and calls to them
1 parent 4e4bfa0 commit 03db62b

File tree

6 files changed

+276
-0
lines changed

6 files changed

+276
-0
lines changed

codegen.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "expand_constantexpr.h"
2121
#include "expand_getelementptr.h"
22+
#include "expand_varargs.h"
2223
#include "gen_runtime_helpers_atomic.h"
2324
#include "runtime_helpers.h"
2425

@@ -1284,6 +1285,10 @@ void translate(llvm::Module *module, std::map<std::string,uintptr_t> *globals,
12841285
llvm::TargetData data_layout(module);
12851286
CodeBuf codebuf(&data_layout, options);
12861287

1288+
llvm::ModulePass *expand_varargs = createExpandVarArgsPass();
1289+
expand_varargs->runOnModule(*module);
1290+
delete expand_varargs;
1291+
12871292
for (llvm::Module::GlobalListType::iterator global = module->global_begin();
12881293
global != module->global_end();
12891294
++global) {

codegen_test.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,20 @@ void test_features() {
567567
ASSERT_EQ(result, 100);
568568
ASSERT_EQ(loc, 120);
569569
}
570+
571+
{
572+
uint32_t (*funcp)(uint32_t *result1,
573+
uint64_t *result2,
574+
uint32_t *result3);
575+
GET_FUNC(funcp, "test_varargs_call");
576+
uint32_t result1;
577+
uint64_t result2;
578+
uint32_t result3;
579+
ASSERT_EQ(funcp(&result1, &result2, &result3), 1234);
580+
ASSERT_EQ(result1, 111);
581+
ASSERT_EQ(result2, 222);
582+
ASSERT_EQ(result3, 333);
583+
}
570584
}
571585

572586
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))

expand_varargs.cc

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
2+
#include "expand_varargs.h"
3+
4+
#include <llvm/BasicBlock.h>
5+
#include <llvm/Constants.h>
6+
#include <llvm/Function.h>
7+
#include <llvm/InstrTypes.h>
8+
#include <llvm/Instructions.h>
9+
#include <llvm/IntrinsicInst.h>
10+
#include <llvm/Module.h>
11+
#include <llvm/Pass.h>
12+
13+
// In LLVM 3.2, this becomes <llvm/DataLayout.h>
14+
#include <llvm/Target/TargetData.h>
15+
16+
using namespace llvm;
17+
18+
namespace {
19+
class ExpandVarArgs : public ModulePass {
20+
public:
21+
static char ID; // Pass identification, replacement for typeid
22+
ExpandVarArgs() : ModulePass(ID) {
23+
}
24+
25+
virtual bool runOnModule(Module &M);
26+
};
27+
}
28+
29+
char ExpandVarArgs::ID = 0;
30+
31+
static void ExpandVarArgFunc(Function *Func) {
32+
Module *Module = Func->getParent();
33+
Type *PtrType = Type::getInt8Ty(Module->getContext())->getPointerTo();
34+
35+
FunctionType *FTy = Func->getFunctionType();
36+
std::vector<Type*> Params(FTy->param_begin(), FTy->param_end());
37+
Params.push_back(PtrType);
38+
FunctionType *NFTy = FunctionType::get(FTy->getReturnType(), Params, false);
39+
40+
// In order to change the function's arguments, we have to recreate
41+
// the function.
42+
Function *NewFunc = Function::Create(NFTy, Func->getLinkage());
43+
NewFunc->copyAttributesFrom(Func);
44+
Func->getParent()->getFunctionList().insert(Func, NewFunc);
45+
NewFunc->takeName(Func);
46+
NewFunc->getBasicBlockList().splice(NewFunc->begin(),
47+
Func->getBasicBlockList());
48+
49+
// Move the arguments across to the new function.
50+
for (Function::arg_iterator Arg = Func->arg_begin(), E = Func->arg_end(),
51+
NewArg = NewFunc->arg_begin();
52+
Arg != E; ++Arg, ++NewArg) {
53+
Arg->replaceAllUsesWith(NewArg);
54+
NewArg->takeName(Arg);
55+
}
56+
57+
Func->replaceAllUsesWith(
58+
ConstantExpr::getBitCast(NewFunc, FTy->getPointerTo()));
59+
Func->eraseFromParent();
60+
61+
Value *VarArgsArg = --NewFunc->arg_end();
62+
VarArgsArg->setName("varargs");
63+
64+
for (Function::iterator BB = NewFunc->begin(), E = NewFunc->end();
65+
BB != E;
66+
++BB) {
67+
for (BasicBlock::iterator Iter = BB->begin(), E = BB->end();
68+
Iter != E; ) {
69+
Instruction *Inst = Iter++;
70+
// TODO: Use VAStartInst when we upgrade LLVM version.
71+
if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(Inst)) {
72+
if (II->getIntrinsicID() == Intrinsic::vastart) {
73+
Value *Cast = new BitCastInst(II->getOperand(0),
74+
PtrType->getPointerTo(),
75+
"arglist", II);
76+
new StoreInst(VarArgsArg, Cast, II);
77+
II->eraseFromParent();
78+
} else if (II->getIntrinsicID() == Intrinsic::vaend) {
79+
II->eraseFromParent();
80+
}
81+
}
82+
}
83+
}
84+
85+
// TODO: Update debug information too.
86+
}
87+
88+
static void ExpandVAArgInst(VAArgInst *Inst, TargetData *DataLayout) {
89+
Module *Module = Inst->getParent()->getParent()->getParent();
90+
Type *I8 = Type::getInt8Ty(Module->getContext());
91+
Type *I32 = Type::getInt32Ty(Module->getContext());
92+
93+
// Read the argument.
94+
Value *ArgList = new BitCastInst(Inst->getPointerOperand(),
95+
I8->getPointerTo()->getPointerTo(),
96+
"arglist", Inst);
97+
Value *CurrentPtr = new LoadInst(ArgList, "arglist_current", Inst);
98+
Value *Result =
99+
new LoadInst(new BitCastInst(CurrentPtr, Inst->getType()->getPointerTo(),
100+
"va_arg_ptr", Inst),
101+
"va_arg", Inst);
102+
103+
// Update the va_list to point to the next argument.
104+
// TODO: Add alignment.
105+
unsigned Offset = DataLayout->getTypeAllocSize(Inst->getType());
106+
std::vector<Value*> Indexes;
107+
Indexes.push_back(ConstantInt::get(I32, Offset));
108+
Value *Next = GetElementPtrInst::Create(CurrentPtr, Indexes,
109+
"arglist_next", Inst);
110+
new StoreInst(Next, ArgList, Inst);
111+
112+
Inst->replaceAllUsesWith(Result);
113+
Inst->eraseFromParent();
114+
}
115+
116+
static bool ExpandVarArgCall(CallInst *Call) {
117+
FunctionType *FuncType = cast<FunctionType>(
118+
Call->getCalledValue()->getType()->getPointerElementType());
119+
if (!FuncType->isFunctionVarArg())
120+
return false;
121+
122+
LLVMContext *Context =
123+
&Call->getParent()->getParent()->getParent()->getContext();
124+
125+
// Split argument list into fixed and variable arguments.
126+
std::vector<Value*> FixedArgs;
127+
std::vector<Value*> VarArgs;
128+
for (unsigned I = 0; I < FuncType->getNumParams(); ++I)
129+
FixedArgs.push_back(Call->getArgOperand(I));
130+
for (unsigned I = FuncType->getNumParams();
131+
I < Call->getNumArgOperands(); ++I)
132+
VarArgs.push_back(Call->getArgOperand(I));
133+
134+
// Create struct type for packing variable arguments into.
135+
std::vector<Type*> VarArgsTypes;
136+
for (std::vector<Value*>::iterator Iter = VarArgs.begin();
137+
Iter != VarArgs.end();
138+
++Iter) {
139+
VarArgsTypes.push_back((*Iter)->getType());
140+
}
141+
// TODO: We create this as packed for now, but we might need to add
142+
// alignments later.
143+
StructType *VarArgsTy = StructType::create(VarArgsTypes, "vararg_call", true);
144+
145+
// Allocate space for the variable argument buffer. Do this at the
146+
// start of the function so that we don't leak space if the function
147+
// is called in a loop.
148+
Function *Func = Call->getParent()->getParent();
149+
Instruction *Buf = new AllocaInst(VarArgsTy, "vararg_buffer");
150+
Func->getEntryBlock().getInstList().push_front(Buf);
151+
152+
// Copy variable arguments into buffer.
153+
int Index = 0;
154+
for (std::vector<Value*>::iterator Iter = VarArgs.begin();
155+
Iter != VarArgs.end();
156+
++Iter, ++Index) {
157+
std::vector<Value*> Indexes;
158+
Indexes.push_back(ConstantInt::get(*Context, APInt(32, 0)));
159+
Indexes.push_back(ConstantInt::get(*Context, APInt(32, Index)));
160+
Value *Ptr = GetElementPtrInst::Create(Buf, Indexes, "vararg_ptr", Call);
161+
new StoreInst(*Iter, Ptr, Call);
162+
}
163+
164+
// Cast function to new type to add our extra pointer argument.
165+
std::vector<Type*> ArgTypes(FuncType->param_begin(), FuncType->param_end());
166+
ArgTypes.push_back(VarArgsTy->getPointerTo());
167+
FunctionType *NFTy = FunctionType::get(FuncType->getReturnType(),
168+
ArgTypes, false);
169+
Value *CastFunc = new BitCastInst(Call->getCalledValue(),
170+
NFTy->getPointerTo(), "vararg_func", Call);
171+
172+
// Create the converted function call.
173+
FixedArgs.push_back(Buf);
174+
Value *NewCall = CallInst::Create(CastFunc, FixedArgs, "", Call);
175+
NewCall->takeName(Call);
176+
Call->replaceAllUsesWith(NewCall);
177+
Call->eraseFromParent();
178+
179+
return true;
180+
}
181+
182+
bool ExpandVarArgs::runOnModule(Module &M) {
183+
bool Changed = false;
184+
TargetData DataLayout(&M);
185+
186+
for (Module::iterator Iter = M.begin(), E = M.end(); Iter != E; ) {
187+
Function *Func = Iter++;
188+
189+
for (Function::iterator BB = Func->begin(), E = Func->end();
190+
BB != E;
191+
++BB) {
192+
for (BasicBlock::iterator Iter = BB->begin(), E = BB->end();
193+
Iter != E; ) {
194+
Instruction *Inst = Iter++;
195+
if (VAArgInst *VI = dyn_cast<VAArgInst>(Inst)) {
196+
Changed = true;
197+
ExpandVAArgInst(VI, &DataLayout);
198+
} else if (CallInst *Call = dyn_cast<CallInst>(Inst)) {
199+
Changed |= ExpandVarArgCall(Call);
200+
}
201+
}
202+
}
203+
204+
if (Func->isVarArg()) {
205+
Changed = true;
206+
ExpandVarArgFunc(Func);
207+
}
208+
}
209+
210+
return Changed;
211+
}
212+
213+
ModulePass *createExpandVarArgsPass() {
214+
return new ExpandVarArgs();
215+
}

expand_varargs.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
#ifndef EXPAND_VARARGS_
3+
#define EXPAND_VARARGS_ 1
4+
5+
#include <llvm/Pass.h>
6+
7+
llvm::ModulePass *createExpandVarArgsPass();
8+
9+
#endif

run.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ clang -O2 -m32 -c gen_runtime_helpers_atomic.ll -o gen_runtime_helpers_atomic.o
3434
3535
$ccache g++ -m32 $cflags -c expand_constantexpr.cc
3636
$ccache g++ -m32 $cflags -c expand_getelementptr.cc
37+
$ccache g++ -m32 $cflags -c expand_varargs.cc
3738
$ccache g++ -m32 $cflags -c codegen.cc
3839
$ccache g++ -m32 $cflags -c codegen_test.cc
3940
$ccache g++ -m32 $cflags -c run_program.cc
@@ -42,6 +43,7 @@ $ccache g++ -m32 $cflags -c -O2 runtime_helpers.c
4243
lib="
4344
expand_constantexpr.o
4445
expand_getelementptr.o
46+
expand_varargs.o
4547
codegen.o
4648
gen_runtime_helpers_atomic.o
4749
runtime_helpers.o"

test.ll

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,3 +458,34 @@ define i32 @test_atomicrmw_i32_add(i32* %ptr, i32 %val) {
458458
%1 = atomicrmw add i32* %ptr, i32 %val seq_cst
459459
ret i32 %1
460460
}
461+
462+
463+
declare void @llvm.va_start(i8*)
464+
declare void @llvm.va_end(i8*)
465+
466+
; PNaCl's va_list is currently 16 bytes.
467+
%va_list = type [4 x i32]
468+
469+
define i32 @varargs_func(i32 %arg, i32* %res1, i64* %res2, i32* %res3, ...) {
470+
%arglist_alloc = alloca %va_list
471+
%arglist = bitcast %va_list* %arglist_alloc to i8*
472+
call void @llvm.va_start(i8* %arglist)
473+
474+
%val1 = va_arg i8* %arglist, i32
475+
store i32 %val1, i32* %res1
476+
%val2 = va_arg i8* %arglist, i64
477+
store i64 %val2, i64* %res2
478+
%val3 = va_arg i8* %arglist, i32
479+
store i32 %val3, i32* %res3
480+
481+
call void @llvm.va_end(i8* %arglist)
482+
ret i32 %arg
483+
}
484+
485+
define i32 @test_varargs_call(i32* %res1, i64* %res2, i32* %res3) {
486+
%result = call i32 (i32, i32*, i64*, i32*, ...)*
487+
@varargs_func(i32 1234,
488+
i32* %res1, i64* %res2, i32* %res3,
489+
i32 111, i64 222, i32 333)
490+
ret i32 %result
491+
}

0 commit comments

Comments
 (0)