Files
ldc/gen/tocall.cpp
Frits van Bommel b237079854 Fun with parameter attributes: For several of the "synthetic" parameters added
to D functions, we can apply noalias and nocapture. They are sret parameters,
'nest' pointers passed to nested functions, and _argptr:
Nocapture:
 - Sret and nest are nocapture because they don't represent D-level variables,
   and thus the callee can't (validly) obtain a pointer to them, let alone keep
   it around after it returns.
 - _argptr is nocapture because although the callee has access to it as a
   pointer, that pointer is invalidated when it returns.

All three are noalias because they're function-local variables
 - Sret and _argptr are noalias because they're freshly alloca'd memory only
   used for a single function call that's not allowed to keep an aliasing
   pointer to it around (since the parameter is nocapture).
 - 'Nest' is noalias because the callee only ever has access to one such pointer
   per parent function, and every parent function has a different one.

This commit also ensures attributes set on sret, _arguments and _argptr are
propagated to calls to such functions.

It also adds one exception to the general rule that attributes on function types
should propagate to calls: the type of a delegate's function pointer has a
'nest' parameter, but this can either be a true 'nest' (for delegates to nested
functions) or a 'this' (for delegates to member functions). Since 'this' is
neither noalias nor nocapture, and there's generally no way to tell which one it
is, we remove these attributes at the call site if the callee is a delegate.
2009-03-14 22:15:31 +01:00

598 lines
19 KiB
C++

#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/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;
}
//////////////////////////////////////////////////////////////////////////////////////////
unsigned 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<LLValue*>& args, std::vector<llvm::AttributeWithIndex>& attrs, 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);
// 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 = (Expression*)arguments->data[i];
vtypes.push_back(DtoType(argexp->type));
size_t sz = getTypePaddedSize(vtypes.back());
if (sz < PTRSIZE)
vtypes.back() = DtoSize_t();
}
const LLStructType* vtype = LLStructType::get(vtypes);
if (Logger::enabled())
Logger::cout() << "d-variadic argument struct type:\n" << *vtype << '\n';
LLValue* mem = DtoAlloca(vtype,"_argptr_storage");
// store arguments in the struct
for (int i=begin,k=0; i<n_arguments; 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
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);
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 = (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);
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::Int8Ty), "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; i<begin; i++)
{
Argument* fnarg = Argument::getNth(tf->parameters, 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();
// if the type has not yet been processed, do so now
if (calleeType->ir.type == NULL)
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);
unsigned 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<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 = DtoAlloca(argiter->get()->getContainedType(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) && "Sret arg not sret?");
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");
}
assert(ctxarg->getType() == 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;
// For delegates, we can't assume 'nest' is noalias and nocapture
// (like we can with nested functions) since it might actually be
// a 'this', and thus neither attribute generally applies to it.
// TODO: don't remove nocapture if it's a "pure" delegate?
if (delegatecall) {
Attr.Attrs &= ~(llvm::Attribute::NoAlias | llvm::Attribute::NoCapture);
}
// LLVM doesn't like it when no bits are set...
if (Attr.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; i<n_arguments; 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, 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 = Argument::dim(tf->parameters);
LLSmallVector<unsigned, 10> attrptr(n, 0);
// do formal params
int beg = argiter-argbegin;
for (int i=0; i<n; i++)
{
Argument* fnarg = Argument::getNth(tf->parameters, i);
assert(fnarg);
DValue* argval = DtoArgument(fnarg, (Expression*)arguments->data[i]);
if (Logger::enabled()) {
Logger::cout() << "Argument before ABI: " << *argval->getRVal() << '\n';
Logger::cout() << "Argument type before ABI: " << *DtoType(argval->getType()) << '\n';
}
// give the ABI a say
LLValue* arg = tf->fty.putParam(argval->getType(), i, argval);
if (Logger::enabled()) {
Logger::cout() << "Argument after ABI: " << *arg << '\n';
Logger::cout() << "Argument type after ABI: " << *arg->getType() << '\n';
}
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() << "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; i<n_arguments; i++)
{
Argument* fnarg = Argument::getNth(tf->parameters, 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; 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";
#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 are in memory:
// If the function returns a struct, and the return value is not a
// pointer to a struct, store it to a stack slot before continuing.
if (tf->next->ty == Tstruct && !isaPointer(retllval)) {
Logger::println("Storing return value to stack slot");
LLValue* mem = DtoAlloca(retllval->getType());
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:
retllval = DtoAggrPaint(retllval, DtoType(rbase));
break;
case Tclass:
case Taarray:
case Tpointer:
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<LLFunction>(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
// make sure we provide a lvalue back!
if (retinptr)
return new DVarValue(resulttype, retllval);
return new DImValue(resulttype, retllval);
}