Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Win64 ABI fix: return non-POD structs via sret. #856

Merged
merged 3 commits into from
Apr 8, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 98 additions & 18 deletions gen/abi-generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,64 @@
#include "gen/structs.h"
#include "gen/tollvm.h"

struct LLTypeMemoryLayout
{
// Structs and static arrays are folded recursively to scalars or anonymous structs.
// Pointer types are folded to an integer type.
static LLType* fold(LLType* type)
{
// T* => integer
if (type->isPointerTy())
return LLIntegerType::get(gIR->context(), getTypeBitSize(type));

if (LLStructType* structType = isaStruct(type))
{
unsigned numElements = structType->getNumElements();

// fold each element
std::vector<LLType*> elements;
elements.reserve(numElements);
for (unsigned i = 0; i < numElements; ++i)
elements.push_back(fold(structType->getElementType(i)));

// single element? then discard wrapping struct
if (numElements == 1)
return elements[0];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is better to check for the one-element case first?
This is really a question - are there one member structs in Phobos and druntime?


return LLStructType::get(gIR->context(), elements, structType->isPacked());
}

if (LLArrayType* arrayType = isaArray(type))
{
unsigned numElements = arrayType->getNumElements();
LLType* foldedElementType = fold(arrayType->getElementType());

// single element? then fold to scalar
if (numElements == 1)
return foldedElementType;

// otherwise: convert to struct of N folded elements
std::vector<LLType*> elements(numElements, foldedElementType);
return LLStructType::get(gIR->context(), elements);
}

return type;
}

// Checks two LLVM types for memory-layout equivalency.
static bool typesAreEquivalent(LLType* a, LLType* b)
{
if (a == b)
return true;
if (!a || !b)
return false;

return fold(a) == fold(b);
}
};

//////////////////////////////////////////////////////////////////////////////

