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