ldc/gen/tocall.cpp
David Nadlinger 8ff3a8060a Use llvm_unreachable instead of assert(0).
Also removed some unused functions.
2013-02-07 03:38:15 +01:00

807 lines
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- tocall.cpp --------------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#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)
{
// FIXME: There is really no reason why the function type should be
// unmerged at this stage, but the frontend still seems to produce such
// cases; for example for the uint(uint) next type of the return type of
// (&zero)(), leading to a crash in DtoCallFunction:
// ---
// void test8198() {
// uint delegate(uint) zero() { return null; }
// auto a = (&zero)()(0);
// }
// ---
// Calling merge() here works around the symptoms, but does not fix the
// root cause.
Type* next = type->nextOf()->merge();
assert(next->ty == Tfunction);
return static_cast<TypeFunction*>(next);
}
llvm_unreachable("Cannot get TypeFunction* from non lazy/function/delegate");
}
//////////////////////////////////////////////////////////////////////////////////////////
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");
}
}
llvm_unreachable("Not a callable type.");
}
//////////////////////////////////////////////////////////////////////////////////////////
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, size_t 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;
}
//////////////////////////////////////////////////////////////////////////////////////////
#if LDC_LLVM_VER >= 303
static inline void addToAttributes(llvm::AttributeSet &Attrs,
unsigned Idx, llvm::AttrBuilder B)
{
llvm::AttrBuilder Builder(B);
Attrs = Attrs.addAttributes(gIR->context(), Idx,
llvm::AttributeSet::get(gIR->context(), Idx, Builder));
}
#else
static inline void addToAttributes(std::vector<llvm::AttributeWithIndex> &attrs,
unsigned Idx, llvm::Attributes Attr)
{
attrs.push_back(llvm::AttributeWithIndex::get(Idx, Attr));
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////
void DtoBuildDVarArgList(std::vector<LLValue*>& args,
#if LDC_LLVM_VER >= 303
llvm::AttributeSet &attrs,
#else
std::vector<llvm::AttributeWithIndex> &attrs,
#endif
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 (size_t 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 (size_t i=begin,k=0; i<n_arguments; i++,k++)
{
Expression* argexp = static_cast<Expression*>(arguments->data[i]);
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 (size_t i=begin; i<n_arguments; i++)
{
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");
// specify arguments
args.push_back(DtoLoad(typeinfoarrayparam));
if (HAS_ATTRIBUTES(tf->fty.arg_arguments->attrs)) {
addToAttributes(attrs, argidx, tf->fty.arg_arguments->attrs);
}
++argidx;
args.push_back(gIR->ir->CreateBitCast(mem, getPtrToType(LLType::getInt8Ty(gIR->context())), "tmp"));
if (HAS_ATTRIBUTES(tf->fty.arg_argptr->attrs)) {
addToAttributes(attrs, argidx, tf->fty.arg_argptr->attrs);
}
// 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 (HAS_ATTRIBUTES(tf->fty.args[i]->attrs))
{
addToAttributes(attrs, argidx, tf->fty.args[i]->attrs);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// 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
#if LDC_LLVM_VER >= 303
llvm::AttributeSet attrs;
#else
std::vector<llvm::AttributeWithIndex> attrs;
#endif
// return attrs
if (HAS_ATTRIBUTES(tf->fty.ret->attrs))
{
addToAttributes(attrs, 0, tf->fty.ret->attrs);
}
// 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
#if LDC_LLVM_VER >= 303
const unsigned Index = 1;
llvm::AttrBuilder builder(tf->fty.arg_sret->attrs);
assert((builder.contains(llvm::Attribute::StructRet) || builder.contains(llvm::Attribute::InReg))
&& "Sret arg not sret or inreg?");
llvm::AttributeSet as = llvm::AttributeSet::get(gIR->context(), Index, builder);
attrs = attrs.addAttributes(gIR->context(), Index, as);
#else
llvm::AttributeWithIndex Attr;
Attr.Index = 1;
Attr.Attrs = tf->fty.arg_sret->attrs;
#if LDC_LLVM_VER == 302
assert((Attr.Attrs.hasAttribute(llvm::Attributes::StructRet) || Attr.Attrs.hasAttribute(llvm::Attributes::InReg))
&& "Sret arg not sret or inreg?");
#else
assert((Attr.Attrs & (llvm::Attribute::StructRet | llvm::Attribute::InReg))
&& "Sret arg not sret or inreg?");
#endif
attrs.push_back(Attr);
#endif
}
// then comes a context argument...
if(thiscall || delegatecall || nestedcall)
{
#if DMDV2
if (dfnval && (dfnval->func->ident == Id::ensure || dfnval->func->ident == Id::require)) {
// ... which can be the this "context" argument for a contract
// invocation (in D2, we do not generate a full nested contexts
// for __require/__ensure as the needed parameters are passed
// explicitly, while in D1, the normal nested function handling
// mechanisms are used)
LLValue* thisarg = DtoBitCast(DtoLoad(gIR->func()->thisArg), getVoidPtrType());
++argiter;
args.push_back(thisarg);
}
else
#endif
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 && HAS_ATTRIBUTES(tf->fty.arg_this->attrs))
{
addToAttributes(attrs, retinptr ? 2 : 1, tf->fty.arg_this->attrs);
}
else if (tf->fty.arg_nest && HAS_ATTRIBUTES(tf->fty.arg_nest->attrs))
{
addToAttributes(attrs, retinptr ? 2 : 1, tf->fty.arg_nest->attrs);
}
}
// handle the rest of the arguments based on param passing style
// variadic intrinsics need some custom casts
if (va_intrinsic)
{
for (size_t 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);
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 (size_t 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);
}
}
#if LDC_LLVM_VER == 302
LLSmallVector<llvm::Attributes, 10> attrptr(n, llvm::Attributes());
#elif LDC_LLVM_VER < 302
LLSmallVector<llvm::Attributes, 10> attrptr(n, llvm::Attribute::None);
#endif
// do formal params
int beg = argiter-argbegin;
for (size_t 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);
#if LDC_LLVM_VER >= 303
addToAttributes(attrs, beg + 1 + (tf->fty.reverseParams ? n-i-1: i), tf->fty.args[i]->attrs);
#else
attrptr[i] = tf->fty.args[i]->attrs;
#endif
++argiter;
}
// reverse the relevant params as well as the param attrs
if (tf->fty.reverseParams)
{
std::reverse(args.begin() + beg, args.end());
#if LDC_LLVM_VER < 303
std::reverse(attrptr.begin(), attrptr.end());
#endif
}
#if LDC_LLVM_VER < 303
// add attributes
for (size_t i = 0; i < n; i++)
{
if (HAS_ATTRIBUTES(attrptr[i]))
{
addToAttributes(attrs, beg + i + 1, attrptr[i]);
}
}
#endif
// do C varargs
if (n_arguments > n)
{
for (size_t 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;
#if DMDV2
case Tstruct:
if (nextbase->ty == Taarray && !tf->isref)
{
// In the D2 frontend, the associative array type and its
// object.AssociativeArray representation are used
// interchangably in some places. However, AAs are returned
// by value and not in an sret argument, so if the struct
// type will be used, give the return value storage here
// so that we get the right amount of indirections.
LLValue* tmp = DtoAlloca(rbase, ".aalvauetmp");
LLValue* val = DtoInsertValue(
llvm::UndefValue::get(DtoType(rbase)), retllval, 0);
DtoStore(val, tmp);
retllval = tmp;
retinptr = true;
break;
}
// Fall through.
#endif
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(); }
// ---
Logger::println("Unknown return mismatch type, ignoring.");
break;
}
if (Logger::enabled())
Logger::cout() << "final return value: " << *retllval << '\n';
}
}
// set calling convention and parameter attributes
#if LDC_LLVM_VER >= 303
llvm::AttributeSet attrlist = attrs;
#elif LDC_LLVM_VER == 302
llvm::AttrListPtr attrlist = llvm::AttrListPtr::get(gIR->context(),
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
#if LDC_LLVM_VER >= 302
attrlist = llvm::Intrinsic::getAttributes(gIR->context(), static_cast<llvm::Intrinsic::ID>(llfunc->getIntrinsicID()));
#else
attrlist = llvm::Intrinsic::getAttributes(static_cast<llvm::Intrinsic::ID>(llfunc->getIntrinsicID()));
#endif
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);
}