changeset 57:a9d29e9f1fed trunk

[svn r61] Added support for D-style variadic functions :)
author lindquist
date Thu, 25 Oct 2007 02:39:53 +0200
parents 3a784f7790d6
children 2c3cd3596187
files dmd/declaration.h dmd/expression.c dmd/func.c gen/runtime.c gen/toir.c gen/tollvm.c gen/toobj.c gen/typinf.c lphobos/phobos.d test/vararg2.d test/vararg3.d
diffstat 11 files changed, 249 insertions(+), 96 deletions(-) [+]
line wrap: on
line diff
--- 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<VarDeclaration*> llvmNestedVars;
     llvm::Value* llvmNested;
+    llvm::Value* llvmArguments;
+    llvm::Value* llvmArgPtr;
 };
 
 struct FuncAliasDeclaration : FuncDeclaration
--- 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
 }
 
 /**************************************************
--- 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
--- 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();
--- 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; 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 @@
     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 @@
     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()) {
--- 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<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 @@
     // 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);
+
+    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<llvm::StructType>(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<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;
-            }
-            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));
+        /*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 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 {
+                Logger::println("in param");
             }
-            /*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);
-            }
+            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");
     }
--- 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);
 
--- 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()) {
--- 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;
--- /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);
+}
--- /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);
+}