# HG changeset patch # User lindquist # Date 1193272793 -7200 # Node ID a9d29e9f1fed93fd23ca79982cb2d43f42278089 # Parent 3a784f7790d66e653e8761bc3c0c61ae57c78da9 [svn r61] Added support for D-style variadic functions :) diff -r 3a784f7790d6 -r a9d29e9f1fed dmd/declaration.h --- a/dmd/declaration.h Wed Oct 24 22:26:37 2007 +0200 +++ b/dmd/declaration.h Thu Oct 25 02:39:53 2007 +0200 @@ -521,6 +521,8 @@ llvm::Value* llvmThisVar; std::set llvmNestedVars; llvm::Value* llvmNested; + llvm::Value* llvmArguments; + llvm::Value* llvmArgPtr; }; struct FuncAliasDeclaration : FuncDeclaration diff -r 3a784f7790d6 -r a9d29e9f1fed dmd/expression.c --- a/dmd/expression.c Wed Oct 24 22:26:37 2007 +0200 +++ b/dmd/expression.c Thu Oct 25 02:39:53 2007 +0200 @@ -580,6 +580,7 @@ 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 @@ arguments->dim - nparams); arguments->insert(0, e); } +#endif } /************************************************** diff -r 3a784f7790d6 -r a9d29e9f1fed dmd/func.c --- a/dmd/func.c Wed Oct 24 22:26:37 2007 +0200 +++ b/dmd/func.c Thu Oct 25 02:39:53 2007 +0200 @@ -76,6 +76,8 @@ llvmQueued = false; llvmThisVar = NULL; llvmNested = NULL; + llvmArguments = NULL; + llvmArgPtr = NULL; } Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s) @@ -1051,6 +1053,8 @@ } } +// 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 @@ 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 @@ a->push(new ExpStatement(0, e)); } +#endif // !IN_LLVM + // Merge contracts together with body into one compound statement #ifdef _DH diff -r 3a784f7790d6 -r a9d29e9f1fed gen/runtime.c --- a/gen/runtime.c Wed Oct 24 22:26:37 2007 +0200 +++ b/gen/runtime.c Thu Oct 25 02:39:53 2007 +0200 @@ -92,6 +92,11 @@ // 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(); diff -r 3a784f7790d6 -r a9d29e9f1fed gen/toir.c --- a/gen/toir.c Wed Oct 24 22:26:37 2007 +0200 +++ b/gen/toir.c Thu Oct 25 02:39:53 2007 +0200 @@ -110,12 +110,31 @@ 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 @@ } // 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 @@ 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 @@ 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 @@ 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 @@ e->type = elem::VAL; } - Logger::println("this arguments"); - // this arguments if (fn->arg) { Logger::println("This Call"); @@ -1142,8 +1158,6 @@ ++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 @@ } // regular arguments else { - for (int i=0; idim; 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 vtypes; + std::vector vvalues; + std::vector vtypeinfos; + + for (int i=0; idim; 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; igetNumElements(); ++i) + p->ir->CreateStore(vvalues[i], LLVM_DtoGEPi(mem,0,i,"tmp")); + + //llvm::Constant* typeinfoparam = llvm::ConstantPointerNull::get(llvm::cast(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; igetNumElements(); ++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; idim; 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; igetReturnType() != llvm::Type::VoidTy) varname = "tmp"; - Logger::println("%d params passed", n); - for (int i=0; igetType() << '\n'; // call the function @@ -1342,7 +1403,7 @@ 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()) { diff -r 3a784f7790d6 -r a9d29e9f1fed gen/tollvm.c --- a/gen/tollvm.c Wed Oct 24 22:26:37 2007 +0200 +++ b/gen/tollvm.c Thu Oct 25 02:39:53 2007 +0200 @@ -262,6 +262,14 @@ return llvm::cast(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 @@ // parameter types std::vector 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(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 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); + + 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; - 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(thisty) || thisty == gIR->topstruct().recty.get()) - thisty = llvm::PointerType::get(thisty); - paramvec.push_back(thisty); - usesthis = true; - } + const llvm::Type* at = LLVM_DtoType(argT); + if (llvm::isa(at)) { + Logger::println("struct param"); + paramvec.push_back(llvm::PointerType::get(at)); + } + else if (llvm::isa(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(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; - } - else - arg->llvmCopy = true; - - const llvm::Type* at = LLVM_DtoType(argT); - if (llvm::isa(at)) { - Logger::println("struct param"); - paramvec.push_back(llvm::PointerType::get(at)); + /*if (llvm::isa(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 if (llvm::isa(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(at)) { - Logger::println("opaque param"); - if (argT->ty == Tstruct || argT->ty == Tclass) - paramvec.push_back(llvm::PointerType::get(at)); - else - assert(0); + else { + Logger::println("in param"); } - /*if (llvm::isa(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); - } + 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 @@ 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 @@ 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 @@ 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"); } diff -r 3a784f7790d6 -r a9d29e9f1fed gen/toobj.c --- a/gen/toobj.c Wed Oct 24 22:26:37 2007 +0200 +++ b/gen/toobj.c Thu Oct 25 02:39:53 2007 +0200 @@ -778,6 +778,14 @@ } } + // 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); diff -r 3a784f7790d6 -r a9d29e9f1fed gen/typinf.c --- a/gen/typinf.c Wed Oct 24 22:26:37 2007 +0200 +++ b/gen/typinf.c Thu Oct 25 02:39:53 2007 +0200 @@ -223,10 +223,10 @@ * 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 @@ 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()) { diff -r 3a784f7790d6 -r a9d29e9f1fed lphobos/phobos.d --- a/lphobos/phobos.d Wed Oct 24 22:26:37 2007 +0200 +++ b/lphobos/phobos.d Thu Oct 25 02:39:53 2007 +0200 @@ -1,4 +1,5 @@ module phobos; import -std.stdio; +std.stdio, +std.stdarg; diff -r 3a784f7790d6 -r a9d29e9f1fed test/vararg2.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/vararg2.d Thu Oct 25 02:39:53 2007 +0200 @@ -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); +} diff -r 3a784f7790d6 -r a9d29e9f1fed test/vararg3.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/vararg3.d Thu Oct 25 02:39:53 2007 +0200 @@ -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); +}