Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit f1b03b6

Browse files
committed
Jit: fix issues with optMethodFlags
Closes #6368. optMethodFlags is a member field of the Compiler object. It is used during importation to flag IR that might benefit from earlyProp, and used in earlyProp to skip execution if nothing is flagged. However, there were a number of issues with the implementation. First, the value was never initialized. So in CHK/DBG builds, it would get set to 0xFFFFFFFF by the default allocator fill. In RELEASE builds the value would be randomly set. This randomness could easily lead to nondeterministic codegen in RELEASE. More on this subsequently. Second, the value was not propagated during inlining. So if interesting constructs only entered the root method via inlines, they potentially might not trigger earlyProp. Third, not all interesting constructs were flagged. The JIT ORs flag values into optMethodFlags as it imports constructs that should trigger earlyProp. It also re-uses the same compiler instance in most cases. So, relatively quickly, even in release, all the relevant flags end up set and earlyProp will always be run. Hence the window for observing the nondeterministic is was limited to the first few methods jitted by each compiler instance. So to sum up: in DBG/CHK, early prop always runs, regardless of need. In RELEASE, it runs unpredictably for the first few methods, then always runs, regardless of need. To see the nondeterminism in RELEASE, early methods would have to be free of interesting constructs but pull them in via inlining (or contain interesting constructs not currently flagged as such during importation). This set of circumstances is evidently rare enough that the nondeterminism has not been noted. Also, it is quite unlikely to lead to bad codegen, just missed opportunities. We might have caught this if we had reliable RELEASE/CHK diffing ready; see for instance #5063. The fix is to initialize optMethodFlags to zero in compInit, and merge in the inlinee's copy during fgInsertInlineeBlocks. Logging tracks when the inlinee's copy causes a flag update. This was tricky to test for diffs because in CHK all that should happen is that we run earlyProp less often. So any diff in CHK is a missed case of flagging during importation. I found once such case via SPMI where the array index is a constant, and added constant indices to the flagging set. Because I also set the block flag, this both caused earlyProp to run and also look at that block; the latter has triggered a fair number of diffs, almost all of them beneficial (0.42% improvement in SPMI), winners outnumber losers by about 50:1. A typical example of a diff is that the null check below is removed. ```asm mov rcx, qword ptr [(reloc)] mov edx, 4 call CORINFO_HELP_NEWARR_1_VC ** mov edx, dword ptr [rax+8] mov byte ptr [rax+16], 255 mov byte ptr [rax+17], 254 ``` With this change in place, earlyProp how runs selectively based on need in all configurations. We're still running earlyProp in all the cases where it can make a difference, and finding more improvements when it does run, but we're not running it in cases where it can't.
1 parent 32a020e commit f1b03b6

File tree

4 files changed

+28
-10
lines changed

4 files changed

+28
-10
lines changed

src/jit/compiler.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,6 +1801,9 @@ void Compiler::compInit(ArenaAllocator * pAlloc, InlineInfo * inl
18011801
//Used by fgFindJumpTargets for inlining heuristics.
18021802
opts.instrCount = 0;
18031803

1804+
// Used to track when we should consider running EarlyProp
1805+
optMethodFlags = 0;
1806+
18041807
for (unsigned i = 0; i < MAX_LOOP_NUM; i++)
18051808
{
18061809
AllVarSetOps::AssignNoCopy(this, optLoopTable[i].lpAsgVars, AllVarSetOps::UninitVal());

src/jit/compiler.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5540,7 +5540,7 @@ protected :
55405540
};
55415541

55425542
#define OMF_HAS_NEWARRAY 0x00000001 // Method contains 'new' of an array
5543-
#define OMF_HAS_NEWOBJ 0x00800002 // Method contains 'new' of an object type.
5543+
#define OMF_HAS_NEWOBJ 0x00000002 // Method contains 'new' of an object type.
55445544
#define OMF_HAS_ARRAYREF 0x00000004 // Method contains array element loads or stores.
55455545
#define OMF_HAS_VTABLEREF 0x00000008 // Method contains method table reference.
55465546

@@ -5557,8 +5557,6 @@ protected :
55575557
OPK_OBJ_GETTYPE
55585558
};
55595559

5560-
bool impHasArrayRef;
5561-
55625560
bool gtIsVtableRef(GenTreePtr tree);
55635561
GenTreePtr getArrayLengthFromAllocation(GenTreePtr tree);
55645562
GenTreePtr getObjectHandleNodeFromAllocation(GenTreePtr tree);
@@ -5569,7 +5567,6 @@ protected :
55695567
bool optDoEarlyPropForFunc();
55705568
void optEarlyProp();
55715569

5572-
55735570
#if ASSERTION_PROP
55745571
/**************************************************************************
55755572
* Value/Assertion propagation

src/jit/flowgraph.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22232,6 +22232,22 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo)
2223222232
compNeedsGSSecurityCookie |= InlineeCompiler->compNeedsGSSecurityCookie;
2223322233
compGSReorderStackLayout |= InlineeCompiler->compGSReorderStackLayout;
2223422234

22235+
// Update optMethodFlags
22236+
22237+
#ifdef DEBUG
22238+
unsigned optMethodFlagsBefore = optMethodFlags;
22239+
#endif
22240+
22241+
optMethodFlags |= InlineeCompiler->optMethodFlags;
22242+
22243+
#ifdef DEBUG
22244+
if (optMethodFlags != optMethodFlagsBefore)
22245+
{
22246+
JITDUMP("INLINER: Updating optMethodFlags -- root:%0x callee:%0x new:%0x\n",
22247+
optMethodFlagsBefore, InlineeCompiler->optMethodFlags, optMethodFlags);
22248+
}
22249+
#endif
22250+
2223522251
// If there is non-NULL return, replace the GT_CALL with its return value expression,
2223622252
// so later it will be picked up by the GT_RET_EXPR node.
2223722253
if ((pInlineInfo->inlineCandidateInfo->fncRetType != TYP_VOID) || (iciCall->gtCall.gtReturnType == TYP_STRUCT))

src/jit/importer.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10053,10 +10053,11 @@ void Compiler::impImportBlockCode(BasicBlock * block)
1005310053

1005410054
/* Mark the block as containing an index expression */
1005510055

10056-
if (op1->gtOper == GT_LCL_VAR)
10056+
if (op1->gtOper == GT_LCL_VAR)
1005710057
{
10058-
if (op2->gtOper == GT_LCL_VAR ||
10059-
op2->gtOper == GT_ADD)
10058+
if (op2->gtOper == GT_LCL_VAR ||
10059+
op2->gtOper == GT_CNS_INT ||
10060+
op2->gtOper == GT_ADD)
1006010061
{
1006110062
block->bbFlags |= BBF_HAS_INDX;
1006210063
optMethodFlags |= OMF_HAS_ARRAYREF;
@@ -10281,10 +10282,11 @@ void Compiler::impImportBlockCode(BasicBlock * block)
1028110282

1028210283
// Mark the block as containing an index expression
1028310284

10284-
if (op3->gtOper == GT_LCL_VAR)
10285+
if (op3->gtOper == GT_LCL_VAR)
1028510286
{
10286-
if (op1->gtOper == GT_LCL_VAR ||
10287-
op1->gtOper == GT_ADD)
10287+
if (op1->gtOper == GT_LCL_VAR ||
10288+
op1->gtOper == GT_CNS_INT ||
10289+
op1->gtOper == GT_ADD)
1028810290
{
1028910291
block->bbFlags |= BBF_HAS_INDX;
1029010292
optMethodFlags |= OMF_HAS_ARRAYREF;

0 commit comments

Comments
 (0)