mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-01 07:30:43 +03:00

This finally fixes the performance regression due to missing inlining after the switch to the DMD-style symbol emission logic (what later became FuncDeclaration::needsCodegen). In my tests, this slightly increased compile times in release mode (~7% for a set of small-ish programs) due to the extra codegen. The impact is that small because we were doing all the extra semantic{2, 3}s anyway (only to throw their results away) and still get to avoid the LLVM backend. The performance gains are, depending on the benchmark, spectacular, as this was responsible for things like std.array.front not getting inlined.
1335 lines
42 KiB
C++
1335 lines
42 KiB
C++
//===-- 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/functions.h"
|
||
#include "aggregate.h"
|
||
#include "declaration.h"
|
||
#include "id.h"
|
||
#include "init.h"
|
||
#include "module.h"
|
||
#include "mtype.h"
|
||
#include "statement.h"
|
||
#include "template.h"
|
||
#include "gen/abi.h"
|
||
#include "gen/arrays.h"
|
||
#include "gen/classes.h"
|
||
#include "gen/dvalue.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/llvm.h"
|
||
#include "gen/llvmhelpers.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/nested.h"
|
||
#include "gen/optimizer.h"
|
||
#include "gen/pragma.h"
|
||
#include "gen/runtime.h"
|
||
#include "gen/tollvm.h"
|
||
#if LDC_LLVM_VER >= 305
|
||
#include "llvm/Linker/Linker.h"
|
||
#else
|
||
#include "llvm/Linker.h"
|
||
#endif
|
||
#if LDC_LLVM_VER >= 303
|
||
#include "llvm/IR/Intrinsics.h"
|
||
#else
|
||
#include "llvm/Intrinsics.h"
|
||
#endif
|
||
#if LDC_LLVM_VER >= 305
|
||
#include "llvm/IR/CFG.h"
|
||
#else
|
||
#include "llvm/Support/CFG.h"
|
||
#endif
|
||
#include <iostream>
|
||
|
||
#if LDC_LLVM_VER == 302
|
||
namespace llvm
|
||
{
|
||
typedef llvm::Attributes Attribute;
|
||
}
|
||
#endif
|
||
|
||
llvm::FunctionType* DtoFunctionType(Type* type, IrFuncTy &irFty, Type* thistype, Type* nesttype,
|
||
bool isMain, bool isCtor, bool isIntrinsic)
|
||
{
|
||
IF_LOG Logger::println("DtoFunctionType(%s)", type->toChars());
|
||
LOG_SCOPE
|
||
|
||
// sanity check
|
||
assert(type->ty == Tfunction);
|
||
TypeFunction* f = static_cast<TypeFunction*>(type);
|
||
assert(f->next && "Encountered function type with invalid return type; "
|
||
"trying to codegen function ignored by the frontend?");
|
||
|
||
// Return cached type if available
|
||
if (irFty.funcType) return irFty.funcType;
|
||
|
||
TargetABI* abi = (isIntrinsic ? TargetABI::getIntrinsic() : gABI);
|
||
// Tell the ABI we're resolving a new function type
|
||
abi->newFunctionType(f);
|
||
|
||
// Do not modify irFty yet; this function may be called recursively if any
|
||
// of the argument types refer to this type.
|
||
IrFuncTy newIrFty;
|
||
|
||
// llvm idx counter
|
||
size_t lidx = 0;
|
||
|
||
// main needs a little special handling
|
||
if (isMain)
|
||
{
|
||
newIrFty.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 = llvm::Attribute::None;
|
||
#endif
|
||
|
||
// sret return
|
||
if (abi->returnInArg(f))
|
||
{
|
||
#if LDC_LLVM_VER >= 302
|
||
#if LDC_LLVM_VER >= 303
|
||
newIrFty.arg_sret = new IrFuncTyArg(rt, true,
|
||
llvm::AttrBuilder().addAttribute(llvm::Attribute::StructRet)
|
||
.addAttribute(llvm::Attribute::NoAlias)
|
||
#else
|
||
newIrFty.arg_sret = new IrFuncTyArg(rt, true, llvm::Attributes::get(gIR->context(),
|
||
llvm::AttrBuilder().addAttribute(llvm::Attributes::StructRet)
|
||
.addAttribute(llvm::Attributes::NoAlias)
|
||
#endif
|
||
#if LDC_LLVM_VER == 302
|
||
)
|
||
#endif
|
||
);
|
||
#else
|
||
newIrFty.arg_sret = new IrFuncTyArg(rt, true,
|
||
llvm::Attribute::StructRet | llvm::Attribute::NoAlias);
|
||
#endif
|
||
rt = Type::tvoid;
|
||
lidx++;
|
||
}
|
||
// sext/zext return
|
||
else
|
||
{
|
||
Type *t = rt;
|
||
if (f->isref)
|
||
t = t->pointerTo();
|
||
#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
|
||
newIrFty.ret = new IrFuncTyArg(rt, f->isref, a);
|
||
}
|
||
lidx++;
|
||
|
||
// member functions
|
||
if (thistype)
|
||
{
|
||
#if LDC_LLVM_VER >= 303
|
||
llvm::AttrBuilder attrBuilder;
|
||
// Issue 624: In case of a ctor 'this' is passed to the function and is also
|
||
// the return value. This could be a perfect case for the 'Returned' attribute.
|
||
// However the 'this' type and the return type are transformed in different
|
||
// ways, making them bitcast incompatible.
|
||
// Example: extern(C): struct Value { this(string) {} string s; }
|
||
// return type: { i64, i64 }
|
||
// this type: %ldc_github_624.Value*
|
||
// FIXME: (1) Investigate why the types are handled in different ways
|
||
// (2) The attributes are cleared by some abi implementations
|
||
// if (isCtor)
|
||
// attrBuilder.addAttribute(llvm::Attribute::Returned);
|
||
#endif
|
||
newIrFty.arg_this = new IrFuncTyArg(thistype, thistype->toBasetype()->ty == Tstruct
|
||
#if LDC_LLVM_VER >= 303
|
||
, attrBuilder
|
||
#endif
|
||
);
|
||
lidx++;
|
||
}
|
||
|
||
// and nested functions
|
||
else if (nesttype)
|
||
{
|
||
newIrFty.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
|
||
newIrFty.arg_arguments = new IrFuncTyArg(Type::dtypeinfo->type->arrayOf(), false);
|
||
lidx++;
|
||
// _argptr
|
||
#if LDC_LLVM_VER >= 303
|
||
newIrFty.arg_argptr = new IrFuncTyArg(Type::tvoid->pointerTo(), false,
|
||
llvm::AttrBuilder().addAttribute(llvm::Attribute::NoAlias)
|
||
.addAttribute(llvm::Attribute::NoCapture));
|
||
#elif LDC_LLVM_VER == 302
|
||
newIrFty.arg_argptr = new IrFuncTyArg(Type::tvoid->pointerTo(), false,
|
||
llvm::Attributes::get(gIR->context(), llvm::AttrBuilder().addAttribute(llvm::Attributes::NoAlias)
|
||
.addAttribute(llvm::Attributes::NoCapture)));
|
||
#else
|
||
newIrFty.arg_argptr = new IrFuncTyArg(Type::tvoid->pointerTo(), false,
|
||
llvm::Attribute::NoAlias | llvm::Attribute::NoCapture);
|
||
#endif
|
||
lidx++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Default to C-style varargs for non-extern(D) variadic functions.
|
||
// This seems to be what DMD does.
|
||
newIrFty.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();
|
||
newIrFty.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);
|
||
|
||
Type* argtype = arg->type;
|
||
#if LDC_LLVM_VER >= 302
|
||
llvm::AttrBuilder attrBuilder;
|
||
#else
|
||
llvm::Attributes a = llvm::Attribute::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 >= 302
|
||
if (!byref) attrBuilder.addAttribute(llvm::Attribute::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
|
||
newIrFty.args.push_back(new IrFuncTyArg(argtype, byref, a));
|
||
lidx++;
|
||
}
|
||
|
||
// let the abi rewrite the types as necesary
|
||
abi->rewriteFunctionType(f, newIrFty);
|
||
|
||
// Tell the ABI we're done with this function type
|
||
abi->doneWithFunctionType();
|
||
|
||
// Now we can modify irFty safely.
|
||
irFty = llvm_move(newIrFty);
|
||
|
||
// build the function type
|
||
std::vector<LLType*> argtypes;
|
||
argtypes.reserve(lidx);
|
||
|
||
if (irFty.arg_sret) argtypes.push_back(irFty.arg_sret->ltype);
|
||
if (irFty.arg_this) argtypes.push_back(irFty.arg_this->ltype);
|
||
if (irFty.arg_nest) argtypes.push_back(irFty.arg_nest->ltype);
|
||
if (irFty.arg_arguments) argtypes.push_back(irFty.arg_arguments->ltype);
|
||
if (irFty.arg_argptr) argtypes.push_back(irFty.arg_argptr->ltype);
|
||
|
||
size_t beg = argtypes.size();
|
||
size_t nargs2 = irFty.args.size();
|
||
for (size_t i = 0; i < nargs2; i++)
|
||
{
|
||
argtypes.push_back(irFty.args[i]->ltype);
|
||
}
|
||
|
||
// reverse params?
|
||
if (irFty.reverseParams && nargs2 > 1)
|
||
{
|
||
std::reverse(argtypes.begin() + beg, argtypes.end());
|
||
}
|
||
|
||
irFty.funcType = LLFunctionType::get(irFty.ret->ltype, argtypes, irFty.c_vararg);
|
||
|
||
IF_LOG Logger::cout() << "Final function type: " << *irFty.funcType << "\n";
|
||
|
||
return irFty.funcType;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
#include "llvm/Support/raw_ostream.h"
|
||
#include "llvm/Support/SourceMgr.h"
|
||
#if LDC_LLVM_VER >= 305
|
||
#include "llvm/AsmParser/Parser.h"
|
||
#else
|
||
#include "llvm/Assembly/Parser.h"
|
||
#endif
|
||
|
||
LLFunction* DtoInlineIRFunction(FuncDeclaration* fdecl)
|
||
{
|
||
const char* mangled_name = fdecl->mangleExact();
|
||
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;
|
||
|
||
#if LDC_LLVM_VER >= 306
|
||
std::unique_ptr<llvm::Module> m = llvm::parseAssemblyString(
|
||
stream.str().c_str(), err, gIR->context());
|
||
#elif LDC_LLVM_VER >= 303
|
||
llvm::Module* m = llvm::ParseAssemblyString(
|
||
stream.str().c_str(), NULL, err, gIR->context());
|
||
#else
|
||
llvm::ParseAssemblyString(
|
||
stream.str().c_str(), gIR->module, err, gIR->context());
|
||
#endif
|
||
|
||
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());
|
||
|
||
#if LDC_LLVM_VER >= 303
|
||
std::string errstr2 = "";
|
||
#if LDC_LLVM_VER >= 306
|
||
llvm::Linker(gIR->module).linkInModule(m.get(), &errstr2);
|
||
#else
|
||
llvm::Linker(gIR->module).linkInModule(m, &errstr2);
|
||
#endif
|
||
if(errstr2 != "")
|
||
error(tinst->loc,
|
||
"Error when linking in llvm inline ir: %s", errstr2.c_str());
|
||
#endif
|
||
|
||
LLFunction* fun = gIR->module->getFunction(mangled_name);
|
||
fun->setLinkage(llvm::GlobalValue::LinkOnceODRLinkage);
|
||
fun->addFnAttr(llvm::Attribute::AlwaysInline);
|
||
return fun;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
static llvm::FunctionType* DtoVaFunctionType(FuncDeclaration* fdecl)
|
||
{
|
||
IrFuncTy &irFty = fdecl->irFty;
|
||
if (irFty.funcType) return irFty.funcType;
|
||
|
||
irFty.ret = new IrFuncTyArg(Type::tvoid, false);
|
||
|
||
irFty.args.push_back(new IrFuncTyArg(Type::tvoid->pointerTo(), false));
|
||
|
||
if (fdecl->llvmInternal == LLVMva_start)
|
||
irFty.funcType = GET_INTRINSIC_DECL(vastart)->getFunctionType();
|
||
else if (fdecl->llvmInternal == LLVMva_copy) {
|
||
irFty.funcType = GET_INTRINSIC_DECL(vacopy)->getFunctionType();
|
||
irFty.args.push_back(new IrFuncTyArg(Type::tvoid->pointerTo(), false));
|
||
}
|
||
else if (fdecl->llvmInternal == LLVMva_end)
|
||
irFty.funcType = GET_INTRINSIC_DECL(vaend)->getFunctionType();
|
||
assert(irFty.funcType);
|
||
|
||
return irFty.funcType;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
llvm::FunctionType* DtoFunctionType(FuncDeclaration* fdecl)
|
||
{
|
||
// handle for C vararg intrinsics
|
||
if (fdecl->isVaIntrinsic())
|
||
return DtoVaFunctionType(fdecl);
|
||
|
||
Type *dthis=0, *dnest=0;
|
||
|
||
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
|
||
if (fdecl->needThis()) {
|
||
if (AggregateDeclaration* ad = fdecl->isMember2()) {
|
||
IF_LOG 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 {
|
||
IF_LOG 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, fdecl->irFty, dthis, dnest,
|
||
fdecl->isMain(), fdecl->isCtorDeclaration(),
|
||
fdecl->llvmInternal == LLVMintrinsic);
|
||
|
||
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);
|
||
|
||
getIrFunc(fdecl)->func = func;
|
||
return func;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
void DtoResolveFunction(FuncDeclaration* fdecl)
|
||
{
|
||
if ((!global.params.useUnitTests || !fdecl->type) && fdecl->isUnitTestDeclaration()) {
|
||
IF_LOG Logger::println("Ignoring unittest %s", fdecl->toPrettyChars());
|
||
return; // ignore declaration completely
|
||
}
|
||
|
||
if (fdecl->ir.isResolved()) return;
|
||
fdecl->ir.setResolved();
|
||
|
||
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())
|
||
{
|
||
if (TemplateDeclaration* tempdecl = tinst->tempdecl->isTemplateDeclaration())
|
||
{
|
||
if (tempdecl->llvmInternal == LLVMva_arg)
|
||
{
|
||
Logger::println("magic va_arg found");
|
||
fdecl->llvmInternal = LLVMva_arg;
|
||
fdecl->ir.setDefined();
|
||
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");
|
||
assert(fdecl->llvmInternal == LLVMintrinsic);
|
||
assert(fdecl->mangleOverride);
|
||
}
|
||
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))
|
||
{
|
||
tempdecl->error("invalid __asm declaration, must be a D style variadic with no explicit parameters");
|
||
fatal();
|
||
}
|
||
fdecl->llvmInternal = LLVMinline_asm;
|
||
fdecl->ir.setDefined();
|
||
return; // this gets mapped to a special inline asm call, no point in going on.
|
||
}
|
||
else if (tempdecl->llvmInternal == LLVMinline_ir)
|
||
{
|
||
Logger::println("magic inline ir found");
|
||
fdecl->llvmInternal = LLVMinline_ir;
|
||
fdecl->linkage = LINKc;
|
||
Type* type = fdecl->type;
|
||
assert(type->ty == Tfunction);
|
||
static_cast<TypeFunction*>(type)->linkage = LINKc;
|
||
|
||
DtoFunctionType(fdecl);
|
||
DtoDeclareFunction(fdecl);
|
||
fdecl->ir.setDefined();
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
DtoFunctionType(fdecl);
|
||
|
||
IF_LOG 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)
|
||
{
|
||
IrFuncTy &irFty = fdecl->irFty;
|
||
llvm::AttributeSet old = func->getAttributes();
|
||
llvm::AttributeSet existingAttrs[] = { old.getFnAttributes(), old.getRetAttributes() };
|
||
llvm::AttributeSet newAttrs = llvm::AttributeSet::get(gIR->context(), existingAttrs);
|
||
|
||
int idx = 0;
|
||
|
||
// handle implicit args
|
||
#define ADD_PA(X) \
|
||
if (irFty.X) { \
|
||
if (irFty.X->attrs.hasAttributes()) { \
|
||
llvm::AttributeSet a = llvm::AttributeSet::get(gIR->context(), idx, irFty.X->attrs); \
|
||
newAttrs = newAttrs.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 = irFty.args[k]->attrs;
|
||
if (a.hasAttributes())
|
||
{
|
||
unsigned i = idx + (irFty.reverseParams ? n-k-1 : k);
|
||
llvm::AttributeSet as = llvm::AttributeSet::get(gIR->context(), i, a);
|
||
newAttrs = newAttrs.addAttributes(gIR->context(), i, as);
|
||
}
|
||
}
|
||
|
||
// Store the final attribute set
|
||
func->setAttributes(newAttrs);
|
||
}
|
||
#else
|
||
static void set_param_attrs(TypeFunction* f, llvm::Function* func, FuncDeclaration* fdecl)
|
||
{
|
||
IrFuncTy &irFty = fdecl->irFty;
|
||
LLSmallVector<llvm::AttributeWithIndex, 9> attrs;
|
||
|
||
int idx = 0;
|
||
|
||
// handle implicit args
|
||
#define ADD_PA(X) \
|
||
if (irFty.X) { \
|
||
if (HAS_ATTRIBUTES(irFty.X->attrs)) { \
|
||
attrs.push_back(llvm::AttributeWithIndex::get(idx, irFty.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, llvm::Attribute::None);
|
||
#endif
|
||
|
||
for (size_t k = 0; k < n; ++k)
|
||
{
|
||
Parameter* fnarg = Parameter::getNth(f->parameters, k);
|
||
assert(fnarg);
|
||
|
||
attrptr[k] = irFty.args[k]->attrs;
|
||
}
|
||
|
||
// reverse params?
|
||
if (irFty.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.isDeclared()) return;
|
||
fdecl->ir.setDeclared();
|
||
|
||
IF_LOG Logger::println("DtoDeclareFunction(%s): %s", fdecl->toPrettyChars(), fdecl->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
if (fdecl->isUnitTestDeclaration() && !global.params.useUnitTests)
|
||
{
|
||
Logger::println("unit tests not enabled");
|
||
return;
|
||
}
|
||
|
||
//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);
|
||
|
||
// create IrFunction
|
||
IrFunction *irFunc = getIrFunc(fdecl, true);
|
||
|
||
LLFunction* vafunc = 0;
|
||
if (fdecl->isVaIntrinsic())
|
||
vafunc = DtoDeclareVaFunction(fdecl);
|
||
|
||
// calling convention
|
||
LINK link = f->linkage;
|
||
if (vafunc || fdecl->llvmInternal == LLVMintrinsic
|
||
// 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()
|
||
)
|
||
{
|
||
link = LINKc;
|
||
}
|
||
|
||
// mangled name
|
||
std::string mangledName(fdecl->mangleExact());
|
||
mangledName = gABI->mangleForLLVM(mangledName, link);
|
||
|
||
// construct function
|
||
LLFunctionType* functype = DtoFunctionType(fdecl);
|
||
LLFunction* func = vafunc ? vafunc : gIR->module->getFunction(mangledName);
|
||
if (!func) {
|
||
if(fdecl->llvmInternal == LLVMinline_ir)
|
||
{
|
||
func = DtoInlineIRFunction(fdecl);
|
||
}
|
||
else
|
||
{
|
||
// All function declarations are "external" - any other linkage type
|
||
// is set when actually defining the function.
|
||
func = LLFunction::Create(functype,
|
||
llvm::GlobalValue::ExternalLinkage, mangledName, 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->mangleExact());
|
||
fatal();
|
||
}
|
||
|
||
func->setCallingConv(gABI->callingConv(link));
|
||
|
||
IF_LOG Logger::cout() << "func = " << *func << std::endl;
|
||
|
||
// add func to IRFunc
|
||
irFunc->func = func;
|
||
|
||
// parameter attributes
|
||
if (!fdecl->isIntrinsic()) {
|
||
set_param_attrs(f, func, fdecl);
|
||
if (global.params.disableRedZone) {
|
||
func->addFnAttr(llvm::Attribute::NoRedZone);
|
||
}
|
||
}
|
||
|
||
// 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 (fdecl->neverInline)
|
||
{
|
||
irFunc->setNeverInline();
|
||
}
|
||
|
||
if (fdecl->llvmInternal == LLVMglobal_crt_ctor || fdecl->llvmInternal == LLVMglobal_crt_dtor)
|
||
{
|
||
AppendFunctionToLLVMGlobalCtorsDtors(func, fdecl->priority, fdecl->llvmInternal == LLVMglobal_crt_ctor);
|
||
}
|
||
|
||
IrFuncTy &irFty = fdecl->irFty;
|
||
|
||
// if (!declareOnly)
|
||
{
|
||
// name parameters
|
||
llvm::Function::arg_iterator iarg = func->arg_begin();
|
||
|
||
if (irFty.arg_sret) {
|
||
iarg->setName(".sret_arg");
|
||
irFunc->retArg = iarg;
|
||
++iarg;
|
||
}
|
||
|
||
if (irFty.arg_this) {
|
||
iarg->setName(".this_arg");
|
||
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(!isIrParameterCreated(v));
|
||
IrParameter *irParam = getIrParameter(v, true);
|
||
irParam->value = iarg;
|
||
irParam->arg = irFty.arg_this;
|
||
irParam->isVthis = true;
|
||
}
|
||
|
||
++iarg;
|
||
}
|
||
else if (irFty.arg_nest) {
|
||
iarg->setName(".nest_arg");
|
||
irFunc->nestArg = iarg;
|
||
assert(irFunc->nestArg);
|
||
++iarg;
|
||
}
|
||
|
||
if (irFty.arg_argptr) {
|
||
iarg->setName("._arguments");
|
||
irFunc->_arguments = iarg;
|
||
++iarg;
|
||
iarg->setName("._argptr");
|
||
irFunc->_argptr = iarg;
|
||
++iarg;
|
||
}
|
||
|
||
// we never reference parameters of function prototypes
|
||
unsigned int k = 0;
|
||
for (; iarg != func->arg_end(); ++iarg)
|
||
{
|
||
if (fdecl->parameters && fdecl->parameters->dim > k)
|
||
{
|
||
int paramIndex = irFty.reverseParams ? fdecl->parameters->dim-k-1 : k;
|
||
Dsymbol* argsym = static_cast<Dsymbol*>(fdecl->parameters->data[paramIndex]);
|
||
|
||
VarDeclaration* argvd = argsym->isVarDeclaration();
|
||
assert(argvd);
|
||
assert(!isIrLocalCreated(argvd));
|
||
std::string str(argvd->ident->toChars());
|
||
str.append("_arg");
|
||
iarg->setName(str);
|
||
|
||
IrParameter *irParam = getIrParameter(argvd, true);
|
||
irParam->value = iarg;
|
||
irParam->arg = irFty.args[paramIndex];
|
||
|
||
k++;
|
||
}
|
||
else
|
||
{
|
||
iarg->setName("unnamed");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// FIXME: this isn't too pretty!
|
||
|
||
void DtoDefineFunction(FuncDeclaration* fd)
|
||
{
|
||
IF_LOG Logger::println("DtoDefineFunction(%s): %s", fd->toPrettyChars(), fd->loc.toChars());
|
||
LOG_SCOPE;
|
||
|
||
if (fd->ir.isDefined()) return;
|
||
|
||
if ((fd->type && fd->type->ty == Terror) ||
|
||
(fd->type && fd->type->ty == Tfunction && static_cast<TypeFunction *>(fd->type)->next == NULL) ||
|
||
(fd->type && fd->type->ty == Tfunction && static_cast<TypeFunction *>(fd->type)->next->ty == Terror))
|
||
{
|
||
IF_LOG Logger::println("Ignoring; has error type, no return type or returns error type");
|
||
fd->ir.setDefined();
|
||
return;
|
||
}
|
||
|
||
if (fd->semanticRun == PASSsemanticdone)
|
||
{
|
||
/* What happened is this function failed semantic3() with errors,
|
||
* but the errors were gagged.
|
||
* Try to reproduce those errors, and then fail.
|
||
*/
|
||
error(fd->loc, "errors compiling function %s", fd->toPrettyChars());
|
||
fd->ir.setDefined();
|
||
return;
|
||
}
|
||
|
||
DtoResolveFunction(fd);
|
||
|
||
if (fd->isUnitTestDeclaration() && !global.params.useUnitTests)
|
||
{
|
||
IF_LOG Logger::println("No code generation for unit test declaration %s", fd->toChars());
|
||
fd->ir.setDefined();
|
||
return;
|
||
}
|
||
|
||
if (fd->semanticRun != PASSsemantic3done || fd->ident == Id::empty)
|
||
{
|
||
// We cannot ever generate code for this function. DMD would just filter
|
||
// it out by checking needsCodegen(), but we want to emit functions even
|
||
// if we do not need as available_exernally for inlining purposes.
|
||
assert(!fd->needsCodegen());
|
||
|
||
IF_LOG Logger::println("No code generation for incomplete function '%s'",
|
||
fd->toPrettyChars());
|
||
fd->ir.setDefined();
|
||
return;
|
||
}
|
||
|
||
// If we do not know already that we do not need to emit this because its
|
||
// from an extra inlining semantic, check whether we can omit it anyway.
|
||
if (!fd->availableExternally && !fd->needsCodegen())
|
||
{
|
||
IF_LOG Logger::println("Emitting '%s' as available_externally",
|
||
fd->toPrettyChars());
|
||
fd->availableExternally = true;
|
||
}
|
||
|
||
DtoDeclareFunction(fd);
|
||
assert(fd->ir.isDeclared());
|
||
|
||
// DtoResolveFunction might also set the defined flag for functions we
|
||
// should not touch.
|
||
if (fd->ir.isDefined()) return;
|
||
fd->ir.setDefined();
|
||
|
||
// We cannot emit nested functions with parents that have not gone through
|
||
// semantic analysis. This can happen as DMD leaks some template instances
|
||
// from constraints into the module member list. DMD gets away with being
|
||
// sloppy as functions in template contraints obviously never need to access
|
||
// data from the template function itself, but it would still mess up our
|
||
// nested context creation code.
|
||
FuncDeclaration* parent = fd;
|
||
while ((parent = getParentFunc(parent, true)))
|
||
{
|
||
if (parent->semanticRun != PASSsemantic3done || parent->semantic3Errors)
|
||
{
|
||
IF_LOG Logger::println("Ignoring nested function with unanalyzed parent.");
|
||
return;
|
||
}
|
||
}
|
||
|
||
assert(fd->semanticRun == PASSsemantic3done);
|
||
assert(fd->ident != Id::empty);
|
||
|
||
if (fd->isUnitTestDeclaration()) {
|
||
gIR->unitTests.push_back(fd);
|
||
} else if (fd->isSharedStaticCtorDeclaration()) {
|
||
gIR->sharedCtors.push_back(fd);
|
||
} else if (StaticDtorDeclaration *dtorDecl = fd->isSharedStaticDtorDeclaration()) {
|
||
gIR->sharedDtors.push_front(fd);
|
||
if (dtorDecl->vgate)
|
||
gIR->sharedGates.push_front(dtorDecl->vgate);
|
||
} else if (fd->isStaticCtorDeclaration()) {
|
||
gIR->ctors.push_back(fd);
|
||
} else if (StaticDtorDeclaration *dtorDecl = fd->isStaticDtorDeclaration()) {
|
||
gIR->dtors.push_front(fd);
|
||
if (dtorDecl->vgate)
|
||
gIR->gates.push_front(dtorDecl->vgate);
|
||
}
|
||
|
||
|
||
// if this function is naked, we take over right away! no standard processing!
|
||
if (fd->naked)
|
||
{
|
||
DtoDefineNakedFunction(fd);
|
||
return;
|
||
}
|
||
|
||
IrFuncTy &irFty = fd->irFty;
|
||
IrFunction *irFunc = getIrFunc(fd);
|
||
|
||
// debug info
|
||
irFunc->diSubprogram = gIR->DBuilder.EmitSubProgram(fd);
|
||
|
||
Type* t = fd->type->toBasetype();
|
||
TypeFunction* f = static_cast<TypeFunction*>(t);
|
||
// assert(f->irtype);
|
||
|
||
llvm::Function* func = irFunc->func;
|
||
|
||
// is there a body?
|
||
if (fd->fbody == NULL)
|
||
return;
|
||
|
||
IF_LOG Logger::println("Doing function body for: %s", fd->toChars());
|
||
gIR->functions.push_back(irFunc);
|
||
|
||
if (fd->isMain())
|
||
gIR->emitMain = true;
|
||
|
||
func->setLinkage(DtoLinkage(fd));
|
||
|
||
// On x86_64, always set 'uwtable' for System V ABI compatibility.
|
||
// TODO: Find a better place for this.
|
||
if (global.params.targetTriple.getArch() == llvm::Triple::x86_64)
|
||
{
|
||
func->addFnAttr(llvm::Attribute::UWTable);
|
||
}
|
||
#if LDC_LLVM_VER >= 303
|
||
if (opts::sanitize != opts::None) {
|
||
// Set the required sanitizer attribute.
|
||
if (opts::sanitize == opts::AddressSanitizer) {
|
||
func->addFnAttr(llvm::Attribute::SanitizeAddress);
|
||
}
|
||
|
||
if (opts::sanitize == opts::MemorySanitizer) {
|
||
func->addFnAttr(llvm::Attribute::SanitizeMemory);
|
||
}
|
||
|
||
if (opts::sanitize == opts::ThreadSanitizer) {
|
||
func->addFnAttr(llvm::Attribute::SanitizeThread);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
llvm::BasicBlock* beginbb = llvm::BasicBlock::Create(gIR->context(), "", 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);
|
||
irFunc->allocapoint = allocaPoint;
|
||
|
||
// debug info - after all allocas, but before any llvm.dbg.declare etc
|
||
gIR->DBuilder.EmitFuncStart(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 (irFty.arg_this)
|
||
{
|
||
LLValue* thisvar = irFunc->thisArg;
|
||
assert(thisvar);
|
||
|
||
LLValue* thismem = thisvar;
|
||
if (!irFty.arg_this->byref)
|
||
{
|
||
thismem = DtoRawAlloca(thisvar->getType(), 0, "this"); // FIXME: align?
|
||
DtoStore(thisvar, thismem);
|
||
irFunc->thisArg = thismem;
|
||
}
|
||
|
||
assert(getIrParameter(fd->vthis)->value == thisvar);
|
||
getIrParameter(fd->vthis)->value = thismem;
|
||
|
||
gIR->DBuilder.EmitLocalVariable(thismem, fd->vthis);
|
||
}
|
||
|
||
// give the 'nestArg' storage
|
||
if (irFty.arg_nest)
|
||
{
|
||
LLValue *nestArg = irFunc->nestArg;
|
||
LLValue *val = DtoRawAlloca(nestArg->getType(), 0, "nestedFrame");
|
||
DtoStore(nestArg, val);
|
||
irFunc->nestArg = val;
|
||
}
|
||
|
||
// give arguments storage
|
||
// and debug info
|
||
if (fd->parameters)
|
||
{
|
||
size_t n = irFty.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 = getIrParameter(vd);
|
||
assert(irparam);
|
||
|
||
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 = i1ToI8(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);
|
||
irFty.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)
|
||
gIR->DBuilder.EmitLocalVariable(irparam->value, vd);
|
||
}
|
||
}
|
||
|
||
FuncGen fg;
|
||
irFunc->gen = &fg;
|
||
|
||
DtoCreateNestedContext(fd);
|
||
|
||
if (fd->vresult && !
|
||
fd->vresult->nestedrefs.dim // FIXME: not sure here :/
|
||
)
|
||
{
|
||
DtoVarDeclaration(fd->vresult);
|
||
}
|
||
|
||
// copy _argptr and _arguments to a memory location
|
||
if (f->linkage == LINKd && f->varargs == 1)
|
||
{
|
||
// _argptr
|
||
LLValue* argptrmem = DtoRawAlloca(irFunc->_argptr->getType(), 0, "_argptr_mem");
|
||
new llvm::StoreInst(irFunc->_argptr, argptrmem, gIR->scopebb());
|
||
irFunc->_argptr = argptrmem;
|
||
|
||
// _arguments
|
||
LLValue* argumentsmem = DtoRawAlloca(irFunc->_arguments->getType(), 0, "_arguments_mem");
|
||
new llvm::StoreInst(irFunc->_arguments, argumentsmem, gIR->scopebb());
|
||
irFunc->_arguments = argumentsmem;
|
||
}
|
||
|
||
// output function body
|
||
Statement_toIR(fd->fbody, gIR);
|
||
irFunc->gen = 0;
|
||
|
||
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
|
||
gIR->DBuilder.EmitFuncEnd(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);
|
||
}
|
||
|
||
// 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();
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
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;
|
||
}
|
||
|
||
DtoResolveFunction(f);
|
||
return llvm::cast<llvm::FunctionType>(DtoType(f->type));
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
DValue* DtoArgument(Parameter* fnarg, Expression* argexp)
|
||
{
|
||
IF_LOG 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)
|
||
{
|
||
IF_LOG 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);
|
||
}
|