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