ldc/gen/functions.cpp
Tomas Lindquist Olsen f46f865375 Removed KDevelop3 project files, CMake can generate them just fine!
Fixed function literals in static initializers.
Changed alignment of delegates from 2*PTRSIZE to just PTRSIZE.
Changed errors to go to stderr instead of stdout.
Fairly major rewriting of struct/union/class handling, STILL A BIT BUGGY !!!
2008-11-29 21:25:43 +01:00

1018 lines
31 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 <algorithm>
const llvm::FunctionType* DtoFunctionType(Type* type, const LLType* thistype, const LLType* nesttype, bool ismain)
{
assert(type->ty == Tfunction);
TypeFunction* f = (TypeFunction*)type;
if (type->ir.type != NULL) {
return llvm::cast<llvm::FunctionType>(type->ir.type->get());
}
bool dVararg = false;
bool arrayVararg = false;
if (f->linkage == LINKd)
{
if (f->varargs == 1)
dVararg = true;
else if (f->varargs == 2)
arrayVararg = true;
}
// return value type
const LLType* rettype;
const LLType* actualRettype;
Type* rt = f->next;
bool retinptr = false;
bool usesthis = false;
bool usesnest = false;
// parameter types
std::vector<const LLType*> paramvec;
if (ismain)
{
rettype = LLType::Int32Ty;
actualRettype = rettype;
if (Argument::dim(f->parameters) == 0)
{
const LLType* arrTy = DtoArrayType(LLType::Int8Ty);
const LLType* arrArrTy = DtoArrayType(arrTy);
paramvec.push_back(arrArrTy);
}
}
else{
assert(rt);
if (DtoIsReturnedInArg(rt)) {
rettype = getPtrToType(DtoType(rt));
actualRettype = LLType::VoidTy;
f->retInPtr = retinptr = true;
}
else {
rettype = DtoType(rt);
actualRettype = rettype;
}
if (unsigned ea = DtoShouldExtend(rt))
{
f->retAttrs |= ea;
}
}
if (retinptr) {
//Logger::cout() << "returning through pointer parameter: " << *rettype << '\n';
paramvec.push_back(rettype);
}
if (thistype) {
paramvec.push_back(thistype);
usesthis = true;
}
else if (nesttype) {
paramvec.push_back(nesttype);
usesnest = true;
}
if (dVararg) {
paramvec.push_back(DtoType(Type::typeinfo->type->arrayOf())); // _arguments
paramvec.push_back(getVoidPtrType()); // _argptr
}
// number of formal params
size_t n = Argument::dim(f->parameters);
#if X86_REVERSE_PARAMS
// on x86 we need to reverse the formal params in some cases to match the ABI
if (global.params.cpu == ARCHx86)
{
// more than one formal arg,
// extern(D) linkage
// not a D-style vararg
if (n > 1 && f->linkage == LINKd && !dVararg)
{
f->reverseParams = true;
f->reverseIndex = paramvec.size();
}
}
#endif // X86_REVERSE_PARAMS
for (int i=0; i < n; ++i) {
Argument* arg = Argument::getNth(f->parameters, i);
// ensure scalar
Type* argT = arg->type->toBasetype();
assert(argT);
bool refOrOut = ((arg->storageClass & STCref) || (arg->storageClass & STCout));
const LLType* at = DtoType(argT);
// 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);
at = DtoType(ltd);
paramvec.push_back(at);
}
// opaque types need special handling
else if (llvm::isa<llvm::OpaqueType>(at)) {
Logger::println("opaque param");
assert(argT->ty == Tstruct || argT->ty == Tclass);
paramvec.push_back(getPtrToType(at));
}
// structs are passed as a reference, but by value
else if (argT->ty == Tstruct) {
Logger::println("struct param");
if (!refOrOut)
arg->llvmAttrs |= llvm::Attribute::ByVal;
paramvec.push_back(getPtrToType(at));
}
// static arrays are passed directly by reference
else if (argT->ty == Tsarray)
{
Logger::println("static array param");
at = getPtrToType(at);
paramvec.push_back(at);
}
// firstclass ' ref/out ' parameter
else if (refOrOut) {
Logger::println("ref/out param");
at = getPtrToType(at);
paramvec.push_back(at);
}
// firstclass ' in ' parameter
else {
Logger::println("in param");
if (unsigned ea = DtoShouldExtend(argT))
arg->llvmAttrs |= ea;
paramvec.push_back(at);
}
}
// reverse params?
if (f->reverseParams)
{
std::reverse(paramvec.begin() + f->reverseIndex, paramvec.end());
}
// construct function type
bool isvararg = !(dVararg || arrayVararg) && f->varargs;
llvm::FunctionType* functype = llvm::FunctionType::get(actualRettype, paramvec, isvararg);
#if X86_PASS_IN_EAX
// tell first param to be passed in a register if we can
// ONLY extern(D) functions !
if ((n > 0 || usesthis || usesnest) && f->linkage == LINKd)
{
// FIXME: Only x86 right now ...
if (global.params.cpu == ARCHx86)
{
// pass first param in EAX if it fits, is not floating point and is not a 3 byte struct.
// FIXME: struct are not passed in EAX yet
int n_inreg = f->reverseParams ? n - 1 : 0;
Argument* arg = Argument::getNth(f->parameters, n_inreg);
// if there is a implicit context parameter, pass it in EAX
if (usesthis || usesnest)
{
f->thisAttrs |= llvm::Attribute::InReg;
assert((!arg || (arg->llvmAttrs & llvm::Attribute::InReg) == 0) && "can't have two inreg args!");
}
// otherwise check the first formal parameter
else
{
Type* t = arg->type->toBasetype();
// 32bit ints, pointers, classes, static arrays and AAs
// are candidate for being passed in EAX
if ((arg->storageClass & STCin) &&
((t->isscalar() && !t->isfloating()) ||
t->ty == Tclass || t->ty == Tsarray || t->ty == Taarray) &&
(t->size() <= PTRSIZE))
{
arg->llvmAttrs |= llvm::Attribute::InReg;
assert((f->thisAttrs & llvm::Attribute::InReg) == 0 && "can't have two inreg args!");
}
}
}
}
#endif // X86_PASS_IN_EAX
// done
f->retInPtr = retinptr;
f->usesThis = usesthis;
f->usesNest = usesnest;
f->ir.type = new llvm::PATypeHolder(functype);
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;
if (fdecl->llvmInternal == LLVMva_start)
fty = GET_INTRINSIC_DECL(vastart)->getFunctionType();
else if (fdecl->llvmInternal == LLVMva_copy)
fty = GET_INTRINSIC_DECL(vacopy)->getFunctionType();
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());
const LLType* thisty = 0;
const LLType* nestty = 0;
if (fdecl->needThis()) {
if (AggregateDeclaration* ad = fdecl->isMember2()) {
Logger::println("isMember = this is: %s", ad->type->toChars());
thisty = DtoType(ad->type);
//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()) {
nestty = getPtrToType(LLType::Int8Ty);
}
const llvm::FunctionType* functype = DtoFunctionType(fdecl->type, thisty, nestty, 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 llidx = 0;
if (f->retInPtr) ++llidx;
if (f->usesThis) ++llidx;
else if (f->usesNest) ++llidx;
if (f->linkage == LINKd && f->varargs == 1)
llidx += 2;
int funcNumArgs = func->getArgumentList().size();
LLSmallVector<llvm::AttributeWithIndex, 9> attrs;
llvm::AttributeWithIndex PAWI;
// set return value attrs if any
if (f->retAttrs)
{
PAWI.Index = 0;
PAWI.Attrs = f->retAttrs;
attrs.push_back(PAWI);
}
// set sret param
if (f->retInPtr)
{
PAWI.Index = 1;
PAWI.Attrs = llvm::Attribute::StructRet;
attrs.push_back(PAWI);
}
// set this/nest param attrs
if (f->thisAttrs)
{
PAWI.Index = f->retInPtr ? 2 : 1;
PAWI.Attrs = f->thisAttrs;
attrs.push_back(PAWI);
}
// set attrs on the rest of the arguments
size_t n = Argument::dim(f->parameters);
assert(funcNumArgs >= n); // main might mismatch, for the implicit char[][] arg
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] = fnarg->llvmAttrs;
}
// reverse params?
if (f->reverseParams)
{
std::reverse(attrptr.begin(), attrptr.end());
}
// build rest of attrs list
for (int i = 0; i < n; i++)
{
if (attrptr[i])
{
PAWI.Index = llidx+i+1;
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 = false;
bool templInst = fdecl->parent && DtoIsTemplateInstance(fdecl->parent);
if (!templInst && fdecl->getModule() != gIR->dmodule)
{
Logger::println("not template instance, and not in this module. declare only!");
Logger::println("current module: %s", gIR->dmodule->ident->toChars());
if(fdecl->getModule())
Logger::println("func module: %s", fdecl->getModule()->ident->toChars());
else {
Logger::println("func not in a module, is runtime");
}
declareOnly = true;
}
else 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(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() && fdecl->getModule() == gIR->dmodule) {
gIR->ctors.push_back(fdecl);
}
// static dtor
else if (fdecl->isStaticDtorDeclaration() && fdecl->getModule() == gIR->dmodule) {
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->retInPtr) {
iarg->setName(".sret_arg");
fdecl->ir.irFunc->retArg = iarg;
++iarg;
}
if (f->usesThis) {
iarg->setName(".this_arg");
fdecl->ir.irFunc->thisArg = iarg;
assert(fdecl->ir.irFunc->thisArg);
++iarg;
}
else if (f->usesNest) {
iarg->setName(".nest_arg");
fdecl->ir.irFunc->nestArg = iarg;
assert(fdecl->ir.irFunc->nestArg);
++iarg;
}
if (f->linkage == LINKd && f->varargs == 1) {
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->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())
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';
}
//////////////////////////////////////////////////////////////////////////////////////////
void DtoDefineFunc(FuncDeclaration* fd)
{
if (fd->ir.defined) return;
fd->ir.defined = true;
assert(fd->ir.declared);
Logger::println("DtoDefineFunc(%s): %s", fd->toPrettyChars(), fd->loc.toChars());
LOG_SCOPE;
// error on naked
if (fd->naked)
{
fd->error("naked is not supported");
fatal();
}
// debug info
if (global.params.symdebug) {
Module* mo = fd->getModule();
fd->ir.irFunc->dwarfSubProg = 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();
// only members of the current module or template instances maybe be defined
if (!(fd->getModule() == gIR->dmodule || DtoIsTemplateInstance(fd->parent)))
return;
// 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.
// naked must always eliminate the framepointer however...
if (fd->inlineAsm && !fd->naked)
{
// 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->usesThis)
{
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 = fd->parameters->dim;
for (int i=0; i < n; ++i)
{
Dsymbol* argsym = (Dsymbol*)fd->parameters->data[i];
VarDeclaration* vd = argsym->isVarDeclaration();
assert(vd);
#if DMDV2
if (vd->nestedrefs.dim)
#else
if (vd->nestedref)
#endif
{
fd->nestedVars.insert(vd);
}
IrLocal* irloc = vd->ir.irLocal;
assert(irloc);
bool refout = vd->storage_class & (STCref | STCout);
bool lazy = vd->storage_class & STClazy;
if (!refout && (!DtoIsPassedByRef(vd->type) || lazy))
{
LLValue* a = irloc->value;
LLValue* v = DtoAlloca(a->getType(), vd->ident->toChars());
DtoStore(a,v);
irloc->value = v;
}
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 add all enclosing parent frames
int nparelems = 0;
Dsymbol* par = fd->toParent2();
while (par)
{
if (FuncDeclaration* parfd = par->isFuncDeclaration())
{
nparelems += parfd->nestedVars.size();
}
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);
// 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())
llvm::ReturnInst::Create(llvm::UndefValue::get(func->getReturnType()), gIR->scopebb());
else
llvm::ReturnInst::Create(llvm::Constant::getNullValue(func->getReturnType()), gIR->scopebb());
}
}
// 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();
// if the last block is empty now, it must be unreachable or it's a bug somewhere else
// would be nice to figure out how to assert that this is correct
llvm::BasicBlock* lastbb = &func->getBasicBlockList().back();
if (lastbb->empty())
{
new llvm::UnreachableInst(lastbb);
}
// if the last block is not terminated we return a null value or void
// for some unknown reason this is needed when a void main() has a inline asm block ...
// this should be harmless for well formed code!
lastbb = &func->getBasicBlockList().back();
if (!lastbb->getTerminator())
{
Logger::println("adding missing return statement");
if (func->getReturnType() == LLType::VoidTy)
llvm::ReturnInst::Create(lastbb);
else
llvm::ReturnInst::Create(llvm::Constant::getNullValue(func->getReturnType()), lastbb);
}
gIR->functions.pop_back();
}
//////////////////////////////////////////////////////////////////////////////////////////
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);
}