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