diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h index 7826a25ae00d48..49a568afb2e7cc 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/lwmlist.h @@ -133,7 +133,8 @@ LWM(GetTokenTypeAsHandle, GetTokenTypeAsHandleValue, DWORDLONG) LWM(GetTypeForBox, DWORDLONG, DWORDLONG) LWM(GetTypeForPrimitiveValueClass, DWORDLONG, DWORD) LWM(GetTypeForPrimitiveNumericClass, DWORDLONG, DWORD) -LWM(GetUnboxedEntry, DWORDLONG, DLD); +LWM(GetUnboxedEntry, DWORDLONG, DLD) +LWM(GetIsClassInitedFieldAddress, DWORDLONG, DLD) LWM(GetUnBoxHelper, DWORDLONG, DWORD) LWM(GetVarArgsHandle, GetVarArgsHandleValue, DLDL) LWM(GetVars, DWORDLONG, Agnostic_GetVars) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index aeed2683d0f92c..d0991ffea4ec11 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -3192,6 +3192,41 @@ CORINFO_METHOD_HANDLE MethodContext::repGetUnboxedEntry(CORINFO_METHOD_HANDLE ft return (CORINFO_METHOD_HANDLE)(result.A); } +void MethodContext::recGetIsClassInitedFieldAddress(CORINFO_CLASS_HANDLE cls, int* pIsInitedMask, size_t result) +{ + if (GetIsClassInitedFieldAddress == nullptr) + { + GetIsClassInitedFieldAddress = new LightWeightMap(); + } + + DWORDLONG key = CastHandle(cls); + DLD value; + value.A = (DWORDLONG)result; + if (pIsInitedMask != nullptr) + { + value.B = (DWORD)*pIsInitedMask; + } + else + { + value.B = 0; + } + GetIsClassInitedFieldAddress->Add(key, value); +} +void MethodContext::dmpGetIsClassInitedFieldAddress(DWORDLONG key, DLD value) +{ + printf("GetIsClassInitedFieldAddressedEntry cls-%016llX, result-%016llX, is-init-mask-%u", key, value.A, value.B); +} +size_t MethodContext::repGetIsClassInitedFieldAddress(CORINFO_CLASS_HANDLE cls, int* pIsInitedMask) +{ + DWORDLONG key = CastHandle(cls); + DLD result = GetIsClassInitedFieldAddress->Get(key); + if (pIsInitedMask != nullptr) + { + *pIsInitedMask = (int)result.B; + } + return (size_t)result.A; +} + void MethodContext::recGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result) { if (GetDefaultComparerClass == nullptr) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h index 4851d369669537..78e7a9e2b264d9 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h @@ -411,6 +411,10 @@ class MethodContext void dmpGetUnboxedEntry(DWORDLONG key, DLD value); CORINFO_METHOD_HANDLE repGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg); + void recGetIsClassInitedFieldAddress(CORINFO_CLASS_HANDLE cls, int* pIsInitedMask, size_t result); + void dmpGetIsClassInitedFieldAddress(DWORDLONG key, DLD value); + size_t repGetIsClassInitedFieldAddress(CORINFO_CLASS_HANDLE cls, int* pIsInitedMask); + void recGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls, CORINFO_CLASS_HANDLE result); void dmpGetDefaultComparerClass(DWORDLONG key, DWORDLONG value); CORINFO_CLASS_HANDLE repGetDefaultComparerClass(CORINFO_CLASS_HANDLE cls); @@ -881,7 +885,7 @@ class MethodContext }; // ********************* Please keep this up-to-date to ease adding more *************** -// Highest packet number: 191 +// Highest packet number: 192 // ************************************************************************************* enum mcPackets { @@ -1012,6 +1016,7 @@ enum mcPackets Packet_GetTypeForPrimitiveValueClass = 91, Packet_GetTypeForPrimitiveNumericClass = 168, // Added 12/7/2017 Packet_GetUnboxedEntry = 165, // Added 10/26/17 + Packet_GetIsClassInitedFieldAddress = 192, // Added 3/5/2021 Packet_GetUnBoxHelper = 92, Packet_GetReadyToRunHelper = 150, // Added 10/10/2014 Packet_GetReadyToRunDelegateCtorHelper = 157, // Added 3/30/2016 diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 9f5f1e912a479d..fcb4d3c3fe9385 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -250,6 +250,19 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ft return result; } +size_t interceptor_ICJI::getIsClassInitedFieldAddress(CORINFO_CLASS_HANDLE cls, int* pIsInitedMask) +{ + mc->cr->AddCall("getIsClassInitedFieldAddress"); + int localPIsInitedMask = false; + size_t result = original_ICorJitInfo->getIsClassInitedFieldAddress(cls, &localPIsInitedMask); + mc->recGetIsClassInitedFieldAddress(cls, &localPIsInitedMask, result); + if (pIsInitedMask != nullptr) + { + *pIsInitedMask = localPIsInitedMask; + } + return result; +} + // Given T, return the type of the default Comparer. // Returns null if the type can't be determined exactly. CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultComparerClass(CORINFO_CLASS_HANDLE cls) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp index 674d9f4e1fd102..ebe69754f4b185 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/icorjitinfo.cpp @@ -132,6 +132,14 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry( return original_ICorJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg); } +size_t interceptor_ICJI::getIsClassInitedFieldAddress( + CORINFO_CLASS_HANDLE cls, + int* pIsInitedMask) +{ + mcs->AddCall("getIsClassInitedFieldAddress"); + return original_ICorJitInfo->getIsClassInitedFieldAddress(cls, pIsInitedMask); +} + CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultComparerClass( CORINFO_CLASS_HANDLE elemType) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp index a276453ba69c99..00f525c99bc959 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/icorjitinfo.cpp @@ -118,6 +118,13 @@ CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry( return original_ICorJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg); } +size_t interceptor_ICJI::getIsClassInitedFieldAddress( + CORINFO_CLASS_HANDLE cls, + int* pIsInitedMask) +{ + return original_ICorJitInfo->getIsClassInitedFieldAddress(cls, pIsInitedMask); +} + CORINFO_CLASS_HANDLE interceptor_ICJI::getDefaultComparerClass( CORINFO_CLASS_HANDLE elemType) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp index 9d59f4449be613..9df6c9866c6303 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/icorjitinfo.cpp @@ -184,6 +184,13 @@ CORINFO_METHOD_HANDLE MyICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* r return result; } +size_t MyICJI::getIsClassInitedFieldAddress(CORINFO_CLASS_HANDLE cls, int* pIsInitedMask) +{ + jitInstance->mc->cr->AddCall("getIsClassInitedFieldAddress"); + size_t result = jitInstance->mc->repGetIsClassInitedFieldAddress(cls, pIsInitedMask); + return result; +} + // Given T, return the type of the default Comparer. // Returns null if the type can't be determined exactly. CORINFO_CLASS_HANDLE MyICJI::getDefaultComparerClass(CORINFO_CLASS_HANDLE cls) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index c7922d3fff579d..d5a1ef6e976f35 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -2064,6 +2064,11 @@ class ICorStaticInfo bool* requiresInstMethodTableArg ) = 0; + virtual size_t getIsClassInitedFieldAddress( + CORINFO_CLASS_HANDLE cls, + int* pIsInitedMask + ) = 0; + // Given T, return the type of the default Comparer. // Returns null if the type can't be determined exactly. virtual CORINFO_CLASS_HANDLE getDefaultComparerClass( diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index beb71ca0d3fa70..f4e8477eca120d 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -85,6 +85,10 @@ CORINFO_METHOD_HANDLE getUnboxedEntry( CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg) override; +size_t getIsClassInitedFieldAddress( + CORINFO_CLASS_HANDLE cls, + int* pIsInitedMask) override; + CORINFO_CLASS_HANDLE getDefaultComparerClass( CORINFO_CLASS_HANDLE elemType) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 07843243c24f6a..da6134ee86c121 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 73d20c3a-75a9-4eea-a952-60419d67b6a6 */ - 0x73d20c3a, - 0x75a9, - 0x4eea, - {0xa9, 0x52, 0x60, 0x41, 0x9d, 0x67, 0xb6, 0xa6} +constexpr GUID JITEEVersionIdentifier = { /* a93ec910-3e98-4eb1-8bf1-a1f272303878 */ + 0xa93ec910, + 0x3e98, + 0x4eb1, + {0x8b, 0xf1, 0xa1, 0xf2, 0x72, 0x30, 0x38, 0x78} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_API_names.h b/src/coreclr/jit/ICorJitInfo_API_names.h index 2e1e67cc24a773..0557b7730d75b4 100644 --- a/src/coreclr/jit/ICorJitInfo_API_names.h +++ b/src/coreclr/jit/ICorJitInfo_API_names.h @@ -18,6 +18,7 @@ DEF_CLR_API(getMethodModule) DEF_CLR_API(getMethodVTableOffset) DEF_CLR_API(resolveVirtualMethod) DEF_CLR_API(getUnboxedEntry) +DEF_CLR_API(getIsClassInitedFieldAddress) DEF_CLR_API(getDefaultComparerClass) DEF_CLR_API(getDefaultEqualityComparerClass) DEF_CLR_API(expandRawHandleIntrinsic) diff --git a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp index d80bb83ef059a7..71688145bb05b5 100644 --- a/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp +++ b/src/coreclr/jit/ICorJitInfo_API_wrapper.hpp @@ -154,6 +154,16 @@ CORINFO_METHOD_HANDLE WrapICorJitInfo::getUnboxedEntry( return temp; } +size_t WrapICorJitInfo::getIsClassInitedFieldAddress( + CORINFO_CLASS_HANDLE cls, + int* pIsInitedMask) +{ + API_ENTER(getIsClassInitedFieldAddress); + size_t temp = wrapHnd->getIsClassInitedFieldAddress(cls, pIsInitedMask); + API_LEAVE(getIsClassInitedFieldAddress); + return temp; +} + CORINFO_CLASS_HANDLE WrapICorJitInfo::getDefaultComparerClass( CORINFO_CLASS_HANDLE elemType) { diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 7a8fb916abfc18..2bced4fc3538d6 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -5026,6 +5026,10 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl compQuirkForPPPflag = compQuirkForPPP(); #endif + // Insert quick "is class statically initialized" checks in front of + // static init helper calls + DoPhase(this, PHASE_INSERT_STATINIT_CHECKS, &Compiler::fgInsertClsInitChecks); + // Insert GC Polls DoPhase(this, PHASE_INSERT_GC_POLLS, &Compiler::fgInsertGCPolls); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 971fd105ea60af..fbcaff3efb188e 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4594,7 +4594,8 @@ class Compiler bool fgDomsComputed; // Have we computed the dominator sets? bool fgOptimizedFinally; // Did we optimize any try-finallys? - bool fgHasSwitch; // any BBJ_SWITCH jumps? + bool fgHasSwitch; // any BBJ_SWITCH jumps? + bool fgHasOptStaticInit; // Do we have a static initialization we can optimize? BlockSet fgEnterBlks; // Set of blocks which have a special transfer of control; the "entry" blocks plus EH handler // begin blocks. @@ -5184,6 +5185,8 @@ class Compiler PhaseStatus fgInsertGCPolls(); BasicBlock* fgCreateGCPoll(GCPollType pollType, BasicBlock* block); + PhaseStatus fgInsertClsInitChecks(); + // Requires that "block" is a block that returns from // a finally. Returns the number of successors (jump targets of // of blocks in the covered "try" that did a "LEAVE".) diff --git a/src/coreclr/jit/compphases.h b/src/coreclr/jit/compphases.h index 49303ecbd64b9c..0855ac790be003 100644 --- a/src/coreclr/jit/compphases.h +++ b/src/coreclr/jit/compphases.h @@ -89,6 +89,7 @@ CompPhaseNameMacro(PHASE_ASSERTION_PROP_MAIN, "Assertion prop", #endif CompPhaseNameMacro(PHASE_OPT_UPDATE_FLOW_GRAPH, "Update flow graph opt pass", "UPD-FG-O", false, -1, false) CompPhaseNameMacro(PHASE_COMPUTE_EDGE_WEIGHTS2, "Compute edge weights (2, false)", "EDG-WGT2", false, -1, false) +CompPhaseNameMacro(PHASE_INSERT_STATINIT_CHECKS, "Insert is-cls-stat-init Checks", "CLSSICHK", false, -1, true) CompPhaseNameMacro(PHASE_INSERT_GC_POLLS, "Insert GC Polls", "GC-POLLS", false, -1, true) CompPhaseNameMacro(PHASE_DETERMINE_FIRST_COLD_BLOCK, "Determine first cold block", "COLD-BLK", false, -1, true) CompPhaseNameMacro(PHASE_RATIONALIZE, "Rationalize IR", "RAT", false, -1, false) diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 8f07bfb324673f..58c452c4b20c87 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -165,6 +165,7 @@ void Compiler::fgInit() #endif fgHasSwitch = false; + fgHasOptStaticInit = false; fgPgoSchema = nullptr; fgPgoData = nullptr; fgPgoSchemaCount = 0; diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 8b89c2e5945a55..d92ea1508a0199 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -52,6 +52,239 @@ static bool blockNeedsGCPoll(BasicBlock* block) return blockMayNeedGCPoll; } +//------------------------------------------------------------------------------ +// fgInsertClsInitChecks : Wraps static init helper calls with "is class statically initialized" +// inlined checks. +// +// Returns: +// PhaseStatus indicating what, if anything, was changed. +// + +PhaseStatus Compiler::fgInsertClsInitChecks() +{ + if (!opts.OptimizationEnabled()) + { + return PhaseStatus::MODIFIED_NOTHING; + } + + if (!fgHasOptStaticInit) + { + JITDUMP("fgInsertClsInitChecks: compiler claims there are no cctors we can optimize."); + return PhaseStatus::MODIFIED_NOTHING; + } + + bool modified = false; + BasicBlock* block; + BasicBlock* prevBb = nullptr; + for (block = fgFirstBB; block; block = block->bbNext) + { + // Skip cold blocks. Also, we might want to split basic-blocks in the middle + // so let's skip those with BBF_GC_SAFE_POINT for simplicity. + if (!block->isRunRarely() && !(block->bbFlags & BBF_GC_SAFE_POINT)) + { + Statement* prevStmt = nullptr; + for (Statement* stmt : block->Statements()) + { + for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext) + { + // We only need GT_CALL nodes with helper funcs + // Looks like BBF_HAS_CALL/GTF_CALL aren't reliable as a fast check. + if (!tree->IsCall()) + { + continue; + } + + GenTreeCall* call = tree->AsCall(); + if ((call->gtCallType != CT_HELPER) || (call->gtRetClsHnd == nullptr)) + { + continue; + } + + CorInfoHelpFunc helpFunc = eeGetHelperNum(call->gtCallMethHnd); + if ((helpFunc != CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) && + (helpFunc != CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS)) + { + continue; + } + assert(call->fgArgInfo->ArgCount() == 2); + + GenTree* moduleIdArg = call->fgArgInfo->GetArgNode(0); + GenTree* clsIdArg = call->fgArgInfo->GetArgNode(1); + + if (!moduleIdArg->IsCnsIntOrI() || !clsIdArg->IsCnsIntOrI()) + { + // ModuleId or/and clsId were passed as indirect loads + // We can consider optimizing this case too (for R2R) + // but it most likely will come with a noticeable size regression. + continue; + } + + int isInitMask = 0; + size_t isInitAddr = info.compCompHnd->getIsClassInitedFieldAddress(call->gtRetClsHnd, &isInitMask); + if ((isInitAddr == 0) || (isInitMask == 0)) + { + JITDUMP("getIsClassInitedFieldAddress: returned empty isInitAddr or isInitedMask."); + continue; + } + assert(isInitMask > 0); + + if (prevBb == nullptr) + { + // We're going to emit a BB in front of fgFirstBB + fgEnsureFirstBBisScratch(); + prevBb = fgFirstBB; + if (prevBb == block) + { + JITDUMP("fgInsertClsInitChecks: fgFirstBB is fgFirstBBScratch with initclass helper"); + // It means the first bb is already a scratch one so it's probably not a good idea + // to insert blocks before them. + continue; + } + } + + // So, we found a helper call inside the "block" - let's extract it to a + // separate block "callInitBb" and guard it with a fast "isInitedBb" bb. + // The final layout should look like this: + // + // BB0 "prevBb": + // ... + // + // BB1 "isInitedBb": (preds: BB0 + %current preds of BB3%) + // + // * JTRUE void + // \--* NE int + // +--* AND int + // | +--* IND ubyte + // | | \--* CNS_INT long isInitAdr + // | \--* CNS_INT int isInitMask + // \--* CNS_INT int 0 + // + // + // BB2 "callInitBb": (preds: BB1) + // + // * CALL long CORINFO_HELP_INITCLASS + // +--* CNS_INT long cl + // + // BB3 "block" (preds: BB1, BB2) + // ... + // + + // Insert a no-op statement that we can use as a splitter in fgSplitBlockAfterStatement + // in case if we're on the first statement. + if (prevStmt == nullptr) + { + prevStmt = fgNewStmtFromTree(gtNewNothingNode()); + fgInsertStmtAtBeg(block, prevStmt); + } + + BasicBlock* newBlock = fgSplitBlockAfterStatement(block, prevStmt); + newBlock->bbFlags |= (BBF_HAS_LABEL | BBF_INTERNAL); + + prevBb = block; + block = newBlock; + + // BB1 "isInitedBb" + BasicBlock* isInitedBb = fgNewBBafter(BBJ_COND, prevBb, true); + isInitedBb->inheritWeight(prevBb); + isInitedBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_LABEL | BBF_HAS_JMP); + + GenTree* isInitAdrNode = gtNewIndOfIconHandleNode(TYP_UBYTE, isInitAddr, GTF_ICON_CONST_PTR, true); + GenTree* isInitedMaskNode = + gtNewOperNode(GT_AND, TYP_INT, isInitAdrNode, gtNewIconNode(isInitMask)); + GenTree* isInitedCmp = gtNewOperNode(GT_NE, TYP_INT, isInitedMaskNode, gtNewIconNode(0)); + isInitedCmp->gtFlags |= (GTF_RELOP_JMP_USED | GTF_DONT_CSE); + gtSetEvalOrder(isInitedCmp); + + Statement* isInitedStmt = fgNewStmtFromTree(gtNewOperNode(GT_JTRUE, TYP_VOID, isInitedCmp)); + gtSetStmtInfo(isInitedStmt); + fgSetStmtSeq(isInitedStmt); + + fgInsertStmtAtEnd(isInitedBb, isInitedStmt); + isInitedBb->bbJumpDest = block; + block->bbFlags |= BBF_JMP_TARGET; + + // Let's start from emitting that BB2 "callInitBb" + BasicBlock* callInitBb = fgNewBBafter(BBJ_ALWAYS, isInitedBb, true); + // it's executed only once so can be marked as cold + callInitBb->bbSetRunRarely(); + callInitBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_CALL | BBF_HAS_LABEL | BBF_HAS_JMP); + callInitBb->bbJumpDest = block; + + // Replace the helper call with a slower CORINFO_HELP_INITCLASS + // it accepts a single argument instead of two so we can save some space. + BOOL runtimeLookup; + CORINFO_RESOLVED_TOKEN resolvedToken = {}; + resolvedToken.hClass = call->gtRetClsHnd; + GenTree* pMT = impParentClassTokenToHandle(&resolvedToken, &runtimeLookup); + GenTreeCall* slowCall = gtNewHelperCallNode(CORINFO_HELP_INITCLASS, TYP_VOID, gtNewCallArgs(pMT)); + slowCall->gtFlags |= call->gtFlags; + slowCall = fgMorphCall(slowCall)->AsCall(); + gtSetEvalOrder(slowCall); + + Statement* callStmt = fgNewStmtFromTree(slowCall); + gtSetStmtInfo(callStmt); + fgSetStmtSeq(callStmt); + fgInsertStmtAtEnd(callInitBb, callStmt); + + // Replace the call with the "moduleId" node. + gtReplaceTree(stmt, call, gtClone(moduleIdArg)); + + // Fix all the connections to predecessors and successors: + fgAddRefPred(block, callInitBb); + fgAddRefPred(block, isInitedBb); + fgAddRefPred(isInitedBb, prevBb); + fgAddRefPred(callInitBb, isInitedBb); + fgRemoveRefPred(block, prevBb); + + // Convert the prevBB (it used to be the current block before fgSplitBlockAfterStatement) + // to BBJ_ALWAYS + if (prevBb->bbJumpKind == BBJ_NONE) + { + prevBb->bbJumpDest = isInitedBb; + prevBb->bbJumpKind = BBJ_ALWAYS; + isInitedBb->bbFlags |= BBF_JMP_TARGET; + prevBb->bbFlags |= BBF_HAS_JMP; + } + + // Make sure all four basic blocks are in the same EH region: + assert(BasicBlock::sameEHRegion(prevBb, block)); + assert(BasicBlock::sameEHRegion(callInitBb, block)); + assert(BasicBlock::sameEHRegion(isInitedBb, block)); + + assert((isInitedBb->bbJumpDest == block) && (isInitedBb->bbNext == callInitBb)); + assert((callInitBb->bbJumpDest == block) && (callInitBb->bbNext == block)); + assert((prevBb->bbNext == isInitedBb) && (prevBb->bbJumpDest == isInitedBb)); + + modified = true; + break; + } + if (modified) + { + // Clear potential leftover GTF_CALL and GTF_EXC flags in the current stmt + gtUpdateStmtSideEffects(stmt); + } + prevStmt = stmt; + } + } + prevBb = block; + } + + if (modified) + { +#ifdef DEBUG + if (verbose) + { + printf("*************** After fgInsertClsInitChecks()\n"); + fgDispBasicBlocks(true); + } +#endif // DEBUG + + fgReorderBlocks(); + return PhaseStatus::MODIFIED_EVERYTHING; + } + return PhaseStatus::MODIFIED_NOTHING; +} + //------------------------------------------------------------------------------ // fgInsertGCPolls : Insert GC polls for basic blocks containing calls to methods // with SuppressGCTransitionAttribute. @@ -926,6 +1159,12 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo GenTreeCall* result = gtNewHelperCallNode(helper, type, argList); result->gtFlags |= callFlags; + if (helper == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) + { + // Save cls for this helper for fgInsertClsInitChecks phase + result->gtRetClsHnd = cls; + } + // If we're importing the special EqualityComparer.Default or Comparer.Default // intrinsics, flag the helper call. Later during inlining, we can // remove the helper call if the associated field lookup is unused. diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 648ddc49661f23..efac6a08a89845 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -9237,6 +9237,17 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) optMethodFlags |= OMF_NEEDS_GCPOLLS; } + // Look for candidates for fgInsertClsInitChecks phase. + if (fgGlobalMorph && (call->gtCallType == CT_HELPER) && (call->gtRetClsHnd != nullptr)) + { + CorInfoHelpFunc helper = eeGetHelperNum(call->gtCallMethHnd); + if ((helper == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) || + (helper == CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS)) + { + fgHasOptStaticInit = true; + } + } + // Morph Type.op_Equality, Type.op_Inequality, and Enum.HasFlag // // We need to do these before the arguments are morphed diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs index 80fc5e6ea34a65..4d26523bba37b5 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoBase.cs @@ -216,6 +216,21 @@ static byte _resolveVirtualMethod(IntPtr thisHandle, IntPtr* ppException, CORINF } } + [UnmanagedCallersOnly] + static UIntPtr _getIsClassInitedFieldAddress(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* cls, int* pIsInitedMask) + { + var _this = GetThis(thisHandle); + try + { + return _this.getIsClassInitedFieldAddress(cls, ref *pIsInitedMask); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + return default; + } + } + [UnmanagedCallersOnly] static CORINFO_CLASS_STRUCT_* _getDefaultComparerClass(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* elemType) { @@ -2551,7 +2566,7 @@ static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_FLAGS* f static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 172); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 173); callbacks[0] = (delegate* unmanaged)&_getMethodAttribs; callbacks[1] = (delegate* unmanaged)&_setMethodAttribs; @@ -2567,164 +2582,165 @@ static IntPtr GetUnmanagedCallbacks() callbacks[11] = (delegate* unmanaged)&_getMethodVTableOffset; callbacks[12] = (delegate* unmanaged)&_resolveVirtualMethod; callbacks[13] = (delegate* unmanaged)&_getUnboxedEntry; - callbacks[14] = (delegate* unmanaged)&_getDefaultComparerClass; - callbacks[15] = (delegate* unmanaged)&_getDefaultEqualityComparerClass; - callbacks[16] = (delegate* unmanaged)&_expandRawHandleIntrinsic; - callbacks[17] = (delegate* unmanaged)&_getIntrinsicID; - callbacks[18] = (delegate* unmanaged)&_isIntrinsicType; - callbacks[19] = (delegate* unmanaged)&_getUnmanagedCallConv; - callbacks[20] = (delegate* unmanaged)&_pInvokeMarshalingRequired; - callbacks[21] = (delegate* unmanaged)&_satisfiesMethodConstraints; - callbacks[22] = (delegate* unmanaged)&_isCompatibleDelegate; - callbacks[23] = (delegate* unmanaged)&_methodMustBeLoadedBeforeCodeIsRun; - callbacks[24] = (delegate* unmanaged)&_mapMethodDeclToMethodImpl; - callbacks[25] = (delegate* unmanaged)&_getGSCookie; - callbacks[26] = (delegate* unmanaged)&_setPatchpointInfo; - callbacks[27] = (delegate* unmanaged)&_getOSRInfo; - callbacks[28] = (delegate* unmanaged)&_resolveToken; - callbacks[29] = (delegate* unmanaged)&_tryResolveToken; - callbacks[30] = (delegate* unmanaged)&_findSig; - callbacks[31] = (delegate* unmanaged)&_findCallSiteSig; - callbacks[32] = (delegate* unmanaged)&_getTokenTypeAsHandle; - callbacks[33] = (delegate* unmanaged)&_isValidToken; - callbacks[34] = (delegate* unmanaged)&_isValidStringRef; - callbacks[35] = (delegate* unmanaged)&_getStringLiteral; - callbacks[36] = (delegate* unmanaged)&_asCorInfoType; - callbacks[37] = (delegate* unmanaged)&_getClassName; - callbacks[38] = (delegate* unmanaged)&_getClassNameFromMetadata; - callbacks[39] = (delegate* unmanaged)&_getTypeInstantiationArgument; - callbacks[40] = (delegate* unmanaged)&_appendClassName; - callbacks[41] = (delegate* unmanaged)&_isValueClass; - callbacks[42] = (delegate* unmanaged)&_canInlineTypeCheck; - callbacks[43] = (delegate* unmanaged)&_getClassAttribs; - callbacks[44] = (delegate* unmanaged)&_isStructRequiringStackAllocRetBuf; - callbacks[45] = (delegate* unmanaged)&_getClassModule; - callbacks[46] = (delegate* unmanaged)&_getModuleAssembly; - callbacks[47] = (delegate* unmanaged)&_getAssemblyName; - callbacks[48] = (delegate* unmanaged)&_LongLifetimeMalloc; - callbacks[49] = (delegate* unmanaged)&_LongLifetimeFree; - callbacks[50] = (delegate* unmanaged)&_getClassModuleIdForStatics; - callbacks[51] = (delegate* unmanaged)&_getClassSize; - callbacks[52] = (delegate* unmanaged)&_getHeapClassSize; - callbacks[53] = (delegate* unmanaged)&_canAllocateOnStack; - callbacks[54] = (delegate* unmanaged)&_getClassAlignmentRequirement; - callbacks[55] = (delegate* unmanaged)&_getClassGClayout; - callbacks[56] = (delegate* unmanaged)&_getClassNumInstanceFields; - callbacks[57] = (delegate* unmanaged)&_getFieldInClass; - callbacks[58] = (delegate* unmanaged)&_checkMethodModifier; - callbacks[59] = (delegate* unmanaged)&_getNewHelper; - callbacks[60] = (delegate* unmanaged)&_getNewArrHelper; - callbacks[61] = (delegate* unmanaged)&_getCastingHelper; - callbacks[62] = (delegate* unmanaged)&_getSharedCCtorHelper; - callbacks[63] = (delegate* unmanaged)&_getTypeForBox; - callbacks[64] = (delegate* unmanaged)&_getBoxHelper; - callbacks[65] = (delegate* unmanaged)&_getUnBoxHelper; - callbacks[66] = (delegate* unmanaged)&_getReadyToRunHelper; - callbacks[67] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; - callbacks[68] = (delegate* unmanaged)&_getHelperName; - callbacks[69] = (delegate* unmanaged)&_initClass; - callbacks[70] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; - callbacks[71] = (delegate* unmanaged)&_getBuiltinClass; - callbacks[72] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; - callbacks[73] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; - callbacks[74] = (delegate* unmanaged)&_canCast; - callbacks[75] = (delegate* unmanaged)&_areTypesEquivalent; - callbacks[76] = (delegate* unmanaged)&_compareTypesForCast; - callbacks[77] = (delegate* unmanaged)&_compareTypesForEquality; - callbacks[78] = (delegate* unmanaged)&_mergeClasses; - callbacks[79] = (delegate* unmanaged)&_isMoreSpecificType; - callbacks[80] = (delegate* unmanaged)&_getParentType; - callbacks[81] = (delegate* unmanaged)&_getChildType; - callbacks[82] = (delegate* unmanaged)&_satisfiesClassConstraints; - callbacks[83] = (delegate* unmanaged)&_isSDArray; - callbacks[84] = (delegate* unmanaged)&_getArrayRank; - callbacks[85] = (delegate* unmanaged)&_getArrayInitializationData; - callbacks[86] = (delegate* unmanaged)&_canAccessClass; - callbacks[87] = (delegate* unmanaged)&_getFieldName; - callbacks[88] = (delegate* unmanaged)&_getFieldClass; - callbacks[89] = (delegate* unmanaged)&_getFieldType; - callbacks[90] = (delegate* unmanaged)&_getFieldOffset; - callbacks[91] = (delegate* unmanaged)&_getFieldInfo; - callbacks[92] = (delegate* unmanaged)&_isFieldStatic; - callbacks[93] = (delegate* unmanaged)&_getBoundaries; - callbacks[94] = (delegate* unmanaged)&_setBoundaries; - callbacks[95] = (delegate* unmanaged)&_getVars; - callbacks[96] = (delegate* unmanaged)&_setVars; - callbacks[97] = (delegate* unmanaged)&_allocateArray; - callbacks[98] = (delegate* unmanaged)&_freeArray; - callbacks[99] = (delegate* unmanaged)&_getArgNext; - callbacks[100] = (delegate* unmanaged)&_getArgType; - callbacks[101] = (delegate* unmanaged)&_getArgClass; - callbacks[102] = (delegate* unmanaged)&_getHFAType; - callbacks[103] = (delegate* unmanaged)&_GetErrorHRESULT; - callbacks[104] = (delegate* unmanaged)&_GetErrorMessage; - callbacks[105] = (delegate* unmanaged)&_FilterException; - callbacks[106] = (delegate* unmanaged)&_HandleException; - callbacks[107] = (delegate* unmanaged)&_ThrowExceptionForJitResult; - callbacks[108] = (delegate* unmanaged)&_ThrowExceptionForHelper; - callbacks[109] = (delegate* unmanaged)&_runWithErrorTrap; - callbacks[110] = (delegate* unmanaged)&_getEEInfo; - callbacks[111] = (delegate* unmanaged)&_getJitTimeLogFilename; - callbacks[112] = (delegate* unmanaged)&_getMethodDefFromMethod; - callbacks[113] = (delegate* unmanaged)&_getMethodName; - callbacks[114] = (delegate* unmanaged)&_getMethodNameFromMetadata; - callbacks[115] = (delegate* unmanaged)&_getMethodHash; - callbacks[116] = (delegate* unmanaged)&_findNameOfToken; - callbacks[117] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; - callbacks[118] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[119] = (delegate* unmanaged)&_getInlinedCallFrameVptr; - callbacks[120] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[121] = (delegate* unmanaged)&_getHelperFtn; - callbacks[122] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[123] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[124] = (delegate* unmanaged)&_getMethodSync; - callbacks[125] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[126] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[127] = (delegate* unmanaged)&_embedClassHandle; - callbacks[128] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[129] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[130] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[131] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[132] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[133] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[134] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[135] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[136] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[137] = (delegate* unmanaged)&_getCallInfo; - callbacks[138] = (delegate* unmanaged)&_canAccessFamily; - callbacks[139] = (delegate* unmanaged)&_isRIDClassDomainID; - callbacks[140] = (delegate* unmanaged)&_getClassDomainID; - callbacks[141] = (delegate* unmanaged)&_getFieldAddress; - callbacks[142] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[143] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[144] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[145] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[146] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[147] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[148] = (delegate* unmanaged)&_setOverride; - callbacks[149] = (delegate* unmanaged)&_addActiveDependency; - callbacks[150] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[151] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[152] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[153] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[154] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[155] = (delegate* unmanaged)&_allocMem; - callbacks[156] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[157] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[158] = (delegate* unmanaged)&_allocGCInfo; - callbacks[159] = (delegate* unmanaged)&_setEHcount; - callbacks[160] = (delegate* unmanaged)&_setEHinfo; - callbacks[161] = (delegate* unmanaged)&_logMsg; - callbacks[162] = (delegate* unmanaged)&_doAssert; - callbacks[163] = (delegate* unmanaged)&_reportFatalError; - callbacks[164] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[165] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[166] = (delegate* unmanaged)&_getLikelyClass; - callbacks[167] = (delegate* unmanaged)&_recordCallSite; - callbacks[168] = (delegate* unmanaged)&_recordRelocation; - callbacks[169] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[170] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[171] = (delegate* unmanaged)&_getJitFlags; + callbacks[14] = (delegate* unmanaged)&_getIsClassInitedFieldAddress; + callbacks[15] = (delegate* unmanaged)&_getDefaultComparerClass; + callbacks[16] = (delegate* unmanaged)&_getDefaultEqualityComparerClass; + callbacks[17] = (delegate* unmanaged)&_expandRawHandleIntrinsic; + callbacks[18] = (delegate* unmanaged)&_getIntrinsicID; + callbacks[19] = (delegate* unmanaged)&_isIntrinsicType; + callbacks[20] = (delegate* unmanaged)&_getUnmanagedCallConv; + callbacks[21] = (delegate* unmanaged)&_pInvokeMarshalingRequired; + callbacks[22] = (delegate* unmanaged)&_satisfiesMethodConstraints; + callbacks[23] = (delegate* unmanaged)&_isCompatibleDelegate; + callbacks[24] = (delegate* unmanaged)&_methodMustBeLoadedBeforeCodeIsRun; + callbacks[25] = (delegate* unmanaged)&_mapMethodDeclToMethodImpl; + callbacks[26] = (delegate* unmanaged)&_getGSCookie; + callbacks[27] = (delegate* unmanaged)&_setPatchpointInfo; + callbacks[28] = (delegate* unmanaged)&_getOSRInfo; + callbacks[29] = (delegate* unmanaged)&_resolveToken; + callbacks[30] = (delegate* unmanaged)&_tryResolveToken; + callbacks[31] = (delegate* unmanaged)&_findSig; + callbacks[32] = (delegate* unmanaged)&_findCallSiteSig; + callbacks[33] = (delegate* unmanaged)&_getTokenTypeAsHandle; + callbacks[34] = (delegate* unmanaged)&_isValidToken; + callbacks[35] = (delegate* unmanaged)&_isValidStringRef; + callbacks[36] = (delegate* unmanaged)&_getStringLiteral; + callbacks[37] = (delegate* unmanaged)&_asCorInfoType; + callbacks[38] = (delegate* unmanaged)&_getClassName; + callbacks[39] = (delegate* unmanaged)&_getClassNameFromMetadata; + callbacks[40] = (delegate* unmanaged)&_getTypeInstantiationArgument; + callbacks[41] = (delegate* unmanaged)&_appendClassName; + callbacks[42] = (delegate* unmanaged)&_isValueClass; + callbacks[43] = (delegate* unmanaged)&_canInlineTypeCheck; + callbacks[44] = (delegate* unmanaged)&_getClassAttribs; + callbacks[45] = (delegate* unmanaged)&_isStructRequiringStackAllocRetBuf; + callbacks[46] = (delegate* unmanaged)&_getClassModule; + callbacks[47] = (delegate* unmanaged)&_getModuleAssembly; + callbacks[48] = (delegate* unmanaged)&_getAssemblyName; + callbacks[49] = (delegate* unmanaged)&_LongLifetimeMalloc; + callbacks[50] = (delegate* unmanaged)&_LongLifetimeFree; + callbacks[51] = (delegate* unmanaged)&_getClassModuleIdForStatics; + callbacks[52] = (delegate* unmanaged)&_getClassSize; + callbacks[53] = (delegate* unmanaged)&_getHeapClassSize; + callbacks[54] = (delegate* unmanaged)&_canAllocateOnStack; + callbacks[55] = (delegate* unmanaged)&_getClassAlignmentRequirement; + callbacks[56] = (delegate* unmanaged)&_getClassGClayout; + callbacks[57] = (delegate* unmanaged)&_getClassNumInstanceFields; + callbacks[58] = (delegate* unmanaged)&_getFieldInClass; + callbacks[59] = (delegate* unmanaged)&_checkMethodModifier; + callbacks[60] = (delegate* unmanaged)&_getNewHelper; + callbacks[61] = (delegate* unmanaged)&_getNewArrHelper; + callbacks[62] = (delegate* unmanaged)&_getCastingHelper; + callbacks[63] = (delegate* unmanaged)&_getSharedCCtorHelper; + callbacks[64] = (delegate* unmanaged)&_getTypeForBox; + callbacks[65] = (delegate* unmanaged)&_getBoxHelper; + callbacks[66] = (delegate* unmanaged)&_getUnBoxHelper; + callbacks[67] = (delegate* unmanaged)&_getReadyToRunHelper; + callbacks[68] = (delegate* unmanaged)&_getReadyToRunDelegateCtorHelper; + callbacks[69] = (delegate* unmanaged)&_getHelperName; + callbacks[70] = (delegate* unmanaged)&_initClass; + callbacks[71] = (delegate* unmanaged)&_classMustBeLoadedBeforeCodeIsRun; + callbacks[72] = (delegate* unmanaged)&_getBuiltinClass; + callbacks[73] = (delegate* unmanaged)&_getTypeForPrimitiveValueClass; + callbacks[74] = (delegate* unmanaged)&_getTypeForPrimitiveNumericClass; + callbacks[75] = (delegate* unmanaged)&_canCast; + callbacks[76] = (delegate* unmanaged)&_areTypesEquivalent; + callbacks[77] = (delegate* unmanaged)&_compareTypesForCast; + callbacks[78] = (delegate* unmanaged)&_compareTypesForEquality; + callbacks[79] = (delegate* unmanaged)&_mergeClasses; + callbacks[80] = (delegate* unmanaged)&_isMoreSpecificType; + callbacks[81] = (delegate* unmanaged)&_getParentType; + callbacks[82] = (delegate* unmanaged)&_getChildType; + callbacks[83] = (delegate* unmanaged)&_satisfiesClassConstraints; + callbacks[84] = (delegate* unmanaged)&_isSDArray; + callbacks[85] = (delegate* unmanaged)&_getArrayRank; + callbacks[86] = (delegate* unmanaged)&_getArrayInitializationData; + callbacks[87] = (delegate* unmanaged)&_canAccessClass; + callbacks[88] = (delegate* unmanaged)&_getFieldName; + callbacks[89] = (delegate* unmanaged)&_getFieldClass; + callbacks[90] = (delegate* unmanaged)&_getFieldType; + callbacks[91] = (delegate* unmanaged)&_getFieldOffset; + callbacks[92] = (delegate* unmanaged)&_getFieldInfo; + callbacks[93] = (delegate* unmanaged)&_isFieldStatic; + callbacks[94] = (delegate* unmanaged)&_getBoundaries; + callbacks[95] = (delegate* unmanaged)&_setBoundaries; + callbacks[96] = (delegate* unmanaged)&_getVars; + callbacks[97] = (delegate* unmanaged)&_setVars; + callbacks[98] = (delegate* unmanaged)&_allocateArray; + callbacks[99] = (delegate* unmanaged)&_freeArray; + callbacks[100] = (delegate* unmanaged)&_getArgNext; + callbacks[101] = (delegate* unmanaged)&_getArgType; + callbacks[102] = (delegate* unmanaged)&_getArgClass; + callbacks[103] = (delegate* unmanaged)&_getHFAType; + callbacks[104] = (delegate* unmanaged)&_GetErrorHRESULT; + callbacks[105] = (delegate* unmanaged)&_GetErrorMessage; + callbacks[106] = (delegate* unmanaged)&_FilterException; + callbacks[107] = (delegate* unmanaged)&_HandleException; + callbacks[108] = (delegate* unmanaged)&_ThrowExceptionForJitResult; + callbacks[109] = (delegate* unmanaged)&_ThrowExceptionForHelper; + callbacks[110] = (delegate* unmanaged)&_runWithErrorTrap; + callbacks[111] = (delegate* unmanaged)&_getEEInfo; + callbacks[112] = (delegate* unmanaged)&_getJitTimeLogFilename; + callbacks[113] = (delegate* unmanaged)&_getMethodDefFromMethod; + callbacks[114] = (delegate* unmanaged)&_getMethodName; + callbacks[115] = (delegate* unmanaged)&_getMethodNameFromMetadata; + callbacks[116] = (delegate* unmanaged)&_getMethodHash; + callbacks[117] = (delegate* unmanaged)&_findNameOfToken; + callbacks[118] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; + callbacks[119] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[120] = (delegate* unmanaged)&_getInlinedCallFrameVptr; + callbacks[121] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[122] = (delegate* unmanaged)&_getHelperFtn; + callbacks[123] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[124] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[125] = (delegate* unmanaged)&_getMethodSync; + callbacks[126] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[127] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[128] = (delegate* unmanaged)&_embedClassHandle; + callbacks[129] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[130] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[131] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[132] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[133] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[134] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[135] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[136] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[137] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[138] = (delegate* unmanaged)&_getCallInfo; + callbacks[139] = (delegate* unmanaged)&_canAccessFamily; + callbacks[140] = (delegate* unmanaged)&_isRIDClassDomainID; + callbacks[141] = (delegate* unmanaged)&_getClassDomainID; + callbacks[142] = (delegate* unmanaged)&_getFieldAddress; + callbacks[143] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[144] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[145] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[146] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[147] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[148] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[149] = (delegate* unmanaged)&_setOverride; + callbacks[150] = (delegate* unmanaged)&_addActiveDependency; + callbacks[151] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[152] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[153] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[154] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[155] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[156] = (delegate* unmanaged)&_allocMem; + callbacks[157] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[158] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[159] = (delegate* unmanaged)&_allocGCInfo; + callbacks[160] = (delegate* unmanaged)&_setEHcount; + callbacks[161] = (delegate* unmanaged)&_setEHinfo; + callbacks[162] = (delegate* unmanaged)&_logMsg; + callbacks[163] = (delegate* unmanaged)&_doAssert; + callbacks[164] = (delegate* unmanaged)&_reportFatalError; + callbacks[165] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[166] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[167] = (delegate* unmanaged)&_getLikelyClass; + callbacks[168] = (delegate* unmanaged)&_recordCallSite; + callbacks[169] = (delegate* unmanaged)&_recordRelocation; + callbacks[170] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[171] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[172] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index cb7e7000b1076a..16a3582b14cc30 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1056,6 +1056,12 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) return result != null ? ObjectToHandle(result) : null; } + private UIntPtr getIsClassInitedFieldAddress(CORINFO_CLASS_STRUCT_* ftn, ref int pIsInitedMask) + { + pIsInitedMask = 0; + return UIntPtr.Zero; + } + private CORINFO_CLASS_STRUCT_* getDefaultComparerClass(CORINFO_CLASS_STRUCT_* elemType) { TypeDesc comparand = HandleToObject(elemType); diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index d4640311e8818a..eb6fc41e7bd482 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -164,6 +164,7 @@ FUNCTIONS void getMethodVTableOffset( CORINFO_METHOD_HANDLE method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection, bool* isRelative); bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info); CORINFO_METHOD_HANDLE getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg); + size_t getIsClassInitedFieldAddress(CORINFO_CLASS_HANDLE cls, int* pIsInitedMask); CORINFO_CLASS_HANDLE getDefaultComparerClass(CORINFO_CLASS_HANDLE elemType); CORINFO_CLASS_HANDLE getDefaultEqualityComparerClass(CORINFO_CLASS_HANDLE elemType); void expandRawHandleIntrinsic(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_GENERICHANDLE_RESULT * pResult); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface.h b/src/coreclr/tools/aot/jitinterface/jitinterface.h index 3e61333832c257..37f1d7393e31d0 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface.h @@ -25,6 +25,7 @@ struct JitInterfaceCallbacks void (* getMethodVTableOffset)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE method, unsigned* offsetOfIndirection, unsigned* offsetAfterIndirection, bool* isRelative); bool (* resolveVirtualMethod)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_DEVIRTUALIZATION_INFO* info); CORINFO_METHOD_HANDLE (* getUnboxedEntry)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg); + size_t (* getIsClassInitedFieldAddress)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls, int* pIsInitedMask); CORINFO_CLASS_HANDLE (* getDefaultComparerClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE elemType); CORINFO_CLASS_HANDLE (* getDefaultEqualityComparerClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE elemType); void (* expandRawHandleIntrinsic)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_GENERICHANDLE_RESULT* pResult); @@ -340,6 +341,16 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } + virtual size_t getIsClassInitedFieldAddress( + CORINFO_CLASS_HANDLE cls, + int* pIsInitedMask) +{ + CorInfoExceptionClass* pException = nullptr; + size_t temp = _callbacks->getIsClassInitedFieldAddress(_thisHandle, &pException, cls, pIsInitedMask); + if (pException != nullptr) throw pException; + return temp; +} + virtual CORINFO_CLASS_HANDLE getDefaultComparerClass( CORINFO_CLASS_HANDLE elemType) { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 4c1ba0afb5c107..cced18611a6fe9 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9073,6 +9073,44 @@ CORINFO_METHOD_HANDLE CEEInfo::getUnboxedEntry( return result; } +size_t CEEInfo::getIsClassInitedFieldAddress( + CORINFO_CLASS_HANDLE cls, + int* pIsInitedMask) +{ + CONTRACTL{ + NOTHROW; + GC_NOTRIGGER; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + size_t result; + + JIT_TO_EE_TRANSITION(); + + TypeHandle VMClsHnd(cls); + PTR_MethodTable pMT = VMClsHnd.AsMethodTable(); + + UINT32 clsIndex = 0; + if (pMT->IsDynamicStatics()) + { + clsIndex = (UINT32)pMT->GetModuleDynamicEntryID(); + } + else + { + clsIndex = (UINT32)pMT->GetClassIndex(); + } + + Module* pModule = pMT->GetModuleForStatics(); + size_t moduleId = pModule->GetModuleID(); + result = (size_t)((UINT8*)moduleId + DomainLocalModule::GetOffsetOfDataBlob() + clsIndex); + + *pIsInitedMask = ClassInitFlags::INITIALIZED_FLAG; + + EE_TO_JIT_TRANSITION(); + + return result; +} + /*********************************************************************/ void CEEInfo::expandRawHandleIntrinsic( CORINFO_RESOLVED_TOKEN * pResolvedToken, diff --git a/src/coreclr/zap/zapinfo.cpp b/src/coreclr/zap/zapinfo.cpp index 73a162497fcaa5..f4a0941e5de359 100644 --- a/src/coreclr/zap/zapinfo.cpp +++ b/src/coreclr/zap/zapinfo.cpp @@ -4069,6 +4069,13 @@ CORINFO_METHOD_HANDLE ZapInfo::getUnboxedEntry( return m_pEEJitInfo->getUnboxedEntry(ftn, requiresInstMethodTableArg); } +size_t ZapInfo::getIsClassInitedFieldAddress( + CORINFO_CLASS_HANDLE cls, + int* pIsInitedMask) +{ + return m_pEEJitInfo->getIsClassInitedFieldAddress(cls, pIsInitedMask); +} + CORINFO_CLASS_HANDLE ZapInfo::getDefaultComparerClass( CORINFO_CLASS_HANDLE elemType) { diff --git a/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrderEffects.cs b/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrderEffects.cs new file mode 100644 index 00000000000000..9c99f5ed8dfa21 --- /dev/null +++ b/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrderEffects.cs @@ -0,0 +1,200 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +public class StaticInitOrder +{ + private static int s_ReturnCode = 100; + + public static int Main(string[] args) + { + AssertEquals(0, TestStatcCtor(0)); + AssertEquals(420, TestStatcCtor(10)); + + CallCounter.Calls = 0; + AssertEquals(0, TestBeforeFieldInit(0)); + AssertEquals(1420, TestBeforeFieldInit(10)); + + CallCounter.Calls = 0; + AssertEquals(0, TestStatcCtor_Generic_vt(0)); + AssertEquals(50, TestStatcCtor_Generic_ref(10)); + + CallCounter.Calls = 0; + AssertEquals(0, TestBeforeFieldInit_Generic_vt(0)); + AssertEquals(50, TestBeforeFieldInit_Generic_ref(10)); + + return s_ReturnCode; + } + + private static int TestStatcCtor(int iterations) + { + int value = 0; + AssertEquals(0, CallCounter.Calls); + for (int i = 0; i < iterations; i++) + { + if (i == 0) + { + AssertEquals(0, CallCounter.Calls); + } + value += StaticCctor.Value; + AssertEquals(1, CallCounter.Calls); + } + AssertEquals(iterations == 0 ? 0 : 1, CallCounter.Calls); + return value; + } + + private static int TestStatcCtor_Generic_vt(int iterations) + { + int value = 0; + AssertEquals(0, CallCounter.Calls); + for (int i = 0; i < iterations; i++) + { + if (i == 0) + { + AssertEquals(0, CallCounter.Calls); + } + value += StaticCctorGeneric.Value; + AssertEquals(1, CallCounter.Calls); + } + AssertEquals(iterations == 0 ? 0 : 1, CallCounter.Calls); + return value; + } + + private static int TestStatcCtor_Generic_ref(int iterations) + { + int value = 0; + AssertEquals(0, CallCounter.Calls); + for (int i = 0; i < iterations; i++) + { + if (i == 0) + { + AssertEquals(0, CallCounter.Calls); + } + value += StaticCctorGeneric.Value.Length; + AssertEquals(1, CallCounter.Calls); + } + AssertEquals(iterations == 0 ? 0 : 1, CallCounter.Calls); + return value; + } + + private static int TestBeforeFieldInit(int iterations) + { + int value = 0; + AssertEquals(0, CallCounter.Calls); + for (int i = 0; i < iterations; i++) + { + if (i == 0) + { + AssertEquals(0, CallCounter.Calls); + } + value += FieldInit.Value; + AssertEquals(1, CallCounter.Calls); + } + AssertEquals(iterations == 0 ? 0 : 1, CallCounter.Calls); + return value; + } + + private static int TestBeforeFieldInit_Generic_vt(int iterations) + { + int value = 0; + AssertEquals(0, CallCounter.Calls); + for (int i = 0; i < iterations; i++) + { + if (i == 0) + { + AssertEquals(0, CallCounter.Calls); + } + value += FieldInitGeneric.Value; + AssertEquals(1, CallCounter.Calls); + } + AssertEquals(iterations == 0 ? 0 : 1, CallCounter.Calls); + return value; + } + + private static int TestBeforeFieldInit_Generic_ref(int iterations) + { + int value = 0; + AssertEquals(0, CallCounter.Calls); + for (int i = 0; i < iterations; i++) + { + if (i == 0) + { + AssertEquals(0, CallCounter.Calls); + } + value += FieldInitGeneric.Value.Length; + AssertEquals(1, CallCounter.Calls); + } + AssertEquals(iterations == 0 ? 0 : 1, CallCounter.Calls); + return value; + } + + private static void AssertEquals(int expected, int actual, [CallerLineNumber] int line = 0) + { + if (expected != actual) + { + Console.WriteLine($"{expected} != {actual}, L{line}"); + s_ReturnCode++; + } + } +} + +public static class CallCounter +{ + public static int Calls { get; set; } +} + +public static class StaticCctor +{ + public static readonly int Value; + + static StaticCctor() + { + CallCounter.Calls++; + Value = 42; + } +} + +public static class StaticCctorGeneric +{ + public static readonly T Value; + + static StaticCctorGeneric() + { + CallCounter.Calls++; + if (typeof(T) == typeof(string)) + Value = (T) (object)"hello"; + else if (typeof(T) == typeof(int)) + Value = (T)(object)242; + else + Value = default(T); + } +} + +public static class FieldInit +{ + public static int Value { get; } = Initialize(); + + private static int Initialize() + { + CallCounter.Calls++; + return 142; + } +} + +public static class FieldInitGeneric +{ + public static T Value { get; } = Initialize(); + + private static T Initialize() + { + CallCounter.Calls++; + if (typeof(T) == typeof(string)) + return (T)(object)"hello"; + else if (typeof(T) == typeof(int)) + return (T)(object)242; + else + return default(T); + } +} diff --git a/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrderEffects.csproj b/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrderEffects.csproj new file mode 100644 index 00000000000000..811f235d6fcfe5 --- /dev/null +++ b/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrderEffects.csproj @@ -0,0 +1,9 @@ + + + Exe + True + + + + + diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 8cbaa5e5cacbd7..eeec7f532ca5db 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1039,6 +1039,9 @@ Mono doesn't have a dynamic pgo or tiered compilation infrastructure + + The order of static initialization is different on Mono + https://github.com/dotnet/runtime/issues/46174