1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (C) 1985-1998 by Symantec 6 * Copyright (C) 2000-2020 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/cgen.d, backend/cgen.d) 10 */ 11 12 module dmd.backend.cgen; 13 14 version (SCPP) 15 version = COMPILE; 16 version (MARS) 17 version = COMPILE; 18 19 version (COMPILE) 20 { 21 22 import core.stdc.stdio; 23 import core.stdc.stdlib; 24 import core.stdc.string; 25 26 import dmd.backend.cc; 27 import dmd.backend.cdef; 28 import dmd.backend.code; 29 import dmd.backend.code_x86; 30 import dmd.backend.codebuilder; 31 import dmd.backend.mem; 32 import dmd.backend.el; 33 import dmd.backend.global; 34 import dmd.backend.obj; 35 import dmd.backend.ty; 36 import dmd.backend.type; 37 38 version (SCPP) 39 { 40 import msgs2; 41 } 42 43 extern (C++): 44 45 nothrow: 46 47 dt_t *dt_get_nzeros(uint n); 48 49 extern __gshared CGstate cgstate; 50 51 /***************************** 52 * Find last code in list. 53 */ 54 55 code *code_last(code *c) 56 { 57 if (c) 58 { while (c.next) 59 c = c.next; 60 } 61 return c; 62 } 63 64 /***************************** 65 * Set flag bits on last code in list. 66 */ 67 68 void code_orflag(code *c,uint flag) 69 { 70 if (flag && c) 71 { while (c.next) 72 c = c.next; 73 c.Iflags |= flag; 74 } 75 } 76 77 /***************************** 78 * Set rex bits on last code in list. 79 */ 80 81 void code_orrex(code *c,uint rex) 82 { 83 if (rex && c) 84 { while (c.next) 85 c = c.next; 86 c.Irex |= rex; 87 } 88 } 89 90 91 /***************************** 92 * Concatenate two code lists together. Return pointer to result. 93 */ 94 95 code *cat(code *c1,code *c2) 96 { code **pc; 97 98 if (!c1) 99 return c2; 100 for (pc = &c1.next; *pc; pc = &(*pc).next) 101 { } 102 *pc = c2; 103 return c1; 104 } 105 106 107 /***************************** 108 * Add code to end of linked list. 109 * Note that unused operands are garbage. 110 * gen1() and gen2() are shortcut routines. 111 * Input: 112 * c -> linked list that code is to be added to end of 113 * cs -> data for the code 114 * Returns: 115 * pointer to start of code list 116 */ 117 118 code *gen(code *c,code *cs) 119 { 120 debug assert(cs); 121 assert(I64 || cs.Irex == 0); 122 code* ce = code_malloc(); 123 *ce = *cs; 124 //printf("ce = %p %02x\n", ce, ce.Iop); 125 //ccheck(ce); 126 simplify_code(ce); 127 ce.next = null; 128 if (c) 129 { code* cstart = c; 130 while (code_next(c)) c = code_next(c); /* find end of list */ 131 c.next = ce; /* link into list */ 132 return cstart; 133 } 134 return ce; 135 } 136 137 code *gen1(code *c,opcode_t op) 138 { 139 code* ce; 140 code* cstart; 141 142 ce = code_calloc(); 143 ce.Iop = op; 144 //ccheck(ce); 145 assert(op != LEA); 146 if (c) 147 { cstart = c; 148 while (code_next(c)) c = code_next(c); /* find end of list */ 149 c.next = ce; /* link into list */ 150 return cstart; 151 } 152 return ce; 153 } 154 155 code *gen2(code *c,opcode_t op,uint rm) 156 { 157 code* ce; 158 code* cstart; 159 160 cstart = ce = code_calloc(); 161 /*cxcalloc++;*/ 162 ce.Iop = op; 163 ce.Iea = rm; 164 //ccheck(ce); 165 if (c) 166 { cstart = c; 167 while (code_next(c)) c = code_next(c); /* find end of list */ 168 c.next = ce; /* link into list */ 169 } 170 return cstart; 171 } 172 173 174 code *gen2sib(code *c,opcode_t op,uint rm,uint sib) 175 { 176 code* ce; 177 code* cstart; 178 179 cstart = ce = code_calloc(); 180 /*cxcalloc++;*/ 181 ce.Iop = op; 182 ce.Irm = cast(ubyte)rm; 183 ce.Isib = cast(ubyte)sib; 184 ce.Irex = cast(ubyte)((rm | (sib & (REX_B << 16))) >> 16); 185 if (sib & (REX_R << 16)) 186 ce.Irex |= REX_X; 187 //ccheck(ce); 188 if (c) 189 { cstart = c; 190 while (code_next(c)) c = code_next(c); /* find end of list */ 191 c.next = ce; /* link into list */ 192 } 193 return cstart; 194 } 195 196 197 code *genc2(code *c,opcode_t op,uint ea,targ_size_t EV2) 198 { code cs; 199 200 cs.Iop = op; 201 cs.Iea = ea; 202 //ccheck(&cs); 203 cs.Iflags = CFoff; 204 cs.IFL2 = FLconst; 205 cs.IEV2.Vsize_t = EV2; 206 return gen(c,&cs); 207 } 208 209 /***************** 210 * Generate code. 211 */ 212 213 code *genc(code *c,opcode_t op,uint ea,uint FL1,targ_size_t EV1,uint FL2,targ_size_t EV2) 214 { code cs; 215 216 assert(FL1 < FLMAX); 217 cs.Iop = op; 218 cs.Iea = ea; 219 //ccheck(&cs); 220 cs.Iflags = CFoff; 221 cs.IFL1 = cast(ubyte)FL1; 222 cs.IEV1.Vsize_t = EV1; 223 assert(FL2 < FLMAX); 224 cs.IFL2 = cast(ubyte)FL2; 225 cs.IEV2.Vsize_t = EV2; 226 return gen(c,&cs); 227 } 228 229 230 /******************************** 231 * Generate 'instruction' which is actually a line number. 232 */ 233 234 code *genlinnum(code *c,Srcpos srcpos) 235 { code cs; 236 237 //srcpos.print("genlinnum"); 238 cs.Iop = ESCAPE | ESClinnum; 239 cs.IEV1.Vsrcpos = srcpos; 240 return gen(c,&cs); 241 } 242 243 /***************************** 244 * Prepend line number to existing code. 245 */ 246 247 void cgen_prelinnum(code **pc,Srcpos srcpos) 248 { 249 *pc = cat(genlinnum(null,srcpos),*pc); 250 } 251 252 /******************************** 253 * Generate 'instruction' which tells the scheduler that the fpu stack has 254 * changed. 255 */ 256 257 code *genadjfpu(code *c, int offset) 258 { code cs; 259 260 if (!I16 && offset) 261 { 262 cs.Iop = ESCAPE | ESCadjfpu; 263 cs.IEV1.Vint = offset; 264 return gen(c,&cs); 265 } 266 else 267 return c; 268 } 269 270 271 /******************************** 272 * Generate 'nop' 273 */ 274 275 code *gennop(code *c) 276 { 277 return gen1(c,NOP); 278 } 279 280 281 /**************************************** 282 * Clean stack after call to codelem(). 283 */ 284 285 void gencodelem(ref CodeBuilder cdb,elem *e,regm_t *pretregs,bool constflag) 286 { 287 if (e) 288 { 289 uint stackpushsave; 290 int stackcleansave; 291 292 stackpushsave = stackpush; 293 stackcleansave = cgstate.stackclean; 294 cgstate.stackclean = 0; // defer cleaning of stack 295 codelem(cdb,e,pretregs,constflag); 296 assert(cgstate.stackclean == 0); 297 cgstate.stackclean = stackcleansave; 298 genstackclean(cdb,stackpush - stackpushsave,*pretregs); // do defered cleaning 299 } 300 } 301 302 /********************************** 303 * Determine if one of the registers in regm has value in it. 304 * If so, return !=0 and set *preg to which register it is. 305 */ 306 307 bool reghasvalue(regm_t regm,targ_size_t value,reg_t *preg) 308 { 309 //printf("reghasvalue(%s, %llx)\n", regm_str(regm), cast(ulong)value); 310 /* See if another register has the right value */ 311 reg_t r = 0; 312 for (regm_t mreg = regcon.immed.mval; mreg; mreg >>= 1) 313 { 314 if (mreg & regm & 1 && regcon.immed.value[r] == value) 315 { *preg = r; 316 return true; 317 } 318 r++; 319 regm >>= 1; 320 } 321 return false; 322 } 323 324 /************************************** 325 * Load a register from the mask regm with value. 326 * Output: 327 * *preg the register selected 328 */ 329 330 void regwithvalue(ref CodeBuilder cdb,regm_t regm,targ_size_t value,reg_t *preg,regm_t flags) 331 { 332 //printf("regwithvalue(value = %lld)\n", (long long)value); 333 reg_t reg; 334 if (!preg) 335 preg = ® 336 337 // If we don't already have a register with the right value in it 338 if (!reghasvalue(regm,value,preg)) 339 { 340 regm_t save = regcon.immed.mval; 341 allocreg(cdb,®m,preg,TYint); // allocate register 342 regcon.immed.mval = save; 343 movregconst(cdb,*preg,value,flags); // store value into reg 344 } 345 } 346 347 /************************ 348 * When we don't know whether a function symbol is defined or not 349 * within this module, we stuff it in an array of references to be 350 * fixed up later. 351 */ 352 struct Fixup 353 { 354 Symbol *sym; // the referenced Symbol 355 int seg; // where the fixup is going (CODE or DATA, never UDATA) 356 int flags; // CFxxxx 357 targ_size_t offset; // addr of reference to Symbol 358 targ_size_t val; // value to add into location 359 static if (TARGET_OSX) 360 { 361 Symbol *funcsym; // function the Symbol goes in 362 } 363 } 364 365 struct FixupArray 366 { 367 nothrow: 368 Fixup *ptr; 369 size_t dim, cap; 370 371 void push(ref Fixup e) 372 { 373 if (dim == cap) 374 { 375 // 0x800 determined experimentally to minimize reallocations 376 cap = cap 377 ? (3 * cap) / 2 // use 'Tau' of 1.5 378 : 0x800; 379 ptr = cast(Fixup *)mem_realloc(ptr, cap * Fixup.sizeof); 380 } 381 ptr[dim++] = e; 382 } 383 384 ref Fixup opIndex(size_t idx) 385 { 386 assert(idx < dim); 387 return ptr[idx]; 388 } 389 390 void clear() 391 { 392 dim = 0; 393 } 394 } 395 396 private __gshared FixupArray fixups; 397 398 /**************************** 399 * Add to the fix list. 400 */ 401 402 size_t addtofixlist(Symbol *s,targ_size_t offset,int seg,targ_size_t val,int flags) 403 { 404 static immutable ubyte[8] zeros = 0; 405 406 //printf("addtofixlist(%p '%s')\n",s,s.Sident); 407 assert(I32 || flags); 408 Fixup f; 409 f.sym = s; 410 f.offset = offset; 411 f.seg = seg; 412 f.flags = flags; 413 f.val = val; 414 static if (TARGET_OSX) 415 { 416 f.funcsym = funcsym_p; 417 } 418 fixups.push(f); 419 420 size_t numbytes; 421 static if (TARGET_SEGMENTED) 422 { 423 switch (flags & (CFoff | CFseg)) 424 { 425 case CFoff: numbytes = tysize(TYnptr); break; 426 case CFseg: numbytes = 2; break; 427 case CFoff | CFseg: numbytes = tysize(TYfptr); break; 428 default: assert(0); 429 } 430 } 431 else 432 { 433 numbytes = tysize(TYnptr); 434 if (I64 && !(flags & CFoffset64)) 435 numbytes = 4; 436 437 static if (TARGET_WINDOS) 438 { 439 /* This can happen when generating CV8 data 440 */ 441 if (flags & CFseg) 442 numbytes += 2; 443 } 444 } 445 debug assert(numbytes <= zeros.sizeof); 446 objmod.bytes(seg,offset,cast(uint)numbytes,cast(ubyte*)zeros.ptr); 447 return numbytes; 448 } 449 450 static if (0) 451 { 452 void searchfixlist (Symbol *s ) 453 { 454 //printf("searchfixlist(%s)\n", s.Sident); 455 } 456 } 457 458 /**************************** 459 * Output fixups as references to external or static Symbol. 460 * First emit data for still undefined static Symbols or mark non-static Symbols as SCextern. 461 */ 462 private void outfixup(ref Fixup f) 463 { 464 symbol_debug(f.sym); 465 //printf("outfixup '%s' offset %04x\n", f.sym.Sident, f.offset); 466 467 static if (TARGET_SEGMENTED) 468 { 469 if (tybasic(f.sym.ty()) == TYf16func) 470 { 471 Obj.far16thunk(f.sym); /* make it into a thunk */ 472 objmod.reftoident(f.seg, f.offset, f.sym, f.val, f.flags); 473 return; 474 } 475 } 476 477 if (f.sym.Sxtrnnum == 0) 478 { 479 if (f.sym.Sclass == SCstatic) 480 { 481 version (SCPP) 482 { 483 if (f.sym.Sdt) 484 { 485 outdata(f.sym); 486 } 487 else if (f.sym.Sseg == UNKNOWN) 488 synerr(EM_no_static_def,prettyident(f.sym)); // no definition found for static 489 } 490 else // MARS 491 { 492 // OBJ_OMF does not set Sxtrnnum for static Symbols, so check 493 // whether the Symbol was assigned to a segment instead, compare 494 // outdata(Symbol *s) 495 if (f.sym.Sseg == UNKNOWN) 496 { 497 printf("Error: no definition for static %s\n", prettyident(f.sym)); // no definition found for static 498 err_exit(); // BUG: do better 499 } 500 } 501 } 502 else if (f.sym.Sflags & SFLwasstatic) 503 { 504 // Put it in BSS 505 f.sym.Sclass = SCstatic; 506 f.sym.Sfl = FLunde; 507 f.sym.Sdt = dt_get_nzeros(cast(uint)type_size(f.sym.Stype)); 508 outdata(f.sym); 509 } 510 else if (f.sym.Sclass != SCsinline) 511 { 512 f.sym.Sclass = SCextern; /* make it external */ 513 objmod.external(f.sym); 514 if (f.sym.Sflags & SFLweak) 515 objmod.wkext(f.sym, null); 516 } 517 } 518 519 static if (TARGET_OSX) 520 { 521 Symbol *funcsymsave = funcsym_p; 522 funcsym_p = f.funcsym; 523 objmod.reftoident(f.seg, f.offset, f.sym, f.val, f.flags); 524 funcsym_p = funcsymsave; 525 } 526 else 527 { 528 objmod.reftoident(f.seg, f.offset, f.sym, f.val, f.flags); 529 } 530 } 531 532 /**************************** 533 * End of module. Output fixups as references 534 * to external Symbols. 535 */ 536 void outfixlist() 537 { 538 for (size_t i = 0; i < fixups.dim; ++i) 539 outfixup(fixups[i]); 540 fixups.clear(); 541 } 542 543 }