mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-12 02:43:14 +01:00
See the comment in DtoCallFunction for an explanation of what is going on. The struct zero initialization code was also refactored out to AssignExp::toElem and modified so that it is only triggered on integer->struct assignments, not for any types where the modifier-stripped types don't match up. This would have lead to silently wrong code in the cases where the assert would have been triggered otherwise. Fixes the Phobos testsuite build.
724 lines
24 KiB
C++
724 lines
24 KiB
C++
#include "gen/llvm.h"
|
|
|
|
#include "mtype.h"
|
|
#include "declaration.h"
|
|
#include "id.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 static_cast<TypeFunction*>(type);
|
|
}
|
|
else if (type->ty == Tdelegate)
|
|
{
|
|
Type* next = type->nextOf();
|
|
assert(next->ty == Tfunction);
|
|
return static_cast<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 || global.params.cpu == ARCHx86_64)
|
|
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?
|
|
// On Windows 64bit, there is only one calling convention!
|
|
else if (l == LINKwindows)
|
|
return global.params.cpu == ARCHx86_64 ? llvm::CallingConv::C : llvm::CallingConv::X86_StdCall;
|
|
else if (l == LINKpascal)
|
|
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);
|
|
LLType* llt = DtoType(type);
|
|
if (DtoIsPassedByRef(type))
|
|
llt = getPtrToType(llt);
|
|
// issue a warning for broken va_arg instruction.
|
|
if (global.params.cpu != ARCHx86)
|
|
warning(Loc(), "%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;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLFunctionType* DtoExtractFunctionType(LLType* type)
|
|
{
|
|
if (LLFunctionType* fty = isaFunction(type))
|
|
return fty;
|
|
else if (LLPointerType* pty = isaPointer(type))
|
|
{
|
|
if (LLFunctionType* fty = isaFunction(pty->getElementType()))
|
|
return fty;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static LLValue *fixArgument(DValue *argval, TypeFunction* tf, LLType *callableArgType, int argIndex)
|
|
{
|
|
#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(), argIndex, argval);
|
|
|
|
#if 0
|
|
if (Logger::enabled()) {
|
|
Logger::cout() << "Argument after ABI: " << *arg << '\n';
|
|
Logger::cout() << "Argument type after ABI: " << *arg->getType() << '\n';
|
|
}
|
|
#endif
|
|
|
|
// Hack around LDC assuming structs and static arrays 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.
|
|
int ty = argval->getType()->toBasetype()->ty;
|
|
if (isaPointer(arg) && !isaPointer(callableArgType) &&
|
|
#if DMDV2
|
|
(ty == Tstruct || ty == Tsarray))
|
|
#else
|
|
ty == Tstruct)
|
|
#endif
|
|
{
|
|
Logger::println("Loading struct type for function argument");
|
|
arg = DtoLoad(arg);
|
|
}
|
|
|
|
// parameter type mismatch, this is hard to get rid of
|
|
if (arg->getType() != callableArgType)
|
|
{
|
|
#if 1
|
|
if (Logger::enabled())
|
|
{
|
|
Logger::cout() << "arg: " << *arg << '\n';
|
|
Logger::cout() << "of type: " << *arg->getType() << '\n';
|
|
Logger::cout() << "expects: " << *callableArgType << '\n';
|
|
}
|
|
#endif
|
|
if (isaStruct(arg))
|
|
arg = DtoAggrPaint(arg, callableArgType);
|
|
else
|
|
arg = DtoBitCast(arg, callableArgType);
|
|
}
|
|
return arg;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoBuildDVarArgList(std::vector<LLValue*>& args,
|
|
std::vector<llvm::AttributeWithIndex>& attrs,
|
|
TypeFunction* tf, Expressions* arguments,
|
|
size_t argidx,
|
|
LLFunctionType* callableTy)
|
|
{
|
|
Logger::println("doing d-style variadic arguments");
|
|
LOG_SCOPE
|
|
|
|
std::vector<LLType*> 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; i<n_arguments; i++)
|
|
{
|
|
Expression* argexp = static_cast<Expression*>(arguments->data[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<LLType*> 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);
|
|
}
|
|
}
|
|
}
|
|
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; i<n_arguments; i++,k++)
|
|
{
|
|
Expression* argexp = static_cast<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
|
|
LLType* typeinfotype = DtoType(Type::typeinfo->type);
|
|
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<LLConstant*> vtypeinfos;
|
|
for (int i=begin,k=0; i<n_arguments; i++,k++)
|
|
{
|
|
Expression* argexp = static_cast<Expression*>(arguments->data[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<LLConstant*> pinits;
|
|
pinits.push_back(DtoConstSize_t(vtype->getNumElements()));
|
|
pinits.push_back(llvm::ConstantExpr::getBitCast(typeinfomem, getPtrToType(typeinfotype)));
|
|
LLType* tiarrty = DtoType(Type::typeinfo->type->arrayOf());
|
|
tiinits = LLConstantStruct::get(isaStruct(tiarrty), pinits);
|
|
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 (llvm::Attributes 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 (llvm::Attributes atts = tf->fty.arg_argptr->attrs) {
|
|
Attr.Index = argidx;
|
|
Attr.Attrs = atts;
|
|
attrs.push_back(Attr);
|
|
}
|
|
|
|
// pass non variadic args
|
|
for (int i=0; i<begin; i++)
|
|
{
|
|
Parameter* fnarg = Parameter::getNth(tf->parameters, i);
|
|
DValue* argval = DtoArgument(fnarg, static_cast<Expression*>(arguments->data[i]));
|
|
args.push_back(fixArgument(argval, tf, callableTy->getParamType(argidx++), i));
|
|
|
|
if (tf->fty.args[i]->attrs)
|
|
{
|
|
llvm::AttributeWithIndex Attr;
|
|
Attr.Index = argidx;
|
|
Attr.Attrs = tf->fty.args[i]->attrs;
|
|
attrs.push_back(Attr);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// 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);
|
|
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<llvm::AttributeWithIndex> 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<LLValue*> args;
|
|
args.reserve(tf->fty.args.size());
|
|
|
|
// return in hidden ptr is first
|
|
if (retinptr)
|
|
{
|
|
LLValue* retvar = DtoRawAlloca((*argiter)->getContainedType(0), resulttype->alignsize(), ".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)
|
|
{
|
|
if (dfnval && (dfnval->func->ident == Id::ensure ||
|
|
dfnval->func->ident == Id::require))
|
|
{
|
|
// ... which can be the this "context" argument for a contract
|
|
// invocation (we do not generate a full nested context struct for
|
|
// these)
|
|
LLValue* thisarg = DtoBitCast(DtoLoad(gIR->func()->thisArg), getVoidPtrType());
|
|
++argiter;
|
|
args.push_back(thisarg);
|
|
}
|
|
else if (thiscall && dfnval && dfnval->vthis)
|
|
{
|
|
// ... or a normal 'this' argument
|
|
LLValue* thisarg = DtoBitCast(dfnval->vthis, *argiter);
|
|
++argiter;
|
|
args.push_back(thisarg);
|
|
}
|
|
else if (delegatecall)
|
|
{
|
|
// ... or a delegate context arg
|
|
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);
|
|
++argiter;
|
|
args.push_back(ctxarg);
|
|
}
|
|
else if (nestedcall)
|
|
{
|
|
/// ... or a nested function context arg
|
|
if (dfnval) {
|
|
LLValue* contextptr = DtoNestedContext(loc, dfnval->func);
|
|
contextptr = DtoBitCast(contextptr, getVoidPtrType());
|
|
args.push_back(contextptr);
|
|
} else {
|
|
args.push_back(llvm::UndefValue::get(getVoidPtrType()));
|
|
}
|
|
++argiter;
|
|
}
|
|
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 intrinsics need some custom casts
|
|
if (va_intrinsic)
|
|
{
|
|
for (int i=0; i<n_arguments; i++)
|
|
{
|
|
Expression* exp = static_cast<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, attrs, tf, arguments, argiter-argbegin+1, callableTy);
|
|
}
|
|
|
|
// 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)", static_cast<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<llvm::Attributes, 10> attrptr(n, llvm::Attribute::None);
|
|
std::vector<DValue*> argvals;
|
|
if (dfnval && dfnval->func->isArrayOp) {
|
|
// slightly different approach for array operators
|
|
for (int i=n-1; i>=0; --i) {
|
|
Parameter* fnarg = Parameter::getNth(tf->parameters, i);
|
|
assert(fnarg);
|
|
DValue* argval = DtoArgument(fnarg, static_cast<Expression*>(arguments->data[i]));
|
|
argvals.insert(argvals.begin(), argval);
|
|
}
|
|
} else {
|
|
for (int i=0; i<n; ++i) {
|
|
Parameter* fnarg = Parameter::getNth(tf->parameters, i);
|
|
assert(fnarg);
|
|
DValue* argval = DtoArgument(fnarg, static_cast<Expression*>(arguments->data[i]));
|
|
argvals.push_back(argval);
|
|
}
|
|
}
|
|
|
|
// do formal params
|
|
int beg = argiter-argbegin;
|
|
for (int i=0; i<n; i++)
|
|
{
|
|
DValue* argval = argvals.at(i);
|
|
|
|
int j = tf->fty.reverseParams ? beg + n - i - 1 : beg + i;
|
|
LLValue *arg = fixArgument(argval, tf, callableTy->getParamType(j), i);
|
|
args.push_back(arg);
|
|
|
|
attrptr[i] = tf->fty.args[i]->attrs;
|
|
++argiter;
|
|
}
|
|
|
|
// 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; i<n_arguments; i++)
|
|
{
|
|
Parameter* fnarg = Parameter::getNth(tf->parameters, i);
|
|
DValue* argval = DtoArgument(fnarg, static_cast<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; 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::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, 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->toBasetype()->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 = stripModifiers(resulttype->toBasetype());
|
|
Type* nextbase = stripModifiers(tf->nextOf()->toBasetype());
|
|
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 Tsarray:
|
|
// nothing ?
|
|
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:
|
|
// Unfortunately, DMD has quirks resp. bugs with regard to name
|
|
// mangling: For voldemort-type functions which return a nested
|
|
// struct, the mangled name of the return type changes during
|
|
// semantic analysis.
|
|
//
|
|
// (When the function deco is first computed as part of
|
|
// determining the return type deco, its return type part is
|
|
// left off to avoid cycles. If mangle/toDecoBuffer is then
|
|
// called again for the type, it will pick up the previous
|
|
// result and return the full deco string for the nested struct
|
|
// type, consisting of both the full mangled function name, and
|
|
// the struct identifier.)
|
|
//
|
|
// Thus, the type merging in stripModifiers does not work
|
|
// reliably, and the equality check above can fail even if the
|
|
// types only differ in a qualifier.
|
|
//
|
|
// Because a proper fix for this in the frontend is hard, we
|
|
// just carry on and hope that the frontend didn't mess up,
|
|
// i.e. that the LLVM types really match up.
|
|
//
|
|
// An example situation where this case occurs is:
|
|
// ---
|
|
// auto iota() {
|
|
// static struct Result {
|
|
// this(int) {}
|
|
// inout(Result) test() inout { return cast(inout)Result(0); }
|
|
// }
|
|
// return Result.init;
|
|
// }
|
|
// void main() { auto r = iota(); }
|
|
// ---
|
|
break;
|
|
}
|
|
if (Logger::enabled())
|
|
Logger::cout() << "final return value: " << *retllval << '\n';
|
|
}
|
|
}
|
|
|
|
// set calling convention and parameter attributes
|
|
#if LDC_LLVM_VER >= 302
|
|
llvm::AttrListPtr attrlist = llvm::AttrListPtr::get(llvm::ArrayRef<llvm::AttributeWithIndex>(attrs));
|
|
#else
|
|
llvm::AttrListPtr attrlist = llvm::AttrListPtr::get(attrs.begin(), attrs.end());
|
|
#endif
|
|
if (dfnval && dfnval->func)
|
|
{
|
|
LLFunction* llfunc = llvm::dyn_cast<LLFunction>(dfnval->val);
|
|
if (llfunc && llfunc->isIntrinsic()) // override intrinsic attrs
|
|
attrlist = llvm::Intrinsic::getAttributes(static_cast<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);
|
|
}
|