-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Conversation
9ec809b
to
7864a81
Compare
@dotnet-bot |
@BruceForstall @CarolEidt @dotnet/jit-contrib PTAL Essentially, this performs dead-store removal on SDSU temps as well as LclVars. Throughput results are good: when compared against the baseline, these changes improve throughput by .1% when using mean times for comparison and regress throughput by the same when using median times for comparison. This lines up pretty well with a reported 0.1% throughput improvement reported by callgrind on There are a couple small changes to the IR that are necessary in order to enable this:
I haven't been able to get |
@dotnet-bot test |
@dotnet-bot test Windows_NT x64 Debug Build and Test |
Internal diffs are good. The most common snippets we end up removing are
Rarer are instances of dead helper calls, e.g. type tests:
and allocations:
Overall change as reported by SPMI:
|
Can you note where you saw the dead isinst/newobj calls? These may be cases we could be cleaning up earlier. |
Presumably in the case of the unused compare we keep the indir and remove everything else? |
We only keep the indir if it might fault. In a number of cases we are able to remove the entire tree, including the indir.
@russellhadley thought the same. I will take a look. |
My guess is that we're better at removing dead code because we're always precompiling during |
@mikedn here's an example where we remove a compare but keep the load around (from
becomes
|
@dotnet-bot test Windows_NT arm64 Cross Debug Build |
src/jit/gentree.h
Outdated
@@ -945,9 +946,8 @@ struct GenTree | |||
#define GTF_INX_REFARR_LAYOUT 0x20000000 // GT_INDEX -- same as GTF_IND_REFARR_LAYOUT | |||
#define GTF_INX_STRING_LAYOUT 0x40000000 // GT_INDEX -- this uses the special string array layout | |||
|
|||
#define GTF_IND_NONFAULTING GTF_SET_FLAGS // GT_IND -- An indir that cannot fault. GTF_SET_FLAGS is not used on indirs. | |||
#define GTF_IND_NONFAULTING 0x20000000 // GT_IND -- An indir that cannot fault. | |||
#define GTF_IND_VOLATILE 0x40000000 // GT_IND -- the load or store must use volatile sematics (this is a nop on X86) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the bits in the per-class sets are generally listed from high value to low
src/jit/gentree.cpp
Outdated
@@ -885,6 +885,42 @@ bool GenTreeCall::IsPure(Compiler* compiler) const | |||
compiler->s_helperCallProperties.IsPure(compiler->eeGetHelperNum(gtCallMethHnd)); | |||
} | |||
|
|||
bool GenTreeCall::IsSideEffectFree(Compiler* compiler, bool ignoreExceptions, bool ignoreCctors) const | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Please add normal function header comment
- Mention and call out relationship to gtNodeHasSideEffects() -- can that one use this one?
- Should this instead be named HasSideEffects() to avoid the pseudo-double-negative?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this instead be named HasSideEffects() to avoid the pseudo-double-negative?
I would be in favor of that
src/jit/gentree.h
Outdated
@@ -945,9 +946,8 @@ struct GenTree | |||
#define GTF_INX_REFARR_LAYOUT 0x20000000 // GT_INDEX -- same as GTF_IND_REFARR_LAYOUT | |||
#define GTF_INX_STRING_LAYOUT 0x40000000 // GT_INDEX -- this uses the special string array layout | |||
|
|||
#define GTF_IND_NONFAULTING GTF_SET_FLAGS // GT_IND -- An indir that cannot fault. GTF_SET_FLAGS is not used on indirs. | |||
#define GTF_IND_NONFAULTING 0x20000000 // GT_IND -- An indir that cannot fault. | |||
#define GTF_IND_VOLATILE 0x40000000 // GT_IND -- the load or store must use volatile sematics (this is a nop on X86) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's disconcerting that (at least) GTF_IND_VOLATILE/GTF_IND_UNALIGNED
also apply to GT_INDEX, but are not commented as such.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is that possible? In the case of GTF_IND_VOLATILE
, there would be an ambiguity with GTF_INX_STRING_LAYOUT
were that the case. As far as I can tell these flags only apply to GT_IND
.
src/jit/liveness.cpp
Outdated
// otherwise. | ||
// | ||
bool Compiler::fgComputeLifeLocal(VARSET_TP& life, VARSET_VALARG_TP keepAliveVars, GenTree* lclVarNode, GenTree* node) | ||
void Compiler::fgComputeLifeTrackedLocalUse(VARSET_TP& life, LclVarDsc& varDsc, GenTreeLclVarCommon* node) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As usual, please add function headers here (and below)
src/jit/liveness.cpp
Outdated
//------------------------------------------------------------------------ | ||
// Compiler::fgComputeLifeLocal: compute the changes to local var liveness | ||
// due to a use or a def of a local var and | ||
// indicates wither the use/def is a dead |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wither => whether
src/jit/liveness.cpp
Outdated
// | ||
// Arguments: | ||
// life - The live set that is being computed. | ||
// keepAliveVars - The currents set of variables to keep alive |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
currents => current
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo nits
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM aside from some suggestions
src/jit/gentree.cpp
Outdated
@@ -885,6 +885,42 @@ bool GenTreeCall::IsPure(Compiler* compiler) const | |||
compiler->s_helperCallProperties.IsPure(compiler->eeGetHelperNum(gtCallMethHnd)); | |||
} | |||
|
|||
bool GenTreeCall::IsSideEffectFree(Compiler* compiler, bool ignoreExceptions, bool ignoreCctors) const | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this instead be named HasSideEffects() to avoid the pseudo-double-negative?
I would be in favor of that
@@ -945,9 +946,8 @@ struct GenTree | |||
#define GTF_INX_REFARR_LAYOUT 0x20000000 // GT_INDEX -- same as GTF_IND_REFARR_LAYOUT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you've eliminated GTF_IND_REFARR_LAYOUT
you should change the above comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I think that there should be a comment here describing the commonality between the GTF_INX
flags and the GTF_IND
flags, and explaining why some can overlap.
src/jit/liveness.cpp
Outdated
{ | ||
// Is the variable already known to be alive? | ||
if (VarSetOps::IsMember(this, life, varIndex)) | ||
/* Mark variable as dead from here to its closest use */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make this a C++ style comment, while you're here.
Also, I would rephrase this as simply "Remove variable from the live set." Talking about its closest use almost makes one think we're talking about its downstream use.
return; | ||
} | ||
|
||
VARSET_TP varBit(VarSetOps::MakeEmpty(this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I know this is pre-existing code, but varBit
seems to be a strange name for a set that is likely to have more than one member. I would probably rename this fieldVars
or something, but IMO it should at least be plural.
src/jit/liveness.cpp
Outdated
|
||
// Removing a call does not affect liveness unless it is a tail call in a nethod with P/Invokes or | ||
// is | ||
// itself a P/Invoke, in which case it may affect the liveness of the frame root variable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird comment formatting
There are three cases that are relevant here: - Defs of tracked locals - Uses of tracked locals - Appearances of untracked locals Each of these cases has been moved into its own function.
c1f1b97
to
44dbecb
Compare
src/jit/gentree.cpp
Outdated
@@ -9511,7 +9560,7 @@ void Compiler::gtDispNode(GenTreePtr tree, IndentStack* indentStack, __in __in_z | |||
|
|||
case GT_IND: | |||
// We prefer printing R, V or U | |||
if ((tree->gtFlags & (GTF_IND_REFARR_LAYOUT | GTF_IND_VOLATILE | GTF_IND_UNALIGNED)) == 0) | |||
if ((tree->gtFlags & (GTF_IND_VOLATILE | GTF_IND_UNALIGNED)) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment above should be updated:
// We prefer printing V or U
if (tree->gtCall.gtCallType == CT_HELPER) | ||
GenTreeCall* const call = tree->AsCall(); | ||
const bool ignoreExceptions = (flags & GTF_EXCEPT) == 0; | ||
const bool ignoreCctors = (flags & GTF_IS_IN_CSE) != 0; // We can CSE helpers that run cctors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that we also might be able to CSE expressions that throw exceptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is the case, yes. CSE does not pass the GTF_EXCEPT
flag to this function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use a function header for this method describing that the caller passes in flags
that control the behavior of this method. It seems a bit odd to me to use the flags
argument on this way.
Alternatively we could pass in two values : 1) ignoreExceptions and 2 ignoreCctors
In particular: - Rather than only removing dead code as part of dead store removal, remove all side-effect-free nodes that either do not produce a value or produce a value that is unused. - When optimizing, set `fgStmtRemoved` in order to indicate that tracked lclVar uses or defs have been removed and liveness may need to be recalculated. Previously this flag was only set upon eliminating a dead store.
if (tree->gtCall.gtCallType == CT_HELPER) | ||
GenTreeCall* const call = tree->AsCall(); | ||
const bool ignoreExceptions = (flags & GTF_EXCEPT) == 0; | ||
const bool ignoreCctors = (flags & GTF_IS_IN_CSE) != 0; // We can CSE helpers that run cctors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use a function header for this method describing that the caller passes in flags
that control the behavior of this method. It seems a bit odd to me to use the flags
argument on this way.
Alternatively we could pass in two values : 1) ignoreExceptions and 2 ignoreCctors
@dotnet-bot test Windows_NT x86 Checked Build and Test |
@mmitche any idea what's up with the RHEL packages not being found? |
@dotnet-bot test Windows_NT x86 Checked Build and Test |
@dotnet-bot |
In particular:
remove all side-effect-free nodes that either do not produce a value
or produce a value that is unused.
fgStmtRemoved
in order to indicate that trackedlclVar uses or defs have been removed and liveness may need to be
recalculated. Previously this flag was only set upon eliminating a
dead store.