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

Applied patch from ticket #129 to compile against latest LLVM. Thanks Frits van Bommel. Fixed implicit return by asm block at the end of a function on x86-32. Other architectures will produce an error at the moment. Adding support for new targets is fairly simple. Fixed return calling convention for complex numbers, ST and ST(1) were switched around. Added some testcases. I've run a dstress test and there are no regressions. However, the runtime does not seem to compile with symbolic debug information. -O3 -release -inline works well and is what I used for the dstress run. Tango does not compile, a small workaround is needed in tango.io.digest.Digest.Digest.hexDigest. See ticket #206 .
1043 lines
32 KiB
C++
1043 lines
32 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
|
|
}
|
|
|
|
// now that all implicit args are done, store the start of the real args
|
|
f->firstRealArg = paramvec.size();
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
#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->firstRealArg, paramvec.end());
|
|
}
|
|
|
|
#if X86_PASS_IN_EAX
|
|
// pass first param in EAX if it fits, is not floating point and is not a 3 byte struct.
|
|
// ONLY extern(D) functions !
|
|
if ((n > 0 || usesthis || usesnest) && f->linkage == LINKd)
|
|
{
|
|
// FIXME: Only x86 right now ...
|
|
if (global.params.cpu == ARCHx86)
|
|
{
|
|
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, AAs, ref and out params,
|
|
// and structs with size <= 4 and != 3
|
|
// are candidate for being passed in EAX
|
|
if (
|
|
(arg->storageClass & (STCref|STCout))
|
|
||
|
|
((arg->storageClass & STCin) &&
|
|
((t->isscalar() && !t->isfloating()) ||
|
|
t->ty == Tclass || t->ty == Tsarray || t->ty == Taarray ||
|
|
(t->ty == Tstruct && t->size() != 3)
|
|
) && (t->size() <= PTRSIZE))
|
|
)
|
|
{
|
|
arg->llvmAttrs |= llvm::Attribute::InReg;
|
|
assert((f->thisAttrs & llvm::Attribute::InReg) == 0 && "can't have two inreg args!");
|
|
|
|
// structs need to go from {...}* byval to i8/i16/i32 inreg
|
|
if ((arg->storageClass & STCin) && t->ty == Tstruct)
|
|
{
|
|
int n_param = f->reverseParams ? f->firstRealArg + n - 1 - n_inreg : f->firstRealArg + n_inreg;
|
|
assert(isaPointer(paramvec[n_param]) && (arg->llvmAttrs & llvm::Attribute::ByVal)
|
|
&& "struct parameter expected to be {...}* byval before inreg is applied");
|
|
f->structInregArg = paramvec[n_param]->getContainedType(0);
|
|
paramvec[n_param] = LLIntegerType::get(8*t->size());
|
|
arg->llvmAttrs &= ~llvm::Attribute::ByVal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // X86_PASS_IN_EAX
|
|
|
|
// construct function type
|
|
bool isvararg = !(dVararg || arrayVararg) && f->varargs;
|
|
llvm::FunctionType* functype = llvm::FunctionType::get(actualRettype, paramvec, isvararg);
|
|
|
|
// 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 = !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 (fdecl->getModule() == gIR->dmodule || fdecl->inTemplateInstance()) {
|
|
gIR->ctors.push_back(fdecl);
|
|
}
|
|
}
|
|
// static dtor
|
|
else if (fdecl->isStaticDtorDeclaration()) {
|
|
if (fdecl->getModule() == gIR->dmodule || fdecl->inTemplateInstance()) {
|
|
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() && !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';
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoDefineFunction(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;
|
|
|
|
// 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) {
|
|
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();
|
|
|
|
// 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->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);
|
|
|
|
IrLocal* irloc = vd->ir.irLocal;
|
|
assert(irloc);
|
|
|
|
// if it's inreg struct arg, allocate storage
|
|
if (f->structInregArg && i == (f->reverseParams ? n - 1 : 0))
|
|
{
|
|
int n_param = f->reverseParams ? f->firstRealArg + n - 1 - i : f->firstRealArg + i;
|
|
const LLType* paramty = functype->getParamType(n_param);
|
|
assert(!f->usesNest && !f->usesThis &&
|
|
llvm::isa<LLIntegerType>(paramty) && isaStruct(f->structInregArg)
|
|
&& "Preconditions for inreg struct arg not met!");
|
|
|
|
LLValue* mem = DtoAlloca(f->structInregArg, "inregstructarg");
|
|
|
|
DtoStore(irloc->value, DtoBitCast(mem, getPtrToType(paramty)));
|
|
irloc->value = mem;
|
|
}
|
|
|
|
#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 && (!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 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);
|
|
}
|