blob: bed1d4020abdff77f207406247eadc7d0ac7fee3 [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 2010-2019 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/argtypes.c
*/
#include "root/dsystem.h"
#include "root/checkedint.h"
#include "mars.h"
#include "dsymbol.h"
#include "mtype.h"
#include "scope.h"
#include "init.h"
#include "expression.h"
#include "attrib.h"
#include "declaration.h"
#include "template.h"
#include "id.h"
#include "enum.h"
#include "import.h"
#include "aggregate.h"
#include "hdrgen.h"
/****************************************************
* This breaks a type down into 'simpler' types that can be passed to a function
* in registers, and returned in registers.
* It's highly platform dependent.
* Params:
* t = type to break down
* Returns:
* tuple of types, each element can be passed in a register.
* A tuple of zero length means the type cannot be passed/returned in registers.
*/
TypeTuple *toArgTypes(Type *t)
{
class ToArgTypes : public Visitor
{
public:
TypeTuple *result;
ToArgTypes()
{
result = NULL;
}
void visit(Type *)
{
// not valid for a parameter
}
void visit(TypeError *)
{
result = new TypeTuple(Type::terror);
}
void visit(TypeBasic *t)
{
Type *t1 = NULL;
Type *t2 = NULL;
switch (t->ty)
{
case Tvoid:
return;
case Tbool:
case Tint8:
case Tuns8:
case Tint16:
case Tuns16:
case Tint32:
case Tuns32:
case Tfloat32:
case Tint64:
case Tuns64:
case Tint128:
case Tuns128:
case Tfloat64:
case Tfloat80:
t1 = t;
break;
case Timaginary32:
t1 = Type::tfloat32;
break;
case Timaginary64:
t1 = Type::tfloat64;
break;
case Timaginary80:
t1 = Type::tfloat80;
break;
case Tcomplex32:
if (global.params.is64bit)
t1 = Type::tfloat64;
else
{
t1 = Type::tfloat64;
t2 = Type::tfloat64;
}
break;
case Tcomplex64:
t1 = Type::tfloat64;
t2 = Type::tfloat64;
break;
case Tcomplex80:
t1 = Type::tfloat80;
t2 = Type::tfloat80;
break;
case Tchar:
t1 = Type::tuns8;
break;
case Twchar:
t1 = Type::tuns16;
break;
case Tdchar:
t1 = Type::tuns32;
break;
default:
assert(0);
}
if (t1)
{
if (t2)
result = new TypeTuple(t1, t2);
else
result = new TypeTuple(t1);
}
else
result = new TypeTuple();
}
void visit(TypeVector *t)
{
result = new TypeTuple(t);
}
void visit(TypeSArray *t)
{
if (t->dim)
{
/* Should really be done as if it were a struct with dim members
* of the array's elements.
* I.e. int[2] should be done like struct S { int a; int b; }
*/
dinteger_t sz = t->dim->toInteger();
// T[1] should be passed like T
if (sz == 1)
{
t->next->accept(this);
return;
}
}
result = new TypeTuple(); // pass on the stack for efficiency
}
void visit(TypeAArray *)
{
result = new TypeTuple(Type::tvoidptr);
}
void visit(TypePointer *)
{
result = new TypeTuple(Type::tvoidptr);
}
/*************************************
* Convert a floating point type into the equivalent integral type.
*/
static Type *mergeFloatToInt(Type *t)
{
switch (t->ty)
{
case Tfloat32:
case Timaginary32:
t = Type::tint32;
break;
case Tfloat64:
case Timaginary64:
case Tcomplex32:
t = Type::tint64;
break;
default:
assert(0);
}
return t;
}
/*************************************
* This merges two types into an 8byte type.
* Params:
* t1 = first type (can be null)
* t2 = second type (can be null)
* offset2 = offset of t2 from start of t1
* Returns:
* type that encompasses both t1 and t2, null if cannot be done
*/
static Type *argtypemerge(Type *t1, Type *t2, unsigned offset2)
{
//printf("argtypemerge(%s, %s, %d)\n", t1 ? t1->toChars() : "", t2 ? t2->toChars() : "", offset2);
if (!t1)
{ assert(!t2 || offset2 == 0);
return t2;
}
if (!t2)
return t1;
const d_uns64 sz1 = t1->size(Loc());
const d_uns64 sz2 = t2->size(Loc());
assert(sz1 != SIZE_INVALID && sz2 != SIZE_INVALID);
if (t1->ty != t2->ty &&
(t1->ty == Tfloat80 || t2->ty == Tfloat80))
return NULL;
// [float,float] => [cfloat]
if (t1->ty == Tfloat32 && t2->ty == Tfloat32 && offset2 == 4)
return Type::tfloat64;
// Merging floating and non-floating types produces the non-floating type
if (t1->isfloating())
{
if (!t2->isfloating())
t1 = mergeFloatToInt(t1);
}
else if (t2->isfloating())
t2 = mergeFloatToInt(t2);
Type *t;
// Pick type with larger size
if (sz1 < sz2)
t = t2;
else
t = t1;
// If t2 does not lie within t1, need to increase the size of t to enclose both
assert(sz2 < UINT64_MAX - UINT32_MAX);
if (offset2 && sz1 < offset2 + sz2)
{
switch (offset2 + sz2)
{
case 2:
t = Type::tint16;
break;
case 3:
case 4:
t = Type::tint32;
break;
default:
t = Type::tint64;
break;
}
}
return t;
}
void visit(TypeDArray *)
{
/* Should be done as if it were:
* struct S { size_t length; void* ptr; }
*/
if (global.params.is64bit && !global.params.isLP64)
{
// For AMD64 ILP32 ABI, D arrays fit into a single integer register.
unsigned offset = (unsigned)Type::tsize_t->size(Loc());
Type *t = argtypemerge(Type::tsize_t, Type::tvoidptr, offset);
if (t)
{
result = new TypeTuple(t);
return;
}
}
result = new TypeTuple(Type::tsize_t, Type::tvoidptr);
}
void visit(TypeDelegate *)
{
/* Should be done as if it were:
* struct S { size_t length; void* ptr; }
*/
if (global.params.is64bit && !global.params.isLP64)
{
// For AMD64 ILP32 ABI, delegates fit into a single integer register.
unsigned offset = (unsigned)Type::tsize_t->size(Loc());
Type *t = argtypemerge(Type::tsize_t, Type::tvoidptr, offset);
if (t)
{
result = new TypeTuple(t);
return;
}
}
result = new TypeTuple(Type::tvoidptr, Type::tvoidptr);
}
void visit(TypeStruct *t)
{
//printf("TypeStruct::toArgTypes() %s\n", t->toChars());
if (!t->sym->isPOD() || t->sym->fields.dim == 0)
{
Lmemory:
//printf("\ttoArgTypes() %s => [ ]\n", t->toChars());
result = new TypeTuple(); // pass on the stack
return;
}
Type *t1 = NULL;
Type *t2 = NULL;
const d_uns64 sz = t->size(Loc());
assert(sz < 0xFFFFFFFF);
switch ((unsigned)sz)
{
case 1:
t1 = Type::tint8;
break;
case 2:
t1 = Type::tint16;
break;
case 3:
if (!global.params.is64bit)
goto Lmemory;
/* fall through */
case 4:
t1 = Type::tint32;
break;
case 5:
case 6:
case 7:
if (!global.params.is64bit)
goto Lmemory;
/* fall through */
case 8:
t1 = Type::tint64;
break;
case 16:
t1 = NULL; // could be a TypeVector
break;
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
if (!global.params.is64bit)
goto Lmemory;
t1 = NULL;
break;
default:
goto Lmemory;
}
if (global.params.is64bit && t->sym->fields.dim)
{
t1 = NULL;
for (size_t i = 0; i < t->sym->fields.dim; i++)
{
VarDeclaration *f = t->sym->fields[i];
//printf(" [%d] %s f->type = %s\n", (int)i, f->toChars(), f->type->toChars());
TypeTuple *tup = toArgTypes(f->type);
if (!tup)
goto Lmemory;
size_t dim = tup->arguments->dim;
Type *ft1 = NULL;
Type *ft2 = NULL;
switch (dim)
{
case 2:
ft1 = (*tup->arguments)[0]->type;
ft2 = (*tup->arguments)[1]->type;
break;
case 1:
if (f->offset < 8)
ft1 = (*tup->arguments)[0]->type;
else
ft2 = (*tup->arguments)[0]->type;
break;
default:
goto Lmemory;
}
if (f->offset & 7)
{
// Misaligned fields goto Lmemory
unsigned alignsz = f->type->alignsize();
if (f->offset & (alignsz - 1))
goto Lmemory;
// Fields that overlap the 8byte boundary goto Lmemory
const d_uns64 fieldsz = f->type->size(Loc());
assert(fieldsz != SIZE_INVALID && fieldsz < UINT64_MAX - UINT32_MAX);
if (f->offset < 8 && (f->offset + fieldsz) > 8)
goto Lmemory;
}
// First field in 8byte must be at start of 8byte
assert(t1 || f->offset == 0);
//printf("ft1 = %s\n", ft1 ? ft1->toChars() : "null");
//printf("ft2 = %s\n", ft2 ? ft2->toChars() : "null");
if (ft1)
{
t1 = argtypemerge(t1, ft1, f->offset);
if (!t1)
goto Lmemory;
}
if (ft2)
{
unsigned off2 = f->offset;
if (ft1)
off2 = 8;
if (!t2 && off2 != 8)
goto Lmemory;
assert(t2 || off2 == 8);
t2 = argtypemerge(t2, ft2, off2 - 8);
if (!t2)
goto Lmemory;
}
}
if (t2)
{
if (t1->isfloating() && t2->isfloating())
{
if ((t1->ty == Tfloat32 || t1->ty == Tfloat64) &&
(t2->ty == Tfloat32 || t2->ty == Tfloat64))
;
else
goto Lmemory;
}
else if (t1->isfloating())
goto Lmemory;
else if (t2->isfloating())
goto Lmemory;
else
{
}
}
}
//printf("\ttoArgTypes() %s => [%s,%s]\n", t->toChars(), t1 ? t1->toChars() : "", t2 ? t2->toChars() : "");
if (t1)
{
//if (t1) printf("test1: %s => %s\n", toChars(), t1->toChars());
if (t2)
result = new TypeTuple(t1, t2);
else
result = new TypeTuple(t1);
}
else
goto Lmemory;
}
void visit(TypeEnum *t)
{
t->toBasetype()->accept(this);
}
void visit(TypeClass *)
{
result = new TypeTuple(Type::tvoidptr);
}
};
ToArgTypes v;
t->accept(&v);
return v.result;
}