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