/// Removes padding fields for (non-union-containing!) structs
struct RemoveStructPadding : ABIRewrite {
/// get a rewritten value back to its original form
Expand Down Expand Up @@ -58,6 +116,44 @@ struct RemoveStructPadding : ABIRewrite {
*/
struct IntegerRewrite : ABIRewrite
{
static LLType* getIntegerType(unsigned minSizeInBytes)
{
if (minSizeInBytes > 8)
return NULL;

unsigned size = minSizeInBytes;
switch (minSizeInBytes) {
case 0:
size = 1;
break;
case 3:
size = 4;
break;
case 5:
case 6:
case 7:
size = 8;
break;
default:
break;
}

return LLIntegerType::get(gIR->context(), size * 8);
}

static bool isObsoleteFor(LLType* llType)
{
if (!llType->isSized()) // e.g., opaque types
{
IF_LOG Logger::cout() << "IntegerRewrite: not rewriting non-sized type "
<< *llType << '\n';
return true;
}

LLType* integerType = getIntegerType(getTypeStoreSize(llType));
return LLTypeMemoryLayout::typesAreEquivalent(llType, integerType);
}

LLValue* get(Type* dty, DValue* dv)
{
LLValue* integer = dv->getRVal();
Expand All @@ -77,29 +173,13 @@ struct IntegerRewrite : ABIRewrite
{
assert(dty == dv->getType());
LLValue* address = getAddressOf(dv);
LLType* integerType = type(dty, NULL);
LLType* integerType = getIntegerType(dty->size());
return loadFromMemory(address, integerType, ".IntegerRewrite_putResult");
}

LLType* type(Type* t, LLType*)
{
unsigned size = t->size();
switch (size) {
case 0:
size = 1;
break;
case 3:
size = 4;
break;
case 5:
case 6:
case 7:
size = 8;
break;
default:
break;
}
return LLIntegerType::get(gIR->context(), size * 8);
return getIntegerType(t->size());
}
};

Expand Down
14 changes: 7 additions & 7 deletions gen/abi-ppc64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,12 @@ struct PPC64TargetABI : TargetABI {
void rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty)
{
// EXPLICIT PARAMETERS

for (IrFuncTy::ArgIter I = fty.args.begin(), E = fty.args.end(); I != E; ++I)
{
IrFuncTyArg& arg = **I;

if (arg.byref)
continue;

rewriteArgument(fty, arg);
if (!arg.byref)
rewriteArgument(fty, arg);
}
}

Expand All @@ -86,8 +83,11 @@ struct PPC64TargetABI : TargetABI {
{
if (canRewriteAsInt(ty))
{
arg.rewrite = &integerRewrite;
arg.ltype = integerRewrite.type(arg.type, arg.ltype);
if (!IntegerRewrite::isObsoleteFor(arg.ltype))
{
arg.rewrite = &integerRewrite;
arg.ltype = integerRewrite.type(arg.type, arg.ltype);
}
}
else
{
Expand Down
78 changes: 41 additions & 37 deletions gen/abi-win64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,16 @@ struct Win64TargetABI : TargetABI
void rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg);

private:
// Returns true if the D type is a composite (struct/static array/complex number).
bool isComposite(Type* t)
// Returns true if the D type is an aggregate:
// * struct
// * static/dynamic array
// * delegate
// * complex number
bool isAggregate(Type* t)
{
return t->ty == Tstruct || t->ty == Tsarray
|| t->iscomplex(); // treat complex numbers as structs too
TY ty = t->ty;
return ty == Tstruct || ty == Tsarray || ty == Tarray || ty == Tdelegate
|| t->iscomplex();
}

// Returns true if the D type can be bit-cast to an integer of the same size.
Expand All @@ -75,10 +80,11 @@ struct Win64TargetABI : TargetABI
bool isPassedWithByvalSemantics(Type* t)
{
return
// * structs/static arrays/complex numbers which can NOT be rewritten as integers
(isComposite(t) && !canRewriteAsInt(t)) ||
// * aggregates which can NOT be rewritten as integers
// (size > 64 bits or not a power of 2)
(isAggregate(t) && !canRewriteAsInt(t)) ||
// * 80-bit real and ireal
((t->ty == Tfloat80 || t->ty == Timaginary80) && realIs80bits());
(realIs80bits() && (t->ty == Tfloat80 || t->ty == Timaginary80));
}
};

Expand Down Expand Up @@ -113,45 +119,36 @@ bool Win64TargetABI::returnInArg(TypeFunction* tf)

Type* rt = tf->next->toBasetype();

// * everything <= 64 bits and of a size that is a power of 2
// (incl. 2x32-bit cfloat) is returned in a register (RAX, or
// * let LLVM return 80-bit real/ireal on the x87 stack, for DMD compliance
if (realIs80bits() && (rt->ty == Tfloat80 || rt->ty == Timaginary80))
return false;

// * all POD types <= 64 bits and of a size that is a power of 2
// (incl. 2x32-bit cfloat) are returned in a register (RAX, or
// XMM0 for single float/ifloat/double/idouble)
// * all other structs/static arrays/complex numbers and 80-bit
// real/ireal are returned via struct-return (sret)
return isPassedWithByvalSemantics(rt);
// * all other types are returned via struct-return (sret)
return (rt->ty == Tstruct && !((TypeStruct*)rt)->sym->isPOD())
|| isPassedWithByvalSemantics(rt);
}

bool Win64TargetABI::passByVal(Type* t)
{
t = t->toBasetype();

// FIXME: LLVM doesn't support ByVal on Win64 yet
//return isPassedWithByvalSemantics(t);
return false;
}

void Win64TargetABI::rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty)
{
// RETURN VALUE
if (!tf->isref)
{
Type* rt = fty.ret->type->toBasetype();
if (isComposite(rt) && canRewriteAsInt(rt))
{
fty.ret->rewrite = &integerRewrite;
fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype);
}
}
if (!fty.ret->byref && fty.ret->type->toBasetype()->ty != Tvoid)
rewriteArgument(fty, *fty.ret);

// EXPLICIT PARAMETERS
for (IrFuncTy::ArgIter I = fty.args.begin(), E = fty.args.end(); I != E; ++I)
{
IrFuncTyArg& arg = **I;

if (arg.byref)
continue;

rewriteArgument(fty, arg);
if (!arg.byref)
rewriteArgument(fty, arg);
}

// extern(D): reverse parameter order for non variadics, for DMD-compliance
Expand All @@ -161,15 +158,10 @@ void Win64TargetABI::rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty)

void Win64TargetABI::rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg)
{
Type* ty = arg.type->toBasetype();
LLType* originalLType = arg.ltype;
Type* t = arg.type->toBasetype();

if (isComposite(ty) && canRewriteAsInt(ty))
{
arg.rewrite = &integerRewrite;
arg.ltype = integerRewrite.type(arg.type, arg.ltype);
}
// FIXME: this should actually be handled by LLVM and the ByVal arg attribute
else if (isPassedWithByvalSemantics(ty))
if (isPassedWithByvalSemantics(t))
{
// these types are passed byval:
// the caller allocates a copy and then passes a pointer to the copy
Expand All @@ -182,4 +174,16 @@ void Win64TargetABI::rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg)
.add(LDC_ATTRIBUTE(NoAlias))
.add(LDC_ATTRIBUTE(NoCapture));
}
else if (isAggregate(t) && canRewriteAsInt(t) && !IntegerRewrite::isObsoleteFor(originalLType))
{
arg.rewrite = &integerRewrite;
arg.ltype = integerRewrite.type(arg.type, arg.ltype);
}

