//===-- tocall.cpp --------------------------------------------------------===//
//
//                         LDC – the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//

#include "gen/llvm.h"

#include "mtype.h"
#include "declaration.h"
#include "id.h"

#include "gen/tollvm.h"
#include "gen/llvmhelpers.h"
#include "gen/irstate.h"
#include "gen/dvalue.h"
#include "gen/functions.h"
#include "gen/abi.h"
#include "gen/nested.h"

#include "gen/logger.h"

//////////////////////////////////////////////////////////////////////////////////////////

TypeFunction* DtoTypeFunction(DValue* fnval)
{
    Type* type = fnval->getType()->toBasetype();
    if (type->ty == Tfunction)
    {
         return static_cast<TypeFunction*>(type);
    }
    else if (type->ty == Tdelegate)
    {
        // FIXME: There is really no reason why the function type should be
        // unmerged at this stage, but the frontend still seems to produce such
        // cases; for example for the uint(uint) next type of the return type of
        // (&zero)(), leading to a crash in DtoCallFunction:
        // ---
        // void test8198() {
        //   uint delegate(uint) zero() { return null; }
        //   auto a = (&zero)()(0);
        // }
        // ---
        // Calling merge() here works around the symptoms, but does not fix the
        // root cause.

        Type* next = type->nextOf()->merge();
        assert(next->ty == Tfunction);
        return static_cast<TypeFunction*>(next);
    }

    assert(0 && "cant get TypeFunction* from non lazy/function/delegate");
    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////

llvm::CallingConv::ID DtoCallingConv(Loc loc, LINK l)
{
    if (l == LINKc || l == LINKcpp || l == LINKintrinsic)
        return llvm::CallingConv::C;
    else if (l == LINKd || l == LINKdefault)
    {
        //TODO: StdCall is not a good base on Windows due to extra name mangling
        // applied there
        if (global.params.cpu == ARCHx86 || global.params.cpu == ARCHx86_64)
            return (global.params.os != OSWindows) ? llvm::CallingConv::X86_StdCall : llvm::CallingConv::C;
        else
            return llvm::CallingConv::Fast;
    }
    // on the other hand, here, it's exactly what we want!!! TODO: right?
    // On Windows 64bit, there is only one calling convention!
    else if (l == LINKwindows)
        return global.params.cpu == ARCHx86_64 ? llvm::CallingConv::C : llvm::CallingConv::X86_StdCall;
    else if (l == LINKpascal)
        return llvm::CallingConv::X86_StdCall;
    else
    {
        error(loc, "unsupported calling convention");
        fatal();
    }
}

//////////////////////////////////////////////////////////////////////////////////////////

DValue* DtoVaArg(Loc& loc, Type* type, Expression* valistArg)
{
    DValue* expelem = valistArg->toElem(gIR);
    LLType* llt = DtoType(type);
    if (DtoIsPassedByRef(type))
        llt = getPtrToType(llt);
    // issue a warning for broken va_arg instruction.
    if (global.params.cpu != ARCHx86)
        warning(Loc(), "%s: va_arg for C variadic functions is probably broken for anything but x86", loc.toChars());
    // done
    return new DImValue(type, gIR->ir->CreateVAArg(expelem->getLVal(), llt, "tmp"));
}

//////////////////////////////////////////////////////////////////////////////////////////

LLValue* DtoCallableValue(DValue* fn)
{
    Type* type = fn->getType()->toBasetype();
    if (type->ty == Tfunction)
    {
        return fn->getRVal();
    }
    else if (type->ty == Tdelegate)
    {
        if (fn->isLVal())
        {
            LLValue* dg = fn->getLVal();
            LLValue* funcptr = DtoGEPi(dg, 0, 1);
            return DtoLoad(funcptr);
        }
        else
        {
            LLValue* dg = fn->getRVal();
            assert(isaStruct(dg));
            return gIR->ir->CreateExtractValue(dg, 1, ".funcptr");
        }
    }
    else
    {
        assert(0 && "not a callable type");
        return NULL;
    }
}

//////////////////////////////////////////////////////////////////////////////////////////

LLFunctionType* DtoExtractFunctionType(LLType* type)
{
    if (LLFunctionType* fty = isaFunction(type))
        return fty;
    else if (LLPointerType* pty = isaPointer(type))
    {
        if (LLFunctionType* fty = isaFunction(pty->getElementType()))
            return fty;
    }
    return NULL;
}

//////////////////////////////////////////////////////////////////////////////////////////

static LLValue *fixArgument(DValue *argval, TypeFunction* tf, LLType *callableArgType, size_t argIndex)
{
#if 0
    if (Logger::enabled()) {
        Logger::cout() << "Argument before ABI: " << *argval->getRVal() << '\n';
        Logger::cout() << "Argument type before ABI: " << *DtoType(argval->getType()) << '\n';
    }
#endif

    // give the ABI a say
    LLValue* arg = tf->fty.putParam(argval->getType(), argIndex, argval);

#if 0
    if (Logger::enabled()) {
        Logger::cout() << "Argument after ABI: " << *arg << '\n';
        Logger::cout() << "Argument type after ABI: " << *arg->getType() << '\n';
    }
#endif

    // Hack around LDC assuming structs and static arrays are in memory:
    // If the function wants a struct, and the argument value is a
    // pointer to a struct, load from it before passing it in.
    int ty = argval->getType()->toBasetype()->ty;
    if (isaPointer(arg) && !isaPointer(callableArgType) &&
#if DMDV2
        (ty == Tstruct || ty == Tsarray))
#else
        ty == Tstruct)
#endif
    {
        Logger::println("Loading struct type for function argument");
        arg = DtoLoad(arg);
    }

    // parameter type mismatch, this is hard to get rid of
    if (arg->getType() != callableArgType)
    {
    #if 1
        if (Logger::enabled())
        {
            Logger::cout() << "arg:     " << *arg << '\n';
            Logger::cout() << "of type: " << *arg->getType() << '\n';
            Logger::cout() << "expects: " << *callableArgType << '\n';
        }
    #endif
        if (isaStruct(arg))
            arg = DtoAggrPaint(arg, callableArgType);
        else
            arg = DtoBitCast(arg, callableArgType);
    }
    return arg;
}

