ldc/gen/functions.cpp
David Nadlinger 8ff3a8060a Use llvm_unreachable instead of assert(0).
Also removed some unused functions.
2013-02-07 03:38:15 +01:00

1276 lines
39 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- functions.cpp -----------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#include "gen/llvm.h"
#include "llvm/Support/CFG.h"
#if LDC_LLVM_VER >= 303
#include "llvm/IR/Intrinsics.h"
#else
#include "llvm/Intrinsics.h"
#endif
#include "mtype.h"
#include "aggregate.h"
#include "init.h"
#include "declaration.h"
#include "template.h"
#include "module.h"
#include "statement.h"
#include "id.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"
#include "gen/nested.h"
#include "gen/pragma.h"
#include <iostream>
#if LDC_LLVM_VER < 302
using namespace llvm::Attribute;
#endif
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 = static_cast<TypeFunction*>(type);
TargetABI* abi = (f->linkage == LINKintrinsic ? TargetABI::getIntrinsic() : gABI);
// Tell the ABI we're resolving a new function type
abi->newFunctionType(f);
// Do not modify f->fty yet; this function may be called recursively if any
// of the argument types refer to this type.
IrFuncTy fty;
// llvm idx counter
size_t lidx = 0;
// main needs a little special handling
if (ismain)
{
fty.ret = new IrFuncTyArg(Type::tint32, false);
}
// sane return value
else
{
Type* rt = f->next;
#if LDC_LLVM_VER >= 302
llvm::AttrBuilder attrBuilder;
#else
llvm::Attributes a = None;
#endif
// sret return
if (abi->returnInArg(f))
{
#if LDC_LLVM_VER >= 302
#if LDC_LLVM_VER >= 303
fty.arg_sret = new IrFuncTyArg(rt, true,
llvm::AttrBuilder().addAttribute(llvm::Attribute::StructRet)
.addAttribute(llvm::Attribute::NoAlias)
#else
fty.arg_sret = new IrFuncTyArg(rt, true, llvm::Attributes::get(gIR->context(),
llvm::AttrBuilder().addAttribute(llvm::Attributes::StructRet)
.addAttribute(llvm::Attributes::NoAlias)
#endif
#if !STRUCTTHISREF
// In D2 where 'this' in structs is a reference, nocapture
// might not actually be applicable, even if it probably still
// is for all sane code from a high-level semantic standpoint.
// Specifying nocapture on a parameter but then passing it as a
// non-nocapture argument in a function call can lead to
// _silent_ miscompilations (especially in the GVN pass).
.addAttribute(llvm::Attributes::NoCapture)
#endif
#if LDC_LLVM_VER == 302
)
#endif
);
#else
fty.arg_sret = new IrFuncTyArg(rt, true, StructRet | NoAlias
#if !STRUCTTHISREF
| NoCapture
#endif
);
#endif
rt = Type::tvoid;
lidx++;
}
// sext/zext return
else
{
Type *t = rt;
#if DMDV2
if (f->isref)
t = t->pointerTo();
#endif
#if LDC_LLVM_VER >= 303
if (llvm::Attribute::AttrKind a = DtoShouldExtend(t))
attrBuilder.addAttribute(a);
#elif LDC_LLVM_VER == 302
if (llvm::Attributes::AttrVal a = DtoShouldExtend(t))
attrBuilder.addAttribute(a);
#else
a = DtoShouldExtend(t);
#endif
}
#if LDC_LLVM_VER >= 303
llvm::AttrBuilder a = attrBuilder;
#elif LDC_LLVM_VER == 302
llvm::Attributes a = llvm::Attributes::get(gIR->context(), attrBuilder);
#endif
#if DMDV2
fty.ret = new IrFuncTyArg(rt, f->isref, a);
#else
fty.ret = new IrFuncTyArg(rt, false, a);
#endif
}
lidx++;
// member functions
if (thistype)
{
fty.arg_this = new IrFuncTyArg(thistype, thistype->toBasetype()->ty == Tstruct);
lidx++;
}
// and nested functions
else if (nesttype)
{
fty.arg_nest = new IrFuncTyArg(nesttype, false);
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
fty.arg_arguments = new IrFuncTyArg(Type::typeinfo->type->arrayOf(), false);
lidx++;
// _argptr
#if LDC_LLVM_VER >= 303
fty.arg_argptr = new IrFuncTyArg(Type::tvoid->pointerTo(), false,
llvm::AttrBuilder().addAttribute(llvm::Attribute::NoAlias)
.addAttribute(llvm::Attribute::NoCapture));
#elif LDC_LLVM_VER == 302
fty.arg_argptr = new IrFuncTyArg(Type::tvoid->pointerTo(), false,
llvm::Attributes::get(gIR->context(), llvm::AttrBuilder().addAttribute(llvm::Attributes::NoAlias)
.addAttribute(llvm::Attributes::NoCapture)));
#else
fty.arg_argptr = new IrFuncTyArg(Type::tvoid->pointerTo(), false, NoAlias | NoCapture);
#endif
lidx++;
}
}
else
{
// Default to C-style varargs for non-extern(D) variadic functions.
// This seems to be what DMD does.
fty.c_vararg = true;
}
}
// if this _Dmain() doesn't have an argument, we force it to have one
int nargs = Parameter::dim(f->parameters);
if (ismain && nargs == 0)
{
Type* mainargs = Type::tchar->arrayOf()->arrayOf();
fty.args.push_back(new IrFuncTyArg(mainargs, false));
lidx++;
}
// add explicit parameters
else for (int i = 0; i < nargs; i++)
{
// get argument
Parameter* arg = Parameter::getNth(f->parameters, i);
// reference semantics? ref, out and d1 static arrays are
bool byref = arg->storageClass & (STCref|STCout);
#if !SARRAYVALUE
byref = byref || (arg->type->toBasetype()->ty == Tsarray);
#endif
Type* argtype = arg->type;
#if LDC_LLVM_VER >= 302
llvm::AttrBuilder attrBuilder;
#else
llvm::Attributes a = None;
#endif
// 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 (abi->passByVal(byref ? argtype->pointerTo() : argtype))
{
#if LDC_LLVM_VER >= 303
if (!byref) attrBuilder.addAttribute(llvm::Attribute::ByVal);
#elif LDC_LLVM_VER == 302
if (!byref) attrBuilder.addAttribute(llvm::Attributes::ByVal);
#else
if (!byref) a |= llvm::Attribute::ByVal;
#endif
// set byref, because byval requires a pointed LLVM type
byref = true;
}
// sext/zext
else if (!byref)
{
#if LDC_LLVM_VER >= 303
if (llvm::Attribute::AttrKind a = DtoShouldExtend(argtype))
attrBuilder.addAttribute(a);
#elif LDC_LLVM_VER == 302
if (llvm::Attributes::AttrVal a = DtoShouldExtend(argtype))
attrBuilder.addAttribute(a);
#else
a |= DtoShouldExtend(argtype);
#endif
}
#if LDC_LLVM_VER >= 303
llvm::AttrBuilder a = attrBuilder;
#elif LDC_LLVM_VER == 302
llvm::Attributes a = llvm::Attributes::get(gIR->context(), attrBuilder);
#endif
fty.args.push_back(new IrFuncTyArg(argtype, byref, a));
lidx++;
}
// Now we can modify f->fty safely.
f->fty = fty;
// let the abi rewrite the types as necesary
abi->rewriteFunctionType(f);
// Tell the ABI we're done with this function type
abi->doneWithFunctionType();
// build the function type
std::vector<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());
}
LLFunctionType* functype = LLFunctionType::get(f->fty.ret->ltype, argtypes, f->fty.c_vararg);
Logger::cout() << "Final function type: " << *functype << "\n";
return functype;
}
//////////////////////////////////////////////////////////////////////////////////////////
#include <llvm/Support/raw_ostream.h>
#include "llvm/Support/SourceMgr.h"
#include "llvm/Assembly/Parser.h"
LLFunction* DtoInlineIRFunction(FuncDeclaration* fdecl)
{
const char* mangled_name = fdecl->mangle();
TemplateInstance* tinst = fdecl->parent->isTemplateInstance();
assert(tinst);
Objects& objs = tinst->tdtypes;
assert(objs.dim == 3);
Expression* a0 = isExpression(objs[0]);
assert(a0);
StringExp* strexp = a0->toString();
assert(strexp);
assert(strexp->sz == 1);
std::string code(static_cast<char*>(strexp->string), strexp->len);
Type* ret = isType(objs[1]);
assert(ret);
Tuple* a2 = isTuple(objs[2]);
assert(a2);
Objects& arg_types = a2->objects;
std::string str;
llvm::raw_string_ostream stream(str);
stream << "define " << *DtoType(ret) << " @" << mangled_name << "(";
for(size_t i = 0; ;)
{
Type* ty = isType(arg_types[i]);
//assert(ty);
if(!ty)
{
error(tinst->loc,
"All parameters of a template defined with pragma llvm_inline_ir, except for the first one, should be types");
fatal();
}
stream << *DtoType(ty);
i++;
if(i >= arg_types.dim)
break;
stream << ", ";
}
if(ret->ty == Tvoid)
code.append("\nret void");
stream << ")\n{\n" << code << "\n}";
llvm::SMDiagnostic err;
llvm::ParseAssemblyString(stream.str().c_str(), gIR->module, err, gIR->context());
std::string errstr = err.getMessage();
if(errstr != "")
error(tinst->loc,
"can't parse inline LLVM IR:\n%s\n%s\n%s\nThe input string was: \n%s",
#if LDC_LLVM_VER >= 303
err.getLineContents().str().c_str(),
#else
err.getLineContents().c_str(),
#endif
(std::string(err.getColumnNo(), ' ') + '^').c_str(),
errstr.c_str(), stream.str().c_str());
LLFunction* fun = gIR->module->getFunction(mangled_name);
fun->setLinkage(llvm::GlobalValue::LinkOnceODRLinkage);
#if LDC_LLVM_VER >= 303
fun->addFnAttr(llvm::Attribute::AlwaysInline);
#elif LDC_LLVM_VER == 302
fun->addFnAttr(llvm::Attributes::AlwaysInline);
#else
fun->addFnAttr(AlwaysInline);
#endif
return fun;
}
//////////////////////////////////////////////////////////////////////////////////////////
static llvm::FunctionType* DtoVaFunctionType(FuncDeclaration* fdecl)
{
TypeFunction* f = static_cast<TypeFunction*>(fdecl->type);
LLFunctionType* 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);
return fty;
}
//////////////////////////////////////////////////////////////////////////////////////////
llvm::FunctionType* DtoFunctionType(FuncDeclaration* fdecl)
{
// handle for C vararg intrinsics
if (fdecl->isVaIntrinsic())
return DtoVaFunctionType(fdecl);
Type *dthis=0, *dnest=0;
#if DMDV2
if (fdecl->ident == Id::ensure || fdecl->ident == Id::require) {
FuncDeclaration *p = fdecl->parent->isFuncDeclaration();
assert(p);
AggregateDeclaration *ad = p->isMember2();
assert(ad);
dnest = Type::tvoid->pointerTo();
} else
#endif
if (fdecl->needThis()) {
if (AggregateDeclaration* ad = fdecl->isMember2()) {
Logger::println("isMember = this is: %s", ad->type->toChars());
dthis = ad->type;
LLType* thisty = DtoType(dthis);
//Logger::cout() << "this llvm type: " << *thisty << '\n';
if (ad->isStructDeclaration())
thisty = getPtrToType(thisty);
}
else {
Logger::println("chars: %s type: %s kind: %s", fdecl->toChars(), fdecl->type->toChars(), fdecl->kind());
llvm_unreachable("needThis, but invalid parent declaration.");
}
}
else if (fdecl->isNested()) {
dnest = Type::tvoid->pointerTo();
}
LLFunctionType* functype = DtoFunctionType(fdecl->type, dthis, dnest, fdecl->isMain());
return functype;
}
//////////////////////////////////////////////////////////////////////////////////////////
static llvm::Function* DtoDeclareVaFunction(FuncDeclaration* fdecl)
{
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->type) && fdecl->isUnitTestDeclaration()) {
Logger::println("Ignoring unittest %s", fdecl->toPrettyChars());
return; // ignore declaration completely
}
if (fdecl->ir.resolved) return;
fdecl->ir.resolved = true;
Type *type = fdecl->type;
// If errors occurred compiling it, such as bugzilla 6118
if (type && type->ty == Tfunction) {
Type *next = static_cast<TypeFunction *>(type)->next;
if (!next || next->ty == Terror)
return;
}
//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.resolved = true;
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;
static_cast<TypeFunction*>(fdecl->type)->linkage = LINKintrinsic;
}
else if (tempdecl->llvmInternal == LLVMinline_asm)
{
Logger::println("magic inline asm found");
TypeFunction* tf = static_cast<TypeFunction*>(fdecl->type);
if (tf->varargs != 1 || (fdecl->parameters && fdecl->parameters->dim != 0))
{
error("invalid __asm declaration, must be a D style variadic with no explicit parameters");
fatal();
}
fdecl->llvmInternal = LLVMinline_asm;
fdecl->ir.resolved = true;
fdecl->ir.declared = true;
fdecl->ir.initialized = true;
fdecl->ir.defined = true;
return; // this gets mapped to a special inline asm call, no point in going on.
}
else if (tempdecl->llvmInternal == LLVMinline_ir)
{
fdecl->llvmInternal = LLVMinline_ir;
fdecl->linkage = LINKc;
fdecl->ir.defined = true;
Type* type = fdecl->type;
assert(type->ty == Tfunction);
static_cast<TypeFunction*>(type)->linkage = LINKc;
}
}
DtoType(fdecl->type);
Logger::println("DtoResolveFunction(%s): %s", fdecl->toPrettyChars(), fdecl->loc.toChars());
LOG_SCOPE;
// queue declaration unless the function is abstract without body
if (!fdecl->isAbstract() || fdecl->fbody)
{
DtoDeclareFunction(fdecl);
}
}
//////////////////////////////////////////////////////////////////////////////////////////
#if LDC_LLVM_VER >= 303
static void set_param_attrs(TypeFunction* f, llvm::Function* func, FuncDeclaration* fdecl)
{
llvm::AttributeSet attrs;
int idx = 0;
// handle implicit args
#define ADD_PA(X) \
if (f->fty.X) { \
if (f->fty.X->attrs.hasAttributes()) { \
llvm::AttributeSet a = llvm::AttributeSet::get(gIR->context(), idx, f->fty.X->attrs); \
attrs = attrs.addAttributes(gIR->context(), idx, a); \
} \
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 = Parameter::dim(f->parameters);
for (size_t k = 0; k < n; k++)
{
Parameter* fnarg = Parameter::getNth(f->parameters, k);
assert(fnarg);
llvm::AttrBuilder a = f->fty.args[k]->attrs;
if (a.hasAttributes())
{
unsigned i = idx + (f->fty.reverseParams ? n-k-1 : k);
llvm::AttributeSet as = llvm::AttributeSet::get(gIR->context(), i, a);
attrs = attrs.addAttributes(gIR->context(), i, as);
}
}
// Merge in any old attributes (attributes for the function itself are
// also stored in a list slot).
llvm::AttributeSet oldAttrs = func->getAttributes();
for (size_t i = 0; i < oldAttrs.getNumSlots(); ++i) {
attrs.addAttributes(gIR->context(), oldAttrs.getSlotIndex(i),
oldAttrs.getSlotAttributes(i));
}
// Store the final attribute set
func->setAttributes(attrs);
}
#else
static void set_param_attrs(TypeFunction* f, llvm::Function* func, FuncDeclaration* fdecl)
{
LLSmallVector<llvm::AttributeWithIndex, 9> attrs;
int idx = 0;
// handle implicit args
#define ADD_PA(X) \
if (f->fty.X) { \
if (HAS_ATTRIBUTES(f->fty.X->attrs)) { \
attrs.push_back(llvm::AttributeWithIndex::get(idx, f->fty.X->attrs)); \
} \
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 = Parameter::dim(f->parameters);
#if LDC_LLVM_VER == 302
LLSmallVector<llvm::Attributes, 8> attrptr(n, llvm::Attributes());
#else
LLSmallVector<llvm::Attributes, 8> attrptr(n, None);
#endif
for (size_t k = 0; k < n; ++k)
{
Parameter* fnarg = Parameter::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 (size_t i = 0; i < n; i++)
{
if (HAS_ATTRIBUTES(attrptr[i]))
{
attrs.push_back(llvm::AttributeWithIndex::get(idx + i, attrptr[i]));
}
}
// Merge in any old attributes (attributes for the function itself are
// also stored in a list slot).
const size_t newSize = attrs.size();
llvm::AttrListPtr oldAttrs = func->getAttributes();
for (size_t i = 0; i < oldAttrs.getNumSlots(); ++i) {
llvm::AttributeWithIndex curr = oldAttrs.getSlot(i);
bool found = false;
for (size_t j = 0; j < newSize; ++j) {
if (attrs[j].Index == curr.Index) {
#if LDC_LLVM_VER == 302
attrs[j].Attrs = llvm::Attributes::get(
gIR->context(),
llvm::AttrBuilder(attrs[j].Attrs).addAttributes(curr.Attrs));
#else
attrs[j].Attrs |= curr.Attrs;
#endif
found = true;
break;
}
}
if (!found) {
attrs.push_back(curr);
}
}
#if LDC_LLVM_VER >= 302
llvm::AttrListPtr attrlist = llvm::AttrListPtr::get(gIR->context(),
llvm::ArrayRef<llvm::AttributeWithIndex>(attrs));
#else
llvm::AttrListPtr attrlist = llvm::AttrListPtr::get(attrs.begin(), attrs.end());
#endif
func->setAttributes(attrlist);
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////
void DtoDeclareFunction(FuncDeclaration* fdecl)
{
DtoResolveFunction(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 = static_cast<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();
LLFunction* vafunc = 0;
if (fdecl->isVaIntrinsic())
vafunc = DtoDeclareVaFunction(fdecl);
// construct function
LLFunctionType* functype = DtoFunctionType(fdecl);
LLFunction* func = vafunc ? vafunc : gIR->module->getFunction(mangled_name);
if (!func) {
if(fdecl->llvmInternal == LLVMinline_ir)
func = DtoInlineIRFunction(fdecl);
else
func = LLFunction::Create(functype, DtoLinkage(fdecl), mangled_name, gIR->module);
} else if (func->getFunctionType() != functype) {
error(fdecl->loc, "Function type does not match previously declared function with the same mangled name: %s", fdecl->mangle());
}
if (Logger::enabled())
Logger::cout() << "func = " << *func << std::endl;
// add func to IRFunc
fdecl->ir.irFunc->func = func;
// calling convention
if (!vafunc && fdecl->llvmInternal != LLVMintrinsic
#if DMDV2
// DMD treats _Dmain as having C calling convention and this has been
// hardcoded into druntime, even if the frontend type has D linkage.
// See Bugzilla issue 9028.
&& !fdecl->isMain()
#endif
)
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);
// parameter attributes
if (!fdecl->isIntrinsic()) {
set_param_attrs(f, func, fdecl);
if (global.params.disableRedZone) {
#if LDC_LLVM_VER >= 303
func->addFnAttr(llvm::Attribute::NoRedZone);
#elif LDC_LLVM_VER == 302
func->addFnAttr(llvm::Attributes::NoRedZone);
#else
func->addFnAttr(NoRedZone);
#endif
}
}
// main
if (fdecl->isMain()) {
// Detect multiple main functions, which is disallowed. DMD checks this
// in the glue code, so we need to do it here as well.
if (gIR->mainFunc) {
error(fdecl->loc, "only one main function allowed");
}
gIR->mainFunc = func;
}
#if DMDV2
// shared static ctor
if (fdecl->isSharedStaticCtorDeclaration()) {
if (mustDefineSymbol(fdecl)) {
gIR->sharedCtors.push_back(fdecl);
}
}
// shared static dtor
else if (StaticDtorDeclaration *dtorDecl = fdecl->isSharedStaticDtorDeclaration()) {
if (mustDefineSymbol(fdecl)) {
gIR->sharedDtors.push_front(fdecl);
if (dtorDecl->vgate)
gIR->sharedGates.push_front(dtorDecl->vgate);
}
} else
#endif
// static ctor
if (fdecl->isStaticCtorDeclaration()) {
if (mustDefineSymbol(fdecl)) {
gIR->ctors.push_back(fdecl);
}
}
// static dtor
else if (StaticDtorDeclaration *dtorDecl = fdecl->isStaticDtorDeclaration()) {
if (mustDefineSymbol(fdecl)) {
gIR->dtors.push_front(fdecl);
#if DMDV2
if (dtorDecl->vgate)
gIR->gates.push_front(dtorDecl->vgate);
#endif
}
}
if (fdecl->llvmInternal == LLVMglobal_crt_ctor || fdecl->llvmInternal == LLVMglobal_crt_dtor)
{
AppendFunctionToLLVMGlobalCtorsDtors(func, fdecl->priority, fdecl->llvmInternal == LLVMglobal_crt_ctor);
}
// 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;
VarDeclaration* v = fdecl->vthis;
if (v) {
// We already build the this argument here if we will need it
// later for codegen'ing the function, just as normal
// parameters below, because it can be referred to in nested
// context types. Will be given storage in DtoDefineFunction.
assert(!v->ir.irParam);
IrParameter* p = new IrParameter(v);
p->isVthis = true;
p->value = iarg;
p->arg = f->fty.arg_this;
v->ir.irParam = p;
}
++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;
}
unsigned int k = 0;
for (; iarg != func->arg_end(); ++iarg)
{
if (fdecl->parameters && fdecl->parameters->dim > k)
{
int paramIndex = f->fty.reverseParams ? fdecl->parameters->dim-k-1 : k;
Dsymbol* argsym = static_cast<Dsymbol*>(fdecl->parameters->data[paramIndex]);
VarDeclaration* argvd = argsym->isVarDeclaration();
assert(argvd);
assert(!argvd->ir.irLocal);
argvd->ir.irParam = new IrParameter(argvd);
argvd->ir.irParam->value = iarg;
argvd->ir.irParam->arg = f->fty.args[paramIndex];
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)
Type::sir->addFunctionBody(fdecl->ir.irFunc);
else
assert(func->getLinkage() != llvm::GlobalValue::InternalLinkage);
}
//////////////////////////////////////////////////////////////////////////////////////////
// FIXME: this isn't too pretty!
void DtoDefineFunction(FuncDeclaration* fd)
{
DtoDeclareFunction(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
fd->ir.irFunc->diSubprogram = DtoDwarfSubProgram(fd);
Type* t = fd->type->toBasetype();
TypeFunction* f = static_cast<TypeFunction*>(t);
// assert(f->irtype);
llvm::Function* func = fd->ir.irFunc->func;
// 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(gIR->context(), entryname,func);
llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endentry",func);
//assert(gIR->scopes.empty());
gIR->scopes.push_back(IRScope(beginbb, endbb));
// create alloca point
// this gets erased when the function is complete, so alignment etc does not matter at all
llvm::Instruction* allocaPoint = new llvm::AllocaInst(LLType::getInt32Ty(gIR->context()), "alloca point", beginbb);
irfunction->allocapoint = allocaPoint;
// debug info - after all allocas, but before any llvm.dbg.declare etc
DtoDwarfFuncStart(fd);
// this hack makes sure the frame pointer elimination optimization is disabled.
// this this eliminates a bunch of inline asm related issues.
if (fd->hasReturnExp & 8) // has inline asm
{
// 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 = thisvar;
#if STRUCTTHISREF
if (!f->fty.arg_this->byref)
#endif
{
thismem = DtoRawAlloca(thisvar->getType(), 0, "this"); // FIXME: align?
DtoStore(thisvar, thismem);
irfunction->thisArg = thismem;
}
assert(fd->vthis->ir.irParam->value == thisvar);
fd->vthis->ir.irParam->value = thismem;
DtoDwarfLocalVariable(thismem, fd->vthis);
}
// give the 'nestArg' storage
if (f->fty.arg_nest)
{
LLValue *nestArg = irfunction->nestArg;
LLValue *val = DtoRawAlloca(nestArg->getType(), 0, "nestedFrame");
DtoStore(nestArg, val);
irfunction->nestArg = val;
}
// give arguments storage
// and debug info
if (fd->parameters)
{
size_t n = f->fty.args.size();
assert(n == fd->parameters->dim);
for (size_t i=0; i < n; ++i)
{
Dsymbol* argsym = static_cast<Dsymbol*>(fd->parameters->data[i]);
VarDeclaration* vd = argsym->isVarDeclaration();
assert(vd);
IrParameter* irparam = vd->ir.irParam;
assert(irparam);
#if DMDV1
if (vd->nestedref)
{
fd->nestedVars.insert(vd);
}
#endif
bool refout = vd->storage_class & (STCref | STCout);
bool lazy = vd->storage_class & STClazy;
if (!refout && (!irparam->arg->byref || lazy))
{
// alloca a stack slot for this first class value arg
LLType* argt;
if (lazy)
argt = irparam->value->getType();
else
argt = DtoType(vd->type);
LLValue* mem = DtoRawAlloca(argt, 0, vd->ident->toChars());
// let the abi transform the argument back first
DImValue arg_dval(vd->type, irparam->value);
f->fty.getParam(vd->type, i, &arg_dval, mem);
// set the arg var value to the alloca
irparam->value = mem;
}
if (global.params.symdebug && !(isaArgument(irparam->value) && isaArgument(irparam->value)->hasByValAttr()) && !refout)
DtoDwarfLocalVariable(irparam->value, vd);
}
}
#if DMDV1
// need result variable? (nested)
if (fd->vresult && fd->vresult->nestedref) {
Logger::println("nested vresult value: %s", fd->vresult->toChars());
fd->nestedVars.insert(fd->vresult);
}
if (fd->vthis && fd->vthis->nestedref && !fd->nestedVars.empty()) {
Logger::println("nested vthis value: %s", fd->vthis->toChars());
fd->nestedVars.insert(fd->vthis);
}
#endif
FuncGen fg;
irfunction->gen = &fg;
DtoCreateNestedContext(fd);
if (fd->vresult && !
#if DMDV2
fd->vresult->nestedrefs.dim // FIXME: not sure here :/
#else
fd->vresult->nestedref
#endif
)
{
DtoVarDeclaration(fd->vresult);
}
// copy _argptr and _arguments to a memory location
if (f->linkage == LINKd && f->varargs == 1)
{
// _argptr
LLValue* argptrmem = DtoRawAlloca(fd->ir.irFunc->_argptr->getType(), 0, "_argptr_mem");
new llvm::StoreInst(fd->ir.irFunc->_argptr, argptrmem, gIR->scopebb());
fd->ir.irFunc->_argptr = argptrmem;
// _arguments
LLValue* argumentsmem = DtoRawAlloca(fd->ir.irFunc->_arguments->getType(), 0, "_arguments_mem");
new llvm::StoreInst(fd->ir.irFunc->_arguments, argumentsmem, gIR->scopebb());
fd->ir.irFunc->_arguments = argumentsmem;
}
// output function body
fd->fbody->toIR(gIR);
irfunction->gen = 0;
// TODO: clean up this mess
// std::cout << *func << std::endl;
llvm::BasicBlock* bb = gIR->scopebb();
if (pred_begin(bb) == pred_end(bb) && bb != &bb->getParent()->getEntryBlock()) {
// This block is trivially unreachable, so just delete it.
// (This is a common case because it happens when 'return'
// is the last statement in a function)
bb->eraseFromParent();
} else if (!gIR->scopereturned()) {
// 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.
// pass the previous block into this block
DtoDwarfFuncEnd(fd);
if (func->getReturnType() == LLType::getVoidTy(gIR->context())) {
llvm::ReturnInst::Create(gIR->context(), gIR->scopebb());
}
else if (!fd->isMain()) {
AsmBlockStatement* asmb = fd->fbody->endsWithAsm();
if (asmb) {
assert(asmb->abiret);
llvm::ReturnInst::Create(gIR->context(), asmb->abiret, bb);
}
else {
llvm::ReturnInst::Create(gIR->context(), llvm::UndefValue::get(func->getReturnType()), bb);
}
}
else
llvm::ReturnInst::Create(gIR->context(), LLConstant::getNullValue(func->getReturnType()), bb);
}
// std::cout << *func << std::endl;
// erase alloca point
if (allocaPoint->getParent())
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;
}
//////////////////////////////////////////////////////////////////////////////////////////
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, static_cast<TypeFunction*>(fdecl->type));
if (f2) {
f = f2;
cd = base;
}
else
break;
}
DtoResolveDsymbol(f);
return llvm::cast<llvm::FunctionType>(DtoType(f->type));
}
//////////////////////////////////////////////////////////////////////////////////////////
DValue* DtoArgument(Parameter* fnarg, Expression* argexp)
{
Logger::println("DtoArgument");
LOG_SCOPE;
DValue* arg = argexp->toElem(gIR);
// ref/out arg
if (fnarg && (fnarg->storageClass & (STCref | STCout)))
{
Loc loc;
arg = new DImValue(argexp->type, makeLValue(loc, arg));
}
// 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(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);
}