ldc/gen/tocall.cpp
Martin ddfaac10b3 Rename DtoAggrCopy/ZeroInit() to DtoMemCpy/SetZero() overloads
And replace some obvious load->store combos by a memcpy.
2015-12-04 22:23:09 +01:00

1011 lines
34 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.

//===-- 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);
}
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();
}
if (type->ty == Tdelegate) {
if (fn->isLVal()) {
LLValue *dg = fn->getLVal();
LLValue *funcptr = DtoGEPi(dg, 0, 1);
return DtoLoad(funcptr, ".funcptr");
}
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;
}
if (LLPointerType *pty = isaPointer(type)) {
if (LLFunctionType *fty = isaFunction(pty->getElementType())) {
return fty;
}
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
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(LLAttribute::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 *>(nullptr));
// 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 = nullptr;
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 = nullptr;
if (isVararg) {
llVal = irFty.putParam(*irArg, argval);
} else {
llVal = irFty.putParam(i, argval);
}
const size_t llArgIdx =
implicitLLArgCount +
(irFty.reverseParams ? explicitLLArgCount - i - 1 : i);
llvm::Type *const callableArgType =
(isVararg ? nullptr : 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) && DtoIsInMemoryOnly(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);
auto typeinfomem = new llvm::GlobalVariable(
gIR->module, typeinfoarraytype, true, llvm::GlobalValue::InternalLinkage,
nullptr, "._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) {
DtoMemCpy(pAp, pArgptr); // 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 (DtoIsInMemoryOnly(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()) {
llvm::PointerType *v = isaPointer(val->getType());
if (v && v->getContainedType(0)->isStructTy()) {
switch (const size_t N = getTypeBitSize(v->getContainedType(0))) {
case 8:
case 16:
case 32:
case 64:
case 128:
val = DtoLoad(
DtoBitCast(val, llvm::Type::getIntNPtrTy(
gIR->context(), static_cast<unsigned>(N))));
ptr = DtoBitCast(ptr, llvm::Type::getIntNPtrTy(
gIR->context(), static_cast<unsigned>(N)));
break;
default:
goto errorStore;
}
} else {
errorStore:
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();
LLType *ptrTy = ptr->getType()->getContainedType(0);
Type *retType = exp->type->nextOf();
if (!ptrTy->isIntegerTy()) {
if (ptrTy->isStructTy()) {
switch (const size_t N = getTypeBitSize(ptrTy)) {
case 8:
case 16:
case 32:
case 64:
case 128:
ptr = DtoBitCast(ptr, llvm::Type::getIntNPtrTy(
gIR->context(), static_cast<unsigned>(N)));
break;
default:
goto errorLoad;
}
} else {
errorLoad:
e->error("atomic load only supports integer types, not '%s'",
retType->toChars());
fatal();
}
}
llvm::LoadInst *load = p->ir->CreateLoad(ptr);
load->setAlignment(getTypeAllocSize(load->getType()));
load->setAtomic(llvm::AtomicOrdering(atomicOrdering));
llvm::Value *val = load;
if (val->getType() != ptrTy) {
val = DtoAllocaDump(val, retType);
}
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();
LLType *retTy = val->getType();
if (!cmp->getType()->isIntegerTy()) {
llvm::PointerType *v = isaPointer(cmp->getType());
if (v && v->getContainedType(0)->isStructTy()) {
switch (const size_t N = getTypeBitSize(v->getContainedType(0))) {
case 8:
case 16:
case 32:
case 64:
case 128:
ptr = DtoBitCast(ptr, llvm::Type::getIntNPtrTy(
gIR->context(), static_cast<unsigned>(N)));
cmp = DtoLoad(
DtoBitCast(cmp, llvm::Type::getIntNPtrTy(
gIR->context(), static_cast<unsigned>(N))));
val = DtoLoad(
DtoBitCast(val, llvm::Type::getIntNPtrTy(
gIR->context(), static_cast<unsigned>(N))));
break;
default:
goto errorCmpxchg;
}
} else {
errorCmpxchg:
e->error("cmpxchg only supports integer types, not '%s'",
exp2->type->toChars());
fatal();
}
}
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);
llvm::Value *retVal = ret;
if (retVal->getType() != retTy) {
retVal = DtoAllocaDump(retVal, exp3->type);
}
result = new DImValue(exp3->type, retVal);
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", nullptr};
int op = 0;
for (;; ++op) {
if (ops[op] == nullptr) {
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;
}
////////////////////////////////////////////////////////////////////////////////
class ImplicitArgumentsBuilder {
public:
ImplicitArgumentsBuilder(std::vector<LLValue *> &args, AttrSet &attrs,
Loc &loc, DValue *fnval,
LLFunctionType *llCalleeType, Expressions *arguments,
Type *resulttype, LLValue *retvar)
: args(args), attrs(attrs), loc(loc), fnval(fnval),
llCalleeType(llCalleeType), arguments(arguments),
resulttype(resulttype), retvar(retvar),
// computed:
calleeType(fnval->getType()), dfnval(fnval->isFunc()),
irFty(DtoIrTypeFunction(fnval)), tf(DtoTypeFunction(fnval)),
llArgTypesBegin(llCalleeType->param_begin()) {}
void addImplicitArgs() {
if (gABI->passThisBeforeSret(tf)) {
addContext();
addSret();
} else {
addSret();
addContext();
}
addArguments();
}
private:
// passed:
std::vector<LLValue *> &args;
AttrSet &attrs;
Loc &loc;
DValue *const fnval;
LLFunctionType *const llCalleeType;
Expressions *const arguments;
Type *const resulttype;
LLValue *const retvar;
// computed:
Type *const calleeType;
DFuncValue *const dfnval;
IrFuncTy &irFty;
TypeFunction *const tf;
LLFunctionType::param_iterator llArgTypesBegin;
// Adds an optional sret pointer argument.
void addSret() {
if (!irFty.arg_sret) {
return;
}
size_t index = args.size();
LLType *llArgType = *(llArgTypesBegin + index);
LLValue *var = retvar;
if (!var) {
var = DtoRawAlloca(llArgType->getContainedType(0),
DtoAlignment(resulttype), ".rettmp");
}
args.push_back(var);
attrs.add(index + 1, irFty.arg_sret->attrs);
// verify that sret and/or inreg attributes are set
const AttrBuilder &sretAttrs = irFty.arg_sret->attrs;
assert((sretAttrs.contains(LLAttribute::StructRet) ||
sretAttrs.contains(LLAttribute::InReg)) &&
"Sret arg not sret or inreg?");
}
// Adds an optional context/this pointer argument.
void addContext() {
bool thiscall = irFty.arg_this;
bool delegatecall = (calleeType->toBasetype()->ty == Tdelegate);
bool nestedcall = irFty.arg_nest;
if (!thiscall && !delegatecall && !nestedcall) {
return;
}
size_t index = args.size();
LLType *llArgType = *(llArgTypesBegin + index);
if (dfnval && (dfnval->func->ident == Id::ensure ||
dfnval->func->ident == Id::require)) {
// 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, llArgType);
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, llArgType);
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
if (irFty.arg_this) {
attrs.add(index + 1, irFty.arg_this->attrs);
} else if (irFty.arg_nest) {
attrs.add(index + 1, irFty.arg_nest->attrs);
}
}
// D vararg functions need a "TypeInfo[] _arguments" argument.
void addArguments() {
if (!irFty.arg_arguments) {
return;
}
int numFormalParams = Parameter::dim(tf->parameters);
LLValue *argumentsArg =
getTypeinfoArrayArgumentForDVarArg(arguments, numFormalParams);
args.push_back(argumentsArg);
attrs.add(args.size(), irFty.arg_arguments->attrs);
}
};
////////////////////////////////////////////////////////////////////////////////
// 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
// make sure the D callee type has been processed
DtoType(fnval->getType());
// get func value if any
DFuncValue *dfnval = fnval->isFunc();
// get function type info
IrFuncTy &irFty = DtoIrTypeFunction(fnval);
TypeFunction *const tf = DtoTypeFunction(fnval);
Type *const returntype = tf->next;
const TY returnTy = returntype->toBasetype()->ty;
if (resulttype == nullptr) {
resulttype = returntype;
}
// get callee llvm value
LLValue *const callable = DtoCallableValue(fnval);
LLFunctionType *const callableTy =
DtoExtractFunctionType(callable->getType());
assert(callableTy);
const llvm::CallingConv::ID callconv =
gABI->callingConv(callableTy, tf->linkage);
// IF_LOG Logger::cout() << "callable: " << *callable << '\n';
// parameter attributes
AttrSet attrs;
// return attrs
attrs.add(0, irFty.ret->attrs);
std::vector<LLValue *> args;
args.reserve(irFty.args.size());
// handle implicit arguments (sret, context/this, _arguments)
ImplicitArgumentsBuilder iab(args, attrs, loc, fnval, callableTy, arguments,
resulttype, retvar);
iab.addImplicitArgs();
// handle explicit arguments
Logger::println("doing normal arguments");
IF_LOG {
Logger::println("Arguments so far: (%d)", static_cast<int>(args.size()));
Logger::indent();
for (auto &arg : args) {
Logger::cout() << *arg << '\n';
}
Logger::undent();
Logger::cout() << "Function type: " << tf->toChars() << '\n';
// Logger::cout() << "LLVM functype: " << *callable->getType() << '\n';
}
const int numFormalParams = Parameter::dim(tf->parameters); // excl. variadics
const size_t n_arguments =
arguments ? arguments->dim : 0; // number of explicit arguments
std::vector<DValue *> argvals(n_arguments, static_cast<DValue *>(nullptr));
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(nullptr, (*arguments)[i]);
}
addExplicitArguments(args, attrs, irFty, callableTy, argvals,
numFormalParams);
// call the function
LLCallSite call = gIR->func()->scopes->callOrInvoke(callable, args);
// get return value
const int sretArgIndex =
(irFty.arg_sret && irFty.arg_this && gABI->passThisBeforeSret(tf) ? 1
: 0);
LLValue *retllval =
(irFty.arg_sret ? args[sretArgIndex] : 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.
bool storeReturnValueOnStack =
(returnTy == Tstruct && !isaPointer(retllval)) ||
(returnTy == Tsarray && isaArray(retllval));
bool retValIsAlloca = false;
// ignore ABI for intrinsics
const bool intrinsic =
(dfnval && dfnval->func && DtoIsIntrinsic(dfnval->func));
if (!intrinsic && !irFty.arg_sret) {
// do ABI specific return value fixups
if (storeReturnValueOnStack) {
Logger::println("Storing return value to stack slot");
LLValue *mem = DtoAlloca(returntype);
irFty.getRet(returntype, retllval, mem);
retllval = mem;
retValIsAlloca = true;
storeReturnValueOnStack = false;
} else {
retllval = irFty.getRet(returntype, retllval);
storeReturnValueOnStack =
(returnTy == Tstruct && !isaPointer(retllval)) ||
(returnTy == Tsarray && isaArray(retllval));
}
}
if (storeReturnValueOnStack) {
Logger::println("Storing return value to stack slot");
retllval = DtoAllocaDump(retllval, returntype);
retValIsAlloca = true;
}
// repaint the type if necessary
Type *rbase = stripModifiers(resulttype->toBasetype(), true);
Type *nextbase = stripModifiers(returntype->toBasetype(), true);
bool retinptr = irFty.arg_sret;
if (!rbase->equals(nextbase)) {
IF_LOG Logger::println("repainting return value from '%s' to '%s'",
returntype->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 *val =
DtoInsertValue(llvm::UndefValue::get(DtoType(rbase)), retllval, 0);
retllval = DtoAllocaDump(val, rbase, ".aalvaluetmp");
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
llvm::AttributeSet &attrlist = attrs;
if (dfnval && dfnval->func) {
LLFunction *llfunc = llvm::dyn_cast<LLFunction>(dfnval->val);
if (llfunc && llfunc->isIntrinsic()) // override intrinsic attrs
{
attrlist = llvm::Intrinsic::getAttributes(
gIR->context(),
static_cast<llvm::Intrinsic::ID>(llfunc->getIntrinsicID()));
} else {
call.setCallingConv(callconv);
}
} else {
call.setCallingConv(callconv);
}
call.setAttributes(attrlist);
// Special case for struct constructor calls: For temporaries, using the
// this pointer value returned from the constructor instead of the alloca
// passed as a parameter (which has the same value anyway) might lead to
// instruction dominance issues because of the way it interacts with the
// cleanups (see struct ctor hack in ToElemVisitor::visit(CallExp *)).
if (dfnval && dfnval->func && dfnval->func->isCtorDeclaration() &&
dfnval->func->isMember2()->isStructDeclaration()) {
return new DVarValue(resulttype, dfnval->vthis);
}
// 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) || retValIsAlloca) {
return new DVarValue(resulttype, retllval);
}
return new DImValue(resulttype, retllval);
}