//////////////////////////////////////////////////////////////////////////////////////////

void DtoBuildDVarArgList(std::vector<LLValue*>& args,
                         std::vector<llvm::AttributeWithIndex>& attrs,
                         TypeFunction* tf, Expressions* arguments,
                         size_t argidx,
                         LLFunctionType* callableTy)
{
    Logger::println("doing d-style variadic arguments");
    LOG_SCOPE

    std::vector<LLType*> vtypes;

    // number of non variadic args
    int begin = Parameter::dim(tf->parameters);
    Logger::println("num non vararg params = %d", begin);

    // get n args in arguments list
    size_t n_arguments = arguments ? arguments->dim : 0;

    // build struct with argument types (non variadic args)
    for (size_t i=begin; i<n_arguments; i++)
    {
        Expression* argexp = static_cast<Expression*>(arguments->data[i]);
        assert(argexp->type->ty != Ttuple);
        vtypes.push_back(DtoType(argexp->type));
        size_t sz = getTypePaddedSize(vtypes.back());
        size_t asz = (sz + PTRSIZE - 1) & ~(PTRSIZE -1);
        if (sz != asz)
        {
            if (sz < PTRSIZE)
            {
                vtypes.back() = DtoSize_t();
            }
            else
            {
                // ok then... so we build some type that is big enough
                // and aligned to PTRSIZE
                std::vector<LLType*> gah;
                gah.reserve(asz/PTRSIZE);
                size_t gah_sz = 0;
                while (gah_sz < asz)
                {
                    gah.push_back(DtoSize_t());
                    gah_sz += PTRSIZE;
                }
                vtypes.back() = LLStructType::get(gIR->context(), gah, true);
            }
        }
    }
    LLStructType* vtype = LLStructType::get(gIR->context(), vtypes);

    if (Logger::enabled())
        Logger::cout() << "d-variadic argument struct type:\n" << *vtype << '\n';

    LLValue* mem = DtoRawAlloca(vtype, 0, "_argptr_storage");

    // store arguments in the struct
    for (size_t i=begin,k=0; i<n_arguments; i++,k++)
    {
        Expression* argexp = static_cast<Expression*>(arguments->data[i]);
        LLValue* argdst = DtoGEPi(mem,0,k);
        argdst = DtoBitCast(argdst, getPtrToType(DtoType(argexp->type)));
        DtoVariadicArgument(argexp, argdst);
    }

    // build type info array
    LLType* typeinfotype = DtoType(Type::typeinfo->type);
    LLArrayType* typeinfoarraytype = LLArrayType::get(typeinfotype,vtype->getNumElements());

    llvm::GlobalVariable* typeinfomem =
        new llvm::GlobalVariable(*gIR->module, typeinfoarraytype, true, llvm::GlobalValue::InternalLinkage, NULL, "._arguments.storage");
    if (Logger::enabled())
        Logger::cout() << "_arguments storage: " << *typeinfomem << '\n';

    std::vector<LLConstant*> vtypeinfos;
    for (size_t i=begin; i<n_arguments; i++)
    {
        Expression* argexp = static_cast<Expression*>(arguments->data[i]);
        vtypeinfos.push_back(DtoTypeInfoOf(argexp->type));
    }

    // apply initializer
    LLConstant* tiinits = LLConstantArray::get(typeinfoarraytype, vtypeinfos);
    typeinfomem->setInitializer(tiinits);

    // put data in d-array
    std::vector<LLConstant*> pinits;
    pinits.push_back(DtoConstSize_t(vtype->getNumElements()));
    pinits.push_back(llvm::ConstantExpr::getBitCast(typeinfomem, getPtrToType(typeinfotype)));
    LLType* tiarrty = DtoType(Type::typeinfo->type->arrayOf());
    tiinits = LLConstantStruct::get(isaStruct(tiarrty), pinits);
    LLValue* typeinfoarrayparam = new llvm::GlobalVariable(*gIR->module, tiarrty,
        true, llvm::GlobalValue::InternalLinkage, tiinits, "._arguments.array");

    llvm::AttributeWithIndex Attr;
    // specify arguments
    args.push_back(DtoLoad(typeinfoarrayparam));
    if (HAS_ATTRIBUTES(tf->fty.arg_arguments->attrs)) {
        Attr.Index = argidx;
        Attr.Attrs = tf->fty.arg_arguments->attrs;
        attrs.push_back(Attr);
    }
    ++argidx;

    args.push_back(gIR->ir->CreateBitCast(mem, getPtrToType(LLType::getInt8Ty(gIR->context())), "tmp"));
    if (HAS_ATTRIBUTES(tf->fty.arg_argptr->attrs)) {
        Attr.Index = argidx;
        Attr.Attrs = tf->fty.arg_argptr->attrs;
        attrs.push_back(Attr);
    }

    // pass non variadic args
    for (int i=0; i<begin; i++)
    {
        Parameter* fnarg = Parameter::getNth(tf->parameters, i);
        DValue* argval = DtoArgument(fnarg, static_cast<Expression*>(arguments->data[i]));
        args.push_back(fixArgument(argval, tf, callableTy->getParamType(argidx++), i));

        if (HAS_ATTRIBUTES(tf->fty.args[i]->attrs))
        {
            llvm::AttributeWithIndex Attr;
            Attr.Index = argidx;
            Attr.Attrs = tf->fty.args[i]->attrs;
            attrs.push_back(Attr);
        }
    }
}

