mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-04 00:55:49 +03:00

This is most visible on x86 (32-bit), where the stack alignment is off otherwise. This change is quite messy because many places assumed that there was always exactly one LLVM parameter per TypeFunction::parameters entry.
884 lines
32 KiB
C++
884 lines
32 KiB
C++
//===-- tocall.cpp --------------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "declaration.h"
|
||
#include "id.h"
|
||
#include "mtype.h"
|
||
#include "target.h"
|
||
#include "pragma.h"
|
||
#include "gen/abi.h"
|
||
#include "gen/dvalue.h"
|
||
#include "gen/functions.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/llvm.h"
|
||
#include "gen/llvmhelpers.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/nested.h"
|
||
#include "gen/tollvm.h"
|
||
#include "ir/irfunction.h"
|
||
#include "ir/irtype.h"
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
IrFuncTy &DtoIrTypeFunction(DValue* fnval)
|
||
{
|
||
if (DFuncValue* dfnval = fnval->isFunc())
|
||
{
|
||
if (dfnval->func)
|
||
return getIrFunc(dfnval->func)->irFty;
|
||
}
|
||
|
||
Type* type = stripModifiers(fnval->getType()->toBasetype());
|
||
DtoType(type);
|
||
assert(type->ctype);
|
||
return type->ctype->getIrFuncTy();
|
||
}
|
||
|
||
TypeFunction* DtoTypeFunction(DValue* fnval)
|
||
{
|
||
Type* type = fnval->getType()->toBasetype();
|
||
if (type->ty == Tfunction)
|
||
{
|
||
return static_cast<TypeFunction*>(type);
|
||
}
|
||
else if (type->ty == Tdelegate)
|
||
{
|
||
// FIXME: There is really no reason why the function type should be
|
||
// unmerged at this stage, but the frontend still seems to produce such
|
||
// cases; for example for the uint(uint) next type of the return type of
|
||
// (&zero)(), leading to a crash in DtoCallFunction:
|
||
// ---
|
||
// void test8198() {
|
||
// uint delegate(uint) zero() { return null; }
|
||
// auto a = (&zero)()(0);
|
||
// }
|
||
// ---
|
||
// Calling merge() here works around the symptoms, but does not fix the
|
||
// root cause.
|
||
|
||
Type* next = type->nextOf()->merge();
|
||
assert(next->ty == Tfunction);
|
||
return static_cast<TypeFunction*>(next);
|
||
}
|
||
|
||
llvm_unreachable("Cannot get TypeFunction* from non lazy/function/delegate");
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLValue* DtoCallableValue(DValue* fn)
|
||
{
|
||
Type* type = fn->getType()->toBasetype();
|
||
if (type->ty == Tfunction)
|
||
{
|
||
return fn->getRVal();
|
||
}
|
||
else if (type->ty == Tdelegate)
|
||
{
|
||
if (fn->isLVal())
|
||
{
|
||
LLValue* dg = fn->getLVal();
|
||
LLValue* funcptr = DtoGEPi(dg, 0, 1);
|
||
return DtoLoad(funcptr, ".funcptr");
|
||
}
|
||
else
|
||
{
|
||
LLValue* dg = fn->getRVal();
|
||
assert(isaStruct(dg));
|
||
return gIR->ir->CreateExtractValue(dg, 1, ".funcptr");
|
||
}
|
||
}
|
||
|
||
llvm_unreachable("Not a callable type.");
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
LLFunctionType* DtoExtractFunctionType(LLType* type)
|
||
{
|
||
if (LLFunctionType* fty = isaFunction(type))
|
||
return fty;
|
||
else if (LLPointerType* pty = isaPointer(type))
|
||
{
|
||
if (LLFunctionType* fty = isaFunction(pty->getElementType()))
|
||
return fty;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
static void addExplicitArguments(std::vector<LLValue*>& args, AttrSet& attrs,
|
||
IrFuncTy& irFty, LLFunctionType* callableTy, const std::vector<DValue*>& argvals, int numFormalParams)
|
||
{
|
||
// Number of arguments added to the LLVM type that are implicit on the
|
||
// frontend side of things (this, context pointers, etc.)
|
||
const size_t implicitLLArgCount = args.size();
|
||
|
||
// Number of formal arguments in the LLVM type (i.e. excluding varargs).
|
||
const size_t formalLLArgCount = irFty.args.size();
|
||
|
||
// The number of explicit arguments in the D call expression (including
|
||
// varargs), not all of which necessarily generate a LLVM argument.
|
||
const size_t explicitDArgCount = argvals.size();
|
||
|
||
// construct and initialize an IrFuncTyArg object for each vararg
|
||
std::vector<IrFuncTyArg*> optionalIrArgs;
|
||
for (size_t i = numFormalParams; i < explicitDArgCount; i++) {
|
||
Type* argType = argvals[i]->getType();
|
||
bool passByVal = gABI->passByVal(argType);
|
||
|
||
AttrBuilder initialAttrs;
|
||
if (passByVal)
|
||
initialAttrs.add(LDC_ATTRIBUTE(ByVal));
|
||
else
|
||
initialAttrs.add(DtoShouldExtend(argType));
|
||
|
||
optionalIrArgs.push_back(new IrFuncTyArg(argType, passByVal, initialAttrs));
|
||
optionalIrArgs.back()->parametersIdx = i;
|
||
}
|
||
|
||
// let the ABI rewrite the IrFuncTyArg objects
|
||
gABI->rewriteVarargs(irFty, optionalIrArgs);
|
||
|
||
const size_t explicitLLArgCount = formalLLArgCount + optionalIrArgs.size();
|
||
args.resize(implicitLLArgCount + explicitLLArgCount, static_cast<llvm::Value*>(0));
|
||
|
||
// Iterate the explicit arguments from left to right in the D source,
|
||
// which is the reverse of the LLVM order if irFty.reverseParams is true.
|
||
for (size_t i = 0; i < explicitLLArgCount; ++i)
|
||
{
|
||
const bool isVararg = (i >= irFty.args.size());
|
||
IrFuncTyArg* irArg = NULL;
|
||
if (isVararg)
|
||
irArg = optionalIrArgs[i - numFormalParams];
|
||
else
|
||
irArg = irFty.args[i];
|
||
|
||
DValue* const argval = argvals[irArg->parametersIdx];
|
||
Type* const argType = argval->getType();
|
||
|
||
llvm::Value* llVal = NULL;
|
||
if (isVararg)
|
||
llVal = irFty.putParam(argType, *irArg, argval);
|
||
else
|
||
llVal = irFty.putParam(argType, i, argval);
|
||
|
||
const size_t llArgIdx = implicitLLArgCount +
|
||
(irFty.reverseParams ? explicitLLArgCount - i - 1 : i);
|
||
llvm::Type* const callableArgType =
|
||
(isVararg ? NULL : callableTy->getParamType(llArgIdx));
|
||
|
||
// Hack around LDC assuming structs and static arrays are in memory:
|
||
// If the function wants a struct, and the argument value is a
|
||
// pointer to a struct, load from it before passing it in.
|
||
if (isaPointer(llVal) && DtoIsPassedByRef(argType) &&
|
||
((!isVararg && !isaPointer(callableArgType)) ||
|
||
(isVararg && !irArg->byref && !irArg->isByVal())))
|
||
{
|
||
Logger::println("Loading struct type for function argument");
|
||
llVal = DtoLoad(llVal);
|
||
}
|
||
|
||
// parameter type mismatch, this is hard to get rid of
|
||
if (!isVararg && llVal->getType() != callableArgType)
|
||
{
|
||
IF_LOG
|
||
{
|
||
Logger::cout() << "arg: " << *llVal << '\n';
|
||
Logger::cout() << "expects: " << *callableArgType << '\n';
|
||
}
|
||
if (isaStruct(llVal))
|
||
llVal = DtoAggrPaint(llVal, callableArgType);
|
||
else
|
||
llVal = DtoBitCast(llVal, callableArgType);
|
||
}
|
||
|
||
args[llArgIdx] = llVal;
|
||
// +1 as index 0 contains the function attributes.
|
||
attrs.add(llArgIdx + 1, irArg->attrs);
|
||
|
||
if (isVararg)
|
||
delete irArg;
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
static LLValue* getTypeinfoArrayArgumentForDVarArg(Expressions* arguments, int begin)
|
||
{
|
||
IF_LOG Logger::println("doing d-style variadic arguments");
|
||
LOG_SCOPE
|
||
|
||
// number of non variadic args
|
||
IF_LOG Logger::println("num non vararg params = %d", begin);
|
||
|
||
// get n args in arguments list
|
||
size_t n_arguments = arguments ? arguments->dim : 0;
|
||
|
||
const size_t numVariadicArgs = n_arguments - begin;
|
||
|
||
// build type info array
|
||
LLType* typeinfotype = DtoType(Type::dtypeinfo->type);
|
||
LLArrayType* typeinfoarraytype = LLArrayType::get(typeinfotype, numVariadicArgs);
|
||
|
||
llvm::GlobalVariable* typeinfomem =
|
||
new llvm::GlobalVariable(gIR->module, typeinfoarraytype, true, llvm::GlobalValue::InternalLinkage, NULL, "._arguments.storage");
|
||
IF_LOG Logger::cout() << "_arguments storage: " << *typeinfomem << '\n';
|
||
|
||
std::vector<LLConstant*> vtypeinfos;
|
||
vtypeinfos.reserve(n_arguments);
|
||
for (size_t i=begin; i<n_arguments; i++)
|
||
{
|
||
vtypeinfos.push_back(DtoTypeInfoOf((*arguments)[i]->type));
|
||
}
|
||
|
||
// apply initializer
|
||
LLConstant* tiinits = LLConstantArray::get(typeinfoarraytype, vtypeinfos);
|
||
typeinfomem->setInitializer(tiinits);
|
||
|
||
// put data in d-array
|
||
LLConstant* pinits[] = {
|
||
DtoConstSize_t(numVariadicArgs),
|
||
llvm::ConstantExpr::getBitCast(typeinfomem, getPtrToType(typeinfotype))
|
||
};
|
||
LLType* tiarrty = DtoType(Type::dtypeinfo->type->arrayOf());
|
||
tiinits = LLConstantStruct::get(isaStruct(tiarrty), llvm::ArrayRef<LLConstant*>(pinits));
|
||
LLValue* typeinfoarrayparam = new llvm::GlobalVariable(gIR->module, tiarrty,
|
||
true, llvm::GlobalValue::InternalLinkage, tiinits, "._arguments.array");
|
||
|
||
return DtoLoad(typeinfoarrayparam);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
bool DtoLowerMagicIntrinsic(IRState* p, FuncDeclaration* fndecl, CallExp *e, DValue*& result)
|
||
{
|
||
// va_start instruction
|
||
if (fndecl->llvmInternal == LLVMva_start) {
|
||
if (e->arguments->dim < 1 || e->arguments->dim > 2) {
|
||
e->error("va_start instruction expects 1 (or 2) arguments");
|
||
fatal();
|
||
}
|
||
LLValue* pAp = toElem((*e->arguments)[0])->getLVal(); // va_list*
|
||
// variadic extern(D) function with implicit _argptr?
|
||
if (LLValue* pArgptr = p->func()->_argptr) {
|
||
DtoStore(DtoLoad(pArgptr), pAp); // ap = _argptr
|
||
result = new DImValue(e->type, pAp);
|
||
} else {
|
||
LLValue* vaStartArg = gABI->prepareVaStart(pAp);
|
||
result = new DImValue(e->type, p->ir->CreateCall(
|
||
GET_INTRINSIC_DECL(vastart), vaStartArg, ""));
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// va_copy instruction
|
||
if (fndecl->llvmInternal == LLVMva_copy) {
|
||
if (e->arguments->dim != 2) {
|
||
e->error("va_copy instruction expects 2 arguments");
|
||
fatal();
|
||
}
|
||
LLValue* pDest = toElem((*e->arguments)[0])->getLVal(); // va_list*
|
||
LLValue* src = toElem((*e->arguments)[1])->getRVal(); // va_list
|
||
gABI->vaCopy(pDest, src);
|
||
result = new DVarValue(e->type, pDest);
|
||
return true;
|
||
}
|
||
|
||
// va_arg instruction
|
||
if (fndecl->llvmInternal == LLVMva_arg) {
|
||
if (e->arguments->dim != 1) {
|
||
e->error("va_arg instruction expects 1 argument");
|
||
fatal();
|
||
}
|
||
LLValue* pAp = toElem((*e->arguments)[0])->getLVal(); // va_list*
|
||
LLValue* vaArgArg = gABI->prepareVaArg(pAp);
|
||
LLType* llType = DtoType(e->type);
|
||
if (DtoIsPassedByRef(e->type))
|
||
llType = getPtrToType(llType);
|
||
result = new DImValue(e->type, p->ir->CreateVAArg(vaArgArg, llType));
|
||
return true;
|
||
}
|
||
|
||
// C alloca
|
||
if (fndecl->llvmInternal == LLVMalloca) {
|
||
if (e->arguments->dim != 1) {
|
||
e->error("alloca expects 1 arguments");
|
||
fatal();
|
||
}
|
||
Expression* exp = (*e->arguments)[0];
|
||
DValue* expv = toElem(exp);
|
||
if (expv->getType()->toBasetype()->ty != Tint32)
|
||
expv = DtoCast(e->loc, expv, Type::tint32);
|
||
result = new DImValue(e->type, p->ir->CreateAlloca(
|
||
LLType::getInt8Ty(p->context()), expv->getRVal(), ".alloca"));
|
||
return true;
|
||
}
|
||
|
||
// fence instruction
|
||
if (fndecl->llvmInternal == LLVMfence) {
|
||
if (e->arguments->dim != 1) {
|
||
e->error("fence instruction expects 1 arguments");
|
||
fatal();
|
||
}
|
||
p->ir->CreateFence(llvm::AtomicOrdering((*e->arguments)[0]->toInteger()));
|
||
return true;
|
||
}
|
||
|
||
// atomic store instruction
|
||
if (fndecl->llvmInternal == LLVMatomic_store) {
|
||
if (e->arguments->dim != 3) {
|
||
e->error("atomic store instruction expects 3 arguments");
|
||
fatal();
|
||
}
|
||
Expression* exp1 = (*e->arguments)[0];
|
||
Expression* exp2 = (*e->arguments)[1];
|
||
int atomicOrdering = (*e->arguments)[2]->toInteger();
|
||
LLValue* val = toElem(exp1)->getRVal();
|
||
LLValue* ptr = toElem(exp2)->getRVal();
|
||
|
||
if (!val->getType()->isIntegerTy()) {
|
||
e->error("atomic store only supports integer types, not '%s'", exp1->type->toChars());
|
||
fatal();
|
||
}
|
||
|
||
llvm::StoreInst* ret = p->ir->CreateStore(val, ptr);
|
||
ret->setAtomic(llvm::AtomicOrdering(atomicOrdering));
|
||
ret->setAlignment(getTypeAllocSize(val->getType()));
|
||
return true;
|
||
}
|
||
|
||
// atomic load instruction
|
||
if (fndecl->llvmInternal == LLVMatomic_load) {
|
||
if (e->arguments->dim != 2) {
|
||
e->error("atomic load instruction expects 2 arguments");
|
||
fatal();
|
||
}
|
||
|
||
Expression* exp = (*e->arguments)[0];
|
||
int atomicOrdering = (*e->arguments)[1]->toInteger();
|
||
|
||
LLValue* ptr = toElem(exp)->getRVal();
|
||
Type* retType = exp->type->nextOf();
|
||
|
||
if (!ptr->getType()->getContainedType(0)->isIntegerTy()) {
|
||
e->error("atomic load only supports integer types, not '%s'", retType->toChars());
|
||
fatal();
|
||
}
|
||
|
||
llvm::LoadInst* val = p->ir->CreateLoad(ptr);
|
||
val->setAlignment(getTypeAllocSize(val->getType()));
|
||
val->setAtomic(llvm::AtomicOrdering(atomicOrdering));
|
||
result = new DImValue(retType, val);
|
||
return true;
|
||
}
|
||
|
||
// cmpxchg instruction
|
||
if (fndecl->llvmInternal == LLVMatomic_cmp_xchg) {
|
||
if (e->arguments->dim != 4) {
|
||
e->error("cmpxchg instruction expects 4 arguments");
|
||
fatal();
|
||
}
|
||
Expression* exp1 = (*e->arguments)[0];
|
||
Expression* exp2 = (*e->arguments)[1];
|
||
Expression* exp3 = (*e->arguments)[2];
|
||
int atomicOrdering = (*e->arguments)[3]->toInteger();
|
||
LLValue* ptr = toElem(exp1)->getRVal();
|
||
LLValue* cmp = toElem(exp2)->getRVal();
|
||
LLValue* val = toElem(exp3)->getRVal();
|
||
#if LDC_LLVM_VER >= 305
|
||
LLValue* ret = p->ir->CreateAtomicCmpXchg(ptr, cmp, val, llvm::AtomicOrdering(atomicOrdering), llvm::AtomicOrdering(atomicOrdering));
|
||
// Use the same quickfix as for dragonegg - see r210956
|
||
ret = p->ir->CreateExtractValue(ret, 0);
|
||
#else
|
||
LLValue* ret = p->ir->CreateAtomicCmpXchg(ptr, cmp, val, llvm::AtomicOrdering(atomicOrdering));
|
||
#endif
|
||
result = new DImValue(exp3->type, ret);
|
||
return true;
|
||
}
|
||
|
||
// atomicrmw instruction
|
||
if (fndecl->llvmInternal == LLVMatomic_rmw) {
|
||
if (e->arguments->dim != 3) {
|
||
e->error("atomic_rmw instruction expects 3 arguments");
|
||
fatal();
|
||
}
|
||
|
||
static const char *ops[] = {
|
||
"xchg",
|
||
"add",
|
||
"sub",
|
||
"and",
|
||
"nand",
|
||
"or",
|
||
"xor",
|
||
"max",
|
||
"min",
|
||
"umax",
|
||
"umin",
|
||
0
|
||
};
|
||
|
||
int op = 0;
|
||
for (; ; ++op) {
|
||
if (ops[op] == 0) {
|
||
e->error("unknown atomic_rmw operation %s", fndecl->intrinsicName.c_str());
|
||
fatal();
|
||
}
|
||
if (fndecl->intrinsicName == ops[op])
|
||
break;
|
||
}
|
||
|
||
Expression* exp1 = (*e->arguments)[0];
|
||
Expression* exp2 = (*e->arguments)[1];
|
||
int atomicOrdering = (*e->arguments)[2]->toInteger();
|
||
LLValue* ptr = toElem(exp1)->getRVal();
|
||
LLValue* val = toElem(exp2)->getRVal();
|
||
LLValue* ret = p->ir->CreateAtomicRMW(llvm::AtomicRMWInst::BinOp(op), ptr, val,
|
||
llvm::AtomicOrdering(atomicOrdering));
|
||
result = new DImValue(exp2->type, ret);
|
||
return true;
|
||
}
|
||
|
||
// bitop
|
||
if (fndecl->llvmInternal == LLVMbitop_bt ||
|
||
fndecl->llvmInternal == LLVMbitop_btr||
|
||
fndecl->llvmInternal == LLVMbitop_btc||
|
||
fndecl->llvmInternal == LLVMbitop_bts)
|
||
{
|
||
if (e->arguments->dim != 2) {
|
||
e->error("bitop intrinsic expects 2 arguments");
|
||
fatal();
|
||
}
|
||
|
||
Expression* exp1 = (*e->arguments)[0];
|
||
Expression* exp2 = (*e->arguments)[1];
|
||
LLValue* ptr = toElem(exp1)->getRVal();
|
||
LLValue* bitnum = toElem(exp2)->getRVal();
|
||
|
||
unsigned bitmask = DtoSize_t()->getBitWidth() - 1;
|
||
assert(bitmask == 31 || bitmask == 63);
|
||
// auto q = cast(size_t*)ptr + (bitnum >> (64bit ? 6 : 5));
|
||
LLValue* q = DtoBitCast(ptr, DtoSize_t()->getPointerTo());
|
||
q = DtoGEP1(q, p->ir->CreateLShr(bitnum, bitmask == 63 ? 6 : 5), "bitop.q");
|
||
|
||
// auto mask = 1 << (bitnum & bitmask);
|
||
LLValue* mask = p->ir->CreateAnd(bitnum, DtoConstSize_t(bitmask), "bitop.tmp");
|
||
mask = p->ir->CreateShl(DtoConstSize_t(1), mask, "bitop.mask");
|
||
|
||
// auto result = (*q & mask) ? -1 : 0;
|
||
LLValue* val = p->ir->CreateZExt(DtoLoad(q, "bitop.tmp"), DtoSize_t(), "bitop.val");
|
||
LLValue* ret = p->ir->CreateAnd(val, mask, "bitop.tmp");
|
||
ret = p->ir->CreateICmpNE(ret, DtoConstSize_t(0), "bitop.tmp");
|
||
ret = p->ir->CreateSelect(ret, DtoConstInt(-1), DtoConstInt(0), "bitop.result");
|
||
|
||
if (fndecl->llvmInternal != LLVMbitop_bt) {
|
||
llvm::Instruction::BinaryOps op;
|
||
if (fndecl->llvmInternal == LLVMbitop_btc) {
|
||
// *q ^= mask;
|
||
op = llvm::Instruction::Xor;
|
||
} else if (fndecl->llvmInternal == LLVMbitop_btr) {
|
||
// *q &= ~mask;
|
||
mask = p->ir->CreateNot(mask);
|
||
op = llvm::Instruction::And;
|
||
} else if (fndecl->llvmInternal == LLVMbitop_bts) {
|
||
// *q |= mask;
|
||
op = llvm::Instruction::Or;
|
||
} else {
|
||
llvm_unreachable("Unrecognized bitop intrinsic.");
|
||
}
|
||
|
||
LLValue *newVal = p->ir->CreateBinOp(op, val, mask, "bitop.new_val");
|
||
newVal = p->ir->CreateTrunc(newVal, DtoSize_t(), "bitop.tmp");
|
||
DtoStore(newVal, q);
|
||
}
|
||
|
||
result = new DImValue(e->type, ret);
|
||
return true;
|
||
}
|
||
|
||
if (fndecl->llvmInternal == LLVMbitop_vld)
|
||
{
|
||
if (e->arguments->dim != 1) {
|
||
e->error("bitop.vld intrinsic expects 1 argument");
|
||
fatal();
|
||
}
|
||
// TODO: Check types
|
||
|
||
Expression* exp1 = (*e->arguments)[0];
|
||
LLValue* ptr = toElem(exp1)->getRVal();
|
||
result = new DImValue(exp1->type, DtoVolatileLoad(ptr));
|
||
return true;
|
||
}
|
||
|
||
if (fndecl->llvmInternal == LLVMbitop_vst)
|
||
{
|
||
if (e->arguments->dim != 2) {
|
||
e->error("bitop.vst intrinsic expects 2 arguments");
|
||
fatal();
|
||
}
|
||
// TODO: Check types
|
||
|
||
Expression* exp1 = (*e->arguments)[0];
|
||
Expression* exp2 = (*e->arguments)[1];
|
||
LLValue* ptr = toElem(exp1)->getRVal();
|
||
LLValue* val = toElem(exp2)->getRVal();
|
||
DtoVolatileStore(val, ptr);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
// FIXME: this function is a mess !
|
||
|
||
DValue* DtoCallFunction(Loc& loc, Type* resulttype, DValue* fnval, Expressions* arguments, llvm::Value *retvar)
|
||
{
|
||
IF_LOG Logger::println("DtoCallFunction()");
|
||
LOG_SCOPE
|
||
|
||
// the callee D type
|
||
Type* calleeType = fnval->getType();
|
||
|
||
// make sure the callee type has been processed
|
||
DtoType(calleeType);
|
||
|
||
// get func value if any
|
||
DFuncValue* dfnval = fnval->isFunc();
|
||
|
||
// handle intrinsics
|
||
bool intrinsic = (dfnval && dfnval->func && dfnval->func->llvmInternal == LLVMintrinsic);
|
||
|
||
// get function type info
|
||
IrFuncTy &irFty = DtoIrTypeFunction(fnval);
|
||
TypeFunction* tf = DtoTypeFunction(fnval);
|
||
|
||
// misc
|
||
bool retinptr = irFty.arg_sret;
|
||
bool thiscall = irFty.arg_this;
|
||
bool delegatecall = (calleeType->toBasetype()->ty == Tdelegate);
|
||
bool nestedcall = irFty.arg_nest;
|
||
bool dvarargs = irFty.arg_arguments;
|
||
|
||
// get callee llvm value
|
||
LLValue* callable = DtoCallableValue(fnval);
|
||
LLFunctionType* callableTy = DtoExtractFunctionType(callable->getType());
|
||
assert(callableTy);
|
||
llvm::CallingConv::ID callconv = gABI->callingConv(callableTy, tf->linkage);
|
||
|
||
// IF_LOG Logger::cout() << "callable: " << *callable << '\n';
|
||
|
||
// get number of explicit arguments
|
||
size_t n_arguments = arguments ? arguments->dim : 0;
|
||
|
||
// get llvm argument iterator, for types
|
||
LLFunctionType::param_iterator argTypesBegin = callableTy->param_begin();
|
||
|
||
// parameter attributes
|
||
AttrSet attrs;
|
||
|
||
// return attrs
|
||
attrs.add(0, irFty.ret->attrs);
|
||
|
||
// handle implicit arguments
|
||
std::vector<LLValue*> args;
|
||
args.reserve(irFty.args.size());
|
||
|
||
// return in hidden ptr is first
|
||
if (retinptr)
|
||
{
|
||
if (!retvar)
|
||
retvar = DtoRawAlloca((*argTypesBegin)->getContainedType(0), resulttype->alignsize(), ".rettmp");
|
||
args.push_back(retvar);
|
||
|
||
// add attrs for hidden ptr
|
||
// after adding the argument to args, args.size() is the index for the
|
||
// related attributes since attrs[0] are the return value's attributes
|
||
attrs.add(args.size(), irFty.arg_sret->attrs);
|
||
|
||
// verify that sret and/or inreg attributes are set
|
||
const AttrBuilder& sretAttrs = irFty.arg_sret->attrs;
|
||
assert((sretAttrs.contains(LDC_ATTRIBUTE(StructRet)) || sretAttrs.contains(LDC_ATTRIBUTE(InReg)))
|
||
&& "Sret arg not sret or inreg?");
|
||
}
|
||
|
||
// then comes a context argument...
|
||
if(thiscall || delegatecall || nestedcall)
|
||
{
|
||
LLType* contextArgType = *(argTypesBegin + args.size());
|
||
|
||
if (dfnval && (dfnval->func->ident == Id::ensure || dfnval->func->ident == Id::require)) {
|
||
// ... which can be the this "context" argument for a contract
|
||
// invocation (in D2, we do not generate a full nested contexts
|
||
// for __require/__ensure as the needed parameters are passed
|
||
// explicitly, while in D1, the normal nested function handling
|
||
// mechanisms are used)
|
||
LLValue* thisarg = DtoBitCast(DtoLoad(gIR->func()->thisArg), getVoidPtrType());
|
||
args.push_back(thisarg);
|
||
}
|
||
else if (thiscall && dfnval && dfnval->vthis)
|
||
{
|
||
// ... or a normal 'this' argument
|
||
LLValue* thisarg = DtoBitCast(dfnval->vthis, contextArgType);
|
||
args.push_back(thisarg);
|
||
}
|
||
else if (delegatecall)
|
||
{
|
||
// ... or a delegate context arg
|
||
LLValue* ctxarg;
|
||
if (fnval->isLVal()) {
|
||
ctxarg = DtoLoad(DtoGEPi(fnval->getLVal(), 0, 0), ".ptr");
|
||
} else {
|
||
ctxarg = gIR->ir->CreateExtractValue(fnval->getRVal(), 0, ".ptr");
|
||
}
|
||
ctxarg = DtoBitCast(ctxarg, contextArgType);
|
||
args.push_back(ctxarg);
|
||
}
|
||
else if (nestedcall)
|
||
{
|
||
// ... or a nested function context arg
|
||
if (dfnval) {
|
||
LLValue* contextptr = DtoNestedContext(loc, dfnval->func);
|
||
contextptr = DtoBitCast(contextptr, getVoidPtrType());
|
||
args.push_back(contextptr);
|
||
} else {
|
||
args.push_back(llvm::UndefValue::get(getVoidPtrType()));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
error(loc, "Context argument required but none given");
|
||
fatal();
|
||
}
|
||
|
||
// add attributes for context argument
|
||
if (irFty.arg_this) {
|
||
attrs.add(args.size(), irFty.arg_this->attrs);
|
||
} else if (irFty.arg_nest) {
|
||
attrs.add(args.size(), irFty.arg_nest->attrs);
|
||
}
|
||
}
|
||
|
||
const int numFormalParams = Parameter::dim(tf->parameters); // excl. variadics
|
||
|
||
// D vararg functions need an additional "TypeInfo[] _arguments" argument
|
||
if (dvarargs) {
|
||
LLValue* argumentsArg = getTypeinfoArrayArgumentForDVarArg(arguments, numFormalParams);
|
||
args.push_back(argumentsArg);
|
||
attrs.add(args.size(), irFty.arg_arguments->attrs);
|
||
}
|
||
|
||
// handle explicit arguments
|
||
|
||
Logger::println("doing normal arguments");
|
||
IF_LOG {
|
||
Logger::println("Arguments so far: (%d)", static_cast<int>(args.size()));
|
||
Logger::indent();
|
||
for (size_t i = 0; i < args.size(); i++) {
|
||
Logger::cout() << *args[i] << '\n';
|
||
}
|
||
Logger::undent();
|
||
Logger::cout() << "Function type: " << tf->toChars() << '\n';
|
||
//Logger::cout() << "LLVM functype: " << *callable->getType() << '\n';
|
||
}
|
||
|
||
std::vector<DValue*> argvals(n_arguments, static_cast<DValue*>(0));
|
||
if (dfnval && dfnval->func->isArrayOp) {
|
||
// For array ops, the druntime implementation signatures are crafted
|
||
// specifically such that the evaluation order is as expected with
|
||
// the strange DMD reverse parameter passing order. Thus, we need
|
||
// to actually build the arguments right-to-left for them.
|
||
for (int i = numFormalParams - 1; i >= 0; --i) {
|
||
Parameter* fnarg = Parameter::getNth(tf->parameters, i);
|
||
assert(fnarg);
|
||
DValue* argval = DtoArgument(fnarg, (*arguments)[i]);
|
||
argvals[i] = argval;
|
||
}
|
||
} else {
|
||
for (int i = 0; i < numFormalParams; ++i) {
|
||
Parameter* fnarg = Parameter::getNth(tf->parameters, i);
|
||
assert(fnarg);
|
||
DValue* argval = DtoArgument(fnarg, (*arguments)[i]);
|
||
argvals[i] = argval;
|
||
}
|
||
}
|
||
// add varargs
|
||
for (size_t i = numFormalParams; i < n_arguments; ++i)
|
||
argvals[i] = DtoArgument(0, (*arguments)[i]);
|
||
|
||
addExplicitArguments(args, attrs, irFty, callableTy, argvals, numFormalParams);
|
||
|
||
// call the function
|
||
LLCallSite call = gIR->func()->scopes->callOrInvoke(callable, args);
|
||
|
||
// get return value
|
||
LLValue* retllval = (retinptr) ? args[0] : call.getInstruction();
|
||
|
||
// Hack around LDC assuming structs and static arrays are in memory:
|
||
// If the function returns a struct or a static array, and the return
|
||
// value is not a pointer to a struct or a static array, store it to
|
||
// a stack slot before continuing.
|
||
Type* dReturnType = tf->next;
|
||
TY returnTy = dReturnType->toBasetype()->ty;
|
||
bool storeReturnValueOnStack =
|
||
(returnTy == Tstruct && !isaPointer(retllval)) ||
|
||
(returnTy == Tsarray && isaArray(retllval));
|
||
|
||
// Ignore ABI for intrinsics
|
||
if (!intrinsic && !retinptr)
|
||
{
|
||
// do abi specific return value fixups
|
||
DImValue dretval(dReturnType, retllval);
|
||
if (storeReturnValueOnStack)
|
||
{
|
||
Logger::println("Storing return value to stack slot");
|
||
LLValue* mem = DtoRawAlloca(DtoType(dReturnType), 0);
|
||
irFty.getRet(dReturnType, &dretval, mem);
|
||
retllval = mem;
|
||
storeReturnValueOnStack = false;
|
||
}
|
||
else
|
||
{
|
||
retllval = irFty.getRet(dReturnType, &dretval);
|
||
storeReturnValueOnStack =
|
||
(returnTy == Tstruct && !isaPointer(retllval)) ||
|
||
(returnTy == Tsarray && isaArray(retllval));
|
||
}
|
||
}
|
||
|
||
if (storeReturnValueOnStack)
|
||
{
|
||
Logger::println("Storing return value to stack slot");
|
||
LLValue* mem = DtoRawAlloca(retllval->getType(), 0);
|
||
DtoStore(retllval, mem);
|
||
retllval = mem;
|
||
}
|
||
|
||
// repaint the type if necessary
|
||
if (resulttype)
|
||
{
|
||
Type* rbase = stripModifiers(resulttype->toBasetype(), true);
|
||
Type* nextbase = stripModifiers(tf->nextOf()->toBasetype(), true);
|
||
if (!rbase->equals(nextbase))
|
||
{
|
||
IF_LOG Logger::println("repainting return value from '%s' to '%s'", tf->nextOf()->toChars(), rbase->toChars());
|
||
switch(rbase->ty)
|
||
{
|
||
case Tarray:
|
||
if (tf->isref)
|
||
retllval = DtoBitCast(retllval, DtoType(rbase->pointerTo()));
|
||
else
|
||
retllval = DtoAggrPaint(retllval, DtoType(rbase));
|
||
break;
|
||
|
||
case Tsarray:
|
||
// nothing ?
|
||
break;
|
||
|
||
case Tclass:
|
||
case Taarray:
|
||
case Tpointer:
|
||
if (tf->isref)
|
||
retllval = DtoBitCast(retllval, DtoType(rbase->pointerTo()));
|
||
else
|
||
retllval = DtoBitCast(retllval, DtoType(rbase));
|
||
break;
|
||
|
||
case Tstruct:
|
||
if (nextbase->ty == Taarray && !tf->isref)
|
||
{
|
||
// In the D2 frontend, the associative array type and its
|
||
// object.AssociativeArray representation are used
|
||
// interchangably in some places. However, AAs are returned
|
||
// by value and not in an sret argument, so if the struct
|
||
// type will be used, give the return value storage here
|
||
// so that we get the right amount of indirections.
|
||
LLValue* tmp = DtoAlloca(rbase, ".aalvauetmp");
|
||
LLValue* val = DtoInsertValue(
|
||
llvm::UndefValue::get(DtoType(rbase)), retllval, 0);
|
||
DtoStore(val, tmp);
|
||
retllval = tmp;
|
||
retinptr = true;
|
||
break;
|
||
}
|
||
// Fall through.
|
||
|
||
default:
|
||
// Unfortunately, DMD has quirks resp. bugs with regard to name
|
||
// mangling: For voldemort-type functions which return a nested
|
||
// struct, the mangled name of the return type changes during
|
||
// semantic analysis.
|
||
//
|
||
// (When the function deco is first computed as part of
|
||
// determining the return type deco, its return type part is
|
||
// left off to avoid cycles. If mangle/toDecoBuffer is then
|
||
// called again for the type, it will pick up the previous
|
||
// result and return the full deco string for the nested struct
|
||
// type, consisting of both the full mangled function name, and
|
||
// the struct identifier.)
|
||
//
|
||
// Thus, the type merging in stripModifiers does not work
|
||
// reliably, and the equality check above can fail even if the
|
||
// types only differ in a qualifier.
|
||
//
|
||
// Because a proper fix for this in the frontend is hard, we
|
||
// just carry on and hope that the frontend didn't mess up,
|
||
// i.e. that the LLVM types really match up.
|
||
//
|
||
// An example situation where this case occurs is:
|
||
// ---
|
||
// auto iota() {
|
||
// static struct Result {
|
||
// this(int) {}
|
||
// inout(Result) test() inout { return cast(inout)Result(0); }
|
||
// }
|
||
// return Result.init;
|
||
// }
|
||
// void main() { auto r = iota(); }
|
||
// ---
|
||
Logger::println("Unknown return mismatch type, ignoring.");
|
||
break;
|
||
}
|
||
IF_LOG Logger::cout() << "final return value: " << *retllval << '\n';
|
||
}
|
||
}
|
||
|
||
// set calling convention and parameter attributes
|
||
#if LDC_LLVM_VER >= 303
|
||
llvm::AttributeSet attrlist = attrs.toNativeSet();
|
||
#else
|
||
llvm::AttrListPtr attrlist = attrs.toNativeSet();
|
||
#endif
|
||
if (dfnval && dfnval->func)
|
||
{
|
||
LLFunction* llfunc = llvm::dyn_cast<LLFunction>(dfnval->val);
|
||
if (llfunc && llfunc->isIntrinsic()) // override intrinsic attrs
|
||
#if LDC_LLVM_VER >= 302
|
||
attrlist = llvm::Intrinsic::getAttributes(gIR->context(), static_cast<llvm::Intrinsic::ID>(llfunc->getIntrinsicID()));
|
||
#else
|
||
attrlist = llvm::Intrinsic::getAttributes(static_cast<llvm::Intrinsic::ID>(llfunc->getIntrinsicID()));
|
||
#endif
|
||
else
|
||
call.setCallingConv(callconv);
|
||
}
|
||
else
|
||
call.setCallingConv(callconv);
|
||
call.setAttributes(attrlist);
|
||
|
||
// if we are returning through a pointer arg
|
||
// or if we are returning a reference
|
||
// make sure we provide a lvalue back!
|
||
if (retinptr || (tf->isref && returnTy != Tvoid))
|
||
return new DVarValue(resulttype, retllval);
|
||
|
||
return new DImValue(resulttype, retllval);
|
||
}
|