[svn r61] Added support for D-style variadic functions :)

This commit is contained in:
Tomas Lindquist Olsen
2007-10-25 02:39:53 +02:00
parent e92b027299
commit 5478de7147
11 changed files with 249 additions and 96 deletions

View File

@@ -521,6 +521,8 @@ struct FuncDeclaration : Declaration
llvm::Value* llvmThisVar;
std::set<VarDeclaration*> llvmNestedVars;
llvm::Value* llvmNested;
llvm::Value* llvmArguments;
llvm::Value* llvmArgPtr;
};
struct FuncAliasDeclaration : FuncDeclaration

View File

@@ -580,6 +580,7 @@ void functionArguments(Loc loc, Scope *sc, TypeFunction *tf, Expressions *argume
break;
}
#if !IN_LLVM
// If D linkage and variadic, add _arguments[] as first argument
if (tf->linkage == LINKd && tf->varargs == 1)
{
@@ -589,6 +590,7 @@ void functionArguments(Loc loc, Scope *sc, TypeFunction *tf, Expressions *argume
arguments->dim - nparams);
arguments->insert(0, e);
}
#endif
}
/**************************************************

View File

@@ -76,6 +76,8 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, enum STC s
llvmQueued = false;
llvmThisVar = NULL;
llvmNested = NULL;
llvmArguments = NULL;
llvmArgPtr = NULL;
}
Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s)
@@ -1051,6 +1053,8 @@ void FuncDeclaration::semantic3(Scope *sc)
}
}
// we'll handle variadics ourselves
#if !IN_LLVM
if (argptr)
{ // Initialize _argptr to point past non-variadic arg
#if IN_GCC
@@ -1075,7 +1079,7 @@ void FuncDeclaration::semantic3(Scope *sc)
e = new AssignExp(0, e1, e);
e->type = t;
a->push(new ExpStatement(0, e));
#endif
#endif // IN_GCC
}
if (_arguments)
@@ -1091,6 +1095,8 @@ void FuncDeclaration::semantic3(Scope *sc)
a->push(new ExpStatement(0, e));
}
#endif // !IN_LLVM
// Merge contracts together with body into one compound statement
#ifdef _DH

View File

@@ -92,6 +92,11 @@ llvm::GlobalVariable* LLVM_D_GetRuntimeGlobal(llvm::Module* target, const char*
// TODO maybe check the target module first, to allow overriding the runtime on a pre module basis?
// could be done and seems like it could be neat too :)
llvm::GlobalVariable* gv = target->getNamedGlobal(name);
if (gv) {
return gv;
}
if (global.params.noruntime) {
error("No implicit runtime calls allowed with -noruntime option enabled");
fatal();

View File

@@ -110,12 +110,31 @@ elem* VarExp::toElem(IRState* p)
assert(var);
if (VarDeclaration* vd = var->isVarDeclaration())
{
Logger::println("VarDeclaration");
Logger::println("VarDeclaration %s", vd->toChars());
if (vd->nestedref) {
Logger::println("has nested ref");
}
// _arguments
if (vd->ident == Id::_arguments)
{
vd->llvmValue = p->func().decl->llvmArguments;
assert(vd->llvmValue);
e->mem = vd->llvmValue;
e->type = elem::VAR;
return e;
}
// _argptr
else if (vd->ident == Id::_argptr)
{
vd->llvmValue = p->func().decl->llvmArgPtr;
assert(vd->llvmValue);
e->mem = vd->llvmValue;
e->type = elem::VAR;
return e;
}
// needed to take care of forward references of global variables
if (!vd->llvmTouched && vd->isDataseg())
vd->toObjFile();
@@ -147,8 +166,8 @@ elem* VarExp::toElem(IRState* p)
}
// global forward ref
else {
Logger::println("unsupported: %s\n", vd->toChars());
assert(0 && "only magic supported is typeinfo");
Logger::println("unsupported magic: %s\n", vd->toChars());
assert(0 && "only magic supported is $, _arguments, _argptr");
}
return e;
}
@@ -447,7 +466,7 @@ llvm::Constant* StringExp::toConstElem(IRState* p)
elem* AssignExp::toElem(IRState* p)
{
Logger::print("AssignExp::toElem: %s | %s = %s\n", toChars(), e1->type->toChars(), e2->type->toChars());
Logger::print("AssignExp::toElem: %s | %s = %s\n", toChars(), e1->type->toChars(), e2->type ? e2->type->toChars() : 0);
LOG_SCOPE;
p->exps.push_back(IRExp(e1,e2,NULL));
@@ -1046,6 +1065,7 @@ elem* CallExp::toElem(IRState* p)
n = 1;
if (fn->arg || delegateCall) n++;
if (retinptr) n++;
if (tf->linkage == LINKd && tf->varargs == 1) n+=2;
llvm::Value* funcval = fn->getValue();
assert(funcval != 0);
@@ -1091,8 +1111,6 @@ elem* CallExp::toElem(IRState* p)
llvm::FunctionType::param_iterator argiter = llfnty->param_begin();
int j = 0;
Logger::println("hidden struct return");
IRExp* topexp = p->topexp();
// hidden struct return arguments
@@ -1118,8 +1136,6 @@ elem* CallExp::toElem(IRState* p)
e->type = elem::VAL;
}
Logger::println("this arguments");
// this arguments
if (fn->arg) {
Logger::println("This Call");
@@ -1142,8 +1158,6 @@ elem* CallExp::toElem(IRState* p)
++argiter;
}
Logger::println("regular arguments");
// va arg function special argument passing
if (va_magic) {
size_t n = va_intrinsic ? arguments->dim : 1;
@@ -1159,10 +1173,63 @@ elem* CallExp::toElem(IRState* p)
}
// regular arguments
else {
for (int i=0; i<arguments->dim; i++,j++)
if (tf->linkage == LINKd && tf->varargs == 1)
{
Argument* fnarg = Argument::getNth(tf->parameters, i);
llargs[j] = LLVM_DtoArgument(llfnty->getParamType(j), fnarg, (Expression*)arguments->data[i]);
Logger::println("doing d-style variadic arguments");
std::vector<const llvm::Type*> vtypes;
std::vector<llvm::Value*> vvalues;
std::vector<llvm::Value*> vtypeinfos;
for (int i=0; i<arguments->dim; i++) {
Argument* fnarg = Argument::getNth(tf->parameters, i);
Expression* argexp = (Expression*)arguments->data[i];
vvalues.push_back(LLVM_DtoArgument(NULL, fnarg, argexp));
vtypes.push_back(vvalues.back()->getType());
TypeInfoDeclaration* tidecl = argexp->type->getTypeInfoDeclaration();
tidecl->toObjFile();
assert(tidecl->llvmValue);
vtypeinfos.push_back(tidecl->llvmValue);
}
const llvm::StructType* vtype = llvm::StructType::get(vtypes);
llvm::Value* mem = new llvm::AllocaInst(vtype,"_argptr_storage",p->topallocapoint());
for (unsigned i=0; i<vtype->getNumElements(); ++i)
p->ir->CreateStore(vvalues[i], LLVM_DtoGEPi(mem,0,i,"tmp"));
//llvm::Constant* typeinfoparam = llvm::ConstantPointerNull::get(llvm::cast<llvm::PointerType>(llfnty->getParamType(j)));
assert(Type::typeinfo->llvmInitZ);
const llvm::Type* typeinfotype = llvm::PointerType::get(Type::typeinfo->llvmInitZ->getType());
Logger::cout() << "typeinfo ptr type: " << *typeinfotype << '\n';
const llvm::ArrayType* typeinfoarraytype = llvm::ArrayType::get(typeinfotype,vtype->getNumElements());
llvm::Value* typeinfomem = new llvm::AllocaInst(typeinfoarraytype,"_arguments_storage",p->topallocapoint());
for (unsigned i=0; i<vtype->getNumElements(); ++i) {
llvm::Value* v = p->ir->CreateBitCast(vtypeinfos[i], typeinfotype, "tmp");
p->ir->CreateStore(v, LLVM_DtoGEPi(typeinfomem,0,i,"tmp"));
}
llvm::Value* typeinfoarrayparam = new llvm::AllocaInst(llfnty->getParamType(j)->getContainedType(0),"_arguments_array",p->topallocapoint());
p->ir->CreateStore(LLVM_DtoConstSize_t(vtype->getNumElements()), LLVM_DtoGEPi(typeinfoarrayparam,0,0,"tmp"));
llvm::Value* casttypeinfomem = p->ir->CreateBitCast(typeinfomem, llvm::PointerType::get(typeinfotype), "tmp");
p->ir->CreateStore(casttypeinfomem, LLVM_DtoGEPi(typeinfoarrayparam,0,1,"tmp"));
llargs[j] = typeinfoarrayparam;;
j++;
llargs[j] = p->ir->CreateBitCast(mem, llvm::PointerType::get(llvm::Type::Int8Ty), "tmp");
j++;
llargs.resize(2);
}
else {
Logger::println("doing normal arguments");
for (int i=0; i<arguments->dim; i++,j++) {
Argument* fnarg = Argument::getNth(tf->parameters, i);
llargs[j] = LLVM_DtoArgument(llfnty->getParamType(j), fnarg, (Expression*)arguments->data[i]);
}
Logger::println("%d params passed", n);
for (int i=0; i<n; ++i) {
Logger::cout() << *llargs[i] << '\n';
}
}
}
@@ -1171,12 +1238,6 @@ elem* CallExp::toElem(IRState* p)
if (llfnty->getReturnType() != llvm::Type::VoidTy)
varname = "tmp";
Logger::println("%d params passed", n);
for (int i=0; i<n; ++i)
{
Logger::cout() << *llargs[i] << '\n';
}
Logger::cout() << "Calling: " << *funcval->getType() << '\n';
// call the function
@@ -1342,7 +1403,7 @@ elem* CastExp::toElem(IRState* p)
else if (fromtype->ty == Tpointer) {
if (totype->ty == Tpointer || totype->ty == Tclass) {
llvm::Value* src = u->getValue();
//Logger::cout() << *src << '|' << *totype << '\n';
Logger::cout() << *src << '|' << *tolltype << '\n';
e->val = new llvm::BitCastInst(src, tolltype, "tmp", p->scopebb());
}
else if (totype->isintegral()) {

View File

@@ -262,6 +262,14 @@ const llvm::FunctionType* LLVM_DtoFunctionType(FuncDeclaration* fdecl)
return llvm::cast<llvm::FunctionType>(f->llvmType);
}
bool typesafeVararg = false;
if (f->linkage == LINKd && f->varargs == 1) {
assert(fdecl->v_arguments);
Logger::println("v_arguments = %s", fdecl->v_arguments->toChars());
assert(fdecl->v_arguments->isParameter());
typesafeVararg = true;
}
// return value type
const llvm::Type* rettype;
const llvm::Type* actualRettype;
@@ -291,84 +299,92 @@ const llvm::FunctionType* LLVM_DtoFunctionType(FuncDeclaration* fdecl)
// parameter types
std::vector<const llvm::Type*> paramvec;
if (fdecl->llvmInternal == LLVMva_start) {
if (retinptr) {
Logger::cout() << "returning through pointer parameter: " << *rettype << '\n';
paramvec.push_back(rettype);
}
if (fdecl->needThis()) {
if (AggregateDeclaration* ad = fdecl->isMember()) {
Logger::print("isMember = this is: %s\n", ad->type->toChars());
const llvm::Type* thisty = LLVM_DtoType(ad->type);
Logger::cout() << "this llvm type: " << *thisty << '\n';
if (llvm::isa<llvm::StructType>(thisty) || thisty == gIR->topstruct().recty.get())
thisty = llvm::PointerType::get(thisty);
paramvec.push_back(thisty);
usesthis = true;
}
else
assert(0);
}
else if (fdecl->isNested()) {
paramvec.push_back(llvm::PointerType::get(llvm::Type::Int8Ty));
usesthis = true;
}
if (typesafeVararg) {
ClassDeclaration* ti = Type::typeinfo;
if (!ti->llvmInitZ)
ti->toObjFile();
assert(ti->llvmInitZ);
std::vector<const llvm::Type*> types;
types.push_back(LLVM_DtoSize_t());
types.push_back(llvm::PointerType::get(llvm::PointerType::get(ti->llvmInitZ->getType())));
const llvm::Type* t1 = llvm::StructType::get(types);
paramvec.push_back(llvm::PointerType::get(t1));
paramvec.push_back(llvm::PointerType::get(llvm::Type::Int8Ty));
}
else {
if (retinptr) {
Logger::cout() << "returning through pointer parameter: " << *rettype << '\n';
paramvec.push_back(rettype);
}
if (fdecl->needThis()) {
if (AggregateDeclaration* ad = fdecl->isMember()) {
Logger::print("isMember = this is: %s\n", ad->type->toChars());
const llvm::Type* thisty = LLVM_DtoType(ad->type);
Logger::cout() << "this llvm type: " << *thisty << '\n';
if (llvm::isa<llvm::StructType>(thisty) || thisty == gIR->topstruct().recty.get())
thisty = llvm::PointerType::get(thisty);
paramvec.push_back(thisty);
usesthis = true;
}
size_t n = Argument::dim(f->parameters);
for (int i=0; i < n; ++i) {
Argument* arg = Argument::getNth(f->parameters, i);
// ensure scalar
Type* argT = LLVM_DtoDType(arg->type);
assert(argT);
if ((arg->storageClass & STCref) || (arg->storageClass & STCout)) {
//assert(arg->vardecl);
//arg->vardecl->refparam = true;
}
else
arg->llvmCopy = true;
const llvm::Type* at = LLVM_DtoType(argT);
if (llvm::isa<llvm::StructType>(at)) {
Logger::println("struct param");
paramvec.push_back(llvm::PointerType::get(at));
}
else if (llvm::isa<llvm::ArrayType>(at)) {
Logger::println("sarray param");
assert(argT->ty == Tsarray);
//paramvec.push_back(llvm::PointerType::get(at->getContainedType(0)));
paramvec.push_back(llvm::PointerType::get(at));
}
else if (llvm::isa<llvm::OpaqueType>(at)) {
Logger::println("opaque param");
if (argT->ty == Tstruct || argT->ty == Tclass)
paramvec.push_back(llvm::PointerType::get(at));
else
assert(0);
}
else if (fdecl->isNested()) {
paramvec.push_back(llvm::PointerType::get(llvm::Type::Int8Ty));
usesthis = true;
}
size_t n = Argument::dim(f->parameters);
for (int i=0; i < n; ++i) {
Argument* arg = Argument::getNth(f->parameters, i);
// ensure scalar
Type* argT = LLVM_DtoDType(arg->type);
assert(argT);
if ((arg->storageClass & STCref) || (arg->storageClass & STCout)) {
//assert(arg->vardecl);
//arg->vardecl->refparam = true;
/*if (llvm::isa<llvm::StructType>(at) || argT->ty == Tstruct || argT->ty == Tsarray) {
paramvec.push_back(llvm::PointerType::get(at));
}*/
else {
if (!arg->llvmCopy) {
Logger::println("ref param");
at = llvm::PointerType::get(at);
}
else
arg->llvmCopy = true;
const llvm::Type* at = LLVM_DtoType(argT);
if (llvm::isa<llvm::StructType>(at)) {
Logger::println("struct param");
paramvec.push_back(llvm::PointerType::get(at));
}
else if (llvm::isa<llvm::ArrayType>(at)) {
Logger::println("sarray param");
assert(argT->ty == Tsarray);
//paramvec.push_back(llvm::PointerType::get(at->getContainedType(0)));
paramvec.push_back(llvm::PointerType::get(at));
}
else if (llvm::isa<llvm::OpaqueType>(at)) {
Logger::println("opaque param");
if (argT->ty == Tstruct || argT->ty == Tclass)
paramvec.push_back(llvm::PointerType::get(at));
else
assert(0);
}
/*if (llvm::isa<llvm::StructType>(at) || argT->ty == Tstruct || argT->ty == Tsarray) {
paramvec.push_back(llvm::PointerType::get(at));
}*/
else {
if (!arg->llvmCopy) {
Logger::println("ref param");
at = llvm::PointerType::get(at);
}
else {
Logger::println("in param");
}
paramvec.push_back(at);
Logger::println("in param");
}
paramvec.push_back(at);
}
}
// construct function type
bool isvararg = f->varargs;
bool isvararg = !typesafeVararg && f->varargs;
llvm::FunctionType* functype = llvm::FunctionType::get(actualRettype, paramvec, isvararg);
f->llvmType = functype;
@@ -1075,18 +1091,33 @@ llvm::Function* LLVM_DtoDeclareFunction(FuncDeclaration* fdecl)
iarg->setName("this");
++iarg;
}
int varargs = -1;
if (f->linkage == LINKd && f->varargs == 1)
varargs = 0;
for (; iarg != func->arg_end(); ++iarg)
{
Argument* arg = Argument::getNth(f->parameters, k++);
assert(arg != 0);
//arg->llvmValue = iarg;
//printf("identifier: '%s' %p\n", arg->ident->toChars(), arg->ident);
if (arg->ident != 0) {
if (arg && arg->ident != 0) {
if (arg->vardecl) {
arg->vardecl->llvmValue = iarg;
}
iarg->setName(arg->ident->toChars());
}
else if (!arg && varargs >= 0) {
if (varargs == 0) {
iarg->setName("_arguments");
fdecl->llvmArguments = iarg;
}
else if (varargs == 1) {
iarg->setName("_argptr");
fdecl->llvmArgPtr = iarg;
}
else
assert(0);
varargs++;
}
else {
iarg->setName("unnamed");
}
@@ -1236,7 +1267,7 @@ llvm::Value* LLVM_DtoArgument(const llvm::Type* paramtype, Argument* fnarg, Expr
else {
Logger::println("as ptr arg");
retval = arg->mem ? arg->mem : arg->val;
if (retval->getType() != paramtype)
if (paramtype && retval->getType() != paramtype)
{
assert(retval->getType() == paramtype->getContainedType(0));
LLVM_DtoGiveArgumentStorage(arg);
@@ -1247,7 +1278,7 @@ llvm::Value* LLVM_DtoArgument(const llvm::Type* paramtype, Argument* fnarg, Expr
delete arg;
if (fnarg && retval->getType() != paramtype) {
if (fnarg && paramtype && retval->getType() != paramtype) {
Logger::cout() << "got '" << *retval->getType() << "' expected '" << *paramtype << "'\n";
assert(0 && "parameter type that was actually passed is invalid");
}

View File

@@ -778,6 +778,14 @@ void FuncDeclaration::toObjFile()
}
}
// copy _argptr to a memory location
if (f->linkage == LINKd && f->varargs == 1)
{
llvm::Value* argptrmem = new llvm::AllocaInst(llvmArgPtr->getType(), "_argptrmem", gIR->topallocapoint());
new llvm::StoreInst(llvmArgPtr, argptrmem, gIR->scopebb());
llvmArgPtr = argptrmem;
}
// output function body
fbody->toIR(gIR);

View File

@@ -223,10 +223,10 @@ int TypeDArray::builtinTypeInfo()
* Used to supply hidden _arguments[] value for variadic D functions.
*/
Expression *createTypeInfoArray(Scope *sc, Expression *args[], int dim)
Expression *createTypeInfoArray(Scope *sc, Expression *exps[], int dim)
{
assert(0);
return 0;
return NULL;
}
/* ========================================================================= */
@@ -237,13 +237,13 @@ Expression *createTypeInfoArray(Scope *sc, Expression *args[], int dim)
void TypeInfoDeclaration::toObjFile()
{
if (llvmTouched) return;
else llvmTouched = true;
Logger::println("TypeInfoDeclaration::toObjFile()");
LOG_SCOPE;
Logger::println("type = '%s'", tinfo->toChars());
if (llvmTouched) return;
else llvmTouched = true;
Logger::println("typeinfo mangle: %s", mangle());
if (tinfo->builtinTypeInfo()) {

View File

@@ -1,4 +1,5 @@
module phobos;
import
std.stdio;
std.stdio,
std.stdarg;

18
test/vararg2.d Normal file
View File

@@ -0,0 +1,18 @@
module vararg2;
void func(...)
{
assert(_arguments.length == 2);
assert(_arguments[0] is typeid(int));
int a = *cast(int*)_argptr;
_argptr += int.sizeof;
assert(_arguments[1] is typeid(int));
a += *cast(int*)_argptr;
_argptr += int.sizeof;
assert(a == 3);
}
void main()
{
func(1,2);
}

19
test/vararg3.d Normal file
View File

@@ -0,0 +1,19 @@
module vararg3;
import std.stdarg;
void func(...)
{
assert(_arguments.length == 3);
assert(_arguments[0] is typeid(int));
assert(_arguments[1] is typeid(float));
assert(_arguments[2] is typeid(long));
assert(va_arg!(int)(_argptr) == 4);
assert(va_arg!(float)(_argptr) == 2.5f);
assert(va_arg!(long)(_argptr) == 42L);
}
void main()
{
func(4, 2.5f, 42L);
}