mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-03 00:20:40 +03:00

Reimplemented support for nested functions/class using a new approach. Added error on taking address of intrinsic. Fixed problems with the ->syntaxCopy of TypeFunction delegate exp. Removed DtoDType and replaced all uses with ->toBasetype() instead. Removed unused inplace stuff. Fixed a bunch of issues in the runtime unittests, not complete yet. Added mini tests.
370 lines
11 KiB
C++
370 lines
11 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/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();
|
|
Logger::cout() << "delegate: " << *dg << '\n';
|
|
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 = DtoAlloca(vtype,"_argptr_storage");
|
|
|
|
// 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();
|
|
|
|
// 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->retInPtr;
|
|
bool thiscall = tf->usesThis;
|
|
bool delegatecall = (calleeType->toBasetype()->ty == Tdelegate);
|
|
bool nestedcall = tf->usesNest;
|
|
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->retAttrs)
|
|
palist = palist.addAttr(0, tf->retAttrs);
|
|
|
|
// handle implicit arguments
|
|
std::vector<LLValue*> args;
|
|
|
|
// return in hidden ptr is first
|
|
if (retinptr)
|
|
{
|
|
LLValue* retvar = DtoAlloca(argiter->get()->getContainedType(0), ".rettmp");
|
|
++argiter;
|
|
args.push_back(retvar);
|
|
palist = palist.addAttr(1, llvm::ParamAttr::StructRet);
|
|
}
|
|
|
|
// then comes a context argument...
|
|
if(thiscall || delegatecall || nestedcall)
|
|
{
|
|
// ... which can be a 'this' argument
|
|
if (thiscall)
|
|
{
|
|
assert(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(loc, dfnval->func);
|
|
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);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|