diff gen/tocall.cpp @ 414:ac1fcc138e42

Fixed issue with internal real representation, incorrect for non x86-32 architectures. Cleaned up CallExp::toElem, moved implementation to tocall.cpp providing a single procedure to call arbitrary D functions fairly easily.
author Tomas Lindquist Olsen <tomas.l.olsen@gmail.com>
date Mon, 28 Jul 2008 02:11:34 +0200
parents 6057fdf797d8
children fa91b03d9cd7
line wrap: on
line diff
--- a/gen/tocall.cpp	Sun Jul 27 18:52:40 2008 +0200
+++ b/gen/tocall.cpp	Mon Jul 28 02:11:34 2008 +0200
@@ -13,49 +13,340 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
-DValue* DtoCallDFunc(FuncDeclaration* fdecl, Array* arguments, TypeClass* type, LLValue* thismem)
+TypeFunction* DtoTypeFunction(Type* type)
+{
+    TypeFunction* tf = 0;
+    type = type->toBasetype();
+    if (type->ty == Tfunction)
+    {
+         tf = (TypeFunction*)type;
+    }
+    else if (type->ty == Tdelegate)
+    {
+        assert(type->next->ty == Tfunction);
+        tf = (TypeFunction*)type->next;
+    }
+    return tf;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+unsigned DtoCallingConv(LINK l)
 {
-    Logger::println("Calling function: %s", fdecl->toPrettyChars());
-    LOG_SCOPE;
+    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"));
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
 
-    assert(fdecl);
-    DtoForceDeclareDsymbol(fdecl);
-    llvm::Function* fn = fdecl->ir.irFunc->func;
-    TypeFunction* tf = (TypeFunction*)DtoDType(fdecl->type);
+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();
+        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;
 
-    llvm::PAListPtr palist;
+    // 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 = new llvm::AllocaInst(vtype,"_argptr_storage",gIR->topallocapoint());
 
-    int thisOffset = 0;
-    if (type || thismem)
+    // store arguments in the struct
+    for (int i=begin,k=0; i<arguments->dim; i++,k++)
     {
-        assert(type && thismem);
-        thisOffset = 1;
+        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));
     }
 
-    std::vector<LLValue*> args;
-    if (thisOffset)
-        args.push_back(thismem);
-    for (size_t i=0; i<arguments->dim; ++i)
+    // 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++)
     {
-        Expression* ex = (Expression*)arguments->data[i];
         Argument* fnarg = Argument::getNth(tf->parameters, i);
-        DValue* argval = DtoArgument(fnarg, ex);
-        LLValue* a = argval->getRVal();
-        const LLType* aty = fn->getFunctionType()->getParamType(i+thisOffset);
-        if (a->getType() != aty)
-        {
-            Logger::cout() << "expected: " << *aty << '\n';
-            Logger::cout() << "got:      " << *a->getType() << '\n';
-            a = DtoBitCast(a, aty);
-        }
-        args.push_back(a);
-        if (fnarg && fnarg->llvmByVal)
-            palist = palist.addAttr(i+thisOffset+1, llvm::ParamAttr::ByVal); // return,this,args...
+        DValue* argval = DtoArgument(fnarg, (Expression*)arguments->data[i]);
+        args.push_back(argval->getRVal());
+
+        if (fnarg->llvmByVal)
+            palist = palist.addAttr(argidx, llvm::ParamAttr::ByVal);
+
+        ++argidx;
+    }
+}
+
+
+DValue* DtoCallFunction(Type* resulttype, DValue* fnval, Expressions* arguments)
+{
+    // the callee D type
+    Type* calleeType = fnval->getType();
+
+    // get func value if any
+    DFuncValue* dfnval = fnval->isFunc();
+
+    // handle special va_copy / va_end intrinsics
+    bool va_intrinsic = (dfnval && dfnval->func && (dfnval->func->llvmInternal == LLVMva_intrinsic));
+
+    // get function type info
+    TypeFunction* tf = DtoTypeFunction(calleeType);
+    assert(tf);
+
+    // misc
+    bool retinptr = tf->llvmRetInPtr;
+    bool usesthis = tf->llvmUsesThis;
+    bool delegatecall = (calleeType->toBasetype()->ty == Tdelegate);
+    bool nestedcall = (dfnval && dfnval->func && dfnval->func->isNested());
+    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;
+
+    // handle implicit arguments
+    std::vector<LLValue*> args;
+
+    // return in hidden ptr is first
+    if (retinptr)
+    {
+        LLValue* retvar = new llvm::AllocaInst(argiter->get()->getContainedType(0), ".rettmp", gIR->topallocapoint());
+        ++argiter;
+        args.push_back(retvar);
     }
 
-    CallOrInvoke* call = gIR->CreateCallOrInvoke(fn, args.begin(), args.end(), "tmp");
-    call->setCallingConv(DtoCallingConv(LINKd));
+    // then comes the 'this' argument
+    if (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(dfnval->func->toParent2()->isFuncDeclaration());
+        if (!contextptr)
+            contextptr = getNullPtr(getVoidPtrType());
+        else
+            contextptr = DtoBitCast(contextptr, getVoidPtrType());
+        ++argiter;
+        args.push_back(contextptr);
+    }
+
+    // handle the rest of the arguments based on param passing style
+    llvm::PAListPtr palist;
+
+    // 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 && arg->getType() != callableTy->getParamType(j))
+                arg = DtoBitCast(arg, callableTy->getParamType(j));
+            if (fnarg && fnarg->llvmByVal)
+                palist = palist.addAttr(j+1, llvm::ParamAttr::ByVal);
+            ++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
+    if (dfnval && dfnval->func)
+    {
+        int li = dfnval->func->llvmInternal;
+        if (li != LLVMintrinsic && li != LLVMva_start && li != LLVMva_intrinsic)
+        {
+            call->setCallingConv(callconv);
+        }
+    }
+    else
+    {
+        call->setCallingConv(callconv);
+    }
+
+    // param attrs
     call->setParamAttrs(palist);
 
-    return new DImValue(type, call->get(), false);
+    return new DImValue(resulttype, retllval, false);
 }
+
+
+
+
+
+
+
+
+
+
+
+
+