/* 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; }