mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-12 02:43:14 +01:00
Changed runtime functions taking opaque[] to void[]. Implemented proper type painting, to avoid "resizing" array casts in runtime calls that previously took opaque[]. Implemented dynamic arrays as first class types, this implements proper ABI for these types on x86. Added dwarf region end after call to assert function, fixes some problems with llvm not allowing this to be missing. Reverted change to WithStatement from rev [704] it breaks MiniD, mini/with2.d needs to be fixed some other way... Fixed tango bug 1339 in runtime, problem with _adReverseChar on invalid UTF-8. Disabled .bc generation in the compiler runtime part, genobj.d triggers some llvm bug when using debug info. the .o seems to work fine.
401 lines
13 KiB
C++
401 lines
13 KiB
C++
#include "gen/llvm.h"
|
|
|
|
#include "mtype.h"
|
|
#include "declaration.h"
|
|
|
|
#include "gen/tollvm.h"
|
|
#include "gen/llvmhelpers.h"
|
|
#include "gen/irstate.h"
|
|
#include "gen/dvalue.h"
|
|
#include "gen/functions.h"
|
|
|
|
#include "gen/logger.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TypeFunction* DtoTypeFunction(DValue* fnval)
|
|
{
|
|
Type* type = fnval->getType()->toBasetype();
|
|
if (type->ty == Tfunction)
|
|
{
|
|
return (TypeFunction*)type;
|
|
}
|
|
else if (type->ty == Tdelegate)
|
|
{
|
|
assert(type->next->ty == Tfunction);
|
|
return (TypeFunction*)type->next;
|
|
}
|
|
|
|
assert(0 && "cant get TypeFunction* from non lazy/function/delegate");
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
unsigned DtoCallingConv(LINK l)
|
|
{
|
|
if (l == LINKc || l == LINKcpp)
|
|
return llvm::CallingConv::C;
|
|
else if (l == LINKd || l == LINKdefault)
|
|
{
|
|
if (global.params.cpu == ARCHx86)
|
|
return llvm::CallingConv::X86_StdCall;
|
|
else
|
|
return llvm::CallingConv::Fast;
|
|
}
|
|
else if (l == LINKwindows)
|
|
return llvm::CallingConv::X86_StdCall;
|
|
else
|
|
assert(0 && "Unsupported calling convention");
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DValue* DtoVaArg(Loc& loc, Type* type, Expression* valistArg)
|
|
{
|
|
DValue* expelem = valistArg->toElem(gIR);
|
|
const LLType* llt = DtoType(type);
|
|
if (DtoIsPassedByRef(type))
|
|
llt = getPtrToType(llt);
|
|
// issue a warning for broken va_arg instruction.
|
|
if (global.params.cpu != ARCHx86)
|
|
warning("%s: va_arg for C variadic functions is probably broken for anything but x86", loc.toChars());
|
|
// done
|
|
return new DImValue(type, gIR->ir->CreateVAArg(expelem->getLVal(), llt, "tmp"));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
LLValue* DtoCallableValue(DValue* fn)
|
|
{
|
|
Type* type = fn->getType()->toBasetype();
|
|
if (type->ty == Tfunction)
|
|
{
|
|
return fn->getRVal();
|
|
}
|
|
else if (type->ty == Tdelegate)
|
|
{
|
|
LLValue* dg = fn->getRVal();
|
|
if (Logger::enabled())
|
|
Logger::cout() << "delegate: " << *dg << '\n';
|
|
LLValue* funcptr = DtoGEPi(dg, 0, 1);
|
|
return DtoLoad(funcptr);
|
|
}
|
|
else
|
|
{
|
|
assert(0 && "not a callable type");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const LLFunctionType* DtoExtractFunctionType(const LLType* type)
|
|
{
|
|
if (const LLFunctionType* fty = isaFunction(type))
|
|
return fty;
|
|
else if (const LLPointerType* pty = isaPointer(type))
|
|
{
|
|
if (const LLFunctionType* fty = isaFunction(pty->getElementType()))
|
|
return fty;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DtoBuildDVarArgList(std::vector<LLValue*>& args, llvm::AttrListPtr& palist, TypeFunction* tf, Expressions* arguments, size_t argidx)
|
|
{
|
|
Logger::println("doing d-style variadic arguments");
|
|
|
|
std::vector<const LLType*> vtypes;
|
|
|
|
// number of non variadic args
|
|
int begin = tf->parameters->dim;
|
|
Logger::println("num non vararg params = %d", begin);
|
|
|
|
// get n args in arguments list
|
|
size_t n_arguments = arguments ? arguments->dim : 0;
|
|
|
|
// build struct with argument types (non variadic args)
|
|
for (int i=begin; i<n_arguments; i++)
|
|
{
|
|
Expression* argexp = (Expression*)arguments->data[i];
|
|
vtypes.push_back(DtoType(argexp->type));
|
|
size_t sz = getABITypeSize(vtypes.back());
|
|
if (sz < PTRSIZE)
|
|
vtypes.back() = DtoSize_t();
|
|
}
|
|
const LLStructType* vtype = LLStructType::get(vtypes);
|
|
|
|
if (Logger::enabled())
|
|
Logger::cout() << "d-variadic argument struct type:\n" << *vtype << '\n';
|
|
|
|
LLValue* mem = DtoAlloca(vtype,"_argptr_storage");
|
|
|
|
// store arguments in the struct
|
|
for (int i=begin,k=0; i<n_arguments; i++,k++)
|
|
{
|
|
Expression* argexp = (Expression*)arguments->data[i];
|
|
if (global.params.llvmAnnotate)
|
|
DtoAnnotation(argexp->toChars());
|
|
LLValue* argdst = DtoGEPi(mem,0,k);
|
|
argdst = DtoBitCast(argdst, getPtrToType(DtoType(argexp->type)));
|
|
DtoVariadicArgument(argexp, argdst);
|
|
}
|
|
|
|
// build type info array
|
|
assert(Type::typeinfo->ir.irStruct->constInit);
|
|
const LLType* typeinfotype = DtoType(Type::typeinfo->type);
|
|
const LLArrayType* typeinfoarraytype = LLArrayType::get(typeinfotype,vtype->getNumElements());
|
|
|
|
llvm::GlobalVariable* typeinfomem =
|
|
new llvm::GlobalVariable(typeinfoarraytype, true, llvm::GlobalValue::InternalLinkage, NULL, "._arguments.storage", gIR->module);
|
|
if (Logger::enabled())
|
|
Logger::cout() << "_arguments storage: " << *typeinfomem << '\n';
|
|
|
|
std::vector<LLConstant*> vtypeinfos;
|
|
for (int i=begin,k=0; i<n_arguments; i++,k++)
|
|
{
|
|
Expression* argexp = (Expression*)arguments->data[i];
|
|
vtypeinfos.push_back(DtoTypeInfoOf(argexp->type));
|
|
}
|
|
|
|
// apply initializer
|
|
LLConstant* tiinits = llvm::ConstantArray::get(typeinfoarraytype, vtypeinfos);
|
|
typeinfomem->setInitializer(tiinits);
|
|
|
|
// put data in d-array
|
|
std::vector<LLConstant*> pinits;
|
|
pinits.push_back(DtoConstSize_t(vtype->getNumElements()));
|
|
pinits.push_back(llvm::ConstantExpr::getBitCast(typeinfomem, getPtrToType(typeinfotype)));
|
|
const LLType* tiarrty = DtoType(Type::typeinfo->type->arrayOf());
|
|
tiinits = llvm::ConstantStruct::get(pinits);
|
|
LLValue* typeinfoarrayparam = new llvm::GlobalVariable(tiarrty,
|
|
true, llvm::GlobalValue::InternalLinkage, tiinits, "._arguments.array", gIR->module);
|
|
|
|
// specify arguments
|
|
args.push_back(DtoLoad(typeinfoarrayparam));
|
|
++argidx;
|
|
args.push_back(gIR->ir->CreateBitCast(mem, getPtrToType(LLType::Int8Ty), "tmp"));
|
|
++argidx;
|
|
|
|
// pass non variadic args
|
|
for (int i=0; i<begin; i++)
|
|
{
|
|
Argument* fnarg = Argument::getNth(tf->parameters, i);
|
|
DValue* argval = DtoArgument(fnarg, (Expression*)arguments->data[i]);
|
|
args.push_back(argval->getRVal());
|
|
|
|
if (fnarg->llvmAttrs)
|
|
palist = palist.addAttr(argidx, fnarg->llvmAttrs);
|
|
|
|
++argidx;
|
|
}
|
|
}
|
|
|
|
|
|
DValue* DtoCallFunction(Loc& loc, Type* resulttype, DValue* fnval, Expressions* arguments)
|
|
{
|
|
// the callee D type
|
|
Type* calleeType = fnval->getType();
|
|
|
|
// if the type has not yet been processed, do so now
|
|
if (calleeType->ir.type == NULL)
|
|
DtoType(calleeType);
|
|
|
|
// get func value if any
|
|
DFuncValue* dfnval = fnval->isFunc();
|
|
|
|
// handle special vararg intrinsics
|
|
bool va_intrinsic = (dfnval && dfnval->func && dfnval->func->isVaIntrinsic());
|
|
|
|
// get function type info
|
|
TypeFunction* tf = DtoTypeFunction(fnval);
|
|
|
|
// misc
|
|
bool retinptr = tf->retInPtr;
|
|
bool thiscall = tf->usesThis;
|
|
bool delegatecall = (calleeType->toBasetype()->ty == Tdelegate);
|
|
bool nestedcall = tf->usesNest;
|
|
bool dvarargs = (tf->linkage == LINKd && tf->varargs == 1);
|
|
|
|
unsigned callconv = DtoCallingConv(tf->linkage);
|
|
|
|
// get callee llvm value
|
|
LLValue* callable = DtoCallableValue(fnval);
|
|
const LLFunctionType* callableTy = DtoExtractFunctionType(callable->getType());
|
|
assert(callableTy);
|
|
|
|
// get n arguments
|
|
size_t n_arguments = arguments ? arguments->dim : 0;
|
|
|
|
// get llvm argument iterator, for types
|
|
LLFunctionType::param_iterator argbegin = callableTy->param_begin();
|
|
LLFunctionType::param_iterator argiter = argbegin;
|
|
|
|
// parameter attributes
|
|
llvm::AttrListPtr palist;
|
|
|
|
// return attrs
|
|
if (tf->retAttrs)
|
|
palist = palist.addAttr(0, tf->retAttrs);
|
|
|
|
// handle implicit arguments
|
|
std::vector<LLValue*> args;
|
|
|
|
// return in hidden ptr is first
|
|
if (retinptr)
|
|
{
|
|
LLValue* retvar = DtoAlloca(argiter->get()->getContainedType(0), ".rettmp");
|
|
++argiter;
|
|
args.push_back(retvar);
|
|
palist = palist.addAttr(1, llvm::Attribute::StructRet);
|
|
}
|
|
|
|
// then comes a context argument...
|
|
if(thiscall || delegatecall || nestedcall)
|
|
{
|
|
// ... which can be a 'this' argument
|
|
if (thiscall && dfnval && dfnval->vthis)
|
|
{
|
|
LLValue* thisarg = DtoBitCast(dfnval->vthis, argiter->get());
|
|
++argiter;
|
|
args.push_back(thisarg);
|
|
}
|
|
// ... or a delegate context arg
|
|
else if (delegatecall)
|
|
{
|
|
LLValue* ctxarg = DtoLoad(DtoGEPi(fnval->getRVal(), 0,0));
|
|
assert(ctxarg->getType() == argiter->get());
|
|
++argiter;
|
|
args.push_back(ctxarg);
|
|
}
|
|
// ... or a nested function context arg
|
|
else if (nestedcall)
|
|
{
|
|
LLValue* contextptr = DtoNestedContext(loc, dfnval->func);
|
|
contextptr = DtoBitCast(contextptr, getVoidPtrType());
|
|
++argiter;
|
|
args.push_back(contextptr);
|
|
}
|
|
else
|
|
{
|
|
error(loc, "Context argument required but none given");
|
|
fatal();
|
|
}
|
|
}
|
|
|
|
// handle the rest of the arguments based on param passing style
|
|
|
|
// variadic instrinsics need some custom casts
|
|
if (va_intrinsic)
|
|
{
|
|
for (int i=0; i<n_arguments; i++)
|
|
{
|
|
Expression* exp = (Expression*)arguments->data[i];
|
|
DValue* expelem = exp->toElem(gIR);
|
|
// cast to va_list*
|
|
LLValue* val = DtoBitCast(expelem->getLVal(), getVoidPtrType());
|
|
++argiter;
|
|
args.push_back(val);
|
|
}
|
|
}
|
|
|
|
// d style varargs needs a few more hidden arguments as well as special passing
|
|
else if (dvarargs)
|
|
{
|
|
DtoBuildDVarArgList(args, palist, tf, arguments, argiter-argbegin+1);
|
|
}
|
|
|
|
// otherwise we're looking at a normal function call
|
|
else
|
|
{
|
|
Logger::println("doing normal arguments");
|
|
for (int i=0; i<n_arguments; i++) {
|
|
int j = argiter-argbegin;
|
|
Argument* fnarg = Argument::getNth(tf->parameters, i);
|
|
DValue* argval = DtoArgument(fnarg, (Expression*)arguments->data[i]);
|
|
LLValue* arg = argval->getRVal();
|
|
if (fnarg) // can fnarg ever be null in this block?
|
|
{
|
|
if (Logger::enabled())
|
|
{
|
|
Logger::cout() << "arg: " << *arg << '\n';
|
|
Logger::cout() << "expects: " << *callableTy->getParamType(j) << '\n';
|
|
}
|
|
if (arg->getType() != callableTy->getParamType(j))
|
|
arg = DtoBitCast(arg, callableTy->getParamType(j));
|
|
if (fnarg->llvmAttrs)
|
|
palist = palist.addAttr(j+1, fnarg->llvmAttrs);
|
|
}
|
|
++argiter;
|
|
args.push_back(arg);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
Logger::println("%d params passed", n);
|
|
for (int i=0; i<args.size(); ++i) {
|
|
assert(args[i]);
|
|
Logger::cout() << "arg["<<i<<"] = " << *args[i] << '\n';
|
|
}
|
|
#endif
|
|
|
|
// void returns cannot not be named
|
|
const char* varname = "";
|
|
if (callableTy->getReturnType() != LLType::VoidTy)
|
|
varname = "tmp";
|
|
|
|
//Logger::cout() << "Calling: " << *funcval << '\n';
|
|
|
|
// call the function
|
|
CallOrInvoke* call = gIR->CreateCallOrInvoke(callable, args.begin(), args.end(), varname);
|
|
|
|
// get return value
|
|
LLValue* retllval = (retinptr) ? args[0] : call->get();
|
|
|
|
// repaint the type if necessary
|
|
if (resulttype)
|
|
{
|
|
Type* rbase = resulttype->toBasetype();
|
|
Type* nextbase = tf->next->toBasetype();
|
|
if (!rbase->equals(nextbase))
|
|
{
|
|
Logger::println("repainting return value from '%s' to '%s'", tf->next->toChars(), rbase->toChars());
|
|
switch(rbase->ty)
|
|
{
|
|
case Tarray:
|
|
retllval = DtoAggrPaint(retllval, DtoType(rbase));
|
|
break;
|
|
|
|
case Tclass:
|
|
case Taarray:
|
|
case Tpointer:
|
|
retllval = DtoBitCast(retllval, DtoType(rbase));
|
|
break;
|
|
|
|
default:
|
|
assert(0 && "unhandled repainting of return value");
|
|
}
|
|
if (Logger::enabled())
|
|
Logger::cout() << "final return value: " << *retllval << '\n';
|
|
}
|
|
}
|
|
|
|
// set calling convention and parameter attributes
|
|
if (dfnval && dfnval->func)
|
|
{
|
|
LLFunction* llfunc = llvm::dyn_cast<LLFunction>(dfnval->val);
|
|
if (llfunc && llfunc->isIntrinsic())
|
|
palist = llvm::Intrinsic::getAttributes((llvm::Intrinsic::ID)llfunc->getIntrinsicID());
|
|
else
|
|
call->setCallingConv(callconv);
|
|
}
|
|
else
|
|
call->setCallingConv(callconv);
|
|
call->setAttributes(palist);
|
|
|
|
return new DImValue(resulttype, retllval);
|
|
}
|