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