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 import core.stdc.stdio;
16 import core.stdc.stdlib;
17 import core.stdc.string;
18 
19 import dmd.backend.barray;
20 import dmd.backend.cc;
21 import dmd.backend.cdef;
22 import dmd.backend.code;
23 import dmd.backend.code_x86;
24 import dmd.backend.codebuilder;
25 import dmd.backend.mem;
26 import dmd.backend.el;
27 import dmd.backend.global;
28 import dmd.backend.obj;
29 import dmd.backend.ty;
30 import dmd.backend.type;
31 
32 
33 nothrow:
34 @safe:
35 
36 public import dmd.backend.dt : dt_get_nzeros;
37 public import dmd.backend.cgcod : cgstate;
38 
39 /*****************************
40  * Find last code in list.
41  */
42 
43 code *code_last(code *c)
44 {
45     if (c)
46     {   while (c.next)
47             c = c.next;
48     }
49     return c;
50 }
51 
52 /*****************************
53  * Set flag bits on last code in list.
54  */
55 
56 void code_orflag(code *c,uint flag)
57 {
58     if (flag && c)
59     {   while (c.next)
60             c = c.next;
61         c.Iflags |= flag;
62     }
63 }
64 
65 /*****************************
66  * Set rex bits on last code in list.
67  */
68 
69 void code_orrex(code *c,uint rex)
70 {
71     if (rex && c)
72     {   while (c.next)
73             c = c.next;
74         c.Irex |= rex;
75     }
76 }
77 
78 
79 /*****************************
80  * Concatenate two code lists together. Return pointer to result.
81  */
82 
83 code *cat(code *c1,code *c2)
84 {   code **pc;
85 
86     if (!c1)
87         return c2;
88     for (pc = &c1.next; *pc; pc = &(*pc).next)
89     { }
90     *pc = c2;
91     return c1;
92 }
93 
94 
95 /*****************************
96  * Add code to end of linked list.
97  * Note that unused operands are garbage.
98  * gen1() and gen2() are shortcut routines.
99  * Input:
100  *      c ->    linked list that code is to be added to end of
101  *      cs ->   data for the code
102  * Returns:
103  *      pointer to start of code list
104  */
105 private
106 code *gen(code *c, ref code cs)
107 {
108     assert(I64 || cs.Irex == 0);
109     code* ce = code_malloc();
110     *ce = cs;
111     //printf("ce = %p %02x\n", ce, ce.Iop);
112     //ccheck(ce);
113     simplify_code(ce);
114     ce.next = null;
115     if (c)
116     {   code* cstart = c;
117         while (code_next(c)) c = code_next(c);  /* find end of list     */
118         c.next = ce;                      /* link into list       */
119         return cstart;
120     }
121     return ce;
122 }
123 
124 code *gen1(code *c,opcode_t op)
125 {
126     code* ce;
127     code* cstart;
128 
129   ce = code_calloc();
130   ce.Iop = op;
131   //ccheck(ce);
132   assert(op != LEA);
133   if (c)
134   {     cstart = c;
135         while (code_next(c)) c = code_next(c);  /* find end of list     */
136         c.next = ce;                      /* link into list       */
137         return cstart;
138   }
139   return ce;
140 }
141 
142 code *gen2(code *c,opcode_t op,uint rm)
143 {
144     code* ce;
145     code* cstart;
146 
147   cstart = ce = code_calloc();
148   /*cxcalloc++;*/
149   ce.Iop = op;
150   ce.Iea = rm;
151   //ccheck(ce);
152   if (c)
153   {     cstart = c;
154         while (code_next(c)) c = code_next(c);  /* find end of list     */
155         c.next = ce;                      /* link into list       */
156   }
157   return cstart;
158 }
159 
160 
161 code *genc2(code *c,opcode_t op,uint ea,targ_size_t EV2)
162 {   code cs;
163 
164     cs.Iop = op;
165     cs.Iea = ea;
166     //ccheck(&cs);
167     cs.Iflags = CFoff;
168     cs.IFL2 = FLconst;
169     cs.IEV2.Vsize_t = EV2;
170     return gen(c,cs);
171 }
172 
173 
174 /********************************
175  * Generate 'nop'
176  */
177 
178 code *gennop(code *c)
179 {
180     return gen1(c,NOP);
181 }
182 
183 
184 /****************************************
185  * Clean stack after call to codelem().
186  */
187 
188 @trusted
189 void gencodelem(ref CodeBuilder cdb,elem *e,regm_t *pretregs,bool constflag)
190 {
191     if (e)
192     {
193         uint stackpushsave;
194         int stackcleansave;
195 
196         stackpushsave = stackpush;
197         stackcleansave = cgstate.stackclean;
198         cgstate.stackclean = 0;                         // defer cleaning of stack
199         codelem(cdb,e,pretregs,constflag);
200         assert(cgstate.stackclean == 0);
201         cgstate.stackclean = stackcleansave;
202         genstackclean(cdb,stackpush - stackpushsave,*pretregs);       // do defered cleaning
203     }
204 }
205 
206 /**********************************
207  * Determine if one of the registers in regm has value in it.
208  * Returns:
209  *      if so, true and preg is set to which register it is.
210  *      otherwise, false and preg is set to 0.
211  */
212 
213 @trusted
214 bool reghasvalue(regm_t regm,targ_size_t value, out reg_t preg)
215 {
216     //printf("reghasvalue(%s, %llx)\n", regm_str(regm), cast(ulong)value);
217     /* See if another register has the right value      */
218     reg_t r = 0;
219     for (regm_t mreg = regcon.immed.mval; mreg; mreg >>= 1)
220     {
221         if (mreg & regm & 1 && regcon.immed.value[r] == value)
222         {   preg = r;
223             return true;
224         }
225         r++;
226         regm >>= 1;
227     }
228     return false;
229 }
230 
231 /**************************************
232  * Load a register from the mask regm with value.
233  * Output:
234  *      preg = the register selected
235  */
236 @trusted
237 void regwithvalue(ref CodeBuilder cdb,regm_t regm,targ_size_t value, out reg_t preg,regm_t flags)
238 {
239     //printf("regwithvalue(value = %lld)\n", cast(long)value);
240 
241     if (reghasvalue(regm,value,preg))
242         return; // already have a register with the right value in it
243 
244     regm_t save = regcon.immed.mval;
245     allocreg(cdb,&regm,&preg,TYint);  // allocate register
246     regcon.immed.mval = save;
247     movregconst(cdb,preg,value,flags);   // store value into reg
248 }
249 
250 /************************
251  * When we don't know whether a function symbol is defined or not
252  * within this module, we stuff it in an array of references to be
253  * fixed up later.
254  */
255 struct Fixup
256 {
257     Symbol      *sym;       // the referenced Symbol
258     int         seg;        // where the fixup is going (CODE or DATA, never UDATA)
259     int         flags;      // CFxxxx
260     targ_size_t offset;     // addr of reference to Symbol
261     targ_size_t val;        // value to add into location
262     Symbol      *funcsym;   // function the Symbol goes in
263 }
264 
265 private __gshared Barray!Fixup fixups;
266 
267 /****************************
268  * Add to the fix list.
269  */
270 @trusted
271 size_t addtofixlist(Symbol *s,targ_size_t offset,int seg,targ_size_t val,int flags)
272 {
273         static immutable ubyte[8] zeros = 0;
274 
275         //printf("addtofixlist(%p '%s')\n",s,s.Sident.ptr);
276         assert(I32 || flags);
277         Fixup* f = fixups.push();
278         f.sym = s;
279         f.offset = offset;
280         f.seg = seg;
281         f.flags = flags;
282         f.val = val;
283         f.funcsym = funcsym_p;
284 
285         size_t numbytes;
286 if (TARGET_SEGMENTED)
287 {
288         switch (flags & (CFoff | CFseg))
289         {
290             case CFoff:         numbytes = tysize(TYnptr);      break;
291             case CFseg:         numbytes = 2;                   break;
292             case CFoff | CFseg: numbytes = tysize(TYfptr);      break;
293             default:            assert(0);
294         }
295 }
296 else
297 {
298         numbytes = tysize(TYnptr);
299         if (I64 && !(flags & CFoffset64))
300             numbytes = 4;
301 
302 if (config.exe & EX_windos)
303 {
304         /* This can happen when generating CV8 data
305          */
306         if (flags & CFseg)
307             numbytes += 2;
308 }
309 }
310         debug assert(numbytes <= zeros.sizeof);
311         objmod.bytes(seg,offset,cast(uint)numbytes,cast(ubyte*)zeros.ptr);
312         return numbytes;
313 }
314 
315 /****************************
316  * Output fixups as references to external or static Symbol.
317  * First emit data for still undefined static Symbols or mark non-static Symbols as SCextern.
318  */
319 @trusted
320 private void outfixup(ref Fixup f)
321 {
322     symbol_debug(f.sym);
323     //printf("outfixup '%s' offset %04x\n", f.sym.Sident, f.offset);
324 
325 static if (TARGET_SEGMENTED)
326 {
327     if (tybasic(f.sym.ty()) == TYf16func)
328     {
329         Obj.far16thunk(f.sym);          /* make it into a thunk         */
330         objmod.reftoident(f.seg, f.offset, f.sym, f.val, f.flags);
331         return;
332     }
333 }
334 
335     if (f.sym.Sxtrnnum == 0)
336     {
337         if (f.sym.Sclass == SC.static_)
338         {
339             // OBJ_OMF does not set Sxtrnnum for static Symbols, so check
340             // whether the Symbol was assigned to a segment instead, compare
341             // outdata(Symbol *s)
342             if (f.sym.Sseg == UNKNOWN)
343             {
344                 printf("Error: no definition for static %s\n", prettyident(f.sym)); // no definition found for static
345                 err_exit(); // BUG: do better
346             }
347         }
348         else if (f.sym.Sflags & SFLwasstatic)
349         {
350             // Put it in BSS
351             f.sym.Sclass = SC.static_;
352             f.sym.Sfl = FLunde;
353             f.sym.Sdt = dt_get_nzeros(cast(uint)type_size(f.sym.Stype));
354             outdata(f.sym);
355         }
356         else if (f.sym.Sclass != SC.sinline)
357         {
358             f.sym.Sclass = SC.extern_;   /* make it external             */
359             objmod.external(f.sym);
360             if (f.sym.Sflags & SFLweak)
361                 objmod.wkext(f.sym, null);
362         }
363     }
364 
365 if (config.exe & (EX_OSX | EX_OSX64))
366 {
367     Symbol *funcsymsave = funcsym_p;
368     funcsym_p = f.funcsym;
369     objmod.reftoident(f.seg, f.offset, f.sym, f.val, f.flags);
370     funcsym_p = funcsymsave;
371 }
372 else
373 {
374     objmod.reftoident(f.seg, f.offset, f.sym, f.val, f.flags);
375 }
376 }
377 
378 /****************************
379  * End of module. Output fixups as references
380  * to external Symbols.
381  */
382 @trusted
383 void outfixlist()
384 {
385     foreach (ref f; fixups)
386         outfixup(f);
387     fixups.reset();
388 }