# HG changeset patch # User Frits van Bommel # Date 1236070275 -3600 # Node ID c061e0725d89c28fb9084ab083de8d96cb46b971 # Parent f756c47f310aad840e1726f00889871c773de541# Parent c519caae31810b8b9d1e6f113ff8371f384e5493 Oops, merge with mainline and actually push this time... diff -r f756c47f310a -r c061e0725d89 dmd/mtype.c --- a/dmd/mtype.c Sun Mar 01 22:59:03 2009 +0100 +++ b/dmd/mtype.c Tue Mar 03 09:51:15 2009 +0100 @@ -2659,14 +2659,9 @@ this->varargs = varargs; this->linkage = linkage; this->inuse = 0; - this->retInPtr = false; - this->usesThis = false; - this->usesNest = false; - this->structInregArg = NULL; - this->retAttrs = 0; - this->thisAttrs = 0; - this->reverseParams = false; - this->firstRealArg = 0; + + // LDC + this->fty = NULL; } Type *TypeFunction::syntaxCopy() @@ -2674,13 +2669,6 @@ Type *treturn = next ? next->syntaxCopy() : NULL; Arguments *params = Argument::arraySyntaxCopy(parameters); TypeFunction *t = new TypeFunction(params, treturn, varargs, linkage); - t->retInPtr = retInPtr; - t->usesThis = usesThis; - t->usesNest = usesNest; - t->retAttrs = retAttrs; - t->thisAttrs = thisAttrs; - t->reverseParams = reverseParams; - t->firstRealArg = firstRealArg; return t; } @@ -5316,7 +5304,6 @@ this->ident = ident; this->storageClass = storageClass; this->defaultArg = defaultArg; - this->llvmAttrs = 0; } Argument *Argument::syntaxCopy() @@ -5325,7 +5312,6 @@ type ? type->syntaxCopy() : NULL, ident, defaultArg ? defaultArg->syntaxCopy() : NULL); - a->llvmAttrs = llvmAttrs; return a; } diff -r f756c47f310a -r c061e0725d89 dmd/mtype.h --- a/dmd/mtype.h Sun Mar 01 22:59:03 2009 +0100 +++ b/dmd/mtype.h Tue Mar 03 09:51:15 2009 +0100 @@ -24,6 +24,7 @@ // llvm #include "../ir/irtype.h" namespace llvm { class Type; } +struct IrFuncTy; struct Scope; struct Identifier; @@ -436,17 +437,7 @@ unsigned totym(); // LDC - bool retInPtr; - bool usesThis; - bool usesNest; - // when the last arg is a struct and passed in EAX, this holds its real type - const llvm::Type* structInregArg; - unsigned retAttrs; - unsigned thisAttrs; // also used for nest - // parameter index in the llvm function that contains the first not-implicit arg - size_t firstRealArg; - - bool reverseParams; + IrFuncTy* fty; }; struct TypeDelegate : Type @@ -699,9 +690,6 @@ static void argsToDecoBuffer(OutBuffer *buf, Arguments *arguments); static size_t dim(Arguments *arguments); static Argument *getNth(Arguments *arguments, size_t nth, size_t *pn = NULL); - - // LDC - unsigned llvmAttrs; }; extern int PTRSIZE; diff -r f756c47f310a -r c061e0725d89 dmd/optimize.c --- a/dmd/optimize.c Sun Mar 01 22:59:03 2009 +0100 +++ b/dmd/optimize.c Tue Mar 03 09:51:15 2009 +0100 @@ -46,7 +46,7 @@ if (e1->op == TOKvar) { VarExp *ve = (VarExp *)e1; VarDeclaration *v = ve->var->isVarDeclaration(); - if (v && v->isConst() && v->init) + if (v && v->isConst() && v->init && !v->init->isStructInitializer()) { Expression *ei = v->init->toExpression(); if (ei && ei->type) e1 = ei; diff -r f756c47f310a -r c061e0725d89 gen/abi.cpp --- a/gen/abi.cpp Sun Mar 01 22:59:03 2009 +0100 +++ b/gen/abi.cpp Tue Mar 03 09:51:15 2009 +0100 @@ -1,5 +1,7 @@ #include "gen/llvm.h" +#include + #include "mars.h" #include "gen/irstate.h" @@ -8,59 +10,7 @@ #include "gen/abi.h" #include "gen/logger.h" -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -///////////////////// baseclass //////////////////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - -// FIXME: Would be nice to come up a better and faster way to do this, right -// now I'm more worried about actually making this abstraction work at all ... -// It's definitely way overkill with the amount of return value rewrites we -// have right now, but I expect this to change with proper x86-64 abi support - -TargetABI::TargetABI() -{ -} - -llvm::Value* TargetABI::getRet(TypeFunction* tf, llvm::Value* io) -{ - if (ABIRetRewrite* r = findRetRewrite(tf)) - { - return r->get(io); - } - return io; -} - -llvm::Value* TargetABI::putRet(TypeFunction* tf, llvm::Value* io) -{ - if (ABIRetRewrite* r = findRetRewrite(tf)) - { - return r->put(io); - } - return io; -} - -const llvm::Type* TargetABI::getRetType(TypeFunction* tf, const llvm::Type* t) -{ - if (ABIRetRewrite* r = findRetRewrite(tf)) - { - return r->type(t); - } - return t; -} - -ABIRetRewrite * TargetABI::findRetRewrite(TypeFunction * tf) -{ - size_t n = retOps.size(); - if (n) - for (size_t i = 0; i < n; i++) - { - if (retOps[i]->test(tf)) - return retOps[i]; - } - return NULL; -} +#include "ir/irfunction.h" ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -69,33 +19,28 @@ ////////////////////////////////////////////////////////////////////////////// // simply swap of real/imag parts for proper x87 complex abi -struct X87_complex_swap : ABIRetRewrite +struct X87_complex_swap : ABIRewrite { - LLValue* get(LLValue* v) + LLValue* get(Type*, LLValue* v) { return DtoAggrPairSwap(v); } - LLValue* put(LLValue* v) + LLValue* put(Type*, LLValue* v) { return DtoAggrPairSwap(v); } - const LLType* type(const LLType* t) + const LLType* type(Type*, const LLType* t) { return t; } - bool test(TypeFunction* tf) - { - // extern(D) && is(T:creal) - return (tf->linkage == LINKd && tf->next->toBasetype()->iscomplex()); - } }; ////////////////////////////////////////////////////////////////////////////// -struct X86_cfloat_rewrite : ABIRetRewrite +struct X86_cfloat_rewrite : ABIRewrite { // i64 -> {float,float} - LLValue* get(LLValue* in) + LLValue* get(Type*, LLValue* in) { // extract real part LLValue* rpart = gIR->ir->CreateTrunc(in, LLType::Int32Ty); @@ -111,7 +56,7 @@ } // {float,float} -> i64 - LLValue* put(LLValue* v) + LLValue* put(Type*, LLValue* v) { // extract real LLValue* r = gIR->ir->CreateExtractValue(v, 0); @@ -134,16 +79,41 @@ } // {float,float} -> i64 - const LLType* type(const LLType* t) + const LLType* type(Type*, const LLType* t) { return LLType::Int64Ty; } +}; - // test if rewrite applies to function - bool test(TypeFunction* tf) +////////////////////////////////////////////////////////////////////////////// + +// FIXME: try into eliminating the alloca or if at least check +// if it gets optimized away + +// convert byval struct +// when +struct X86_struct_to_register : ABIRewrite +{ + // int -> struct + LLValue* get(Type* dty, LLValue* v) { - return (tf->linkage != LINKd) - && (tf->next->toBasetype() == Type::tcomplex32); + Logger::println("rewriting int -> struct"); + LLValue* mem = DtoAlloca(DtoType(dty), ".int_to_struct"); + DtoStore(v, DtoBitCast(mem, getPtrToType(v->getType()))); + return DtoLoad(mem); + } + // struct -> int + LLValue* put(Type* dty, LLValue* v) + { + Logger::println("rewriting struct -> int"); + LLValue* mem = DtoAlloca(v->getType(), ".struct_to_int"); + DtoStore(v, mem); + DtoLoad(DtoBitCast(mem, getPtrToType(type(dty, v->getType())))); + } + const LLType* type(Type*, const LLType* t) + { + size_t sz = getTypePaddedSize(t)*8; + return LLIntegerType::get(sz); } }; @@ -151,11 +121,9 @@ struct X86TargetABI : TargetABI { - X86TargetABI() - { - retOps.push_back(new X87_complex_swap); - retOps.push_back(new X86_cfloat_rewrite); - } + X87_complex_swap swapComplex; + X86_cfloat_rewrite cfloatToInt; + X86_struct_to_register structToReg; bool returnInArg(TypeFunction* tf) { @@ -168,6 +136,98 @@ else return (rt->ty == Tstruct || rt->ty == Tcomplex64 || rt->ty == Tcomplex80); } + + bool passByVal(Type* t) + { + return t->toBasetype()->ty == Tstruct; + } + + void rewriteFunctionType(TypeFunction* tf) + { + IrFuncTy* fty = tf->fty; + Type* rt = fty->ret->type->toBasetype(); + + // extern(D) + if (tf->linkage == LINKd) + { + // RETURN VALUE + + // complex {re,im} -> {im,re} + if (rt->iscomplex()) + { + fty->ret->rewrite = &swapComplex; + } + + // IMPLICIT PARAMETERS + + // mark this/nested params inreg + if (fty->arg_this) + { + fty->arg_this->attrs = llvm::Attribute::InReg; + } + else if (fty->arg_nest) + { + fty->arg_nest->attrs = llvm::Attribute::InReg; + } + // otherwise try to mark the last param inreg + else if (!fty->arg_sret && !fty->args.empty()) + { + // The last parameter is passed in EAX rather than being pushed on the stack if the following conditions are met: + // * It fits in EAX. + // * It is not a 3 byte struct. + // * It is not a floating point type. + + IrFuncTyArg* last = fty->args.back(); + Type* lastTy = last->type->toBasetype(); + unsigned sz = lastTy->size(); + + if (last->byref && !last->isByVal()) + { + last->attrs = llvm::Attribute::InReg; + } + else if (!lastTy->isfloating() && (sz == 1 || sz == 2 || sz == 4)) // right? + { + // rewrite the struct into an integer to make inreg work + if (lastTy->ty == Tstruct) + { + last->rewrite = &structToReg; + last->ltype = structToReg.type(last->type, last->ltype); + last->byref = false; + } + last->attrs = llvm::Attribute::InReg; + } + } + + // FIXME: tf->varargs == 1 need to use C calling convention and vararg mechanism to live up to the spec: + // "The caller is expected to clean the stack. _argptr is not passed, it is computed by the callee." + + // EXPLICIT PARAMETERS + + // reverse parameter order + // for non variadics + if (!fty->args.empty() && tf->varargs != 1) + { + fty->reverseParams = true; + } + } + + // extern(C) and all others + else + { + // RETURN VALUE + + // cfloat -> i64 + if (tf->next->toBasetype() == Type::tcomplex32) + { + fty->ret->rewrite = &cfloatToInt; + fty->ret->ltype = LLType::Int64Ty; + } + + // IMPLICIT PARAMETERS + + // EXPLICIT PARAMETERS + } + } }; ////////////////////////////////////////////////////////////////////////////// @@ -176,10 +236,10 @@ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// -struct X86_64_cfloat_rewrite : ABIRetRewrite +struct X86_64_cfloat_rewrite : ABIRewrite { // {double} -> {float,float} - LLValue* get(LLValue* in) + LLValue* get(Type*, LLValue* in) { // extract double LLValue* v = gIR->ir->CreateExtractValue(in, 0); @@ -200,7 +260,7 @@ } // {float,float} -> {double} - LLValue* put(LLValue* v) + LLValue* put(Type*, LLValue* v) { // extract real LLValue* r = gIR->ir->CreateExtractValue(v, 0); @@ -231,33 +291,41 @@ } // {float,float} -> {double} - const LLType* type(const LLType* t) + const LLType* type(Type*, const LLType* t) { return LLStructType::get(LLType::DoubleTy, NULL); } - - // test if rewrite applies to function - bool test(TypeFunction* tf) - { - return (tf->linkage != LINKd) - && (tf->next->toBasetype() == Type::tcomplex32); - } }; ////////////////////////////////////////////////////////////////////////////// struct X86_64TargetABI : TargetABI { - X86_64TargetABI() - { - retOps.push_back(new X86_64_cfloat_rewrite); - } + X86_64_cfloat_rewrite cfloat_rewrite; bool returnInArg(TypeFunction* tf) { Type* rt = tf->next->toBasetype(); return (rt->ty == Tstruct); } + + bool passByVal(Type* t) + { + return t->toBasetype()->ty == Tstruct; + } + + void rewriteFunctionType(TypeFunction* tf) + { + IrFuncTy* fty = tf->fty; + Type* rt = fty->ret->type->toBasetype(); + + // rewrite cfloat return for !extern(D) + if (tf->linkage != LINKd && rt == Type::tcomplex32) + { + fty->ret->rewrite = &cfloat_rewrite; + fty->ret->ltype = cfloat_rewrite.type(fty->ret->type, fty->ret->ltype); + } + } }; ////////////////////////////////////////////////////////////////////////////// @@ -269,15 +337,19 @@ // Some reasonable defaults for when we don't know what ABI to use. struct UnknownTargetABI : TargetABI { - UnknownTargetABI() + bool returnInArg(TypeFunction* tf) { - // Don't push anything into retOps, assume defaults will be fine. + return (tf->next->toBasetype()->ty == Tstruct); } - bool returnInArg(TypeFunction* tf) + bool passByVal(Type* t) { - Type* rt = tf->next->toBasetype(); - return (rt->ty == Tstruct); + return t->toBasetype()->ty == Tstruct; + } + + void rewriteFunctionType(TypeFunction* t) + { + // why? } }; diff -r f756c47f310a -r c061e0725d89 gen/abi.h --- a/gen/abi.h Sun Mar 01 22:59:03 2009 +0100 +++ b/gen/abi.h Tue Mar 03 09:51:15 2009 +0100 @@ -4,6 +4,7 @@ #include struct Type; +struct IrFuncTyArg; namespace llvm { class Type; @@ -11,38 +12,27 @@ } // return rewrite rule -struct ABIRetRewrite +struct ABIRewrite { // get original value from rewritten one - virtual LLValue* get(LLValue* v) = 0; + virtual LLValue* get(Type* dty, LLValue* v) = 0; // rewrite original value - virtual LLValue* put(LLValue* v) = 0; + virtual LLValue* put(Type* dty, LLValue* v) = 0; // returns target type of this rewrite - virtual const LLType* type(const LLType* t) = 0; - - // test if rewrite applies - virtual bool test(TypeFunction* tf) = 0; + virtual const LLType* type(Type* dty, const LLType* t) = 0; }; - // interface called by codegen struct TargetABI { static TargetABI* getTarget(); - TargetABI(); - - const llvm::Type* getRetType(TypeFunction* tf, const llvm::Type* t); - llvm::Value* getRet(TypeFunction* tf, llvm::Value* v); - llvm::Value* putRet(TypeFunction* tf, llvm::Value* v); + virtual bool returnInArg(TypeFunction* tf) = 0; + virtual bool passByVal(Type* t) = 0; - virtual bool returnInArg(TypeFunction* t) = 0; - -protected: - std::vector retOps; - ABIRetRewrite* findRetRewrite(TypeFunction* tf); + virtual void rewriteFunctionType(TypeFunction* t) = 0; }; #endif diff -r f756c47f310a -r c061e0725d89 gen/functions.cpp --- a/gen/functions.cpp Sun Mar 01 22:59:03 2009 +0100 +++ b/gen/functions.cpp Tue Mar 03 09:51:15 2009 +0100 @@ -22,136 +22,114 @@ #include "gen/dvalue.h" #include "gen/abi.h" -#include - -const llvm::FunctionType* DtoFunctionType(Type* type, const LLType* thistype, const LLType* nesttype, bool ismain) +const llvm::FunctionType* DtoFunctionType(Type* type, Type* thistype, Type* nesttype, bool ismain) { + // sanity check assert(type->ty == Tfunction); TypeFunction* f = (TypeFunction*)type; + // already built ? if (type->ir.type != NULL) { + assert(f->fty != NULL); return llvm::cast(type->ir.type->get()); } - bool dVararg = false; - bool arrayVararg = false; - if (f->linkage == LINKd) + // create new ir funcTy + assert(f->fty == NULL); + f->fty = new IrFuncTy(); + + // llvm idx counter + size_t lidx = 0; + + // main needs a little special handling + if (ismain) + { + f->fty->ret = new IrFuncTyArg(Type::tint32, false); + } + // sane return value + else { - if (f->varargs == 1) - dVararg = true; - else if (f->varargs == 2) - arrayVararg = true; + Type* rt = f->next; + unsigned a = 0; + // sret return + if (gABI->returnInArg(f)) + { + f->fty->arg_sret = new IrFuncTyArg(rt, true, llvm::Attribute::StructRet); + rt = Type::tvoid; + lidx++; + } + // sext/zext return + else if (unsigned se = DtoShouldExtend(rt)) + { + a = se; + } + f->fty->ret = new IrFuncTyArg(rt, false, a); + } + lidx++; + + // member functions + if (thistype) + { + bool toref = (thistype->toBasetype()->ty == Tstruct); + f->fty->arg_this = new IrFuncTyArg(thistype, toref); + lidx++; } - // return value type - const LLType* rettype; - const LLType* actualRettype; - Type* rt = f->next; - bool retinptr = false; - bool usesthis = false; - bool usesnest = false; + // and nested functions + else if (nesttype) + { + f->fty->arg_nest = new IrFuncTyArg(nesttype, false); + lidx++; + } - // parameter types - std::vector paramvec; - - // special case main - if (ismain) + // vararg functions are special too + if (f->varargs) { - rettype = LLType::Int32Ty; - actualRettype = rettype; - if (Argument::dim(f->parameters) == 0) + if (f->linkage == LINKd) { - const LLType* arrTy = DtoArrayType(LLType::Int8Ty); - const LLType* arrArrTy = DtoArrayType(arrTy); - paramvec.push_back(arrArrTy); + // d style with hidden args + // 2 (array) is handled by the frontend + if (f->varargs == 1) + { + // _arguments + f->fty->arg_arguments = new IrFuncTyArg(Type::typeinfo->type->arrayOf(), false); + lidx++; + // _argptr + f->fty->arg_argptr = new IrFuncTyArg(Type::tvoid->pointerTo(), false); + lidx++; + } } - } - // default handling - else - { - assert(rt); - if (f->linkage == LINKintrinsic) + else if (f->linkage == LINKc) { - // Intrinsics don't care about ABI - Logger::cout() << "Intrinsic returning " << rt->toChars() << '\n'; - actualRettype = rettype = DtoType(rt); - Logger::cout() << " (LLVM type: " << *rettype << ")\n"; + f->fty->c_vararg = true; } else { - if (gABI->returnInArg(f)) - { - rettype = getPtrToType(DtoType(rt)); - actualRettype = LLType::VoidTy; - f->retInPtr = retinptr = true; - } - else - { - rettype = DtoType(rt); - // do abi specific transformations - actualRettype = gABI->getRetType(f, rettype); - } - - // FIXME: should probably be part of the abi - if (unsigned ea = DtoShouldExtend(rt)) - { - f->retAttrs |= ea; - } + type->error(0, "invalid linkage for variadic function"); + fatal(); } } - // build up parameter list - if (retinptr) { - //Logger::cout() << "returning through pointer parameter: " << *rettype << '\n'; - paramvec.push_back(rettype); - } - - // this/context param - if (thistype) { - paramvec.push_back(thistype); - usesthis = true; - } - else if (nesttype) { - paramvec.push_back(nesttype); - usesnest = true; - } - - // dstyle vararg - if (dVararg) { - paramvec.push_back(DtoType(Type::typeinfo->type->arrayOf())); // _arguments - paramvec.push_back(getVoidPtrType()); // _argptr - } - - // now that all implicit args are done, store the start of the real args - f->firstRealArg = paramvec.size(); + // if this _Dmain() doesn't have an argument, we force it to have one + int nargs = Argument::dim(f->parameters); - // number of formal params - size_t n = Argument::dim(f->parameters); - -#if X86_REVERSE_PARAMS - // on x86 we need to reverse the formal params in some cases to match the ABI - if (global.params.cpu == ARCHx86) + if (ismain && nargs == 0) + { + Type* mainargs = Type::tchar->arrayOf()->arrayOf(); + f->fty->args.push_back(new IrFuncTyArg(mainargs, false)); + lidx++; + } + // add explicit parameters + else for (int i = 0; i < nargs; i++) { - // more than one formal arg, - // extern(D) linkage - // not a D-style vararg - if (n > 1 && f->linkage == LINKd && !dVararg) - { - f->reverseParams = true; - } - } -#endif // X86_REVERSE_PARAMS + // get argument + Argument* arg = Argument::getNth(f->parameters, i); + // reference semantics? ref, out and static arrays are + bool byref = (arg->storageClass & (STCref|STCout)) || (arg->type->toBasetype()->ty == Tsarray); - for (int i=0; i < n; ++i) { - Argument* arg = Argument::getNth(f->parameters, i); - // ensure scalar - Type* argT = arg->type->toBasetype(); - assert(argT); - - bool refOrOut = ((arg->storageClass & STCref) || (arg->storageClass & STCout)); - - const LLType* at = DtoType(argT); + Type* argtype = arg->type; + unsigned a = 0; // handle lazy args if (arg->storageClass & STClazy) @@ -159,113 +137,51 @@ Logger::println("lazy param"); TypeFunction *ltf = new TypeFunction(NULL, arg->type, 0, LINKd); TypeDelegate *ltd = new TypeDelegate(ltf); - at = DtoType(ltd); - paramvec.push_back(at); + argtype = ltd; } - // opaque types need special handling - else if (llvm::isa(at)) { - Logger::println("opaque param"); - assert(argT->ty == Tstruct || argT->ty == Tclass); - paramvec.push_back(getPtrToType(at)); + // byval + else if (gABI->passByVal(argtype)) + { + if (!byref) a |= llvm::Attribute::ByVal; + byref = true; } - // structs are passed as a reference, but by value - else if (argT->ty == Tstruct) { - Logger::println("struct param"); - if (!refOrOut) - arg->llvmAttrs |= llvm::Attribute::ByVal; - paramvec.push_back(getPtrToType(at)); + // sext/zext + else if (!byref) + { + a |= DtoShouldExtend(argtype); } - // static arrays are passed directly by reference - else if (argT->ty == Tsarray) - { - Logger::println("static array param"); - at = getPtrToType(at); - paramvec.push_back(at); - } - // firstclass ' ref/out ' parameter - else if (refOrOut) { - Logger::println("ref/out param"); - at = getPtrToType(at); - paramvec.push_back(at); - } - // firstclass ' in ' parameter - else { - Logger::println("in param"); - if (unsigned ea = DtoShouldExtend(argT)) - arg->llvmAttrs |= ea; - paramvec.push_back(at); - } + + f->fty->args.push_back(new IrFuncTyArg(argtype, byref, a)); + lidx++; + } + + // let the abi rewrite the types as necesary + gABI->rewriteFunctionType(f); + + // build the function type + std::vector argtypes; + argtypes.reserve(lidx); + + if (f->fty->arg_sret) argtypes.push_back(f->fty->arg_sret->ltype); + if (f->fty->arg_this) argtypes.push_back(f->fty->arg_this->ltype); + if (f->fty->arg_nest) argtypes.push_back(f->fty->arg_nest->ltype); + if (f->fty->arg_arguments) argtypes.push_back(f->fty->arg_arguments->ltype); + if (f->fty->arg_argptr) argtypes.push_back(f->fty->arg_argptr->ltype); + + size_t beg = argtypes.size(); + size_t nargs2 = f->fty->args.size(); + for (size_t i = 0; i < nargs2; i++) + { + argtypes.push_back(f->fty->args[i]->ltype); } // reverse params? - if (f->reverseParams) + if (f->fty->reverseParams && f->parameters->dim > 1) { - std::reverse(paramvec.begin() + f->firstRealArg, paramvec.end()); + std::reverse(argtypes.begin() + beg, argtypes.end()); } -#if X86_PASS_IN_EAX - // pass first param in EAX if it fits, is not floating point and is not a 3 byte struct. - // ONLY extern(D) functions ! - if ((n > 0 || usesthis || usesnest) && f->linkage == LINKd) - { - // FIXME: Only x86 right now ... - if (global.params.cpu == ARCHx86) - { - int n_inreg = f->reverseParams ? n - 1 : 0; - Argument* arg = Argument::getNth(f->parameters, n_inreg); - - // if there is a implicit context parameter, pass it in EAX - if (usesthis || usesnest) - { - f->thisAttrs |= llvm::Attribute::InReg; - assert((!arg || (arg->llvmAttrs & llvm::Attribute::InReg) == 0) && "can't have two inreg args!"); - } - // otherwise check the first formal parameter - else - { - Type* t = arg->type->toBasetype(); - - // 32bit ints, pointers, classes, static arrays, AAs, ref and out params, - // and structs with size <= 4 and != 3 - // are candidate for being passed in EAX - if ( - (arg->storageClass & (STCref|STCout)) - || - ((arg->storageClass & STCin) && - ((t->isscalar() && !t->isfloating()) || - t->ty == Tclass || t->ty == Tsarray || t->ty == Taarray || - (t->ty == Tstruct && t->size() != 3) - ) && (t->size() <= PTRSIZE)) - ) - { - arg->llvmAttrs |= llvm::Attribute::InReg; - assert((f->thisAttrs & llvm::Attribute::InReg) == 0 && "can't have two inreg args!"); - - // structs need to go from {...}* byval to i8/i16/i32 inreg - if ((arg->storageClass & STCin) && t->ty == Tstruct) - { - int n_param = f->reverseParams ? f->firstRealArg + n - 1 - n_inreg : f->firstRealArg + n_inreg; - assert(isaPointer(paramvec[n_param]) && (arg->llvmAttrs & llvm::Attribute::ByVal) - && "struct parameter expected to be {...}* byval before inreg is applied"); - f->structInregArg = paramvec[n_param]->getContainedType(0); - paramvec[n_param] = LLIntegerType::get(8*t->size()); - arg->llvmAttrs &= ~llvm::Attribute::ByVal; - } - } - } - } - } -#endif // X86_PASS_IN_EAX - - // construct function type - bool isvararg = !(dVararg || arrayVararg) && f->varargs; - llvm::FunctionType* functype = llvm::FunctionType::get(actualRettype, paramvec, isvararg); - - // done - f->retInPtr = retinptr; - f->usesThis = usesthis; - f->usesNest = usesnest; - + llvm::FunctionType* functype = llvm::FunctionType::get(f->fty->ret->ltype, argtypes, f->fty->c_vararg); f->ir.type = new llvm::PATypeHolder(functype); return functype; @@ -283,10 +199,19 @@ TypeFunction* f = (TypeFunction*)fdecl->type; const llvm::FunctionType* fty = 0; + // create new ir funcTy + assert(f->fty == NULL); + f->fty = new IrFuncTy(); + f->fty->ret = new IrFuncTyArg(Type::tvoid, false); + + f->fty->args.push_back(new IrFuncTyArg(Type::tvoid->pointerTo(), false)); + if (fdecl->llvmInternal == LLVMva_start) fty = GET_INTRINSIC_DECL(vastart)->getFunctionType(); - else if (fdecl->llvmInternal == LLVMva_copy) + else if (fdecl->llvmInternal == LLVMva_copy) { fty = GET_INTRINSIC_DECL(vacopy)->getFunctionType(); + f->fty->args.push_back(new IrFuncTyArg(Type::tvoid->pointerTo(), false)); + } else if (fdecl->llvmInternal == LLVMva_end) fty = GET_INTRINSIC_DECL(vaend)->getFunctionType(); assert(fty); @@ -307,13 +232,13 @@ if (fdecl->type->ir.type != 0) return llvm::cast(fdecl->type->ir.type->get()); - const LLType* thisty = 0; - const LLType* nestty = 0; + Type *dthis=0, *dnest=0; if (fdecl->needThis()) { if (AggregateDeclaration* ad = fdecl->isMember2()) { Logger::println("isMember = this is: %s", ad->type->toChars()); - thisty = DtoType(ad->type); + dthis = ad->type; + const LLType* thisty = DtoType(dthis); //Logger::cout() << "this llvm type: " << *thisty << '\n'; if (isaStruct(thisty) || (!gIR->structs.empty() && thisty == gIR->topstruct()->type->ir.type->get())) thisty = getPtrToType(thisty); @@ -324,10 +249,10 @@ } } else if (fdecl->isNested()) { - nestty = getPtrToType(LLType::Int8Ty); + dnest = Type::tvoid->pointerTo(); } - const llvm::FunctionType* functype = DtoFunctionType(fdecl->type, thisty, nestty, fdecl->isMain()); + const llvm::FunctionType* functype = DtoFunctionType(fdecl->type, dthis, dnest, fdecl->isMain()); return functype; } @@ -414,46 +339,35 @@ static void set_param_attrs(TypeFunction* f, llvm::Function* func, FuncDeclaration* fdecl) { - int llidx = 0; - if (f->retInPtr) ++llidx; - if (f->usesThis) ++llidx; - else if (f->usesNest) ++llidx; - if (f->linkage == LINKd && f->varargs == 1) - llidx += 2; - int funcNumArgs = func->getArgumentList().size(); LLSmallVector attrs; llvm::AttributeWithIndex PAWI; - // set return value attrs if any - if (f->retAttrs) - { - PAWI.Index = 0; - PAWI.Attrs = f->retAttrs; - attrs.push_back(PAWI); + int idx = 0; + + // handle implicit args + #define ADD_PA(X) \ + if (f->fty->X) { \ + if (f->fty->X->attrs) { \ + PAWI.Index = idx; \ + PAWI.Attrs = f->fty->X->attrs; \ + attrs.push_back(PAWI); \ + } \ + idx++; \ } - // set sret param - if (f->retInPtr) - { - PAWI.Index = 1; - PAWI.Attrs = llvm::Attribute::StructRet; - attrs.push_back(PAWI); - } + ADD_PA(ret) + ADD_PA(arg_sret) + ADD_PA(arg_this) + ADD_PA(arg_nest) + ADD_PA(arg_arguments) + ADD_PA(arg_argptr) - // set this/nest param attrs - if (f->thisAttrs) - { - PAWI.Index = f->retInPtr ? 2 : 1; - PAWI.Attrs = f->thisAttrs; - attrs.push_back(PAWI); - } + #undef ADD_PA // set attrs on the rest of the arguments size_t n = Argument::dim(f->parameters); - assert(funcNumArgs >= n); // main might mismatch, for the implicit char[][] arg - LLSmallVector attrptr(n, 0); for (size_t k = 0; k < n; ++k) @@ -461,11 +375,11 @@ Argument* fnarg = Argument::getNth(f->parameters, k); assert(fnarg); - attrptr[k] = fnarg->llvmAttrs; + attrptr[k] = f->fty->args[k]->attrs; } // reverse params? - if (f->reverseParams) + if (f->fty->reverseParams) { std::reverse(attrptr.begin(), attrptr.end()); } @@ -475,7 +389,7 @@ { if (attrptr[i]) { - PAWI.Index = llidx+i+1; + PAWI.Index = idx+i; PAWI.Attrs = attrptr[i]; attrs.push_back(PAWI); } @@ -575,26 +489,26 @@ // name parameters llvm::Function::arg_iterator iarg = func->arg_begin(); - if (f->retInPtr) { + if (f->fty->arg_sret) { iarg->setName(".sret_arg"); fdecl->ir.irFunc->retArg = iarg; ++iarg; } - if (f->usesThis) { + if (f->fty->arg_this) { iarg->setName(".this_arg"); fdecl->ir.irFunc->thisArg = iarg; assert(fdecl->ir.irFunc->thisArg); ++iarg; } - else if (f->usesNest) { + else if (f->fty->arg_nest) { iarg->setName(".nest_arg"); fdecl->ir.irFunc->nestArg = iarg; assert(fdecl->ir.irFunc->nestArg); ++iarg; } - if (f->linkage == LINKd && f->varargs == 1) { + if (f->fty->arg_argptr) { iarg->setName("._arguments"); fdecl->ir.irFunc->_arguments = iarg; ++iarg; @@ -610,7 +524,7 @@ if (fdecl->parameters && fdecl->parameters->dim > k) { Dsymbol* argsym; - if (f->reverseParams) + if (f->fty->reverseParams) argsym = (Dsymbol*)fdecl->parameters->data[fdecl->parameters->dim-k-1]; else argsym = (Dsymbol*)fdecl->parameters->data[k]; @@ -648,6 +562,8 @@ ////////////////////////////////////////////////////////////////////////////////////////// +// FIXME: this isn't too pretty! + void DtoDefineFunction(FuncDeclaration* fd) { if (fd->ir.defined) return; @@ -727,7 +643,7 @@ } // give the 'this' argument storage and debug info - if (f->usesThis) + if (f->fty->arg_this) { LLValue* thisvar = irfunction->thisArg; assert(thisvar); @@ -757,7 +673,8 @@ // and debug info if (fd->parameters) { - size_t n = fd->parameters->dim; + size_t n = f->fty->args.size(); + assert(n == fd->parameters->dim); for (int i=0; i < n; ++i) { Dsymbol* argsym = (Dsymbol*)fd->parameters->data[i]; @@ -767,20 +684,8 @@ IrLocal* irloc = vd->ir.irLocal; assert(irloc); - // if it's inreg struct arg, allocate storage - if (f->structInregArg && i == (f->reverseParams ? n - 1 : 0)) - { - int n_param = f->reverseParams ? f->firstRealArg + n - 1 - i : f->firstRealArg + i; - const LLType* paramty = functype->getParamType(n_param); - assert(!f->usesNest && !f->usesThis && - llvm::isa(paramty) && isaStruct(f->structInregArg) - && "Preconditions for inreg struct arg not met!"); - - LLValue* mem = DtoAlloca(f->structInregArg, "inregstructarg"); - - DtoStore(irloc->value, DtoBitCast(mem, getPtrToType(paramty))); - irloc->value = mem; - } + // let the abi transform the argument back first + LLValue* argvalue = f->fty->getParam(vd->type, i, irloc->value); #if DMDV2 if (vd->nestedrefs.dim) @@ -794,9 +699,9 @@ bool refout = vd->storage_class & (STCref | STCout); bool lazy = vd->storage_class & STClazy; - if (!refout && (!DtoIsPassedByRef(vd->type) || lazy)) + if (!refout && (!f->fty->args[i]->byref || lazy)) { - LLValue* a = irloc->value; + LLValue* a = argvalue; LLValue* v = DtoAlloca(a->getType(), vd->ident->toChars()); DtoStore(a,v); irloc->value = v; diff -r f756c47f310a -r c061e0725d89 gen/functions.h --- a/gen/functions.h Sun Mar 01 22:59:03 2009 +0100 +++ b/gen/functions.h Tue Mar 03 09:51:15 2009 +0100 @@ -1,7 +1,7 @@ #ifndef LDC_GEN_FUNCTIONS_H #define LDC_GEN_FUNCTIONS_H -const llvm::FunctionType* DtoFunctionType(Type* t, const LLType* thistype, const LLType* nesttype, bool ismain = false); +const llvm::FunctionType* DtoFunctionType(Type* t, Type* thistype, Type* nesttype, bool ismain = false); const llvm::FunctionType* DtoFunctionType(FuncDeclaration* fdecl); const llvm::FunctionType* DtoBaseFunctionType(FuncDeclaration* fdecl); diff -r f756c47f310a -r c061e0725d89 gen/statements.cpp --- a/gen/statements.cpp Sun Mar 01 22:59:03 2009 +0100 +++ b/gen/statements.cpp Tue Mar 03 09:51:15 2009 +0100 @@ -63,7 +63,6 @@ { // sanity check IrFunction* f = p->func(); - assert(f->type->retInPtr); assert(f->decl->ir.irFunc->retArg); // emit dbg line @@ -94,7 +93,7 @@ delete e; // do abi specific transformations on the return value - v = gABI->putRet(p->func()->type, v); + v = p->func()->type->fty->putRet(exp->type, v); if (Logger::enabled()) Logger::cout() << "return value is '" <<*v << "'\n"; @@ -111,7 +110,7 @@ v = llvm::Constant::getNullValue(p->mainFunc->getReturnType()); else v = gIR->ir->CreateBitCast(v, p->topfunc()->getReturnType(), "tmp"); - + if (Logger::enabled()) Logger::cout() << "return value after cast: " << *v << '\n'; } diff -r f756c47f310a -r c061e0725d89 gen/tocall.cpp --- a/gen/tocall.cpp Sun Mar 01 22:59:03 2009 +0100 +++ b/gen/tocall.cpp Tue Mar 03 09:51:15 2009 +0100 @@ -201,11 +201,11 @@ DValue* argval = DtoArgument(fnarg, (Expression*)arguments->data[i]); args.push_back(argval->getRVal()); - if (fnarg->llvmAttrs) + if (tf->fty->args[i]->attrs) { llvm::AttributeWithIndex Attr; Attr.Index = argidx; - Attr.Attrs = fnarg->llvmAttrs; + Attr.Attrs = tf->fty->args[i]->attrs; attrs.push_back(Attr); } @@ -213,6 +213,7 @@ } } +// FIXME: this function is a mess ! DValue* DtoCallFunction(Loc& loc, Type* resulttype, DValue* fnval, Expressions* arguments) { @@ -233,10 +234,10 @@ TypeFunction* tf = DtoTypeFunction(fnval); // misc - bool retinptr = tf->retInPtr; - bool thiscall = tf->usesThis; + bool retinptr = tf->fty->arg_sret; + bool thiscall = tf->fty->arg_this; bool delegatecall = (calleeType->toBasetype()->ty == Tdelegate); - bool nestedcall = tf->usesNest; + bool nestedcall = tf->fty->arg_nest; bool dvarargs = (tf->linkage == LINKd && tf->varargs == 1); unsigned callconv = DtoCallingConv(loc, tf->linkage); @@ -261,15 +262,16 @@ llvm::AttributeWithIndex Attr; // return attrs - if (tf->retAttrs) + if (tf->fty->ret->attrs) { Attr.Index = 0; - Attr.Attrs = tf->retAttrs; + Attr.Attrs = tf->fty->ret->attrs; attrs.push_back(Attr); } // handle implicit arguments std::vector args; + args.reserve(tf->fty->args.size()); // return in hidden ptr is first if (retinptr) @@ -325,10 +327,16 @@ } // add attributes for context argument - if (tf->thisAttrs) + if (tf->fty->arg_this && tf->fty->arg_this->attrs) { Attr.Index = retinptr ? 2 : 1; - Attr.Attrs = tf->thisAttrs; + Attr.Attrs = tf->fty->arg_this->attrs; + attrs.push_back(Attr); + } + else if (tf->fty->arg_nest && tf->fty->arg_nest->attrs) + { + Attr.Index = retinptr ? 2 : 1; + Attr.Attrs = tf->fty->arg_nest->attrs; attrs.push_back(Attr); } } @@ -374,20 +382,15 @@ DValue* argval = DtoArgument(fnarg, (Expression*)arguments->data[i]); LLValue* arg = argval->getRVal(); - int j = tf->reverseParams ? beg + n - i - 1 : beg + i; + int j = tf->fty->reverseParams ? beg + n - i - 1 : beg + i; - // if it's a struct inreg arg, load first to pass as first-class value - if (tf->structInregArg && i == (tf->reverseParams ? n - 1 : 0)) - { - assert((fnarg->llvmAttrs & llvm::Attribute::InReg) && isaStruct(tf->structInregArg)); - arg = DtoBitCast(arg, getPtrToType(callableTy->getParamType(j))); - arg = DtoLoad(arg); - } + // give the ABI a say + arg = tf->fty->putParam(argval->getType(), i, arg); // parameter type mismatch, this is hard to get rid of if (arg->getType() != callableTy->getParamType(j)) { - #if 0 + #if 1 if (Logger::enabled()) { Logger::cout() << "arg: " << *arg << '\n'; @@ -398,16 +401,16 @@ } // param attrs - attrptr[i] = fnarg->llvmAttrs; + attrptr[i] = tf->fty->args[i]->attrs; ++argiter; args.push_back(arg); } // reverse the relevant params as well as the param attrs - if (tf->reverseParams) + if (tf->fty->reverseParams) { - std::reverse(args.begin() + tf->firstRealArg, args.end()); + std::reverse(args.begin() + beg, args.end()); std::reverse(attrptr.begin(), attrptr.end()); } @@ -475,10 +478,10 @@ retllval = mem; } } - else + else if (!retinptr) { // do abi specific return value fixups - retllval = gABI->getRet(tf, retllval); + retllval = tf->fty->getRet(tf->next, retllval); } // repaint the type if necessary diff -r f756c47f310a -r c061e0725d89 gen/tollvm.cpp --- a/gen/tollvm.cpp Sun Mar 01 22:59:03 2009 +0100 +++ b/gen/tollvm.cpp Tue Mar 03 09:51:15 2009 +0100 @@ -227,7 +227,7 @@ { assert(t->ty == Tdelegate); const LLType* i8ptr = getVoidPtrType(); - const LLType* func = DtoFunctionType(t->nextOf(), NULL, i8ptr); + const LLType* func = DtoFunctionType(t->nextOf(), NULL, Type::tvoid->pointerTo()); const LLType* funcptr = getPtrToType(func); return LLStructType::get(i8ptr, funcptr, NULL); } diff -r f756c47f310a -r c061e0725d89 ir/irfunction.cpp --- a/ir/irfunction.cpp Sun Mar 01 22:59:03 2009 +0100 +++ b/ir/irfunction.cpp Tue Mar 03 09:51:15 2009 +0100 @@ -1,6 +1,7 @@ #include "gen/llvm.h" #include "gen/tollvm.h" +#include "gen/abi.h" #include "ir/irfunction.h" #include @@ -9,6 +10,55 @@ ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// +IrFuncTyArg::IrFuncTyArg(Type* t, bool bref, unsigned a) +{ + type = t; + ltype = bref ? DtoType(t->pointerTo()) : DtoType(t); + attrs = a; + byref = bref; + rewrite = NULL; +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + +llvm::Value* IrFuncTy::putRet(Type* dty, llvm::Value* val) +{ + assert(!arg_sret); + if (ret->rewrite) + return ret->rewrite->put(dty, val); + return val; +} + +llvm::Value* IrFuncTy::getRet(Type* dty, llvm::Value* val) +{ + assert(!arg_sret); + if (ret->rewrite) + return ret->rewrite->get(dty, val); + return val; +} + +llvm::Value* IrFuncTy::putParam(Type* dty, int idx, llvm::Value* val) +{ + assert(idx >= 0 && idx < args.size() && "invalid putParam"); + if (args[idx]->rewrite) + return args[idx]->rewrite->put(dty, val); + return val; +} + +llvm::Value* IrFuncTy::getParam(Type* dty, int idx, llvm::Value* val) +{ + assert(idx >= 0 && idx < args.size() && "invalid getParam"); + if (args[idx]->rewrite) + return args[idx]->rewrite->get(dty, val); + return val; +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + IrFunction::IrFunction(FuncDeclaration* fd) { decl = fd; diff -r f756c47f310a -r c061e0725d89 ir/irfunction.h --- a/ir/irfunction.h Sun Mar 01 22:59:03 2009 +0100 +++ b/ir/irfunction.h Tue Mar 03 09:51:15 2009 +0100 @@ -1,6 +1,7 @@ #ifndef LDC_IR_IRFUNCTION_H #define LDC_IR_IRFUNCTION_H +#include "gen/llvm.h" #include "ir/ir.h" #include "ir/irlandingpad.h" @@ -8,6 +9,66 @@ #include #include +struct ABIRewrite; + +// represents a function type argument +// both explicit and implicit as well as return values +struct IrFuncTyArg : IrBase +{ + Type* type; + const llvm::Type* ltype; + unsigned attrs; + bool byref; + + ABIRewrite* rewrite; + + bool isInReg() const { return (attrs & llvm::Attribute::InReg) != 0; } + bool isSRet() const { return (attrs & llvm::Attribute::StructRet) != 0; } + bool isByVal() const { return (attrs & llvm::Attribute::ByVal) != 0; } + + IrFuncTyArg(Type* t, bool byref, unsigned a = 0); +}; + +// represents a function type +struct IrFuncTy : IrBase +{ + // return value + IrFuncTyArg* ret; + + // null if not applicable + IrFuncTyArg* arg_sret; + IrFuncTyArg* arg_this; + IrFuncTyArg* arg_nest; + IrFuncTyArg* arg_arguments; + IrFuncTyArg* arg_argptr; + + // normal explicit arguments + LLSmallVector args; + + // C varargs + bool c_vararg; + + // range of normal parameters to reverse + bool reverseParams; + + IrFuncTy() + : ret(NULL), + arg_sret(NULL), + arg_this(NULL), + arg_nest(NULL), + arg_arguments(NULL), + arg_argptr(NULL), + c_vararg(false), + reverseParams(false) + {} + + llvm::Value* putRet(Type* dty, llvm::Value* val); + llvm::Value* getRet(Type* dty, llvm::Value* val); + + llvm::Value* getParam(Type* dty, int idx, llvm::Value* val); + llvm::Value* putParam(Type* dty, int idx, llvm::Value* val); +}; + // represents a function struct IrFunction : IrBase {