From c91495efc25c30c0bb979487d01c6ebbd6523f71 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 3 Mar 2021 09:46:29 +0300 Subject: [PATCH 01/20] Insert checks in a later phase --- src/coreclr/jit/compiler.cpp | 4 ++ src/coreclr/jit/compiler.h | 2 + src/coreclr/jit/compphases.h | 1 + src/coreclr/jit/flowgraph.cpp | 127 ++++++++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+) 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 a38071f31a3696..f253e3fbf42eb1 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5184,6 +5184,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/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 8b89c2e5945a55..50d8ed602f2ae7 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -52,6 +52,133 @@ 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 (!strcmp(info.compMethodName, "Test")) + { + fgDispBasicBlocks(true); + } + + if (!opts.OptimizationEnabled()) + { + return PhaseStatus::MODIFIED_NOTHING; + } + + bool modified = false; + BasicBlock* block; + for (block = fgFirstBB; block; block = block->bbNext) + { + if (!block->isRunRarely() && block->bbFlags) + { + for (Statement* stmt : block->Statements()) + { + for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext) + { + if (!tree->IsCall()) + { + continue; + } + + GenTreeCall* call = tree->AsCall(); + if (call->gtCallType != CT_HELPER) + { + continue; + } + + CorInfoHelpFunc helpFunc = eeGetHelperNum(call->gtCallMethHnd); + if (helpFunc != CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) + { + continue; + } + + GenTree* moduleIdArg = call->LateArgs().begin()->GetNode(); + GenTree* clsIdArg = call->LateArgs().begin()->GetNext()->GetNode(); + + if (!moduleIdArg->IsCnsIntOrI() || !clsIdArg->IsCnsIntOrI()) + { + // Looks like moduleId or/and clsId were passed as indirect loads + continue; + } + + // + // * JTRUE void + // \--* NE int + // +--* AND int + // | +--* IND ubyte + // | | \--* CNS_INT long moduleIdArg + clsIdArg + dataBlobOffset + // | \--* CNS_INT int isInitMask + // \--* CNS_INT int 0 + // + // * CALL help long HELPER.CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE + // +--* CNS_INT long moduleIdArg + // \--* CNS_INT int clsIdArg + // + + BasicBlock* callInitBb = fgNewBBbefore(BBJ_NONE, block, true); + fgAddRefPred(block, callInitBb); + callInitBb->bbSetRunRarely(); + callInitBb->bbFlags |= block->bbFlags & (BBF_SPLIT_GAINED | BBF_IMPORTED | BBF_HAS_CALL); + + GenTree* clonedHelperCall = gtCloneExprCallHelper(call); + clonedHelperCall->gtFlags |= call->gtFlags; + fgInsertStmtAtEnd(callInitBb, fgNewStmtFromTree(clonedHelperCall)); + + BasicBlock* isInitedBb = fgNewBBbefore(BBJ_COND, callInitBb, true); + fgAddRefPred(callInitBb, isInitedBb); + fgAddRefPred(block, isInitedBb); + isInitedBb->inheritWeight(block); + isInitedBb->bbFlags |= block->bbFlags & (BBF_SPLIT_GAINED | BBF_IMPORTED | BBF_HAS_CALL); + + // TODO: ask VM for these constants: + const int dataBlobOffset = 48; + const int isInitMask = 1; + + size_t address = moduleIdArg->AsIntCon()->IconValue() + dataBlobOffset + clsIdArg->AsIntCon()->IconValue(); + GenTree* indir = gtNewIndir(TYP_UBYTE, gtNewIconNode(address, TYP_I_IMPL)); + indir->gtFlags = (GTF_IND_NONFAULTING | GTF_IND_INVARIANT); + + GenTree* isInitedMask = gtNewOperNode(GT_AND, TYP_INT, indir, gtNewIconNode(isInitMask)); + GenTree* isInitedCmp = gtNewOperNode(GT_NE, TYP_INT, isInitedMask, gtNewIconNode(0)); + + fgInsertStmtAtEnd(isInitedBb, fgNewStmtFromTree(gtNewOperNode(GT_JTRUE, TYP_VOID, isInitedCmp))); + isInitedBb->bbJumpDest = block; + gtReplaceTree(stmt, call, gtNewNothingNode()); + + + modified = true; + } + if (modified) + { + // clear GTF_CALL and GTF_EXC flags + gtUpdateStmtSideEffects(stmt); + } + } + } + } + + if (!strcmp(info.compMethodName, "Test")) + { + fgDispBasicBlocks(true); + } + + if (modified) + { + fgReorderBlocks(); + fgUpdateChangedFlowGraph(false); + return PhaseStatus::MODIFIED_EVERYTHING; + } + return PhaseStatus::MODIFIED_NOTHING; +} + //------------------------------------------------------------------------------ // fgInsertGCPolls : Insert GC polls for basic blocks containing calls to methods // with SuppressGCTransitionAttribute. From 0e1585d2039ae28cba193c2877a12bcff0795d53 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 3 Mar 2021 17:43:20 +0300 Subject: [PATCH 02/20] Add comments --- src/coreclr/jit/flowgraph.cpp | 143 ++++++++++++++++++++++++++-------- 1 file changed, 112 insertions(+), 31 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 50d8ed602f2ae7..44dd15a53ae4ba 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -62,12 +62,6 @@ static bool blockNeedsGCPoll(BasicBlock* block) PhaseStatus Compiler::fgInsertClsInitChecks() { - - if (!strcmp(info.compMethodName, "Test")) - { - fgDispBasicBlocks(true); - } - if (!opts.OptimizationEnabled()) { return PhaseStatus::MODIFIED_NOTHING; @@ -75,16 +69,20 @@ PhaseStatus Compiler::fgInsertClsInitChecks() bool modified = false; BasicBlock* block; + + BasicBlock* prevBb = nullptr; for (block = fgFirstBB; block; block = block->bbNext) { - if (!block->isRunRarely() && block->bbFlags) + if (!block->isRunRarely()) { for (Statement* stmt : block->Statements()) { for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext) { - if (!tree->IsCall()) + // we only need GT_CALL nodes with helper funcs + if (!tree->IsCall() || (tree->gtFlags & GTF_CALL_HOISTABLE)) { + // TODO: remove that GTF_CALL_HOISTABLE check continue; } @@ -95,13 +93,14 @@ PhaseStatus Compiler::fgInsertClsInitChecks() } CorInfoHelpFunc helpFunc = eeGetHelperNum(call->gtCallMethHnd); - if (helpFunc != CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) + if ((helpFunc != CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) || + (call->fgArgInfo->ArgCount() != 2)) { continue; } - GenTree* moduleIdArg = call->LateArgs().begin()->GetNode(); - GenTree* clsIdArg = call->LateArgs().begin()->GetNext()->GetNode(); + GenTree* moduleIdArg = call->fgArgInfo->GetArgNode(0); + GenTree* clsIdArg = call->fgArgInfo->GetArgNode(1); if (!moduleIdArg->IsCnsIntOrI() || !clsIdArg->IsCnsIntOrI()) { @@ -109,6 +108,31 @@ PhaseStatus Compiler::fgInsertClsInitChecks() continue; } + if (clsIdArg->AsIntCon()->IconValue() < 0) + { + // Unknown clsId + continue; + } + + if (prevBb == nullptr) + { + // We're going to emit a BB in front of fgFirstBB + fgEnsureFirstBBisScratch(); + prevBb = fgFirstBB; + if (prevBb == block) + { + 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 @@ -118,62 +142,119 @@ PhaseStatus Compiler::fgInsertClsInitChecks() // | \--* CNS_INT int isInitMask // \--* CNS_INT int 0 // + // + // BB2 "callInitBb": (preds: BB1) + // // * CALL help long HELPER.CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE // +--* CNS_INT long moduleIdArg // \--* CNS_INT int clsIdArg // - + // BB3 "block" (preds: BB1, BB2) + // ... + // + + + // Let's start from emitting that BB2 "callInitBb" BasicBlock* callInitBb = fgNewBBbefore(BBJ_NONE, block, true); - fgAddRefPred(block, callInitBb); + // it's executed only once so can be marked as cold callInitBb->bbSetRunRarely(); - callInitBb->bbFlags |= block->bbFlags & (BBF_SPLIT_GAINED | BBF_IMPORTED | BBF_HAS_CALL); - + callInitBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_CALL | BBF_HAS_LABEL); GenTree* clonedHelperCall = gtCloneExprCallHelper(call); clonedHelperCall->gtFlags |= call->gtFlags; - fgInsertStmtAtEnd(callInitBb, fgNewStmtFromTree(clonedHelperCall)); + Statement* callStmt = fgNewStmtFromTree(clonedHelperCall); + if (fgStmtListThreaded) + { + gtSetStmtInfo(callStmt); + fgSetStmtSeq(callStmt); + } + fgInsertStmtAtEnd(callInitBb, callStmt); + gtUpdateStmtSideEffects(callStmt); + + // BB1 "isInitedBb" BasicBlock* isInitedBb = fgNewBBbefore(BBJ_COND, callInitBb, true); - fgAddRefPred(callInitBb, isInitedBb); - fgAddRefPred(block, isInitedBb); isInitedBb->inheritWeight(block); - isInitedBb->bbFlags |= block->bbFlags & (BBF_SPLIT_GAINED | BBF_IMPORTED | BBF_HAS_CALL); + isInitedBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_LABEL | BBF_HAS_JMP); // TODO: ask VM for these constants: - const int dataBlobOffset = 48; - const int isInitMask = 1; + const int dataBlobOffset = 48; // DomainLocalModule::GetOffsetOfDataBlob() + const int isInitMask = 1; // ClassInitFlags::INITIALIZED_FLAG; size_t address = moduleIdArg->AsIntCon()->IconValue() + dataBlobOffset + clsIdArg->AsIntCon()->IconValue(); GenTree* indir = gtNewIndir(TYP_UBYTE, gtNewIconNode(address, TYP_I_IMPL)); indir->gtFlags = (GTF_IND_NONFAULTING | GTF_IND_INVARIANT); GenTree* isInitedMask = gtNewOperNode(GT_AND, TYP_INT, indir, gtNewIconNode(isInitMask)); - GenTree* isInitedCmp = gtNewOperNode(GT_NE, TYP_INT, isInitedMask, gtNewIconNode(0)); + GenTree* isInitedCmp = gtNewOperNode(GT_GT, TYP_INT, isInitedMask, gtNewIconNode(0)); + isInitedCmp->gtFlags |= (GTF_UNSIGNED | GTF_RELOP_JMP_USED); - fgInsertStmtAtEnd(isInitedBb, fgNewStmtFromTree(gtNewOperNode(GT_JTRUE, TYP_VOID, isInitedCmp))); + Statement* isInitedStmt = fgNewStmtFromTree(gtNewOperNode(GT_JTRUE, TYP_VOID, isInitedCmp)); + if (fgStmtListThreaded) + { + gtSetStmtInfo(isInitedStmt); + fgSetStmtSeq(isInitedStmt); + } + + fgInsertStmtAtEnd(isInitedBb, isInitedStmt); isInitedBb->bbJumpDest = block; - gtReplaceTree(stmt, call, gtNewNothingNode()); + block->bbFlags |= BBF_JMP_TARGET; + + // Now we can remove the call from the current block + // We're going to replace the call with just "moduleId" node (it's what it was supposed to return) + gtReplaceTree(stmt, call, gtNewIconNode(moduleIdArg->AsIntCon()->IconValue(), call->TypeGet())); + + // Now we need to fix all the preds: + + // isInitedBb is a pred of callInitBb + fgAddRefPred(callInitBb, isInitedBb); + for (flowList* pred = block->bbPreds; pred != nullptr; pred = pred->flNext) + { + // Redirect all the preds from the current block to isInitedBb + + // TODO: should I check EH region here? + // TODO: should I update some loop info if I'm inside a loop? + + BasicBlock* predBlock = pred->getBlock(); + if (predBlock->bbJumpDest == block) + { + predBlock->bbJumpDest = isInitedBb; + isInitedBb->bbFlags |= BBF_JMP_TARGET; + } + fgRemoveRefPred(block, predBlock); + fgAddRefPred(isInitedBb, predBlock); + } + // Both callInitBb and isInitedBb are preds of block now + fgAddRefPred(block, callInitBb); + fgAddRefPred(block, isInitedBb); + // Make sure all three basic blocks are in the same EH region: + BasicBlock::sameEHRegion(callInitBb, block); + BasicBlock::sameEHRegion(isInitedBb, block); modified = true; } if (modified) { - // clear GTF_CALL and GTF_EXC flags + // clear GTF_CALL and GTF_EXC flags (we've just removed a call) gtUpdateStmtSideEffects(stmt); } } } - } - - if (!strcmp(info.compMethodName, "Test")) - { - fgDispBasicBlocks(true); + prevBb = block; } if (modified) { +#ifdef DEBUG + if (verbose) + { + printf("\nAfter fgInsertClsInitChecks:"); + fgDispBasicBlocks(true); + } +#endif // DEBUG fgReorderBlocks(); - fgUpdateChangedFlowGraph(false); + constexpr bool computeDoms = false; + fgUpdateChangedFlowGraph(computeDoms); return PhaseStatus::MODIFIED_EVERYTHING; } return PhaseStatus::MODIFIED_NOTHING; From 6cc18c6f07c2d64ae412586483b7dd96feef5443 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Wed, 3 Mar 2021 23:51:10 +0300 Subject: [PATCH 03/20] fix bugs --- src/coreclr/jit/flowgraph.cpp | 75 +++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 44dd15a53ae4ba..36213895ab11e5 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -80,9 +80,8 @@ PhaseStatus Compiler::fgInsertClsInitChecks() for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext) { // we only need GT_CALL nodes with helper funcs - if (!tree->IsCall() || (tree->gtFlags & GTF_CALL_HOISTABLE)) + if (!tree->IsCall()) { - // TODO: remove that GTF_CALL_HOISTABLE check continue; } @@ -108,9 +107,24 @@ PhaseStatus Compiler::fgInsertClsInitChecks() continue; } - if (clsIdArg->AsIntCon()->IconValue() < 0) + bool hasNotSupportedPreds = false; + for (flowList* pred = block->bbPreds; pred != nullptr; pred = pred->flNext) + { + BasicBlock* predBlock = pred->getBlock(); + if ((predBlock->bbJumpKind != BBJ_NONE) && (predBlock->bbJumpKind != BBJ_ALWAYS) && + (predBlock->bbJumpKind != BBJ_COND)) + { + hasNotSupportedPreds = true; + break; + } + if (!BasicBlock::sameEHRegion(predBlock, block)) + { + hasNotSupportedPreds = true; + break; + } + } + if (hasNotSupportedPreds) { - // Unknown clsId continue; } @@ -153,6 +167,12 @@ PhaseStatus Compiler::fgInsertClsInitChecks() // ... // + // TODO: ask VM for these constants: + const int dataBlobOffset = 48; // DomainLocalModule::GetOffsetOfDataBlob() + const int isInitMask = 1; // ClassInitFlags::INITIALIZED_FLAG; + + UINT8* isInitAdr = (UINT8*)moduleIdArg->AsIntCon()->IconValue() + dataBlobOffset + clsIdArg->AsIntCon()->IconValue(); + GenTree* isInitAdrNode = gtNewIndOfIconHandleNode(TYP_UBYTE, (size_t)isInitAdr, GTF_ICON_CONST_PTR, true); // Let's start from emitting that BB2 "callInitBb" BasicBlock* callInitBb = fgNewBBbefore(BBJ_NONE, block, true); @@ -176,17 +196,10 @@ PhaseStatus Compiler::fgInsertClsInitChecks() isInitedBb->inheritWeight(block); isInitedBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_LABEL | BBF_HAS_JMP); - // TODO: ask VM for these constants: - const int dataBlobOffset = 48; // DomainLocalModule::GetOffsetOfDataBlob() - const int isInitMask = 1; // ClassInitFlags::INITIALIZED_FLAG; - - size_t address = moduleIdArg->AsIntCon()->IconValue() + dataBlobOffset + clsIdArg->AsIntCon()->IconValue(); - GenTree* indir = gtNewIndir(TYP_UBYTE, gtNewIconNode(address, TYP_I_IMPL)); - indir->gtFlags = (GTF_IND_NONFAULTING | GTF_IND_INVARIANT); - - GenTree* isInitedMask = gtNewOperNode(GT_AND, TYP_INT, indir, gtNewIconNode(isInitMask)); - GenTree* isInitedCmp = gtNewOperNode(GT_GT, TYP_INT, isInitedMask, gtNewIconNode(0)); - isInitedCmp->gtFlags |= (GTF_UNSIGNED | GTF_RELOP_JMP_USED); + GenTree* isInitedMask = gtNewOperNode(GT_AND, TYP_INT, gtNewCastNode(TYP_INT, isInitAdrNode, true, TYP_INT), gtNewIconNode(isInitMask)); + GenTree* isInitedCmp = gtNewOperNode(GT_NE, TYP_INT, isInitedMask, gtNewIconNode(0)); + isInitedCmp->gtFlags |= (GTF_RELOP_JMP_USED | GTF_DONT_CSE); + gtSetEvalOrder(isInitedCmp); Statement* isInitedStmt = fgNewStmtFromTree(gtNewOperNode(GT_JTRUE, TYP_VOID, isInitedCmp)); if (fgStmtListThreaded) @@ -201,7 +214,11 @@ PhaseStatus Compiler::fgInsertClsInitChecks() // Now we can remove the call from the current block // We're going to replace the call with just "moduleId" node (it's what it was supposed to return) - gtReplaceTree(stmt, call, gtNewIconNode(moduleIdArg->AsIntCon()->IconValue(), call->TypeGet())); + + GenTree* clonedHelperCall2 = gtCloneExprCallHelper(call); + clonedHelperCall2->gtFlags |= call->gtFlags; + + gtReplaceTree(stmt, call, gtClone(moduleIdArg)); // Now we need to fix all the preds: @@ -210,16 +227,19 @@ PhaseStatus Compiler::fgInsertClsInitChecks() for (flowList* pred = block->bbPreds; pred != nullptr; pred = pred->flNext) { // Redirect all the preds from the current block to isInitedBb - - // TODO: should I check EH region here? - // TODO: should I update some loop info if I'm inside a loop? - BasicBlock* predBlock = pred->getBlock(); + assert((predBlock->bbJumpKind == BBJ_NONE) || (predBlock->bbJumpKind == BBJ_ALWAYS) || + (predBlock->bbJumpKind == BBJ_COND)); + if (predBlock->bbJumpDest == block) { predBlock->bbJumpDest = isInitedBb; isInitedBb->bbFlags |= BBF_JMP_TARGET; } + if (predBlock->bbNext == block) + { + predBlock->bbNext = isInitedBb; + } fgRemoveRefPred(block, predBlock); fgAddRefPred(isInitedBb, predBlock); } @@ -228,8 +248,12 @@ PhaseStatus Compiler::fgInsertClsInitChecks() fgAddRefPred(block, isInitedBb); // Make sure all three basic blocks are in the same EH region: - BasicBlock::sameEHRegion(callInitBb, block); - BasicBlock::sameEHRegion(isInitedBb, block); + assert(BasicBlock::sameEHRegion(callInitBb, block)); + assert(BasicBlock::sameEHRegion(isInitedBb, block)); + + assert(isInitedBb->bbNext == callInitBb); + assert(callInitBb->bbNext == block); + assert(isInitedBb->bbJumpDest == block); modified = true; } @@ -248,13 +272,14 @@ PhaseStatus Compiler::fgInsertClsInitChecks() #ifdef DEBUG if (verbose) { - printf("\nAfter fgInsertClsInitChecks:"); + printf("*************** After fgInsertClsInitChecks()\n"); fgDispBasicBlocks(true); } #endif // DEBUG fgReorderBlocks(); - constexpr bool computeDoms = false; - fgUpdateChangedFlowGraph(computeDoms); + + // TODO: do I need to call it since I fixed all the preds by hands? + fgUpdateChangedFlowGraph(false); return PhaseStatus::MODIFIED_EVERYTHING; } return PhaseStatus::MODIFIED_NOTHING; From 7acf8e57038cbe98ef222430a246a24290de9955 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 4 Mar 2021 00:34:43 +0300 Subject: [PATCH 04/20] Formatting --- src/coreclr/jit/flowgraph.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 36213895ab11e5..9de1b9f071e234 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -69,7 +69,6 @@ PhaseStatus Compiler::fgInsertClsInitChecks() bool modified = false; BasicBlock* block; - BasicBlock* prevBb = nullptr; for (block = fgFirstBB; block; block = block->bbNext) { @@ -92,8 +91,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() } CorInfoHelpFunc helpFunc = eeGetHelperNum(call->gtCallMethHnd); - if ((helpFunc != CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) || - (call->fgArgInfo->ArgCount() != 2)) + if ((helpFunc != CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) || (call->fgArgInfo->ArgCount() != 2)) { continue; } @@ -130,7 +128,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() if (prevBb == nullptr) { - // We're going to emit a BB in front of fgFirstBB + // We're going to emit a BB in front of fgFirstBB fgEnsureFirstBBisScratch(); prevBb = fgFirstBB; if (prevBb == block) @@ -171,8 +169,12 @@ PhaseStatus Compiler::fgInsertClsInitChecks() const int dataBlobOffset = 48; // DomainLocalModule::GetOffsetOfDataBlob() const int isInitMask = 1; // ClassInitFlags::INITIALIZED_FLAG; - UINT8* isInitAdr = (UINT8*)moduleIdArg->AsIntCon()->IconValue() + dataBlobOffset + clsIdArg->AsIntCon()->IconValue(); - GenTree* isInitAdrNode = gtNewIndOfIconHandleNode(TYP_UBYTE, (size_t)isInitAdr, GTF_ICON_CONST_PTR, true); + UINT8* isInitAdr = (UINT8*)moduleIdArg->AsIntCon()->IconValue() + dataBlobOffset + + clsIdArg->AsIntCon()->IconValue(); + + GenTree* isInitAdrNode = + gtNewIndOfIconHandleNode(TYP_UBYTE, (size_t)isInitAdr, GTF_ICON_CONST_PTR, true); + // Let's start from emitting that BB2 "callInitBb" BasicBlock* callInitBb = fgNewBBbefore(BBJ_NONE, block, true); @@ -196,8 +198,11 @@ PhaseStatus Compiler::fgInsertClsInitChecks() isInitedBb->inheritWeight(block); isInitedBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_LABEL | BBF_HAS_JMP); - GenTree* isInitedMask = gtNewOperNode(GT_AND, TYP_INT, gtNewCastNode(TYP_INT, isInitAdrNode, true, TYP_INT), gtNewIconNode(isInitMask)); - GenTree* isInitedCmp = gtNewOperNode(GT_NE, TYP_INT, isInitedMask, gtNewIconNode(0)); + GenTree* isInitedMask = + gtNewOperNode(GT_AND, TYP_INT, gtNewCastNode(TYP_INT, isInitAdrNode, true, TYP_INT), + gtNewIconNode(isInitMask)); + + GenTree* isInitedCmp = gtNewOperNode(GT_NE, TYP_INT, isInitedMask, gtNewIconNode(0)); isInitedCmp->gtFlags |= (GTF_RELOP_JMP_USED | GTF_DONT_CSE); gtSetEvalOrder(isInitedCmp); From b4adf0027debb37401fbf505210bd99cf49e84fa Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 4 Mar 2021 01:10:47 +0300 Subject: [PATCH 05/20] Formatting --- src/coreclr/jit/flowgraph.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 9de1b9f071e234..0a1e1c37feefc6 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -175,7 +175,6 @@ PhaseStatus Compiler::fgInsertClsInitChecks() GenTree* isInitAdrNode = gtNewIndOfIconHandleNode(TYP_UBYTE, (size_t)isInitAdr, GTF_ICON_CONST_PTR, true); - // Let's start from emitting that BB2 "callInitBb" BasicBlock* callInitBb = fgNewBBbefore(BBJ_NONE, block, true); // it's executed only once so can be marked as cold From c0c2cf31cafdb89f51c75f88e91ea7875c854829 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 4 Mar 2021 12:04:51 +0300 Subject: [PATCH 06/20] Fix 32bit, clean up --- src/coreclr/jit/flowgraph.cpp | 59 ++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 0a1e1c37feefc6..56162c30c41bb5 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -78,7 +78,8 @@ PhaseStatus Compiler::fgInsertClsInitChecks() { for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext) { - // we only need GT_CALL nodes with helper funcs + // 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; @@ -101,7 +102,9 @@ PhaseStatus Compiler::fgInsertClsInitChecks() if (!moduleIdArg->IsCnsIntOrI() || !clsIdArg->IsCnsIntOrI()) { - // Looks like moduleId or/and clsId were passed as indirect loads + // 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; } @@ -109,6 +112,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() for (flowList* pred = block->bbPreds; pred != nullptr; pred = pred->flNext) { BasicBlock* predBlock = pred->getBlock(); + // Bail out if one of the preds is e.g. BBJ_SWITCH or some EH type of BB if ((predBlock->bbJumpKind != BBJ_NONE) && (predBlock->bbJumpKind != BBJ_ALWAYS) && (predBlock->bbJumpKind != BBJ_COND)) { @@ -140,7 +144,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() // 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": // ... // @@ -166,14 +170,23 @@ PhaseStatus Compiler::fgInsertClsInitChecks() // // TODO: ask VM for these constants: +#ifdef TARGET_64BIT const int dataBlobOffset = 48; // DomainLocalModule::GetOffsetOfDataBlob() +#else + const int dataBlobOffset = 24; // DomainLocalModule::GetOffsetOfDataBlob() +#endif const int isInitMask = 1; // ClassInitFlags::INITIALIZED_FLAG; - UINT8* isInitAdr = (UINT8*)moduleIdArg->AsIntCon()->IconValue() + dataBlobOffset + + // See VM's IsClassInitialized + size_t isInitAdr = moduleIdArg->AsIntCon()->IconValue() + dataBlobOffset + clsIdArg->AsIntCon()->IconValue(); + // TODO-CQ: Actually, we can check the class initialization status right here using that ^ + // address, what if it's already initialized while we're compiling code + // in that case we can just drop the call. + GenTree* isInitAdrNode = - gtNewIndOfIconHandleNode(TYP_UBYTE, (size_t)isInitAdr, GTF_ICON_CONST_PTR, true); + gtNewIndOfIconHandleNode(TYP_UBYTE, isInitAdr, GTF_ICON_CONST_PTR, true); // Let's start from emitting that BB2 "callInitBb" BasicBlock* callInitBb = fgNewBBbefore(BBJ_NONE, block, true); @@ -184,11 +197,8 @@ PhaseStatus Compiler::fgInsertClsInitChecks() clonedHelperCall->gtFlags |= call->gtFlags; Statement* callStmt = fgNewStmtFromTree(clonedHelperCall); - if (fgStmtListThreaded) - { - gtSetStmtInfo(callStmt); - fgSetStmtSeq(callStmt); - } + gtSetStmtInfo(callStmt); + fgSetStmtSeq(callStmt); fgInsertStmtAtEnd(callInitBb, callStmt); gtUpdateStmtSideEffects(callStmt); @@ -206,31 +216,24 @@ PhaseStatus Compiler::fgInsertClsInitChecks() gtSetEvalOrder(isInitedCmp); Statement* isInitedStmt = fgNewStmtFromTree(gtNewOperNode(GT_JTRUE, TYP_VOID, isInitedCmp)); - if (fgStmtListThreaded) - { - gtSetStmtInfo(isInitedStmt); - fgSetStmtSeq(isInitedStmt); - } + gtSetStmtInfo(isInitedStmt); + fgSetStmtSeq(isInitedStmt); fgInsertStmtAtEnd(isInitedBb, isInitedStmt); isInitedBb->bbJumpDest = block; block->bbFlags |= BBF_JMP_TARGET; // Now we can remove the call from the current block - // We're going to replace the call with just "moduleId" node (it's what it was supposed to return) - - GenTree* clonedHelperCall2 = gtCloneExprCallHelper(call); - clonedHelperCall2->gtFlags |= call->gtFlags; - + // We're going to replace the call with the "moduleId" node. gtReplaceTree(stmt, call, gtClone(moduleIdArg)); - // Now we need to fix all the preds: + // Now we need to fix all the connections to predecessors and successors: - // isInitedBb is a pred of callInitBb + // isInitedBb is a predecessor of callInitBb fgAddRefPred(callInitBb, isInitedBb); for (flowList* pred = block->bbPreds; pred != nullptr; pred = pred->flNext) { - // Redirect all the preds from the current block to isInitedBb + // Redirect all the predecessors from the current block to isInitedBb BasicBlock* predBlock = pred->getBlock(); assert((predBlock->bbJumpKind == BBJ_NONE) || (predBlock->bbJumpKind == BBJ_ALWAYS) || (predBlock->bbJumpKind == BBJ_COND)); @@ -247,7 +250,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() fgRemoveRefPred(block, predBlock); fgAddRefPred(isInitedBb, predBlock); } - // Both callInitBb and isInitedBb are preds of block now + // Both callInitBb and isInitedBb are predecessors of block now fgAddRefPred(block, callInitBb); fgAddRefPred(block, isInitedBb); @@ -255,15 +258,15 @@ PhaseStatus Compiler::fgInsertClsInitChecks() assert(BasicBlock::sameEHRegion(callInitBb, block)); assert(BasicBlock::sameEHRegion(isInitedBb, block)); + assert(isInitedBb->bbJumpDest == block); assert(isInitedBb->bbNext == callInitBb); assert(callInitBb->bbNext == block); - assert(isInitedBb->bbJumpDest == block); modified = true; } if (modified) { - // clear GTF_CALL and GTF_EXC flags (we've just removed a call) + // Clear potential leftover GTF_CALL and GTF_EXC flags in the current stmt gtUpdateStmtSideEffects(stmt); } } @@ -280,10 +283,8 @@ PhaseStatus Compiler::fgInsertClsInitChecks() fgDispBasicBlocks(true); } #endif // DEBUG - fgReorderBlocks(); - // TODO: do I need to call it since I fixed all the preds by hands? - fgUpdateChangedFlowGraph(false); + fgReorderBlocks(); return PhaseStatus::MODIFIED_EVERYTHING; } return PhaseStatus::MODIFIED_NOTHING; From 1093a1e9aa24c3333c8c77e8e794cb3078b1eac0 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 4 Mar 2021 12:36:21 +0300 Subject: [PATCH 07/20] Add tests --- .../CctorsWithSideEffects/CCtorOrder.csproj | 9 + .../CctorsWithSideEffects/CctorOrder.cs | 200 ++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CCtorOrder.csproj create mode 100644 src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrder.cs diff --git a/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CCtorOrder.csproj b/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CCtorOrder.csproj new file mode 100644 index 00000000000000..54b2c09446d905 --- /dev/null +++ b/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CCtorOrder.csproj @@ -0,0 +1,9 @@ + + + Exe + True + + + + + diff --git a/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrder.cs b/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrder.cs new file mode 100644 index 00000000000000..9c99f5ed8dfa21 --- /dev/null +++ b/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrder.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); + } +} From 0c3bcba3a84b8146509e15bf2b71ab5a024ea615 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 5 Mar 2021 14:16:46 +0300 Subject: [PATCH 08/20] Address Jan's feedback --- .../superpmi/superpmi-shared/lwmlist.h | 3 +- .../superpmi-shared/methodcontext.cpp | 35 ++ .../superpmi/superpmi-shared/methodcontext.h | 7 +- .../superpmi-shim-collector/icorjitinfo.cpp | 13 + .../superpmi-shim-counter/icorjitinfo.cpp | 8 + .../superpmi-shim-simple/icorjitinfo.cpp | 7 + .../ToolBox/superpmi/superpmi/icorjitinfo.cpp | 7 + src/coreclr/inc/corinfo.h | 5 + src/coreclr/inc/icorjitinfoimpl_generated.h | 4 + src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/ICorJitInfo_API_names.h | 1 + src/coreclr/jit/ICorJitInfo_API_wrapper.hpp | 10 + src/coreclr/jit/flowgraph.cpp | 60 ++-- .../tools/Common/JitInterface/CorInfoBase.cs | 334 +++++++++--------- .../tools/Common/JitInterface/CorInfoImpl.cs | 6 + .../ThunkGenerator/ThunkInput.txt | 1 + .../tools/aot/jitinterface/jitinterface.h | 11 + src/coreclr/vm/jitinterface.cpp | 38 ++ src/coreclr/zap/zapinfo.cpp | 7 + src/tests/issues.targets | 3 + 20 files changed, 377 insertions(+), 193 deletions(-) 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 1aac4350ba3cb6..1fd42c2b71b366 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -3188,6 +3188,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/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 56162c30c41bb5..4d0796784d1ead 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -86,7 +86,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() } GenTreeCall* call = tree->AsCall(); - if (call->gtCallType != CT_HELPER) + if ((call->gtCallType != CT_HELPER) || (call->gtRetClsHnd == nullptr)) { continue; } @@ -108,6 +108,13 @@ PhaseStatus Compiler::fgInsertClsInitChecks() continue; } + int isInitedMask = 0; + size_t isInitAdr = info.compCompHnd->getIsClassInitedFieldAddress(call->gtRetClsHnd, &isInitedMask); + if ((isInitAdr == 0) || (isInitedMask == 0)) + { + return PhaseStatus::MODIFIED_NOTHING; + } + bool hasNotSupportedPreds = false; for (flowList* pred = block->bbPreds; pred != nullptr; pred = pred->flNext) { @@ -154,37 +161,20 @@ PhaseStatus Compiler::fgInsertClsInitChecks() // \--* NE int // +--* AND int // | +--* IND ubyte - // | | \--* CNS_INT long moduleIdArg + clsIdArg + dataBlobOffset + // | | \--* CNS_INT long isInitAdr // | \--* CNS_INT int isInitMask // \--* CNS_INT int 0 // // // BB2 "callInitBb": (preds: BB1) // - // * CALL help long HELPER.CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE - // +--* CNS_INT long moduleIdArg - // \--* CNS_INT int clsIdArg + // * CALL long CORINFO_HELP_INITCLASS + // +--* CNS_INT long cl // // BB3 "block" (preds: BB1, BB2) // ... // - // TODO: ask VM for these constants: -#ifdef TARGET_64BIT - const int dataBlobOffset = 48; // DomainLocalModule::GetOffsetOfDataBlob() -#else - const int dataBlobOffset = 24; // DomainLocalModule::GetOffsetOfDataBlob() -#endif - const int isInitMask = 1; // ClassInitFlags::INITIALIZED_FLAG; - - // See VM's IsClassInitialized - size_t isInitAdr = moduleIdArg->AsIntCon()->IconValue() + dataBlobOffset + - clsIdArg->AsIntCon()->IconValue(); - - // TODO-CQ: Actually, we can check the class initialization status right here using that ^ - // address, what if it's already initialized while we're compiling code - // in that case we can just drop the call. - GenTree* isInitAdrNode = gtNewIndOfIconHandleNode(TYP_UBYTE, isInitAdr, GTF_ICON_CONST_PTR, true); @@ -193,10 +183,19 @@ PhaseStatus Compiler::fgInsertClsInitChecks() // it's executed only once so can be marked as cold callInitBb->bbSetRunRarely(); callInitBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_CALL | BBF_HAS_LABEL); - GenTree* clonedHelperCall = gtCloneExprCallHelper(call); - clonedHelperCall->gtFlags |= call->gtFlags; - Statement* callStmt = fgNewStmtFromTree(clonedHelperCall); + // Replace CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE 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); @@ -207,11 +206,11 @@ PhaseStatus Compiler::fgInsertClsInitChecks() isInitedBb->inheritWeight(block); isInitedBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_LABEL | BBF_HAS_JMP); - GenTree* isInitedMask = + GenTree* isInitedMaskNode = gtNewOperNode(GT_AND, TYP_INT, gtNewCastNode(TYP_INT, isInitAdrNode, true, TYP_INT), - gtNewIconNode(isInitMask)); + gtNewIconNode(isInitedMask)); - GenTree* isInitedCmp = gtNewOperNode(GT_NE, TYP_INT, isInitedMask, gtNewIconNode(0)); + GenTree* isInitedCmp = gtNewOperNode(GT_NE, TYP_INT, isInitedMaskNode, gtNewIconNode(0)); isInitedCmp->gtFlags |= (GTF_RELOP_JMP_USED | GTF_DONT_CSE); gtSetEvalOrder(isInitedCmp); @@ -1100,6 +1099,7 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo FALLTHROUGH; case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE: + case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE: case CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS: type = TYP_I_IMPL; @@ -1164,6 +1164,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/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/issues.targets b/src/tests/issues.targets index 3c65be6590f5ae..b6592716477b79 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1029,6 +1029,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 From 5e6124f12f6c4788f3ada00470f3399fe72b9b6d Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 5 Mar 2021 14:30:42 +0300 Subject: [PATCH 09/20] JIT-TP optimization --- src/coreclr/jit/compiler.h | 3 ++- src/coreclr/jit/fgbasic.cpp | 1 + src/coreclr/jit/flowgraph.cpp | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f253e3fbf42eb1..ef63d9961ab608 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. 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 4d0796784d1ead..45d744ab93a6e4 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -62,7 +62,7 @@ static bool blockNeedsGCPoll(BasicBlock* block) PhaseStatus Compiler::fgInsertClsInitChecks() { - if (!opts.OptimizationEnabled()) + if (!opts.OptimizationEnabled() || opts.IsReadyToRun() || !fgHasOptStaticInit) { return PhaseStatus::MODIFIED_NOTHING; } @@ -1168,6 +1168,7 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo { // Save cls for this helper for fgInsertClsInitChecks phase result->gtRetClsHnd = cls; + fgHasOptStaticInit = true; } // If we're importing the special EqualityComparer.Default or Comparer.Default From af0bc3e5d2a7dc63bcdbbd250656a15218081e31 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 5 Mar 2021 14:57:46 +0300 Subject: [PATCH 10/20] Add comments/logs --- src/coreclr/jit/flowgraph.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 45d744ab93a6e4..a5f93710a2842b 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -62,11 +62,17 @@ static bool blockNeedsGCPoll(BasicBlock* block) PhaseStatus Compiler::fgInsertClsInitChecks() { - if (!opts.OptimizationEnabled() || opts.IsReadyToRun() || !fgHasOptStaticInit) + if (!opts.OptimizationEnabled() || opts.IsReadyToRun()) { 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; @@ -108,12 +114,14 @@ PhaseStatus Compiler::fgInsertClsInitChecks() continue; } - int isInitedMask = 0; - size_t isInitAdr = info.compCompHnd->getIsClassInitedFieldAddress(call->gtRetClsHnd, &isInitedMask); - if ((isInitAdr == 0) || (isInitedMask == 0)) + int isInitMask = 0; + size_t isInitAddr = info.compCompHnd->getIsClassInitedFieldAddress(call->gtRetClsHnd, &isInitMask); + if ((isInitAddr == 0) || (isInitMask == 0)) { + JITDUMP("getIsClassInitedFieldAddress: returned empty isInitAddr or isInitedMask."); return PhaseStatus::MODIFIED_NOTHING; } + assert(isInitMask > 0); bool hasNotSupportedPreds = false; for (flowList* pred = block->bbPreds; pred != nullptr; pred = pred->flNext) @@ -144,6 +152,9 @@ PhaseStatus Compiler::fgInsertClsInitChecks() prevBb = fgFirstBB; if (prevBb == block) { + JITDUMP("fgInsertClsInitChecks: fgFirstBB is fgFirstBBScratch with a static initialization inside"); + // It means the first bb is already a scratch one so we're not allowed + // to insert a new block in front of it. continue; } } @@ -175,8 +186,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() // ... // - GenTree* isInitAdrNode = - gtNewIndOfIconHandleNode(TYP_UBYTE, isInitAdr, GTF_ICON_CONST_PTR, true); + GenTree* isInitAdrNode = gtNewIndOfIconHandleNode(TYP_UBYTE, isInitAddr, GTF_ICON_CONST_PTR, true); // Let's start from emitting that BB2 "callInitBb" BasicBlock* callInitBb = fgNewBBbefore(BBJ_NONE, block, true); @@ -188,8 +198,8 @@ PhaseStatus Compiler::fgInsertClsInitChecks() // 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); + 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(); @@ -208,7 +218,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() GenTree* isInitedMaskNode = gtNewOperNode(GT_AND, TYP_INT, gtNewCastNode(TYP_INT, isInitAdrNode, true, TYP_INT), - gtNewIconNode(isInitedMask)); + gtNewIconNode(isInitMask)); GenTree* isInitedCmp = gtNewOperNode(GT_NE, TYP_INT, isInitedMaskNode, gtNewIconNode(0)); isInitedCmp->gtFlags |= (GTF_RELOP_JMP_USED | GTF_DONT_CSE); From 89f198c994d072ef963f718490fc86e02e9cdf30 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 5 Mar 2021 17:03:46 +0300 Subject: [PATCH 11/20] Formatting --- src/coreclr/jit/flowgraph.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index a5f93710a2842b..adeb1e971386e0 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -152,9 +152,9 @@ PhaseStatus Compiler::fgInsertClsInitChecks() prevBb = fgFirstBB; if (prevBb == block) { - JITDUMP("fgInsertClsInitChecks: fgFirstBB is fgFirstBBScratch with a static initialization inside"); - // It means the first bb is already a scratch one so we're not allowed - // to insert a new block in front of it. + JITDUMP("fgInsertClsInitChecks: fgFirstBB is fgFirstBBScratch with a static initialization"); + // It means the first bb is already a scratch one so it's probably not a good idea + // to insert blocks before them. continue; } } From 665863914d92ae63c0bbbc96c47dfe9110219f82 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 5 Mar 2021 17:15:18 +0300 Subject: [PATCH 12/20] Clean up --- src/coreclr/jit/flowgraph.cpp | 3 ++- src/tests/issues.targets | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index adeb1e971386e0..de9fccbc30ce5d 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -100,6 +100,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() CorInfoHelpFunc helpFunc = eeGetHelperNum(call->gtCallMethHnd); if ((helpFunc != CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) || (call->fgArgInfo->ArgCount() != 2)) { + // Only CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE is currently supported. continue; } @@ -194,7 +195,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() callInitBb->bbSetRunRarely(); callInitBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_CALL | BBF_HAS_LABEL); - // Replace CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE with a slower CORINFO_HELP_INITCLASS + // 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 = {}; diff --git a/src/tests/issues.targets b/src/tests/issues.targets index b6592716477b79..15910e336045ad 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1029,7 +1029,7 @@ Mono doesn't have a dynamic pgo or tiered compilation infrastructure - + The order of static initialization is different on Mono From ae412d2da84470fef9a203ea62771c2106da5eb0 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 5 Mar 2021 17:39:53 +0300 Subject: [PATCH 13/20] Clean up --- src/coreclr/jit/flowgraph.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index de9fccbc30ce5d..d578e530366f99 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -153,7 +153,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() prevBb = fgFirstBB; if (prevBb == block) { - JITDUMP("fgInsertClsInitChecks: fgFirstBB is fgFirstBBScratch with a static initialization"); + 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; @@ -1110,7 +1110,6 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo FALLTHROUGH; case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE: - case CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE: case CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS: type = TYP_I_IMPL; From a3857e4ffdc14ba8f0fc806878f605f1404c73c8 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 5 Mar 2021 22:52:30 +0300 Subject: [PATCH 14/20] Use fgSplitBlockAfterStatement --- src/coreclr/jit/flowgraph.cpp | 111 +++++++++++++++++----------------- 1 file changed, 57 insertions(+), 54 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index d578e530366f99..55394d9c902110 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -62,7 +62,7 @@ static bool blockNeedsGCPoll(BasicBlock* block) PhaseStatus Compiler::fgInsertClsInitChecks() { - if (!opts.OptimizationEnabled() || opts.IsReadyToRun()) + if (!opts.OptimizationEnabled()) { return PhaseStatus::MODIFIED_NOTHING; } @@ -80,6 +80,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() { if (!block->isRunRarely()) { + Statement* prevStmt = nullptr; for (Statement* stmt : block->Statements()) { for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext) @@ -187,13 +188,45 @@ PhaseStatus Compiler::fgInsertClsInitChecks() // ... // + // 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 = fgNewBBbefore(BBJ_NONE, block, true); + 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); + 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. @@ -210,75 +243,45 @@ PhaseStatus Compiler::fgInsertClsInitChecks() gtSetStmtInfo(callStmt); fgSetStmtSeq(callStmt); fgInsertStmtAtEnd(callInitBb, callStmt); - gtUpdateStmtSideEffects(callStmt); - // BB1 "isInitedBb" - BasicBlock* isInitedBb = fgNewBBbefore(BBJ_COND, callInitBb, true); - isInitedBb->inheritWeight(block); - isInitedBb->bbFlags |= (BBF_INTERNAL | BBF_HAS_LABEL | BBF_HAS_JMP); - - GenTree* isInitedMaskNode = - gtNewOperNode(GT_AND, TYP_INT, gtNewCastNode(TYP_INT, isInitAdrNode, true, TYP_INT), - 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; - - // Now we can remove the call from the current block - // We're going to replace the call with the "moduleId" node. + // Replace the call with the "moduleId" node. gtReplaceTree(stmt, call, gtClone(moduleIdArg)); - // Now we need to fix all the connections to predecessors and successors: - - // isInitedBb is a predecessor of callInitBb + // Fix all the connections to predecessors and successors: + fgAddRefPred(block, callInitBb); + fgAddRefPred(block, isInitedBb); + fgAddRefPred(isInitedBb, prevBb); fgAddRefPred(callInitBb, isInitedBb); - for (flowList* pred = block->bbPreds; pred != nullptr; pred = pred->flNext) - { - // Redirect all the predecessors from the current block to isInitedBb - BasicBlock* predBlock = pred->getBlock(); - assert((predBlock->bbJumpKind == BBJ_NONE) || (predBlock->bbJumpKind == BBJ_ALWAYS) || - (predBlock->bbJumpKind == BBJ_COND)); + fgRemoveRefPred(block, prevBb); - if (predBlock->bbJumpDest == block) - { - predBlock->bbJumpDest = isInitedBb; - isInitedBb->bbFlags |= BBF_JMP_TARGET; - } - if (predBlock->bbNext == block) - { - predBlock->bbNext = isInitedBb; - } - fgRemoveRefPred(block, predBlock); - fgAddRefPred(isInitedBb, predBlock); + // 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; } - // Both callInitBb and isInitedBb are predecessors of block now - fgAddRefPred(block, callInitBb); - fgAddRefPred(block, isInitedBb); - // Make sure all three basic blocks are in the same EH region: + // 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); - assert(isInitedBb->bbNext == callInitBb); - assert(callInitBb->bbNext == 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; From 147860cf167569679a2cc12c6d9aa9d250034cfa Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 6 Mar 2021 00:18:44 +0300 Subject: [PATCH 15/20] Clean up --- src/coreclr/jit/flowgraph.cpp | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 55394d9c902110..6f02fa91bfe91e 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -125,28 +125,6 @@ PhaseStatus Compiler::fgInsertClsInitChecks() } assert(isInitMask > 0); - bool hasNotSupportedPreds = false; - for (flowList* pred = block->bbPreds; pred != nullptr; pred = pred->flNext) - { - BasicBlock* predBlock = pred->getBlock(); - // Bail out if one of the preds is e.g. BBJ_SWITCH or some EH type of BB - if ((predBlock->bbJumpKind != BBJ_NONE) && (predBlock->bbJumpKind != BBJ_ALWAYS) && - (predBlock->bbJumpKind != BBJ_COND)) - { - hasNotSupportedPreds = true; - break; - } - if (!BasicBlock::sameEHRegion(predBlock, block)) - { - hasNotSupportedPreds = true; - break; - } - } - if (hasNotSupportedPreds) - { - continue; - } - if (prevBb == nullptr) { // We're going to emit a BB in front of fgFirstBB @@ -208,8 +186,9 @@ PhaseStatus Compiler::fgInsertClsInitChecks() 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)); + 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); From 85b27fe6f07402cd5c56c766f8a21fa28c76a493 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sat, 6 Mar 2021 01:25:37 +0300 Subject: [PATCH 16/20] Add CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS --- src/coreclr/jit/flowgraph.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 6f02fa91bfe91e..95a6fecfbdb1b1 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -99,11 +99,12 @@ PhaseStatus Compiler::fgInsertClsInitChecks() } CorInfoHelpFunc helpFunc = eeGetHelperNum(call->gtCallMethHnd); - if ((helpFunc != CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) || (call->fgArgInfo->ArgCount() != 2)) + if ((helpFunc != CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) && + (helpFunc != CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS)) { - // Only CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE is currently supported. continue; } + assert(call->fgArgInfo->ArgCount() == 2); GenTree* moduleIdArg = call->fgArgInfo->GetArgNode(0); GenTree* clsIdArg = call->fgArgInfo->GetArgNode(1); @@ -1156,7 +1157,8 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo GenTreeCall* result = gtNewHelperCallNode(helper, type, argList); result->gtFlags |= callFlags; - if (helper == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) + if ((helper == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) || + (helper == CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS)) { // Save cls for this helper for fgInsertClsInitChecks phase result->gtRetClsHnd = cls; From 468b4e76e23c1fc17112e8af874befc89fabbd96 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sat, 6 Mar 2021 01:53:11 +0300 Subject: [PATCH 17/20] Update flowgraph.cpp --- src/coreclr/jit/flowgraph.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 95a6fecfbdb1b1..20abc11fadb6dc 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -1157,8 +1157,7 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo GenTreeCall* result = gtNewHelperCallNode(helper, type, argList); result->gtFlags |= callFlags; - if ((helper == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) || - (helper == CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS)) + if ((helper == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) || (helper == CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS)) { // Save cls for this helper for fgInsertClsInitChecks phase result->gtRetClsHnd = cls; From e6ea83b48ed2a5a329dcd8f2d8f21135fdff7f7a Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sat, 6 Mar 2021 08:44:14 +0300 Subject: [PATCH 18/20] Update flowgraph.cpp --- src/coreclr/jit/flowgraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 20abc11fadb6dc..f63332148be9db 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -1157,7 +1157,7 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo GenTreeCall* result = gtNewHelperCallNode(helper, type, argList); result->gtFlags |= callFlags; - if ((helper == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) || (helper == CORINFO_HELP_CLASSINIT_SHARED_DYNAMICCLASS)) + if (helper == CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE) { // Save cls for this helper for fgInsertClsInitChecks phase result->gtRetClsHnd = cls; From 667ba3ddeb7c5e07c8cc0a51639260e5130b0828 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 7 Mar 2021 22:23:23 +0300 Subject: [PATCH 19/20] fix failures --- src/coreclr/jit/flowgraph.cpp | 4 +++- .../{CctorOrder.cs => CctorOrderEffects.cs} | 0 .../{CCtorOrder.csproj => CctorOrderEffects.csproj} | 2 +- src/tests/issues.targets | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) rename src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/{CctorOrder.cs => CctorOrderEffects.cs} (100%) rename src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/{CCtorOrder.csproj => CctorOrderEffects.csproj} (78%) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index f63332148be9db..a97b594f80f7a0 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -78,7 +78,9 @@ PhaseStatus Compiler::fgInsertClsInitChecks() BasicBlock* prevBb = nullptr; for (block = fgFirstBB; block; block = block->bbNext) { - if (!block->isRunRarely()) + // 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()) diff --git a/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrder.cs b/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrderEffects.cs similarity index 100% rename from src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrder.cs rename to src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrderEffects.cs diff --git a/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CCtorOrder.csproj b/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrderEffects.csproj similarity index 78% rename from src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CCtorOrder.csproj rename to src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrderEffects.csproj index 54b2c09446d905..811f235d6fcfe5 100644 --- a/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CCtorOrder.csproj +++ b/src/tests/Loader/classloader/TypeInitialization/CctorsWithSideEffects/CctorOrderEffects.csproj @@ -4,6 +4,6 @@ True - + diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 15910e336045ad..3e0dab3f2e7c57 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1029,7 +1029,7 @@ Mono doesn't have a dynamic pgo or tiered compilation infrastructure - + The order of static initialization is different on Mono From 15318fab42ffb98155c6ec70874b705f8713bc7b Mon Sep 17 00:00:00 2001 From: EgorBo Date: Mon, 8 Mar 2021 19:53:31 +0300 Subject: [PATCH 20/20] Move fgHasOptStaticInit to morph --- src/coreclr/jit/flowgraph.cpp | 3 +-- src/coreclr/jit/morph.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index a97b594f80f7a0..d92ea1508a0199 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -124,7 +124,7 @@ PhaseStatus Compiler::fgInsertClsInitChecks() if ((isInitAddr == 0) || (isInitMask == 0)) { JITDUMP("getIsClassInitedFieldAddress: returned empty isInitAddr or isInitedMask."); - return PhaseStatus::MODIFIED_NOTHING; + continue; } assert(isInitMask > 0); @@ -1163,7 +1163,6 @@ GenTreeCall* Compiler::fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfo { // Save cls for this helper for fgInsertClsInitChecks phase result->gtRetClsHnd = cls; - fgHasOptStaticInit = true; } // If we're importing the special EqualityComparer.Default or Comparer.Default 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