IF_LOG if (arg.rewrite)
{
Logger::println("Rewriting argument type %s", t->toChars());
LOG_SCOPE;
Logger::cout() << *originalLType << " => " << *arg.ltype << '\n';
}
}
37 changes: 2 additions & 35 deletions gen/abi-x86-64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,39 +94,6 @@ namespace {
}
} // namespace dmd_abi

// Checks two LLVM types for memory-layout equivalency.
// A pointer type is equivalent to any other pointer type.
bool typesAreEquivalent(LLType* a, LLType* b) {
if (a == b)
return true;
if (!a || !b)
return false;

LLStructType* structA;
while ((structA = isaStruct(a)) && structA->getNumElements() == 1)
a = structA->getElementType(0);

LLStructType* structB;
while ((structB = isaStruct(b)) && structB->getNumElements() == 1)
b = structB->getElementType(0);

if (a == b || (a->isPointerTy() && b->isPointerTy()))
return true;

if (!(structA && structB) ||
structA->isPacked() != structB->isPacked() ||
structA->getNumElements() != structB->getNumElements()) {
return false;
}

for (unsigned i = 0; i < structA->getNumElements(); ++i) {
if (!typesAreEquivalent(structA->getElementType(i), structB->getElementType(i)))
return false;
}

return true;
}

LLType* getAbiType(Type* ty) {
return dmd_abi::getAbiType(ty->toBasetype());
}
Expand Down Expand Up @@ -319,7 +286,7 @@ void X86_64TargetABI::rewriteArgument(IrFuncTyArg& arg, RegCount& regCount) {
Type* t = arg.type->toBasetype();

LLType* abiTy = getAbiType(t);
if (abiTy && !typesAreEquivalent(abiTy, originalLType)) {
if (abiTy && !LLTypeMemoryLayout::typesAreEquivalent(abiTy, originalLType)) {
IF_LOG {
Logger::println("Rewriting argument type %s", t->toChars());
LOG_SCOPE;
Expand All @@ -346,7 +313,7 @@ void X86_64TargetABI::rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty) {
regCount = RegCount(); // initialize

// RETURN VALUE
if (!tf->isref && !fty.arg_sret && tf->next->toBasetype()->ty != Tvoid) {
if (!fty.ret->byref && fty.ret->type->toBasetype()->ty != Tvoid) {
Logger::println("x86-64 ABI: Transforming return type");
LOG_SCOPE;
RegCount dummy;
Expand Down