# HG changeset patch # User lindquist # Date 1193257086 -7200 # Node ID 0ccfae271c456d7e6359d2bdcb1a816467f0c4a9 # Parent 28e99b04a1324ca5b71bf7181a26cea5f82c662d [svn r59] Added support for C-style variadic functions. Currently only works on x86, x86-64 va_arg is broken in LLVM 2.1. PPC and PPC64 unknown. Updates to runtime. Rebuild! diff -r 28e99b04a132 -r 0ccfae271c45 dmd/attrib.c --- a/dmd/attrib.c Wed Oct 24 01:37:34 2007 +0200 +++ b/dmd/attrib.c Wed Oct 24 22:18:06 2007 +0200 @@ -29,8 +29,10 @@ #include "aggregate.h" #include "module.h" #include "parse.h" +#include "template.h" #include "../gen/enums.h" +#include "../gen/logger.h" extern void obj_includelib(char *name); @@ -752,9 +754,9 @@ #if IN_LLVM int llvm_internal = 0; char* llvm_str1 = NULL; - + #endif - + //printf("\tPragmaDeclaration::semantic '%s'\n",toChars()); if (ident == Id::msg) { @@ -873,6 +875,18 @@ llvm_internal = LLVMbind; assert(args->dim == 2); } + else if (strcmp(str,"va_start")==0) { + llvm_internal = LLVMva_start; + assert(args->dim == 1); + } + else if (strcmp(str,"va_arg")==0) { + llvm_internal = LLVMva_arg; + assert(args->dim == 1); + } + else if (strcmp(str,"va_intrinsic")==0) { + llvm_internal = LLVMva_intrinsic; + assert(args->dim == 2); + } else { error("unknown pragma command: %s", str); } @@ -886,6 +900,7 @@ case LLVMintrinsic: case LLVMmangle: case LLVMbind: + case LLVMva_intrinsic: e = (Expression *)args->data[1]; e = e->semantic(sc); e = e->optimize(WANTvalue); @@ -897,6 +912,8 @@ break; case LLVMnull: + case LLVMva_arg: + case LLVMva_start: break; default: @@ -923,6 +940,7 @@ { case LLVMintrinsic: case LLVMmangle: + case LLVMva_intrinsic: if (FuncDeclaration* fd = s->isFuncDeclaration()) { fd->llvmInternal = llvm_internal; fd->llvmInternal1 = llvm_str1; @@ -932,7 +950,7 @@ assert(0); } break; - + case LLVMnull: if (StaticCtorDeclaration* sd = s->isStaticCtorDeclaration()) { sd->llvmInternal = llvm_internal; @@ -942,7 +960,7 @@ assert(0); } break; - + case LLVMbind: if (VarDeclaration* vd = s->isVarDeclaration()) { vd->llvmInternal = llvm_internal; @@ -953,7 +971,23 @@ assert(0); } break; - + + case LLVMva_start: + case LLVMva_arg: + if (TemplateDeclaration* td = s->isTemplateDeclaration()) { + td->llvmInternal = llvm_internal; + assert(td->parameters->dim == 1); + assert(!td->overnext); + assert(!td->overroot); + assert(td->onemember); + Logger::println("template->onemember = %s", td->onemember->toChars()); + } + else { + error("can only be used on templates"); + assert(0); + } + break; + default: assert(0 && "invalid LLVM_internal pragma got through :/"); } diff -r 28e99b04a132 -r 0ccfae271c45 dmd/dsymbol.c --- a/dmd/dsymbol.c Wed Oct 24 01:37:34 2007 +0200 +++ b/dmd/dsymbol.c Wed Oct 24 22:18:06 2007 +0200 @@ -47,6 +47,7 @@ this->llvmInternal1 = NULL; this->llvmInternal2 = NULL; this->llvmValue = NULL; + this->llvmDModule = NULL; } Dsymbol::Dsymbol(Identifier *ident) diff -r 28e99b04a132 -r 0ccfae271c45 dmd/mars.c --- a/dmd/mars.c Wed Oct 24 01:37:34 2007 +0200 +++ b/dmd/mars.c Wed Oct 24 22:18:06 2007 +0200 @@ -15,6 +15,7 @@ #include #include #include +#include #if _WIN32 #include @@ -667,6 +668,10 @@ global.params.llvmArch = const_cast(e->Name); if (global.params.verbose || very_verbose) printf("Default target found: %s\n", global.params.llvmArch); + if (very_verbose) { + int X = sizeof(va_list); + printf("valist.sizeof = %d\n", X); + } } } diff -r 28e99b04a132 -r 0ccfae271c45 gen/enums.h --- a/gen/enums.h Wed Oct 24 01:37:34 2007 +0200 +++ b/gen/enums.h Wed Oct 24 22:18:06 2007 +0200 @@ -5,4 +5,7 @@ LLVMmangle, LLVMintrinsic, LLVMbind, + LLVMva_arg, + LLVMva_start, + LLVMva_intrinsic }; diff -r 28e99b04a132 -r 0ccfae271c45 gen/toir.c --- a/gen/toir.c Wed Oct 24 01:37:34 2007 +0200 +++ b/gen/toir.c Wed Oct 24 22:18:06 2007 +0200 @@ -210,12 +210,12 @@ else if (FuncDeclaration* fdecl = var->isFuncDeclaration()) { Logger::println("FuncDeclaration"); - if (fdecl->llvmValue == 0) { + if (fdecl->llvmInternal != LLVMva_arg && fdecl->llvmValue == 0) fdecl->toObjFile(); - } e->val = fdecl->llvmValue; e->type = elem::FUNC; e->funcdecl = fdecl; + return e; } else if (SymbolDeclaration* sdecl = var->isSymbolDeclaration()) { @@ -971,21 +971,21 @@ { Logger::print("CallExp::toElem: %s\n", toChars()); LOG_SCOPE; + elem* e = new elem; elem* fn = e1->toElem(p); - LINK dlink = LINKdefault; + + TypeFunction* tf = 0; + Type* e1type = LLVM_DtoDType(e1->type); bool delegateCall = false; llvm::Value* zero = llvm::ConstantInt::get(llvm::Type::Int32Ty,0,false); llvm::Value* one = llvm::ConstantInt::get(llvm::Type::Int32Ty,1,false); + LINK dlink = LINKdefault; // hidden struct return parameter handling bool retinptr = false; - TypeFunction* tf = 0; - - Type* e1type = LLVM_DtoDType(e1->type); - // regular functions if (e1type->ty == Tfunction) { tf = (TypeFunction*)e1type; @@ -1012,7 +1012,38 @@ assert(tf); } + // va args + bool va_magic = false; + bool va_intrinsic = false; + if (fn->funcdecl) { + if (fn->funcdecl->llvmInternal == LLVMva_intrinsic) { + va_magic = true; + va_intrinsic = true; + } + else if (fn->funcdecl->llvmInternal == LLVMva_start) { + va_magic = true; + } + else if (fn->funcdecl->llvmInternal == LLVMva_arg) { + Argument* fnarg = Argument::getNth(tf->parameters, 0); + Expression* exp = (Expression*)arguments->data[0]; + elem* expelem = exp->toElem(p); + assert(expelem->mem); + elem* e = new elem; + Type* t = LLVM_DtoDType(type); + const llvm::Type* llt = LLVM_DtoType(type); + if (LLVM_DtoIsPassedByRef(t)) + llt = llvm::PointerType::get(llt); + e->type = elem::VAL; + e->val = p->ir->CreateVAArg(expelem->mem,llt,"tmp"); + delete expelem; + return e; + } + } + + // args size_t n = arguments->dim; + if (fn->funcdecl && fn->funcdecl->llvmInternal == LLVMva_start) + n = 1; if (fn->arg || delegateCall) n++; if (retinptr) n++; @@ -1113,11 +1144,26 @@ Logger::println("regular arguments"); + // va arg function special argument passing + if (va_magic) { + size_t n = va_intrinsic ? arguments->dim : 1; + for (int i=0; iparameters, i); + Expression* exp = (Expression*)arguments->data[i]; + elem* expelem = exp->toElem(p); + assert(expelem->mem); + llargs[j] = p->ir->CreateBitCast(expelem->mem, llvm::PointerType::get(llvm::Type::Int8Ty), "tmp"); + delete expelem; + } + } // regular 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]); + else { + 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]); + } } // void returns cannot not be named @@ -1131,7 +1177,7 @@ Logger::cout() << *llargs[i] << '\n'; } - //Logger::cout() << "Calling: " << *funcval->getType() << '\n'; + Logger::cout() << "Calling: " << *funcval->getType() << '\n'; // call the function llvm::CallInst* call = new llvm::CallInst(funcval, llargs.begin(), llargs.end(), varname, p->scopebb()); @@ -1141,7 +1187,7 @@ e->val = call; // set calling convention - if ((fn->funcdecl && (fn->funcdecl->llvmInternal != LLVMintrinsic)) || delegateCall) + if ((fn->funcdecl && (fn->funcdecl->llvmInternal != LLVMintrinsic && fn->funcdecl->llvmInternal != LLVMva_start)) || delegateCall) call->setCallingConv(LLVM_DtoCallingConv(dlink)); else if (fn->callconv != (unsigned)-1) call->setCallingConv(fn->callconv); diff -r 28e99b04a132 -r 0ccfae271c45 gen/tollvm.c --- a/gen/tollvm.c Wed Oct 24 01:37:34 2007 +0200 +++ b/gen/tollvm.c Wed Oct 24 22:18:06 2007 +0200 @@ -221,8 +221,39 @@ ////////////////////////////////////////////////////////////////////////////////////////// +static const llvm::FunctionType* LLVM_DtoVaFunctionType(FuncDeclaration* fdecl) +{ + TypeFunction* f = (TypeFunction*)fdecl->type; + assert(f != 0); + + const llvm::PointerType* i8pty = llvm::PointerType::get(llvm::Type::Int8Ty); + std::vector args; + + if (fdecl->llvmInternal == LLVMva_start) { + args.push_back(i8pty); + } + else if (fdecl->llvmInternal == LLVMva_intrinsic) { + size_t n = Argument::dim(f->parameters); + for (size_t i=0; illvmType = fty; + return fty; +} + +////////////////////////////////////////////////////////////////////////////////////////// + const llvm::FunctionType* LLVM_DtoFunctionType(FuncDeclaration* fdecl) { + if ((fdecl->llvmInternal == LLVMva_start) || (fdecl->llvmInternal == LLVMva_intrinsic)) { + return LLVM_DtoVaFunctionType(fdecl); + } + TypeFunction* f = (TypeFunction*)fdecl->type; assert(f != 0); @@ -260,73 +291,79 @@ // parameter types std::vector paramvec; - if (retinptr) { - Logger::cout() << "returning through pointer parameter: " << *rettype << '\n'; - paramvec.push_back(rettype); + if (fdecl->llvmInternal == LLVMva_start) { + paramvec.push_back(llvm::PointerType::get(llvm::Type::Int8Ty)); } - - 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 { + if (retinptr) { + Logger::cout() << "returning through pointer parameter: " << *rettype << '\n'; + paramvec.push_back(rettype); } - 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)); - } - 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)); + 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); } - /*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 (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)); } + 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); + } + /*if (llvm::isa(at) || argT->ty == Tstruct || argT->ty == Tsarray) { + paramvec.push_back(llvm::PointerType::get(at)); + }*/ else { - Logger::println("in param"); + 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); } } @@ -931,10 +968,44 @@ ////////////////////////////////////////////////////////////////////////////////////////// +static llvm::Function* LLVM_DtoDeclareVaFunction(FuncDeclaration* fdecl) +{ + TypeFunction* f = (TypeFunction*)LLVM_DtoDType(fdecl->type); + const llvm::FunctionType* fty = LLVM_DtoVaFunctionType(fdecl); + llvm::Constant* fn = 0; + + if (fdecl->llvmInternal == LLVMva_start) { + fn = gIR->module->getOrInsertFunction("llvm.va_start", fty); + assert(fn); + } + else if (fdecl->llvmInternal == LLVMva_intrinsic) { + fn = gIR->module->getOrInsertFunction(fdecl->llvmInternal1, fty); + assert(fn); + } + else + assert(0); + + llvm::Function* func = llvm::cast_or_null(fn); + assert(func); + assert(func->isIntrinsic()); + fdecl->llvmValue = func; + return func; +} + +////////////////////////////////////////////////////////////////////////////////////////// + llvm::Function* LLVM_DtoDeclareFunction(FuncDeclaration* fdecl) { + if ((fdecl->llvmInternal == LLVMva_start) || (fdecl->llvmInternal == LLVMva_intrinsic)) { + return LLVM_DtoDeclareVaFunction(fdecl); + } + // mangled name - char* mangled_name = (fdecl->llvmInternal == LLVMintrinsic) ? fdecl->llvmInternal1 : fdecl->mangle(); + char* mangled_name; + if (fdecl->llvmInternal == LLVMintrinsic) + mangled_name = fdecl->llvmInternal1; + else + mangled_name = fdecl->mangle(); // unit test special handling if (fdecl->isUnitTestDeclaration()) diff -r 28e99b04a132 -r 0ccfae271c45 gen/toobj.c --- a/gen/toobj.c Wed Oct 24 01:37:34 2007 +0200 +++ b/gen/toobj.c Wed Oct 24 22:18:06 2007 +0200 @@ -646,8 +646,31 @@ return; } + Type* t = LLVM_DtoDType(type); + TypeFunction* f = (TypeFunction*)t; + + bool declareOnly = false; + if (TemplateInstance* tinst = parent->isTemplateInstance()) { + TemplateDeclaration* tempdecl = tinst->tempdecl; + if (tempdecl->llvmInternal == LLVMva_start) + { + Logger::println("magic va_start found"); + llvmInternal = LLVMva_start; + declareOnly = true; + } + else if (tempdecl->llvmInternal == LLVMva_arg) + { + Logger::println("magic va_arg found"); + llvmInternal = LLVMva_arg; + return; + } + } + llvm::Function* func = LLVM_DtoDeclareFunction(this); + if (declareOnly) + return; + if (!gIR->structs.empty() && gIR->topstruct().queueFuncs) { if (!llvmQueued) { Logger::println("queueing %s", toChars()); @@ -657,8 +680,6 @@ return; // we wait with the definition as they might invoke a virtual method and the vtable is not yet complete } - Type* t = LLVM_DtoDType(type); - TypeFunction* f = (TypeFunction*)t; assert(f->llvmType); const llvm::FunctionType* functype = llvm::cast(llvmValue->getType()->getContainedType(0)); diff -r 28e99b04a132 -r 0ccfae271c45 lphobos/build.sh --- a/lphobos/build.sh Wed Oct 24 01:37:34 2007 +0200 +++ b/lphobos/build.sh Wed Oct 24 22:18:06 2007 +0200 @@ -25,6 +25,10 @@ rebuild typeinfos.d -c -oqobj -dc=llvmdc-posix || exit 1 llvm-link -f -o=../lib/llvmdcore.bc `ls obj/typeinfo.*.bc` ../lib/llvmdcore.bc || exit 1 +echo "compiling llvm runtime support" +rebuild llvmsupport.d -c -oqobj -dc=llvmdc-posix || exit +llvm-link -f -o=../lib/llvmdcore.bc `ls obj/llvm.*.bc` ../lib/llvmdcore.bc || exit 1 + echo "optimizing" opt -f -std-compile-opts -o=../lib/llvmdcore.bc ../lib/llvmdcore.bc || exit 1 diff -r 28e99b04a132 -r 0ccfae271c45 lphobos/llvm/intrinsic.d --- a/lphobos/llvm/intrinsic.d Wed Oct 24 01:37:34 2007 +0200 +++ b/lphobos/llvm/intrinsic.d Wed Oct 24 22:18:06 2007 +0200 @@ -1,15 +1,5 @@ module llvm.intrinsic; -// variable argument handling intrinsics -pragma(LLVM_internal, "intrinsic", "llvm.va_start") - void llvm_va_start(void* args); - -pragma(LLVM_internal, "intrinsic", "llvm.va_end") - void llvm_va_end(void* args); - -pragma(LLVM_internal, "intrinsic", "llvm.va_copy") - void llvm_va_copy(void* dst, void* src); - // code generator intrinsics /* pragma(LLVM_internal, "intrinsic", "llvm.returnaddress") diff -r 28e99b04a132 -r 0ccfae271c45 lphobos/llvm/va_list.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lphobos/llvm/va_list.d Wed Oct 24 22:18:06 2007 +0200 @@ -0,0 +1,25 @@ +module llvm.va_list; + +alias void* va_list; + +/* + +version(X86) +{ + alias void* va_list; +} +else version(X86_64) +{ + struct X86_64_va_list + { + uint gp_offset; + uint fp_offset; + void* overflow_arg_area; + void* reg_save_area; + } + alias X86_64_va_list va_list; +} +else +static assert("only x86 and x86-64 support va_list"); + +*/ diff -r 28e99b04a132 -r 0ccfae271c45 lphobos/llvmsupport.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lphobos/llvmsupport.d Wed Oct 24 22:18:06 2007 +0200 @@ -0,0 +1,5 @@ +module llvmsupport; + +import +llvm.intrinsic, +llvm.va_list; diff -r 28e99b04a132 -r 0ccfae271c45 lphobos/std/c/stdarg.d --- a/lphobos/std/c/stdarg.d Wed Oct 24 01:37:34 2007 +0200 +++ b/lphobos/std/c/stdarg.d Wed Oct 24 22:18:06 2007 +0200 @@ -1,7 +1,7 @@ /** * C's <stdarg.h> - * Authors: Hauke Duden and Walter Bright, Digital Mars, www.digitalmars.com + * Authors: Hauke Duden, Walter Bright and Tomas Lindquist Olsen, Digital Mars, www.digitalmars.com * License: Public Domain * Macros: * WIKI=Phobos/StdCStdarg @@ -11,32 +11,16 @@ module std.c.stdarg; -alias void* va_list; +public import llvm.va_list; -template va_start(T) -{ - void va_start(out va_list ap, inout T parmn) - { - ap = cast(va_list)(cast(void*)&parmn + ((T.sizeof + int.sizeof - 1) & ~(int.sizeof - 1))); - } -} +pragma(LLVM_internal, "va_start") + void va_start(T)(va_list ap, ref T); -template va_arg(T) -{ - T va_arg(inout va_list ap) - { - T arg = *cast(T*)ap; - ap = cast(va_list)(cast(void*)ap + ((T.sizeof + int.sizeof - 1) & ~(int.sizeof - 1))); - return arg; - } -} +pragma(LLVM_internal, "va_arg") + T va_arg(T)(va_list ap); -void va_end(va_list ap) -{ - -} +pragma(LLVM_internal, "va_intrinsic", "llvm.va_end") + void va_end(va_list args); -void va_copy(out va_list dest, va_list src) -{ - dest = src; -} +pragma(LLVM_internal, "va_intrinsic", "llvm.va_copy") + void va_copy(va_list dst, va_list src); diff -r 28e99b04a132 -r 0ccfae271c45 test/vararg1.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/vararg1.d Wed Oct 24 22:18:06 2007 +0200 @@ -0,0 +1,21 @@ +module vararg1; + +import std.c.stdarg; + +extern(C) int add(int n, ...) +{ + va_list ap=void; + va_start(ap, n); + int r; + //for (int i=0; i