Merge pull request #18504 from mikedn/comp-small
[platform/upstream/coreclr.git] / src / jit / disasm.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 /***********************************************************************
5 *
6 * File: dis.cpp
7 *
8
9 *
10 * File Comments:
11 *
12 *  This file handles disassembly. It is adapted from the MS linker.
13 *
14 ***********************************************************************/
15
16 #include "jitpch.h"
17 #ifdef _MSC_VER
18 #pragma hdrstop
19 #endif
20
21 /*****************************************************************************/
22 #ifdef LATE_DISASM
23 /*****************************************************************************/
24
25 // Define DISASM_DEBUG to get verbose output of late disassembler inner workings.
26 //#define DISASM_DEBUG
27 #ifdef DISASM_DEBUG
28 #ifdef DEBUG
29 #define DISASM_DUMP(...)                                                                                               \
30     if (VERBOSE)                                                                                                       \
31     printf(__VA_ARGS__)
32 #else // !DEBUG
33 #define DISASM_DUMP(...) printf(__VA_ARGS__)
34 #endif // !DEBUG
35 #else  // !DISASM_DEBUG
36 #define DISASM_DUMP(...)
37 #endif // !DISASM_DEBUG
38
39 /*****************************************************************************/
40
41 #define MAX_CLASSNAME_LENGTH 1024
42
43 #if defined(_AMD64_)
44
45 #pragma comment(linker,                                                                                                \
46                 "/ALTERNATENAME:__imp_?CchFormatAddr@DIS@@QEBA_K_KPEAG0@Z=__imp_?CchFormatAddr@DIS@@QEBA_K_KPEA_W0@Z")
47 #pragma comment(linker,                                                                                                \
48                 "/ALTERNATENAME:__imp_?CchFormatInstr@DIS@@QEBA_KPEAG_K@Z=__imp_?CchFormatInstr@DIS@@QEBA_KPEA_W_K@Z")
49 #pragma comment(                                                                                                       \
50     linker,                                                                                                            \
51     "/ALTERNATENAME:__imp_?PfncchaddrSet@DIS@@QEAAP6A_KPEBV1@_KPEAG1PEA_K@ZP6A_K01213@Z@Z=__imp_?PfncchaddrSet@DIS@@QEAAP6A_KPEBV1@_KPEA_W1PEA_K@ZP6A_K01213@Z@Z")
52 #pragma comment(                                                                                                       \
53     linker,                                                                                                            \
54     "/ALTERNATENAME:__imp_?PfncchregSet@DIS@@QEAAP6A_KPEBV1@W4REGA@1@PEAG_K@ZP6A_K0123@Z@Z=__imp_?PfncchregSet@DIS@@QEAAP6A_KPEBV1@W4REGA@1@PEA_W_K@ZP6A_K0123@Z@Z")
55 #pragma comment(                                                                                                       \
56     linker,                                                                                                            \
57     "/ALTERNATENAME:__imp_?PfncchregrelSet@DIS@@QEAAP6A_KPEBV1@W4REGA@1@KPEAG_KPEAK@ZP6A_K01K234@Z@Z=__imp_?PfncchregrelSet@DIS@@QEAAP6A_KPEBV1@W4REGA@1@KPEA_W_KPEAK@ZP6A_K01K234@Z@Z")
58 #pragma comment(                                                                                                       \
59     linker,                                                                                                            \
60     "/ALTERNATENAME:__imp_?PfncchfixupSet@DIS@@QEAAP6A_KPEBV1@_K1PEAG1PEA_K@ZP6A_K011213@Z@Z=__imp_?PfncchfixupSet@DIS@@QEAAP6A_KPEBV1@_K1PEA_W1PEA_K@ZP6A_K011213@Z@Z")
61
62 #elif defined(_X86_)
63
64 #pragma comment(linker, "/ALTERNATENAME:__imp_?CchFormatAddr@DIS@@QBEI_KPAGI@Z=__imp_?CchFormatAddr@DIS@@QBEI_KPA_WI@Z")
65 #pragma comment(linker, "/ALTERNATENAME:__imp_?CchFormatInstr@DIS@@QBEIPAGI@Z=__imp_?CchFormatInstr@DIS@@QBEIPA_WI@Z")
66 #pragma comment(                                                                                                       \
67     linker,                                                                                                            \
68     "/ALTERNATENAME:__imp_?PfncchaddrSet@DIS@@QAEP6GIPBV1@_KPAGIPA_K@ZP6GI012I3@Z@Z=__imp_?PfncchaddrSet@DIS@@QAEP6GIPBV1@_KPA_WIPA_K@ZP6GI012I3@Z@Z")
69 #pragma comment(                                                                                                       \
70     linker,                                                                                                            \
71     "/ALTERNATENAME:__imp_?PfncchregSet@DIS@@QAEP6GIPBV1@W4REGA@1@PAGI@ZP6GI012I@Z@Z=__imp_?PfncchregSet@DIS@@QAEP6GIPBV1@W4REGA@1@PA_WI@ZP6GI012I@Z@Z")
72 #pragma comment(                                                                                                       \
73     linker,                                                                                                            \
74     "/ALTERNATENAME:__imp_?PfncchregrelSet@DIS@@QAEP6GIPBV1@W4REGA@1@KPAGIPAK@ZP6GI01K2I3@Z@Z=__imp_?PfncchregrelSet@DIS@@QAEP6GIPBV1@W4REGA@1@KPA_WIPAK@ZP6GI01K2I3@Z@Z")
75 #pragma comment(                                                                                                       \
76     linker,                                                                                                            \
77     "/ALTERNATENAME:__imp_?PfncchfixupSet@DIS@@QAEP6GIPBV1@_KIPAGIPA_K@ZP6GI01I2I3@Z@Z=__imp_?PfncchfixupSet@DIS@@QAEP6GIPBV1@_KIPA_WIPA_K@ZP6GI01I2I3@Z@Z")
78
79 #endif
80
81 /*****************************************************************************
82  * Given an absolute address from the beginning of the code
83  * find the corresponding emitter block and the relative offset
84  * of the current address in that block
85  * Was used to get to the fixup list of each block. The new emitter has
86  * no such fixups. Something needs to be added for this.
87  */
88
89 // These structs were defined in emit.h. Fake them here so DisAsm.cpp can compile
90
91 typedef struct codeFix
92 {
93     codeFix* cfNext;
94     unsigned cfFixup;
95 } * codeFixPtr;
96
97 typedef struct codeBlk
98 {
99     codeFix* cbFixupLst;
100 } * codeBlkPtr;
101
102 /*****************************************************************************
103  * The following is the callback for jump label and direct function calls fixups.
104  * "addr" represents the address of jump that has to be
105  * replaced with a label or function name.
106  *
107  * Return 1 if a name was written representing the address, 0 otherwise.
108  */
109
110 /* static */
111 size_t __stdcall DisAssembler::disCchAddr(
112     const DIS* pdis, DIS::ADDR addr, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp)
113 {
114     DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
115     assert(pDisAsm);
116     return pDisAsm->disCchAddrMember(pdis, addr, wz, cchMax, pdwDisp);
117 }
118
119 size_t DisAssembler::disCchAddrMember(
120     const DIS* pdis, DIS::ADDR addr, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp)
121 {
122     /* First check the termination type of the instruction
123      * because this might be a helper or static function call
124      * check to see if we have a fixup for the current address */
125
126     size_t retval = 0; // assume we don't know
127
128 #if defined(_TARGET_XARCH_)
129
130     DISX86::TRMTA terminationType = DISX86::TRMTA(pdis->Trmta());
131
132     DISASM_DUMP("AddrMember %p (%p), termType %u\n", addr, disGetLinearAddr((size_t)addr), terminationType);
133
134     switch (terminationType)
135     {
136         // int disCallSize;
137
138         case DISX86::trmtaJmpShort:
139         case DISX86::trmtaJmpCcShort:
140
141             /* We have a short jump in the current code block - generate the label to which we jump */
142
143             assert(0 <= disTarget && disTarget < disTotalCodeSize);
144             swprintf_s(wz, cchMax, W("short L_%02u"), disLabels[disTarget]);
145             retval = 1;
146             break;
147
148         case DISX86::trmtaJmpNear:
149         case DISX86::trmtaJmpCcNear:
150
151             /* We have a near jump. Check if is in the current code block.
152              * Otherwise we have no target for it. */
153
154             if (0 <= disTarget && disTarget < disTotalCodeSize)
155             {
156                 swprintf_s(wz, cchMax, W("L_%02u"), disLabels[disTarget]);
157                 retval = 1;
158             }
159             break;
160
161         case DISX86::trmtaCallNear16:
162         case DISX86::trmtaCallNear32:
163
164             /* check for local calls (i.e. CALL label) */
165
166             if (0 <= disTarget && disTarget < disTotalCodeSize)
167             {
168                 /* not a "call ds:[0000]" - go ahead */
169                 /* disTarget within block boundary -> local call */
170
171                 swprintf_s(wz, cchMax, W("short L_%02u"), disLabels[disTarget]);
172                 retval = 1;
173                 break;
174             }
175
176             /* this is a near call - in our case usually VM helper functions */
177
178             /* find the emitter block and the offset of the call fixup */
179             /* for the fixup offset we have to add the opcode size for the call - in the case of a near call is 1 */
180
181             // disCallSize = 1;
182
183             {
184                 size_t      absoluteTarget = (size_t)disGetLinearAddr(disTarget);
185                 const char* name           = disGetMethodFullName(absoluteTarget);
186                 if (name != nullptr)
187                 {
188                     swprintf_s(wz, cchMax, W("%p %S"), dspAddr(absoluteTarget), name);
189                     retval = 1;
190                     break;
191                 }
192             }
193
194             break;
195
196 #ifdef _TARGET_AMD64_
197
198         case DISX86::trmtaFallThrough:
199
200             /* memory indirect case. Could be for an LEA for the base address of a switch table, which is an arbitrary
201              * address, currently of the first block after the prolog. */
202
203             /* find the emitter block and the offset for the fixup
204              * "addr" is the address of the immediate */
205
206             break;
207
208 #endif // _TARGET_AMD64_
209
210         default:
211
212             printf("Termination type is %d\n", (int)terminationType);
213             assert(!"treat this case\n");
214             break;
215     }
216
217 #elif defined(_TARGET_ARM64_)
218
219     DISARM64::TRMTA terminationType = DISARM64::TRMTA(pdis->Trmta());
220
221     DISASM_DUMP("AddrMember %p (%p), termType %u\n", addr, disGetLinearAddr((size_t)addr), terminationType);
222
223     switch (terminationType)
224     {
225         // int disCallSize;
226
227         case DISARM64::TRMTA::trmtaBra:
228         case DISARM64::TRMTA::trmtaBraCase:
229         case DISARM64::TRMTA::trmtaBraCc:
230         case DISARM64::TRMTA::trmtaBraCcCase:
231         case DISARM64::TRMTA::trmtaBraCcInd:
232         case DISARM64::TRMTA::trmtaBraInd:
233
234             /* We have a jump. Check if is in the current code block.
235              * Otherwise we have no target for it. */
236
237             if (0 <= disTarget && disTarget < disTotalCodeSize)
238             {
239                 swprintf_s(wz, cchMax, W("L_%02u"), disLabels[disTarget]);
240                 retval = 1;
241             }
242             break;
243
244         case DISARM64::trmtaCall:
245         case DISARM64::trmtaCallCc:
246         case DISARM64::trmtaCallCcInd:
247         case DISARM64::trmtaCallInd:
248
249             /* check for local calls (i.e. CALL label) */
250
251             if (0 <= disTarget && disTarget < disTotalCodeSize)
252             {
253                 /* not a "call [0000]" - go ahead */
254                 /* disTarget within block boundary -> local call */
255
256                 swprintf_s(wz, cchMax, W("L_%02u"), disLabels[disTarget]);
257                 retval = 1;
258                 break;
259             }
260
261             /* this is a near call - in our case usually VM helper functions */
262
263             /* find the emitter block and the offset of the call fixup */
264             /* for the fixup offset we have to add the opcode size for the call - in the case of a near call is 1 */
265
266             // disCallSize = 1;
267
268             {
269                 size_t      absoluteTarget = (size_t)disGetLinearAddr(disTarget);
270                 const char* name           = disGetMethodFullName(absoluteTarget);
271                 if (name != nullptr)
272                 {
273                     swprintf_s(wz, cchMax, W("%p %S"), dspAddr(absoluteTarget), name);
274                     retval = 1;
275                     break;
276                 }
277             }
278
279             break;
280
281         case DISARM64::trmtaFallThrough:
282
283             /* memory indirect case. Could be for an LEA for the base address of a switch table, which is an arbitrary
284              * address, currently of the first block after the prolog. */
285
286             /* find the emitter block and the offset for the fixup
287              * "addr" is the address of the immediate */
288
289             {
290                 DIS::INSTRUCTION instr;
291                 DIS::OPERAND     ops[DISARM64::coperandMax];
292                 bool             ok = pdis->FDecode(&instr, ops, ArrLen(ops));
293                 if (ok)
294                 {
295                     bool isAddress = false;
296                     switch ((DISARM64::OPA)instr.opa)
297                     {
298                         case DISARM64::opaAdr:
299                         case DISARM64::opaAdrp:
300                             isAddress = true;
301                             break;
302                         default:
303                             break;
304                     }
305
306                     if (isAddress && 0 <= addr && addr < disTotalCodeSize)
307                     {
308                         swprintf_s(wz, cchMax, W("L_%02u"), disLabels[addr]);
309                         retval = 1;
310                     }
311                 }
312             }
313             break;
314
315         default:
316
317             printf("Termination type is %d\n", (int)terminationType);
318             assert(!"treat this case\n");
319             break;
320     }
321
322 #else // _TARGET_*
323 #error Unsupported or unset target architecture
324 #endif // _TARGET_*
325
326     if (retval == 0)
327     {
328         if (disDiffable)
329         {
330             swprintf_s(wz, cchMax, W("%p"), dspAddr((void*)1));
331         }
332     }
333     else
334     {
335         /* no displacement */
336
337         *pdwDisp = 0x0;
338     }
339
340     return retval;
341 }
342
343 /*****************************************************************************
344  * We annotate some instructions to get info needed to display the symbols
345  * for that instruction.
346  *
347  * Return 1 if a name was written representing the address, 0 otherwise.
348  */
349
350 /* static */
351 size_t __stdcall DisAssembler::disCchFixup(
352     const DIS* pdis, DIS::ADDR addr, size_t size, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp)
353 {
354     DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
355     assert(pDisAsm);
356
357     return pDisAsm->disCchFixupMember(pdis, addr, size, wz, cchMax, pdwDisp);
358 }
359
360 size_t DisAssembler::disCchFixupMember(
361     const DIS* pdis, DIS::ADDR addr, size_t size, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORDLONG* pdwDisp)
362 {
363 #if defined(_TARGET_XARCH_)
364
365     DISX86::TRMTA terminationType = DISX86::TRMTA(pdis->Trmta());
366     // DIS::ADDR disIndAddr;
367
368     DISASM_DUMP("FixupMember %016I64X (%08IX), size %d, termType %u\n", addr, disGetLinearAddr((size_t)addr), size,
369                 terminationType);
370
371     // Is there a relocation registered for the address?
372
373     size_t absoluteAddr = (size_t)disGetLinearAddr((size_t)addr);
374     size_t targetAddr;
375     bool   anyReloc = GetRelocationMap()->Lookup(absoluteAddr, &targetAddr);
376
377     switch (terminationType)
378     {
379         DIS::ADDR disCallSize;
380
381         case DISX86::trmtaFallThrough:
382
383             /* memory indirect case */
384
385             assert(addr > pdis->Addr());
386
387             /* find the emitter block and the offset for the fixup
388              * "addr" is the address of the immediate */
389
390             if (anyReloc)
391             {
392                 // Make instructions like "mov rcx, 7FE8247A638h" diffable.
393                 swprintf_s(wz, cchMax, W("%IXh"), dspAddr(targetAddr));
394                 break;
395             }
396
397             return 0;
398
399         case DISX86::trmtaJmpInd:
400
401             /* pretty rare case - something like "jmp [eax*4]"
402              * not a function call or anything worth annotating */
403
404             return 0;
405
406         case DISX86::trmtaTrap:
407         case DISX86::trmtaTrapCc:
408
409             /* some instructions like division have a TRAP termination type - ignore it */
410
411             return 0;
412
413         case DISX86::trmtaJmpShort:
414         case DISX86::trmtaJmpCcShort:
415
416         case DISX86::trmtaJmpNear:
417         case DISX86::trmtaJmpCcNear:
418
419             /* these are treated by the CchAddr callback - skip them */
420
421             return 0;
422
423         case DISX86::trmtaCallNear16:
424         case DISX86::trmtaCallNear32:
425
426             if (anyReloc)
427             {
428                 const char* name = disGetMethodFullName(targetAddr);
429                 if (name != nullptr)
430                 {
431                     swprintf_s(wz, cchMax, W("%p %S"), dspAddr(targetAddr), name);
432                     break;
433                 }
434             }
435
436             /* these are treated by the CchAddr callback - skip them */
437
438             return 0;
439
440         case DISX86::trmtaCallInd:
441
442             /* here we have an indirect call - find the indirect address */
443
444             // BYTE * code = disGetLinearAddr((size_t)addr);
445             // disIndAddr = (DIS::ADDR) (code+0);
446
447             /* find the size of the call opcode - less the immediate */
448             /* for the fixup offset we have to add the opcode size for the call */
449             /* addr is the address of the immediate, pdis->Addr() returns the address of the disassembled instruction */
450
451             assert(addr > pdis->Addr());
452             disCallSize = addr - pdis->Addr();
453
454             /* find the emitter block and the offset of the call fixup */
455
456             return 0;
457
458         default:
459
460             printf("Termination type is %d\n", (int)terminationType);
461             assert(!"treat this case\n");
462             break;
463     }
464
465 #elif defined(_TARGET_ARM64_)
466
467     DISARM64::TRMTA terminationType = DISARM64::TRMTA(pdis->Trmta());
468     // DIS::ADDR disIndAddr;
469
470     DISASM_DUMP("FixupMember %016I64X (%08IX), size %d, termType %u\n", addr, disGetLinearAddr((size_t)addr), size,
471                 terminationType);
472
473     // Is there a relocation registered for the address?
474
475     size_t absoluteAddr = (size_t)disGetLinearAddr((size_t)addr);
476     size_t targetAddr;
477     bool   anyReloc = GetRelocationMap()->Lookup(absoluteAddr, &targetAddr);
478
479     switch (terminationType)
480     {
481         DIS::ADDR disCallSize;
482
483         case DISARM64::TRMTA::trmtaUnknown:
484             return 0;
485
486         case DISARM64::TRMTA::trmtaFallThrough:
487
488             if (anyReloc)
489             {
490                 /* memory indirect case */
491
492                 assert(addr > pdis->Addr());
493
494                 /* find the emitter block and the offset for the fixup
495                  * "addr" is the address of the immediate */
496
497                 // Make instructions like "mov rcx, 7FE8247A638h" diffable.
498                 swprintf_s(wz, cchMax, W("%IXh"), dspAddr(targetAddr));
499                 break;
500             }
501
502             return 0;
503
504         case DISARM64::TRMTA::trmtaBraInd:
505         case DISARM64::TRMTA::trmtaBraCcInd:
506
507             /* pretty rare case - something like "jmp [eax*4]"
508              * not a function call or anything worth annotating */
509
510             return 0;
511
512         case DISARM64::TRMTA::trmtaTrap:
513         case DISARM64::TRMTA::trmtaTrapCc:
514
515             /* some instructions like division have a TRAP termination type - ignore it */
516
517             return 0;
518
519         case DISARM64::TRMTA::trmtaBra:
520         case DISARM64::TRMTA::trmtaBraCase:
521         case DISARM64::TRMTA::trmtaBraCc:
522         case DISARM64::TRMTA::trmtaBraCcCase:
523
524             /* these are treated by the CchAddr callback - skip them */
525
526             return 0;
527
528         case DISARM64::TRMTA::trmtaCall:
529         case DISARM64::TRMTA::trmtaCallCc:
530
531             if (anyReloc)
532             {
533                 const char* name = disGetMethodFullName(targetAddr);
534                 if (name != nullptr)
535                 {
536                     swprintf_s(wz, cchMax, W("%p %S"), dspAddr(targetAddr), name);
537                     break;
538                 }
539             }
540
541             /* these are treated by the CchAddr callback - skip them */
542
543             return 0;
544
545         case DISARM64::TRMTA::trmtaCallInd:
546         case DISARM64::TRMTA::trmtaCallCcInd:
547
548             /* here we have an indirect call - find the indirect address */
549
550             // BYTE * code = disGetLinearAddr((size_t)addr);
551             // disIndAddr = (DIS::ADDR) (code+0);
552
553             /* find the size of the call opcode - less the immediate */
554             /* for the fixup offset we have to add the opcode size for the call */
555             /* addr is the address of the immediate, pdis->Addr() returns the address of the disassembled instruction */
556
557             assert(addr > pdis->Addr());
558             disCallSize = addr - pdis->Addr();
559
560             /* find the emitter block and the offset of the call fixup */
561
562             return 0;
563
564         default:
565
566             printf("Termination type is %d\n", (int)terminationType);
567             assert(!"treat this case\n");
568             break;
569     }
570
571 #else // _TARGET_*
572 #error Unsupported or unset target architecture
573 #endif // _TARGET_*
574
575     /* no displacement */
576
577     *pdwDisp = 0x0;
578
579     return 1;
580 }
581
582 /*****************************************************************************
583  * This the callback for register-relative operands in an instruction.
584  * If the register is ESP or EBP, the operand may be a local variable
585  * or a parameter, else the operand may be an instance variable
586  *
587  * Return 1 if a name was written representing the register-relative operand, 0 otherwise.
588  */
589
590 /* static */
591 size_t __stdcall DisAssembler::disCchRegRel(
592     const DIS* pdis, DIS::REGA reg, DWORD disp, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORD* pdwDisp)
593 {
594     DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
595     assert(pDisAsm);
596
597     return pDisAsm->disCchRegRelMember(pdis, reg, disp, wz, cchMax, pdwDisp);
598 }
599
600 size_t DisAssembler::disCchRegRelMember(
601     const DIS* pdis, DIS::REGA reg, DWORD disp, __in_ecount(cchMax) wchar_t* wz, size_t cchMax, DWORD* pdwDisp)
602 {
603 #if defined(_TARGET_XARCH_)
604
605     DISX86::TRMTA terminationType = DISX86::TRMTA(pdis->Trmta());
606     // DIS::ADDR disIndAddr;
607
608     DISASM_DUMP("RegRelMember reg %u, disp %u, termType %u\n", reg, disp, terminationType);
609
610     switch (terminationType)
611     {
612         int         disOpcodeSize;
613         const char* var;
614
615         case DISX86::trmtaFallThrough:
616
617         /* some instructions like division have a TRAP termination type - ignore it */
618
619         case DISX86::trmtaTrap:
620         case DISX86::trmtaTrapCc:
621
622             var = disComp->codeGen->siStackVarName((size_t)(pdis->Addr() - disStartAddr), pdis->Cb(), reg, disp);
623             if (var)
624             {
625                 swprintf_s(wz, cchMax, W("%hs+%Xh '%hs'"), getRegName(reg), disp, var);
626                 *pdwDisp = 0;
627
628                 return 1;
629             }
630
631             /* This case consists of non-static members */
632
633             /* find the emitter block and the offset for the fixup
634              * fixup is emited after the coding of the instruction - size = word (2 bytes)
635              * GRRRR!!! - for the 16 bit case we have to check for the address size prefix = 0x66
636              */
637
638             if (*disGetLinearAddr(disCurOffset) == 0x66)
639             {
640                 disOpcodeSize = 3;
641             }
642             else
643             {
644                 disOpcodeSize = 2;
645             }
646
647             return 0;
648
649         case DISX86::trmtaCallNear16:
650         case DISX86::trmtaCallNear32:
651         case DISX86::trmtaJmpInd:
652
653             break;
654
655         case DISX86::trmtaCallInd:
656
657             /* check if this is a one byte displacement */
658
659             if ((signed char)disp == (int)disp)
660             {
661                 /* we have a one byte displacement -> there were no previous callbacks */
662
663                 /* find the size of the call opcode - less the immediate */
664                 /* this is a call R/M indirect -> opcode size is 2 */
665
666                 disOpcodeSize = 2;
667
668                 /* find the emitter block and the offset of the call fixup */
669
670                 return 0;
671             }
672             else
673             {
674                 /* check if we already have a symbol name as replacement */
675
676                 if (disHasName)
677                 {
678                     /* CchFixup has been called before - we have a symbol name saved in global var disFuncTempBuf */
679
680                     swprintf_s(wz, cchMax, W("%hs+%u '%hs'"), getRegName(reg), disp, disFuncTempBuf);
681                     *pdwDisp   = 0;
682                     disHasName = false;
683                     return 1;
684                 }
685                 else
686                 {
687                     return 0;
688                 }
689             }
690
691         default:
692
693             printf("Termination type is %d\n", (int)terminationType);
694             assert(!"treat this case\n");
695
696             break;
697     }
698
699 #elif defined(_TARGET_ARM64_)
700
701     DISARM64::TRMTA terminationType = DISARM64::TRMTA(pdis->Trmta());
702
703     DISASM_DUMP("RegRelMember reg %u, disp %u, termType %u\n", reg, disp, terminationType);
704
705     switch (terminationType)
706     {
707         int         disOpcodeSize;
708         const char* var;
709
710         case DISARM64::TRMTA::trmtaFallThrough:
711
712         /* some instructions like division have a TRAP termination type - ignore it */
713
714         case DISARM64::TRMTA::trmtaTrap:
715         case DISARM64::TRMTA::trmtaTrapCc:
716
717             var = disComp->codeGen->siStackVarName((size_t)(pdis->Addr() - disStartAddr), pdis->Cb(), reg, disp);
718             if (var)
719             {
720                 swprintf_s(wz, cchMax, W("%hs+%Xh '%hs'"), getRegName(reg), disp, var);
721                 *pdwDisp = 0;
722
723                 return 1;
724             }
725
726             /* This case consists of non-static members */
727
728             // TODO-ARM64-Bug?: Is this correct?
729             disOpcodeSize = 2;
730             return 0;
731
732         case DISARM64::TRMTA::trmtaCall:
733         case DISARM64::TRMTA::trmtaCallCc:
734         case DISARM64::TRMTA::trmtaBraInd:
735         case DISARM64::TRMTA::trmtaBraCcInd:
736             break;
737
738         case DISARM64::TRMTA::trmtaCallInd:
739         case DISARM64::TRMTA::trmtaCallCcInd:
740
741             /* check if this is a one byte displacement */
742
743             if ((signed char)disp == (int)disp)
744             {
745                 /* we have a one byte displacement -> there were no previous callbacks */
746
747                 /* find the size of the call opcode - less the immediate */
748                 /* this is a call R/M indirect -> opcode size is 2 */
749
750                 // TODO-ARM64-Bug?: Is this correct?
751                 disOpcodeSize = 2;
752
753                 /* find the emitter block and the offset of the call fixup */
754
755                 return 0;
756             }
757             else
758             {
759                 /* check if we already have a symbol name as replacement */
760
761                 if (disHasName)
762                 {
763                     /* CchFixup has been called before - we have a symbol name saved in global var disFuncTempBuf */
764
765                     swprintf_s(wz, cchMax, W("%hs+%u '%hs'"), getRegName(reg), disp, disFuncTempBuf);
766                     *pdwDisp   = 0;
767                     disHasName = false;
768                     return 1;
769                 }
770                 else
771                 {
772                     return 0;
773                 }
774             }
775
776         default:
777
778             printf("Termination type is %d\n", (int)terminationType);
779             assert(!"treat this case\n");
780
781             break;
782     }
783
784 #else // _TARGET_*
785 #error Unsupported or unset target architecture
786 #endif // _TARGET_*
787
788     /* save displacement */
789
790     *pdwDisp = disp;
791
792     return 1;
793 }
794
795 /*****************************************************************************
796  *
797  * Callback for register operands. Most probably, this is a local variable or
798  * a parameter
799  *
800  * Return 1 if a name was written representing the register, 0 otherwise.
801  */
802
803 /* static */
804 size_t __stdcall DisAssembler::disCchReg(const DIS* pdis, DIS::REGA reg, __in_ecount(cchMax) wchar_t* wz, size_t cchMax)
805 {
806     DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
807     assert(pDisAsm);
808
809     return pDisAsm->disCchRegMember(pdis, reg, wz, cchMax);
810 }
811
812 size_t DisAssembler::disCchRegMember(const DIS* pdis, DIS::REGA reg, __in_ecount(cchMax) wchar_t* wz, size_t cchMax)
813 {
814     // TODO-Review: DIS::REGA does not directly map to our regNumber! E.g., look at DISARM64::REGA --
815     // the Wt registers come first (and do map to our regNumber), but the Xt registers follow.
816     // Until this is fixed, don't use this function!
817     disHasName = false;
818     return 0;
819
820 #if 0
821     const char * var = disComp->codeGen->siRegVarName(
822                                             (size_t)(pdis->Addr() - disStartAddr),
823                                             pdis->Cb(),
824                                             reg);
825
826     if (var)
827     {
828         if (disHasName)
829         {
830             /* CchRegRel has been called before - we have a symbol name saved in global var disFuncTempBuf */
831
832             swprintf_s(wz, cchMax, W("%hs'%hs.%hs'"), getRegName(reg), var, disFuncTempBuf);
833             disHasName = false;
834             return 1;
835         }
836         else
837         {
838             swprintf_s(wz, cchMax, W("%hs'%hs'"), getRegName(reg), var);
839             return 1;
840         }
841     }
842     else
843     {
844         if (disHasName)
845         {
846             /* this is the ugly case when a variable is incorrectly presumed dead */
847
848             swprintf_s(wz, cchMax, W("%hs'%hs.%hs'"), getRegName(reg), "<InstVar>", disFuncTempBuf);
849             disHasName = false;
850             return 1;
851         }
852
853         /* just to make sure we didn't bungle if var returns NULL */
854         disHasName = false;
855         return 0;
856     }
857 #endif // 0
858 }
859
860 /*****************************************************************************
861  * Helper function to lazily create a map from code address to CORINFO_METHOD_HANDLE.
862  */
863 AddrToMethodHandleMap* DisAssembler::GetAddrToMethodHandleMap()
864 {
865     if (disAddrToMethodHandleMap == nullptr)
866     {
867         disAddrToMethodHandleMap = new (disComp->getAllocator()) AddrToMethodHandleMap(disComp->getAllocator());
868     }
869     return disAddrToMethodHandleMap;
870 }
871
872 /*****************************************************************************
873  * Helper function to lazily create a map from code address to CORINFO_METHOD_HANDLE.
874  */
875 AddrToMethodHandleMap* DisAssembler::GetHelperAddrToMethodHandleMap()
876 {
877     if (disHelperAddrToMethodHandleMap == nullptr)
878     {
879         disHelperAddrToMethodHandleMap = new (disComp->getAllocator()) AddrToMethodHandleMap(disComp->getAllocator());
880     }
881     return disHelperAddrToMethodHandleMap;
882 }
883
884 /*****************************************************************************
885  * Helper function to lazily create a map from relocation address to relocation target address.
886  */
887 AddrToAddrMap* DisAssembler::GetRelocationMap()
888 {
889     if (disRelocationMap == nullptr)
890     {
891         disRelocationMap = new (disComp->getAllocator()) AddrToAddrMap(disComp->getAllocator());
892     }
893     return disRelocationMap;
894 }
895
896 /*****************************************************************************
897  * Return the count of bytes disassembled.
898  */
899
900 size_t DisAssembler::CbDisassemble(DIS*        pdis,
901                                    size_t      offs,
902                                    DIS::ADDR   addr,
903                                    const BYTE* pb,
904                                    size_t      cbMax,
905                                    FILE*       pfile,
906                                    bool        findLabels,
907                                    bool        printit /* = false */,
908                                    bool        dispOffs /* = false */,
909                                    bool        dispCodeBytes /* = false */)
910 {
911     assert(pdis);
912
913     size_t cb = pdis->CbDisassemble(addr, pb, cbMax);
914
915     if (cb == 0)
916     {
917         DISASM_DUMP("CbDisassemble offs %Iu addr %I64u\n", offs, addr);
918         // assert(!"can't disassemble instruction!!!");
919         fprintf(pfile, "MSVCDIS can't disassemble instruction @ offset %Iu (0x%02x)!!!\n", offs, offs);
920 #if defined(_TARGET_ARM64_)
921         fprintf(pfile, "%08Xh\n", *(unsigned int*)pb);
922         return 4;
923 #else
924         fprintf(pfile, "%02Xh\n", *pb);
925         return 1;
926 #endif
927     }
928
929 #if defined(_TARGET_ARM64_)
930     assert(cb == 4); // all instructions are 4 bytes!
931 #endif               // _TARGET_ARM64_
932
933     /* remember current offset and instruction size */
934
935     disCurOffset = (size_t)addr;
936     disInstSize  = cb;
937
938     /* Set the disTarget address */
939
940     disTarget = (size_t)pdis->AddrTarget();
941
942     if (findLabels)
943     {
944 #if defined(_TARGET_XARCH_)
945         DISX86::TRMTA terminationType = DISX86::TRMTA(pdis->Trmta());
946
947         /* check the termination type of the instruction */
948
949         switch (terminationType)
950         {
951             case DISX86::trmtaCallNear16:
952             case DISX86::trmtaCallNear32:
953             case DISX86::trmtaCallFar:
954
955             {
956                 // Don't count addresses in the relocation table
957                 size_t targetAddr;
958                 size_t absoluteAddr =
959                     (size_t)disGetLinearAddr((size_t)pdis->AddrAddress(1)); // Get the address in the instruction of the
960                                                                             // call target address (the address the
961                                                                             // reloc is applied to).
962                 if (GetRelocationMap()->Lookup(absoluteAddr, &targetAddr))
963                 {
964                     break;
965                 }
966             }
967
968                 __fallthrough;
969
970             case DISX86::trmtaJmpShort:
971             case DISX86::trmtaJmpNear:
972             case DISX86::trmtaJmpFar:
973             case DISX86::trmtaJmpCcShort:
974             case DISX86::trmtaJmpCcNear:
975
976                 /* a CALL is local iff the disTarget is within the block boundary */
977
978                 /* mark the jump label in the disTarget vector and return */
979
980                 if (disTarget != DIS::addrNil) // There seems to be an assumption that you can't branch to the first
981                                                // address of the function (prolog).
982                 {
983                     if (0 <= disTarget && disTarget < disTotalCodeSize)
984                     {
985                         /* we're OK, disTarget within block boundary */
986
987                         disLabels[disTarget] = 1;
988                     }
989                 }
990                 break;
991
992             case DISX86::trmtaFallThrough:
993                 // We'd like to be able to get a label for code like "lea rcx, [4]" that we use for jump tables, but I
994                 // can't figure out how.
995                 break;
996
997             default:
998
999                 /* jump is not in the current code block */
1000                 break;
1001
1002         } // end switch
1003 #elif defined(_TARGET_ARM64_)
1004         DISARM64::TRMTA terminationType = DISARM64::TRMTA(pdis->Trmta());
1005
1006         /* check the termination type of the instruction */
1007
1008         switch (terminationType)
1009         {
1010             case DISARM64::TRMTA::trmtaCall:
1011             case DISARM64::TRMTA::trmtaCallCc:
1012
1013             {
1014                 // Don't count addresses in the relocation table
1015                 size_t targetAddr;
1016                 size_t absoluteAddr =
1017                     (size_t)disGetLinearAddr((size_t)pdis->AddrAddress(1)); // Get the address in the instruction of the
1018                                                                             // call target address (the address the
1019                                                                             // reloc is applied to).
1020                 if (GetRelocationMap()->Lookup(absoluteAddr, &targetAddr))
1021                 {
1022                     break;
1023                 }
1024             }
1025
1026                 __fallthrough;
1027
1028             case DISARM64::TRMTA::trmtaBra:
1029             case DISARM64::TRMTA::trmtaBraCase:
1030             case DISARM64::TRMTA::trmtaBraCc:
1031             case DISARM64::TRMTA::trmtaBraCcCase:
1032
1033                 /* a CALL is local iff the disTarget is within the block boundary */
1034
1035                 /* mark the jump label in the disTarget vector and return */
1036
1037                 if (disTarget != DIS::addrNil) // There seems to be an assumption that you can't branch to the first
1038                                                // address of the function (prolog).
1039                 {
1040                     if (0 <= disTarget && disTarget < disTotalCodeSize)
1041                     {
1042                         /* we're OK, disTarget within block boundary */
1043
1044                         disLabels[disTarget] = 1;
1045                     }
1046                 }
1047                 break;
1048
1049             case DISARM64::TRMTA::trmtaFallThrough:
1050             {
1051                 DIS::INSTRUCTION instr;
1052                 DIS::OPERAND     ops[DISARM64::coperandMax];
1053                 bool             ok = pdis->FDecode(&instr, ops, ArrLen(ops));
1054                 if (ok)
1055                 {
1056                     switch ((DISARM64::OPA)instr.opa)
1057                     {
1058                         case DISARM64::opaAdr:
1059                         case DISARM64::opaAdrp:
1060                             // operand 1 is an address
1061                             assert(instr.coperand >= 2);
1062                             assert(ops[1].opcls == DIS::opclsImmediate);
1063                             assert(ops[1].imcls == DIS::imclsAddress);
1064                             disTarget = ops[1].dwl;
1065                             break;
1066                         default:
1067                             break;
1068                     }
1069
1070                     if (0 <= disTarget && disTarget < disTotalCodeSize)
1071                     {
1072                         /* we're OK, disTarget within block boundary */
1073
1074                         disLabels[disTarget] = 1;
1075                     }
1076                 }
1077             }
1078             break;
1079
1080             default:
1081
1082                 /* jump is not in the current code block */
1083                 break;
1084
1085         } // end switch
1086 #else // _TARGET_*
1087 #error Unsupported or unset target architecture
1088 #endif // _TARGET_*
1089
1090         return cb;
1091     } // end if
1092
1093     /* check if we have a label here */
1094
1095     if (printit)
1096     {
1097         if (disLabels[addr])
1098         {
1099             /* print the label and the offset */
1100
1101             fprintf(pfile, "L_%02u:\n", disLabels[addr]);
1102         }
1103     }
1104
1105     wchar_t wz[MAX_CLASSNAME_LENGTH];
1106     pdis->CchFormatInstr(wz, _countof(wz));
1107
1108     if (printit)
1109     {
1110         if (dispOffs)
1111         {
1112             fprintf(pfile, "%03X", offs);
1113         }
1114
1115 #ifdef _TARGET_ARM64_
1116 #define CCH_INDENT 8 // fixed sized instructions, always 8 characters
1117 #elif defined(_TARGET_AMD64_)
1118 #define CCH_INDENT 30 // large constants sometimes
1119 #else
1120 #define CCH_INDENT 24
1121 #endif
1122
1123         size_t cchIndent = CCH_INDENT;
1124
1125         if (dispCodeBytes)
1126         {
1127             static size_t cchBytesMax = -1;
1128
1129             if (cchBytesMax == -1)
1130             {
1131                 cchBytesMax = pdis->CchFormatBytesMax();
1132             }
1133
1134             wchar_t wzBytes[MAX_CLASSNAME_LENGTH];
1135             assert(cchBytesMax < MAX_CLASSNAME_LENGTH);
1136
1137             size_t cchBytes = pdis->CchFormatBytes(wzBytes, _countof(wzBytes));
1138
1139             if (cchBytes > CCH_INDENT)
1140             {
1141                 // Truncate the bytes if they are too long
1142
1143                 static const wchar_t* elipses    = W("...\0");
1144                 const size_t          cchElipses = 4;
1145
1146                 memcpy(&wzBytes[CCH_INDENT - cchElipses], elipses, cchElipses * sizeof(wchar_t));
1147
1148                 cchBytes = CCH_INDENT;
1149             }
1150
1151             fprintf(pfile, "  %ls", wzBytes);
1152             cchIndent = CCH_INDENT - cchBytes;
1153         }
1154
1155         // print the dis-assembled instruction
1156
1157         fprintf(pfile, "%*c %ls\n", cchIndent, ' ', wz);
1158     }
1159
1160     return cb;
1161 }
1162
1163 // TODO-Cleanup: this is currently unused, unreferenced.
1164 size_t CbDisassembleWithBytes(DIS* pdis, DIS::ADDR addr, const BYTE* pb, size_t cbMax, FILE* pfile)
1165 {
1166     assert(pdis);
1167     DisAssembler* pDisAsm = (DisAssembler*)pdis->PvClient();
1168     assert(pDisAsm);
1169
1170     wchar_t wz[MAX_CLASSNAME_LENGTH];
1171
1172     pdis->CchFormatAddr(addr, wz, _countof(wz));
1173
1174     size_t cchIndent = (size_t)fprintf(pfile, "  %ls: ", wz);
1175
1176     size_t cb = pdis->CbDisassemble(addr, pb, cbMax);
1177
1178     if (cb == 0)
1179     {
1180         fprintf(pfile, "%02Xh\n", *pb);
1181         return (1);
1182     }
1183
1184     size_t cchBytesMax = pdis->CchFormatBytesMax();
1185
1186     if (cchBytesMax > 18)
1187     {
1188         // Limit bytes coded to 18 characters
1189
1190         cchBytesMax = 18;
1191     }
1192
1193     wchar_t wzBytes[64];
1194     size_t  cchBytes = pdis->CchFormatBytes(wzBytes, _countof(wzBytes));
1195
1196     wchar_t* pwzBytes;
1197     wchar_t* pwzNext;
1198
1199     for (pwzBytes = wzBytes; pwzBytes != NULL; pwzBytes = pwzNext)
1200     {
1201         BOOL fFirst = (pwzBytes == wzBytes);
1202
1203         cchBytes = wcslen(pwzBytes);
1204
1205         if (cchBytes <= cchBytesMax)
1206         {
1207             pwzNext = NULL;
1208         }
1209
1210         else
1211         {
1212             wchar_t ch            = pwzBytes[cchBytesMax];
1213             pwzBytes[cchBytesMax] = '\0';
1214
1215             if (ch == W(' '))
1216             {
1217                 pwzNext = pwzBytes + cchBytesMax + 1;
1218             }
1219
1220             else
1221             {
1222                 pwzNext = wcsrchr(pwzBytes, W(' '));
1223                 assert(pwzNext);
1224
1225                 pwzBytes[cchBytesMax] = ch;
1226                 *pwzNext++            = '\0';
1227             }
1228         }
1229
1230         if (fFirst)
1231         {
1232             pdis->CchFormatInstr(wz, _countof(wz));
1233             fprintf(pfile, "%-*ls %ls\n", cchBytesMax, pwzBytes, wz);
1234         }
1235
1236         else
1237         {
1238             fprintf(pfile, "%*c%ls\n", cchIndent, ' ', pwzBytes);
1239         }
1240     }
1241
1242     return (cb);
1243 }
1244
1245 void DisAssembler::DisasmBuffer(FILE* pfile, bool printit)
1246 {
1247     DIS* pdis = NULL;
1248
1249 #ifdef _TARGET_X86_
1250     pdis = DIS::PdisNew(DIS::distX86);
1251 #elif defined(_TARGET_AMD64_)
1252     pdis = DIS::PdisNew(DIS::distX8664);
1253 #elif defined(_TARGET_ARM64_)
1254     pdis = DIS::PdisNew(DIS::distArm64);
1255 #else // _TARGET_*
1256 #error Unsupported or unset target architecture
1257 #endif
1258
1259     if (pdis == NULL)
1260     {
1261         assert(!"out of memory in disassembler?");
1262         return;
1263     }
1264
1265 #ifdef _TARGET_64BIT_
1266     pdis->SetAddr64(true);
1267 #endif
1268
1269     // Store a pointer to the DisAssembler so that the callback functions
1270     // can get to it.
1271
1272     pdis->PvClientSet((void*)this);
1273
1274     /* Calculate addresses */
1275
1276     size_t    ibCur = 0;
1277     DIS::ADDR addr  = 0; // Always emit code with respect to a "0" base address.
1278
1279     /* First walk the code to find all jump targets */
1280
1281     while (ibCur < disTotalCodeSize)
1282     {
1283         size_t cb;
1284
1285         cb = CbDisassemble(pdis, ibCur, addr + ibCur, disGetLinearAddr(ibCur), disGetBufferSize(ibCur), pfile,
1286                            true); // find labels
1287
1288         // CbDisassemble returning > MAX_INT... give me a break.
1289         ibCur += cb;
1290     }
1291
1292     /* reset the label counter and start assigning consecutive number labels to the label locations */
1293
1294     BYTE label = 0;
1295     for (unsigned i = 0; i < disTotalCodeSize; i++)
1296     {
1297         if (disLabels[i] != 0)
1298         {
1299             disLabels[i] = ++label;
1300         }
1301     }
1302
1303     /* Re-initialize addresses for disassemble phase */
1304
1305     ibCur = 0;
1306     addr  = 0;
1307
1308     // Set callbacks only if we are displaying it. Else, the scheduler has called it
1309
1310     if (printit)
1311     {
1312         /* Set the callback functions for symbol lookup */
1313
1314         pdis->PfncchaddrSet(disCchAddr);
1315         pdis->PfncchfixupSet(disCchFixup);
1316         pdis->PfncchregrelSet(disCchRegRel);
1317         pdis->PfncchregSet(disCchReg);
1318     }
1319
1320     while (ibCur < disTotalCodeSize)
1321     {
1322         size_t cb;
1323
1324         cb = CbDisassemble(pdis, ibCur, addr + ibCur, disGetLinearAddr(ibCur), disGetBufferSize(ibCur), pfile,
1325                            false, // find labels
1326                            printit,
1327                            !disDiffable, // display relative offset
1328 #ifdef DEBUG
1329                            !disDiffable // Display code bytes?
1330 #else
1331                            false // Display code bytes?
1332 #endif
1333                            );
1334
1335         ibCur += (unsigned)cb;
1336     }
1337
1338     delete pdis;
1339 }
1340
1341 /*****************************************************************************
1342  * Given a linear offset into the code, find a pointer to the actual code (either in the hot or cold section)
1343  *
1344  * Arguments:
1345  *      offset  - The linear offset into the code. It must point within the code.
1346  */
1347
1348 const BYTE* DisAssembler::disGetLinearAddr(size_t offset)
1349 {
1350     if (offset < disHotCodeSize)
1351     {
1352         return (const BYTE*)disHotCodeBlock + offset;
1353     }
1354     else
1355     {
1356         return (const BYTE*)disColdCodeBlock + offset - disHotCodeSize;
1357     }
1358 }
1359
1360 /*****************************************************************************
1361  * Given a linear offset into the code, determine how many bytes are remaining in the buffer.
1362  * This will only return the number of bytes left in either the hot or cold buffer. This is used
1363  * to avoid walking off the end of the buffer.
1364  *
1365  * Arguments:
1366  *      offset  - The linear offset into the code. It must point within the code.
1367  */
1368
1369 size_t DisAssembler::disGetBufferSize(size_t offset)
1370 {
1371     if (offset < disHotCodeSize)
1372     {
1373         return disHotCodeSize - offset;
1374     }
1375     else
1376     {
1377         return disHotCodeSize + disColdCodeSize - offset;
1378     }
1379 }
1380
1381 /*****************************************************************************
1382  * Get the function name for a given absolute address.
1383  */
1384
1385 const char* DisAssembler::disGetMethodFullName(size_t addr)
1386 {
1387     CORINFO_METHOD_HANDLE res;
1388
1389     // First check the JIT helper table: they're very common.
1390     if (GetHelperAddrToMethodHandleMap()->Lookup(addr, &res))
1391     {
1392         return disComp->eeGetMethodFullName(res);
1393     }
1394
1395     // Next check the "normal" registered call targets
1396     if (GetAddrToMethodHandleMap()->Lookup(addr, &res))
1397     {
1398         return disComp->eeGetMethodFullName(res);
1399     }
1400
1401     return nullptr;
1402 }
1403
1404 /*****************************************************************************
1405  * Register a called function address as associated with a CORINFO_METHOD_HANDLE.
1406  *
1407  * Arguments:
1408  *      addr    - The absolute address of the target function.
1409  *      methHnd - The method handle associated with 'addr'.
1410  */
1411
1412 void DisAssembler::disSetMethod(size_t addr, CORINFO_METHOD_HANDLE methHnd)
1413 {
1414     if (!disComp->opts.doLateDisasm)
1415     {
1416         return;
1417     }
1418
1419     if (disComp->eeGetHelperNum(methHnd))
1420     {
1421         DISASM_DUMP("Helper function: %p => %p\n", addr, methHnd);
1422         GetHelperAddrToMethodHandleMap()->Set(addr, methHnd);
1423     }
1424     else
1425     {
1426         DISASM_DUMP("Function: %p => %p\n", addr, methHnd);
1427         GetAddrToMethodHandleMap()->Set(addr, methHnd);
1428     }
1429 }
1430
1431 /*****************************************************************************
1432  * Register a relocation.
1433  *
1434  * Arguments:
1435  *      relocAddr   - The absolute address the relocation applies to.
1436  *      targetAddr  - The absolute address the relocation points to.
1437  */
1438
1439 void DisAssembler::disRecordRelocation(size_t relocAddr, size_t targetAddr)
1440 {
1441     if (!disComp->opts.doLateDisasm)
1442     {
1443         return;
1444     }
1445
1446     DISASM_DUMP("Relocation %p => %p\n", relocAddr, targetAddr);
1447     GetRelocationMap()->Set(relocAddr, targetAddr);
1448 }
1449
1450 /*****************************************************************************
1451  *
1452  * Disassemble the code which has been generated
1453  */
1454
1455 void DisAssembler::disAsmCode(BYTE* hotCodePtr, size_t hotCodeSize, BYTE* coldCodePtr, size_t coldCodeSize)
1456 {
1457     if (!disComp->opts.doLateDisasm)
1458     {
1459         return;
1460     }
1461
1462 #ifdef DEBUG
1463     // Should we make it diffable?
1464     disDiffable = disComp->opts.dspDiffable;
1465 #else  // !DEBUG
1466     // NOTE: non-debug builds are always diffable!
1467     disDiffable = true;
1468 #endif // !DEBUG
1469
1470 #ifdef DEBUG
1471     const wchar_t* fileName = JitConfig.JitLateDisasmTo();
1472     if (fileName != nullptr)
1473     {
1474         errno_t ec = _wfopen_s(&disAsmFile, fileName, W("a+"));
1475         if (ec != 0)
1476         {
1477             disAsmFile = nullptr;
1478         }
1479     }
1480 #else  // !DEBUG
1481     // NOTE: non-DEBUG builds always use jitstdout currently!
1482     disAsmFile = jitstdout;
1483 #endif // !DEBUG
1484
1485     if (disAsmFile == nullptr)
1486     {
1487         disAsmFile = jitstdout;
1488     }
1489
1490     // As this writes to a common file, this is not reentrant.
1491
1492     assert(hotCodeSize > 0);
1493     if (coldCodeSize == 0)
1494     {
1495         fprintf(disAsmFile, "************************** %hs:%hs size 0x%04IX **************************\n\n",
1496                 disCurClassName, disCurMethodName, hotCodeSize);
1497
1498         fprintf(disAsmFile, "Base address : %ph\n", dspAddr(hotCodePtr));
1499     }
1500     else
1501     {
1502         fprintf(disAsmFile,
1503                 "************************** %hs:%hs hot size 0x%04IX cold size 0x%04IX **************************\n\n",
1504                 disCurClassName, disCurMethodName, hotCodeSize, coldCodeSize);
1505
1506         fprintf(disAsmFile, "Hot  address : %ph\n", dspAddr(hotCodePtr));
1507         fprintf(disAsmFile, "Cold address : %ph\n", dspAddr(coldCodePtr));
1508     }
1509
1510     disStartAddr     = 0;
1511     disHotCodeBlock  = (size_t)hotCodePtr;
1512     disHotCodeSize   = hotCodeSize;
1513     disColdCodeBlock = (size_t)coldCodePtr;
1514     disColdCodeSize  = coldCodeSize;
1515
1516     disTotalCodeSize = disHotCodeSize + disColdCodeSize;
1517
1518     disLabels = new (disComp, CMK_DebugOnly) BYTE[disTotalCodeSize]();
1519
1520     DisasmBuffer(disAsmFile, /* printIt */ true);
1521     fprintf(disAsmFile, "\n");
1522
1523     if (disAsmFile != jitstdout)
1524     {
1525         fclose(disAsmFile);
1526     }
1527     else
1528     {
1529         fflush(disAsmFile);
1530     }
1531 }
1532
1533 /*****************************************************************************/
1534 // This function is called for every method. Checks if we are supposed to disassemble
1535 // the method, and where to send the disassembly output.
1536
1537 void DisAssembler::disOpenForLateDisAsm(const char* curMethodName, const char* curClassName, PCCOR_SIGNATURE sig)
1538 {
1539     if (!disComp->opts.doLateDisasm)
1540     {
1541         return;
1542     }
1543
1544     disCurMethodName = curMethodName;
1545     disCurClassName  = curClassName;
1546 }
1547
1548 /*****************************************************************************/
1549
1550 void DisAssembler::disInit(Compiler* pComp)
1551 {
1552     assert(pComp);
1553     disComp                        = pComp;
1554     disHasName                     = false;
1555     disLabels                      = nullptr;
1556     disAddrToMethodHandleMap       = nullptr;
1557     disHelperAddrToMethodHandleMap = nullptr;
1558     disRelocationMap               = nullptr;
1559     disDiffable                    = false;
1560     disAsmFile                     = nullptr;
1561 }
1562
1563 /*****************************************************************************/
1564 #endif // LATE_DISASM
1565 /*****************************************************************************/