1 /**
2  * CodeView 8 symbolic debug info generation
3  *
4  * This module generates the `.debug$S` and ``.debug$T` sections for Win64,
5  * which are the MS-Coff symbolic debug info and type debug info sections.
6  *
7  * Compiler implementation of the
8  * $(LINK2 https://www.dlang.org, D programming language).
9  *
10  * Copyright:    Copyright (C) 2012-2023 by The D Language Foundation, All Rights Reserved
11  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
12  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
13  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/cv8.d, backend/cv8.d)
14  */
15 
16 module dmd.backend.cv8;
17 
18 import core.stdc.stdio;
19 import core.stdc.stdlib;
20 import core.stdc.string;
21 extern (C) nothrow char* getcwd(char*, size_t);
22 
23 import dmd.backend.cc;
24 import dmd.backend.cdef;
25 import dmd.backend.cgcv;
26 import dmd.backend.code;
27 import dmd.backend.code_x86;
28 import dmd.backend.cv4;
29 import dmd.backend.mem;
30 import dmd.backend.el;
31 import dmd.backend.global;
32 import dmd.backend.mscoffobj;
33 import dmd.backend.obj;
34 import dmd.backend.oper;
35 import dmd.common.outbuffer;
36 import dmd.backend.rtlsym;
37 import dmd.backend.ty;
38 import dmd.backend.type;
39 import dmd.backend.dvarstats;
40 import dmd.backend.xmm;
41 
42 
43 nothrow:
44 @safe:
45 
46 static if (1)
47 {
48 
49 
50 // Determine if this Symbol is stored in a COMDAT
51 @trusted
52 private bool symbol_iscomdat4(Symbol* s)
53 {
54     return s.Sclass == SC.comdat ||
55         config.flags2 & CFG2comdat && s.Sclass == SC.inline ||
56         config.flags4 & CFG4allcomdat && s.Sclass == SC.global;
57 }
58 
59 
60 // if symbols get longer than 65500 bytes, the linker reports corrupt debug info or exits with
61 // 'fatal error LNK1318: Unexpected PDB error; RPC (23) '(0x000006BA)'
62 enum CV8_MAX_SYMBOL_LENGTH = 0xffd8;
63 
64 // The "F1" section, which is the symbols
65 private __gshared OutBuffer *F1_buf;
66 
67 // The "F2" section, which is the line numbers
68 private __gshared OutBuffer *F2_buf;
69 
70 // The "F3" section, which is global and a string table of source file names.
71 private __gshared OutBuffer *F3_buf;
72 
73 // The "F4" section, which is global and a lists info about source files.
74 private __gshared OutBuffer *F4_buf;
75 
76 /* Fixups that go into F1 section
77  */
78 struct F1_Fixups
79 {
80     Symbol *s;
81     uint offset;
82     uint value;
83 }
84 
85 private __gshared OutBuffer *F1fixup;      // array of F1_Fixups
86 
87 /* Struct in which to collect per-function data, for later emission
88  * into .debug$S.
89  */
90 struct FuncData
91 {
92     Symbol *sfunc;
93     uint section_length;
94     const(char)* srcfilename;
95     uint srcfileoff;
96     uint linepairstart;     // starting byte index of offset/line pairs in linebuf[]
97     uint linepairbytes;     // number of bytes for offset/line pairs
98     uint linepairsegment;   // starting byte index of filename segment for offset/line pairs
99     OutBuffer *f1buf;
100     OutBuffer *f1fixup;
101 }
102 
103 __gshared FuncData currentfuncdata;
104 
105 private __gshared OutBuffer *funcdata;     // array of FuncData's
106 
107 private __gshared OutBuffer *linepair;     // array of offset/line pairs
108 
109 private @trusted
110 void cv8_writename(OutBuffer *buf, const(char)* name, size_t len)
111 {
112     if(config.flags2 & CFG2gms)
113     {
114         const(char)* start = name;
115         const(char)* cur = strchr(start, '.');
116         const(char)* end = start + len;
117         while(cur != null)
118         {
119             if(cur >= end)
120             {
121                 buf.writen(start, end - start);
122                 return;
123             }
124             buf.writen(start, cur - start);
125             buf.writeByte('@');
126             start = cur + 1;
127             if(start >= end)
128                 return;
129             cur = strchr(start, '.');
130         }
131         buf.writen(start, end - start);
132     }
133     else
134         buf.writen(name, len);
135 }
136 
137 /************************************************
138  * Called at the start of an object file generation.
139  * One source file can generate multiple object files; this starts an object file.
140  * Input:
141  *      filename        source file name
142  */
143 @trusted
144 void cv8_initfile(const(char)* filename)
145 {
146     //printf("cv8_initfile()\n");
147 
148     // Recycle buffers; much faster than delete/renew
149 
150     if (!F1_buf)
151     {
152         __gshared OutBuffer f1buf;
153         f1buf.reserve(1024);
154         F1_buf = &f1buf;
155     }
156     F1_buf.reset();
157 
158     if (!F1fixup)
159     {
160         __gshared OutBuffer f1fixupbuf;
161         f1fixupbuf.reserve(1024);
162         F1fixup = &f1fixupbuf;
163     }
164     F1fixup.reset();
165 
166     if (!F2_buf)
167     {
168         __gshared OutBuffer f2buf;
169         f2buf.reserve(1024);
170         F2_buf = &f2buf;
171     }
172     F2_buf.reset();
173 
174     if (!F3_buf)
175     {
176         __gshared OutBuffer f3buf;
177         f3buf.reserve(1024);
178         F3_buf = &f3buf;
179     }
180     F3_buf.reset();
181     F3_buf.writeByte(0);       // first "filename"
182 
183     if (!F4_buf)
184     {
185         __gshared OutBuffer f4buf;
186         f4buf.reserve(1024);
187         F4_buf = &f4buf;
188     }
189     F4_buf.reset();
190 
191     if (!funcdata)
192     {
193         __gshared OutBuffer funcdatabuf;
194         funcdatabuf.reserve(1024);
195         funcdata = &funcdatabuf;
196     }
197     funcdata.reset();
198 
199     if (!linepair)
200     {
201         __gshared OutBuffer linepairbuf;
202         linepairbuf.reserve(1024);
203         linepair = &linepairbuf;
204     }
205     linepair.reset();
206 
207     memset(&currentfuncdata, 0, currentfuncdata.sizeof);
208     currentfuncdata.f1buf = F1_buf;
209     currentfuncdata.f1fixup = F1fixup;
210 
211     cv_init();
212 }
213 
214 @trusted
215 void cv8_termfile(const(char)* objfilename)
216 {
217     //printf("cv8_termfile()\n");
218 
219     /* Write out the debug info sections.
220      */
221 
222     int seg = MsCoffObj_seg_debugS();
223 
224     uint value = 4;
225     objmod.bytes(seg,0,4,&value);
226 
227     /* Start with starting symbol in separate "F1" section
228      */
229     auto buf = OutBuffer(1024);
230     size_t len = strlen(objfilename);
231     buf.write16(cast(int)(2 + 4 + len + 1));
232     buf.write16(S_COMPILAND_V3);
233     buf.write32(0);
234     buf.write(objfilename, cast(uint)(len + 1));
235 
236     // write S_COMPILE record
237     buf.write16(2 + 1 + 1 + 2 + 1 + VERSION.length + 1);
238     buf.write16(S_COMPILE);
239     buf.writeByte(I64 ? 0xD0 : 6); // target machine AMD64 or x86 (Pentium II)
240     buf.writeByte(config.flags2 & CFG2gms ? (CPP != 0) : 'D'); // language index (C/C++/D)
241     buf.write16(0x800 | (config.inline8087 ? 0 : (1<<3)));   // 32-bit, float package
242     buf.writeByte(VERSION.length + 1);
243     buf.writeByte('Z');
244     buf.write(VERSION.ptr, VERSION.length);
245 
246     cv8_writesection(seg, 0xF1, &buf);
247 
248     // Write out "F2" sections
249     uint length = cast(uint)funcdata.length();
250     ubyte *p = funcdata.buf;
251     for (uint u = 0; u < length; u += FuncData.sizeof)
252     {   FuncData *fd = cast(FuncData *)(p + u);
253 
254         F2_buf.reset();
255 
256         F2_buf.write32(cast(uint)fd.sfunc.Soffset);
257         F2_buf.write32(0);
258         F2_buf.write32(fd.section_length);
259         F2_buf.write(linepair.buf + fd.linepairstart, fd.linepairbytes);
260 
261         int f2seg = seg;
262         if (symbol_iscomdat4(fd.sfunc))
263         {
264             f2seg = MsCoffObj_seg_debugS_comdat(fd.sfunc);
265             objmod.bytes(f2seg, 0, 4, &value);
266         }
267 
268         uint offset = cast(uint)SegData[f2seg].SDoffset + 8;
269         cv8_writesection(f2seg, 0xF2, F2_buf);
270         objmod.reftoident(f2seg, offset, fd.sfunc, 0, CFseg | CFoff);
271 
272         if (f2seg != seg && fd.f1buf.length())
273         {
274             // Write out "F1" section
275             const uint f1offset = cast(uint)SegData[f2seg].SDoffset;
276             cv8_writesection(f2seg, 0xF1, fd.f1buf);
277 
278             // Fixups for "F1" section
279             const uint fixupLength = cast(uint)fd.f1fixup.length();
280             ubyte *pfixup = fd.f1fixup.buf;
281             for (uint v = 0; v < fixupLength; v += F1_Fixups.sizeof)
282             {   F1_Fixups *f = cast(F1_Fixups *)(pfixup + v);
283 
284                 objmod.reftoident(f2seg, f1offset + 8 + f.offset, f.s, f.value, CFseg | CFoff);
285             }
286         }
287     }
288 
289     // Write out "F3" section
290     if (F3_buf.length() > 1)
291         cv8_writesection(seg, 0xF3, F3_buf);
292 
293     // Write out "F4" section
294     if (F4_buf.length() > 0)
295         cv8_writesection(seg, 0xF4, F4_buf);
296 
297     if (F1_buf.length())
298     {
299         // Write out "F1" section
300         uint f1offset = cast(uint)SegData[seg].SDoffset;
301         cv8_writesection(seg, 0xF1, F1_buf);
302 
303         // Fixups for "F1" section
304         length = cast(uint)F1fixup.length();
305         p = F1fixup.buf;
306         for (uint u = 0; u < length; u += F1_Fixups.sizeof)
307         {   F1_Fixups *f = cast(F1_Fixups *)(p + u);
308 
309             objmod.reftoident(seg, f1offset + 8 + f.offset, f.s, f.value, CFseg | CFoff);
310         }
311     }
312 
313     // Write out .debug$T section
314     cv_term();
315 }
316 
317 /************************************************
318  * Called at the start of a module.
319  * Note that there can be multiple modules in one object file.
320  * cv8_initfile() must be called first.
321  */
322 void cv8_initmodule(const(char)* filename, const(char)* modulename)
323 {
324     //printf("cv8_initmodule(filename = %s, modulename = %s)\n", filename, modulename);
325 }
326 
327 @trusted
328 void cv8_termmodule()
329 {
330     //printf("cv8_termmodule()\n");
331     assert(config.objfmt == OBJ_MSCOFF);
332 }
333 
334 /******************************************
335  * Called at the start of a function.
336  */
337 @trusted
338 void cv8_func_start(Symbol *sfunc)
339 {
340     //printf("cv8_func_start(%s)\n", sfunc.Sident);
341     currentfuncdata.sfunc = sfunc;
342     currentfuncdata.section_length = 0;
343     currentfuncdata.srcfilename = null;
344     currentfuncdata.linepairstart += currentfuncdata.linepairbytes;
345     currentfuncdata.linepairbytes = 0;
346     currentfuncdata.f1buf = F1_buf;
347     currentfuncdata.f1fixup = F1fixup;
348     if (symbol_iscomdat4(sfunc))
349     {
350         // This leaks memory
351         currentfuncdata.f1buf = cast(OutBuffer*)mem_calloc(OutBuffer.sizeof);
352         currentfuncdata.f1buf.reserve(128);
353         currentfuncdata.f1fixup = cast(OutBuffer*)mem_calloc(OutBuffer.sizeof);
354         currentfuncdata.f1fixup.reserve(128);
355     }
356 
357     varStats_startFunction();
358 }
359 
360 @trusted
361 void cv8_func_term(Symbol *sfunc)
362 {
363     //printf("cv8_func_term(%s)\n", sfunc.Sident);
364 
365     assert(currentfuncdata.sfunc == sfunc);
366     currentfuncdata.section_length = cast(uint)sfunc.Ssize;
367 
368     funcdata.write(&currentfuncdata, currentfuncdata.sizeof);
369 
370     // Write function symbol
371     assert(tyfunc(sfunc.ty()));
372     idx_t typidx;
373     func_t* fn = sfunc.Sfunc;
374     if(fn.Fclass)
375     {
376         // generate member function type info
377         // it would be nicer if this could be in cv4_typidx, but the function info is not available there
378         uint nparam;
379         ubyte call = cv4_callconv(sfunc.Stype);
380         idx_t paramidx = cv4_arglist(sfunc.Stype,&nparam);
381         uint next = cv4_typidx(sfunc.Stype.Tnext);
382 
383         type* classtype = cast(type*)fn.Fclass;
384         uint classidx = cv4_typidx(classtype);
385         type *tp = type_allocn(TYnptr, classtype);
386         uint thisidx = cv4_typidx(tp);  // TODO
387         debtyp_t *d = debtyp_alloc(2 + 4 + 4 + 4 + 1 + 1 + 2 + 4 + 4);
388         TOWORD(d.data.ptr,LF_MFUNCTION_V2);
389         TOLONG(d.data.ptr + 2,next);       // return type
390         TOLONG(d.data.ptr + 6,classidx);   // class type
391         TOLONG(d.data.ptr + 10,thisidx);   // this type
392         d.data.ptr[14] = call;
393         d.data.ptr[15] = 0;                // reserved
394         TOWORD(d.data.ptr + 16,nparam);
395         TOLONG(d.data.ptr + 18,paramidx);
396         TOLONG(d.data.ptr + 22,0);  // this adjust
397         typidx = cv_debtyp(d);
398     }
399     else
400         typidx = cv_typidx(sfunc.Stype);
401 
402     const(char)* id = sfunc.prettyIdent ? sfunc.prettyIdent : prettyident(sfunc);
403 
404     size_t len = strlen(id);
405     if(len > CV8_MAX_SYMBOL_LENGTH)
406         len = CV8_MAX_SYMBOL_LENGTH;
407     /*
408      *  2       length (not including these 2 bytes)
409      *  2       S_GPROC_V3
410      *  4       parent
411      *  4       pend
412      *  4       pnext
413      *  4       size of function
414      *  4       size of function prolog
415      *  4       offset to function epilog
416      *  4       type index
417      *  6       seg:offset of function start
418      *  1       flags
419      *  n       0 terminated name string
420      */
421     auto buf = currentfuncdata.f1buf;
422     buf.reserve(cast(uint)(2 + 2 + 4 * 7 + 6 + 1 + len + 1));
423     buf.write16n(cast(int)(2 + 4 * 7 + 6 + 1 + len + 1));
424     buf.write16n(sfunc.Sclass == SC.static_ ? S_LPROC_V3 : S_GPROC_V3);
425     buf.write32(0);            // parent
426     buf.write32(0);            // pend
427     buf.write32(0);            // pnext
428     buf.write32(cast(uint)currentfuncdata.section_length); // size of function
429     buf.write32(cast(uint)startoffset);                    // size of prolog
430     buf.write32(cast(uint)retoffset);                      // offset to epilog
431     buf.write32(typidx);
432 
433     F1_Fixups f1f;
434     f1f.s = sfunc;
435     f1f.offset = cast(uint)buf.length();
436     f1f.value = 0;
437     currentfuncdata.f1fixup.write(&f1f, f1f.sizeof);
438     buf.write32(0);
439     buf.write16n(0);
440 
441     buf.writeByte(0);
442     buf.writen(id, len);
443     buf.writeByte(0);
444 
445     struct cv8
446     {
447     nothrow:
448         // record for CV record S_BLOCK_V3
449         struct block_v3_data
450         {
451             ushort len;
452             ushort id;
453             uint pParent;
454             uint pEnd;
455             uint length;
456             uint offset;
457             ushort seg;
458             ubyte[1] name;
459         }
460 
461         static void endArgs()
462         {
463             auto buf = currentfuncdata.f1buf;
464             buf.write16(2);
465             buf.write16(S_ENDARG);
466         }
467         static void beginBlock(int offset, int length)
468         {
469             auto buf = currentfuncdata.f1buf;
470             uint soffset = cast(uint)buf.length();
471             // parent and end to be filled by linker
472             block_v3_data block32 = { block_v3_data.sizeof - 2, S_BLOCK_V3, 0, 0, length, offset, 0, [ 0 ] };
473             buf.write(&block32, block32.sizeof);
474             size_t offOffset = cast(char*)&block32.offset - cast(char*)&block32;
475 
476             F1_Fixups f1f;
477             f1f.s = currentfuncdata.sfunc;
478             f1f.offset = cast(uint)(soffset + offOffset);
479             f1f.value = offset;
480             currentfuncdata.f1fixup.write(&f1f, f1f.sizeof);
481         }
482         static void endBlock()
483         {
484             auto buf = currentfuncdata.f1buf;
485             buf.write16(2);
486             buf.write16(S_END);
487         }
488     }
489     varStats_writeSymbolTable(globsym, &cv8_outsym, &cv8.endArgs, &cv8.beginBlock, &cv8.endBlock);
490 
491     /* Put out function return record S_RETURN
492      * (VC doesn't, so we won't bother, either.)
493      */
494 
495     // Write function end symbol
496     buf.write16(2);
497     buf.write16(S_END);
498 
499     currentfuncdata.f1buf = F1_buf;
500     currentfuncdata.f1fixup = F1fixup;
501 }
502 
503 /**********************************************
504  */
505 
506 @trusted
507 void cv8_linnum(Srcpos srcpos, uint offset)
508 {
509     const sfilename = srcpos.Sfilename;
510     //printf("cv8_linnum(file = %s, line = %d, offset = x%x)\n", sfilename, cast(int)srcpos.Slinnum, cast(uint)offset);
511 
512     if (!sfilename)
513         return;
514 
515     varStats_recordLineOffset(srcpos, offset);
516 
517     __gshared uint lastoffset;
518     __gshared uint lastlinnum;
519 
520     if (!currentfuncdata.srcfilename ||
521         (currentfuncdata.srcfilename != sfilename && strcmp(currentfuncdata.srcfilename, sfilename)))
522     {
523         currentfuncdata.srcfilename = sfilename;
524         uint srcfileoff = cv8_addfile(sfilename);
525 
526         // new file segment
527         currentfuncdata.linepairsegment = currentfuncdata.linepairstart + currentfuncdata.linepairbytes;
528 
529         linepair.write32(srcfileoff);
530         linepair.write32(0); // reserve space for length information
531         linepair.write32(12);
532         currentfuncdata.linepairbytes += 12;
533     }
534     else if (offset <= lastoffset || srcpos.Slinnum == lastlinnum)
535         return; // avoid multiple entries for the same offset
536 
537     lastoffset = offset;
538     lastlinnum = srcpos.Slinnum;
539     linepair.write32(offset);
540     linepair.write32(srcpos.Slinnum | 0x80000000); // mark as statement, not expression
541 
542     currentfuncdata.linepairbytes += 8;
543 
544     // update segment length
545     auto segmentbytes = currentfuncdata.linepairstart + currentfuncdata.linepairbytes - currentfuncdata.linepairsegment;
546     auto segmentheader = cast(uint*)(linepair.buf + currentfuncdata.linepairsegment);
547     segmentheader[1] = (segmentbytes - 12) / 8;
548     segmentheader[2] = segmentbytes;
549 }
550 
551 /**********************************************
552  * Add source file, if it isn't already there.
553  * Return offset into F4.
554  */
555 
556 @trusted
557 uint cv8_addfile(const(char)* filename)
558 {
559     //printf("cv8_addfile('%s')\n", filename);
560 
561     /* The algorithms here use a linear search. This is acceptable only
562      * because we expect only 1 or 2 files to appear.
563      * Unlike C, there won't be lots of .h source files to be accounted for.
564      */
565 
566     uint length = cast(uint)F3_buf.length();
567     ubyte *p = F3_buf.buf;
568     size_t len = strlen(filename);
569 
570     // ensure the filename is absolute to help the debugger to find the source
571     // without having to know the working directory during compilation
572     __gshared char[260] cwd = 0;
573     __gshared uint cwdlen;
574     bool abs = (*filename == '\\') ||
575                (*filename == '/')  ||
576                (*filename && filename[1] == ':');
577 
578     if (!abs && cwd[0] == 0)
579     {
580         if (getcwd(cwd.ptr, cwd.sizeof))
581         {
582             cwdlen = cast(uint)strlen(cwd.ptr);
583             if(cwd[cwdlen - 1] != '\\' && cwd[cwdlen - 1] != '/')
584                 cwd[cwdlen++] = '\\';
585         }
586     }
587     uint off = 1;
588     while (off + len < length)
589     {
590         if (!abs)
591         {
592             if (memcmp(p + off, cwd.ptr, cwdlen) == 0 &&
593                 memcmp(p + off + cwdlen, filename, len + 1) == 0)
594                 goto L1;
595         }
596         else if (memcmp(p + off, filename, len + 1) == 0)
597         {   // Already there
598             //printf("\talready there at %x\n", off);
599             goto L1;
600         }
601         off += strlen(cast(const(char)* )(p + off)) + 1;
602     }
603     off = length;
604     // Add it
605     if(!abs)
606         F3_buf.write(cwd.ptr, cwdlen);
607     F3_buf.write(filename, cast(uint)(len + 1));
608 
609 L1:
610     // off is the offset of the filename in F3.
611     // Find it in F4.
612 
613     length = cast(uint)F4_buf.length();
614     p = F4_buf.buf;
615 
616     uint u = 0;
617     while (u + 8 <= length)
618     {
619         //printf("\t%x\n", *cast(uint *)(p + u));
620         if (off == *cast(uint *)(p + u))
621         {
622             //printf("\tfound %x\n", u);
623             return u;
624         }
625         u += 4;
626         ushort type = *cast(ushort *)(p + u);
627         u += 2;
628         if (type == 0x0110)
629             u += 16;            // MD5 checksum
630         u += 2;
631     }
632 
633     // Not there. Add it.
634     F4_buf.write32(off);
635 
636     /* Write 10 01 [MD5 checksum]
637      *   or
638      * 00 00
639      */
640     F4_buf.write16(0);
641 
642     // 2 bytes of pad
643     F4_buf.write16(0);
644 
645     //printf("\tadded %x\n", length);
646     return length;
647 }
648 
649 private @trusted
650 void cv8_writesection(int seg, uint type, OutBuffer *buf)
651 {
652     /* Write out as:
653      *  bytes   desc
654      *  -------+----
655      *  4       type
656      *  4       length
657      *  length  data
658      *  pad     pad to 4 byte boundary
659      */
660     uint off = cast(uint)SegData[seg].SDoffset;
661     objmod.bytes(seg,off,4,&type);
662     uint length = cast(uint)buf.length();
663     objmod.bytes(seg,off+4,4,&length);
664     objmod.bytes(seg,off+8,length,buf.buf);
665     // Align to 4
666     uint pad = ((length + 3) & ~3) - length;
667     objmod.lidata(seg,off+8+length,pad);
668 }
669 
670 @trusted
671 void cv8_outsym(Symbol *s)
672 {
673     //printf("cv8_outsym(s = '%s')\n", s.Sident);
674     //type_print(s.Stype);
675     //symbol_print(s);
676     if (s.Sflags & SFLnodebug)
677         return;
678 
679     idx_t typidx = cv_typidx(s.Stype);
680     //printf("typidx = %x\n", typidx);
681     const(char)* id = s.prettyIdent ? s.prettyIdent : prettyident(s);
682     size_t len = strlen(id);
683 
684     if(len > CV8_MAX_SYMBOL_LENGTH)
685         len = CV8_MAX_SYMBOL_LENGTH;
686 
687     F1_Fixups f1f;
688     f1f.value = 0;
689     auto buf = currentfuncdata.f1buf;
690 
691     uint sr;
692     uint base;
693     switch (s.Sclass)
694     {
695         case SC.parameter:
696         case SC.regpar:
697         case SC.shadowreg:
698             if (s.Sfl == FLreg)
699             {
700                 s.Sfl = FLpara;
701                 cv8_outsym(s);
702                 s.Sfl = FLreg;
703                 goto case_register;
704             }
705             base = cast(uint)(Para.size - BPoff);    // cancel out add of BPoff
706             goto L1;
707 
708         case SC.auto_:
709             if (s.Sfl == FLreg)
710                 goto case_register;
711         case_auto:
712             base = cast(uint)Auto.size;
713         L1:
714             if (s.Sscope) // local variables moved into the closure cannot be emitted directly
715                 break;
716 static if (1)
717 {
718             // Register relative addressing
719             buf.reserve(cast(uint)(2 + 2 + 4 + 4 + 2 + len + 1));
720             buf.write16n(cast(uint)(2 + 4 + 4 + 2 + len + 1));
721             buf.write16n(0x1111);
722             buf.write32(cast(uint)(s.Soffset + base + BPoff));
723             buf.write32(typidx);
724             buf.write16n(I64 ? 334 : 22);       // relative to RBP/EBP
725             cv8_writename(buf, id, len);
726             buf.writeByte(0);
727 }
728 else
729 {
730             // This is supposed to work, implicit BP relative addressing, but it does not
731             buf.reserve(2 + 2 + 4 + 4 + len + 1);
732             buf.write16n( 2 + 4 + 4 + len + 1);
733             buf.write16n(S_BPREL_V3);
734             buf.write32(s.Soffset + base + BPoff);
735             buf.write32(typidx);
736             cv8_writename(buf, id, len);
737             buf.writeByte(0);
738 }
739             break;
740 
741         case SC.bprel:
742             base = -BPoff;
743             goto L1;
744 
745         case SC.fastpar:
746             if (s.Sfl != FLreg)
747             {   base = cast(uint)Fast.size;
748                 goto L1;
749             }
750             goto L2;
751 
752         case SC.register:
753             if (s.Sfl != FLreg)
754                 goto case_auto;
755             goto case;
756 
757         case SC.pseudo:
758         case_register:
759         L2:
760             buf.reserve(cast(uint)(2 + 2 + 4 + 2 + len + 1));
761             buf.write16n(cast(uint)(2 + 4 + 2 + len + 1));
762             buf.write16n(S_REGISTER_V3);
763             buf.write32(typidx);
764             buf.write16n(cv8_regnum(s));
765             cv8_writename(buf, id, len);
766             buf.writeByte(0);
767             break;
768 
769         case SC.extern_:
770             break;
771 
772         case SC.static_:
773         case SC.locstat:
774             sr = S_LDATA_V3;
775             goto Ldata;
776 
777         case SC.global:
778         case SC.comdat:
779         case SC.comdef:
780             sr = S_GDATA_V3;
781         Ldata:
782             /*
783              *  2       length (not including these 2 bytes)
784              *  2       S_GDATA_V2
785              *  4       typidx
786              *  6       ref to symbol
787              *  n       0 terminated name string
788              */
789             if (s.ty() & mTYthread)            // thread local storage
790                 sr = (sr == S_GDATA_V3) ? 0x1113 : 0x1112;
791 
792             buf.reserve(cast(uint)(2 + 2 + 4 + 6 + len + 1));
793             buf.write16n(cast(uint)(2 + 4 + 6 + len + 1));
794             buf.write16n(sr);
795             buf.write32(typidx);
796 
797             f1f.s = s;
798             f1f.offset = cast(uint)buf.length();
799             F1fixup.write(&f1f, f1f.sizeof);
800             buf.write32(0);
801             buf.write16n(0);
802 
803             cv8_writename(buf, id, len);
804             buf.writeByte(0);
805             break;
806 
807         default:
808             break;
809     }
810 }
811 
812 
813 /*******************************************
814  * Put out a name for a user defined type.
815  * Input:
816  *      id      the name
817  *      typidx  and its type
818  */
819 @trusted
820 void cv8_udt(const(char)* id, idx_t typidx)
821 {
822     //printf("cv8_udt('%s', %x)\n", id, typidx);
823     auto buf = currentfuncdata.f1buf;
824     size_t len = strlen(id);
825 
826     if (len > CV8_MAX_SYMBOL_LENGTH)
827         len = CV8_MAX_SYMBOL_LENGTH;
828     buf.reserve(cast(uint)(2 + 2 + 4 + len + 1));
829     buf.write16n(cast(uint)(2 + 4 + len + 1));
830     buf.write16n(S_UDT_V3);
831     buf.write32(typidx);
832     cv8_writename(buf, id, len);
833     buf.writeByte(0);
834 }
835 
836 /*********************************************
837  * Get Codeview register number for symbol s.
838  */
839 int cv8_regnum(Symbol *s)
840 {
841     int reg = s.Sreglsw;
842     assert(s.Sfl == FLreg);
843     if ((1 << reg) & XMMREGS)
844         return reg - XMM0 + 154;
845     switch (type_size(s.Stype))
846     {
847         case 1:
848             if (reg < 4)
849                 reg += 1;
850             else if (reg >= 4 && reg < 8)
851                 reg += 324 - 4;
852             else
853                 reg += 344 - 4;
854             break;
855 
856         case 2:
857             if (reg < 8)
858                 reg += 9;
859             else
860                 reg += 352 - 8;
861             break;
862 
863         case 4:
864             if (reg < 8)
865                 reg += 17;
866             else
867                 reg += 360 - 8;
868             break;
869 
870         case 8:
871             reg += 328;
872             break;
873 
874         default:
875             reg = 0;
876             break;
877     }
878     return reg;
879 }
880 
881 /***************************************
882  * Put out a forward ref for structs, unions, and classes.
883  * Only put out the real definitions with toDebug().
884  */
885 @trusted
886 idx_t cv8_fwdref(Symbol *s)
887 {
888     assert(config.fulltypes == CV8);
889 //    if (s.Stypidx && !global.params.multiobj)
890 //      return s.Stypidx;
891     struct_t *st = s.Sstruct;
892     uint leaf;
893     uint numidx;
894     if (st.Sflags & STRunion)
895     {
896         leaf = LF_UNION_V3;
897         numidx = 10;
898     }
899     else if (st.Sflags & STRclass)
900     {
901         leaf = LF_CLASS_V3;
902         numidx = 18;
903     }
904     else
905     {
906         leaf = LF_STRUCTURE_V3;
907         numidx = 18;
908     }
909     uint len = numidx + cv4_numericbytes(0);
910     int idlen = cast(int)strlen(s.Sident.ptr);
911 
912     if (idlen > CV8_MAX_SYMBOL_LENGTH)
913         idlen = CV8_MAX_SYMBOL_LENGTH;
914 
915     debtyp_t *d = debtyp_alloc(len + idlen + 1);
916     TOWORD(d.data.ptr, leaf);
917     TOWORD(d.data.ptr + 2, 0);     // number of fields
918     TOWORD(d.data.ptr + 4, 0x80);  // property
919     TOLONG(d.data.ptr + 6, 0);     // field list
920     if (leaf == LF_CLASS_V3 || leaf == LF_STRUCTURE_V3)
921     {
922         TOLONG(d.data.ptr + 10, 0);        // dList
923         TOLONG(d.data.ptr + 14, 0);        // vshape
924     }
925     cv4_storenumeric(d.data.ptr + numidx, 0);
926     cv_namestring(d.data.ptr + len, s.Sident.ptr, idlen);
927     d.data.ptr[len + idlen] = 0;
928     idx_t typidx = cv_debtyp(d);
929     s.Stypidx = typidx;
930 
931     return typidx;
932 }
933 
934 /****************************************
935  * Return type index for a darray of type E[]
936  * Input:
937  *      t       darray type
938  *      etypidx type index for E
939  */
940 @trusted
941 idx_t cv8_darray(type *t, idx_t etypidx)
942 {
943     //printf("cv8_darray(etypidx = %x)\n", etypidx);
944     /* Put out a struct:
945      *    struct dArray {
946      *      size_t length;
947      *      E* ptr;
948      *    }
949      */
950 
951 static if (0)
952 {
953     d = debtyp_alloc(18);
954     TOWORD(d.data.ptr, 0x100F);
955     TOWORD(d.data.ptr + 2, OEM);
956     TOWORD(d.data.ptr + 4, 1);     // 1 = dynamic array
957     TOLONG(d.data.ptr + 6, 2);     // count of type indices to follow
958     TOLONG(d.data.ptr + 10, 0x23); // index type, T_UQUAD
959     TOLONG(d.data.ptr + 14, next); // element type
960     return cv_debtyp(d);
961 }
962 
963     type *tp = type_pointer(t.Tnext);
964     idx_t ptridx = cv4_typidx(tp);
965     type_free(tp);
966 
967     __gshared const ubyte[38] fl =
968     [
969         0x03, 0x12,             // LF_FIELDLIST_V2
970         0x0d, 0x15,             // LF_MEMBER_V3
971         0x03, 0x00,             // attribute
972         0x23, 0x00, 0x00, 0x00, // size_t
973         0x00, 0x00,             // offset
974         'l', 'e', 'n', 'g', 't', 'h', 0x00,
975         0xf3, 0xf2, 0xf1,       // align to 4-byte including length word before data
976         0x0d, 0x15,
977         0x03, 0x00,
978         0x00, 0x00, 0x00, 0x00, // etypidx
979         0x08, 0x00,
980         'p', 't', 'r', 0x00,
981         0xf2, 0xf1,
982     ];
983 
984     debtyp_t *f = debtyp_alloc(fl.sizeof);
985     memcpy(f.data.ptr,fl.ptr,fl.sizeof);
986     TOLONG(f.data.ptr + 6, I64 ? 0x23 : 0x22); // size_t
987     TOLONG(f.data.ptr + 26, ptridx);
988     TOWORD(f.data.ptr + 30, _tysize[TYnptr]);
989     idx_t fieldlist = cv_debtyp(f);
990 
991     const(char)* id;
992     switch (t.Tnext.Tty)
993     {
994         case mTYimmutable | TYchar:
995             id = "string";
996             break;
997 
998         case mTYimmutable | TYwchar_t:
999             id = "wstring";
1000             break;
1001 
1002         case mTYimmutable | TYdchar:
1003             id = "dstring";
1004             break;
1005 
1006         default:
1007             id = t.Tident ? t.Tident : "dArray";
1008             break;
1009     }
1010 
1011     int idlen = cast(int)strlen(id);
1012 
1013     if (idlen > CV8_MAX_SYMBOL_LENGTH)
1014         idlen = CV8_MAX_SYMBOL_LENGTH;
1015 
1016     debtyp_t *d = debtyp_alloc(20 + idlen + 1);
1017     TOWORD(d.data.ptr, LF_STRUCTURE_V3);
1018     TOWORD(d.data.ptr + 2, 2);     // count
1019     TOWORD(d.data.ptr + 4, 0);     // property
1020     TOLONG(d.data.ptr + 6, fieldlist);
1021     TOLONG(d.data.ptr + 10, 0);    // dList
1022     TOLONG(d.data.ptr + 14, 0);    // vtshape
1023     TOWORD(d.data.ptr + 18, 2 * _tysize[TYnptr]);   // size
1024     cv_namestring(d.data.ptr + 20, id, idlen);
1025     d.data.ptr[20 + idlen] = 0;
1026 
1027     idx_t top = cv_numdebtypes();
1028     idx_t debidx = cv_debtyp(d);
1029     if(top != cv_numdebtypes())
1030         cv8_udt(id, debidx);
1031 
1032     return debidx;
1033 }
1034 
1035 /****************************************
1036  * Return type index for a delegate
1037  * Input:
1038  *      t          delegate type
1039  *      functypidx type index for pointer to function
1040  */
1041 @trusted
1042 idx_t cv8_ddelegate(type *t, idx_t functypidx)
1043 {
1044     //printf("cv8_ddelegate(functypidx = %x)\n", functypidx);
1045     /* Put out a struct:
1046      *    struct dDelegate {
1047      *      void* ptr;
1048      *      function* funcptr;
1049      *    }
1050      */
1051 
1052     type *tv = type_fake(TYnptr);
1053     tv.Tcount++;
1054     idx_t pvidx = cv4_typidx(tv);
1055     type_free(tv);
1056 
1057     type *tp = type_pointer(t.Tnext);
1058     idx_t ptridx = cv4_typidx(tp);
1059     type_free(tp);
1060 
1061 static if (0)
1062 {
1063     debtyp_t *d = debtyp_alloc(18);
1064     TOWORD(d.data.ptr, 0x100F);
1065     TOWORD(d.data.ptr + 2, OEM);
1066     TOWORD(d.data.ptr + 4, 3);     // 3 = delegate
1067     TOLONG(d.data.ptr + 6, 2);     // count of type indices to follow
1068     TOLONG(d.data.ptr + 10, key);  // void* type
1069     TOLONG(d.data.ptr + 14, functypidx); // function type
1070 }
1071 else
1072 {
1073     __gshared const ubyte[38] fl =
1074     [
1075         0x03, 0x12,             // LF_FIELDLIST_V2
1076         0x0d, 0x15,             // LF_MEMBER_V3
1077         0x03, 0x00,             // attribute
1078         0x00, 0x00, 0x00, 0x00, // void*
1079         0x00, 0x00,             // offset
1080         'p','t','r',0,          // "ptr"
1081         0xf2, 0xf1,             // align to 4-byte including length word before data
1082         0x0d, 0x15,
1083         0x03, 0x00,
1084         0x00, 0x00, 0x00, 0x00, // ptrtypidx
1085         0x08, 0x00,
1086         'f', 'u','n','c','p','t','r', 0,        // "funcptr"
1087         0xf2, 0xf1,
1088     ];
1089 
1090     debtyp_t *f = debtyp_alloc(fl.sizeof);
1091     memcpy(f.data.ptr,fl.ptr,fl.sizeof);
1092     TOLONG(f.data.ptr + 6, pvidx);
1093     TOLONG(f.data.ptr + 22, ptridx);
1094     TOWORD(f.data.ptr + 26, _tysize[TYnptr]);
1095     idx_t fieldlist = cv_debtyp(f);
1096 
1097     const(char)* id = "dDelegate";
1098     int idlen = cast(int)strlen(id);
1099     if (idlen > CV8_MAX_SYMBOL_LENGTH)
1100         idlen = CV8_MAX_SYMBOL_LENGTH;
1101 
1102     debtyp_t *d = debtyp_alloc(20 + idlen + 1);
1103     TOWORD(d.data.ptr, LF_STRUCTURE_V3);
1104     TOWORD(d.data.ptr + 2, 2);     // count
1105     TOWORD(d.data.ptr + 4, 0);     // property
1106     TOLONG(d.data.ptr + 6, fieldlist);
1107     TOLONG(d.data.ptr + 10, 0);    // dList
1108     TOLONG(d.data.ptr + 14, 0);    // vtshape
1109     TOWORD(d.data.ptr + 18, 2 * _tysize[TYnptr]);   // size
1110     memcpy(d.data.ptr + 20, id, idlen);
1111     d.data.ptr[20 + idlen] = 0;
1112 }
1113     return cv_debtyp(d);
1114 }
1115 
1116 /****************************************
1117  * Return type index for a aarray of type Value[Key]
1118  * Input:
1119  *      t          associative array type
1120  *      keyidx     key type
1121  *      validx     value type
1122  */
1123 @trusted
1124 idx_t cv8_daarray(type *t, idx_t keyidx, idx_t validx)
1125 {
1126     //printf("cv8_daarray(keyidx = %x, validx = %x)\n", keyidx, validx);
1127     /* Put out a struct:
1128      *    struct dAssocArray {
1129      *      void* ptr;
1130      *      typedef key-type __key_t;
1131      *      typedef val-type __val_t;
1132      *    }
1133      */
1134 
1135 static if (0)
1136 {
1137     debtyp_t *d = debtyp_alloc(18);
1138     TOWORD(d.data.ptr, 0x100F);
1139     TOWORD(d.data.ptr + 2, OEM);
1140     TOWORD(d.data.ptr + 4, 2);     // 2 = associative array
1141     TOLONG(d.data.ptr + 6, 2);     // count of type indices to follow
1142     TOLONG(d.data.ptr + 10, keyidx);  // key type
1143     TOLONG(d.data.ptr + 14, validx);  // element type
1144 }
1145 else
1146 {
1147     type *tv = type_fake(TYnptr);
1148     tv.Tcount++;
1149     idx_t pvidx = cv4_typidx(tv);
1150     type_free(tv);
1151 
1152     __gshared const ubyte[50] fl =
1153     [
1154         0x03, 0x12,             // LF_FIELDLIST_V2
1155         0x0d, 0x15,             // LF_MEMBER_V3
1156         0x03, 0x00,             // attribute
1157         0x00, 0x00, 0x00, 0x00, // void*
1158         0x00, 0x00,             // offset
1159         'p','t','r',0,          // "ptr"
1160         0xf2, 0xf1,             // align to 4-byte including field id
1161         // offset 18
1162         0x10, 0x15,             // LF_NESTTYPE_V3
1163         0x00, 0x00,             // padding
1164         0x00, 0x00, 0x00, 0x00, // key type
1165         '_','_','k','e','y','_','t',0,  // "__key_t"
1166         // offset 34
1167         0x10, 0x15,             // LF_NESTTYPE_V3
1168         0x00, 0x00,             // padding
1169         0x00, 0x00, 0x00, 0x00, // value type
1170         '_','_','v','a','l','_','t',0,  // "__val_t"
1171     ];
1172 
1173     debtyp_t *f = debtyp_alloc(fl.sizeof);
1174     memcpy(f.data.ptr,fl.ptr,fl.sizeof);
1175     TOLONG(f.data.ptr + 6, pvidx);
1176     TOLONG(f.data.ptr + 22, keyidx);
1177     TOLONG(f.data.ptr + 38, validx);
1178     idx_t fieldlist = cv_debtyp(f);
1179 
1180     const(char)* id = t.Tident ? t.Tident : "dAssocArray";
1181     int idlen = cast(int)strlen(id);
1182     if (idlen > CV8_MAX_SYMBOL_LENGTH)
1183         idlen = CV8_MAX_SYMBOL_LENGTH;
1184 
1185     debtyp_t *d = debtyp_alloc(20 + idlen + 1);
1186     TOWORD(d.data.ptr, LF_STRUCTURE_V3);
1187     TOWORD(d.data.ptr + 2, 1);     // count
1188     TOWORD(d.data.ptr + 4, 0);     // property
1189     TOLONG(d.data.ptr + 6, fieldlist);
1190     TOLONG(d.data.ptr + 10, 0);    // dList
1191     TOLONG(d.data.ptr + 14, 0);    // vtshape
1192     TOWORD(d.data.ptr + 18, _tysize[TYnptr]);   // size
1193     memcpy(d.data.ptr + 20, id, idlen);
1194     d.data.ptr[20 + idlen] = 0;
1195 
1196 }
1197     return cv_debtyp(d);
1198 }
1199 
1200 }