mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-13 03:13:13 +01:00
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.
995 lines
29 KiB
C++
995 lines
29 KiB
C++
#include "gen/llvm.h"
|
|
#include "llvm/Support/CFG.h"
|
|
#include "llvm/Intrinsics.h"
|
|
|
|
#include "mtype.h"
|
|
#include "aggregate.h"
|
|
#include "init.h"
|
|
#include "declaration.h"
|
|
#include "template.h"
|
|
#include "module.h"
|
|
#include "statement.h"
|
|
|
|
#include "gen/irstate.h"
|
|
#include "gen/tollvm.h"
|
|
#include "gen/llvmhelpers.h"
|
|
#include "gen/runtime.h"
|
|
#include "gen/arrays.h"
|
|
#include "gen/logger.h"
|
|
#include "gen/functions.h"
|
|
#include "gen/todebug.h"
|
|
#include "gen/classes.h"
|
|
#include "gen/dvalue.h"
|
|
#include "gen/abi.h"
|
|
|
|
using namespace llvm::Attribute;
|
|
|
|
const llvm::FunctionType* DtoFunctionType(Type* type, Type* thistype, Type* nesttype, bool ismain)
|
|
{
|
|
if (Logger::enabled())
|
|
Logger::println("DtoFunctionType(%s)", type->toChars());
|
|
LOG_SCOPE
|
|
// sanity check
|
|
assert(type->ty == Tfunction);
|
|
TypeFunction* f = (TypeFunction*)type;
|
|
|
|
// already built ?
|
|
if (type->ir.type != NULL) {
|
|
//assert(f->fty != NULL);
|
|
return llvm::cast<llvm::FunctionType>(type->ir.type->get());
|
|
}
|
|
|
|
if (f->linkage != LINKintrinsic) {
|
|
// Tell the ABI we're resolving a new function type
|
|
gABI->newFunctionType(f);
|
|
}
|
|
|
|
// start new ir funcTy
|
|
f->fty.reset();
|
|
|
|
// llvm idx counter
|
|
size_t lidx = 0;
|
|
|
|
// main needs a little special handling
|
|
if (ismain)
|
|
{
|
|
f->fty.ret = new IrFuncTyArg(Type::tint32, false);
|
|
}
|
|
// sane return value
|
|
else
|
|
{
|
|
Type* rt = f->next;
|
|
unsigned a = 0;
|
|
// sret return
|
|
if (f->linkage != LINKintrinsic)
|
|
if (gABI->returnInArg(f))
|
|
{
|
|
f->fty.arg_sret = new IrFuncTyArg(rt, true, StructRet | NoAlias | NoCapture);
|
|
rt = Type::tvoid;
|
|
lidx++;
|
|
}
|
|
// sext/zext return
|
|
else if (unsigned se = DtoShouldExtend(rt))
|
|
{
|
|
a = se;
|
|
}
|
|
f->fty.ret = new IrFuncTyArg(rt, false, a);
|
|
}
|
|
lidx++;
|
|
|
|
// member functions
|
|
if (thistype)
|
|
{
|
|
bool toref = (thistype->toBasetype()->ty == Tstruct);
|
|
f->fty.arg_this = new IrFuncTyArg(thistype, toref);
|
|
lidx++;
|
|
}
|
|
|
|
// and nested functions
|
|
else if (nesttype)
|
|
{
|
|
f->fty.arg_nest = new IrFuncTyArg(nesttype, false, NoAlias | NoCapture);
|
|
lidx++;
|
|
}
|
|
|
|
// vararg functions are special too
|
|
if (f->varargs)
|
|
{
|
|
if (f->linkage == LINKd)
|
|
{
|
|
// d style with hidden args
|
|
// 2 (array) is handled by the frontend
|
|
if (f->varargs == 1)
|
|
{
|
|
// _arguments
|
|
f->fty.arg_arguments = new IrFuncTyArg(Type::typeinfo->type->arrayOf(), false);
|
|
lidx++;
|
|
// _argptr
|
|
f->fty.arg_argptr = new IrFuncTyArg(Type::tvoid->pointerTo(), false, NoAlias | NoCapture);
|
|
lidx++;
|
|
}
|
|
}
|
|
else if (f->linkage == LINKc)
|
|
{
|
|
f->fty.c_vararg = true;
|
|
}
|
|
else
|
|
{
|
|
type->error(0, "invalid linkage for variadic function");
|
|
fatal();
|
|
}
|
|
}
|
|
|
|
// if this _Dmain() doesn't have an argument, we force it to have one
|
|
int nargs = Argument::dim(f->parameters);
|
|
|
|
if (ismain && nargs == 0)
|
|
{
|
|
Type* mainargs = Type::tchar->arrayOf()->arrayOf();
|
|
f->fty.args.push_back(new IrFuncTyArg(mainargs, false));
|
|
lidx++;
|
|
}
|
|
// add explicit parameters
|
|
else for (int i = 0; i < nargs; i++)
|
|
{
|
|
// get argument
|
|
Argument* arg = Argument::getNth(f->parameters, i);
|
|
|
|
// reference semantics? ref, out and static arrays are
|
|
bool byref = (arg->storageClass & (STCref|STCout)) || (arg->type->toBasetype()->ty == Tsarray);
|
|
|
|
Type* argtype = arg->type;
|
|
unsigned a = 0;
|
|
|
|
// handle lazy args
|
|
if (arg->storageClass & STClazy)
|
|
{
|
|
Logger::println("lazy param");
|
|
TypeFunction *ltf = new TypeFunction(NULL, arg->type, 0, LINKd);
|
|
TypeDelegate *ltd = new TypeDelegate(ltf);
|
|
argtype = ltd;
|
|
}
|
|
// byval
|
|
else if (f->linkage != LINKintrinsic
|
|
&& gABI->passByVal(argtype))
|
|
{
|
|
if (!byref) a |= llvm::Attribute::ByVal;
|
|
byref = true;
|
|
}
|
|
// sext/zext
|
|
else if (!byref)
|
|
{
|
|
a |= DtoShouldExtend(argtype);
|
|
}
|
|
|
|
f->fty.args.push_back(new IrFuncTyArg(argtype, byref, a));
|
|
lidx++;
|
|
}
|
|
|
|
if (f->linkage != LINKintrinsic) {
|
|
// let the abi rewrite the types as necesary
|
|
gABI->rewriteFunctionType(f);
|
|
|
|
// Tell the ABI we're done with this function type
|
|
gABI->doneWithFunctionType();
|
|
}
|
|
|
|
// build the function type
|
|
std::vector<const LLType*> argtypes;
|
|
argtypes.reserve(lidx);
|
|
|
|
if (f->fty.arg_sret) argtypes.push_back(f->fty.arg_sret->ltype);
|
|
if (f->fty.arg_this) argtypes.push_back(f->fty.arg_this->ltype);
|
|
if (f->fty.arg_nest) argtypes.push_back(f->fty.arg_nest->ltype);
|
|
if (f->fty.arg_arguments) argtypes.push_back(f->fty.arg_arguments->ltype);
|
|
if (f->fty.arg_argptr) argtypes.push_back(f->fty.arg_argptr->ltype);
|
|
|
|
size_t beg = argtypes.size();
|
|
size_t nargs2 = f->fty.args.size();
|
|
for (size_t i = 0; i < nargs2; i++)
|
|
{
|
|
argtypes.push_back(f->fty.args[i]->ltype);
|
|
}
|
|
|
|
// reverse params?
|
|
if (f->fty.reverseParams && nargs2 > 1)
|
|
{
|
|
std::reverse(argtypes.begin() + beg, argtypes.end());
|
|
}
|
|
|
|
llvm::FunctionType* functype = llvm::FunctionType::get(f->fty.ret->ltype, argtypes, f->fty.c_vararg);
|
|
f->ir.type = new llvm::PATypeHolder(functype);
|
|
|
|
Logger::cout() << "Final function type: " << *functype << "\n";
|
|
|
|
return functype;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static const llvm::FunctionType* DtoVaFunctionType(FuncDeclaration* fdecl)
|
|
{
|
|
// type has already been resolved
|
|
if (fdecl->type->ir.type != 0) {
|
|
return llvm::cast<llvm::FunctionType>(fdecl->type->ir.type->get());
|
|
}
|
|
|
|
TypeFunction* f = (TypeFunction*)fdecl->type;
|
|
const llvm::FunctionType* fty = 0;
|
|
|
|
// create new ir funcTy
|
|
f->fty.reset();
|
|
f->fty.ret = new IrFuncTyArg(Type::tvoid, false);
|
|
|
|
f->fty.args.push_back(new IrFuncTyArg(Type::tvoid->pointerTo(), false));
|
|
|
|
if (fdecl->llvmInternal == LLVMva_start)
|
|
fty = GET_INTRINSIC_DECL(vastart)->getFunctionType();
|
|
else if (fdecl->llvmInternal == LLVMva_copy) {
|
|
fty = GET_INTRINSIC_DECL(vacopy)->getFunctionType();
|
|
f->fty.args.push_back(new IrFuncTyArg(Type::tvoid->pointerTo(), false));
|
|
}
|
|
else if (fdecl->llvmInternal == LLVMva_end)
|
|
fty = GET_INTRINSIC_DECL(vaend)->getFunctionType();
|
|
assert(fty);
|
|
|
|
f->ir.type = new llvm::PATypeHolder(fty);
|
|
return fty;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const llvm::FunctionType* DtoFunctionType(FuncDeclaration* fdecl)
|
|
{
|
|
// handle for C vararg intrinsics
|
|
if (fdecl->isVaIntrinsic())
|
|
return DtoVaFunctionType(fdecl);
|
|
|
|
// type has already been resolved
|
|
if (fdecl->type->ir.type != 0)
|
|
return llvm::cast<llvm::FunctionType>(fdecl->type->ir.type->get());
|
|
|
|
Type *dthis=0, *dnest=0;
|
|
|
|
if (fdecl->needThis()) {
|
|
if (AggregateDeclaration* ad = fdecl->isMember2()) {
|
|
Logger::println("isMember = this is: %s", ad->type->toChars());
|
|
dthis = ad->type;
|
|
const LLType* thisty = DtoType(dthis);
|
|
//Logger::cout() << "this llvm type: " << *thisty << '\n';
|
|
if (isaStruct(thisty) || (!gIR->structs.empty() && thisty == gIR->topstruct()->type->ir.type->get()))
|
|
thisty = getPtrToType(thisty);
|
|
}
|
|
else {
|
|
Logger::println("chars: %s type: %s kind: %s", fdecl->toChars(), fdecl->type->toChars(), fdecl->kind());
|
|
assert(0);
|
|
}
|
|
}
|
|
else if (fdecl->isNested()) {
|
|
dnest = Type::tvoid->pointerTo();
|
|
}
|
|
|
|
const llvm::FunctionType* functype = DtoFunctionType(fdecl->type, dthis, dnest, fdecl->isMain());
|
|
|
|
return functype;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static llvm::Function* DtoDeclareVaFunction(FuncDeclaration* fdecl)
|
|
{
|
|
TypeFunction* f = (TypeFunction*)fdecl->type->toBasetype();
|
|
const llvm::FunctionType* fty = DtoVaFunctionType(fdecl);
|
|
llvm::Function* func = 0;
|
|
|
|
if (fdecl->llvmInternal == LLVMva_start)
|
|
func = GET_INTRINSIC_DECL(vastart);
|
|
else if (fdecl->llvmInternal == LLVMva_copy)
|
|
func = GET_INTRINSIC_DECL(vacopy);
|
|
else if (fdecl->llvmInternal == LLVMva_end)
|
|
func = GET_INTRINSIC_DECL(vaend);
|
|
assert(func);
|
|
|
|
fdecl->ir.irFunc->func = func;
|
|
return func;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoResolveFunction(FuncDeclaration* fdecl)
|
|
{
|
|
if (!global.params.useUnitTests && fdecl->isUnitTestDeclaration()) {
|
|
return; // ignore declaration completely
|
|
}
|
|
|
|
// is imported and we don't have access?
|
|
if (fdecl->getModule() != gIR->dmodule)
|
|
{
|
|
if (fdecl->prot() == PROTprivate)
|
|
return;
|
|
}
|
|
|
|
if (fdecl->ir.resolved) return;
|
|
fdecl->ir.resolved = true;
|
|
|
|
Logger::println("DtoResolveFunction(%s): %s", fdecl->toPrettyChars(), fdecl->loc.toChars());
|
|
LOG_SCOPE;
|
|
|
|
//printf("resolve function: %s\n", fdecl->toPrettyChars());
|
|
|
|
if (fdecl->parent)
|
|
if (TemplateInstance* tinst = fdecl->parent->isTemplateInstance())
|
|
{
|
|
TemplateDeclaration* tempdecl = tinst->tempdecl;
|
|
if (tempdecl->llvmInternal == LLVMva_arg)
|
|
{
|
|
Logger::println("magic va_arg found");
|
|
fdecl->llvmInternal = LLVMva_arg;
|
|
fdecl->ir.declared = true;
|
|
fdecl->ir.initialized = true;
|
|
fdecl->ir.defined = true;
|
|
return; // this gets mapped to an instruction so a declaration makes no sence
|
|
}
|
|
else if (tempdecl->llvmInternal == LLVMva_start)
|
|
{
|
|
Logger::println("magic va_start found");
|
|
fdecl->llvmInternal = LLVMva_start;
|
|
}
|
|
else if (tempdecl->llvmInternal == LLVMintrinsic)
|
|
{
|
|
Logger::println("overloaded intrinsic found");
|
|
fdecl->llvmInternal = LLVMintrinsic;
|
|
DtoOverloadedIntrinsicName(tinst, tempdecl, fdecl->intrinsicName);
|
|
fdecl->linkage = LINKintrinsic;
|
|
((TypeFunction*)fdecl->type)->linkage = LINKintrinsic;
|
|
}
|
|
}
|
|
|
|
DtoFunctionType(fdecl);
|
|
|
|
// queue declaration
|
|
if (!fdecl->isAbstract())
|
|
gIR->declareList.push_back(fdecl);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void set_param_attrs(TypeFunction* f, llvm::Function* func, FuncDeclaration* fdecl)
|
|
{
|
|
int funcNumArgs = func->getArgumentList().size();
|
|
|
|
LLSmallVector<llvm::AttributeWithIndex, 9> attrs;
|
|
llvm::AttributeWithIndex PAWI;
|
|
|
|
int idx = 0;
|
|
|
|
// handle implicit args
|
|
#define ADD_PA(X) \
|
|
if (f->fty.X) { \
|
|
if (f->fty.X->attrs) { \
|
|
PAWI.Index = idx; \
|
|
PAWI.Attrs = f->fty.X->attrs; \
|
|
attrs.push_back(PAWI); \
|
|
} \
|
|
idx++; \
|
|
}
|
|
|
|
ADD_PA(ret)
|
|
ADD_PA(arg_sret)
|
|
ADD_PA(arg_this)
|
|
ADD_PA(arg_nest)
|
|
ADD_PA(arg_arguments)
|
|
ADD_PA(arg_argptr)
|
|
|
|
#undef ADD_PA
|
|
|
|
// set attrs on the rest of the arguments
|
|
size_t n = Argument::dim(f->parameters);
|
|
LLSmallVector<unsigned,8> attrptr(n, 0);
|
|
|
|
for (size_t k = 0; k < n; ++k)
|
|
{
|
|
Argument* fnarg = Argument::getNth(f->parameters, k);
|
|
assert(fnarg);
|
|
|
|
attrptr[k] = f->fty.args[k]->attrs;
|
|
}
|
|
|
|
// reverse params?
|
|
if (f->fty.reverseParams)
|
|
{
|
|
std::reverse(attrptr.begin(), attrptr.end());
|
|
}
|
|
|
|
// build rest of attrs list
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
if (attrptr[i])
|
|
{
|
|
PAWI.Index = idx+i;
|
|
PAWI.Attrs = attrptr[i];
|
|
attrs.push_back(PAWI);
|
|
}
|
|
}
|
|
|
|
llvm::AttrListPtr attrlist = llvm::AttrListPtr::get(attrs.begin(), attrs.end());
|
|
func->setAttributes(attrlist);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoDeclareFunction(FuncDeclaration* fdecl)
|
|
{
|
|
if (fdecl->ir.declared) return;
|
|
fdecl->ir.declared = true;
|
|
|
|
Logger::println("DtoDeclareFunction(%s): %s", fdecl->toPrettyChars(), fdecl->loc.toChars());
|
|
LOG_SCOPE;
|
|
|
|
//printf("declare function: %s\n", fdecl->toPrettyChars());
|
|
|
|
// intrinsic sanity check
|
|
if (fdecl->llvmInternal == LLVMintrinsic && fdecl->fbody) {
|
|
error(fdecl->loc, "intrinsics cannot have function bodies");
|
|
fatal();
|
|
}
|
|
|
|
// get TypeFunction*
|
|
Type* t = fdecl->type->toBasetype();
|
|
TypeFunction* f = (TypeFunction*)t;
|
|
|
|
bool declareOnly = !mustDefineSymbol(fdecl);
|
|
|
|
if (fdecl->llvmInternal == LLVMva_start)
|
|
declareOnly = true;
|
|
|
|
if (!fdecl->ir.irFunc) {
|
|
fdecl->ir.irFunc = new IrFunction(fdecl);
|
|
}
|
|
|
|
// mangled name
|
|
const char* mangled_name;
|
|
if (fdecl->llvmInternal == LLVMintrinsic)
|
|
mangled_name = fdecl->intrinsicName.c_str();
|
|
else
|
|
mangled_name = fdecl->mangle();
|
|
|
|
llvm::Function* vafunc = 0;
|
|
if (fdecl->isVaIntrinsic())
|
|
vafunc = DtoDeclareVaFunction(fdecl);
|
|
|
|
// construct function
|
|
const llvm::FunctionType* functype = DtoFunctionType(fdecl);
|
|
llvm::Function* func = vafunc ? vafunc : gIR->module->getFunction(mangled_name);
|
|
if (!func)
|
|
func = llvm::Function::Create(functype, DtoLinkage(fdecl), mangled_name, gIR->module);
|
|
|
|
// add func to IRFunc
|
|
fdecl->ir.irFunc->func = func;
|
|
|
|
// calling convention
|
|
if (!vafunc && fdecl->llvmInternal != LLVMintrinsic)
|
|
func->setCallingConv(DtoCallingConv(fdecl->loc, f->linkage));
|
|
else // fall back to C, it should be the right thing to do
|
|
func->setCallingConv(llvm::CallingConv::C);
|
|
|
|
fdecl->ir.irFunc->func = func;
|
|
assert(llvm::isa<llvm::FunctionType>(f->ir.type->get()));
|
|
|
|
// parameter attributes
|
|
if (!fdecl->isIntrinsic()) {
|
|
set_param_attrs(f, func, fdecl);
|
|
}
|
|
|
|
// main
|
|
if (fdecl->isMain()) {
|
|
gIR->mainFunc = func;
|
|
}
|
|
|
|
// static ctor
|
|
if (fdecl->isStaticCtorDeclaration()) {
|
|
if (mustDefineSymbol(fdecl)) {
|
|
gIR->ctors.push_back(fdecl);
|
|
}
|
|
}
|
|
// static dtor
|
|
else if (fdecl->isStaticDtorDeclaration()) {
|
|
if (mustDefineSymbol(fdecl)) {
|
|
gIR->dtors.push_back(fdecl);
|
|
}
|
|
}
|
|
|
|
// we never reference parameters of function prototypes
|
|
std::string str;
|
|
if (!declareOnly)
|
|
{
|
|
// name parameters
|
|
llvm::Function::arg_iterator iarg = func->arg_begin();
|
|
|
|
if (f->fty.arg_sret) {
|
|
iarg->setName(".sret_arg");
|
|
fdecl->ir.irFunc->retArg = iarg;
|
|
++iarg;
|
|
}
|
|
|
|
if (f->fty.arg_this) {
|
|
iarg->setName(".this_arg");
|
|
fdecl->ir.irFunc->thisArg = iarg;
|
|
assert(fdecl->ir.irFunc->thisArg);
|
|
++iarg;
|
|
}
|
|
else if (f->fty.arg_nest) {
|
|
iarg->setName(".nest_arg");
|
|
fdecl->ir.irFunc->nestArg = iarg;
|
|
assert(fdecl->ir.irFunc->nestArg);
|
|
++iarg;
|
|
}
|
|
|
|
if (f->fty.arg_argptr) {
|
|
iarg->setName("._arguments");
|
|
fdecl->ir.irFunc->_arguments = iarg;
|
|
++iarg;
|
|
iarg->setName("._argptr");
|
|
fdecl->ir.irFunc->_argptr = iarg;
|
|
++iarg;
|
|
}
|
|
|
|
int k = 0;
|
|
|
|
for (; iarg != func->arg_end(); ++iarg)
|
|
{
|
|
if (fdecl->parameters && fdecl->parameters->dim > k)
|
|
{
|
|
Dsymbol* argsym;
|
|
if (f->fty.reverseParams)
|
|
argsym = (Dsymbol*)fdecl->parameters->data[fdecl->parameters->dim-k-1];
|
|
else
|
|
argsym = (Dsymbol*)fdecl->parameters->data[k];
|
|
|
|
VarDeclaration* argvd = argsym->isVarDeclaration();
|
|
assert(argvd);
|
|
assert(!argvd->ir.irLocal);
|
|
argvd->ir.irLocal = new IrLocal(argvd);
|
|
argvd->ir.irLocal->value = iarg;
|
|
|
|
str = argvd->ident->toChars();
|
|
str.append("_arg");
|
|
iarg->setName(str);
|
|
|
|
k++;
|
|
}
|
|
else
|
|
{
|
|
iarg->setName("unnamed");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fdecl->isUnitTestDeclaration() && !declareOnly)
|
|
gIR->unitTests.push_back(fdecl);
|
|
|
|
if (!declareOnly)
|
|
gIR->defineList.push_back(fdecl);
|
|
else
|
|
assert(func->getLinkage() != llvm::GlobalValue::InternalLinkage);
|
|
|
|
if (Logger::enabled())
|
|
Logger::cout() << "func decl: " << *func << '\n';
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// FIXME: this isn't too pretty!
|
|
|
|
void DtoDefineFunction(FuncDeclaration* fd)
|
|
{
|
|
if (fd->ir.defined) return;
|
|
fd->ir.defined = true;
|
|
|
|
assert(fd->ir.declared);
|
|
|
|
if (Logger::enabled())
|
|
Logger::println("DtoDefineFunc(%s): %s", fd->toPrettyChars(), fd->loc.toChars());
|
|
LOG_SCOPE;
|
|
|
|
// if this function is naked, we take over right away! no standard processing!
|
|
if (fd->naked)
|
|
{
|
|
DtoDefineNakedFunction(fd);
|
|
return;
|
|
}
|
|
|
|
// debug info
|
|
if (global.params.symdebug) {
|
|
fd->ir.irFunc->diSubprogram = DtoDwarfSubProgram(fd);
|
|
}
|
|
|
|
Type* t = fd->type->toBasetype();
|
|
TypeFunction* f = (TypeFunction*)t;
|
|
assert(f->ir.type);
|
|
|
|
llvm::Function* func = fd->ir.irFunc->func;
|
|
const llvm::FunctionType* functype = func->getFunctionType();
|
|
|
|
// sanity check
|
|
assert(mustDefineSymbol(fd));
|
|
|
|
// set module owner
|
|
fd->ir.DModule = gIR->dmodule;
|
|
|
|
// is there a body?
|
|
if (fd->fbody == NULL)
|
|
return;
|
|
|
|
Logger::println("Doing function body for: %s", fd->toChars());
|
|
assert(fd->ir.irFunc);
|
|
IrFunction* irfunction = fd->ir.irFunc;
|
|
gIR->functions.push_back(irfunction);
|
|
|
|
if (fd->isMain())
|
|
gIR->emitMain = true;
|
|
|
|
std::string entryname("entry");
|
|
|
|
llvm::BasicBlock* beginbb = llvm::BasicBlock::Create(entryname,func);
|
|
llvm::BasicBlock* endbb = llvm::BasicBlock::Create("endentry",func);
|
|
|
|
//assert(gIR->scopes.empty());
|
|
gIR->scopes.push_back(IRScope(beginbb, endbb));
|
|
|
|
// create alloca point
|
|
llvm::Instruction* allocaPoint = new llvm::AllocaInst(LLType::Int32Ty, "alloca point", beginbb);
|
|
irfunction->allocapoint = allocaPoint;
|
|
|
|
// debug info - after all allocas, but before any llvm.dbg.declare etc
|
|
if (global.params.symdebug) DtoDwarfFuncStart(fd);
|
|
|
|
// need result variable?
|
|
if (fd->vresult) {
|
|
Logger::println("vresult value");
|
|
fd->vresult->ir.irLocal = new IrLocal(fd->vresult);
|
|
fd->vresult->ir.irLocal->value = DtoAlloca(DtoType(fd->vresult->type), "function_vresult");
|
|
}
|
|
|
|
// this hack makes sure the frame pointer elimination optimization is disabled.
|
|
// this this eliminates a bunch of inline asm related issues.
|
|
if (fd->inlineAsm)
|
|
{
|
|
// emit a call to llvm_eh_unwind_init
|
|
LLFunction* hack = GET_INTRINSIC_DECL(eh_unwind_init);
|
|
gIR->ir->CreateCall(hack, "");
|
|
}
|
|
|
|
// give the 'this' argument storage and debug info
|
|
if (f->fty.arg_this)
|
|
{
|
|
LLValue* thisvar = irfunction->thisArg;
|
|
assert(thisvar);
|
|
|
|
LLValue* thismem = DtoAlloca(thisvar->getType(), "this");
|
|
DtoStore(thisvar, thismem);
|
|
irfunction->thisArg = thismem;
|
|
|
|
assert(!fd->vthis->ir.irLocal);
|
|
fd->vthis->ir.irLocal = new IrLocal(fd->vthis);
|
|
fd->vthis->ir.irLocal->value = thismem;
|
|
|
|
if (global.params.symdebug)
|
|
DtoDwarfLocalVariable(thismem, fd->vthis);
|
|
|
|
#if DMDV2
|
|
if (fd->vthis->nestedrefs.dim)
|
|
#else
|
|
if (fd->vthis->nestedref)
|
|
#endif
|
|
{
|
|
fd->nestedVars.insert(fd->vthis);
|
|
}
|
|
}
|
|
|
|
// give arguments storage
|
|
// and debug info
|
|
if (fd->parameters)
|
|
{
|
|
size_t n = f->fty.args.size();
|
|
assert(n == fd->parameters->dim);
|
|
for (int i=0; i < n; ++i)
|
|
{
|
|
Dsymbol* argsym = (Dsymbol*)fd->parameters->data[i];
|
|
VarDeclaration* vd = argsym->isVarDeclaration();
|
|
assert(vd);
|
|
|
|
IrLocal* irloc = vd->ir.irLocal;
|
|
assert(irloc);
|
|
|
|
#if DMDV2
|
|
if (vd->nestedrefs.dim)
|
|
#else
|
|
if (vd->nestedref)
|
|
#endif
|
|
{
|
|
fd->nestedVars.insert(vd);
|
|
}
|
|
|
|
bool refout = vd->storage_class & (STCref | STCout);
|
|
bool lazy = vd->storage_class & STClazy;
|
|
|
|
if (!refout && (!f->fty.args[i]->byref || lazy))
|
|
{
|
|
// alloca a stack slot for this first class value arg
|
|
const LLType* argt;
|
|
if (lazy)
|
|
argt = irloc->value->getType();
|
|
else
|
|
argt = DtoType(vd->type);
|
|
LLValue* mem = DtoAlloca(argt, vd->ident->toChars());
|
|
|
|
// let the abi transform the argument back first
|
|
DImValue arg_dval(vd->type, irloc->value);
|
|
f->fty.getParam(vd->type, i, &arg_dval, mem);
|
|
|
|
// set the arg var value to the alloca
|
|
irloc->value = mem;
|
|
}
|
|
|
|
if (global.params.symdebug && !(isaArgument(irloc->value) && !isaArgument(irloc->value)->hasByValAttr()) && !refout)
|
|
DtoDwarfLocalVariable(irloc->value, vd);
|
|
}
|
|
}
|
|
|
|
// need result variable? (nested)
|
|
#if DMDV2
|
|
if (fd->vresult && fd->vresult->nestedrefs.dim) {
|
|
#else
|
|
if (fd->vresult && fd->vresult->nestedref) {
|
|
#endif
|
|
Logger::println("nested vresult value: %s", fd->vresult->toChars());
|
|
fd->nestedVars.insert(fd->vresult);
|
|
}
|
|
|
|
// construct nested variables array
|
|
if (!fd->nestedVars.empty())
|
|
{
|
|
Logger::println("has nested frame");
|
|
// start with adding all enclosing parent frames until a static parent is reached
|
|
int nparelems = 0;
|
|
if (!fd->isStatic())
|
|
{
|
|
Dsymbol* par = fd->toParent2();
|
|
while (par)
|
|
{
|
|
if (FuncDeclaration* parfd = par->isFuncDeclaration())
|
|
{
|
|
nparelems += parfd->nestedVars.size();
|
|
// stop at first static
|
|
if (parfd->isStatic())
|
|
break;
|
|
}
|
|
else if (ClassDeclaration* parcd = par->isClassDeclaration())
|
|
{
|
|
// nothing needed
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
par = par->toParent2();
|
|
}
|
|
}
|
|
int nelems = fd->nestedVars.size() + nparelems;
|
|
|
|
// make array type for nested vars
|
|
const LLType* nestedVarsTy = LLArrayType::get(getVoidPtrType(), nelems);
|
|
|
|
// alloca it
|
|
LLValue* nestedVars = DtoAlloca(nestedVarsTy, ".nested_vars");
|
|
|
|
// copy parent frame into beginning
|
|
if (nparelems)
|
|
{
|
|
LLValue* src = irfunction->nestArg;
|
|
if (!src)
|
|
{
|
|
assert(irfunction->thisArg);
|
|
assert(fd->isMember2());
|
|
LLValue* thisval = DtoLoad(irfunction->thisArg);
|
|
ClassDeclaration* cd = fd->isMember2()->isClassDeclaration();
|
|
assert(cd);
|
|
assert(cd->vthis);
|
|
src = DtoLoad(DtoGEPi(thisval, 0,cd->vthis->ir.irField->index, ".vthis"));
|
|
}
|
|
DtoMemCpy(nestedVars, src, DtoConstSize_t(nparelems*PTRSIZE));
|
|
}
|
|
|
|
// store in IrFunction
|
|
irfunction->nestedVar = nestedVars;
|
|
|
|
// go through all nested vars and assign indices
|
|
int idx = nparelems;
|
|
for (std::set<VarDeclaration*>::iterator i=fd->nestedVars.begin(); i!=fd->nestedVars.end(); ++i)
|
|
{
|
|
VarDeclaration* vd = *i;
|
|
if (!vd->ir.irLocal)
|
|
vd->ir.irLocal = new IrLocal(vd);
|
|
|
|
if (vd->isParameter())
|
|
{
|
|
Logger::println("nested param: %s", vd->toChars());
|
|
LLValue* gep = DtoGEPi(nestedVars, 0, idx);
|
|
LLValue* val = DtoBitCast(vd->ir.irLocal->value, getVoidPtrType());
|
|
DtoStore(val, gep);
|
|
}
|
|
else
|
|
{
|
|
Logger::println("nested var: %s", vd->toChars());
|
|
}
|
|
|
|
vd->ir.irLocal->nestedIndex = idx++;
|
|
}
|
|
|
|
// fixup nested result variable
|
|
#if DMDV2
|
|
if (fd->vresult && fd->vresult->nestedrefs.dim) {
|
|
#else
|
|
if (fd->vresult && fd->vresult->nestedref) {
|
|
#endif
|
|
Logger::println("nested vresult value: %s", fd->vresult->toChars());
|
|
LLValue* gep = DtoGEPi(nestedVars, 0, fd->vresult->ir.irLocal->nestedIndex);
|
|
LLValue* val = DtoBitCast(fd->vresult->ir.irLocal->value, getVoidPtrType());
|
|
DtoStore(val, gep);
|
|
}
|
|
}
|
|
|
|
// copy _argptr and _arguments to a memory location
|
|
if (f->linkage == LINKd && f->varargs == 1)
|
|
{
|
|
// _argptr
|
|
LLValue* argptrmem = DtoAlloca(fd->ir.irFunc->_argptr->getType(), "_argptr_mem");
|
|
new llvm::StoreInst(fd->ir.irFunc->_argptr, argptrmem, gIR->scopebb());
|
|
fd->ir.irFunc->_argptr = argptrmem;
|
|
|
|
// _arguments
|
|
LLValue* argumentsmem = DtoAlloca(fd->ir.irFunc->_arguments->getType(), "_arguments_mem");
|
|
new llvm::StoreInst(fd->ir.irFunc->_arguments, argumentsmem, gIR->scopebb());
|
|
fd->ir.irFunc->_arguments = argumentsmem;
|
|
}
|
|
|
|
// output function body
|
|
fd->fbody->toIR(gIR);
|
|
|
|
// TODO: clean up this mess
|
|
|
|
// std::cout << *func << std::endl;
|
|
|
|
// llvm requires all basic blocks to end with a TerminatorInst but DMD does not put a return statement
|
|
// in automatically, so we do it here.
|
|
if (!gIR->scopereturned()) {
|
|
// pass the previous block into this block
|
|
if (global.params.symdebug) DtoDwarfFuncEnd(fd);
|
|
if (func->getReturnType() == LLType::VoidTy) {
|
|
llvm::ReturnInst::Create(gIR->scopebb());
|
|
}
|
|
else {
|
|
if (!fd->isMain())
|
|
{
|
|
AsmBlockStatement* asmb = fd->fbody->endsWithAsm();
|
|
if (asmb) {
|
|
assert(asmb->abiret);
|
|
llvm::ReturnInst::Create(asmb->abiret, gIR->scopebb());
|
|
}
|
|
else {
|
|
llvm::ReturnInst::Create(llvm::UndefValue::get(func->getReturnType()), gIR->scopebb());
|
|
}
|
|
}
|
|
else
|
|
llvm::ReturnInst::Create(llvm::Constant::getNullValue(func->getReturnType()), gIR->scopebb());
|
|
}
|
|
}
|
|
|
|
// std::cout << *func << std::endl;
|
|
|
|
// erase alloca point
|
|
allocaPoint->eraseFromParent();
|
|
allocaPoint = 0;
|
|
gIR->func()->allocapoint = 0;
|
|
|
|
gIR->scopes.pop_back();
|
|
|
|
// get rid of the endentry block, it's never used
|
|
assert(!func->getBasicBlockList().empty());
|
|
func->getBasicBlockList().pop_back();
|
|
|
|
gIR->functions.pop_back();
|
|
|
|
// std::cout << *func << std::endl;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const llvm::FunctionType* DtoBaseFunctionType(FuncDeclaration* fdecl)
|
|
{
|
|
Dsymbol* parent = fdecl->toParent();
|
|
ClassDeclaration* cd = parent->isClassDeclaration();
|
|
assert(cd);
|
|
|
|
FuncDeclaration* f = fdecl;
|
|
|
|
while (cd)
|
|
{
|
|
ClassDeclaration* base = cd->baseClass;
|
|
if (!base)
|
|
break;
|
|
FuncDeclaration* f2 = base->findFunc(fdecl->ident, (TypeFunction*)fdecl->type);
|
|
if (f2) {
|
|
f = f2;
|
|
cd = base;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
DtoResolveDsymbol(f);
|
|
return llvm::cast<llvm::FunctionType>(DtoType(f->type));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DtoArgument(Argument* fnarg, Expression* argexp)
|
|
{
|
|
Logger::println("DtoArgument");
|
|
LOG_SCOPE;
|
|
|
|
DValue* arg = argexp->toElem(gIR);
|
|
|
|
// ref/out arg
|
|
if (fnarg && (fnarg->storageClass & (STCref | STCout)))
|
|
{
|
|
if (arg->isVar() || arg->isLRValue())
|
|
arg = new DImValue(argexp->type, arg->getLVal());
|
|
else
|
|
arg = new DImValue(argexp->type, arg->getRVal());
|
|
}
|
|
// lazy arg
|
|
else if (fnarg && (fnarg->storageClass & STClazy))
|
|
{
|
|
assert(argexp->type->toBasetype()->ty == Tdelegate);
|
|
assert(!arg->isLVal());
|
|
return arg;
|
|
}
|
|
// byval arg, but expr has no storage yet
|
|
else if (DtoIsPassedByRef(argexp->type) && (arg->isSlice() || arg->isNull()))
|
|
{
|
|
LLValue* alloc = DtoAlloca(DtoType(argexp->type), ".tmp_arg");
|
|
DVarValue* vv = new DVarValue(argexp->type, alloc);
|
|
DtoAssign(argexp->loc, vv, arg);
|
|
arg = vv;
|
|
}
|
|
|
|
return arg;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoVariadicArgument(Expression* argexp, LLValue* dst)
|
|
{
|
|
Logger::println("DtoVariadicArgument");
|
|
LOG_SCOPE;
|
|
DVarValue vv(argexp->type, dst);
|
|
DtoAssign(argexp->loc, &vv, argexp->toElem(gIR));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool FuncDeclaration::isIntrinsic()
|
|
{
|
|
return (llvmInternal == LLVMintrinsic || isVaIntrinsic());
|
|
}
|
|
|
|
bool FuncDeclaration::isVaIntrinsic()
|
|
{
|
|
return (llvmInternal == LLVMva_start ||
|
|
llvmInternal == LLVMva_copy ||
|
|
llvmInternal == LLVMva_end);
|
|
}
|