//////////////////////////////////////////////////////////////////////////////////////////

// FIXME: this function is a mess !

DValue* DtoCallFunction(Loc& loc, Type* resulttype, DValue* fnval, Expressions* arguments)
{
    if (Logger::enabled()) {
        Logger::println("DtoCallFunction()");
    }
    LOG_SCOPE

    // the callee D type
    Type* calleeType = fnval->getType();

    // make sure the callee type has been processed
    DtoType(calleeType);

    // get func value if any
    DFuncValue* dfnval = fnval->isFunc();

    // handle special vararg intrinsics
    bool va_intrinsic = (dfnval && dfnval->func && dfnval->func->isVaIntrinsic());

    // get function type info
    TypeFunction* tf = DtoTypeFunction(fnval);

    // misc
    bool retinptr = tf->fty.arg_sret;
    bool thiscall = tf->fty.arg_this;
    bool delegatecall = (calleeType->toBasetype()->ty == Tdelegate);
    bool nestedcall = tf->fty.arg_nest;
    bool dvarargs = (tf->linkage == LINKd && tf->varargs == 1);

    llvm::CallingConv::ID callconv = DtoCallingConv(loc, tf->linkage);

    // get callee llvm value
    LLValue* callable = DtoCallableValue(fnval);
    LLFunctionType* callableTy = DtoExtractFunctionType(callable->getType());
    assert(callableTy);

//     if (Logger::enabled())
//         Logger::cout() << "callable: " << *callable << '\n';

    // get n arguments
    size_t n_arguments = arguments ? arguments->dim : 0;

    // get llvm argument iterator, for types
    LLFunctionType::param_iterator argbegin = callableTy->param_begin();
    LLFunctionType::param_iterator argiter = argbegin;

    // parameter attributes
    std::vector<llvm::AttributeWithIndex> attrs;
    llvm::AttributeWithIndex Attr;

    // return attrs
    if (HAS_ATTRIBUTES(tf->fty.ret->attrs))
    {
        Attr.Index = 0;
        Attr.Attrs = tf->fty.ret->attrs;
        attrs.push_back(Attr);
    }

    // handle implicit arguments
    std::vector<LLValue*> args;
    args.reserve(tf->fty.args.size());

    // return in hidden ptr is first
    if (retinptr)
    {
        LLValue* retvar = DtoRawAlloca((*argiter)->getContainedType(0), resulttype->alignsize(), ".rettmp");
        ++argiter;
        args.push_back(retvar);

        // add attrs for hidden ptr
        Attr.Index = 1;
        Attr.Attrs = tf->fty.arg_sret->attrs;
#if LDC_LLVM_VER >= 303
        assert((Attr.Attrs.hasAttribute(llvm::Attribute::StructRet) || Attr.Attrs.hasAttribute(llvm::Attribute::InReg))
            && "Sret arg not sret or inreg?");
#elif LDC_LLVM_VER == 302
        assert((Attr.Attrs.hasAttribute(llvm::Attributes::StructRet) || Attr.Attrs.hasAttribute(llvm::Attributes::InReg))
            && "Sret arg not sret or inreg?");
#else
        assert((Attr.Attrs & (llvm::Attribute::StructRet | llvm::Attribute::InReg))
            && "Sret arg not sret or inreg?");
#endif
        attrs.push_back(Attr);
    }

    // then comes a context argument...
    if(thiscall || delegatecall || nestedcall)
    {
#if DMDV2
        if (dfnval && (dfnval->func->ident == Id::ensure || dfnval->func->ident == Id::require)) {
            // ... which can be the this "context" argument for a contract
            // invocation (in D2, we do not generate a full nested contexts
            // for __require/__ensure as the needed parameters are passed
            // explicitly, while in D1, the normal nested function handling
            // mechanisms are used)
            LLValue* thisarg = DtoBitCast(DtoLoad(gIR->func()->thisArg), getVoidPtrType());
            ++argiter;
            args.push_back(thisarg);
        }
        else
#endif
        if (thiscall && dfnval && dfnval->vthis)
        {
            // ... or a normal 'this' argument
            LLValue* thisarg = DtoBitCast(dfnval->vthis, *argiter);
            ++argiter;
            args.push_back(thisarg);
        }
        else if (delegatecall)
        {
            // ... or a delegate context arg
            LLValue* ctxarg;
            if (fnval->isLVal())
            {
                ctxarg = DtoLoad(DtoGEPi(fnval->getLVal(), 0,0));
            }
            else
            {
                ctxarg = gIR->ir->CreateExtractValue(fnval->getRVal(), 0, ".ptr");
            }
            ctxarg = DtoBitCast(ctxarg, *argiter);
            ++argiter;
            args.push_back(ctxarg);
        }
        else if (nestedcall)
        {
            // ... or a nested function context arg
            if (dfnval) {
                LLValue* contextptr = DtoNestedContext(loc, dfnval->func);
                contextptr = DtoBitCast(contextptr, getVoidPtrType());
                args.push_back(contextptr);
            } else {
                args.push_back(llvm::UndefValue::get(getVoidPtrType()));
            }
            ++argiter;
        }
        else
        {
            error(loc, "Context argument required but none given");
            fatal();
        }

        // add attributes for context argument
        if (tf->fty.arg_this && HAS_ATTRIBUTES(tf->fty.arg_this->attrs))
        {
            Attr.Index = retinptr ? 2 : 1;
            Attr.Attrs = tf->fty.arg_this->attrs;
            attrs.push_back(Attr);
        }
        else if (tf->fty.arg_nest && HAS_ATTRIBUTES(tf->fty.arg_nest->attrs))
        {
            Attr.Index = retinptr ? 2 : 1;
            Attr.Attrs = tf->fty.arg_nest->attrs;
            attrs.push_back(Attr);
        }
    }

    // handle the rest of the arguments based on param passing style

    // variadic intrinsics need some custom casts
    if (va_intrinsic)
    {
        for (size_t i=0; i<n_arguments; i++)
        {
            Expression* exp = static_cast<Expression*>(arguments->data[i]);
            DValue* expelem = exp->toElem(gIR);
            // cast to va_list*
            LLValue* val = DtoBitCast(expelem->getLVal(), getVoidPtrType());
            ++argiter;
            args.push_back(val);
        }
    }

    // d style varargs needs a few more hidden arguments as well as special passing
    else if (dvarargs)
    {
        DtoBuildDVarArgList(args, attrs, tf, arguments, argiter-argbegin+1, callableTy);
    }

    // otherwise we're looking at a normal function call
    // or a C style vararg call
    else
    {
        Logger::println("doing normal arguments");
        if (Logger::enabled()) {
            Logger::println("Arguments so far: (%d)", static_cast<int>(args.size()));
            Logger::indent();
            for (size_t i = 0; i < args.size(); i++) {
                Logger::cout() << *args[i] << '\n';
            }
            Logger::undent();
            Logger::cout() << "Function type: " << tf->toChars() << '\n';
            //Logger::cout() << "LLVM functype: " << *callable->getType() << '\n';
        }

        size_t n = Parameter::dim(tf->parameters);
#if LDC_LLVM_VER >= 303
        LLSmallVector<llvm::Attribute, 10> attrptr(n, llvm::Attribute());
#elif LDC_LLVM_VER == 302
        LLSmallVector<llvm::Attributes, 10> attrptr(n, llvm::Attributes());
#else
        LLSmallVector<llvm::Attributes, 10> attrptr(n, llvm::Attribute::None);
#endif
        std::vector<DValue*> argvals;
        if (dfnval && dfnval->func->isArrayOp) {
            // slightly different approach for array operators
            for (int i=n-1; i>=0; --i) {
                Parameter* fnarg = Parameter::getNth(tf->parameters, i);
                assert(fnarg);
                DValue* argval = DtoArgument(fnarg, static_cast<Expression*>(arguments->data[i]));
                argvals.insert(argvals.begin(), argval);
            }
        } else {
            for (size_t i=0; i<n; ++i) {
                Parameter* fnarg = Parameter::getNth(tf->parameters, i);
                assert(fnarg);
                DValue* argval = DtoArgument(fnarg, static_cast<Expression*>(arguments->data[i]));
                argvals.push_back(argval);
            }
        }

        // do formal params
        int beg = argiter-argbegin;
        for (size_t i=0; i<n; i++)
        {
            DValue* argval = argvals.at(i);

            int j = tf->fty.reverseParams ? beg + n - i - 1 : beg + i;
            LLValue *arg = fixArgument(argval, tf, callableTy->getParamType(j), i);
            args.push_back(arg);

            attrptr[i] = tf->fty.args[i]->attrs;
            ++argiter;
        }

        // reverse the relevant params as well as the param attrs
        if (tf->fty.reverseParams)
        {
            std::reverse(args.begin() + beg, args.end());
            std::reverse(attrptr.begin(), attrptr.end());
        }

        // add attributes
        for (size_t i = 0; i < n; i++)
        {
            if (HAS_ATTRIBUTES(attrptr[i]))
            {
                Attr.Index = beg + i + 1;
                Attr.Attrs = attrptr[i];
                attrs.push_back(Attr);
            }
        }

        // do C varargs
        if (n_arguments > n)
        {
            for (size_t i=n; i<n_arguments; i++)
            {
                Parameter* fnarg = Parameter::getNth(tf->parameters, i);
                DValue* argval = DtoArgument(fnarg, static_cast<Expression*>(arguments->data[i]));
                LLValue* arg = argval->getRVal();

                // FIXME: do we need any param attrs here ?

                ++argiter;
                args.push_back(arg);
            }
        }
    }

#if 0
    if (Logger::enabled())
    {
        Logger::println("%lu params passed", args.size());
        for (int i=0; i<args.size(); ++i) {
            assert(args[i]);
            Logger::cout() << "arg["<<i<<"] = " << *args[i] << '\n';
        }
    }
#endif

    // void returns cannot not be named
    const char* varname = "";
    if (callableTy->getReturnType() != LLType::getVoidTy(gIR->context()))
        varname = "tmp";

#if 0
    if (Logger::enabled())
        Logger::cout() << "Calling: " << *callable << '\n';
#endif

    // call the function
    LLCallSite call = gIR->CreateCallOrInvoke(callable, args, varname);

    // get return value
    LLValue* retllval = (retinptr) ? args[0] : call.getInstruction();

    // Ignore ABI for intrinsics
    if (tf->linkage != LINKintrinsic && !retinptr)
    {
        // do abi specific return value fixups
        DImValue dretval(tf->next, retllval);
        retllval = tf->fty.getRet(tf->next, &dretval);
    }

    // Hack around LDC assuming structs and static arrays are in memory:
    // If the function returns a struct or a static array, and the return
    // value is not a pointer to a struct or a static array, store it to
    // a stack slot before continuing.
    int ty = tf->next->toBasetype()->ty;
    if ((ty == Tstruct && !isaPointer(retllval))
#if DMDV2
        || (ty == Tsarray && isaArray(retllval))
#endif
        )
    {
        Logger::println("Storing return value to stack slot");
        LLValue* mem = DtoRawAlloca(retllval->getType(), 0);
        DtoStore(retllval, mem);
        retllval = mem;
    }

    // repaint the type if necessary
    if (resulttype)
    {
        Type* rbase = stripModifiers(resulttype->toBasetype());
        Type* nextbase = stripModifiers(tf->nextOf()->toBasetype());
        if (!rbase->equals(nextbase))
        {
            Logger::println("repainting return value from '%s' to '%s'", tf->nextOf()->toChars(), rbase->toChars());
            switch(rbase->ty)
            {
            case Tarray:
            #if DMDV2
                if (tf->isref)
                    retllval = DtoBitCast(retllval, DtoType(rbase->pointerTo()));
                else
            #endif
                retllval = DtoAggrPaint(retllval, DtoType(rbase));
                break;

            case Tsarray:
                // nothing ?
                break;

            case Tclass:
            case Taarray:
            case Tpointer:
            #if DMDV2
                if (tf->isref)
                    retllval = DtoBitCast(retllval, DtoType(rbase->pointerTo()));
                else
            #endif
                retllval = DtoBitCast(retllval, DtoType(rbase));
                break;

#if DMDV2
            case Tstruct:
                if (nextbase->ty == Taarray && !tf->isref)
                {
                    // In the D2 frontend, the associative array type and its
                    // object.AssociativeArray representation are used
                    // interchangably in some places. However, AAs are returned
                    // by value and not in an sret argument, so if the struct
                    // type will be used, give the return value storage here
                    // so that we get the right amount of indirections.
                    LLValue* tmp = DtoAlloca(rbase, ".aalvauetmp");
                    LLValue* val = DtoInsertValue(
                        llvm::UndefValue::get(DtoType(rbase)), retllval, 0);
                    DtoStore(val, tmp);
                    retllval = tmp;
                    retinptr = true;
                    break;
                }
                // Fall through.
#endif

            default:
                // Unfortunately, DMD has quirks resp. bugs with regard to name
                // mangling: For voldemort-type functions which return a nested
                // struct, the mangled name of the return type changes during
                // semantic analysis.
                //
                // (When the function deco is first computed as part of
                // determining the return type deco, its return type part is
                // left off to avoid cycles. If mangle/toDecoBuffer is then
                // called again for the type, it will pick up the previous
                // result and return the full deco string for the nested struct
                // type, consisting of both the full mangled function name, and
                // the struct identifier.)
                //
                // Thus, the type merging in stripModifiers does not work
                // reliably, and the equality check above can fail even if the
                // types only differ in a qualifier.
                //
                // Because a proper fix for this in the frontend is hard, we
                // just carry on and hope that the frontend didn't mess up,
                // i.e. that the LLVM types really match up.
                //
                // An example situation where this case occurs is:
                // ---
                // auto iota() {
                //     static struct Result {
                //         this(int) {}
                //         inout(Result) test() inout { return cast(inout)Result(0); }
                //     }
                //     return Result.init;
                // }
                // void main() { auto r = iota(); }
                // ---
                Logger::println("Unknown return mismatch type, ignoring.");
                break;
            }
            if (Logger::enabled())
                Logger::cout() << "final return value: " << *retllval << '\n';
        }
    }

    // set calling convention and parameter attributes
#if LDC_LLVM_VER >= 303
  // FIXME: This is horrible inefficient. The real fix is to use AttributeSet
  // right from the beginning.
	llvm::AttributeSet attrlist;
  for (std::vector<llvm::AttributeWithIndex>::iterator I = attrs.begin(), E = attrs.end(); I != E; ++I)
  {
      llvm::AttributeSet  tmp = llvm::AttributeSet::get(gIR->context(), (*I).Index, llvm::AttrBuilder().addAttributes((*I).Attrs));
      attrlist = attrlist.addAttributes(gIR->context(), (*I).Index, tmp);
  }
#elif LDC_LLVM_VER == 302
	llvm::AttrListPtr attrlist = llvm::AttrListPtr::get(gIR->context(),
        llvm::ArrayRef<llvm::AttributeWithIndex>(attrs));
#else
    llvm::AttrListPtr attrlist = llvm::AttrListPtr::get(attrs.begin(), attrs.end());
#endif
    if (dfnval && dfnval->func)
    {
        LLFunction* llfunc = llvm::dyn_cast<LLFunction>(dfnval->val);
        if (llfunc && llfunc->isIntrinsic()) // override intrinsic attrs
#if LDC_LLVM_VER >= 302
            attrlist = llvm::Intrinsic::getAttributes(gIR->context(), static_cast<llvm::Intrinsic::ID>(llfunc->getIntrinsicID()));
#else
            attrlist = llvm::Intrinsic::getAttributes(static_cast<llvm::Intrinsic::ID>(llfunc->getIntrinsicID()));
#endif
        else
            call.setCallingConv(callconv);
    }
    else
        call.setCallingConv(callconv);
    call.setAttributes(attrlist);

    // if we are returning through a pointer arg
    // or if we are returning a reference
    // make sure we provide a lvalue back!
    if (retinptr
#if DMDV2
        || tf->isref
#endif
        )
        return new DVarValue(resulttype, retllval);

    return new DImValue(resulttype, retllval);
}
