view gen/tocall.cpp @ 632:df196c8dea26

Updated to latest LLVM trunk, function notes have been removed and merged with parameter attributes, which have been renamed to just attributes. Nothing seems to have broke!
author Tomas Lindquist Olsen <tomas.l.olsen@gmail.com>
date Wed, 01 Oct 2008 23:17:14 +0200
parents 26fce59fe80a
children 30b42a283c8e
line wrap: on
line source

#include "gen/llvm.h"

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

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

#include "gen/logger.h"

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

TypeFunction* DtoTypeFunction(DValue* fnval)
{
    Type* type = fnval->getType()->toBasetype();
    if (type->ty == Tfunction)
    {
         return (TypeFunction*)type;
    }
    else if (type->ty == Tdelegate)
    {
        assert(type->next->ty == Tfunction);
        return (TypeFunction*)type->next;
    }

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

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

unsigned DtoCallingConv(LINK l)
{
    if (l == LINKc || l == LINKcpp)
        return llvm::CallingConv::C;
    else if (l == LINKd || l == LINKdefault)
    {
        if (global.params.cpu == ARCHx86)
            return llvm::CallingConv::X86_StdCall;
        else
            return llvm::CallingConv::Fast;
    }
    else if (l == LINKwindows)
        return llvm::CallingConv::X86_StdCall;
    else
        assert(0 && "Unsupported calling convention");
}

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

DValue* DtoVaArg(Loc& loc, Type* type, Expression* valistArg)
{
    DValue* expelem = valistArg->toElem(gIR);
    const LLType* llt = DtoType(type);
    if (DtoIsPassedByRef(type))
        llt = getPtrToType(llt);
    // issue a warning for broken va_arg instruction.
    if (global.params.cpu != ARCHx86)
        warning("%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)
    {
        LLValue* dg = fn->getRVal();
        if (Logger::enabled())
            Logger::cout() << "delegate: " << *dg << '\n';
        LLValue* funcptr = DtoGEPi(dg, 0, 1);
        return DtoLoad(funcptr);
    }
    else
    {
        assert(0 && "not a callable type");
        return NULL;
    }
}

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

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

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

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

    std::vector<const LLType*> vtypes;

    // number of non variadic args
    int begin = tf->parameters->dim;
    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 (int i=begin; i<n_arguments; i++)
    {
        Expression* argexp = (Expression*)arguments->data[i];
        vtypes.push_back(DtoType(argexp->type));
        size_t sz = getABITypeSize(vtypes.back());
        if (sz < PTRSIZE)
            vtypes.back() = DtoSize_t();
    }
    const LLStructType* vtype = LLStructType::get(vtypes);

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

    LLValue* mem = DtoAlloca(vtype,"_argptr_storage");

    // store arguments in the struct
    for (int i=begin,k=0; i<n_arguments; i++,k++)
    {
        Expression* argexp = (Expression*)arguments->data[i];
        if (global.params.llvmAnnotate)
            DtoAnnotation(argexp->toChars());
        LLValue* argdst = DtoGEPi(mem,0,k);
        argdst = DtoBitCast(argdst, getPtrToType(DtoType(argexp->type)));
        DtoVariadicArgument(argexp, argdst);
    }

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

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

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

    // apply initializer
    LLConstant* tiinits = llvm::ConstantArray::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)));
    const LLType* tiarrty = DtoType(Type::typeinfo->type->arrayOf());
    tiinits = llvm::ConstantStruct::get(pinits);
    LLValue* typeinfoarrayparam = new llvm::GlobalVariable(tiarrty,
        true, llvm::GlobalValue::InternalLinkage, tiinits, "._arguments.array", gIR->module);

    // specify arguments
    args.push_back(typeinfoarrayparam);
    ++argidx;
    args.push_back(gIR->ir->CreateBitCast(mem, getPtrToType(LLType::Int8Ty), "tmp"));
    ++argidx;

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

        if (fnarg->llvmAttrs)
            palist = palist.addAttr(argidx, fnarg->llvmAttrs);

        ++argidx;
    }
}


