Mercurial > projects > ldc
view gen/tocall.cpp @ 454:283d113d4753
Added generation of the llvm 'sret' parameter attribute where applicable.
Fixed some wrong argument handling code when setting parameter attributes.
Updated the tango unittest script in the tango patch, does not work yet, all modules don't compile...
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Sat, 02 Aug 2008 02:54:57 +0200 |
parents | cc40db549aea |
children | b975f29b7256 |
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(); 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 = new llvm::AllocaInst(vtype,"_argptr_storage",gIR->topallocapoint()); // 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(); // 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->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; // parameter attributes llvm::PAListPtr palist; // return attrs if (tf->llvmRetAttrs) palist = palist.addAttr(0, tf->llvmRetAttrs); // 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); palist = palist.addAttr(1, llvm::ParamAttr::StructRet); } // then comes a context argument... if(usesthis || delegatecall || nestedcall) { // ... which can be a '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); } 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, false); }