1 /**
2  * Intermediate representation for static data
3  *
4  * Compiler implementation of the
5  * $(LINK2 https://www.dlang.org, D programming language).
6  *
7  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
8  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
9  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
10  * Source:      https://github.com/dlang/dmd/blob/master/src/dmd/backend/dt.d
11  * Documentation:  https://dlang.org/phobos/dmd_backend_dt.html
12  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/dt.d
13  */
14 
15 module dmd.backend.dt;
16 
17 import core.stdc.stdio;
18 import core.stdc.stdlib;
19 import core.stdc.string;
20 
21 import dmd.backend.cc;
22 import dmd.backend.cdef;
23 import dmd.backend.global;
24 import dmd.backend.mem;
25 import dmd.backend.ty;
26 import dmd.backend.type;
27 
28 nothrow:
29 @nogc:
30 @safe:
31 
32 extern (C++):
33 
34 /**********************************************
35  * Free a data definition struct.
36  */
37 
38 @trusted
39 void dt_free(dt_t *dt)
40 {
41     if (dt)
42     {
43         dt_t *dtn = dt;
44         while (1)
45         {
46             switch (dtn.dt)
47             {
48                 case DT_abytes:
49                 case DT_nbytes:
50                     mem_free(dtn.DTpbytes);
51                     break;
52 
53                 default:
54                     break;
55             }
56             dt_t *dtnext = dtn.DTnext;
57             if (!dtnext)
58                 break;
59             dtn = dtnext;
60         }
61         dtn.DTnext = dt_freelist;
62         dt_freelist = dt;
63     }
64 }
65 
66 /*********************************
67  * Free free list.
68  */
69 
70 void dt_term()
71 {
72 static if (0 && TERMCODE)
73 {
74     dt_t *dtn;
75 
76     while (dt_freelist)
77     {   dtn = dt_freelist.DTnext;
78         mem_ffree(dt_freelist);
79         dt_freelist = dtn;
80     }
81 }
82 }
83 
84 dt_t **dtend(dt_t **pdtend)
85 {
86     while (*pdtend)
87         pdtend = &((*pdtend).DTnext);
88     return pdtend;
89 }
90 
91 
92 /*********************************
93  */
94 void dtpatchoffset(dt_t *dt, uint offset)
95 {
96     dt.DToffset = offset;
97 }
98 
99 /**************************
100  * Make a common block for s.
101  */
102 
103 @trusted
104 void init_common(Symbol *s)
105 {
106     //printf("init_common('%s')\n", s.Sident);
107 
108     uint size = cast(uint)type_size(s.Stype);
109     if (size)
110     {
111         dt_t *dt = dt_calloc(DT_common);
112         dt.DTazeros = size;
113         s.Sdt = dt;
114     }
115 }
116 
117 /**********************************
118  * Compute size of a dt
119  */
120 
121 @trusted
122 uint dt_size(const(dt_t)* dtstart)
123 {
124     uint datasize = 0;
125     for (auto dt = dtstart; dt; dt = dt.DTnext)
126     {
127         switch (dt.dt)
128         {
129             case DT_abytes:
130                 datasize += size(dt.Dty);
131                 break;
132             case DT_ibytes:
133                 datasize += dt.DTn;
134                 break;
135             case DT_nbytes:
136                 datasize += dt.DTnbytes;
137                 break;
138             case DT_azeros:
139                 datasize += dt.DTazeros;
140                 break;
141             case DT_common:
142                 break;
143             case DT_xoff:
144             case DT_coff:
145                 datasize += size(dt.Dty);
146                 break;
147             default:
148                 debug printf("dt = %p, dt = %d\n",dt,dt.dt);
149                 assert(0);
150         }
151     }
152     return datasize;
153 }
154 
155 /************************************
156  * Return true if dt is all zeros.
157  */
158 
159 bool dtallzeros(const(dt_t)* dt)
160 {
161     return dt && dt.dt == DT_azeros && !dt.DTnext;
162 }
163 
164 /************************************
165  * Return true if dt contains pointers (requires relocations).
166  */
167 
168 bool dtpointers(const(dt_t)* dtstart)
169 {
170     for (auto dt = dtstart; dt; dt = dt.DTnext)
171     {
172         switch (dt.dt)
173         {
174             case DT_abytes:
175             case DT_xoff:
176             case DT_coff:
177                 return true;
178 
179             default:
180                 break;
181         }
182     }
183     return false;
184 }
185 
186 /***********************************
187  * Turn DT_azeros into DTcommon
188  */
189 
190 void dt2common(dt_t **pdt)
191 {
192     assert((*pdt).dt == DT_azeros);
193     (*pdt).dt = DT_common;
194 }
195 
196 /**********************************************************/
197 
198 struct DtBuilder
199 {
200 private:
201 
202     dt_t* head;
203     dt_t** pTail;
204 
205 public:
206 nothrow:
207 @nogc:
208     @trusted
209     this(int dummy)
210     {
211         pTail = &head;
212     }
213 
214     /************************************
215      * Useful for checking if DtBuilder got initialized.
216      */
217     void checkInitialized()
218     {
219         if (!head)
220             assert(pTail == &head);
221     }
222 
223     /************************************
224      * Print state of DtBuilder for debugging.
225      */
226     void print() @trusted
227     {
228         debug printf("DtBuilder: %p head: %p, pTail: %p\n", &head, head, pTail);
229     }
230 
231     /*************************
232      * Finish and return completed data structure.
233      */
234     dt_t *finish()
235     {
236         /* Merge all the 0s at the start of the list
237          * so we can later check for dtallzeros()
238          */
239         if (head && head.dt == DT_azeros)
240         {
241             while (1)
242             {
243                 dt_t *dtn = head.DTnext;
244                 if (!(dtn && dtn.dt == DT_azeros))
245                     break;
246 
247                 // combine head and dtn
248                 head.DTazeros += dtn.DTazeros;
249                 head.DTnext = dtn.DTnext;
250                 dtn.DTnext = null;
251                 dt_free(dtn);
252             }
253         }
254 
255         return head;
256     }
257 
258     /***********************
259      * Append data represented by ptr[0..size]
260      */
261     @trusted
262     void nbytes(uint size, const(char)* ptr)
263     {
264         if (!size)
265             return;
266 
267         dt_t *dt;
268 
269         if (size < dt_t.DTibytesMax)
270         {   dt = dt_calloc(DT_ibytes);
271             dt.DTn = cast(ubyte)size;
272             memcpy(dt.DTdata.ptr,ptr,size);
273         }
274         else
275         {
276             dt = dt_calloc(DT_nbytes);
277             dt.DTnbytes = size;
278             dt.DTpbytes = cast(byte *) mem_malloc(size);
279             memcpy(dt.DTpbytes,ptr,size);
280         }
281 
282         assert(!*pTail);
283         *pTail = dt;
284         pTail = &dt.DTnext;
285         assert(!*pTail);
286     }
287 
288     /*****************************************
289      * Write a reference to the data ptr[0..size+nzeros]
290      * Params:
291      *  ty = pointer type
292      *  offset = to be added to offset of data generated
293      *  size = number of bytes pointed to by ptr
294      *  ptr = points to data bytes
295      *  nzeros = number of zero bytes to add to the end
296      *  _align = alignment of pointed-to data
297      */
298     @trusted
299     void abytes(tym_t ty, uint offset, uint size, const(char)* ptr, uint nzeros, ubyte _align)
300     {
301         dt_t *dt = dt_calloc(DT_abytes);
302         const n = size + nzeros;
303         assert(n >= size);      // overflow check
304         dt.DTnbytes = n;
305         dt.DTpbytes = cast(byte *) mem_malloc(n);
306         dt.Dty = cast(ubyte)ty;
307         dt.DTalign = _align;
308         dt.DTabytes = offset;
309         memcpy(dt.DTpbytes,ptr,size);
310         if (nzeros)
311             memset(dt.DTpbytes + size, 0, nzeros);
312 
313         assert(!*pTail);
314         *pTail = dt;
315         pTail = &dt.DTnext;
316         assert(!*pTail);
317     }
318 
319     void abytes(uint offset, uint size, const(char)* ptr, uint nzeros, ubyte _align)
320     {
321         abytes(TYnptr, offset, size, ptr, nzeros, _align);
322     }
323 
324     /**************************************
325      * Write 4 bytes of value.
326      */
327     @trusted
328     void dword(int value)
329     {
330         if (value == 0)
331         {
332             nzeros(4);
333             return;
334         }
335 
336         dt_t *dt = dt_calloc(DT_ibytes);
337         dt.DTn = 4;
338 
339         union U { char* cp; int* lp; }
340         U u = void;
341         u.cp = cast(char*)dt.DTdata.ptr;
342         *u.lp = value;
343 
344         assert(!*pTail);
345         *pTail = dt;
346         pTail = &dt.DTnext;
347         assert(!*pTail);
348     }
349 
350     /***********************
351      * Write a size_t value.
352      */
353     @trusted
354     void size(ulong value)
355     {
356         if (value == 0)
357         {
358             nzeros(_tysize[TYnptr]);
359             return;
360         }
361         dt_t *dt = dt_calloc(DT_ibytes);
362         dt.DTn = _tysize[TYnptr];
363 
364         union U { char* cp; int* lp; }
365         U u = void;
366         u.cp = cast(char*)dt.DTdata.ptr;
367         *u.lp = cast(int)value;
368         if (_tysize[TYnptr] == 8)
369             u.lp[1] = cast(int)(value >> 32);
370 
371         assert(!*pTail);
372         *pTail = dt;
373         pTail = &dt.DTnext;
374         assert(!*pTail);
375     }
376 
377     /***********************
378      * Write a bunch of zeros
379      */
380     void nzeros(uint size)
381     {
382         if (!size)
383             return;
384         assert(cast(int) size > 0);
385 
386         dt_t *dt = dt_calloc(DT_azeros);
387         dt.DTazeros = size;
388 
389         assert(!*pTail);
390         *pTail = dt;
391         pTail = &dt.DTnext;
392         assert(!*pTail);
393     }
394 
395     /*************************
396      * Write a reference to s+offset
397      */
398     @trusted
399     void xoff(Symbol *s, uint offset, tym_t ty)
400     {
401         dt_t *dt = dt_calloc(DT_xoff);
402         dt.DTsym = s;
403         dt.DToffset = offset;
404         dt.Dty = cast(ubyte)ty;
405 
406         assert(!*pTail);
407         *pTail = dt;
408         pTail = &dt.DTnext;
409         assert(!*pTail);
410     }
411 
412     /******************************
413      * Create reference to s+offset
414      */
415     void xoff(Symbol *s, uint offset)
416     {
417         xoff(s, offset, TYnptr);
418     }
419 
420     /*******************************
421      * Like xoff(), but returns handle with which to patch 'offset' value.
422      */
423     @trusted
424     dt_t *xoffpatch(Symbol *s, uint offset, tym_t ty)
425     {
426         dt_t *dt = dt_calloc(DT_xoff);
427         dt.DTsym = s;
428         dt.DToffset = offset;
429         dt.Dty = cast(ubyte)ty;
430 
431         dt_t **pxoff = pTail;
432 
433         assert(!*pTail);
434         *pTail = dt;
435         pTail = &dt.DTnext;
436         assert(!*pTail);
437 
438         return *pxoff;
439     }
440 
441     /*************************************
442      * Create a reference to another dt.
443      * Returns: the internal symbol used for the other dt
444      */
445     @trusted
446     Symbol *dtoff(dt_t *dt, uint offset)
447     {
448         type *t = type_alloc(TYint);
449         t.Tcount++;
450         Symbol *s = symbol_calloc("internal");
451         s.Sclass = SC.static_;
452         s.Sfl = FLextern;
453         s.Sflags |= SFLnodebug;
454         s.Stype = t;
455         s.Sdt = dt;
456         outdata(s);
457 
458         xoff(s, offset);
459         return s;
460     }
461 
462     /********************************
463      * Write reference to offset in code segment.
464      */
465     @trusted
466     void coff(uint offset)
467     {
468         dt_t *dt = dt_calloc(DT_coff);
469 
470         if (config.exe & EX_segmented)
471             dt.Dty = TYcptr;
472         else
473             dt.Dty = TYnptr;
474 
475         dt.DToffset = offset;
476 
477         assert(!*pTail);
478         *pTail = dt;
479         pTail = &dt.DTnext;
480         assert(!*pTail);
481     }
482 
483 
484     /**********************
485      * Append dt to data.
486      */
487     void cat(dt_t *dt)
488     {
489         assert(!*pTail);
490         *pTail = dt;
491         pTail = &dt.DTnext;
492         while (*pTail)
493             pTail = &((*pTail).DTnext);
494         assert(!*pTail);
495     }
496 
497     /**********************
498      * Append dtb to data.
499      */
500     void cat(ref DtBuilder dtb)
501     {
502         if (dtb.head) // if non-zero length
503         {
504             assert(!*pTail);
505             *pTail = dtb.head;
506             pTail = dtb.pTail; // if dtb is zero length, this will point pTail to dtb.head, oops
507             assert(!*pTail);
508         }
509     }
510 
511     /**************************************
512      * Repeat a list of dt_t's count times.
513      */
514     @trusted
515     void repeat(dt_t *dt, size_t count)
516     {
517         if (!count)
518             return;
519 
520         uint size = dt_size(dt);
521         if (!size)
522             return;
523 
524         if (dtallzeros(dt))
525         {
526             if (head && dtallzeros(head))
527                 head.DTazeros += size * count;
528             else
529                 nzeros(cast(uint)(size * count));
530             return;
531         }
532 
533         if (dtpointers(dt))
534         {
535             dt_t *dtp = null;
536             dt_t **pdt = &dtp;
537             for (size_t i = 0; i < count; ++i)
538             {
539                 for (dt_t *dtn = dt; dtn; dtn = dtn.DTnext)
540                 {
541                     dt_t *dtx = dt_calloc(dtn.dt);
542                     *dtx = *dtn;
543                     dtx.DTnext = null;
544                     switch (dtx.dt)
545                     {
546                         case DT_abytes:
547                         case DT_nbytes:
548                             dtx.DTpbytes = cast(byte *) mem_malloc(dtx.DTnbytes);
549                             memcpy(dtx.DTpbytes, dtn.DTpbytes, dtx.DTnbytes);
550                             break;
551 
552                         default:
553                             break;
554                     }
555 
556                     *pdt = dtx;
557                     pdt = &dtx.DTnext;
558                 }
559             }
560             assert(!*pTail);
561             *pTail = dtp;
562             assert(*pdt == null);
563             pTail = pdt;
564             return;
565         }
566 
567         const n = size * count;
568         assert(n >= size);
569         char *p = cast(char *)mem_malloc(n);
570         size_t offset = 0;
571 
572         for (dt_t *dtn = dt; dtn; dtn = dtn.DTnext)
573         {
574             switch (dtn.dt)
575             {
576                 case DT_nbytes:
577                     memcpy(p + offset, dtn.DTpbytes, dtn.DTnbytes);
578                     offset += dtn.DTnbytes;
579                     break;
580                 case DT_ibytes:
581                     memcpy(p + offset, dtn.DTdata.ptr, dtn.DTn);
582                     offset += dtn.DTn;
583                     break;
584                 case DT_azeros:
585                     memset(p + offset, 0, cast(uint)dtn.DTazeros);
586                     offset += dtn.DTazeros;
587                     break;
588                 default:
589                     debug printf("dt = %p, dt = %d\n",dt,dt.dt);
590                     assert(0);
591             }
592         }
593         assert(offset == size);
594 
595         for (size_t i = 1; i < count; ++i)
596         {
597             memcpy(p + offset, p, size);
598             offset += size;
599         }
600 
601         dt_t *dtx = dt_calloc(DT_nbytes);
602         dtx.DTnbytes = cast(uint)(size * count);
603         dtx.DTpbytes = cast(byte*)p;
604 
605 
606         assert(!*pTail);
607         *pTail = dtx;
608         pTail = &dtx.DTnext;
609         assert(!*pTail);
610     }
611 
612     /***************************
613      * Return size of data.
614      */
615     uint length()
616     {
617         return dt_size(head);
618     }
619 
620     /************************
621      * Return true if size of data is 0.
622      */
623     bool isZeroLength()
624     {
625         return head == null;
626     }
627 }
628 
629 private __gshared dt_t *dt_freelist;
630 
631 /**********************************************
632  * Allocate a data definition struct.
633  */
634 
635 @trusted
636 private dt_t *dt_calloc(int dtx)
637 {
638     dt_t *dt = dt_freelist;
639     if (!dt)
640     {
641         const size_t n = 4096 / dt_t.sizeof;
642         dt_t *chunk = cast(dt_t *)mem_fmalloc(n * dt_t.sizeof);
643         for (size_t i = 0; i < n - 1; ++i)
644         {
645             chunk[i].DTnext = &chunk[i + 1];
646         }
647         chunk[n - 1].DTnext = null;
648         dt_freelist = chunk;
649         dt = chunk;
650     }
651 
652     dt_freelist = dt.DTnext;
653     debug memset(dt, 0xBE, (*dt).sizeof);
654     dt.DTnext = null;
655     dt.dt = cast(char)dtx;
656     return dt;
657 }
658 
659 
660 /******************************************
661  * Temporary hack to initialize a dt_t* for C.
662  */
663 
664 dt_t* dt_get_nzeros(uint n)
665 {
666     dt_t *dt = dt_calloc(DT_azeros);
667     dt.DTazeros = n;
668     return dt;
669 }