1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
4 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
9 XX The functionality needed for the JIT DLL. Includes the DLL entry point XX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
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
32 /*****************************************************************************/
34 ICorJitHost* g_jitHost = nullptr;
35 bool g_jitInitialized = false;
37 /*****************************************************************************/
39 extern "C" DLLEXPORT void jitStartup(ICorJitHost* jitHost)
43 if (jitHost != g_jitHost)
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);
61 int err = PAL_InitializeDLL();
70 assert(!JitConfig.isInitialized());
71 JitConfig.initialize(jitHost);
73 #ifdef FEATURE_TRACELOGGING
74 JitTelemetry::NotifyDllProcessAttach();
76 Compiler::compStartup();
78 g_jitInitialized = true;
81 static FILE* volatile s_jitstdout;
83 static FILE* jitstdoutInit()
85 const WCHAR* jitStdOutFile = JitConfig.JitStdOutFile();
87 if (jitStdOutFile != nullptr)
89 file = _wfopen(jitStdOutFile, W("a"));
90 assert(file != nullptr);
93 #if !defined(HOST_UNIX)
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))
103 int jitstdoutFd = _dup(stdoutFd);
104 // Check the error status returned by dup.
105 if (jitstdoutFd != -1)
107 _setmode(jitstdoutFd, _O_TEXT);
108 file = _fdopen(jitstdoutFd, "w");
109 assert(file != nullptr);
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);
124 FILE* observed = InterlockedCompareExchangeT(&s_jitstdout, file, nullptr);
126 if (observed != nullptr)
128 if (file != procstdout())
141 FILE* file = s_jitstdout;
147 return jitstdoutInit();
150 // Like printf/logf, but only outputs to jitstdout -- skips call back into EE.
151 int jitprintf(const char* fmt, ...)
155 int status = vfprintf(jitstdout(), fmt, vl);
160 void jitShutdown(bool processIsTerminating)
162 if (!g_jitInitialized)
167 Compiler::compShutdown();
169 FILE* file = s_jitstdout;
170 if ((file != nullptr) && (file != procstdout()))
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)
181 #ifdef FEATURE_TRACELOGGING
182 JitTelemetry::NotifyDllProcessDetach();
185 g_jitInitialized = false;
188 /*****************************************************************************/
190 static CILJit g_CILJit;
192 DLLEXPORT ICorJitCompiler* getJit()
194 if (!g_jitInitialized)
202 /*****************************************************************************/
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.
208 thread_local void* gJitTls = nullptr;
210 static void* GetJitTls()
215 void SetJitTls(void* value)
222 JitTls::JitTls(ICorJitInfo* jitInfo) : m_compiler(nullptr), m_logEnv(jitInfo)
224 m_next = reinterpret_cast<JitTls*>(GetJitTls());
233 LogEnv* JitTls::GetLogEnv()
235 return &reinterpret_cast<JitTls*>(GetJitTls())->m_logEnv;
238 Compiler* JitTls::GetCompiler()
240 return reinterpret_cast<JitTls*>(GetJitTls())->m_compiler;
243 void JitTls::SetCompiler(Compiler* compiler)
245 reinterpret_cast<JitTls*>(GetJitTls())->m_compiler = compiler;
248 #else // !defined(DEBUG)
250 JitTls::JitTls(ICorJitInfo* jitInfo)
258 Compiler* JitTls::GetCompiler()
260 return reinterpret_cast<Compiler*>(GetJitTls());
263 void JitTls::SetCompiler(Compiler* compiler)
268 #endif // !defined(DEBUG)
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.
275 CorJitResult CILJit::compileMethod(ICorJitInfo* compHnd,
276 CORINFO_METHOD_INFO* methodInfo,
278 uint8_t** entryAddress,
279 uint32_t* nativeSizeOfCode)
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);
290 void* methodCodePtr = nullptr;
291 CORINFO_METHOD_HANDLE methodHandle = methodInfo->ftn;
293 JitTls jitTls(compHnd); // Initialize any necessary thread-local state
295 assert(methodInfo->ILCode);
297 result = jitNativeCode(methodHandle, methodInfo->scope, compHnd, methodInfo, &methodCodePtr, nativeSizeOfCode,
300 if (result == CORJIT_OK)
302 *entryAddress = (BYTE*)methodCodePtr;
305 return CorJitResult(result);
308 void CILJit::ProcessShutdownWork(ICorStaticInfo* statInfo)
312 Compiler::ProcessShutdownWork(statInfo);
315 /*****************************************************************************
316 * Verify the JIT/EE interface identifier.
318 void CILJit::getVersionIdentifier(GUID* versionIdentifier)
320 assert(versionIdentifier != nullptr);
321 memcpy(versionIdentifier, &JITEEVersionIdentifier, sizeof(GUID));
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
328 bool TargetOS::IsWindows = false;
329 bool TargetOS::IsUnix = false;
331 bool TargetOS::IsMacOS = false;
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.
338 void CILJit::setTargetOS(CORINFO_OS os)
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
344 TargetOS::IsUnix = (os == CORINFO_UNIX) || (os == CORINFO_MACOS);
345 TargetOS::IsWindows = os == CORINFO_WINNT;
347 TargetOS::OSSettingConfigured = true;
351 //------------------------------------------------------------------------
352 // eeGetArgSize: Returns the number of bytes required for the given type argument
353 // including padding after the actual value.
356 // list - the arg list handle pointing to the argument
357 // sig - the signature for the arg's method
360 // the number of stack slots in stack arguments for the call.
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.
368 unsigned Compiler::eeGetArgSize(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig)
370 #if defined(TARGET_AMD64)
372 // Everything fits into a single 'slot' size
373 // to accommodate irregular sized structs, they are passed byref
374 CLANG_FORMAT_COMMENT_ANCHOR;
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))
382 unsigned structSize = info.compCompHnd->getClassSize(argClass);
383 return roundUp(structSize, TARGET_POINTER_SIZE);
385 #endif // UNIX_AMD64_ABI
386 return TARGET_POINTER_SIZE;
388 #else // !TARGET_AMD64
390 CORINFO_CLASS_HANDLE argClass;
391 CorInfoType argTypeJit = strip(info.compCompHnd->getArgType(sig, list, &argClass));
392 var_types argType = JITtype2varType(argTypeJit);
395 var_types hfaType = TYP_UNDEF;
398 if (varTypeIsStruct(argType))
400 hfaType = GetHfaType(argClass);
401 isHfa = (hfaType != TYP_UNDEF);
402 unsigned structSize = info.compCompHnd->getClassSize(argClass);
404 // make certain the EE passes us back the right thing for refanys
405 assert(argTypeJit != CORINFO_TYPE_REFANY || structSize == 2 * TARGET_POINTER_SIZE);
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;
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)
416 // This struct is passed by reference using a single 'slot'
417 return TARGET_POINTER_SIZE;
421 // Is the struct larger than 16 bytes
422 if (structSize > (2 * TARGET_POINTER_SIZE))
425 if (TargetOS::IsWindows && info.compIsVarArgs)
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.
434 // This struct is passed by reference using a single 'slot'
435 return TARGET_POINTER_SIZE;
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)
443 // This struct is passed by reference using a single 'slot'
444 return TARGET_POINTER_SIZE;
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
452 // Otherwise we will pass this struct by value in multiple registers/stack bytes.
453 argSize = structSize;
457 argSize = genTypeSize(argType);
460 const unsigned argSizeAlignment = eeGetArgSizeAlignment(argType, (hfaType == TYP_FLOAT));
461 const unsigned alignedArgSize = roundUp(argSize, argSizeAlignment);
462 return alignedArgSize;
467 //------------------------------------------------------------------------
468 // eeGetArgSizeAlignment: Return alignment for an argument size.
471 // type - the argument type
472 // isFloatHfa - is it an HFA<float> type
475 // the required argument size alignment in bytes.
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.
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.
488 unsigned Compiler::eeGetArgSizeAlignment(var_types type, bool isFloatHfa)
490 if (compMacOsArm64Abi())
494 assert(varTypeIsStruct(type));
495 return sizeof(float);
497 if (varTypeIsStruct(type))
499 return TARGET_POINTER_SIZE;
501 const unsigned argSize = genTypeSize(type);
502 assert((0 < argSize) && (argSize <= TARGET_POINTER_SIZE));
507 return TARGET_POINTER_SIZE;
511 /*****************************************************************************/
513 GenTree* Compiler::eeGetPInvokeCookie(CORINFO_SIG_INFO* szMetaSig)
515 void *cookie, *pCookie;
516 cookie = info.compCompHnd->GetCookieForPInvokeCalliSig(szMetaSig, &pCookie);
517 assert((cookie == nullptr) != (pCookie == nullptr));
519 return gtNewIconEmbHndNode(cookie, pCookie, GTF_ICON_PINVKI_HDL, szMetaSig);
522 //------------------------------------------------------------------------
523 // eeGetArrayDataOffset: Gets the offset of a SDArray's first element
526 // The offset to the first array element.
529 // See the comments at the definition of CORINFO_Array for a description of how arrays are laid out in memory.
532 unsigned Compiler::eeGetArrayDataOffset()
534 return OFFSETOF__CORINFO_Array__data;
537 //------------------------------------------------------------------------
538 // eeGetMDArrayDataOffset: Gets the offset of a MDArray's first element
541 // rank - The array rank
544 // The offset to the first array element.
547 // The rank should be greater than 0.
550 unsigned Compiler::eeGetMDArrayDataOffset(unsigned rank)
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;
558 //------------------------------------------------------------------------
559 // eeGetMDArrayLengthOffset: Returns the offset from the Array object to the
560 // size for the given dimension.
563 // rank - the rank of the array
564 // dimension - the dimension for which the lower bound offset will be returned.
570 unsigned Compiler::eeGetMDArrayLengthOffset(unsigned rank, unsigned dimension)
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.
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;
580 //------------------------------------------------------------------------
581 // eeGetMDArrayLowerBoundOffset: Returns the offset from the Array object to the
582 // lower bound for the given dimension.
585 // rank - the rank of the array
586 // dimension - the dimension for which the lower bound offset will be returned.
592 unsigned Compiler::eeGetMDArrayLowerBoundOffset(unsigned rank, unsigned dimension)
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);
600 /*****************************************************************************/
602 void Compiler::eeGetStmtOffsets()
604 ULONG32 offsetsCount;
606 ICorDebugInfo::BoundaryTypes offsetsImplicit;
608 if (compIsForInlining())
610 // We do not get explicit boundaries for inlinees, only implicit ones.
611 offsetsImplicit = impInlineRoot()->info.compStmtOffsetsImplicit;
617 info.compCompHnd->getBoundaries(info.compMethodHnd, &offsetsCount, &offsets, &offsetsImplicit);
620 /* Set the implicit boundaries */
622 info.compStmtOffsetsImplicit = (ICorDebugInfo::BoundaryTypes)offsetsImplicit;
624 /* Process the explicit boundaries */
626 info.compStmtOffsetsCount = 0;
628 if (offsetsCount == 0)
633 info.compStmtOffsets = new (this, CMK_DebugInfo) IL_OFFSET[offsetsCount];
635 for (unsigned i = 0; i < offsetsCount; i++)
637 if (offsets[i] > info.compILCodeSize)
642 info.compStmtOffsets[info.compStmtOffsetsCount] = offsets[i];
643 info.compStmtOffsetsCount++;
646 info.compCompHnd->freeArray(offsets);
649 /*****************************************************************************
651 * Debugging support - Local var info
654 void Compiler::eeSetLVcount(unsigned count)
656 assert(opts.compScopeInfo);
658 JITDUMP("VarLocInfo count is %d\n", count);
663 eeVars = (VarResultInfo*)info.compCompHnd->allocateArray(eeVarsCount * sizeof(eeVars[0]));
671 void Compiler::eeSetLVinfo(unsigned which,
672 UNATIVE_OFFSET startOffs,
673 UNATIVE_OFFSET length,
675 const CodeGenInterface::siVarLoc& varLoc)
677 // ICorDebugInfo::VarLoc and CodeGenInterface::siVarLoc have to overlap
678 // This is checked in siInit()
680 assert(opts.compScopeInfo);
681 assert(eeVarsCount > 0);
682 assert(which < eeVarsCount);
684 if (eeVars != nullptr)
686 eeVars[which].startOffset = startOffs;
687 eeVars[which].endOffset = startOffs + length;
688 eeVars[which].varNumber = varNum;
689 eeVars[which].loc = varLoc;
693 void Compiler::eeSetLVdone()
695 // necessary but not sufficient condition that the 2 struct definitions overlap
696 assert(sizeof(eeVars[0]) == sizeof(ICorDebugInfo::NativeVarInfo));
697 assert(opts.compScopeInfo);
700 if (verbose || opts.dspDebugInfo)
702 eeDispVars(info.compMethodHnd, eeVarsCount, (ICorDebugInfo::NativeVarInfo*)eeVars);
705 if ((eeVarsCount == 0) && (eeVars != nullptr))
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);
713 info.compCompHnd->setVars(info.compMethodHnd, eeVarsCount, (ICorDebugInfo::NativeVarInfo*)eeVars);
714 eeVars = nullptr; // We give up ownership after setVars()
717 void Compiler::eeGetVars()
719 ICorDebugInfo::ILVarInfo* varInfoTable;
720 ULONG32 varInfoCount;
723 info.compCompHnd->getVars(info.compMethodHnd, &varInfoCount, &varInfoTable, &extendOthers);
728 printf("getVars() returned cVars = %d, extendOthers = %s\n", varInfoCount, extendOthers ? "true" : "false");
732 // Over allocate in case extendOthers is set.
734 SIZE_T varInfoCountExtra = varInfoCount;
737 varInfoCountExtra += info.compLocalsCount;
740 if (varInfoCountExtra == 0)
745 info.compVarScopes = new (this, CMK_DebugInfo) VarScopeDsc[varInfoCountExtra];
747 VarScopeDsc* localVarPtr = info.compVarScopes;
748 ICorDebugInfo::ILVarInfo* v = varInfoTable;
750 for (unsigned i = 0; i < varInfoCount; i++, v++)
755 printf("var:%d start:%d end:%d\n", v->varNumber, v->startOffset, v->endOffset);
759 if (v->startOffset >= v->endOffset)
764 assert(v->startOffset <= info.compILCodeSize);
765 assert(v->endOffset <= info.compILCodeSize);
767 localVarPtr->vsdLifeBeg = v->startOffset;
768 localVarPtr->vsdLifeEnd = v->endOffset;
769 localVarPtr->vsdLVnum = i;
770 localVarPtr->vsdVarNum = compMapILvarNum(v->varNumber);
773 localVarPtr->vsdName = gtGetLclVarName(localVarPtr->vsdVarNum);
777 info.compVarScopesCount++;
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.
787 // Allocate a bit-array for all the variables and initialize to false
789 bool* varInfoProvided = getAllocator(CMK_Unknown).allocate<bool>(info.compLocalsCount);
791 for (i = 0; i < info.compLocalsCount; i++)
793 varInfoProvided[i] = false;
796 // Find which vars have absolutely no varInfo provided
798 for (i = 0; i < info.compVarScopesCount; i++)
800 varInfoProvided[info.compVarScopes[i].vsdVarNum] = true;
803 // Create entries for the variables with no varInfo
805 for (unsigned varNum = 0; varNum < info.compLocalsCount; varNum++)
807 if (varInfoProvided[varNum])
812 // Create a varInfo with scope over the entire method
814 localVarPtr->vsdLifeBeg = 0;
815 localVarPtr->vsdLifeEnd = info.compILCodeSize;
816 localVarPtr->vsdVarNum = varNum;
817 localVarPtr->vsdLVnum = info.compVarScopesCount;
820 localVarPtr->vsdName = gtGetLclVarName(localVarPtr->vsdVarNum);
824 info.compVarScopesCount++;
828 assert(localVarPtr <= info.compVarScopes + varInfoCountExtra);
830 if (varInfoCount != 0)
832 info.compCompHnd->freeArray(varInfoTable);
844 void Compiler::eeDispVar(ICorDebugInfo::NativeVarInfo* var)
846 const char* name = nullptr;
848 if (var->varNumber == (DWORD)ICorDebugInfo::VARARGS_HND_ILNUM)
850 name = "varargsHandle";
852 else if (var->varNumber == (DWORD)ICorDebugInfo::RETBUF_ILNUM)
856 else if (var->varNumber == (DWORD)ICorDebugInfo::TYPECTXT_ILNUM)
860 if (0 <= var->varNumber && var->varNumber < lvaCount)
863 gtDispLclVar(var->varNumber, false);
868 printf("(%10s)", (VarNameToStr(name) == nullptr) ? "UNKNOWN" : VarNameToStr(name));
870 printf(" : From %08Xh to %08Xh, in ", var->startOffset, var->endOffset);
872 switch ((CodeGenInterface::siVarLocType)var->loc.vlType)
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)
884 case CodeGenInterface::VLT_STK:
885 case CodeGenInterface::VLT_STK_BYREF:
886 if ((int)var->loc.vlStk.vlsBaseReg != (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
888 printf("%s[%d] (1 slot)", getRegName(var->loc.vlStk.vlsBaseReg), var->loc.vlStk.vlsOffset);
892 printf(STR_SPBASE "'[%d] (1 slot)", var->loc.vlStk.vlsOffset);
894 if (var->loc.vlType == (ICorDebugInfo::VarLocType)CodeGenInterface::VLT_REG_BYREF)
900 case CodeGenInterface::VLT_REG_REG:
901 printf("%s-%s", getRegName(var->loc.vlRegReg.vlrrReg1), getRegName(var->loc.vlRegReg.vlrrReg2));
905 case CodeGenInterface::VLT_REG_STK:
906 if ((int)var->loc.vlRegStk.vlrsStk.vlrssBaseReg != (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
908 printf("%s-%s[%d]", getRegName(var->loc.vlRegStk.vlrsReg),
909 getRegName(var->loc.vlRegStk.vlrsStk.vlrssBaseReg), var->loc.vlRegStk.vlrsStk.vlrssOffset);
913 printf("%s-" STR_SPBASE "'[%d]", getRegName(var->loc.vlRegStk.vlrsReg),
914 var->loc.vlRegStk.vlrsStk.vlrssOffset);
918 case CodeGenInterface::VLT_STK_REG:
919 unreached(); // unexpected
921 case CodeGenInterface::VLT_STK2:
922 if ((int)var->loc.vlStk2.vls2BaseReg != (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
924 printf("%s[%d] (2 slots)", getRegName(var->loc.vlStk2.vls2BaseReg), var->loc.vlStk2.vls2Offset);
928 printf(STR_SPBASE "'[%d] (2 slots)", var->loc.vlStk2.vls2Offset);
932 case CodeGenInterface::VLT_FPSTK:
933 printf("ST(L-%d)", var->loc.vlFPstk.vlfReg);
936 case CodeGenInterface::VLT_FIXED_VA:
937 printf("fxd_va[%d]", var->loc.vlFixedVarArg.vlfvOffset);
939 #endif // !TARGET_AMD64
942 unreached(); // unexpected
948 // Same parameters as ICorStaticInfo::setVars().
949 void Compiler::eeDispVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo* vars)
951 // Estimate number of unique vars with debug info
953 ALLVARSET_TP uniqueVars(AllVarSetOps::MakeEmpty(this));
954 for (unsigned i = 0; i < cVars; i++)
956 // ignore "special vars" and out of bounds vars
957 if ((((int)vars[i].varNumber) >= 0) && (vars[i].varNumber < lclMAX_ALLSET_TRACKED))
959 AllVarSetOps::AddElemD(this, uniqueVars, vars[i].varNumber);
963 printf("; Variable debug info: %d live ranges, %d vars for method %s\n", cVars,
964 AllVarSetOps::Count(this, uniqueVars), info.compFullName);
966 for (unsigned i = 0; i < cVars; i++)
973 /*****************************************************************************
975 * Debugging support - Line number info
978 void Compiler::eeSetLIcount(unsigned count)
980 assert(opts.compDbgInfo);
982 eeBoundariesCount = count;
983 if (eeBoundariesCount)
986 (ICorDebugInfo::OffsetMapping*)info.compCompHnd->allocateArray(eeBoundariesCount * sizeof(eeBoundaries[0]));
990 eeBoundaries = nullptr;
994 void Compiler::eeSetLIinfo(unsigned which, UNATIVE_OFFSET nativeOffset, IPmappingDscKind kind, const ILLocation& loc)
996 assert(opts.compDbgInfo);
997 assert(eeBoundariesCount > 0 && eeBoundaries != nullptr);
998 assert(which < eeBoundariesCount);
1000 eeBoundaries[which].nativeOffset = nativeOffset;
1001 eeBoundaries[which].source = (ICorDebugInfo::SourceTypes)0;
1005 case IPmappingDscKind::Normal:
1006 eeBoundaries[which].ilOffset = loc.GetOffset();
1007 eeBoundaries[which].source = loc.EncodeSourceTypes();
1009 case IPmappingDscKind::Prolog:
1010 eeBoundaries[which].ilOffset = ICorDebugInfo::PROLOG;
1011 eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY;
1013 case IPmappingDscKind::Epilog:
1014 eeBoundaries[which].ilOffset = ICorDebugInfo::EPILOG;
1015 eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY;
1017 case IPmappingDscKind::NoMapping:
1018 eeBoundaries[which].ilOffset = ICorDebugInfo::NO_MAPPING;
1019 eeBoundaries[which].source = ICorDebugInfo::STACK_EMPTY;
1026 void Compiler::eeSetLIdone()
1028 assert(opts.compDbgInfo);
1031 if (verbose || opts.dspDebugInfo)
1037 // necessary but not sufficient condition that the 2 struct definitions overlap
1038 assert(sizeof(eeBoundaries[0]) == sizeof(ICorDebugInfo::OffsetMapping));
1040 info.compCompHnd->setBoundaries(info.compMethodHnd, eeBoundariesCount, (ICorDebugInfo::OffsetMapping*)eeBoundaries);
1042 eeBoundaries = nullptr; // we give up ownership after setBoundaries();
1047 void Compiler::eeDispILOffs(IL_OFFSET offs)
1049 printf("0x%04X", offs);
1053 void Compiler::eeDispSourceMappingOffs(uint32_t offs)
1055 const char* specialOffs[] = {"EPILOG", "PROLOG", "NO_MAP"};
1057 switch ((int)offs) // Need the cast since offs is unsigned and the case statements are comparing to signed.
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);
1065 specialOffsNum = offs - DWORD(ICorDebugInfo::EPILOG);
1066 printf("%s", specialOffs[specialOffsNum]);
1075 void Compiler::eeDispLineInfo(const ICorDebugInfo::OffsetMapping* line)
1079 eeDispSourceMappingOffs(line->ilOffset);
1081 printf(" : 0x%08X", line->nativeOffset);
1082 if (line->source != 0)
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".
1088 if ((line->source & ICorDebugInfo::STACK_EMPTY) != 0)
1090 printf("STACK_EMPTY ");
1092 if ((line->source & ICorDebugInfo::CALL_INSTRUCTION) != 0)
1094 printf("CALL_INSTRUCTION ");
1096 if ((line->source & ICorDebugInfo::CALL_SITE) != 0)
1098 printf("CALL_SITE ");
1104 // We don't expect to see any other bits.
1105 assert((line->source & ~(ICorDebugInfo::STACK_EMPTY | ICorDebugInfo::CALL_INSTRUCTION)) == 0);
1108 void Compiler::eeDispLineInfos()
1110 printf("IP mapping count : %d\n", eeBoundariesCount); // this might be zero
1111 for (unsigned i = 0; i < eeBoundariesCount; i++)
1113 eeDispLineInfo(&eeBoundaries[i]);
1119 /*****************************************************************************
1121 * ICorJitInfo wrapper functions
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.
1128 void Compiler::eeAllocMem(AllocMemArgs* args, const UNATIVE_OFFSET roDataSectionAlignment)
1132 // Fake splitting implementation: place hot/cold code in contiguous section.
1133 UNATIVE_OFFSET coldCodeOffset = 0;
1134 if (JitConfig.JitFakeProcedureSplitting() && (args->coldCodeSize > 0))
1136 coldCodeOffset = args->hotCodeSize;
1137 assert(coldCodeOffset > 0);
1138 args->hotCodeSize += args->coldCodeSize;
1139 args->coldCodeSize = 0;
1144 #if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1146 // For arm64/LoongArch64/RISCV64, we want to allocate JIT data always adjacent to code similar to what native
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.
1151 UNATIVE_OFFSET roDataAlignmentDelta = 0;
1152 if (args->roDataSize > 0)
1154 roDataAlignmentDelta = AlignmentPad(args->hotCodeSize, roDataSectionAlignment);
1157 const UNATIVE_OFFSET roDataOffset = args->hotCodeSize + roDataAlignmentDelta;
1158 args->hotCodeSize = roDataOffset + args->roDataSize;
1159 args->roDataSize = 0;
1161 #endif // defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1163 info.compCompHnd->allocMem(args);
1167 if (JitConfig.JitFakeProcedureSplitting() && (coldCodeOffset > 0))
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;
1178 #if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
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;
1186 #endif // defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1189 void Compiler::eeReserveUnwindInfo(bool isFunclet, bool isColdCode, ULONG unwindSize)
1194 printf("reserveUnwindInfo(isFunclet=%s, isColdCode=%s, unwindSize=0x%x)\n", isFunclet ? "true" : "false",
1195 isColdCode ? "true" : "false", unwindSize);
1199 if (info.compMatchedVM)
1201 info.compCompHnd->reserveUnwindInfo(isFunclet, isColdCode, unwindSize);
1205 void Compiler::eeAllocUnwindInfo(BYTE* pHotCode,
1211 CorJitFuncKind funcKind)
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);
1221 case CORJIT_FUNC_ROOT:
1222 printf(" (main function)");
1224 case CORJIT_FUNC_HANDLER:
1225 printf(" (handler)");
1227 case CORJIT_FUNC_FILTER:
1228 printf(" (filter)");
1231 printf(" (ILLEGAL)");
1238 if (info.compMatchedVM)
1240 info.compCompHnd->allocUnwindInfo(pHotCode, pColdCode, startOffset, endOffset, unwindSize, pUnwindBlock,
1245 void Compiler::eeSetEHcount(unsigned cEH)
1250 printf("setEHcount(cEH=%u)\n", cEH);
1254 if (info.compMatchedVM)
1256 info.compCompHnd->setEHcount(cEH);
1260 void Compiler::eeSetEHinfo(unsigned EHnumber, const CORINFO_EH_CLAUSE* clause)
1263 if (opts.dspEHTable)
1265 dispOutgoingEHClause(EHnumber, *clause);
1269 if (info.compMatchedVM)
1271 info.compCompHnd->setEHinfo(EHnumber, clause);
1275 WORD Compiler::eeGetRelocTypeHint(void* target)
1277 if (info.compMatchedVM)
1279 return info.compCompHnd->getRelocTypeHint(target);
1288 CORINFO_FIELD_HANDLE Compiler::eeFindJitDataOffs(unsigned dataOffs)
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);
1295 bool Compiler::eeIsJitDataOffs(CORINFO_FIELD_HANDLE field)
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)
1301 return false; // some bits in the upper 32 bits were set, not a jit data offset
1304 // Data offsets are marked by the fact that the low two bits are 0b01
1305 return (value & iaut_MASK) == iaut_DATA_OFFSET;
1308 int Compiler::eeGetJitDataOffs(CORINFO_FIELD_HANDLE field)
1310 // Data offsets are marked by the fact that the low two bits are 0b01 0x1
1311 if (eeIsJitDataOffs(field))
1313 unsigned dataOffs = static_cast<unsigned>(reinterpret_cast<uintptr_t>(field));
1314 assert(((CORINFO_FIELD_HANDLE)(size_t)dataOffs) == field);
1315 assert(dataOffs < 0x40000000);
1317 // Shift away the low two bits
1318 return (static_cast<int>(reinterpret_cast<intptr_t>(field))) >> iaut_SHIFT;
1326 /*****************************************************************************
1328 * ICorStaticInfo wrapper functions
1331 #if defined(UNIX_AMD64_ABI)
1334 void Compiler::dumpSystemVClassificationType(SystemVClassificationType ct)
1338 case SystemVClassificationTypeUnknown:
1341 case SystemVClassificationTypeStruct:
1344 case SystemVClassificationTypeNoClass:
1347 case SystemVClassificationTypeMemory:
1350 case SystemVClassificationTypeInteger:
1353 case SystemVClassificationTypeIntegerReference:
1354 printf("IntegerReference");
1356 case SystemVClassificationTypeIntegerByRef:
1357 printf("IntegerByReference");
1359 case SystemVClassificationTypeSSE:
1369 void Compiler::eeGetSystemVAmd64PassStructInRegisterDescriptor(
1370 /*IN*/ CORINFO_CLASS_HANDLE structHnd,
1371 /*OUT*/ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr)
1373 bool ok = info.compCompHnd->getSystemVAmd64PassStructInRegisterDescriptor(structHnd, structPassInRegDescPtr);
1379 printf("**** getSystemVAmd64PassStructInRegisterDescriptor(0x%x (%s), ...) =>\n", dspPtr(structHnd),
1380 eeGetClassName(structHnd));
1381 printf(" passedInRegisters = %s\n", dspBool(structPassInRegDescPtr->passedInRegisters));
1382 if (structPassInRegDescPtr->passedInRegisters)
1384 printf(" eightByteCount = %d\n", structPassInRegDescPtr->eightByteCount);
1385 for (unsigned int i = 0; i < structPassInRegDescPtr->eightByteCount; i++)
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]);
1397 #endif // UNIX_AMD64_ABI
1399 bool Compiler::eeRunWithErrorTrapImp(void (*function)(void*), void* param)
1401 return info.compCompHnd->runWithErrorTrap(function, param);
1404 bool Compiler::eeRunWithSPMIErrorTrapImp(void (*function)(void*), void* param)
1406 return info.compCompHnd->runWithSPMIErrorTrap(function, param);
1410 //------------------------------------------------------------------------
1411 // eeTryGetClassSize: wraps getClassSize but if doing SuperPMI replay
1412 // and the value isn't found, use a bogus size.
1414 // NOTE: This is only allowed for JitDump output.
1417 // Either the actual class size, or (unsigned)-1 if SuperPMI didn't have it.
1419 unsigned Compiler::eeTryGetClassSize(CORINFO_CLASS_HANDLE clsHnd)
1421 unsigned classSize = UINT_MAX;
1422 eeRunFunctorWithSPMIErrorTrap([&]() { classSize = info.compCompHnd->getClassSize(clsHnd); });