DValue* DtoCallFunction(Loc& loc, Type* resulttype, DValue* fnval, Expressions* arguments)
{
    // the callee D type
    Type* calleeType = fnval->getType();

    // if the type has not yet been processed, do so now
    if (calleeType->ir.type == NULL)
        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->retInPtr;
    bool thiscall = tf->usesThis;
    bool delegatecall = (calleeType->toBasetype()->ty == Tdelegate);
    bool nestedcall = tf->usesNest;
    bool dvarargs = (tf->linkage == LINKd && tf->varargs == 1);

    unsigned callconv = DtoCallingConv(tf->linkage);

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

    // 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
    llvm::AttrListPtr palist;

    // return attrs
    if (tf->retAttrs)
        palist = palist.addAttr(0, tf->retAttrs);

    // handle implicit arguments
    std::vector<LLValue*> args;

    // return in hidden ptr is first
    if (retinptr)
    {
        LLValue* retvar = DtoAlloca(argiter->get()->getContainedType(0), ".rettmp");
        ++argiter;
        args.push_back(retvar);
        palist = palist.addAttr(1, llvm::Attribute::StructRet);
    }

    // then comes a context argument...
    if(thiscall || delegatecall || nestedcall)
    {
        // ... which can be a 'this' argument
        if (thiscall && dfnval && dfnval->vthis)
        {
            LLValue* thisarg = DtoBitCast(dfnval->vthis, argiter->get());
            ++argiter;
            args.push_back(thisarg);
        }
        // ... or a delegate context arg
        else if (delegatecall)
        {
            LLValue* ctxarg = DtoLoad(DtoGEPi(fnval->getRVal(), 0,0));
            assert(ctxarg->getType() == argiter->get());
            ++argiter;
            args.push_back(ctxarg);
        }
        // ... or a nested function context arg
        else if (nestedcall)
        {
            LLValue* contextptr = DtoNestedContext(loc, dfnval->func);
            contextptr = DtoBitCast(contextptr, getVoidPtrType());
            ++argiter;
            args.push_back(contextptr);
        }
        else
        {
            error(loc, "Context argument required but none given");
            fatal();
        }
    }

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

    // variadic instrinsics need some custom casts
    if (va_intrinsic)
    {
        for (int i=0; i<n_arguments; i++)
        {
            Expression* exp = (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, palist, tf, arguments, argiter-argbegin+1);
    }

    // otherwise we're looking at a normal function call
    else
    {
        Logger::println("doing normal arguments");
        for (int i=0; i<n_arguments; i++) {
            int j = argiter-argbegin;
            Argument* fnarg = Argument::getNth(tf->parameters, i);
            DValue* argval = DtoArgument(fnarg, (Expression*)arguments->data[i]);
            LLValue* arg = argval->getRVal();
            if (fnarg) // can fnarg ever be null in this block?
            {
//                 if (Logger::enabled())
//                 {
//                     Logger::cout() << "arg:     " << *arg << '\n';
//                     Logger::cout() << "expects: " << *callableTy->getParamType(j) << '\n';
//                 }
                if (arg->getType() != callableTy->getParamType(j))
                    arg = DtoBitCast(arg, callableTy->getParamType(j));
                if (fnarg->llvmAttrs)
                    palist = palist.addAttr(j+1, fnarg->llvmAttrs);
            }
            ++argiter;
            args.push_back(arg);
        }
    }

    #if 0
    Logger::println("%d params passed", n);
    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::VoidTy)
        varname = "tmp";

    //Logger::cout() << "Calling: " << *funcval << '\n';

    // call the function
    CallOrInvoke* call = gIR->CreateCallOrInvoke(callable, args.begin(), args.end(), varname);

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

    // if the type of retllval is abstract, refine to concrete
    if (retllval->getType()->isAbstract())
        retllval = DtoBitCast(retllval, getPtrToType(DtoType(resulttype)), "retval");

    // set calling convention and parameter attributes
    if (dfnval && dfnval->func)
    {
        LLFunction* llfunc = llvm::dyn_cast<LLFunction>(dfnval->val);
        if (llfunc && llfunc->isIntrinsic())
            palist = llvm::Intrinsic::getAttributes((llvm::Intrinsic::ID)llfunc->getIntrinsicID());
        else
            call->setCallingConv(callconv);
    }
    else
        call->setCallingConv(callconv);
    call->setAttributes(palist);

    return new DImValue(resulttype, retllval);
}