#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/abi.h" #include "gen/nested.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) { Type* next = type->nextOf(); assert(next->ty == Tfunction); return (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) 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? else if (l == LINKwindows) 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); 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) { 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; } } ////////////////////////////////////////////////////////////////////////////////////////// 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& args, std::vector& attrs, TypeFunction* tf, Expressions* arguments, size_t argidx) { Logger::println("doing d-style variadic arguments"); LOG_SCOPE std::vector 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 (int i=begin; idata[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 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); } } } const 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 (int i=begin,k=0; idata[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 const LLType* typeinfotype = DtoType(Type::typeinfo->type); const 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 vtypeinfos; for (int i=begin,k=0; idata[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 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 = LLConstantStruct::get(gIR->context(), pinits, false); 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 (unsigned atts = tf->fty.arg_arguments->attrs) { Attr.Index = argidx; Attr.Attrs = atts; attrs.push_back(Attr); } ++argidx; args.push_back(gIR->ir->CreateBitCast(mem, getPtrToType(LLType::getInt8Ty(gIR->context())), "tmp")); if (unsigned atts = tf->fty.arg_argptr->attrs) { Attr.Index = argidx; Attr.Attrs = atts; attrs.push_back(Attr); } ++argidx; // pass non variadic args for (int i=0; iparameters, i); DValue* argval = DtoArgument(fnarg, (Expression*)arguments->data[i]); args.push_back(argval->getRVal()); if (tf->fty.args[i]->attrs) { llvm::AttributeWithIndex Attr; Attr.Index = argidx; Attr.Attrs = tf->fty.args[i]->attrs; attrs.push_back(Attr); } ++argidx; } } // 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); const 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 attrs; llvm::AttributeWithIndex Attr; // return attrs if (tf->fty.ret->attrs) { Attr.Index = 0; 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) { LLValue* retvar = DtoRawAlloca(argiter->get()->getContainedType(0), 0, ".rettmp"); ++argiter; args.push_back(retvar); // add attrs for hidden ptr Attr.Index = 1; Attr.Attrs = tf->fty.arg_sret->attrs; assert((Attr.Attrs & (llvm::Attribute::StructRet | llvm::Attribute::InReg)) && "Sret arg not sret or inreg?"); attrs.push_back(Attr); } // 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; if (fnval->isLVal()) { ctxarg = DtoLoad(DtoGEPi(fnval->getLVal(), 0,0)); } else { ctxarg = gIR->ir->CreateExtractValue(fnval->getRVal(), 0, ".ptr"); } ctxarg = DtoBitCast(ctxarg, 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(); } // add attributes for context argument if (tf->fty.arg_this && 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 && 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 instrinsics need some custom casts if (va_intrinsic) { for (int i=0; idata[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); } // 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)", (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); LLSmallVector attrptr(n, 0); // do formal params int beg = argiter-argbegin; for (int i=0; iparameters, i); assert(fnarg); DValue* argval = DtoArgument(fnarg, (Expression*)arguments->data[i]); #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(), i, argval); #if 0 if (Logger::enabled()) { Logger::cout() << "Argument after ABI: " << *arg << '\n'; Logger::cout() << "Argument type after ABI: " << *arg->getType() << '\n'; } #endif int j = tf->fty.reverseParams ? beg + n - i - 1 : beg + i; // Hack around LDC assuming structs 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. if (argval->getType()->ty == Tstruct && isaPointer(arg) && !isaPointer(callableTy->getParamType(j))) { Logger::println("Loading struct type for function argument"); arg = DtoLoad(arg); } // parameter type mismatch, this is hard to get rid of if (arg->getType() != callableTy->getParamType(j)) { #if 1 if (Logger::enabled()) { Logger::cout() << "arg: " << *arg << '\n'; Logger::cout() << "of type: " << *arg->getType() << '\n'; Logger::cout() << "expects: " << *callableTy->getParamType(j) << '\n'; } #endif arg = DtoBitCast(arg, callableTy->getParamType(j)); } // param attrs attrptr[i] = tf->fty.args[i]->attrs; ++argiter; args.push_back(arg); } // 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 (int i = 0; i < n; i++) { if (attrptr[i]) { Attr.Index = beg + i + 1; Attr.Attrs = attrptr[i]; attrs.push_back(Attr); } } // do C varargs if (n_arguments > n) { for (int i=n; iparameters, i); DValue* argval = DtoArgument(fnarg, (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; igetReturnType() != 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.begin(), args.end(), 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->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 = resulttype->toBasetype(); Type* nextbase = tf->nextOf()->toBasetype(); #if DMDV2 rbase = rbase->mutableOf(); nextbase = nextbase->mutableOf(); #endif 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 Tclass: case Taarray: case Tpointer: #if DMDV2 if (tf->isref) retllval = DtoBitCast(retllval, DtoType(rbase->pointerTo())); else #endif retllval = DtoBitCast(retllval, DtoType(rbase)); break; default: assert(0 && "unhandled repainting of return value"); } if (Logger::enabled()) Logger::cout() << "final return value: " << *retllval << '\n'; } } // set calling convention and parameter attributes llvm::AttrListPtr attrlist = llvm::AttrListPtr::get(attrs.begin(), attrs.end()); if (dfnval && dfnval->func) { LLFunction* llfunc = llvm::dyn_cast(dfnval->val); if (llfunc && llfunc->isIntrinsic()) // override intrinsic attrs attrlist = llvm::Intrinsic::getAttributes((llvm::Intrinsic::ID)llfunc->getIntrinsicID()); 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); }