1 /**
2  * Generate code instructions
3  *
4  * Copyright:   Copyright (C) 1985-1998 by Symantec
5  *              Copyright (C) 2000-2023 by The D Language Foundation, All Rights Reserved
6  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 https://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 private
120 code *gen(code *c, ref code cs)
121 {
122     assert(I64 || cs.Irex == 0);
123     code* ce = code_malloc();
124     *ce = cs;
125     //printf("ce = %p %02x\n", ce, ce.Iop);
126     //ccheck(ce);
127     simplify_code(ce);
128     ce.next = null;
129     if (c)
130     {   code* cstart = c;
131         while (code_next(c)) c = code_next(c);  /* find end of list     */
132         c.next = ce;                      /* link into list       */
133         return cstart;
134     }
135     return ce;
136 }
137 
138 code *gen1(code *c,opcode_t op)
139 {
140     code* ce;
141     code* cstart;
142 
143   ce = code_calloc();
144   ce.Iop = op;
145   //ccheck(ce);
146   assert(op != LEA);
147   if (c)
148   {     cstart = c;
149         while (code_next(c)) c = code_next(c);  /* find end of list     */
150         c.next = ce;                      /* link into list       */
151         return cstart;
152   }
153   return ce;
154 }
155 
156 code *gen2(code *c,opcode_t op,uint rm)
157 {
158     code* ce;
159     code* cstart;
160 
161   cstart = ce = code_calloc();
162   /*cxcalloc++;*/
163   ce.Iop = op;
164   ce.Iea = rm;
165   //ccheck(ce);
166   if (c)
167   {     cstart = c;
168         while (code_next(c)) c = code_next(c);  /* find end of list     */
169         c.next = ce;                      /* link into list       */
170   }
171   return cstart;
172 }
173 
174 
175 code *genc2(code *c,opcode_t op,uint ea,targ_size_t EV2)
176 {   code cs;
177 
178     cs.Iop = op;
179     cs.Iea = ea;
180     //ccheck(&cs);
181     cs.Iflags = CFoff;
182     cs.IFL2 = FLconst;
183     cs.IEV2.Vsize_t = EV2;
184     return gen(c,cs);
185 }
186 
187 
188 /********************************
189  * Generate 'nop'
190  */
191 
192 code *gennop(code *c)
193 {
194     return gen1(c,NOP);
195 }
196 
197 
198 /****************************************
199  * Clean stack after call to codelem().
200  */
201 
202 void gencodelem(ref CodeBuilder cdb,elem *e,regm_t *pretregs,bool constflag)
203 {
204     if (e)
205     {
206         uint stackpushsave;
207         int stackcleansave;
208 
209         stackpushsave = stackpush;
210         stackcleansave = cgstate.stackclean;
211         cgstate.stackclean = 0;                         // defer cleaning of stack
212         codelem(cdb,e,pretregs,constflag);
213         assert(cgstate.stackclean == 0);
214         cgstate.stackclean = stackcleansave;
215         genstackclean(cdb,stackpush - stackpushsave,*pretregs);       // do defered cleaning
216     }
217 }
218 
219 /**********************************
220  * Determine if one of the registers in regm has value in it.
221  * If so, return !=0 and set *preg to which register it is.
222  */
223 
224 bool reghasvalue(regm_t regm,targ_size_t value,reg_t *preg)
225 {
226     //printf("reghasvalue(%s, %llx)\n", regm_str(regm), cast(ulong)value);
227     /* See if another register has the right value      */
228     reg_t r = 0;
229     for (regm_t mreg = regcon.immed.mval; mreg; mreg >>= 1)
230     {
231         if (mreg & regm & 1 && regcon.immed.value[r] == value)
232         {   *preg = r;
233             return true;
234         }
235         r++;
236         regm >>= 1;
237     }
238     return false;
239 }
240 
241 /**************************************
242  * Load a register from the mask regm with value.
243  * Output:
244  *      *preg   the register selected
245  */
246 
247 void regwithvalue(ref CodeBuilder cdb,regm_t regm,targ_size_t value,reg_t *preg,regm_t flags)
248 {
249     //printf("regwithvalue(value = %lld)\n", cast(long)value);
250     reg_t reg;
251     if (!preg)
252         preg = ®
253 
254     // If we don't already have a register with the right value in it
255     if (!reghasvalue(regm,value,preg))
256     {
257         regm_t save = regcon.immed.mval;
258         allocreg(cdb,&regm,preg,TYint);  // allocate register
259         regcon.immed.mval = save;
260         movregconst(cdb,*preg,value,flags);   // store value into reg
261     }
262 }
263 
264 /************************
265  * When we don't know whether a function symbol is defined or not
266  * within this module, we stuff it in an array of references to be
267  * fixed up later.
268  */
269 struct Fixup
270 {
271     Symbol      *sym;       // the referenced Symbol
272     int         seg;        // where the fixup is going (CODE or DATA, never UDATA)
273     int         flags;      // CFxxxx
274     targ_size_t offset;     // addr of reference to Symbol
275     targ_size_t val;        // value to add into location
276     Symbol      *funcsym;   // function the Symbol goes in
277 }
278 
279 private __gshared Barray!Fixup fixups;
280 
281 /****************************
282  * Add to the fix list.
283  */
284 
285 size_t addtofixlist(Symbol *s,targ_size_t offset,int seg,targ_size_t val,int flags)
286 {
287         static immutable ubyte[8] zeros = 0;
288 
289         //printf("addtofixlist(%p '%s')\n",s,s.Sident.ptr);
290         assert(I32 || flags);
291         Fixup* f = fixups.push();
292         f.sym = s;
293         f.offset = offset;
294         f.seg = seg;
295         f.flags = flags;
296         f.val = val;
297         f.funcsym = funcsym_p;
298 
299         size_t numbytes;
300 if (TARGET_SEGMENTED)
301 {
302         switch (flags & (CFoff | CFseg))
303         {
304             case CFoff:         numbytes = tysize(TYnptr);      break;
305             case CFseg:         numbytes = 2;                   break;
306             case CFoff | CFseg: numbytes = tysize(TYfptr);      break;
307             default:            assert(0);
308         }
309 }
310 else
311 {
312         numbytes = tysize(TYnptr);
313         if (I64 && !(flags & CFoffset64))
314             numbytes = 4;
315 
316 if (config.exe & EX_windos)
317 {
318         /* This can happen when generating CV8 data
319          */
320         if (flags & CFseg)
321             numbytes += 2;
322 }
323 }
324         debug assert(numbytes <= zeros.sizeof);
325         objmod.bytes(seg,offset,cast(uint)numbytes,cast(ubyte*)zeros.ptr);
326         return numbytes;
327 }
328 
329 static if (0)
330 {
331 void searchfixlist (Symbol *s )
332 {
333     //printf("searchfixlist(%s)\n", s.Sident);
334 }
335 }
336 
337 /****************************
338  * Output fixups as references to external or static Symbol.
339  * First emit data for still undefined static Symbols or mark non-static Symbols as SCextern.
340  */
341 private void outfixup(ref Fixup f)
342 {
343     symbol_debug(f.sym);
344     //printf("outfixup '%s' offset %04x\n", f.sym.Sident, f.offset);
345 
346 static if (TARGET_SEGMENTED)
347 {
348     if (tybasic(f.sym.ty()) == TYf16func)
349     {
350         Obj.far16thunk(f.sym);          /* make it into a thunk         */
351         objmod.reftoident(f.seg, f.offset, f.sym, f.val, f.flags);
352         return;
353     }
354 }
355 
356     if (f.sym.Sxtrnnum == 0)
357     {
358         if (f.sym.Sclass == SC.static_)
359         {
360 version (SCPP)
361 {
362             if (f.sym.Sdt)
363             {
364                 outdata(f.sym);
365             }
366             else if (f.sym.Sseg == UNKNOWN)
367                 synerr(EM_no_static_def,prettyident(f.sym)); // no definition found for static
368 }
369 else // MARS
370 {
371             // OBJ_OMF does not set Sxtrnnum for static Symbols, so check
372             // whether the Symbol was assigned to a segment instead, compare
373             // outdata(Symbol *s)
374             if (f.sym.Sseg == UNKNOWN)
375             {
376                 printf("Error: no definition for static %s\n", prettyident(f.sym)); // no definition found for static
377                 err_exit(); // BUG: do better
378             }
379 }
380         }
381         else if (f.sym.Sflags & SFLwasstatic)
382         {
383             // Put it in BSS
384             f.sym.Sclass = SC.static_;
385             f.sym.Sfl = FLunde;
386             f.sym.Sdt = dt_get_nzeros(cast(uint)type_size(f.sym.Stype));
387             outdata(f.sym);
388         }
389         else if (f.sym.Sclass != SC.sinline)
390         {
391             f.sym.Sclass = SC.extern_;   /* make it external             */
392             objmod.external(f.sym);
393             if (f.sym.Sflags & SFLweak)
394                 objmod.wkext(f.sym, null);
395         }
396     }
397 
398 if (config.exe & (EX_OSX | EX_OSX64))
399 {
400     Symbol *funcsymsave = funcsym_p;
401     funcsym_p = f.funcsym;
402     objmod.reftoident(f.seg, f.offset, f.sym, f.val, f.flags);
403     funcsym_p = funcsymsave;
404 }
405 else
406 {
407     objmod.reftoident(f.seg, f.offset, f.sym, f.val, f.flags);
408 }
409 }
410 
411 /****************************
412  * End of module. Output fixups as references
413  * to external Symbols.
414  */
415 void outfixlist()
416 {
417     foreach (ref f; fixups)
418         outfixup(f);
419     fixups.reset();
420 }
421 
422 }