mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-06 19:06:02 +03:00
C ABI: Do not pass empty structs as parameters at all
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.
This commit is contained in:
parent
410203b37a
commit
ee50259dfd
5 changed files with 130 additions and 91 deletions
|
@ -164,6 +164,17 @@ llvm::FunctionType* DtoFunctionType(Type* type, IrFuncTy &irFty, Type* thistype,
|
||||||
}
|
}
|
||||||
else if (!passPointer)
|
else if (!passPointer)
|
||||||
{
|
{
|
||||||
|
if (loweredDType->toBasetype()->ty == Tstruct)
|
||||||
|
{
|
||||||
|
// Do not pass empty structs at all for C++ ABI compatibility.
|
||||||
|
// Tests with clang reveal that more complex "empty" types, for
|
||||||
|
// example a struct containing an empty struct, are not
|
||||||
|
// optimized in the same way.
|
||||||
|
StructDeclaration *sd =
|
||||||
|
static_cast<TypeStruct*>(loweredDType->toBasetype())->sym;
|
||||||
|
if (sd->fields.empty()) continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (abi->passByVal(loweredDType))
|
if (abi->passByVal(loweredDType))
|
||||||
{
|
{
|
||||||
attrBuilder.add(LDC_ATTRIBUTE(ByVal));
|
attrBuilder.add(LDC_ATTRIBUTE(ByVal));
|
||||||
|
@ -177,6 +188,7 @@ llvm::FunctionType* DtoFunctionType(Type* type, IrFuncTy &irFty, Type* thistype,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newIrFty.args.push_back(new IrFuncTyArg(loweredDType, passPointer, attrBuilder));
|
newIrFty.args.push_back(new IrFuncTyArg(loweredDType, passPointer, attrBuilder));
|
||||||
|
newIrFty.args.back()->parametersIdx = i;
|
||||||
++nextLLArgIdx;
|
++nextLLArgIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,13 +423,11 @@ static void set_param_attrs(TypeFunction* f, llvm::Function* func, FuncDeclarati
|
||||||
|
|
||||||
#undef ADD_PA
|
#undef ADD_PA
|
||||||
|
|
||||||
// set attrs on the rest of the arguments
|
// Set attributes on the explicit parameters.
|
||||||
size_t n = Parameter::dim(f->parameters);
|
const size_t n = irFty.args.size();
|
||||||
for (size_t k = 0; k < n; k++)
|
for (size_t k = 0; k < n; k++)
|
||||||
{
|
{
|
||||||
assert(Parameter::getNth(f->parameters, k));
|
const size_t i = idx + (irFty.reverseParams ? (n - k - 1) : k);
|
||||||
|
|
||||||
unsigned i = idx + (irFty.reverseParams ? n-k-1 : k);
|
|
||||||
newAttrs.add(i, irFty.args[k]->attrs);
|
newAttrs.add(i, irFty.args[k]->attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,32 +586,28 @@ void DtoDeclareFunction(FuncDeclaration* fdecl)
|
||||||
++iarg;
|
++iarg;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we never reference parameters of function prototypes
|
|
||||||
unsigned int k = 0;
|
unsigned int k = 0;
|
||||||
for (; iarg != func->arg_end(); ++iarg)
|
for (; iarg != func->arg_end(); ++iarg)
|
||||||
{
|
{
|
||||||
if (fdecl->parameters && fdecl->parameters->dim > k)
|
size_t llExplicitIdx = irFty.reverseParams ? irFty.args.size() - k - 1 : k;
|
||||||
{
|
++k;
|
||||||
int paramIndex = irFty.reverseParams ? fdecl->parameters->dim-k-1 : k;
|
IrFuncTyArg *arg = irFty.args[llExplicitIdx];
|
||||||
Dsymbol* argsym = static_cast<Dsymbol*>(fdecl->parameters->data[paramIndex]);
|
|
||||||
|
|
||||||
VarDeclaration* argvd = argsym->isVarDeclaration();
|
if (!fdecl->parameters || arg->parametersIdx >= fdecl->parameters->dim)
|
||||||
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");
|
iarg->setName("unnamed");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dsymbol* const argsym = (*fdecl->parameters)[arg->parametersIdx];
|
||||||
|
VarDeclaration* argvd = argsym->isVarDeclaration();
|
||||||
|
assert(argvd);
|
||||||
|
|
||||||
|
iarg->setName(argvd->ident->toChars() + llvm::Twine("_arg"));
|
||||||
|
|
||||||
|
IrParameter *irParam = getIrParameter(argvd, true);
|
||||||
|
irParam->arg = arg;
|
||||||
|
irParam->value = iarg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -842,24 +848,34 @@ void DtoDefineFunction(FuncDeclaration* fd)
|
||||||
irFunc->nestArg = val;
|
irFunc->nestArg = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
// give arguments storage
|
// give arguments storage and debug info
|
||||||
// and debug info
|
|
||||||
if (fd->parameters)
|
if (fd->parameters)
|
||||||
{
|
{
|
||||||
size_t n = irFty.args.size();
|
// Not all arguments are necessarily passed on the LLVM level
|
||||||
assert(n == fd->parameters->dim);
|
// (e.g. zero-member structs), so we need to keep track of the
|
||||||
for (size_t i=0; i < n; ++i)
|
// index in the IrFuncTy args array separately.
|
||||||
|
size_t llArgIdx = 0;
|
||||||
|
for (size_t i = 0; i < fd->parameters->dim; ++i)
|
||||||
{
|
{
|
||||||
Dsymbol* argsym = static_cast<Dsymbol*>(fd->parameters->data[i]);
|
Dsymbol* const argsym = (*fd->parameters)[i];
|
||||||
VarDeclaration* vd = argsym->isVarDeclaration();
|
VarDeclaration* const vd = argsym->isVarDeclaration();
|
||||||
assert(vd);
|
assert(vd);
|
||||||
|
const bool refout = vd->storage_class & (STCref | STCout);
|
||||||
|
|
||||||
IrParameter* irparam = getIrParameter(vd);
|
IrParameter* irparam = getIrParameter(vd);
|
||||||
assert(irparam);
|
Type* debugInfoType = vd->type;
|
||||||
|
if (!irparam)
|
||||||
bool refout = vd->storage_class & (STCref | STCout);
|
{
|
||||||
bool lazy = vd->storage_class & STClazy;
|
// This is a parameter that is not passed on the LLVM level.
|
||||||
bool firstClassVal = !refout && (!irparam->arg->byref || lazy);
|
// Create the param here and set it to a "dummy" alloca that
|
||||||
|
// we do not store to here.
|
||||||
|
irparam = getIrParameter(vd, true);
|
||||||
|
irparam->value = DtoAlloca(vd->type, vd->ident->toChars());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const bool lazy = vd->storage_class & STClazy;
|
||||||
|
const bool firstClassVal = !refout && (!irparam->arg->byref || lazy);
|
||||||
if (firstClassVal)
|
if (firstClassVal)
|
||||||
{
|
{
|
||||||
// alloca a stack slot for this first class value arg
|
// alloca a stack slot for this first class value arg
|
||||||
|
@ -867,14 +883,18 @@ void DtoDefineFunction(FuncDeclaration* fd)
|
||||||
|
|
||||||
// let the abi transform the argument back first
|
// let the abi transform the argument back first
|
||||||
DImValue arg_dval(vd->type, irparam->value);
|
DImValue arg_dval(vd->type, irparam->value);
|
||||||
irFty.getParam(vd->type, i, &arg_dval, mem);
|
irFty.getParam(vd->type, llArgIdx, &arg_dval, mem);
|
||||||
|
|
||||||
// set the arg var value to the alloca
|
// set the arg var value to the alloca
|
||||||
irparam->value = mem;
|
irparam->value = mem;
|
||||||
|
|
||||||
|
debugInfoType = irparam->arg->type;
|
||||||
|
}
|
||||||
|
++llArgIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (global.params.symdebug && !(isaArgument(irparam->value) && isaArgument(irparam->value)->hasByValAttr()) && !refout)
|
if (global.params.symdebug && !(isaArgument(irparam->value) && isaArgument(irparam->value)->hasByValAttr()) && !refout)
|
||||||
gIR->DBuilder.EmitLocalVariable(irparam->value, vd, firstClassVal ? irparam->arg->type : 0);
|
gIR->DBuilder.EmitLocalVariable(irparam->value, vd, debugInfoType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,7 +158,7 @@ DValue* DtoNestedVariable(Loc& loc, Type* astype, VarDeclaration* vd, bool byref
|
||||||
Logger::cout() << "Addr: " << *val << '\n';
|
Logger::cout() << "Addr: " << *val << '\n';
|
||||||
Logger::cout() << "of type: " << *val->getType() << '\n';
|
Logger::cout() << "of type: " << *val->getType() << '\n';
|
||||||
}
|
}
|
||||||
if (byref || (vd->isParameter() && getIrParameter(vd)->arg->byref)) {
|
if (byref || (vd->isParameter() && getIrParameter(vd)->arg && getIrParameter(vd)->arg->byref)) {
|
||||||
val = DtoAlignedLoad(val);
|
val = DtoAlignedLoad(val);
|
||||||
//dwarfOpDeref(dwarfAddr);
|
//dwarfOpDeref(dwarfAddr);
|
||||||
IF_LOG {
|
IF_LOG {
|
||||||
|
@ -364,9 +364,11 @@ static void DtoCreateNestedContextType(FuncDeclaration* fd) {
|
||||||
irLocal->nestedIndex = types.size();
|
irLocal->nestedIndex = types.size();
|
||||||
irLocal->nestedDepth = depth;
|
irLocal->nestedDepth = depth;
|
||||||
|
|
||||||
if (vd->isParameter()) {
|
if (vd->isParameter() && getIrParameter(vd)->arg) {
|
||||||
// Parameters will have storage associated with them (to handle byref etc.),
|
// Parameters that are part of the LLVM signature will have
|
||||||
// so handle those cases specially by storing a pointer instead of a value.
|
// storage associated with them (to handle byref etc.), so
|
||||||
|
// handle those cases specially by storing a pointer instead
|
||||||
|
// of a value.
|
||||||
const IrParameter* irparam = getIrParameter(vd);
|
const IrParameter* irparam = getIrParameter(vd);
|
||||||
const bool refout = vd->storage_class & (STCref | STCout);
|
const bool refout = vd->storage_class & (STCref | STCout);
|
||||||
const bool lazy = vd->storage_class & STClazy;
|
const bool lazy = vd->storage_class & STClazy;
|
||||||
|
@ -479,7 +481,7 @@ void DtoCreateNestedContext(FuncDeclaration* fd) {
|
||||||
LOG_SCOPE
|
LOG_SCOPE
|
||||||
IrParameter* parm = getIrParameter(vd);
|
IrParameter* parm = getIrParameter(vd);
|
||||||
|
|
||||||
if (parm->arg->byref)
|
if (parm->arg && parm->arg->byref)
|
||||||
{
|
{
|
||||||
storeVariable(vd, gep);
|
storeVariable(vd, gep);
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,14 +117,20 @@ LLFunctionType* DtoExtractFunctionType(LLType* type)
|
||||||
static void addExplicitArguments(std::vector<LLValue*>& args, AttrSet& attrs,
|
static void addExplicitArguments(std::vector<LLValue*>& args, AttrSet& attrs,
|
||||||
IrFuncTy& irFty, LLFunctionType* callableTy, const std::vector<DValue*>& argvals, int numFormalParams)
|
IrFuncTy& irFty, LLFunctionType* callableTy, const std::vector<DValue*>& argvals, int numFormalParams)
|
||||||
{
|
{
|
||||||
const int numImplicitArgs = args.size();
|
// Number of arguments added to the LLVM type that are implicit on the
|
||||||
const int numExplicitArgs = argvals.size();
|
// frontend side of things (this, context pointers, etc.)
|
||||||
|
const size_t implicitLLArgCount = args.size();
|
||||||
|
|
||||||
args.resize(numImplicitArgs + numExplicitArgs, static_cast<LLValue*>(0));
|
// 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
|
// construct and initialize an IrFuncTyArg object for each vararg
|
||||||
std::vector<IrFuncTyArg*> optionalIrArgs;
|
std::vector<IrFuncTyArg*> optionalIrArgs;
|
||||||
for (int i = numFormalParams; i < numExplicitArgs; i++) {
|
for (size_t i = numFormalParams; i < explicitDArgCount; i++) {
|
||||||
Type* argType = argvals[i]->getType();
|
Type* argType = argvals[i]->getType();
|
||||||
bool passByVal = gABI->passByVal(argType);
|
bool passByVal = gABI->passByVal(argType);
|
||||||
|
|
||||||
|
@ -135,64 +141,68 @@ static void addExplicitArguments(std::vector<LLValue*>& args, AttrSet& attrs,
|
||||||
initialAttrs.add(DtoShouldExtend(argType));
|
initialAttrs.add(DtoShouldExtend(argType));
|
||||||
|
|
||||||
optionalIrArgs.push_back(new IrFuncTyArg(argType, passByVal, initialAttrs));
|
optionalIrArgs.push_back(new IrFuncTyArg(argType, passByVal, initialAttrs));
|
||||||
|
optionalIrArgs.back()->parametersIdx = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// let the ABI rewrite the IrFuncTyArg objects
|
// let the ABI rewrite the IrFuncTyArg objects
|
||||||
gABI->rewriteVarargs(irFty, optionalIrArgs);
|
gABI->rewriteVarargs(irFty, optionalIrArgs);
|
||||||
|
|
||||||
for (int i = 0; i < numExplicitArgs; i++)
|
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)
|
||||||
{
|
{
|
||||||
int j = numImplicitArgs + (irFty.reverseParams ? numExplicitArgs - i - 1 : i);
|
const bool isVararg = (i >= irFty.args.size());
|
||||||
|
|
||||||
DValue* argval = argvals[i];
|
|
||||||
Type* argType = argval->getType();
|
|
||||||
|
|
||||||
const bool isVararg = (i >= numFormalParams);
|
|
||||||
IrFuncTyArg* irArg = NULL;
|
IrFuncTyArg* irArg = NULL;
|
||||||
LLValue* arg = NULL;
|
if (isVararg)
|
||||||
|
|
||||||
if (!isVararg)
|
|
||||||
{
|
|
||||||
irArg = irFty.args[i];
|
|
||||||
arg = irFty.putParam(argType, i, argval);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
irArg = optionalIrArgs[i - numFormalParams];
|
irArg = optionalIrArgs[i - numFormalParams];
|
||||||
arg = irFty.putParam(argType, *irArg, argval);
|
else
|
||||||
}
|
irArg = irFty.args[i];
|
||||||
|
|
||||||
LLType* callableArgType = (isVararg ? NULL : callableTy->getParamType(j));
|
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:
|
// Hack around LDC assuming structs and static arrays are in memory:
|
||||||
// If the function wants a struct, and the argument value is a
|
// If the function wants a struct, and the argument value is a
|
||||||
// pointer to a struct, load from it before passing it in.
|
// pointer to a struct, load from it before passing it in.
|
||||||
if (isaPointer(arg) && DtoIsPassedByRef(argType) &&
|
if (isaPointer(llVal) && DtoIsPassedByRef(argType) &&
|
||||||
( (!isVararg && !isaPointer(callableArgType)) ||
|
((!isVararg && !isaPointer(callableArgType)) ||
|
||||||
(isVararg && !irArg->byref && !irArg->isByVal()) ) )
|
(isVararg && !irArg->byref && !irArg->isByVal())))
|
||||||
{
|
{
|
||||||
Logger::println("Loading struct type for function argument");
|
Logger::println("Loading struct type for function argument");
|
||||||
arg = DtoLoad(arg);
|
llVal = DtoLoad(llVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// parameter type mismatch, this is hard to get rid of
|
// parameter type mismatch, this is hard to get rid of
|
||||||
if (!isVararg && arg->getType() != callableArgType)
|
if (!isVararg && llVal->getType() != callableArgType)
|
||||||
{
|
{
|
||||||
#if 1
|
IF_LOG
|
||||||
IF_LOG {
|
{
|
||||||
Logger::cout() << "arg: " << *arg << '\n';
|
Logger::cout() << "arg: " << *llVal << '\n';
|
||||||
Logger::cout() << "of type: " << *arg->getType() << '\n';
|
|
||||||
Logger::cout() << "expects: " << *callableArgType << '\n';
|
Logger::cout() << "expects: " << *callableArgType << '\n';
|
||||||
}
|
}
|
||||||
#endif
|
if (isaStruct(llVal))
|
||||||
if (isaStruct(arg))
|
llVal = DtoAggrPaint(llVal, callableArgType);
|
||||||
arg = DtoAggrPaint(arg, callableArgType);
|
|
||||||
else
|
else
|
||||||
arg = DtoBitCast(arg, callableArgType);
|
llVal = DtoBitCast(llVal, callableArgType);
|
||||||
}
|
}
|
||||||
|
|
||||||
args[j] = arg;
|
args[llArgIdx] = llVal;
|
||||||
attrs.add(j + 1, irArg->attrs);
|
// +1 as index 0 contains the function attributes.
|
||||||
|
attrs.add(llArgIdx + 1, irArg->attrs);
|
||||||
|
|
||||||
if (isVararg)
|
if (isVararg)
|
||||||
delete irArg;
|
delete irArg;
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include "gen/tollvm.h"
|
#include "gen/tollvm.h"
|
||||||
|
|
||||||
IrFuncTyArg::IrFuncTyArg(Type* t, bool bref, const AttrBuilder& a)
|
IrFuncTyArg::IrFuncTyArg(Type* t, bool bref, const AttrBuilder& a)
|
||||||
: type(t),
|
: type(t), parametersIdx(0),
|
||||||
ltype(t != Type::tvoid && bref ? DtoType(t->pointerTo()) : DtoType(t)),
|
ltype(t != Type::tvoid && bref ? DtoType(t->pointerTo()) : DtoType(t)),
|
||||||
attrs(a), byref(bref), rewrite(0)
|
attrs(a), byref(bref), rewrite(0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,14 +37,21 @@ namespace llvm {
|
||||||
class FunctionType;
|
class FunctionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// represents a function type argument
|
/// Represents a function type argument (both explicit and implicit as well as
|
||||||
// both explicit and implicit as well as return values
|
/// return values).
|
||||||
|
///
|
||||||
|
/// Instances of this only exist for arguments that are actually lowered to an
|
||||||
|
/// LLVM parameter (e.g. not for empty structs).
|
||||||
struct IrFuncTyArg
|
struct IrFuncTyArg
|
||||||
{
|
{
|
||||||
/** This is the original D type as the frontend knows it
|
/** This is the original D type as the frontend knows it
|
||||||
* May NOT be rewritten!!! */
|
* May NOT be rewritten!!! */
|
||||||
Type* const type;
|
Type* const type;
|
||||||
|
|
||||||
|
/// The index of the declaration in the FuncDeclaration::parameters array
|
||||||
|
/// corresponding to this argument.
|
||||||
|
size_t parametersIdx;
|
||||||
|
|
||||||
/// This is the final LLVM Type used for the parameter/return value type
|
/// This is the final LLVM Type used for the parameter/return value type
|
||||||
llvm::Type* ltype;
|
llvm::Type* ltype;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue