1 /** 2 * Break down a D type into basic (register) types for the x86_64 System V 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_sysv_x64.d, _argtypes_sysv_x64.d) 8 * Documentation: https://dlang.org/phobos/dmd_argtypes_sysv_x64.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/argtypes_sysv_x64.d 10 */ 11 12 module dmd.argtypes_sysv_x64; 13 14 import dmd.astenums; 15 import dmd.declaration; 16 import dmd.globals; 17 import dmd.mtype; 18 import dmd.target; 19 import dmd.visitor; 20 21 /**************************************************** 22 * This breaks a type down into 'simpler' types that can be passed to a function 23 * in registers, and returned in registers. 24 * This is the implementation for the x86_64 System V ABI (not used for Win64), 25 * based on https://www.uclibc.org/docs/psABI-x86_64.pdf. 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_sysv_x64(Type t) 34 { 35 if (t == Type.terror) 36 return new TypeTuple(t); 37 38 const size = cast(size_t) t.size(); 39 if (size == 0) 40 return null; 41 if (size > 32) 42 return TypeTuple.empty; 43 44 const classification = classify(t, size); 45 const classes = classification.slice(); 46 const N = classes.length; 47 const c0 = classes[0]; 48 49 switch (c0) 50 { 51 case Class.memory: 52 return TypeTuple.empty; 53 case Class.x87: 54 return new TypeTuple(Type.tfloat80); 55 case Class.complexX87: 56 return new TypeTuple(Type.tfloat80, Type.tfloat80); 57 default: 58 break; 59 } 60 61 if (N > 2 || (N == 2 && classes[1] == Class.sseUp)) 62 { 63 assert(c0 == Class.sse); 64 foreach (c; classes[1 .. $]) 65 assert(c == Class.sseUp); 66 67 assert(size % 8 == 0); 68 return new TypeTuple(new TypeVector(Type.tfloat64.sarrayOf(N))); 69 } 70 71 assert(N >= 1 && N <= 2); 72 Type[2] argtypes; 73 foreach (i, c; classes) 74 { 75 // the last eightbyte may be filled partially only 76 auto sizeInEightbyte = (i < N - 1) ? 8 : size % 8; 77 if (sizeInEightbyte == 0) 78 sizeInEightbyte = 8; 79 80 if (c == Class.integer) 81 { 82 argtypes[i] = 83 sizeInEightbyte > 4 ? Type.tint64 : 84 sizeInEightbyte > 2 ? Type.tint32 : 85 sizeInEightbyte > 1 ? Type.tint16 : 86 Type.tint8; 87 } 88 else if (c == Class.sse) 89 { 90 argtypes[i] = 91 sizeInEightbyte > 4 ? Type.tfloat64 : 92 Type.tfloat32; 93 } 94 else 95 assert(0, "Unexpected class"); 96 } 97 98 return N == 1 99 ? new TypeTuple(argtypes[0]) 100 : new TypeTuple(argtypes[0], argtypes[1]); 101 } 102 103 104 private: 105 106 // classification per eightbyte (64-bit chunk) 107 enum Class : ubyte 108 { 109 integer, 110 sse, 111 sseUp, 112 x87, 113 x87Up, 114 complexX87, 115 noClass, 116 memory 117 } 118 119 Class merge(Class a, Class b) @safe 120 { 121 bool any(Class value) { return a == value || b == value; } 122 123 if (a == b) 124 return a; 125 if (a == Class.noClass) 126 return b; 127 if (b == Class.noClass) 128 return a; 129 if (any(Class.memory)) 130 return Class.memory; 131 if (any(Class.integer)) 132 return Class.integer; 133 if (any(Class.x87) || any(Class.x87Up) || any(Class.complexX87)) 134 return Class.memory; 135 return Class.sse; 136 } 137 138 struct Classification 139 { 140 Class[4] classes; 141 int numEightbytes; 142 143 const(Class[]) slice() const return @safe { return classes[0 .. numEightbytes]; } 144 } 145 146 Classification classify(Type t, size_t size) 147 { 148 scope v = new ToClassesVisitor(size); 149 t.accept(v); 150 return Classification(v.result, v.numEightbytes); 151 } 152 153 extern (C++) final class ToClassesVisitor : Visitor 154 { 155 const size_t size; 156 int numEightbytes; 157 Class[4] result = Class.noClass; 158 159 this(size_t size) scope @safe 160 { 161 assert(size > 0); 162 this.size = size; 163 this.numEightbytes = cast(int) ((size + 7) / 8); 164 } 165 166 void memory() 167 { 168 result[0 .. numEightbytes] = Class.memory; 169 } 170 171 void one(Class a) 172 { 173 result[0] = a; 174 } 175 176 void two(Class a, Class b) 177 { 178 result[0] = a; 179 result[1] = b; 180 } 181 182 alias visit = Visitor.visit; 183 184 override void visit(Type) 185 { 186 assert(0, "Unexpected type"); 187 } 188 189 override void visit(TypeEnum t) 190 { 191 t.toBasetype().accept(this); 192 } 193 194 override void visit(TypeNoreturn t) 195 { 196 // Treat as void 197 return visit(Type.tvoid); 198 } 199 200 override void visit(TypeBasic t) 201 { 202 switch (t.ty) 203 { 204 case Tvoid: 205 case Tbool: 206 case Tint8: 207 case Tuns8: 208 case Tint16: 209 case Tuns16: 210 case Tint32: 211 case Tuns32: 212 case Tint64: 213 case Tuns64: 214 case Tchar: 215 case Twchar: 216 case Tdchar: 217 return one(Class.integer); 218 219 case Tint128: 220 case Tuns128: 221 return two(Class.integer, Class.integer); 222 223 case Tfloat80: 224 case Timaginary80: 225 return two(Class.x87, Class.x87Up); 226 227 case Tfloat32: 228 case Tfloat64: 229 case Timaginary32: 230 case Timaginary64: 231 case Tcomplex32: // struct { float a, b; } 232 return one(Class.sse); 233 234 case Tcomplex64: // struct { double a, b; } 235 return two(Class.sse, Class.sse); 236 237 case Tcomplex80: // struct { real a, b; } 238 result[0 .. 4] = Class.complexX87; 239 return; 240 241 default: 242 assert(0, "Unexpected basic type"); 243 } 244 } 245 246 override void visit(TypeVector t) 247 { 248 result[0] = Class.sse; 249 result[1 .. numEightbytes] = Class.sseUp; 250 } 251 252 override void visit(TypeAArray) 253 { 254 return one(Class.integer); 255 } 256 257 override void visit(TypePointer) 258 { 259 return one(Class.integer); 260 } 261 262 override void visit(TypeNull) 263 { 264 return one(Class.integer); 265 } 266 267 override void visit(TypeClass) 268 { 269 return one(Class.integer); 270 } 271 272 override void visit(TypeDArray) 273 { 274 if (!target.isLP64) 275 return one(Class.integer); 276 return two(Class.integer, Class.integer); 277 } 278 279 override void visit(TypeDelegate) 280 { 281 if (!target.isLP64) 282 return one(Class.integer); 283 return two(Class.integer, Class.integer); 284 } 285 286 override void visit(TypeSArray t) 287 { 288 // treat as struct with N fields 289 290 auto baseElemType = t.next.toBasetype().isTypeStruct(); 291 if (baseElemType && !baseElemType.sym.isPOD()) 292 return memory(); 293 294 classifyStaticArrayElements(0, t); 295 finalizeAggregate(); 296 } 297 298 override void visit(TypeStruct t) 299 { 300 if (!t.sym.isPOD()) 301 return memory(); 302 303 classifyStructFields(0, t); 304 finalizeAggregate(); 305 } 306 307 void classifyStructFields(uint baseOffset, TypeStruct t) 308 { 309 extern(D) Type getNthField(size_t n, out uint offset, out uint typeAlignment) 310 { 311 auto field = t.sym.fields[n]; 312 offset = field.offset; 313 typeAlignment = field.type.alignsize(); 314 return field.type; 315 } 316 317 classifyFields(baseOffset, t.sym.fields.length, &getNthField); 318 } 319 320 void classifyStaticArrayElements(uint baseOffset, TypeSArray t) 321 { 322 Type elemType = t.next; 323 const elemSize = elemType.size(); 324 const elemTypeAlignment = elemType.alignsize(); 325 326 extern(D) Type getNthElement(size_t n, out uint offset, out uint typeAlignment) 327 { 328 offset = cast(uint)(n * elemSize); 329 typeAlignment = elemTypeAlignment; 330 return elemType; 331 } 332 333 classifyFields(baseOffset, cast(size_t) t.dim.toInteger(), &getNthElement); 334 } 335 336 extern(D) void classifyFields(uint baseOffset, size_t nfields, Type delegate(size_t, out uint, out uint) getFieldInfo) 337 { 338 // classify each field (recursively for aggregates) and merge all classes per eightbyte 339 foreach (n; 0 .. nfields) 340 { 341 uint foffset_relative; 342 uint ftypeAlignment; 343 Type ftype = getFieldInfo(n, foffset_relative, ftypeAlignment); 344 const fsize = cast(size_t) ftype.size(); 345 346 const foffset = baseOffset + foffset_relative; 347 if (foffset & (ftypeAlignment - 1)) // not aligned 348 return memory(); 349 350 if (auto ts = ftype.isTypeStruct()) 351 classifyStructFields(foffset, ts); 352 else if (auto tsa = ftype.isTypeSArray()) 353 classifyStaticArrayElements(foffset, tsa); 354 else if (ftype.toBasetype().isTypeNoreturn()) 355 { 356 // Ignore noreturn members with sizeof = 0 357 // Potential custom alignment changes are factored in above 358 nfields--; 359 continue; 360 } 361 else 362 { 363 const fEightbyteStart = foffset / 8; 364 const fEightbyteEnd = (foffset + fsize + 7) / 8; 365 if (ftype.ty == Tcomplex32) // may lie in 2 eightbytes 366 { 367 assert(foffset % 4 == 0); 368 foreach (ref existingClass; result[fEightbyteStart .. fEightbyteEnd]) 369 existingClass = merge(existingClass, Class.sse); 370 } 371 else 372 { 373 assert(foffset % 8 == 0 || 374 fEightbyteEnd - fEightbyteStart <= 1 || 375 !target.isLP64, 376 "Field not aligned at eightbyte boundary but contributing to multiple eightbytes?" 377 ); 378 foreach (i, fclass; classify(ftype, fsize).slice()) 379 { 380 Class* existingClass = &result[fEightbyteStart + i]; 381 *existingClass = merge(*existingClass, fclass); 382 } 383 } 384 } 385 } 386 387 if (nfields == 0) 388 return memory(); 389 } 390 391 void finalizeAggregate() 392 { 393 foreach (i, ref c; result) 394 { 395 if (c == Class.memory || 396 (c == Class.x87Up && !(i > 0 && result[i - 1] == Class.x87))) 397 return memory(); 398 399 if (c == Class.sseUp && !(i > 0 && 400 (result[i - 1] == Class.sse || result[i - 1] == Class.sseUp))) 401 c = Class.sse; 402 } 403 404 if (numEightbytes > 2) 405 { 406 if (result[0] != Class.sse) 407 return memory(); 408 409 foreach (c; result[1 .. numEightbytes]) 410 if (c != Class.sseUp) 411 return memory(); 412 } 413 414 // Undocumented special case for aggregates with the 2nd eightbyte 415 // consisting of padding only (`struct S { align(16) int a; }`). 416 // clang only passes the first eightbyte in that case, so let's do the 417 // same. 418 if (numEightbytes == 2 && result[1] == Class.noClass) 419 numEightbytes = 1; 420 } 421 }