1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved 6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 7 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(DMDSRC _argtypes.d) 9 */ 10 11 module ddmd.argtypes; 12 13 import core.stdc.stdio; 14 import ddmd.declaration; 15 import ddmd.globals; 16 import ddmd.mtype; 17 import ddmd.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 * It's highly platform dependent. 23 * Params: 24 * t = type to break down 25 * Returns: 26 * tuple of types, each element can be passed in a register. 27 * A tuple of zero length means the type cannot be passed/returned in registers. 28 */ 29 extern (C++) TypeTuple toArgTypes(Type t) 30 { 31 extern (C++) final class ToArgTypes : Visitor 32 { 33 alias visit = super.visit; 34 public: 35 TypeTuple result; 36 37 override void visit(Type) 38 { 39 // not valid for a parameter 40 } 41 42 override void visit(TypeError) 43 { 44 result = new TypeTuple(Type.terror); 45 } 46 47 override void visit(TypeBasic t) 48 { 49 Type t1 = null; 50 Type t2 = null; 51 switch (t.ty) 52 { 53 case Tvoid: 54 return; 55 case Tbool: 56 case Tint8: 57 case Tuns8: 58 case Tint16: 59 case Tuns16: 60 case Tint32: 61 case Tuns32: 62 case Tfloat32: 63 case Tint64: 64 case Tuns64: 65 case Tint128: 66 case Tuns128: 67 case Tfloat64: 68 case Tfloat80: 69 t1 = t; 70 break; 71 case Timaginary32: 72 t1 = Type.tfloat32; 73 break; 74 case Timaginary64: 75 t1 = Type.tfloat64; 76 break; 77 case Timaginary80: 78 t1 = Type.tfloat80; 79 break; 80 case Tcomplex32: 81 if (global.params.is64bit) 82 t1 = Type.tfloat64; 83 else 84 { 85 t1 = Type.tfloat64; 86 t2 = Type.tfloat64; 87 } 88 break; 89 case Tcomplex64: 90 t1 = Type.tfloat64; 91 t2 = Type.tfloat64; 92 break; 93 case Tcomplex80: 94 t1 = Type.tfloat80; 95 t2 = Type.tfloat80; 96 break; 97 case Tchar: 98 t1 = Type.tuns8; 99 break; 100 case Twchar: 101 t1 = Type.tuns16; 102 break; 103 case Tdchar: 104 t1 = Type.tuns32; 105 break; 106 default: 107 assert(0); 108 } 109 if (t1) 110 { 111 if (t2) 112 result = new TypeTuple(t1, t2); 113 else 114 result = new TypeTuple(t1); 115 } 116 else 117 result = new TypeTuple(); 118 } 119 120 override void visit(TypeVector t) 121 { 122 result = new TypeTuple(t); 123 } 124 125 override void visit(TypeSArray t) 126 { 127 if (t.dim) 128 { 129 /* Should really be done as if it were a struct with dim members 130 * of the array's elements. 131 * I.e. int[2] should be done like struct S { int a; int b; } 132 */ 133 dinteger_t sz = t.dim.toInteger(); 134 // T[1] should be passed like T 135 if (sz == 1) 136 { 137 t.next.accept(this); 138 return; 139 } 140 } 141 result = new TypeTuple(); // pass on the stack for efficiency 142 } 143 144 override void visit(TypeAArray) 145 { 146 result = new TypeTuple(Type.tvoidptr); 147 } 148 149 override void visit(TypePointer) 150 { 151 result = new TypeTuple(Type.tvoidptr); 152 } 153 154 /************************************* 155 * Convert a floating point type into the equivalent integral type. 156 */ 157 static Type mergeFloatToInt(Type t) 158 { 159 switch (t.ty) 160 { 161 case Tfloat32: 162 case Timaginary32: 163 t = Type.tint32; 164 break; 165 case Tfloat64: 166 case Timaginary64: 167 case Tcomplex32: 168 t = Type.tint64; 169 break; 170 default: 171 debug 172 { 173 printf("mergeFloatToInt() %s\n", t.toChars()); 174 } 175 assert(0); 176 } 177 return t; 178 } 179 180 /************************************* 181 * This merges two types into an 8byte type. 182 * Params: 183 * t1 = first type (can be null) 184 * t2 = second type (can be null) 185 * offset2 = offset of t2 from start of t1 186 * Returns: 187 * type that encompasses both t1 and t2, null if cannot be done 188 */ 189 static Type argtypemerge(Type t1, Type t2, uint offset2) 190 { 191 //printf("argtypemerge(%s, %s, %d)\n", t1 ? t1.toChars() : "", t2 ? t2.toChars() : "", offset2); 192 if (!t1) 193 { 194 assert(!t2 || offset2 == 0); 195 return t2; 196 } 197 if (!t2) 198 return t1; 199 const sz1 = t1.size(Loc()); 200 const sz2 = t2.size(Loc()); 201 assert(sz1 != SIZE_INVALID && sz2 != SIZE_INVALID); 202 if (t1.ty != t2.ty && (t1.ty == Tfloat80 || t2.ty == Tfloat80)) 203 return null; 204 // [float,float] => [cfloat] 205 if (t1.ty == Tfloat32 && t2.ty == Tfloat32 && offset2 == 4) 206 return Type.tfloat64; 207 // Merging floating and non-floating types produces the non-floating type 208 if (t1.isfloating()) 209 { 210 if (!t2.isfloating()) 211 t1 = mergeFloatToInt(t1); 212 } 213 else if (t2.isfloating()) 214 t2 = mergeFloatToInt(t2); 215 Type t; 216 // Pick type with larger size 217 if (sz1 < sz2) 218 t = t2; 219 else 220 t = t1; 221 // If t2 does not lie within t1, need to increase the size of t to enclose both 222 assert(sz2 < sz2.max - offset2.max); 223 if (offset2 && sz1 < offset2 + sz2) 224 { 225 switch (offset2 + sz2) 226 { 227 case 2: 228 t = Type.tint16; 229 break; 230 case 3: 231 case 4: 232 t = Type.tint32; 233 break; 234 default: 235 t = Type.tint64; 236 break; 237 } 238 } 239 return t; 240 } 241 242 override void visit(TypeDArray) 243 { 244 /* Should be done as if it were: 245 * struct S { size_t length; void* ptr; } 246 */ 247 if (global.params.is64bit && !global.params.isLP64) 248 { 249 // For AMD64 ILP32 ABI, D arrays fit into a single integer register. 250 uint offset = cast(uint)Type.tsize_t.size(Loc()); 251 Type t = argtypemerge(Type.tsize_t, Type.tvoidptr, offset); 252 if (t) 253 { 254 result = new TypeTuple(t); 255 return; 256 } 257 } 258 result = new TypeTuple(Type.tsize_t, Type.tvoidptr); 259 } 260 261 override void visit(TypeDelegate) 262 { 263 /* Should be done as if it were: 264 * struct S { size_t length; void* ptr; } 265 */ 266 if (global.params.is64bit && !global.params.isLP64) 267 { 268 // For AMD64 ILP32 ABI, delegates fit into a single integer register. 269 uint offset = cast(uint)Type.tsize_t.size(Loc()); 270 Type t = argtypemerge(Type.tsize_t, Type.tvoidptr, offset); 271 if (t) 272 { 273 result = new TypeTuple(t); 274 return; 275 } 276 } 277 result = new TypeTuple(Type.tvoidptr, Type.tvoidptr); 278 } 279 280 override void visit(TypeStruct t) 281 { 282 //printf("TypeStruct.toArgTypes() %s\n", t.toChars()); 283 if (!t.sym.isPOD() || t.sym.fields.dim == 0) 284 { 285 Lmemory: 286 //printf("\ttoArgTypes() %s => [ ]\n", t->toChars()); 287 result = new TypeTuple(); // pass on the stack 288 return; 289 } 290 Type t1 = null; 291 Type t2 = null; 292 const sz = t.size(Loc()); 293 assert(sz < 0xFFFFFFFF); 294 switch (cast(uint)sz) 295 { 296 case 1: 297 t1 = Type.tint8; 298 break; 299 case 2: 300 t1 = Type.tint16; 301 break; 302 case 3: 303 if (!global.params.is64bit) 304 goto Lmemory; 305 goto case; 306 case 4: 307 t1 = Type.tint32; 308 break; 309 case 5: 310 case 6: 311 case 7: 312 if (!global.params.is64bit) 313 goto Lmemory; 314 goto case; 315 case 8: 316 t1 = Type.tint64; 317 break; 318 case 16: 319 t1 = null; // could be a TypeVector 320 break; 321 case 9: 322 case 10: 323 case 11: 324 case 12: 325 case 13: 326 case 14: 327 case 15: 328 if (!global.params.is64bit) 329 goto Lmemory; 330 t1 = null; 331 break; 332 default: 333 goto Lmemory; 334 } 335 if (global.params.is64bit && t.sym.fields.dim) 336 { 337 version (all) 338 { 339 t1 = null; 340 for (size_t i = 0; i < t.sym.fields.dim; i++) 341 { 342 VarDeclaration f = t.sym.fields[i]; 343 //printf(" [%d] %s f.type = %s\n", cast(int)i, f.toChars(), f.type.toChars()); 344 TypeTuple tup = toArgTypes(f.type); 345 if (!tup) 346 goto Lmemory; 347 size_t dim = tup.arguments.dim; 348 Type ft1 = null; 349 Type ft2 = null; 350 switch (dim) 351 { 352 case 2: 353 ft1 = (*tup.arguments)[0].type; 354 ft2 = (*tup.arguments)[1].type; 355 break; 356 case 1: 357 if (f.offset < 8) 358 ft1 = (*tup.arguments)[0].type; 359 else 360 ft2 = (*tup.arguments)[0].type; 361 break; 362 default: 363 goto Lmemory; 364 } 365 if (f.offset & 7) 366 { 367 // Misaligned fields goto Lmemory 368 uint alignsz = f.type.alignsize(); 369 if (f.offset & (alignsz - 1)) 370 goto Lmemory; 371 // Fields that overlap the 8byte boundary goto Lmemory 372 const fieldsz = f.type.size(Loc()); 373 assert(fieldsz != SIZE_INVALID && fieldsz < fieldsz.max - f.offset.max); 374 if (f.offset < 8 && (f.offset + fieldsz) > 8) 375 goto Lmemory; 376 } 377 // First field in 8byte must be at start of 8byte 378 assert(t1 || f.offset == 0); 379 //printf("ft1 = %s\n", ft1 ? ft1.toChars() : "null"); 380 //printf("ft2 = %s\n", ft2 ? ft2.toChars() : "null"); 381 if (ft1) 382 { 383 t1 = argtypemerge(t1, ft1, f.offset); 384 if (!t1) 385 goto Lmemory; 386 } 387 if (ft2) 388 { 389 uint off2 = f.offset; 390 if (ft1) 391 off2 = 8; 392 if (!t2 && off2 != 8) 393 goto Lmemory; 394 assert(t2 || off2 == 8); 395 t2 = argtypemerge(t2, ft2, off2 - 8); 396 if (!t2) 397 goto Lmemory; 398 } 399 } 400 if (t2) 401 { 402 if (t1.isfloating() && t2.isfloating()) 403 { 404 if ((t1.ty == Tfloat32 || t1.ty == Tfloat64) && (t2.ty == Tfloat32 || t2.ty == Tfloat64)) 405 { 406 } 407 else 408 goto Lmemory; 409 } 410 else if (t1.isfloating()) 411 goto Lmemory; 412 else if (t2.isfloating()) 413 goto Lmemory; 414 else 415 { 416 } 417 } 418 } 419 else 420 { 421 if (t.sym.fields.dim == 1) 422 { 423 VarDeclaration f = t.sym.fields[0]; 424 //printf("f->type = %s\n", f->type->toChars()); 425 TypeTuple tup = toArgTypes(f.type); 426 if (tup) 427 { 428 size_t dim = tup.arguments.dim; 429 if (dim == 1) 430 t1 = (*tup.arguments)[0].type; 431 } 432 } 433 } 434 } 435 //printf("\ttoArgTypes() %s => [%s,%s]\n", t->toChars(), t1 ? t1->toChars() : "", t2 ? t2->toChars() : ""); 436 if (t1) 437 { 438 //if (t1) printf("test1: %s => %s\n", toChars(), t1->toChars()); 439 if (t2) 440 result = new TypeTuple(t1, t2); 441 else 442 result = new TypeTuple(t1); 443 } 444 else 445 goto Lmemory; 446 } 447 448 override void visit(TypeEnum t) 449 { 450 t.toBasetype().accept(this); 451 } 452 453 override void visit(TypeClass) 454 { 455 result = new TypeTuple(Type.tvoidptr); 456 } 457 } 458 459 scope ToArgTypes v = new ToArgTypes(); 460 t.accept(v); 461 return v.result; 462 }