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