From faeae611f7da63b3175951690c6a8691b398da6d Mon Sep 17 00:00:00 2001 From: Johannes Pfau Date: Sun, 4 Feb 2018 20:52:46 +0100 Subject: [PATCH 1/4] WIP: Generate target specific typeinfo fields in backend --- gcc/d/d-target.def | 24 ++++ gcc/d/d-target.h | 30 +++++ gcc/d/typeinfo.cc | 276 ++++++++++++++++++++++++++++++--------------- 3 files changed, 236 insertions(+), 94 deletions(-) diff --git a/gcc/d/d-target.def b/gcc/d/d-target.def index e80f2ad36..4775e971d 100644 --- a/gcc/d/d-target.def +++ b/gcc/d/d-target.def @@ -56,5 +56,29 @@ DEFHOOK unsigned, (void), hook_uint_void_0) +/* The types of extra fields to store in TypeInfo for va_arg implementation. */ +DEFHOOK +(d_vaarg_ti_types, + "Returns types of target-specific fields which need to be inserted into\ + TypeInfo to support the va_arg variant taking a TypeInfo argument.\ + The va_arg imlementation can be found in core/stdc/stdarg.d. This function's\ + first parameter is a @code{tinfo_kind} as fields can be differnt depending\ + on the kind of TypeInfo. Returns a @code{TREE_LIST} of types and void_zero_node\ + if no extra fields are needed.", + tree, (d_tinfo_kind), + d_default_vaarg_ti_types) + +/* The initializer values of extra fields to store in TypeInfo. */ +DEFHOOK +(d_vaarg_ti_values, + "Returns initial values for target-specific fields which need to be inserted\ + into TypeInfo to support the va_arg variant taking a TypeInfo argument.\ + The va_arg imlementation can be found in core/stdc/stdarg.d. This function's\ + first parameter is a @code{tree} for the type described by the TypeInfo\ + instance. Returns a @code{TREE_LIST} of initial values and NULL_TREE\ + if no extra fields are needed.", + tree, (d_tinfo_kind, tree), + d_default_vaarg_ti_values) + /* Close the 'struct gcc_targetdm' definition. */ HOOK_VECTOR_END (C90_EMPTY_HACK) diff --git a/gcc/d/d-target.h b/gcc/d/d-target.h index b3c4100f2..ef30cd179 100644 --- a/gcc/d/d-target.h +++ b/gcc/d/d-target.h @@ -18,6 +18,30 @@ #ifndef GCC_D_TARGET_H #define GCC_D_TARGET_H +/* Used by target as parameter for d_vaarg_ti_types */ +enum d_tinfo_kind +{ + D_TK_TYPEINFO_TYPE, /* object.TypeInfo */ + D_TK_CLASSINFO_TYPE, /* object.TypeInfo_Class */ + D_TK_INTERFACE_TYPE, /* object.TypeInfo_Interface */ + D_TK_STRUCT_TYPE, /* object.TypeInfo_Struct */ + D_TK_POINTER_TYPE, /* object.TypeInfo_Pointer */ + D_TK_ARRAY_TYPE, /* object.TypeInfo_Array */ + D_TK_STATICARRAY_TYPE, /* object.TypeInfo_StaticArray */ + D_TK_ASSOCIATIVEARRAY_TYPE, /* object.TypeInfo_AssociativeArray */ + D_TK_VECTOR_TYPE, /* object.TypeInfo_Vector */ + D_TK_ENUMERAL_TYPE, /* object.TypeInfo_Enum */ + D_TK_FUNCTION_TYPE, /* object.TypeInfo_Function */ + D_TK_DELEGATE_TYPE, /* object.TypeInfo_Delegate */ + D_TK_TYPELIST_TYPE, /* object.TypeInfo_Tuple */ + D_TK_CONST_TYPE, /* object.TypeInfo_Const */ + D_TK_IMMUTABLE_TYPE, /* object.TypeInfo_Invariant */ + D_TK_SHARED_TYPE, /* object.TypeInfo_Shared */ + D_TK_INOUT_TYPE, /* object.TypeInfo_Inout */ + D_TK_CPPTI_TYPE, /* object.__cpp_type_info_ptr */ + D_TK_END, +}; + #define DEFHOOKPOD(NAME, DOC, TYPE, INIT) TYPE NAME; #define DEFHOOK(NAME, DOC, TYPE, PARAMS, INIT) TYPE (* NAME) PARAMS; #define DEFHOOK_UNDOC DEFHOOK @@ -31,4 +55,10 @@ extern struct gcc_targetdm targetdm; /* Used by target to add predefined version idenditiers. */ extern void d_add_builtin_version (const char *); +/* default implementation for d_vaarg_ti_types */ +extern tree d_default_vaarg_ti_types (d_tinfo_kind); + +/* default implementation for d_vaarg_ti_values */ +extern tree d_default_vaarg_ti_values (d_tinfo_kind, tree); + #endif /* GCC_D_TARGET_H */ diff --git a/gcc/d/typeinfo.cc b/gcc/d/typeinfo.cc index 04be9346d..29d18620b 100644 --- a/gcc/d/typeinfo.cc +++ b/gcc/d/typeinfo.cc @@ -32,12 +32,12 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "toplev.h" #include "stor-layout.h" +#include "config.h" #include "d-tree.h" #include "d-frontend.h" #include "d-target.h" - /* D returns type information to the user as TypeInfo class objects, and can be retrieved for any type using `typeid()'. We also use type information to implement many runtime library helpers, including `new', `delete', most @@ -60,90 +60,67 @@ along with GCC; see the file COPYING3. If not see has no declaration. We however only need the addresses of such incomplete TypeInfo objects for static initialization. */ -enum tinfo_kind -{ - TK_TYPEINFO_TYPE, /* object.TypeInfo */ - TK_CLASSINFO_TYPE, /* object.TypeInfo_Class */ - TK_INTERFACE_TYPE, /* object.TypeInfo_Interface */ - TK_STRUCT_TYPE, /* object.TypeInfo_Struct */ - TK_POINTER_TYPE, /* object.TypeInfo_Pointer */ - TK_ARRAY_TYPE, /* object.TypeInfo_Array */ - TK_STATICARRAY_TYPE, /* object.TypeInfo_StaticArray */ - TK_ASSOCIATIVEARRAY_TYPE, /* object.TypeInfo_AssociativeArray */ - TK_VECTOR_TYPE, /* object.TypeInfo_Vector */ - TK_ENUMERAL_TYPE, /* object.TypeInfo_Enum */ - TK_FUNCTION_TYPE, /* object.TypeInfo_Function */ - TK_DELEGATE_TYPE, /* object.TypeInfo_Delegate */ - TK_TYPELIST_TYPE, /* object.TypeInfo_Tuple */ - TK_CONST_TYPE, /* object.TypeInfo_Const */ - TK_IMMUTABLE_TYPE, /* object.TypeInfo_Invariant */ - TK_SHARED_TYPE, /* object.TypeInfo_Shared */ - TK_INOUT_TYPE, /* object.TypeInfo_Inout */ - TK_CPPTI_TYPE, /* object.__cpp_type_info_ptr */ - TK_END, -}; - /* An array of all internal TypeInfo derived types we need. The TypeInfo and ClassInfo types are created early, the remainder are generated as needed. */ -static GTY(()) tree tinfo_types[TK_END]; +static GTY(()) tree tinfo_types[D_TK_END]; /* Return the kind of TypeInfo used to describe TYPE. */ -static tinfo_kind +static d_tinfo_kind get_typeinfo_kind (Type *type) { /* Check head shared/const modifiers first. */ if (type->isShared ()) - return TK_SHARED_TYPE; + return D_TK_SHARED_TYPE; else if (type->isConst ()) - return TK_CONST_TYPE; + return D_TK_CONST_TYPE; else if (type->isImmutable ()) - return TK_IMMUTABLE_TYPE; + return D_TK_IMMUTABLE_TYPE; else if (type->isWild ()) - return TK_INOUT_TYPE; + return D_TK_INOUT_TYPE; switch (type->ty) { case Tpointer: - return TK_POINTER_TYPE; + return D_TK_POINTER_TYPE; case Tarray: - return TK_ARRAY_TYPE; + return D_TK_ARRAY_TYPE; case Tsarray: - return TK_STATICARRAY_TYPE; + return D_TK_STATICARRAY_TYPE; case Taarray: - return TK_ASSOCIATIVEARRAY_TYPE; + return D_TK_ASSOCIATIVEARRAY_TYPE; case Tstruct: - return TK_STRUCT_TYPE; + return D_TK_STRUCT_TYPE; case Tvector: - return TK_VECTOR_TYPE; + return D_TK_VECTOR_TYPE; case Tenum: - return TK_ENUMERAL_TYPE; + return D_TK_ENUMERAL_TYPE; case Tfunction: - return TK_FUNCTION_TYPE; + return D_TK_FUNCTION_TYPE; case Tdelegate: - return TK_DELEGATE_TYPE; + return D_TK_DELEGATE_TYPE; case Ttuple: - return TK_TYPELIST_TYPE; + return D_TK_TYPELIST_TYPE; case Tclass: if (((TypeClass *) type)->sym->isInterfaceDeclaration ()) - return TK_INTERFACE_TYPE; + return D_TK_INTERFACE_TYPE; else - return TK_CLASSINFO_TYPE; + return D_TK_CLASSINFO_TYPE; default: - return TK_TYPEINFO_TYPE; + return D_TK_TYPEINFO_TYPE; } } @@ -152,7 +129,7 @@ get_typeinfo_kind (Type *type) the `object.d' module. */ static void -make_internal_typeinfo (tinfo_kind tk, Identifier *ident, ...) +make_internal_typeinfo (d_tinfo_kind tk, Identifier *ident, ...) { va_list ap; @@ -167,10 +144,25 @@ make_internal_typeinfo (tinfo_kind tk, Identifier *ident, ...) tree field_type = va_arg (ap, tree); while (field_type != NULL_TREE) { - tree field = create_field_decl (field_type, NULL, 1, 1); - DECL_CHAIN (field) = fields; - fields = field; - field_type = va_arg (ap, tree); + tree next_field = NULL_TREE; + if (TREE_CODE (field_type) == TREE_LIST) + { + next_field = TREE_CHAIN (field_type); + field_type = TREE_VALUE (field_type); + } + + if (field_type != void_type_node) + { + tree field = create_field_decl (field_type, NULL, 1, 1); + DECL_CHAIN (field) = fields; + fields = field; + field_type = TREE_CHAIN (field_type); + } + + if (next_field == NULL_TREE) + field_type = va_arg (ap, tree); + else + field_type = next_field; } /* Create the TypeInfo type. */ @@ -214,15 +206,18 @@ create_tinfo_types (Module *mod) { /* Build the internal TypeInfo and ClassInfo types. See TypeInfoVisitor for documentation of field layout. */ - make_internal_typeinfo (TK_TYPEINFO_TYPE, Identifier::idPool ("TypeInfo"), + make_internal_typeinfo (D_TK_TYPEINFO_TYPE, Identifier::idPool ("TypeInfo"), + targetdm.d_vaarg_ti_types (D_TK_TYPEINFO_TYPE), NULL); - make_internal_typeinfo (TK_CLASSINFO_TYPE, + make_internal_typeinfo (D_TK_CLASSINFO_TYPE, Identifier::idPool ("TypeInfo_Class"), array_type_node, array_type_node, array_type_node, array_type_node, ptr_type_node, ptr_type_node, ptr_type_node, uint_type_node, ptr_type_node, - array_type_node, ptr_type_node, ptr_type_node, NULL); + array_type_node, ptr_type_node, ptr_type_node, + targetdm.d_vaarg_ti_types (D_TK_CLASSINFO_TYPE), + NULL); /* Create all frontend TypeInfo classes declarations. We rely on all existing, even if only just as stubs. */ @@ -342,7 +337,7 @@ class TypeInfoVisitor : public Visitor tree layout_interfaces (ClassDeclaration *cd) { - size_t offset = int_size_in_bytes (tinfo_types[TK_CLASSINFO_TYPE]); + size_t offset = int_size_in_bytes (tinfo_types[D_TK_CLASSINFO_TYPE]); tree csym = build_address (get_classinfo_decl (cd)); /* Put out the offset to where vtblInterfaces are written. */ @@ -421,7 +416,7 @@ class TypeInfoVisitor : public Visitor /* First entry is struct Interface reference. */ if (id->vtblOffset ()) { - size_t offset = int_size_in_bytes (tinfo_types[TK_CLASSINFO_TYPE]); + size_t offset = int_size_in_bytes (tinfo_types[D_TK_CLASSINFO_TYPE]); offset += (index * int_size_in_bytes (vtbl_interface_type_node)); tree value = build_offset (build_address (get_classinfo_decl (bcd)), size_int (offset)); @@ -570,6 +565,13 @@ class TypeInfoVisitor : public Visitor } else this->layout_field (null_array_node); + + tree target_field = targetdm.d_vaarg_ti_values (get_typeinfo_kind(ti), build_ctype (ti)); + while (target_field != NULL_TREE) + { + this->layout_field(TREE_VALUE(target_field)); + target_field = TREE_CHAIN (target_field); + } } /* Layout of TypeInfo_Pointer is: @@ -587,6 +589,13 @@ class TypeInfoVisitor : public Visitor /* TypeInfo for pointer-to type. */ this->layout_field (build_typeinfo (ti->next)); + + tree target_field = targetdm.d_vaarg_ti_values (get_typeinfo_kind(ti), build_ctype (ti)); + while (target_field != NULL_TREE) + { + this->layout_field(TREE_VALUE(target_field)); + target_field = TREE_CHAIN (target_field); + } } /* Layout of TypeInfo_Array is: @@ -604,6 +613,13 @@ class TypeInfoVisitor : public Visitor /* TypeInfo for array of type. */ this->layout_field (build_typeinfo (ti->next)); + + tree target_field = targetdm.d_vaarg_ti_values (get_typeinfo_kind(ti), build_ctype (ti)); + while (target_field != NULL_TREE) + { + this->layout_field(TREE_VALUE(target_field)); + target_field = TREE_CHAIN (target_field); + } } /* Layout of TypeInfo_StaticArray is: @@ -625,6 +641,14 @@ class TypeInfoVisitor : public Visitor /* Static array length. */ this->layout_field (size_int (ti->dim->toInteger ())); + + /* Add target specific TypeInfo fields */ + tree target_field = targetdm.d_vaarg_ti_values (get_typeinfo_kind(ti), build_ctype (ti)); + while (target_field != NULL_TREE) + { + this->layout_field(TREE_VALUE(target_field)); + target_field = TREE_CHAIN (target_field); + } } /* Layout of TypeInfo_AssociativeArray is: @@ -646,6 +670,13 @@ class TypeInfoVisitor : public Visitor /* TypeInfo for index of type. */ this->layout_field (build_typeinfo (ti->index)); + + tree target_field = targetdm.d_vaarg_ti_values (get_typeinfo_kind(ti), build_ctype (ti)); + while (target_field != NULL_TREE) + { + this->layout_field(TREE_VALUE(target_field)); + target_field = TREE_CHAIN (target_field); + } } /* Layout of TypeInfo_Vector is: @@ -663,6 +694,13 @@ class TypeInfoVisitor : public Visitor /* TypeInfo for equivalent static array. */ this->layout_field (build_typeinfo (ti->basetype)); + + tree target_field = targetdm.d_vaarg_ti_values (get_typeinfo_kind(ti), build_ctype (ti)); + while (target_field != NULL_TREE) + { + this->layout_field(TREE_VALUE(target_field)); + target_field = TREE_CHAIN (target_field); + } } /* Layout of TypeInfo_Function is: @@ -684,6 +722,13 @@ class TypeInfoVisitor : public Visitor /* Mangled name of function declaration. */ this->layout_string (d->tinfo->deco); + + tree target_field = targetdm.d_vaarg_ti_values (get_typeinfo_kind(ti), build_ctype (ti)); + while (target_field != NULL_TREE) + { + this->layout_field(TREE_VALUE(target_field)); + target_field = TREE_CHAIN (target_field); + } } /* Layout of TypeInfo_Delegate is: @@ -705,6 +750,13 @@ class TypeInfoVisitor : public Visitor /* Mangled name of delegate declaration. */ this->layout_string (d->tinfo->deco); + + tree target_field = targetdm.d_vaarg_ti_values (get_typeinfo_kind(ti), build_ctype (ti)); + while (target_field != NULL_TREE) + { + this->layout_field(TREE_VALUE(target_field)); + target_field = TREE_CHAIN (target_field); + } } /* Layout of ClassInfo/TypeInfo_Class is: @@ -735,6 +787,13 @@ class TypeInfoVisitor : public Visitor /* The vtable for ClassInfo. */ this->layout_base (Type::typeinfoclass); + tree target_field = targetdm.d_vaarg_ti_values (get_typeinfo_kind(ti), build_ctype (ti)); + while (target_field != NULL_TREE) + { + this->layout_field(TREE_VALUE(target_field)); + target_field = TREE_CHAIN (target_field); + } + if (!cd->members) return; @@ -930,6 +989,13 @@ class TypeInfoVisitor : public Visitor /* TypeInfo for class inheriting the interface. */ tree tidecl = get_typeinfo_decl (ti->sym->vclassinfo); this->layout_field (build_address (tidecl)); + + tree target_field = targetdm.d_vaarg_ti_values (get_typeinfo_kind(ti), build_ctype (ti)); + while (target_field != NULL_TREE) + { + this->layout_field(TREE_VALUE(target_field)); + target_field = TREE_CHAIN (target_field); + } } /* Layout of TypeInfo_Struct is: @@ -1038,6 +1104,13 @@ class TypeInfoVisitor : public Visitor this->layout_field (arg2type); } + tree target_field = targetdm.d_vaarg_ti_values (get_typeinfo_kind(ti), build_ctype (ti)); + while (target_field != NULL_TREE) + { + this->layout_field(TREE_VALUE(target_field)); + target_field = TREE_CHAIN (target_field); + } + /* immutable(void)* xgetRTInfo; */ if (sd->getRTInfo) this->layout_field (build_expr (sd->getRTInfo, true)); @@ -1080,6 +1153,13 @@ class TypeInfoVisitor : public Visitor tree ptr = build_address (decl); this->layout_field (d_array_value (array_type_node, length, ptr)); + tree target_field = /*targetdm.d_vaarg_ti_values (get_typeinfo_kind(ti), build_ctype (ti));*/ NULL_TREE; + while (target_field != NULL_TREE) + { + this->layout_field(TREE_VALUE(target_field)); + target_field = TREE_CHAIN (target_field); + } + d_pushdecl (decl); rest_of_decl_compilation (decl, 1, 0); } @@ -1118,7 +1198,7 @@ layout_classinfo (ClassDeclaration *cd) static tree layout_classinfo_interfaces (ClassDeclaration *decl) { - tree type = tinfo_types[TK_CLASSINFO_TYPE]; + tree type = tinfo_types[D_TK_CLASSINFO_TYPE]; size_t structsize = int_size_in_bytes (type); if (decl->vtblInterfaces->dim) @@ -1167,7 +1247,7 @@ layout_classinfo_interfaces (ClassDeclaration *decl) if (id->vtbl.dim && offset != ~0u) { - if (type == tinfo_types[TK_CLASSINFO_TYPE]) + if (type == tinfo_types[D_TK_CLASSINFO_TYPE]) type = copy_aggregate_type (type); tree vtbldomain = build_index_type (size_int (id->vtbl.dim - 1)); @@ -1181,7 +1261,7 @@ layout_classinfo_interfaces (ClassDeclaration *decl) } /* Update the type size and record mode for the classinfo type. */ - if (type != tinfo_types[TK_CLASSINFO_TYPE]) + if (type != tinfo_types[D_TK_CLASSINFO_TYPE]) finish_aggregate_type (structsize, TYPE_ALIGN_UNIT (type), type, NULL); return type; @@ -1349,13 +1429,15 @@ get_cpp_typeinfo_decl (ClassDeclaration *decl) if (decl->cpp_type_info_ptr_sym) return decl->cpp_type_info_ptr_sym; - if (!tinfo_types[TK_CPPTI_TYPE]) - make_internal_typeinfo (TK_CPPTI_TYPE, + if (!tinfo_types[D_TK_CPPTI_TYPE]) + make_internal_typeinfo (D_TK_CPPTI_TYPE, Identifier::idPool ("__cpp_type_info_ptr"), - ptr_type_node, NULL); + ptr_type_node, + targetdm.d_vaarg_ti_types (D_TK_CPPTI_TYPE), + NULL); tree ident = mangle_internal_decl (decl, "_cpp_type_info_ptr", ""); - tree type = tinfo_types[TK_CPPTI_TYPE]; + tree type = tinfo_types[D_TK_CPPTI_TYPE]; decl->cpp_type_info_ptr_sym = declare_extern_var (ident, type); DECL_LANG_SPECIFIC (decl->cpp_type_info_ptr_sym) = build_lang_decl (NULL); @@ -1384,55 +1466,57 @@ create_typeinfo (Type *type, Module *mod) if (!t->vtinfo) { - tinfo_kind tk = get_typeinfo_kind (t); + d_tinfo_kind tk = get_typeinfo_kind (t); + tree target_fields = targetdm.d_vaarg_ti_types (tk); + switch (tk) { - case TK_SHARED_TYPE: - case TK_CONST_TYPE: - case TK_IMMUTABLE_TYPE: - case TK_INOUT_TYPE: - case TK_POINTER_TYPE: - case TK_ARRAY_TYPE: - case TK_VECTOR_TYPE: - case TK_INTERFACE_TYPE: + case D_TK_SHARED_TYPE: + case D_TK_CONST_TYPE: + case D_TK_IMMUTABLE_TYPE: + case D_TK_INOUT_TYPE: + case D_TK_POINTER_TYPE: + case D_TK_ARRAY_TYPE: + case D_TK_VECTOR_TYPE: + case D_TK_INTERFACE_TYPE: /* Kinds of TypeInfo that add one extra pointer field. */ - if (tk == TK_SHARED_TYPE) + if (tk == D_TK_SHARED_TYPE) { /* Does both 'shared' and 'shared const'. */ t->vtinfo = TypeInfoSharedDeclaration::create (t); ident = Identifier::idPool ("TypeInfo_Shared"); } - else if (tk == TK_CONST_TYPE) + else if (tk == D_TK_CONST_TYPE) { t->vtinfo = TypeInfoConstDeclaration::create (t); ident = Identifier::idPool ("TypeInfo_Const"); } - else if (tk == TK_IMMUTABLE_TYPE) + else if (tk == D_TK_IMMUTABLE_TYPE) { t->vtinfo = TypeInfoInvariantDeclaration::create (t); ident = Identifier::idPool ("TypeInfo_Invariant"); } - else if (tk == TK_INOUT_TYPE) + else if (tk == D_TK_INOUT_TYPE) { t->vtinfo = TypeInfoWildDeclaration::create (t); ident = Identifier::idPool ("TypeInfo_Wild"); } - else if (tk == TK_POINTER_TYPE) + else if (tk == D_TK_POINTER_TYPE) { t->vtinfo = TypeInfoPointerDeclaration::create (t); ident = Identifier::idPool ("TypeInfo_Pointer"); } - else if (tk == TK_ARRAY_TYPE) + else if (tk == D_TK_ARRAY_TYPE) { t->vtinfo = TypeInfoArrayDeclaration::create (t); ident = Identifier::idPool ("TypeInfo_Array"); } - else if (tk == TK_VECTOR_TYPE) + else if (tk == D_TK_VECTOR_TYPE) { t->vtinfo = TypeInfoVectorDeclaration::create (t); ident = Identifier::idPool ("TypeInfo_Vector"); } - else if (tk == TK_INTERFACE_TYPE) + else if (tk == D_TK_INTERFACE_TYPE) { t->vtinfo = TypeInfoInterfaceDeclaration::create (t); ident = Identifier::idPool ("TypeInfo_Interface"); @@ -1441,30 +1525,31 @@ create_typeinfo (Type *type, Module *mod) gcc_unreachable (); if (!tinfo_types[tk]) - make_internal_typeinfo (tk, ident, ptr_type_node, NULL); + make_internal_typeinfo (tk, ident, ptr_type_node, + target_fields, NULL); break; - case TK_STATICARRAY_TYPE: + case D_TK_STATICARRAY_TYPE: if (!tinfo_types[tk]) { ident = Identifier::idPool ("TypeInfo_StaticArray"); make_internal_typeinfo (tk, ident, ptr_type_node, size_type_node, - NULL); + target_fields, NULL); } t->vtinfo = TypeInfoStaticArrayDeclaration::create (t); break; - case TK_ASSOCIATIVEARRAY_TYPE: + case D_TK_ASSOCIATIVEARRAY_TYPE: if (!tinfo_types[tk]) { ident = Identifier::idPool ("TypeInfo_AssociativeArray"); make_internal_typeinfo (tk, ident, ptr_type_node, ptr_type_node, - NULL); + target_fields, NULL); } t->vtinfo = TypeInfoAssociativeArrayDeclaration::create (t); break; - case TK_STRUCT_TYPE: + case D_TK_STRUCT_TYPE: if (!tinfo_types[tk]) { /* Some ABIs add extra TypeInfo fields on the end. */ @@ -1477,31 +1562,32 @@ create_typeinfo (Type *type, Module *mod) ptr_type_node, ptr_type_node, size_type_node, ptr_type_node, ptr_type_node, size_type_node, - ptr_type_node, argtype, argtype, NULL); + target_fields, ptr_type_node, NULL); } t->vtinfo = TypeInfoStructDeclaration::create (t); break; - case TK_ENUMERAL_TYPE: + case D_TK_ENUMERAL_TYPE: if (!tinfo_types[tk]) { ident = Identifier::idPool ("TypeInfo_Enum"); make_internal_typeinfo (tk, ident, ptr_type_node, array_type_node, - array_type_node, NULL); + array_type_node, + target_fields, NULL); } t->vtinfo = TypeInfoEnumDeclaration::create (t); break; - case TK_FUNCTION_TYPE: - case TK_DELEGATE_TYPE: + case D_TK_FUNCTION_TYPE: + case D_TK_DELEGATE_TYPE: /* Functions and delegates share a common TypeInfo layout. */ - if (tk == TK_FUNCTION_TYPE) + if (tk == D_TK_FUNCTION_TYPE) { t->vtinfo = TypeInfoFunctionDeclaration::create (t); ident = Identifier::idPool ("TypeInfo_Function"); } - else if (tk == TK_DELEGATE_TYPE) + else if (tk == D_TK_DELEGATE_TYPE) { t->vtinfo = TypeInfoDelegateDeclaration::create (t); ident = Identifier::idPool ("TypeInfo_Delegate"); @@ -1512,20 +1598,22 @@ create_typeinfo (Type *type, Module *mod) if (!tinfo_types[tk]) { make_internal_typeinfo (tk, ident, ptr_type_node, - array_type_node, NULL); + array_type_node, + target_fields, NULL); } break; - case TK_TYPELIST_TYPE: + case D_TK_TYPELIST_TYPE: if (!tinfo_types[tk]) { ident = Identifier::idPool ("TypeInfo_Tuple"); - make_internal_typeinfo (tk, ident, array_type_node, NULL); + make_internal_typeinfo (tk, ident, array_type_node, + target_fields, NULL); } t->vtinfo = TypeInfoTupleDeclaration::create (t); break; - case TK_CLASSINFO_TYPE: + case D_TK_CLASSINFO_TYPE: t->vtinfo = TypeInfoClassDeclaration::create (t); break; From ff5ecbd2e8d6cb90be1910d68bad59d37a81da3d Mon Sep 17 00:00:00 2001 From: Johannes Pfau Date: Sun, 4 Feb 2018 21:00:08 +0100 Subject: [PATCH 2/4] Implement va_arg for AArch64 --- libphobos/libdruntime/core/stdc/stdarg.d | 191 +++++++++++++++++- libphobos/libdruntime/object.d | 115 ++++++++++- .../libdruntime/rt/typeinfo/ti_cdouble.d | 8 + libphobos/libdruntime/rt/typeinfo/ti_cfloat.d | 8 + libphobos/libdruntime/rt/typeinfo/ti_creal.d | 8 + libphobos/libdruntime/rt/typeinfo/ti_double.d | 7 + libphobos/libdruntime/rt/typeinfo/ti_float.d | 7 + libphobos/libdruntime/rt/typeinfo/ti_real.d | 8 + 8 files changed, 349 insertions(+), 3 deletions(-) diff --git a/libphobos/libdruntime/core/stdc/stdarg.d b/libphobos/libdruntime/core/stdc/stdarg.d index 667e7be25..8e2b3f48c 100644 --- a/libphobos/libdruntime/core/stdc/stdarg.d +++ b/libphobos/libdruntime/core/stdc/stdarg.d @@ -200,6 +200,195 @@ version( GNU ) parmn[0..tsize] = p[0..tsize]; } } + else version( AArch64 ) + { + // Note: SoftFloat support is not in the ARM documents but apparently + // supported in some GCC versions. Not tested! + + private pure @property bool inGeneralRegister(const TypeInfo ti) + { + // Float types and HAs are always in SIMD register (or stack in SoftFloat) + // Integers, pointers and composite types up to 64 byte are + // passed in registers + if (ti.argTypes.isFloat) + return false; + else if (ti.argTypes.isHA) + return false; + else + return ti.tsize <= 64; + } + + private pure @property bool inFloatRegister(const TypeInfo ti) + { + version (D_SoftFloat) + return false; + else + return ti.argTypes.isFloat; + } + + private pure @property bool isHA(const TypeInfo ti) + { + version (D_SoftFloat) + return false; + else + return ti.argTypes.isHA; + } + + private pure @property bool passByReference(const TypeInfo ti) + { + // See aarch64_pass_by_reference + if (ti.inFloatRegister || ti.isHA) + return false; + else + return ti.tsize > 16; + } + + /// Layout of this struct must match __builtin_va_list for C ABI compatibility + struct __va_list + { + void* __stack; + void* __gr_top; + void* __vr_top; + int __gr_offs; + int __vr_offs; + } + + /// + // This code is based on the pseudo code in 'Procedure call standard + // for the ARM 64-bit architecture' release 1.0, 22.05.13 + void va_arg()(ref va_list apx, TypeInfo ti, void* parmn) + { + import core.stdc.stdint : intptr_t; + + __va_list* ap = cast(__va_list*)&apx; + auto uparmn = cast(ubyte*) parmn; + + void assignValue(void* pFrom) + { + auto len = ti.tsize; + uparmn[0 .. len] = (cast(ubyte*) pFrom)[0 .. len]; + } + + void readFromStack() + { + intptr_t arg = cast(intptr_t) ap.__stack; + // round up + if (ti.talign > 8) + arg = (arg + 15) & -16; + + ap.__stack = cast(void*)((arg + ti.tsize + 7) & -8); + version (BigEndian) + { + if (!ti.argTypes.isAggregate && ti.tsize < 8) + arg += 8 - ti.tsize; + } + return assignValue(cast(void*) arg); + } + + void readReferenceFromStack() + { + intptr_t arg = cast(intptr_t) ap.__stack; + + ap.__stack = cast(void*)((arg + 8 + 7) & -8); + auto len = ti.tsize; + uparmn[0 .. len] = (*cast(ubyte**)(arg))[0 .. len]; + } + + // Note: This code is missing in the AArch64 PCS pseudo code + if (ti.passByReference) + { + // Passed as a reference => always a 8 byte pointer + auto offs = ap.__gr_offs; + // reg save area empty + if (offs >= 0) + return readReferenceFromStack(); + + ap.__gr_offs = offs + 8; + // overflowed reg save area + if (ap.__gr_offs > 0) + return readReferenceFromStack(); + + auto len = ti.tsize; + uparmn[0 .. len] = (*cast(ubyte**)(ap.__gr_top + offs))[0 .. len]; + } + else if (ti.inGeneralRegister) + { + auto offs = ap.__gr_offs; + // reg save area empty + if (offs >= 0) + return readFromStack(); + // round up + if (ti.talign > 8) + offs = (offs + 15) & -16; + + auto nreg = cast(int)((ti.tsize + 7) / 8); + ap.__gr_offs = offs + (nreg * 8); + // overflowed reg save area + if (ap.__gr_offs > 0) + return readFromStack(); + + version (BigEndian) + { + if (!ti.argTypes.isAggregate && ti.tsize < 8) + offs += 8 - ti.tsize; + } + return assignValue(ap.__gr_top + offs); + } + else if (ti.isHA) + { + auto offs = ap.__vr_offs; + // reg save area empty + if (offs >= 0) + return readFromStack(); + + auto nreg = ti.argTypes.haFieldNum; + ap.__vr_offs = offs + (nreg * 16); + // overflowed reg save area + if (ap.__vr_offs > 0) + return readFromStack(); + + version (BigEndian) + { + if (ti.argTypes.haFieldSize < 16) + offs += 16 - ti.argTypes.haFieldSize; + } + + size_t pos = 0; + for (size_t i = 0; i < nreg; i++) + { + uparmn[pos .. pos + ti.argTypes.haFieldSize] = ( + cast(ubyte*)(ap.__vr_top + offs))[0 .. ti.argTypes.haFieldSize]; + + pos += ti.argTypes.haFieldSize; + offs += 16; + } + } + else if (ti.inFloatRegister) + { + auto offs = ap.__vr_offs; + // reg save area empty + if (offs >= 0) + return readFromStack(); + + auto nreg = cast(int)((ti.tsize + 15) / 16); + ap.__vr_offs = offs + (nreg * 16); + // overflowed reg save area + if (ap.__vr_offs > 0) + return readFromStack(); + + version (BigEndian) + { + if (!ti.argTypes.isAggregate && ti.tsize < 16) + offs += 16 - ti.tsize; + } + return assignValue(ap.__vr_top + offs); + } + else + { + return readFromStack(); + } + } + } else { /// @@ -209,13 +398,11 @@ version( GNU ) } } - /*********************** * End use of ap. */ alias __builtin_va_end va_end; - /*********************** * Make a copy of ap. */ diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d index 3156a373c..0981de0d3 100644 --- a/libphobos/libdruntime/object.d +++ b/libphobos/libdruntime/object.d @@ -195,6 +195,70 @@ struct OffsetTypeInfo TypeInfo ti; /// TypeInfo for this member } +version (AArch64) +{ + enum AArch64ArgFlag : ubyte + { + none = 0, + isAggregate = 0x1, + isHA = 0x2 + } + + /** + * AArch64 ABI specific extended type information. + * This is required to implement the va_arg function. + */ + struct AArch64ArgInfo + { + private AArch64ArgFlag arg_flags; + /** + * Type is passed in one floating point register. + * True for half float, float, double, quad and short vector + * types. + */ + public bool isFloat; + + /** + * For HFA or HVA: size of the field base type. + */ + public ubyte haFieldSize; + + /** + * For HFA or HVA: size of the field base type. + */ + public ubyte haFieldNum; + + public this(bool isFloat) @safe pure nothrow @nogc + { + this.isFloat = isFloat; + } + + public this(AArch64ArgFlag arg_flags, bool isFloat, ubyte haFieldSize, ubyte haFieldNum) @safe pure nothrow @nogc + { + this.arg_flags = arg_flags; + this.isFloat = isFloat; + this.haFieldSize = haFieldSize; + this.haFieldNum = haFieldNum; + } + + /** + * Type is an aggregate according to AArch64 ABI requirements. + */ + @property bool isAggregate() @safe pure nothrow @nogc const + { + return (arg_flags & AArch64ArgFlag.isAggregate) != 0; + } + + /** + * Type is a HFA or HVA type. + */ + @property bool isHA() @safe pure nothrow @nogc const + { + return (arg_flags & AArch64ArgFlag.isHA) != 0; + } + } +} + /** * Runtime type information about a type. * Can be retrieved for any type using a @@ -308,6 +372,12 @@ class TypeInfo arg1 = this; return 0; } + /** Return internal info on arguments for AArch64 ABI. + */ + version (AArch64) AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + return AArch64ArgInfo(false); + } /** Return info used by the garbage collector to do precise collection. */ @@ -347,6 +417,10 @@ class TypeInfo_Enum : TypeInfo { return base.argTypes(arg1, arg2); } + version (AArch64) override AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + return base.argTypes(); + } override @property immutable(void)* rtInfo() const { return base.rtInfo; } @@ -621,6 +695,12 @@ class TypeInfo_StaticArray : TypeInfo TypeInfo value; size_t len; + version (AArch64) + { + AArch64ArgFlag arg_flags; + ubyte haFieldSize; + ubyte haFieldNum; + } override @property size_t talign() nothrow pure const { @@ -632,6 +712,11 @@ class TypeInfo_StaticArray : TypeInfo arg1 = typeid(void*); return 0; } + + version (AArch64) override AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + return AArch64ArgInfo(arg_flags, false, haFieldSize, haFieldNum); + } } class TypeInfo_AssociativeArray : TypeInfo @@ -722,8 +807,18 @@ class TypeInfo_Vector : TypeInfo { return base.argTypes(arg1, arg2); } - TypeInfo base; + + version (AArch64) + { + override AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + return AArch64ArgInfo(arg_flags, false, haFieldSize, haFieldNum); + } + AArch64ArgFlag arg_flags; + ubyte haFieldSize; + ubyte haFieldNum; + } } class TypeInfo_Function : TypeInfo @@ -1259,6 +1354,16 @@ class TypeInfo_Struct : TypeInfo TypeInfo m_arg1; TypeInfo m_arg2; } + version (AArch64) + { + override AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + return AArch64ArgInfo(arg_flags, false, haFieldSize, haFieldNum); + } + AArch64ArgFlag arg_flags; + ubyte haFieldSize; + ubyte haFieldNum; + } immutable(void)* m_RTInfo; // data for precise GC } @@ -1359,6 +1464,10 @@ class TypeInfo_Tuple : TypeInfo { assert(0); } + version (AArch64) override AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + assert(0); + } } class TypeInfo_Const : TypeInfo @@ -1401,6 +1510,10 @@ class TypeInfo_Const : TypeInfo { return base.argTypes(arg1, arg2); } + version (AArch64) override AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + return base.argTypes(); + } TypeInfo base; } diff --git a/libphobos/libdruntime/rt/typeinfo/ti_cdouble.d b/libphobos/libdruntime/rt/typeinfo/ti_cdouble.d index 15bec3525..639ed8708 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_cdouble.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_cdouble.d @@ -71,4 +71,12 @@ class TypeInfo_r : TypeInfo arg2 = typeid(double); return 0; } + version (AArch64) + { + override AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + return AArch64ArgInfo(cast(AArch64ArgFlag)(AArch64ArgFlag.isAggregate + | AArch64ArgFlag.isHA), false, 8, 2); + } + } } diff --git a/libphobos/libdruntime/rt/typeinfo/ti_cfloat.d b/libphobos/libdruntime/rt/typeinfo/ti_cfloat.d index 3b82f04d0..3e156462d 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_cfloat.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_cfloat.d @@ -70,4 +70,12 @@ class TypeInfo_q : TypeInfo arg1 = typeid(double); return 0; } + version (AArch64) + { + override AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + return AArch64ArgInfo(cast(AArch64ArgFlag)(AArch64ArgFlag.isAggregate + | AArch64ArgFlag.isHA), false, 4, 2); + } + } } diff --git a/libphobos/libdruntime/rt/typeinfo/ti_creal.d b/libphobos/libdruntime/rt/typeinfo/ti_creal.d index b77429cf8..0c3727d7d 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_creal.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_creal.d @@ -71,4 +71,12 @@ class TypeInfo_c : TypeInfo arg2 = typeid(real); return 0; } + version (AArch64) + { + override AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + return AArch64ArgInfo(cast(AArch64ArgFlag)(AArch64ArgFlag.isAggregate + | AArch64ArgFlag.isHA), false, 16, 2); + } + } } diff --git a/libphobos/libdruntime/rt/typeinfo/ti_double.d b/libphobos/libdruntime/rt/typeinfo/ti_double.d index 8aa281f11..aae0b783f 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_double.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_double.d @@ -73,4 +73,11 @@ class TypeInfo_d : TypeInfo // 2 means arg to function is passed in XMM registers override @property uint flags() const { return 2; } } + version (AArch64) + { + override AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + return AArch64ArgInfo(true); + } + } } diff --git a/libphobos/libdruntime/rt/typeinfo/ti_float.d b/libphobos/libdruntime/rt/typeinfo/ti_float.d index 60837d1c9..137894d3e 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_float.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_float.d @@ -68,4 +68,11 @@ class TypeInfo_f : TypeInfo // 2 means arg to function is passed in XMM registers override @property uint flags() const { return 2; } } + version (AArch64) + { + override AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + return AArch64ArgInfo(true); + } + } } diff --git a/libphobos/libdruntime/rt/typeinfo/ti_real.d b/libphobos/libdruntime/rt/typeinfo/ti_real.d index 77ae12651..e223fa85b 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_real.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_real.d @@ -64,4 +64,12 @@ class TypeInfo_e : TypeInfo { return F.alignof; } + + version (AArch64) + { + override AArch64ArgInfo argTypes() @safe @nogc nothrow pure const + { + return AArch64ArgInfo(true); + } + } } From f4dc9d4058eafc180abf754667e3bafe0a777d56 Mon Sep 17 00:00:00 2001 From: Johannes Pfau Date: Sun, 4 Feb 2018 21:00:22 +0100 Subject: [PATCH 3/4] FIXME: Fix this in GDC --- libphobos/libdruntime/object.d | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d index 0981de0d3..e714888f8 100644 --- a/libphobos/libdruntime/object.d +++ b/libphobos/libdruntime/object.d @@ -1294,7 +1294,7 @@ class TypeInfo_Struct : TypeInfo return m_init; } - override @property uint flags() nothrow pure const { return m_flags; } + override @property uint flags() nothrow pure const { return cast(uint)m_flags; } override @property size_t talign() nothrow pure const { return m_align; } @@ -1325,7 +1325,7 @@ class TypeInfo_Struct : TypeInfo int function(in void*, in void*) xopCmp; string function(in void*) xtoString; - enum StructFlags : uint + enum StructFlags : size_t { hasPointers = 0x1, isDynamicType = 0x2, // built at runtime, needs type info in xdtor @@ -1339,7 +1339,7 @@ class TypeInfo_Struct : TypeInfo } void function(void*) xpostblit; - uint m_align; + size_t m_align; override @property immutable(void)* rtInfo() const { return m_RTInfo; } From b7e5023387b66e24b381712d61a13fc69d5abd68 Mon Sep 17 00:00:00 2001 From: Johannes Pfau Date: Sun, 4 Feb 2018 21:01:08 +0100 Subject: [PATCH 4/4] stdarg: Add unittests for va_arg --- libphobos/libdruntime/core/internal/traits.d | 20 + libphobos/libdruntime/core/stdc/stdarg.d | 590 +++++++++++++++++++ 2 files changed, 610 insertions(+) diff --git a/libphobos/libdruntime/core/internal/traits.d b/libphobos/libdruntime/core/internal/traits.d index e15dff520..7f790367c 100644 --- a/libphobos/libdruntime/core/internal/traits.d +++ b/libphobos/libdruntime/core/internal/traits.d @@ -210,3 +210,23 @@ template Filter(alias pred, TList...) Filter!(pred, TList[$/2 .. $ ])); } } + +// std.meta.staticMap +template staticMap(alias F, T...) +{ + static if (T.length == 0) + { + alias staticMap = TypeTuple!(); + } + else static if (T.length == 1) + { + alias staticMap = TypeTuple!(F!(T[0])); + } + else + { + alias staticMap = + TypeTuple!( + staticMap!(F, T[ 0 .. $/2]), + staticMap!(F, T[$/2 .. $ ])); + } +} diff --git a/libphobos/libdruntime/core/stdc/stdarg.d b/libphobos/libdruntime/core/stdc/stdarg.d index 8e2b3f48c..0d38e5e14 100644 --- a/libphobos/libdruntime/core/stdc/stdarg.d +++ b/libphobos/libdruntime/core/stdc/stdarg.d @@ -872,3 +872,593 @@ else { static assert(false, "Unsupported platform"); } + +private version (unittest) +{ + import core.stdc.stdarg, core.simd; + import core.internal.traits : Unqual, staticMap; + + template isVectorType(T) + { + enum isVectorType = false; + } + + template isVectorType(T : __vector(T[N]), size_t N) + { + enum isVectorType = true; + } + + template vectorSize(T : __vector(T[N]), size_t N) + { + enum vectorSize = N; + } + + bool compare(T)(T t1, T t2) + { + static if (isVectorType!T) + { + for (size_t i = 0; i < vectorSize!T; i++) + { + if (t1[i] != t2[i]) + return false; + } + return true; + } + else + { + return t1 == t2; + } + } + + enum TestMode + { + C, + D, + both + } + + template testTypeInfoVaArg(TestMode mode, CTArgs...) + { + staticMap!(Unqual, CTArgs) ctArgs; + + /* + * This function implements the C-style variadic function. + * The first arg in CTArgs is used as named parameter, the + * remaining args are used as variadic parameters. + */ + extern (C) void testTypeInfoC(CTArgs[0] p1, ...) + { + // Make sure our buffer is properly aligned + union Buffer + { + ubyte[1024] buf; + double4 vect; + } + + Buffer buffu; + + // Check first parameter, just to be sure + assert(compare(p1, ctArgs[0])); + + // Setup va_list + va_list ap; + va_start(ap, p1); + + // Compare all variadic arguments + foreach (i, CTArg; CTArgs[1 .. $]) + { + // Get TypeInfo for static type + auto ti = typeid(CTArg); + + va_arg(ap, ti, buffu.buf.ptr); + assert(compare(ctArgs[i + 1], *(cast(CTArg*) buffu.buf.ptr))); + } + va_end(ap); + } + + /* + * This function implements the C-style, extern(D) variadic function. + * The first arg in CTArgs is used as named parameter, the + * remaining args are used as variadic parameters. + */ + void testTypeInfoD(...) + { + // Make sure our buffer is properly aligned + union Buffer + { + ubyte[1024] buf; + double4 vect; + } + + Buffer buffu; + + // Compare all variadic arguments + foreach (i, CTArg; CTArgs[1 .. $]) + { + // Get TypeInfo + auto ti = _arguments[i]; + + va_arg(_argptr, ti, buffu.buf.ptr); + assert(compare(ctArgs[i + 1], *(cast(CTArg*) buffu.buf.ptr))); + } + } + + void testTypeInfoVaArg(CTArgs lctArgs) + { + ctArgs = lctArgs; + static if (mode == TestMode.C || mode == TestMode.both) + testTypeInfoC(lctArgs[0], lctArgs[1 .. $]); + static if (mode == TestMode.D || mode == TestMode.both) + testTypeInfoD(lctArgs[1 .. $]); + } + } + + // An AArch64 HFA, passed in SIMD registers + struct HFA1 + { + double a, b; + } + + // An AArch64 HFA, passed in SIMD registers + struct HFA2 + { + cdouble a; + double b; + } + + // An AArch64 HFA, passed in SIMD registers + struct HFA3 + { + HFA1 a; + double b; + } + + // An AArch64 HFA, passed in SIMD registers + struct HFA4 + { + union + { + double a1; + double a2; + } + + double b, c, d; + + this(double a, double b, double c, double d) + { + this.a1 = a; + this.b = b; + this.c = c; + this.d = d; + } + } + + version (AArch64) + { + struct HVA1 + { + float4 a, b; + bool opEquals()(auto ref const HVA1 rhs) const + { + return compare(a, rhs.a) && compare(b, rhs.b); + } + } + + struct HVA2 + { + float4 a; + double2 b; + + bool opEquals()(auto ref const HVA2 rhs) const + { + return compare(a, rhs.a) && compare(b, rhs.b); + } + } + + struct HVA3 + { + double4 a; + double4 b; + + bool opEquals()(auto ref const HVA3 rhs) const + { + return compare(a, rhs.a) && compare(b, rhs.b); + } + } + } + + struct Normal1 + { + double a; + float b; + } + + struct Normal2 + { + double a; + int b; + } + + struct Normal3 + { + union + { + double a1; + long a2; + } + + double b, c, d; + + this(double a, double b, double c, double d) + { + this.a1 = a; + this.b = b; + this.c = c; + this.d = d; + } + } + + // Not a HFA: Too many members + struct Normal4 + { + double a, b, c, d, e; + } + + // Not a HVA: Too many members + version (AArch64) struct Normal5 + { + double2 a, b, c, d, e; + bool opEquals()(auto ref const Normal5 rhs) const + { + return compare(a, rhs.a) && compare(b, rhs.b) && compare(c, rhs.c) + && compare(d, rhs.d) && compare(e, rhs.e); + } + } + + // An AArch64 composite, passed in general purpose registers + struct Reg1 + { + int a, b; + } + + // An AArch64 composite, passed in general purpose registers + struct Reg2 + { + double a; + float b; + } + + // An AArch64 composite, passed in general purpose registers + struct Reg3 + { + ubyte[16] a; + static opCall() + { + Reg3 result; + for (size_t i = 0; i < 16; i++) + result.a[i] = cast(ubyte) i; + + return result; + } + } + + // An AArch64 composite, passed in general purpose registers by reference + // for Num > 16 + struct Mem1(size_t Num) + { + ubyte[Num] a; + + static opCall() + { + Mem1!Num result; + for (size_t i = 0; i < Num; i++) + result.a[i] = cast(ubyte) i; + + return result; + } + } + + class Class1 + { + void foo() + { + } + + int a; + double b; + } +} + +/* + * Test TypeInfo va_arg function for basic types, both extern(C) and D funcs. + */ +unittest +{ + void* ptr = cast(void*) 0xABCDEF; + bool boolI = false; + byte byteI = 1; + ubyte ubyteI = 1; + short shortI = 2; + ushort ushortI = 2; + int intI = 3; + uint uintI = 3; + long longI = 4; + ulong ulongI = 4; + char charI = 'c'; + wchar wcharI = 'w'; + dchar dcharI = 'd'; + float floatI = 21; + double doubleI = 42; + real realI = 11; + ifloat ifloatI = 11i; + idouble idoubleI = 12i; + ireal irealI = 13i; + cfloat cfloatI = 1 + 1i; + cdouble cdoubleI = 2 + 2i; + creal crealI = 3 + 3i; + immutable(creal) crealII = 3 + 3i; + + testTypeInfoVaArg!(TestMode.both)(ptr, ptr); + testTypeInfoVaArg!(TestMode.both)(ptr, boolI); + testTypeInfoVaArg!(TestMode.both)(ptr, byteI); + testTypeInfoVaArg!(TestMode.both)(ptr, ubyteI); + testTypeInfoVaArg!(TestMode.both)(ptr, shortI); + testTypeInfoVaArg!(TestMode.both)(ptr, ushortI); + testTypeInfoVaArg!(TestMode.both)(ptr, intI); + testTypeInfoVaArg!(TestMode.both)(ptr, uintI); + testTypeInfoVaArg!(TestMode.both)(ptr, longI); + testTypeInfoVaArg!(TestMode.both)(ptr, ulongI); + testTypeInfoVaArg!(TestMode.both)(ptr, charI); + testTypeInfoVaArg!(TestMode.both)(ptr, wcharI); + testTypeInfoVaArg!(TestMode.both)(ptr, dcharI); + testTypeInfoVaArg!(TestMode.both)(ptr, ptr); + testTypeInfoVaArg!(TestMode.both)(ptr, doubleI); + testTypeInfoVaArg!(TestMode.both)(ptr, realI); + testTypeInfoVaArg!(TestMode.both)(ptr, idoubleI); + testTypeInfoVaArg!(TestMode.both)(ptr, irealI); + testTypeInfoVaArg!(TestMode.both)(ptr, cfloatI); + testTypeInfoVaArg!(TestMode.both)(ptr, cdoubleI); + testTypeInfoVaArg!(TestMode.both)(ptr, crealI); + testTypeInfoVaArg!(TestMode.both)(ptr, crealII); +} + +version (AArch64) +{ + /* + * Special cases for AArch64 float types: + * When float is passed to an extern(C) varags func, it is promoted to double. + * When pased to extern(D) varargs func, it does not promote. + */ + unittest + { + void* ptr = cast(void*) 0xABCDEF; + float floatI = 1; + ifloat ifloatI = 2i; + testTypeInfoVaArg!(TestMode.D)(ptr, floatI); + testTypeInfoVaArg!(TestMode.D)(ptr, ifloatI); + + static extern (C) void testFloat(void* p1, ...) + { + va_list ap; + va_start(ap, p1); + double val; + va_arg(ap, typeid(double), &val); + assert(val == 1); + va_end(ap); + } + + static extern (C) testIFloat(void* p1, ...) + { + va_list ap; + va_start(ap, p1); + idouble val; + va_arg(ap, typeid(idouble), &val); + assert(val == 2i); + va_end(ap); + } + + static extern (C) testIFloatI(void* p1, ...) + { + va_list ap; + va_start(ap, p1); + idouble val; + va_arg(ap, typeid(immutable(idouble)), &val); + assert(val == 3i); + va_end(ap); + } + + float f = 1; + testFloat(ptr, f); + ifloat fi = 2i; + testIFloat(ptr, fi); + immutable(ifloat) ifi = 3i; + testIFloatI(ptr, ifi); + } + + /* + * Test TypeInfo va_arg function for vector types, both extern(C) and D funcs. + */ + unittest + { + void* ptr = cast(void*) 0xABCDEF; + float2 float2I = [1.0, 2.0]; + float4 float4I = [3.0, 4.0, 5.0, 6.0]; + double2 double2I = [7.0, 8.0]; + // Note: AArch64 short vector must be 8 or 16 in size, so this + // is not a short vector! + double4 double4I = [9.0, 10.0, 11.0, 12.0]; + ubyte16 ubyte16I = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + uint4 uint4I = [1, 2, 3, 4]; + + testTypeInfoVaArg!(TestMode.both)(ptr, float2I); + testTypeInfoVaArg!(TestMode.both)(ptr, float4I); + testTypeInfoVaArg!(TestMode.both)(ptr, double2I); + testTypeInfoVaArg!(TestMode.both)(ptr, double4I); + testTypeInfoVaArg!(TestMode.both)(ptr, uint4I); + testTypeInfoVaArg!(TestMode.both)(ptr, ubyte16I); + } + + /* + * Homogeneous vector arrays are special types on AArch64. + */ + unittest + { + void* ptr = cast(void*) 0xABCDEF; + auto hva1 = HVA1([1.0, 2.0, 3.0, 4.0], [1.0, 2.0, 3.0, 4.0]); + auto hva2 = HVA2([1.0, 2.0, 3.0, 4.0], [1.0, 2.0]); + auto hva3 = HVA3([1.0, 2.0, 3.0, 4.0], [1.0, 2.0, 3.0, 4.0]); + immutable(HVA3) hva3I = HVA3([1.0, 2.0, 3.0, 4.0], [1.0, 2.0, 3.0, 4.0]); + auto normal5 = Normal5([9.0, 9.0], [10.0, 10.0], [11.0, 11.0], [12.0, 12.0], [13.0, 13.0]); + immutable(Normal5) normal5I = Normal5([9.0, 9.0], [10.0, 10.0], [11.0, + 11.0], [12.0, 12.0], [13.0, 13.0]); + + testTypeInfoVaArg!(TestMode.both)(ptr, hva1); + testTypeInfoVaArg!(TestMode.both)(ptr, hva2); + testTypeInfoVaArg!(TestMode.both)(ptr, hva3); + testTypeInfoVaArg!(TestMode.both)(ptr, hva3I); + testTypeInfoVaArg!(TestMode.both)(ptr, normal5); + testTypeInfoVaArg!(TestMode.both)(ptr, normal5I); + } +} + +/* + * Homogeneous float arrays are special types on AArch64. + */ +unittest +{ + void* ptr = cast(void*) 0xABCDEF; + auto hfa1 = HFA1(1.0, 2.0); + auto hfa2 = HFA2(1.0 + 1.0i, 2.0); + auto hfa3 = HFA3(HFA1(1.0, 2.0), 3.0); + auto hfa4 = HFA4(1.0, 2.0, 3.0, 4.0); + immutable(HFA4) hfa4I = HFA4(1.0, 2.0, 3.0, 4.0); + + testTypeInfoVaArg!(TestMode.both)(ptr, hfa1); + testTypeInfoVaArg!(TestMode.both)(ptr, hfa2); + testTypeInfoVaArg!(TestMode.both)(ptr, hfa3); + testTypeInfoVaArg!(TestMode.both)(ptr, hfa4); +} + +/* + * Composite types. + */ +unittest +{ + void* ptr = cast(void*) 0xABCDEF; + auto normal1 = Normal1(1.0, 2.0); + auto normal2 = Normal2(3.0, 4); + auto normal3 = Normal3(5.0, 6.0, 7.0, 8.0); + auto normal4 = Normal4(9.0, 10.0, 11.0, 12.0, 13.0); + auto reg1 = Reg1(1, 2); + auto reg2 = Reg2(1, 2); + auto reg3 = Reg3(); + auto mem1 = Mem1!512(); + // 16 byte is larged type passed in registers on Aarch64 + auto mem2 = Mem1!17(); + immutable(Mem1!32) mem3 = Mem1!32(); + + auto class1 = new Class1(); + uint[] darray1 = [1, 2, 3]; + immutable(uint[]) darray1I = [1, 2, 3]; + auto del1 = &class1.foo; + int[string] aa1 = ["tst" : 1]; + + testTypeInfoVaArg!(TestMode.both)(ptr, normal1); + testTypeInfoVaArg!(TestMode.both)(ptr, normal2); + testTypeInfoVaArg!(TestMode.both)(ptr, normal3); + testTypeInfoVaArg!(TestMode.both)(ptr, normal4); + testTypeInfoVaArg!(TestMode.both)(ptr, reg1); + testTypeInfoVaArg!(TestMode.both)(ptr, reg2); + testTypeInfoVaArg!(TestMode.both)(ptr, reg3); + testTypeInfoVaArg!(TestMode.both)(ptr, mem1); + testTypeInfoVaArg!(TestMode.both)(ptr, mem2); + testTypeInfoVaArg!(TestMode.both)(ptr, mem3); + + testTypeInfoVaArg!(TestMode.both)(ptr, class1); + testTypeInfoVaArg!(TestMode.both)(ptr, del1); + testTypeInfoVaArg!(TestMode.both)(ptr, aa1); + + testTypeInfoVaArg!(TestMode.D)(ptr, darray1); + testTypeInfoVaArg!(TestMode.D)(ptr, darray1I); +} + +version (unittest) +{ + void testStaticArray(T, T[] value)(void* ptr, ...) + { + // Make sure our buffer is properly aligned + union Buffer + { + ubyte[1024] buf; + T[] arr; + } + + Buffer buffu; + + auto ti = _arguments[0]; + va_arg(_argptr, ti, buffu.buf.ptr); + assert(buffu.arr == value); + } +} + +/* + * Arrays can only be passed to D-style varargs functions and + * static arrays get converted into dynamic arrays. + */ +unittest +{ + void* ptr = cast(void*) 0xABCDEF; + uint[4] sarray1 = [1, 2, 3, 4]; + immutable(uint[4]) sarray1I = [1, 2, 3, 4]; + double[2] sarray2 = [1, 2]; + immutable(double[2]) sarray2I = [1, 2]; + float[3] sarray3 = [1, 2, 3]; + real[4] sarray4 = [1, 2, 3, 4]; + + testStaticArray!(uint, [1, 2, 3, 4])(ptr, sarray1); + testStaticArray!(uint, [1, 2, 3, 4])(ptr, sarray1I); + testStaticArray!(double, [1, 2])(ptr, sarray2); + testStaticArray!(double, [1, 2])(ptr, sarray2I); + testStaticArray!(float, [1, 2, 3])(ptr, sarray3); + testStaticArray!(real, [1, 2, 3, 4])(ptr, sarray4); +} + +/* + * This tests special cases for AArch64 + */ +unittest +{ + static align(16) struct S2 + { + int a; + int b; + int c; + int d; + } + + void* ptr = cast(void*) 0xABCDEF; + auto hfa1 = HFA1(1.0, 2.0); + double doubleI = 3.0; + auto reg1 = Reg1(10, 20); + auto s2 = S2(30, 40, 50, 60); + + // Fill all SIMD registers, so rest is passed on stack + testTypeInfoVaArg!(TestMode.both)(ptr, hfa1, hfa1, hfa1, hfa1, hfa1); + testTypeInfoVaArg!(TestMode.both)(ptr, hfa1, hfa1, hfa1, hfa1, doubleI); + testTypeInfoVaArg!(TestMode.both)(ptr, doubleI, doubleI, hfa1, doubleI, hfa1, hfa1); + // Passing a simple double, then hfas requiring two registers, then single double + testTypeInfoVaArg!(TestMode.both)(ptr, doubleI, hfa1, hfa1, hfa1, hfa1, doubleI); + + // Fill all general purpose registers, so rest is passed on stack + testTypeInfoVaArg!(TestMode.both)(ptr, reg1, reg1, reg1, reg1, reg1, reg1, + reg1, reg1, reg1, reg1); + testTypeInfoVaArg!(TestMode.both)(ptr, s2, s2, reg1, reg1, reg1, reg1, reg1, reg1, reg1); + + // Mixing register types + testTypeInfoVaArg!(TestMode.both)(ptr, reg1, s2, reg1, s2, reg1, reg1, + reg1, reg1, reg1, reg1, doubleI, doubleI, hfa1, doubleI, hfa1, hfa1); + testTypeInfoVaArg!(TestMode.both)(ptr, doubleI, doubleI, hfa1, doubleI, + hfa1, hfa1, reg1, reg1, reg1, reg1, reg1, s2, reg1, s2, reg1, reg1); + testTypeInfoVaArg!(TestMode.both)(ptr, s2, doubleI, reg1, hfa1, doubleI, + reg1, hfa1, reg1, hfa1, reg1, hfa1, reg1, s2, reg1, s2, reg1, doubleI); +}