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 = &reg;
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,&regm,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 }