1 /** 2 * Break down a D type into basic (register) types for the AArch64 ABI. 3 * 4 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved 5 * Authors: Martin Kinkelin 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_aarch64.d, _argtypes_aarch64.d) 8 * Documentation: https://dlang.org/phobos/dmd_argtypes_aarch64.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/argtypes_aarch64.d 10 */ 11 12 module dmd.argtypes_aarch64; 13 14 import dmd.mtype; 15 16 /**************************************************** 17 * This breaks a type down into 'simpler' types that can be passed to a function 18 * in registers, and returned in registers. 19 * This is the implementation for the AAPCS64 ABI, based on 20 * https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst. 21 * Params: 22 * t = type to break down 23 * Returns: 24 * tuple of 1 type if `t` can be passed in registers; e.g., a static array 25 * for Homogeneous Floating-point/Vector Aggregates (HFVA). 26 * A tuple of zero length means the type cannot be passed/returned in registers. 27 * null indicates a `void`. 28 */ 29 extern (C++) TypeTuple toArgTypes_aarch64(Type t) 30 { 31 if (t == Type.terror) 32 return new TypeTuple(t); 33 34 const size = cast(size_t) t.size(); 35 if (size == 0) 36 return null; 37 38 Type tb = t.toBasetype(); 39 const isAggregate = tb.ty == Tstruct || tb.ty == Tsarray || tb.ty == Tarray || tb.ty == Tdelegate || tb.iscomplex(); 40 if (!isAggregate) 41 return new TypeTuple(t); 42 43 Type hfvaType; 44 enum maxNumHFVAElements = 4; 45 const isHFVA = size > maxNumHFVAElements * 16 ? false : isHFVA(tb, maxNumHFVAElements, &hfvaType); 46 47 // non-PODs and larger non-HFVA PODs are passed indirectly by value (pointer to caller-allocated copy) 48 if ((size > 16 && !isHFVA) || !isPOD(tb)) 49 return TypeTuple.empty; 50 51 if (isHFVA) 52 { 53 // pass in SIMD registers 54 return new TypeTuple(hfvaType); 55 } 56 57 // pass remaining aggregates in 1 or 2 GP registers 58 static Type getGPType(size_t size) 59 { 60 switch (size) 61 { 62 case 1: return Type.tint8; 63 case 2: return Type.tint16; 64 case 4: return Type.tint32; 65 case 8: return Type.tint64; 66 default: return Type.tint64.sarrayOf((size + 7) / 8); 67 } 68 } 69 return new TypeTuple(getGPType(size)); 70 } 71 72 /** 73 * A Homogeneous Floating-point/Vector Aggregate (HFA/HVA) is an ARM/AArch64 74 * concept that consists of up to 4 elements of the same floating point/vector 75 * type. It is the aggregate final data layout that matters so structs, unions, 76 * static arrays and complex numbers can result in an HFVA. 77 * 78 * simple HFAs: struct F1 {float f;} struct D4 {double a,b,c,d;} 79 * interesting HFA: struct {F1[2] vals; float weight;} 80 * 81 * If the type is an HFVA and `rewriteType` is specified, it is set to a 82 * corresponding static array type. 83 */ 84 extern (C++) bool isHFVA(Type t, int maxNumElements = 4, Type* rewriteType = null) 85 { 86 t = t.toBasetype(); 87 if ((t.ty != Tstruct && t.ty != Tsarray && !t.iscomplex()) || !isPOD(t)) 88 return false; 89 90 Type fundamentalType; 91 const N = getNestedHFVA(t, fundamentalType); 92 if (N < 1 || N > maxNumElements) 93 return false; 94 95 if (rewriteType) 96 *rewriteType = fundamentalType.sarrayOf(N); 97 98 return true; 99 } 100 101 private: 102 103 bool isPOD(Type t) 104 { 105 auto baseType = t.baseElemOf(); 106 if (auto ts = baseType.isTypeStruct()) 107 return ts.sym.isPOD(); 108 return true; 109 } 110 111 /** 112 * Recursive helper. 113 * Returns size_t.max if the type isn't suited as HFVA (element) or incompatible 114 * to the specified fundamental type, otherwise the number of consumed elements 115 * of that fundamental type. 116 * If `fundamentalType` is null, it is set on the first occasion and then left 117 * untouched. 118 */ 119 size_t getNestedHFVA(Type t, ref Type fundamentalType) 120 { 121 t = t.toBasetype(); 122 123 if (auto tarray = t.isTypeSArray()) 124 { 125 const N = getNestedHFVA(tarray.nextOf(), fundamentalType); 126 return N == size_t.max ? N : N * cast(size_t) tarray.dim.toUInteger(); // => T[0] may return 0 127 } 128 129 if (auto tstruct = t.isTypeStruct()) 130 { 131 // check each field recursively and set fundamentalType 132 bool isEmpty = true; 133 foreach (field; tstruct.sym.fields) 134 { 135 const field_N = getNestedHFVA(field.type, fundamentalType); 136 if (field_N == size_t.max) 137 return field_N; 138 if (field_N > 0) // might be 0 for empty static array 139 isEmpty = false; 140 } 141 142 // an empty struct (no fields or only empty static arrays) is an undefined 143 // byte, i.e., no HFVA 144 if (isEmpty) 145 return size_t.max; 146 147 // due to possibly overlapping fields (for unions and nested anonymous 148 // unions), use the overall struct size to determine N 149 const structSize = t.size(); 150 const fundamentalSize = fundamentalType.size(); 151 assert(structSize % fundamentalSize == 0); 152 return cast(size_t) (structSize / fundamentalSize); 153 } 154 155 Type thisFundamentalType; 156 size_t N; 157 158 if (t.isTypeVector()) 159 { 160 thisFundamentalType = t; 161 N = 1; 162 } 163 else if (t.isfloating()) // incl. imaginary and complex 164 { 165 auto ftSize = t.size(); 166 N = 1; 167 168 if (t.iscomplex()) 169 { 170 ftSize /= 2; 171 N = 2; 172 } 173 174 switch (ftSize) 175 { 176 case 4: thisFundamentalType = Type.tfloat32; break; 177 case 8: thisFundamentalType = Type.tfloat64; break; 178 case 16: thisFundamentalType = Type.tfloat80; break; // IEEE quadruple 179 default: assert(0, "unexpected floating-point type size"); 180 } 181 } 182 else 183 { 184 return size_t.max; // reject all other types 185 } 186 187 if (!fundamentalType) 188 fundamentalType = thisFundamentalType; // initialize 189 else if (fundamentalType != thisFundamentalType) 190 return size_t.max; // incompatible fundamental types, reject 191 192 return N; 193 }