1 /** 2 * Break down a D type into basic (register) types for the 32-bit x86 ABI. 3 * 4 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 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_x86.d, _argtypes_x86.d) 8 * Documentation: https://dlang.org/phobos/dmd_argtypes_x86.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/argtypes_x86.d 10 */ 11 12 module dmd.argtypes_x86; 13 14 import core.stdc.stdio; 15 import core.checkedint; 16 17 import dmd.astenums; 18 import dmd.declaration; 19 import dmd.globals; 20 import dmd.location; 21 import dmd.mtype; 22 import dmd.target; 23 import dmd.visitor; 24 25 /**************************************************** 26 * This breaks a type down into 'simpler' types that can be passed to a function 27 * in registers, and returned in registers. 28 * This is the implementation for the 32-bit x86 ABI. 29 * Params: 30 * t = type to break down 31 * Returns: 32 * tuple of types, each element can be passed in a register. 33 * A tuple of zero length means the type cannot be passed/returned in registers. 34 * null indicates a `void`. 35 */ 36 extern (C++) TypeTuple toArgTypes_x86(Type t) 37 { 38 extern (C++) final class ToArgTypes : Visitor 39 { 40 alias visit = Visitor.visit; 41 public: 42 TypeTuple result; 43 44 /***** 45 * Pass type in memory (i.e. on the stack), a tuple of one type, or a tuple of 2 types 46 */ 47 void memory() 48 { 49 //printf("\ttoArgTypes() %s => [ ]\n", t.toChars()); 50 result = TypeTuple.empty; // pass on the stack 51 } 52 53 /// 54 void oneType(Type t) 55 { 56 result = new TypeTuple(t); 57 } 58 59 /// 60 void twoTypes(Type t1, Type t2) 61 { 62 result = new TypeTuple(t1, t2); 63 } 64 65 66 override void visit(Type) 67 { 68 // not valid for a parameter 69 } 70 71 override void visit(TypeError) 72 { 73 result = new TypeTuple(Type.terror); 74 } 75 76 override void visit(TypeBasic t) 77 { 78 Type t1 = null; 79 Type t2 = null; 80 switch (t.ty) 81 { 82 case Tvoid: 83 return; 84 case Tbool: 85 case Tint8: 86 case Tuns8: 87 case Tint16: 88 case Tuns16: 89 case Tint32: 90 case Tuns32: 91 case Tfloat32: 92 case Tint64: 93 case Tuns64: 94 case Tint128: 95 case Tuns128: 96 case Tfloat64: 97 case Tfloat80: 98 t1 = t; 99 break; 100 case Timaginary32: 101 t1 = Type.tfloat32; 102 break; 103 case Timaginary64: 104 t1 = Type.tfloat64; 105 break; 106 case Timaginary80: 107 t1 = Type.tfloat80; 108 break; 109 case Tcomplex32: 110 t1 = Type.tfloat64; 111 t2 = Type.tfloat64; 112 break; 113 case Tcomplex64: 114 t1 = Type.tfloat64; 115 t2 = Type.tfloat64; 116 break; 117 case Tcomplex80: 118 t1 = Type.tfloat80; 119 t2 = Type.tfloat80; 120 break; 121 case Tchar: 122 t1 = Type.tuns8; 123 break; 124 case Twchar: 125 t1 = Type.tuns16; 126 break; 127 case Tdchar: 128 t1 = Type.tuns32; 129 break; 130 default: 131 assert(0); 132 } 133 if (t1) 134 { 135 if (t2) 136 return twoTypes(t1, t2); 137 else 138 return oneType(t1); 139 } 140 else 141 return memory(); 142 } 143 144 override void visit(TypeVector t) 145 { 146 return oneType(t); 147 } 148 149 override void visit(TypeAArray) 150 { 151 return oneType(Type.tvoidptr); 152 } 153 154 override void visit(TypePointer) 155 { 156 return oneType(Type.tvoidptr); 157 } 158 159 /************************************* 160 * Convert a floating point type into the equivalent integral type. 161 */ 162 static Type mergeFloatToInt(Type t) 163 { 164 switch (t.ty) 165 { 166 case Tfloat32: 167 case Timaginary32: 168 t = Type.tint32; 169 break; 170 case Tfloat64: 171 case Timaginary64: 172 case Tcomplex32: 173 t = Type.tint64; 174 break; 175 default: 176 debug 177 { 178 printf("mergeFloatToInt() %s\n", t.toChars()); 179 } 180 assert(0); 181 } 182 return t; 183 } 184 185 /************************************* 186 * This merges two types into an 8byte type. 187 * Params: 188 * t1 = first type (can be null) 189 * t2 = second type (can be null) 190 * offset2 = offset of t2 from start of t1 191 * Returns: 192 * type that encompasses both t1 and t2, null if cannot be done 193 */ 194 static Type argtypemerge(Type t1, Type t2, uint offset2) 195 { 196 //printf("argtypemerge(%s, %s, %d)\n", t1 ? t1.toChars() : "", t2 ? t2.toChars() : "", offset2); 197 if (!t1) 198 { 199 assert(!t2 || offset2 == 0); 200 return t2; 201 } 202 if (!t2) 203 return t1; 204 const sz1 = t1.size(Loc.initial); 205 const sz2 = t2.size(Loc.initial); 206 assert(sz1 != SIZE_INVALID && sz2 != SIZE_INVALID); 207 if (t1.ty != t2.ty && (t1.ty == Tfloat80 || t2.ty == Tfloat80)) 208 return null; 209 // [float,float] => [cfloat] 210 if (t1.ty == Tfloat32 && t2.ty == Tfloat32 && offset2 == 4) 211 return Type.tfloat64; 212 // Merging floating and non-floating types produces the non-floating type 213 if (t1.isfloating()) 214 { 215 if (!t2.isfloating()) 216 t1 = mergeFloatToInt(t1); 217 } 218 else if (t2.isfloating()) 219 t2 = mergeFloatToInt(t2); 220 Type t; 221 // Pick type with larger size 222 if (sz1 < sz2) 223 t = t2; 224 else 225 t = t1; 226 // If t2 does not lie within t1, need to increase the size of t to enclose both 227 bool overflow; 228 const offset3 = addu(offset2, sz2, overflow); 229 assert(!overflow); 230 if (offset2 && sz1 < offset3) 231 { 232 switch (offset3) 233 { 234 case 2: 235 t = Type.tint16; 236 break; 237 case 3: 238 case 4: 239 t = Type.tint32; 240 break; 241 default: 242 t = Type.tint64; 243 break; 244 } 245 } 246 return t; 247 } 248 249 override void visit(TypeDArray) 250 { 251 /* Should be done as if it were: 252 * struct S { size_t length; void* ptr; } 253 */ 254 return twoTypes(Type.tsize_t, Type.tvoidptr); 255 } 256 257 override void visit(TypeDelegate) 258 { 259 /* Should be done as if it were: 260 * struct S { void* funcptr; void* ptr; } 261 */ 262 return twoTypes(Type.tvoidptr, Type.tvoidptr); 263 } 264 265 override void visit(TypeSArray t) 266 { 267 const sz = t.size(Loc.initial); 268 if (sz > 16) 269 return memory(); 270 271 const dim = t.dim.toInteger(); 272 Type tn = t.next; 273 const tnsize = tn.size(); 274 const tnalignsize = tn.alignsize(); 275 276 /***** 277 * Get the nth element of this array. 278 * Params: 279 * n = element number, from 0..length 280 * offset = set to offset of the element from the start of the array 281 * alignsize = set to the aligned size of the element 282 * Returns: 283 * type of the element 284 */ 285 extern (D) Type getNthElement(size_t n, out uint offset, out uint alignsize) 286 { 287 offset = cast(uint)(n * tnsize); 288 alignsize = tnalignsize; 289 return tn; 290 } 291 292 aggregate(sz, cast(size_t)dim, &getNthElement); 293 } 294 295 override void visit(TypeStruct t) 296 { 297 //printf("TypeStruct.toArgTypes() %s\n", t.toChars()); 298 299 if (!t.sym.isPOD()) 300 return memory(); 301 302 /***** 303 * Get the nth field of this struct. 304 * Params: 305 * n = field number, from 0..nfields 306 * offset = set to offset of the field from the start of the type 307 * alignsize = set to the aligned size of the field 308 * Returns: 309 * type of the field 310 */ 311 extern (D) Type getNthField(size_t n, out uint offset, out uint alignsize) 312 { 313 auto field = t.sym.fields[n]; 314 offset = field.offset; 315 alignsize = field.type.alignsize(); 316 return field.type; 317 } 318 319 aggregate(t.size(Loc.initial), t.sym.fields.length, &getNthField); 320 } 321 322 /******************* 323 * Handle aggregates (struct, union, and static array) and set `result` 324 * Params: 325 * sz = total size of aggregate 326 * nfields = number of fields in the aggregate (dimension for static arrays) 327 * getFieldInfo = get information about the nth field in the aggregate 328 */ 329 extern (D) void aggregate(uinteger_t sz, size_t nfields, Type delegate(size_t, out uint, out uint) getFieldInfo) 330 { 331 if (nfields == 0) 332 return memory(); 333 334 Type t1 = null; 335 switch (cast(uint)sz) 336 { 337 case 1: 338 t1 = Type.tint8; 339 break; 340 case 2: 341 t1 = Type.tint16; 342 break; 343 case 4: 344 t1 = Type.tint32; 345 break; 346 case 8: 347 t1 = Type.tint64; 348 break; 349 case 16: 350 t1 = null; // could be a TypeVector 351 break; 352 default: 353 return memory(); 354 } 355 if (target.os == Target.OS.FreeBSD && nfields == 1 && 356 (sz == 4 || sz == 8)) 357 { 358 /* FreeBSD changed their 32 bit ABI at some point before 10.3 for the following: 359 * struct { float f; } => arg1type is float 360 * struct { double d; } => arg1type is double 361 * Cannot find any documentation on it. 362 */ 363 364 uint foffset; 365 uint falignsize; 366 Type ftype = getFieldInfo(0, foffset, falignsize); 367 TypeTuple tup = toArgTypes_x86(ftype); 368 if (tup && tup.arguments.length == 1) 369 { 370 Type ft1 = (*tup.arguments)[0].type; 371 if (ft1.ty == Tfloat32 || ft1.ty == Tfloat64) 372 return oneType(ft1); 373 } 374 } 375 376 if (t1) 377 return oneType(t1); 378 else 379 return memory(); 380 } 381 382 override void visit(TypeEnum t) 383 { 384 t.toBasetype().accept(this); 385 } 386 387 override void visit(TypeClass) 388 { 389 result = new TypeTuple(Type.tvoidptr); 390 } 391 } 392 393 scope ToArgTypes v = new ToArgTypes(); 394 t.accept(v); 395 return v.result; 396 }