Sync may31 release/8.0-tizen (#510)
[platform/upstream/dotnet/runtime.git] / src / coreclr / jit / ee_il_dll.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
4 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XX                                                                           XX
7 XX                            ee_jit.cpp                                     XX
8 XX                                                                           XX
9 XX   The functionality needed for the JIT DLL. Includes the DLL entry point  XX
10 XX                                                                           XX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
13 */
14
15 #include "jitpch.h"
16 #ifdef _MSC_VER
17 #pragma hdrstop
18 #endif
19 #include "emit.h"
20 #include "corexcep.h"
21
22 #if !defined(HOST_UNIX)
23 #include <io.h>    // For _dup, _setmode
24 #include <fcntl.h> // For _O_TEXT
25 #include <errno.h> // For EINVAL
26 #endif
27
28 #ifndef DLLEXPORT
29 #define DLLEXPORT
30 #endif // !DLLEXPORT
31
32 /*****************************************************************************/
33
34 ICorJitHost* g_jitHost        = nullptr;
35 bool         g_jitInitialized = false;
36
37 /*****************************************************************************/
38
39 extern "C" DLLEXPORT void jitStartup(ICorJitHost* jitHost)
40 {
41     if (g_jitInitialized)
42     {
43         if (jitHost != g_jitHost)
44         {
45             // We normally don't expect jitStartup() to be invoked more than once.
46             // (We check whether it has been called once due to an abundance of caution.)
47             // However, during SuperPMI playback of MCH file, we need to JIT many different methods.
48             // Each one carries its own environment configuration state.
49             // So, we need the JIT to reload the JitConfig state for each change in the environment state of the
50             // replayed compilations.
51             // We do this by calling jitStartup with a different ICorJitHost,
52             // and have the JIT re-initialize its JitConfig state when this happens.
53             JitConfig.destroy(g_jitHost);
54             JitConfig.initialize(jitHost);
55             g_jitHost = jitHost;
56         }
57         return;
58     }
59
60 #ifdef HOST_UNIX
61     int err = PAL_InitializeDLL();
62     if (err != 0)
63     {
64         return;
65     }
66 #endif
67
68     g_jitHost = jitHost;
69
70     assert(!JitConfig.isInitialized());
71     JitConfig.initialize(jitHost);
72
73 #ifdef FEATURE_TRACELOGGING
74     JitTelemetry::NotifyDllProcessAttach();
75 #endif
76     Compiler::compStartup();
77
78     g_jitInitialized = true;
79 }
80
81 static FILE* volatile s_jitstdout;
82
83 static FILE* jitstdoutInit()
84 {
85     const WCHAR* jitStdOutFile = JitConfig.JitStdOutFile();
86     FILE*        file          = nullptr;
87     if (jitStdOutFile != nullptr)
88     {
89         file = _wfopen(jitStdOutFile, W("a"));
90         assert(file != nullptr);
91     }
92
93 #if !defined(HOST_UNIX)
94     if (file == nullptr)
95     {
96         int stdoutFd = _fileno(procstdout());
97         // Check fileno error output(s) -1 may overlap with errno result
98         // but is included for completeness.
99         // We want to detect the case where the initial handle is null
100         // or bogus and avoid making further calls.
101         if ((stdoutFd != -1) && (stdoutFd != -2) && (errno != EINVAL))
102         {
103             int jitstdoutFd = _dup(stdoutFd);
104             // Check the error status returned by dup.
105             if (jitstdoutFd != -1)
106             {
107                 _setmode(jitstdoutFd, _O_TEXT);
108                 file = _fdopen(jitstdoutFd, "w");
109                 assert(file != nullptr);
110
111                 // Prevent the FILE* from buffering its output in order to avoid calls to
112                 // `fflush()` throughout the code.
113                 setvbuf(file, nullptr, _IONBF, 0);
114             }
115         }
116     }
117 #endif // !HOST_UNIX
118
119     if (file == nullptr)
120     {
121         file = procstdout();
122     }
123
124     FILE* observed = InterlockedCompareExchangeT(&s_jitstdout, file, nullptr);
125
126     if (observed != nullptr)
127     {
128         if (file != procstdout())
129         {
130             fclose(file);
131         }
132
133         return observed;
134     }
135
136     return file;
137 }
138
139 FILE* jitstdout()
140 {
141     FILE* file = s_jitstdout;
142     if (file != nullptr)
143     {
144         return file;
145     }
146
147     return jitstdoutInit();
148 }
149
150 // Like printf/logf, but only outputs to jitstdout -- skips call back into EE.
151 int jitprintf(const char* fmt, ...)
152 {
153     va_list vl;
154     va_start(vl, fmt);
155     int status = vfprintf(jitstdout(), fmt, vl);
156     va_end(vl);
157     return status;
158 }
159
160 void jitShutdown(bool processIsTerminating)
161 {
162     if (!g_jitInitialized)
163     {
164         return;
165     }
166
167     Compiler::compShutdown();
168
169     FILE* file = s_jitstdout;
170     if ((file != nullptr) && (file != procstdout()))
171     {
172         // When the process is terminating, the fclose call is unnecessary and is also prone to
173         // crashing since the UCRT itself often frees the backing memory earlier on in the
174         // termination sequence.
175         if (!processIsTerminating)
176         {
177             fclose(file);
178         }
179     }
180
181 #ifdef FEATURE_TRACELOGGING
182     JitTelemetry::NotifyDllProcessDetach();
183 #endif
184
185     g_jitInitialized = false;
186 }
187
188 /*****************************************************************************/
189
190 static CILJit g_CILJit;
191
192 DLLEXPORT ICorJitCompiler* getJit()
193 {
194     if (!g_jitInitialized)
195     {
196         return nullptr;
197     }
198
199     return &g_CILJit;
200 }
201
202 /*****************************************************************************/
203
204 // Information kept in thread-local storage. This is used in the noway_assert exceptional path.
205 // If you are using it more broadly in retail code, you would need to understand the
206 // performance implications of accessing TLS.
207
208 thread_local void* gJitTls = nullptr;
209
210 static void* GetJitTls()
211 {
212     return gJitTls;
213 }
214
215 void SetJitTls(void* value)
216 {
217     gJitTls = value;
218 }
219
220 #if defined(DEBUG)
221
222 JitTls::JitTls(ICorJitInfo* jitInfo) : m_compiler(nullptr), m_logEnv(jitInfo)
223 {
224     m_next = reinterpret_cast<JitTls*>(GetJitTls());
225     SetJitTls(this);
226 }
227
228 JitTls::~JitTls()
229 {
230     SetJitTls(m_next);
231 }
232
233 LogEnv* JitTls::GetLogEnv()
234 {
235     return &reinterpret_cast<JitTls*>(GetJitTls())->m_logEnv;
236 }
237
238 Compiler* JitTls::GetCompiler()
239 {
240     return reinterpret_cast<JitTls*>(GetJitTls())->m_compiler;
241 }
242
243 void JitTls::SetCompiler(Compiler* compiler)
244 {
245     reinterpret_cast<JitTls*>(GetJitTls())->m_compiler = compiler;
246 }
247
248 #else // !defined(DEBUG)
249
250 JitTls::JitTls(ICorJitInfo* jitInfo)
251 {
252 }
253
254 JitTls::~JitTls()
255 {
256 }
257
258 Compiler* JitTls::GetCompiler()
259 {
260     return reinterpret_cast<Compiler*>(GetJitTls());
261 }
262
263 void JitTls::SetCompiler(Compiler* compiler)
264 {
265     SetJitTls(compiler);
266 }
267
268 #endif // !defined(DEBUG)
269
270 //****************************************************************************
271 // The main JIT function for the 32 bit JIT.  See code:ICorJitCompiler#EEToJitInterface for more on the EE-JIT
272 // interface. Things really don't get going inside the JIT until the code:Compiler::compCompile#Phases
273 // method.  Usually that is where you want to go.
274
275 CorJitResult CILJit::compileMethod(ICorJitInfo*         compHnd,
276                                    CORINFO_METHOD_INFO* methodInfo,
277                                    unsigned             flags,
278                                    uint8_t**            entryAddress,
279                                    uint32_t*            nativeSizeOfCode)
280 {
281     JitFlags jitFlags;
282
283     assert(flags == CORJIT_FLAGS::CORJIT_FLAG_CALL_GETJITFLAGS);
284     CORJIT_FLAGS corJitFlags;
285     DWORD        jitFlagsSize = compHnd->getJitFlags(&corJitFlags, sizeof(corJitFlags));
286     assert(jitFlagsSize == sizeof(corJitFlags));
287     jitFlags.SetFromFlags(corJitFlags);
288
289     int                   result;
290     void*                 methodCodePtr = nullptr;
291     CORINFO_METHOD_HANDLE methodHandle  = methodInfo->ftn;
292
293     JitTls jitTls(compHnd); // Initialize any necessary thread-local state
294
295     assert(methodInfo->ILCode);
296
297     result = jitNativeCode(methodHandle, methodInfo->scope, compHnd, methodInfo, &methodCodePtr, nativeSizeOfCode,
298                            &jitFlags, nullptr);
299
300     if (result == CORJIT_OK)
301     {
302         *entryAddress = (BYTE*)methodCodePtr;
303     }
304
305     return CorJitResult(result);
306 }
307
308 void CILJit::ProcessShutdownWork(ICorStaticInfo* statInfo)
309 {
310     jitShutdown(false);
311
312     Compiler::ProcessShutdownWork(statInfo);
313 }
314
315 /*****************************************************************************
316  * Verify the JIT/EE interface identifier.
317  */
318 void CILJit::getVersionIdentifier(GUID* versionIdentifier)
319 {
320     assert(versionIdentifier != nullptr);
321     memcpy(versionIdentifier, &JITEEVersionIdentifier, sizeof(GUID));
322 }
323
324 #ifdef TARGET_OS_RUNTIMEDETERMINED
325 bool TargetOS::OSSettingConfigured = false;
326 #ifndef TARGET_UNIX_OS_RUNTIMEDETERMINED // This define is only set if ONLY the different unix variants are
327                                          // runtimedetermined
328 bool TargetOS::IsWindows = false;
329 bool TargetOS::IsUnix    = false;
330 #endif
331 bool TargetOS::IsMacOS = false;
332 #endif
333
334 /*****************************************************************************
335  * Set the OS that this JIT should be generating code for. The contract with the VM
336  * is that this must be called before compileMethod is called.
337  */
338 void CILJit::setTargetOS(CORINFO_OS os)
339 {
340 #ifdef TARGET_OS_RUNTIMEDETERMINED
341     TargetOS::IsMacOS = os == CORINFO_MACOS;
342 #ifndef TARGET_UNIX_OS_RUNTIMEDETERMINED // This define is only set if ONLY the different unix variants are
343                                          // runtimedetermined
344     TargetOS::IsUnix    = (os == CORINFO_UNIX) || (os == CORINFO_MACOS);
345     TargetOS::IsWindows = os == CORINFO_WINNT;
346 #endif
347     TargetOS::OSSettingConfigured = true;
348 #endif
349 }
350
351 //------------------------------------------------------------------------
352 // eeGetArgSize: Returns the number of bytes required for the given type argument
353 //   including padding after the actual value.
354 //
355 // Arguments:
356 //   list - the arg list handle pointing to the argument
357 //   sig  - the signature for the arg's method
358 //
359 // Return value:
360 //   the number of stack slots in stack arguments for the call.
361 //
362 // Notes:
363 //   - On most platforms arguments are passed with TARGET_POINTER_SIZE alignment,
364 //   so all types take an integer number of TARGET_POINTER_SIZE slots.
365 //   It is different for arm64 apple that packs some types without alignment and padding.
366 //   If the argument is passed by reference then the method returns REF size.
367 //
368 unsigned Compiler::eeGetArgSize(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig)
369 {
370 #if defined(TARGET_AMD64)
371
372     // Everything fits into a single 'slot' size
373     // to accommodate irregular sized structs, they are passed byref
374     CLANG_FORMAT_COMMENT_ANCHOR;
375
376 #ifdef UNIX_AMD64_ABI
377     CORINFO_CLASS_HANDLE argClass;
378     CorInfoType          argTypeJit = strip(info.compCompHnd->getArgType(sig, list, &argClass));
379     var_types            argType    = JITtype2varType(argTypeJit);
380     if (varTypeIsStruct(argType))
381     {
382         unsigned structSize = info.compCompHnd->getClassSize(argClass);
383         return roundUp(structSize, TARGET_POINTER_SIZE);
384     }
385 #endif // UNIX_AMD64_ABI
386     return TARGET_POINTER_SIZE;
387
388 #else // !TARGET_AMD64
389
390     CORINFO_CLASS_HANDLE argClass;
391     CorInfoType          argTypeJit = strip(info.compCompHnd->getArgType(sig, list, &argClass));
392     var_types            argType    = JITtype2varType(argTypeJit);
393     unsigned             argSize;
394
395     var_types hfaType = TYP_UNDEF;
396     bool      isHfa   = false;
397
398     if (varTypeIsStruct(argType))
399     {
400         hfaType             = GetHfaType(argClass);
401         isHfa               = (hfaType != TYP_UNDEF);
402         unsigned structSize = info.compCompHnd->getClassSize(argClass);
403
404         // make certain the EE passes us back the right thing for refanys
405         assert(argTypeJit != CORINFO_TYPE_REFANY || structSize == 2 * TARGET_POINTER_SIZE);
406
407         // For each target that supports passing struct args in multiple registers
408         // apply the target specific rules for them here:
409         CLANG_FORMAT_COMMENT_ANCHOR;
410
411 #if FEATURE_MULTIREG_ARGS
412 #if defined(TARGET_ARM64)
413         // Any structs that are larger than MAX_PASS_MULTIREG_BYTES are always passed by reference
414         if (structSize > MAX_PASS_MULTIREG_BYTES)
415         {
416             // This struct is passed by reference using a single 'slot'
417             return TARGET_POINTER_SIZE;
418         }
419         else
420         {
421             // Is the struct larger than 16 bytes
422             if (structSize > (2 * TARGET_POINTER_SIZE))
423             {
424
425                 if (TargetOS::IsWindows && info.compIsVarArgs)
426                 {
427                     // Arm64 Varargs ABI requires passing in general purpose
428                     // registers. Force the decision of whether this is an HFA
429                     // to false to correctly pass as if it was not an HFA.
430                     isHfa = false;
431                 }
432                 if (!isHfa)
433                 {
434                     // This struct is passed by reference using a single 'slot'
435                     return TARGET_POINTER_SIZE;
436                 }
437             }
438         }
439 #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
440         // Any structs that are larger than MAX_PASS_MULTIREG_BYTES are always passed by reference
441         if (structSize > MAX_PASS_MULTIREG_BYTES)
442         {
443             // This struct is passed by reference using a single 'slot'
444             return TARGET_POINTER_SIZE;
445         }
446 //  otherwise will we pass this struct by value in multiple registers
447 #elif !defined(TARGET_ARM)
448         NYI("unknown target");
449 #endif // defined(TARGET_XXX)
450 #endif // FEATURE_MULTIREG_ARGS
451
452         // Otherwise we will pass this struct by value in multiple registers/stack bytes.
453         argSize = structSize;
454     }
455     else
456     {
457         argSize = genTypeSize(argType);
458     }
459
460     const unsigned argSizeAlignment = eeGetArgSizeAlignment(argType, (hfaType == TYP_FLOAT));
461     const unsigned alignedArgSize   = roundUp(argSize, argSizeAlignment);
462     return alignedArgSize;
463
464 #endif
465 }
466
467 //------------------------------------------------------------------------
468 // eeGetArgSizeAlignment: Return alignment for an argument size.
469 //
470 // Arguments:
471 //   type - the argument type
472 //   isFloatHfa - is it an HFA<float> type
473 //
474 // Return value:
475 //   the required argument size alignment in bytes.
476 //
477 // Notes:
478 //   Usually values passed on the stack are aligned to stack slot (i.e. pointer size), except for
479 //   on macOS ARM ABI that allows packing multiple args into a single stack slot.
480 //
481 //   The arg size alignment can be different from the normal alignment. One
482 //   example is on arm32 where a struct containing a double and float can
483 //   explicitly have size 12 but with alignment 8, in which case the size is
484 //   aligned to 4 (the stack slot size) while frame layout must still handle
485 //   aligning the argument to 8.
486 //
487 // static
488 unsigned Compiler::eeGetArgSizeAlignment(var_types type, bool isFloatHfa)
489 {
490     if (compMacOsArm64Abi())
491     {
492         if (isFloatHfa)
493         {
494             assert(varTypeIsStruct(type));
495             return sizeof(float);
496         }
497         if (varTypeIsStruct(type))
498         {
499             return TARGET_POINTER_SIZE;
500         }
501         const unsigned argSize = genTypeSize(type);
502         assert((0 < argSize) && (argSize <= TARGET_POINTER_SIZE));
503         return argSize;
504     }
505     else
506     {
507         return TARGET_POINTER_SIZE;
508     }
509 }
510
511 /*****************************************************************************/
512
513 GenTree* Compiler::eeGetPInvokeCookie(CORINFO_SIG_INFO* szMetaSig)
514 {
515     void *cookie, *pCookie;
516     cookie = info.compCompHnd->GetCookieForPInvokeCalliSig(szMetaSig, &pCookie);
517     assert((cookie == nullptr) != (pCookie == nullptr));
518
519     return gtNewIconEmbHndNode(cookie, pCookie, GTF_ICON_PINVKI_HDL, szMetaSig);
520 }
521
522 //------------------------------------------------------------------------
523 // eeGetArrayDataOffset: Gets the offset of a SDArray's first element
524 //
525 // Return Value:
526 //    The offset to the first array element.
527 //
528 // Notes:
529 //    See the comments at the definition of CORINFO_Array for a description of how arrays are laid out in memory.
530 //
531 // static
532 unsigned Compiler::eeGetArrayDataOffset()
533 {
534     return OFFSETOF__CORINFO_Array__data;
535 }
536
537 //------------------------------------------------------------------------
538 // eeGetMDArrayDataOffset: Gets the offset of a MDArray's first element
539 //
540 // Arguments:
541 //    rank - The array rank
542 //
543 // Return Value:
544 //    The offset to the first array element.
545 //
546 // Assumptions:
547 //    The rank should be greater than 0.
548 //
549 // static
550 unsigned Compiler::eeGetMDArrayDataOffset(unsigned rank)
551 {
552     assert(rank > 0);
553     // Note that below we're specifically using genTypeSize(TYP_INT) because array
554     // indices are not native int.
555     return eeGetArrayDataOffset() + 2 * genTypeSize(TYP_INT) * rank;
556 }
557
558 //------------------------------------------------------------------------
559 // eeGetMDArrayLengthOffset: Returns the offset from the Array object to the
560 //   size for the given dimension.
561 //
562 // Arguments:
563 //    rank      - the rank of the array
564 //    dimension - the dimension for which the lower bound offset will be returned.
565 //
566 // Return Value:
567 //    The offset.
568 //
569 // static
570 unsigned Compiler::eeGetMDArrayLengthOffset(unsigned rank, unsigned dimension)
571 {
572     // Note that we don't actually need the `rank` value for this calculation, but we pass it anyway,
573     // to be consistent with other MD array functions.
574     assert(rank > 0);
575     assert(dimension < rank);
576     // Note that the lower bound and length fields of the Array object are always TYP_INT, even on 64-bit targets.
577     return eeGetArrayDataOffset() + genTypeSize(TYP_INT) * dimension;
578 }
579
580 //------------------------------------------------------------------------
581 // eeGetMDArrayLowerBoundOffset: Returns the offset from the Array object to the
582 //   lower bound for the given dimension.
583 //
584 // Arguments:
585 //    rank      - the rank of the array
586 //    dimension - the dimension for which the lower bound offset will be returned.
587 //
588 // Return Value:
589 //    The offset.
590 //
591 // static
592 unsigned Compiler::eeGetMDArrayLowerBoundOffset(unsigned rank, unsigned dimension)
593 {
594     assert(rank > 0);
595     assert(dimension < rank);
596     // Note that the lower bound and length fields of the Array object are always TYP_INT, even on 64-bit targets.
597     return eeGetArrayDataOffset() + genTypeSize(TYP_INT) * (dimension + rank);
598 }
599
600 /*****************************************************************************/
601
602 void Compiler::eeGetStmtOffsets()
603 {
604     ULONG32                      offsetsCount;
605     uint32_t*                    offsets;
606     ICorDebugInfo::BoundaryTypes offsetsImplicit;
607
608     if (compIsForInlining())
609     {
610         // We do not get explicit boundaries for inlinees, only implicit ones.
611         offsetsImplicit = impInlineRoot()->info.compStmtOffsetsImplicit;
612         offsetsCount    = 0;
613         offsets         = nullptr;
614     }
615     else
616     {
617         info.compCompHnd->getBoundaries(info.compMethodHnd, &offsetsCount, &offsets, &offsetsImplicit);
618     }
619
620     /* Set the implicit boundaries */
621
622     info.compStmtOffsetsImplicit = (ICorDebugInfo::BoundaryTypes)offsetsImplicit;
623
624     /* Process the explicit boundaries */
625
626     info.compStmtOffsetsCount = 0;
627
628     if (offsetsCount == 0)
629     {
630         return;
631     }
632
633     info.compStmtOffsets = new (this, CMK_DebugInfo) IL_OFFSET[offsetsCount];
634
635     for (unsigned i = 0; i < offsetsCount; i++)
636     {
637         if (offsets[i] > info.compILCodeSize)
638         {
639             continue;
640         }
641
642         info.compStmtOffsets[info.compStmtOffsetsCount] = offsets[i];
643         info.compStmtOffsetsCount++;
644     }
645
646     info.compCompHnd->freeArray(offsets);
647 }
648
649 /*****************************************************************************
650  *
651  *                  Debugging support - Local var info
652  */
653
654 void Compiler::eeSetLVcount(unsigned count)
655 {
656     assert(opts.compScopeInfo);
657
658     JITDUMP("VarLocInfo count is %d\n", count);
659
660     eeVarsCount = count;
661     if (eeVarsCount)
662     {
663         eeVars = (VarResultInfo*)info.compCompHnd->allocateArray(eeVarsCount * sizeof(eeVars[0]));
664     }
665     else
666     {
667         eeVars = nullptr;
668     }
669 }
670
671 void Compiler::eeSetLVinfo(unsigned                          which,
672                            UNATIVE_OFFSET                    startOffs,
673                            UNATIVE_OFFSET                    length,
674                            unsigned                          varNum,
675                            const CodeGenInterface::siVarLoc& varLoc)
676 {
677     // ICorDebugInfo::VarLoc and CodeGenInterface::siVarLoc have to overlap
678     // This is checked in siInit()
679
680     assert(opts.compScopeInfo);
681     assert(eeVarsCount > 0);
682     assert(which < eeVarsCount);
683
684     if (eeVars != nullptr)
685     {
686         eeVars[which].startOffset = startOffs;
687         eeVars[which].endOffset   = startOffs + length;
688         eeVars[which].varNumber   = varNum;
689         eeVars[which].loc         = varLoc;
690     }
691 }
692
693 void Compiler::eeSetLVdone()
694 {
695     // necessary but not sufficient condition that the 2 struct definitions overlap
696     assert(sizeof(eeVars[0]) == sizeof(ICorDebugInfo::NativeVarInfo));
697     assert(opts.compScopeInfo);
698
699 #ifdef DEBUG
700     if (verbose || opts.dspDebugInfo)
701     {
702         eeDispVars(info.compMethodHnd, eeVarsCount, (ICorDebugInfo::NativeVarInfo*)eeVars);
703     }
704 #endif // DEBUG
705     if ((eeVarsCount == 0) && (eeVars != nullptr))
706     {
707         // We still call setVars with nullptr when eeVarsCount is 0 as part of the contract.
708         // We also need to free the nonused memory.
709         info.compCompHnd->freeArray(eeVars);
710         eeVars = nullptr;
711     }
712
713     info.compCompHnd->setVars(info.compMethodHnd, eeVarsCount, (ICorDebugInfo::NativeVarInfo*)eeVars);
714     eeVars = nullptr; // We give up ownership after setVars()
715 }
716
717 void Compiler::eeGetVars()
718 {
719     ICorDebugInfo::ILVarInfo* varInfoTable;
720     ULONG32                   varInfoCount;
721     bool                      extendOthers;
722
723     info.compCompHnd->getVars(info.compMethodHnd, &varInfoCount, &varInfoTable, &extendOthers);
724
725 #ifdef DEBUG
726     if (verbose)
727     {
728         printf("getVars() returned cVars = %d, extendOthers = %s\n", varInfoCount, extendOthers ? "true" : "false");
729     }
730 #endif
731
732     // Over allocate in case extendOthers is set.
733
734     SIZE_T varInfoCountExtra = varInfoCount;
735     if (extendOthers)
736     {
737         varInfoCountExtra += info.compLocalsCount;
738     }
739
740     if (varInfoCountExtra == 0)
741     {
742         return;
743     }
744
745     info.compVarScopes = new (this, CMK_DebugInfo) VarScopeDsc[varInfoCountExtra];
746
747     VarScopeDsc*              localVarPtr = info.compVarScopes;
748     ICorDebugInfo::ILVarInfo* v           = varInfoTable;
749
750     for (unsigned i = 0; i < varInfoCount; i++, v++)
751     {
752 #ifdef DEBUG
753         if (verbose)
754         {
755             printf("var:%d start:%d end:%d\n", v->varNumber, v->startOffset, v->endOffset);
756         }
757 #endif
758
759         if (v->startOffset >= v->endOffset)
760         {
761             continue;
762         }
763
764         assert(v->startOffset <= info.compILCodeSize);
765         assert(v->endOffset <= info.compILCodeSize);
766
767         localVarPtr->vsdLifeBeg = v->startOffset;
768         localVarPtr->vsdLifeEnd = v->endOffset;
769         localVarPtr->vsdLVnum   = i;
770         localVarPtr->vsdVarNum  = compMapILvarNum(v->varNumber);
771
772 #ifdef DEBUG
773         localVarPtr->vsdName = gtGetLclVarName(localVarPtr->vsdVarNum);
774 #endif
775
776         localVarPtr++;
777         info.compVarScopesCount++;
778     }
779
780     /* If extendOthers is set, then assume the scope of unreported vars
781        is the entire method. Note that this will cause fgExtendDbgLifetimes()
782        to zero-initialize all of them. This will be expensive if it's used
783        for too many variables.
784      */
785     if (extendOthers)
786     {
787         // Allocate a bit-array for all the variables and initialize to false
788
789         bool*    varInfoProvided = getAllocator(CMK_Unknown).allocate<bool>(info.compLocalsCount);
790         unsigned i;
791         for (i = 0; i < info.compLocalsCount; i++)
792         {
793             varInfoProvided[i] = false;
794         }
795
796         // Find which vars have absolutely no varInfo provided
797
798         for (i = 0; i < info.compVarScopesCount; i++)
799         {
800             varInfoProvided[info.compVarScopes[i].vsdVarNum] = true;
801         }
802
803         // Create entries for the variables with no varInfo
804
805         for (unsigned varNum = 0; varNum < info.compLocalsCount; varNum++)
806         {
807             if (varInfoProvided[varNum])
808             {
809                 continue;
810             }
811
812             // Create a varInfo with scope over the entire method
813
814             localVarPtr->vsdLifeBeg = 0;
815             localVarPtr->vsdLifeEnd = info.compILCodeSize;
816             localVarPtr->vsdVarNum  = varNum;
817             localVarPtr->vsdLVnum   = info.compVarScopesCount;
818
819 #ifdef DEBUG
820             localVarPtr->vsdName = gtGetLclVarName(localVarPtr->vsdVarNum);
821 #endif
822
823             localVarPtr++;
824             info.compVarScopesCount++;
825         }
826     }
827
828     assert(localVarPtr <= info.compVarScopes + varInfoCountExtra);
829
830     if (varInfoCount != 0)
831     {
832         info.compCompHnd->freeArray(varInfoTable);
833     }
834
835 #ifdef DEBUG
836     if (verbose)
837     {
838         compDispLocalVars();
839     }
840 #endif // DEBUG
841 }
842
843 #ifdef DEBUG
844 void Compiler::eeDispVar(ICorDebugInfo::NativeVarInfo* var)
845 {
846     const char* name = nullptr;
847
848     if (var->varNumber == (DWORD)ICorDebugInfo::VARARGS_HND_ILNUM)
849     {
850         name = "varargsHandle";
851     }
852     else if (var->varNumber == (DWORD)ICorDebugInfo::RETBUF_ILNUM)
853     {
854         name = "retBuff";
855     }
856     else if (var->varNumber == (DWORD)ICorDebugInfo::TYPECTXT_ILNUM)
857     {
858         name = "typeCtx";
859     }
860     if (0 <= var->varNumber && var->varNumber < lvaCount)
861     {
862         printf("(");
863         gtDispLclVar(var->varNumber, false);
864         printf(")");
865     }
866     else
867     {
868         printf("(%10s)", (VarNameToStr(name) == nullptr) ? "UNKNOWN" : VarNameToStr(name));
869     }
870     printf(" : From %08Xh to %08Xh, in ", var->startOffset, var->endOffset);
871
872     switch ((CodeGenInterface::siVarLocType)var->loc.vlType)
873     {
874         case CodeGenInterface::VLT_REG:
875         case CodeGenInterface::VLT_REG_BYREF:
876         case CodeGenInterface::VLT_REG_FP:
877             printf("%s", getRegName(var->loc.vlReg.vlrReg));
878             if (var->loc.vlType == (ICorDebugInfo::VarLocType)CodeGenInterface::VLT_REG_BYREF)
879             {
880                 printf(" byref");
881             }
882             break;
883
884         case CodeGenInterface::VLT_STK:
885         case CodeGenInterface::VLT_STK_BYREF:
886             if ((int)var->loc.vlStk.vlsBaseReg != (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
887             {
888                 printf("%s[%d] (1 slot)", getRegName(var->loc.vlStk.vlsBaseReg), var->loc.vlStk.vlsOffset);
889             }
890             else
891             {
892                 printf(STR_SPBASE "'[%d] (1 slot)", var->loc.vlStk.vlsOffset);
893             }
894             if (var->loc.vlType == (ICorDebugInfo::VarLocType)CodeGenInterface::VLT_REG_BYREF)
895             {
896                 printf(" byref");
897             }
898             break;
899
900         case CodeGenInterface::VLT_REG_REG:
901             printf("%s-%s", getRegName(var->loc.vlRegReg.vlrrReg1), getRegName(var->loc.vlRegReg.vlrrReg2));
902             break;
903
904 #ifndef TARGET_AMD64
905         case CodeGenInterface::VLT_REG_STK:
906             if ((int)var->loc.vlRegStk.vlrsStk.vlrssBaseReg != (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
907             {
908                 printf("%s-%s[%d]", getRegName(var->loc.vlRegStk.vlrsReg),
909                        getRegName(var->loc.vlRegStk.vlrsStk.vlrssBaseReg), var->loc.vlRegStk.vlrsStk.vlrssOffset);
910             }
911             else
912             {
913                 printf("%s-" STR_SPBASE "'[%d]", getRegName(var->loc.vlRegStk.vlrsReg),
914                        var->loc.vlRegStk.vlrsStk.vlrssOffset);
915             }
916             break;
917
918         case CodeGenInterface::VLT_STK_REG:
919             unreached(); // unexpected
920
921         case CodeGenInterface::VLT_STK2:
922             if ((int)var->loc.vlStk2.vls2BaseReg != (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
923             {
924                 printf("%s[%d] (2 slots)", getRegName(var->loc.vlStk2.vls2BaseReg), var->loc.vlStk2.vls2Offset);
925             }
926             else
927             {
928                 printf(STR_SPBASE "'[%d] (2 slots)", var->loc.vlStk2.vls2Offset);
929             }
930             break;
931
932         case CodeGenInterface::VLT_FPSTK:
933             printf("ST(L-%d)", var->loc.vlFPstk.vlfReg);
934             break;
935
936         case CodeGenInterface::VLT_FIXED_VA:
937             printf("fxd_va[%d]", var->loc.vlFixedVarArg.vlfvOffset);
938             break;
939 #endif // !TARGET_AMD64
940
941         default:
942             unreached(); // unexpected
943     }
944
945     printf("\n");
946 }
947
948 // Same parameters as ICorStaticInfo::setVars().
949 void Compiler::eeDispVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo* vars)
950 {
951     // Estimate number of unique vars with debug info
952     //
953     ALLVARSET_TP uniqueVars(AllVarSetOps::MakeEmpty(this));
954     for (unsigned i = 0; i < cVars; i++)
955     {
956         // ignore "special vars" and out of bounds vars
957         if ((((int)vars[i].varNumber) >= 0) && (vars[i].varNumber < lclMAX_ALLSET_TRACKED))
958         {
959             AllVarSetOps::AddElemD(this, uniqueVars, vars[i].varNumber);
960         }
961     }
962
963     printf("; Variable debug info: %d live ranges, %d vars for method %s\n", cVars,
964            AllVarSetOps::Count(this, uniqueVars), info.compFullName);
965
966     for (unsigned i = 0; i < cVars; i++)
967     {
968         eeDispVar(&vars[i]);
969     }
970 }
971 #endif // DEBUG
972
973 /*****************************************************************************
974  *
975  *                  Debugging support - Line number info
976  */
977
978 void Compiler::eeSetLIcount(unsigned count)
979 {
980     assert(opts.compDbgInfo);
981
982     eeBoundariesCount = count;
983     if (eeBoundariesCount)
984     {
985         eeBoundaries =
986             (ICorDebugInfo::OffsetMapping*)info.compCompHnd->allocateArray(eeBoundariesCount * sizeof(eeBoundaries[0]));
987     }
988     else
989     {
990         eeBoundaries = nullptr;
991     }
992 }
993
994 void Compiler::eeSetLIinfo(unsigned which, UNATIVE_OFFSET nativeOffset, IPmappingDscKind kind, const ILLocation& loc)
995 {
996     assert(opts.compDbgInfo);
997     assert(eeBoundariesCount > 0 && eeBoundaries != nullptr);
998     assert(which < eeBoundariesCount);
999
1000     eeBoundaries[which].nativeOffset = nativeOffset;
1001     eeBoundaries[which].source       = (ICorDebugInfo::SourceTypes)0;
1002
1003     switch (kind)
1004     {
1005         case IPmappingDscKind::Normal:
1006             eeBoundaries[which].ilOffset = loc.GetOffset();
1007             eeBoundaries[which].source   = loc.EncodeSourceTypes();
1008             break;
1009         case IPmappingDscKind::Prolog:
1010             eeBoundaries[which].ilOffset = ICorDebugInfo::PROLOG;
1011             eeBoundaries[which].source   = ICorDebugInfo::STACK_EMPTY;
1012             break;
1013         case IPmappingDscKind::Epilog:
1014             eeBoundaries[which].ilOffset = ICorDebugInfo::EPILOG;
1015             eeBoundaries[which].source   = ICorDebugInfo::STACK_EMPTY;
1016             break;
1017         case IPmappingDscKind::NoMapping:
1018             eeBoundaries[which].ilOffset = ICorDebugInfo::NO_MAPPING;
1019             eeBoundaries[which].source   = ICorDebugInfo::STACK_EMPTY;
1020             break;
1021         default:
1022             unreached();
1023     }
1024 }
1025
1026 void Compiler::eeSetLIdone()
1027 {
1028     assert(opts.compDbgInfo);
1029
1030 #if defined(DEBUG)
1031     if (verbose || opts.dspDebugInfo)
1032     {
1033         eeDispLineInfos();
1034     }
1035 #endif // DEBUG
1036
1037     // necessary but not sufficient condition that the 2 struct definitions overlap
1038     assert(sizeof(eeBoundaries[0]) == sizeof(ICorDebugInfo::OffsetMapping));
1039
1040     info.compCompHnd->setBoundaries(info.compMethodHnd, eeBoundariesCount, (ICorDebugInfo::OffsetMapping*)eeBoundaries);
1041
1042     eeBoundaries = nullptr; // we give up ownership after setBoundaries();
1043 }
1044
1045 #if defined(DEBUG)
1046
1047 void Compiler::eeDispILOffs(IL_OFFSET offs)
1048 {
1049     printf("0x%04X", offs);
1050 }
1051
1052 /* static */
1053 void Compiler::eeDispSourceMappingOffs(uint32_t offs)
1054 {
1055     const char* specialOffs[] = {"EPILOG", "PROLOG", "NO_MAP"};
1056
1057     switch ((int)offs) // Need the cast since offs is unsigned and the case statements are comparing to signed.
1058     {
1059         case ICorDebugInfo::EPILOG:
1060         case ICorDebugInfo::PROLOG:
1061         case ICorDebugInfo::NO_MAPPING:
1062             assert(DWORD(ICorDebugInfo::EPILOG) + 1 == (unsigned)ICorDebugInfo::PROLOG);
1063             assert(DWORD(ICorDebugInfo::EPILOG) + 2 == (unsigned)ICorDebugInfo::NO_MAPPING);
1064             int specialOffsNum;
1065             specialOffsNum = offs - DWORD(ICorDebugInfo::EPILOG);
1066             printf("%s", specialOffs[specialOffsNum]);
1067             break;
1068         default:
1069             eeDispILOffs(offs);
1070             break;
1071     }
1072 }
1073
1074 /* static */
1075 void Compiler::eeDispLineInfo(const ICorDebugInfo::OffsetMapping* line)
1076 {
1077     printf("IL offs ");
1078
1079     eeDispSourceMappingOffs(line->ilOffset);
1080
1081     printf(" : 0x%08X", line->nativeOffset);
1082     if (line->source != 0)
1083     {
1084         // It seems like it should probably never be zero since ICorDebugInfo::SOURCE_TYPE_INVALID is zero.
1085         // However, the JIT has always generated this and printed "stack non-empty".
1086
1087         printf(" ( ");
1088         if ((line->source & ICorDebugInfo::STACK_EMPTY) != 0)
1089         {
1090             printf("STACK_EMPTY ");
1091         }
1092         if ((line->source & ICorDebugInfo::CALL_INSTRUCTION) != 0)
1093         {
1094             printf("CALL_INSTRUCTION ");
1095         }
1096         if ((line->source & ICorDebugInfo::CALL_SITE) != 0)
1097         {
1098             printf("CALL_SITE ");
1099         }
1100         printf(")");
1101     }
1102     printf("\n");
1103
1104     // We don't expect to see any other bits.
1105     assert((line->source & ~(ICorDebugInfo::STACK_EMPTY | ICorDebugInfo::CALL_INSTRUCTION)) == 0);
1106 }
1107
1108 void Compiler::eeDispLineInfos()
1109 {
1110     printf("IP mapping count : %d\n", eeBoundariesCount); // this might be zero
1111     for (unsigned i = 0; i < eeBoundariesCount; i++)
1112     {
1113         eeDispLineInfo(&eeBoundaries[i]);
1114     }
1115     printf("\n");
1116 }
1117 #endif // DEBUG
1118
1119 /*****************************************************************************
1120  *
1121  *                      ICorJitInfo wrapper functions
1122  *
1123  * In many cases here, we don't tell the VM about various unwind or EH information if
1124  * we're an altjit for an unexpected architecture. If it's not a same architecture JIT
1125  * (e.g., host AMD64, target ARM64), then VM will get confused anyway.
1126  */
1127
1128 void Compiler::eeAllocMem(AllocMemArgs* args, const UNATIVE_OFFSET roDataSectionAlignment)
1129 {
1130 #ifdef DEBUG
1131
1132     // Fake splitting implementation: place hot/cold code in contiguous section.
1133     UNATIVE_OFFSET coldCodeOffset = 0;
1134     if (JitConfig.JitFakeProcedureSplitting() && (args->coldCodeSize > 0))
1135     {
1136         coldCodeOffset = args->hotCodeSize;
1137         assert(coldCodeOffset > 0);
1138         args->hotCodeSize += args->coldCodeSize;
1139         args->coldCodeSize = 0;
1140     }
1141
1142 #endif // DEBUG
1143
1144 #if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1145
1146     // For arm64/LoongArch64/RISCV64, we want to allocate JIT data always adjacent to code similar to what native
1147     // compiler does.
1148     // This way allows us to use a single `ldr` to access such data like float constant/jmp table.
1149     // For LoongArch64 using `pcaddi + ld` to access such data.
1150
1151     UNATIVE_OFFSET roDataAlignmentDelta = 0;
1152     if (args->roDataSize > 0)
1153     {
1154         roDataAlignmentDelta = AlignmentPad(args->hotCodeSize, roDataSectionAlignment);
1155     }
1156
1157     const UNATIVE_OFFSET roDataOffset = args->hotCodeSize + roDataAlignmentDelta;
1158     args->hotCodeSize                 = roDataOffset + args->roDataSize;
1159     args->roDataSize                  = 0;
1160
1161 #endif // defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1162
1163     info.compCompHnd->allocMem(args);
1164
1165 #ifdef DEBUG
1166
1167     if (JitConfig.JitFakeProcedureSplitting() && (coldCodeOffset > 0))
1168     {
1169         // Fix up cold code pointers. Cold section is adjacent to hot section.
1170         assert(args->coldCodeBlock == nullptr);
1171         assert(args->coldCodeBlockRW == nullptr);
1172         args->coldCodeBlock   = ((BYTE*)args->hotCodeBlock) + coldCodeOffset;
1173         args->coldCodeBlockRW = ((BYTE*)args->hotCodeBlockRW) + coldCodeOffset;
1174     }
1175
1176 #endif // DEBUG
1177
1178 #if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1179
1180     // Fix up data section pointers.
1181     assert(args->roDataBlock == nullptr);
1182     assert(args->roDataBlockRW == nullptr);
1183     args->roDataBlock   = ((BYTE*)args->hotCodeBlock) + roDataOffset;
1184     args->roDataBlockRW = ((BYTE*)args->hotCodeBlockRW) + roDataOffset;
1185
1186 #endif // defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1187 }
1188
1189 void Compiler::eeReserveUnwindInfo(bool isFunclet, bool isColdCode, ULONG unwindSize)
1190 {
1191 #ifdef DEBUG
1192     if (verbose)
1193     {
1194         printf("reserveUnwindInfo(isFunclet=%s, isColdCode=%s, unwindSize=0x%x)\n", isFunclet ? "true" : "false",
1195                isColdCode ? "true" : "false", unwindSize);
1196     }
1197 #endif // DEBUG
1198
1199     if (info.compMatchedVM)
1200     {
1201         info.compCompHnd->reserveUnwindInfo(isFunclet, isColdCode, unwindSize);
1202     }
1203 }
1204
1205 void Compiler::eeAllocUnwindInfo(BYTE*          pHotCode,
1206                                  BYTE*          pColdCode,
1207                                  ULONG          startOffset,
1208                                  ULONG          endOffset,
1209                                  ULONG          unwindSize,
1210                                  BYTE*          pUnwindBlock,
1211                                  CorJitFuncKind funcKind)
1212 {
1213 #ifdef DEBUG
1214     if (verbose)
1215     {
1216         printf("allocUnwindInfo(pHotCode=0x%p, pColdCode=0x%p, startOffset=0x%x, endOffset=0x%x, unwindSize=0x%x, "
1217                "pUnwindBlock=0x%p, funKind=%d",
1218                dspPtr(pHotCode), dspPtr(pColdCode), startOffset, endOffset, unwindSize, dspPtr(pUnwindBlock), funcKind);
1219         switch (funcKind)
1220         {
1221             case CORJIT_FUNC_ROOT:
1222                 printf(" (main function)");
1223                 break;
1224             case CORJIT_FUNC_HANDLER:
1225                 printf(" (handler)");
1226                 break;
1227             case CORJIT_FUNC_FILTER:
1228                 printf(" (filter)");
1229                 break;
1230             default:
1231                 printf(" (ILLEGAL)");
1232                 break;
1233         }
1234         printf(")\n");
1235     }
1236 #endif // DEBUG
1237
1238     if (info.compMatchedVM)
1239     {
1240         info.compCompHnd->allocUnwindInfo(pHotCode, pColdCode, startOffset, endOffset, unwindSize, pUnwindBlock,
1241                                           funcKind);
1242     }
1243 }
1244
1245 void Compiler::eeSetEHcount(unsigned cEH)
1246 {
1247 #ifdef DEBUG
1248     if (verbose)
1249     {
1250         printf("setEHcount(cEH=%u)\n", cEH);
1251     }
1252 #endif // DEBUG
1253
1254     if (info.compMatchedVM)
1255     {
1256         info.compCompHnd->setEHcount(cEH);
1257     }
1258 }
1259
1260 void Compiler::eeSetEHinfo(unsigned EHnumber, const CORINFO_EH_CLAUSE* clause)
1261 {
1262 #ifdef DEBUG
1263     if (opts.dspEHTable)
1264     {
1265         dispOutgoingEHClause(EHnumber, *clause);
1266     }
1267 #endif // DEBUG
1268
1269     if (info.compMatchedVM)
1270     {
1271         info.compCompHnd->setEHinfo(EHnumber, clause);
1272     }
1273 }
1274
1275 WORD Compiler::eeGetRelocTypeHint(void* target)
1276 {
1277     if (info.compMatchedVM)
1278     {
1279         return info.compCompHnd->getRelocTypeHint(target);
1280     }
1281     else
1282     {
1283         // No hints
1284         return (WORD)-1;
1285     }
1286 }
1287
1288 CORINFO_FIELD_HANDLE Compiler::eeFindJitDataOffs(unsigned dataOffs)
1289 {
1290     // Data offsets are marked by the fact that the low two bits are 0b01 0x1
1291     assert(dataOffs < 0x40000000);
1292     return (CORINFO_FIELD_HANDLE)(size_t)((dataOffs << iaut_SHIFT) | iaut_DATA_OFFSET);
1293 }
1294
1295 bool Compiler::eeIsJitDataOffs(CORINFO_FIELD_HANDLE field)
1296 {
1297     // if 'field' is a jit data offset it has to fit into a 32-bit unsigned int
1298     unsigned value = static_cast<unsigned>(reinterpret_cast<uintptr_t>(field));
1299     if (((CORINFO_FIELD_HANDLE)(size_t)value) != field)
1300     {
1301         return false; // some bits in the upper 32 bits were set, not a jit data offset
1302     }
1303
1304     // Data offsets are marked by the fact that the low two bits are 0b01
1305     return (value & iaut_MASK) == iaut_DATA_OFFSET;
1306 }
1307
1308 int Compiler::eeGetJitDataOffs(CORINFO_FIELD_HANDLE field)
1309 {
1310     // Data offsets are marked by the fact that the low two bits are 0b01 0x1
1311     if (eeIsJitDataOffs(field))
1312     {
1313         unsigned dataOffs = static_cast<unsigned>(reinterpret_cast<uintptr_t>(field));
1314         assert(((CORINFO_FIELD_HANDLE)(size_t)dataOffs) == field);
1315         assert(dataOffs < 0x40000000);
1316
1317         // Shift away the low two bits
1318         return (static_cast<int>(reinterpret_cast<intptr_t>(field))) >> iaut_SHIFT;
1319     }
1320     else
1321     {
1322         return -1;
1323     }
1324 }
1325
1326 /*****************************************************************************
1327  *
1328  *                      ICorStaticInfo wrapper functions
1329  */
1330
1331 #if defined(UNIX_AMD64_ABI)
1332
1333 #ifdef DEBUG
1334 void Compiler::dumpSystemVClassificationType(SystemVClassificationType ct)
1335 {
1336     switch (ct)
1337     {
1338         case SystemVClassificationTypeUnknown:
1339             printf("UNKNOWN");
1340             break;
1341         case SystemVClassificationTypeStruct:
1342             printf("Struct");
1343             break;
1344         case SystemVClassificationTypeNoClass:
1345             printf("NoClass");
1346             break;
1347         case SystemVClassificationTypeMemory:
1348             printf("Memory");
1349             break;
1350         case SystemVClassificationTypeInteger:
1351             printf("Integer");
1352             break;
1353         case SystemVClassificationTypeIntegerReference:
1354             printf("IntegerReference");
1355             break;
1356         case SystemVClassificationTypeIntegerByRef:
1357             printf("IntegerByReference");
1358             break;
1359         case SystemVClassificationTypeSSE:
1360             printf("SSE");
1361             break;
1362         default:
1363             printf("ILLEGAL");
1364             break;
1365     }
1366 }
1367 #endif // DEBUG
1368
1369 void Compiler::eeGetSystemVAmd64PassStructInRegisterDescriptor(
1370     /*IN*/ CORINFO_CLASS_HANDLE                                  structHnd,
1371     /*OUT*/ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr)
1372 {
1373     bool ok = info.compCompHnd->getSystemVAmd64PassStructInRegisterDescriptor(structHnd, structPassInRegDescPtr);
1374     noway_assert(ok);
1375
1376 #ifdef DEBUG
1377     if (verbose)
1378     {
1379         printf("**** getSystemVAmd64PassStructInRegisterDescriptor(0x%x (%s), ...) =>\n", dspPtr(structHnd),
1380                eeGetClassName(structHnd));
1381         printf("        passedInRegisters = %s\n", dspBool(structPassInRegDescPtr->passedInRegisters));
1382         if (structPassInRegDescPtr->passedInRegisters)
1383         {
1384             printf("        eightByteCount   = %d\n", structPassInRegDescPtr->eightByteCount);
1385             for (unsigned int i = 0; i < structPassInRegDescPtr->eightByteCount; i++)
1386             {
1387                 printf("        eightByte #%d -- classification: ", i);
1388                 dumpSystemVClassificationType(structPassInRegDescPtr->eightByteClassifications[i]);
1389                 printf(", byteSize: %d, byteOffset: %d\n", structPassInRegDescPtr->eightByteSizes[i],
1390                        structPassInRegDescPtr->eightByteOffsets[i]);
1391             }
1392         }
1393     }
1394 #endif // DEBUG
1395 }
1396
1397 #endif // UNIX_AMD64_ABI
1398
1399 bool Compiler::eeRunWithErrorTrapImp(void (*function)(void*), void* param)
1400 {
1401     return info.compCompHnd->runWithErrorTrap(function, param);
1402 }
1403
1404 bool Compiler::eeRunWithSPMIErrorTrapImp(void (*function)(void*), void* param)
1405 {
1406     return info.compCompHnd->runWithSPMIErrorTrap(function, param);
1407 }
1408
1409 #ifdef DEBUG
1410 //------------------------------------------------------------------------
1411 // eeTryGetClassSize: wraps getClassSize but if doing SuperPMI replay
1412 // and the value isn't found, use a bogus size.
1413 //
1414 // NOTE: This is only allowed for JitDump output.
1415 //
1416 // Return value:
1417 //      Either the actual class size, or (unsigned)-1 if SuperPMI didn't have it.
1418 //
1419 unsigned Compiler::eeTryGetClassSize(CORINFO_CLASS_HANDLE clsHnd)
1420 {
1421     unsigned classSize = UINT_MAX;
1422     eeRunFunctorWithSPMIErrorTrap([&]() { classSize = info.compCompHnd->getClassSize(clsHnd); });
1423
1424     return classSize;
1425 }
1426
1427 #endif // !DEBUG