view gen/tocall.cpp @ 486:a34078905d01

Added pragma(llvmdc, "string") for misc per-module compiler configuration, currently "string" can only be "verbose" which forces -vv for module it appears in. Reimplemented support for nested functions/class using a new approach. Added error on taking address of intrinsic. Fixed problems with the ->syntaxCopy of TypeFunction delegate exp. Removed DtoDType and replaced all uses with ->toBasetype() instead. Removed unused inplace stuff. Fixed a bunch of issues in the runtime unittests, not complete yet. Added mini tests.
author Tomas Lindquist Olsen <tomas.l.olsen@gmail.com>
date Sun, 10 Aug 2008 08:37:38 +0200
parents 672eb4893b55
children 993b217af574
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)
        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();
        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::PAListPtr& 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);

    // build struct with argument types (non variadic args)
    for (int i=begin; i<arguments->dim; 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);
    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<arguments->dim; 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);
    Logger::cout() << "_arguments storage: " << *typeinfomem << '\n';

    std::vector<LLConstant*> vtypeinfos;
    for (int i=begin,k=0; i<arguments->dim; 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 llvm argument iterator, for types
    LLFunctionType::param_iterator argbegin = callableTy->param_begin();
    LLFunctionType::param_iterator argiter = argbegin;

    // parameter attributes
    llvm::PAListPtr 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::ParamAttr::StructRet);
    }

    // then comes a context argument...
    if(thiscall || delegatecall || nestedcall)
    {
        // ... which can be a 'this' argument
        if (thiscall)
        {
            assert(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)
    {
        size_t n = arguments->dim;
        for (int i=0; i<n; 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<arguments->dim; 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 (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::getParamAttrs((llvm::Intrinsic::ID)llfunc->getIntrinsicID());
        else
            call->setCallingConv(callconv);
    }
    else
        call->setCallingConv(callconv);
    call->setParamAttrs(palist);

    return new DImValue(resulttype, retllval);
}