-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Eliminate redundant test instruction #53214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
06dda15
b6f80ea
8867374
01fb6ba
81eb9eb
53c4325
7ded469
e7e04b7
b1e0fc3
068fb15
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -144,6 +144,98 @@ bool emitter::IsDstSrcSrcAVXInstruction(instruction ins) | |
| return ((CodeGenInterface::instInfo[ins] & INS_Flags_IsDstSrcSrcAVXInstruction) != 0) && IsAVXInstruction(ins); | ||
| } | ||
|
|
||
| //------------------------------------------------------------------------ | ||
| // DoesWriteZeroFlag: check if the instruction write the | ||
| // ZF flag. | ||
| // | ||
| // Arguments: | ||
| // ins - instruction to test | ||
| // | ||
| // Return Value: | ||
| // true if instruction writes the ZF flag, false otherwise. | ||
| // | ||
| bool emitter::DoesWriteZeroFlag(instruction ins) | ||
| { | ||
| return (CodeGenInterface::instInfo[ins] & INS_FLAGS_WritesZF) != 0; | ||
| } | ||
|
|
||
| //------------------------------------------------------------------------ | ||
| // DoesResetOverflowAndCarryFlags: check if the instruction resets the | ||
| // OF and CF flag to 0. | ||
| // | ||
| // Arguments: | ||
| // ins - instruction to test | ||
| // | ||
| // Return Value: | ||
| // true if instruction resets the OF and CF flag, false otherwise. | ||
kunalspathak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // | ||
| bool emitter::DoesResetOverflowAndCarryFlags(instruction ins) | ||
| { | ||
| return (CodeGenInterface::instInfo[ins] & INS_FLAGS_Resets_CF_OF_Flags) != 0; | ||
| } | ||
|
|
||
| //------------------------------------------------------------------------ | ||
| // IsFlagsAlwaysModified: check if the instruction guarantee to modify any flags. | ||
| // | ||
| // Arguments: | ||
| // id - instruction to test | ||
| // | ||
| // Return Value: | ||
| // false, if instruction is guaranteed to not modify any flag. | ||
| // true, if instruction will modify some flag. | ||
| // | ||
| bool emitter::IsFlagsAlwaysModified(instrDesc* id) | ||
| { | ||
| instruction ins = id->idIns(); | ||
| insFormat fmt = id->idInsFmt(); | ||
|
|
||
| if (fmt == IF_RRW_SHF) | ||
| { | ||
| if (id->idIsLargeCns()) | ||
| { | ||
| return true; | ||
| } | ||
| else if (id->idSmallCns() == 0) | ||
| { | ||
| switch (ins) | ||
| { | ||
| // If shift-amount for below instructions is 0, then flags are unaffected. | ||
| case INS_rcl_N: | ||
| case INS_rcr_N: | ||
| case INS_rol_N: | ||
| case INS_ror_N: | ||
| case INS_shl_N: | ||
| case INS_shr_N: | ||
| case INS_sar_N: | ||
| return false; | ||
| default: | ||
| return true; | ||
| } | ||
| } | ||
| } | ||
| else if (fmt == IF_RRW) | ||
| { | ||
| switch (ins) | ||
| { | ||
| // If shift-amount for below instructions is 0, then flags are unaffected. | ||
| // So, to be conservative, do not optimize if the instruction has register | ||
| // as the shift-amount operand. | ||
| case INS_rcl: | ||
| case INS_rcr: | ||
| case INS_rol: | ||
| case INS_ror: | ||
| case INS_shl: | ||
| case INS_shr: | ||
| case INS_sar: | ||
| return false; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this might require some careful considerations. For the purposes of this PR, this is fine and is simply used as a "did the previous instruction set the flag and therefore a However, for future improvements we may consider more advanced analysis such as: Then, this will become problematic. The latter happens in Clang/MSVC in various places and are one of the reasons that
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps we can return some ternary state here representing "yes", "no", and "it depends"?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Today, we only track prevInstruction (
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, it's not something I think we need to be concerned with "today", but rather something I could see being problematic in the future for certain optimizations. The method implies boolean state but in practice its slightly more nuanced and one of the answers is "maybe", so it may end up confusing callers.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Exactly - what are callers supposed to do with "maybe". So for now, I will leave it as-is.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that is contextual. In the case of this PR, you care "did the last instruction definitely set the flag I care about" and so you would treat In other cases, optimizations may care "did the last instruction definitely not set the flag I care about" and so they would treat it as
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renamed the method to |
||
| default: | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| //------------------------------------------------------------------------ | ||
| // AreUpper32BitsZero: check if some previously emitted | ||
| // instruction set the upper 32 bits of reg to zero. | ||
|
|
@@ -220,27 +312,29 @@ bool emitter::AreUpper32BitsZero(regNumber reg) | |
| // the same values as if there were a compare to 0 | ||
| // | ||
| // Arguments: | ||
| // reg - register of interest | ||
| // opSize - size of register | ||
| // needsOCFlags - additionally check the overflow and carry flags | ||
| // reg - register of interest | ||
| // opSize - size of register | ||
| // treeOps - type of tree node operation | ||
| // | ||
| // Return Value: | ||
| // true if the previous instruction set the flags for reg | ||
| // false if not, or if we can't safely determine | ||
| // | ||
| // Notes: | ||
| // Currently only looks back one instruction. | ||
| bool emitter::AreFlagsSetToZeroCmp(regNumber reg, emitAttr opSize, bool needsOCFlags) | ||
| bool emitter::AreFlagsSetToZeroCmp(regNumber reg, emitAttr opSize, genTreeOps treeOps) | ||
kunalspathak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| assert(reg != REG_NA); | ||
|
|
||
| // Don't look back across IG boundaries (possible control flow) | ||
| if (emitCurIGinsCnt == 0 && ((emitCurIG->igFlags & IGF_EXTEND) == 0)) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| instrDesc* id = emitLastIns; | ||
| insFormat fmt = id->idInsFmt(); | ||
| instrDesc* id = emitLastIns; | ||
| instruction lastIns = id->idIns(); | ||
| insFormat fmt = id->idInsFmt(); | ||
|
|
||
| // make sure op1 is a reg | ||
| switch (fmt) | ||
|
|
@@ -259,7 +353,6 @@ bool emitter::AreFlagsSetToZeroCmp(regNumber reg, emitAttr opSize, bool needsOCF | |
| case IF_RRD: | ||
| case IF_RRW: | ||
| break; | ||
|
|
||
| default: | ||
| return false; | ||
| } | ||
|
|
@@ -269,34 +362,20 @@ bool emitter::AreFlagsSetToZeroCmp(regNumber reg, emitAttr opSize, bool needsOCF | |
| return false; | ||
| } | ||
|
|
||
| switch (id->idIns()) | ||
| // Certain instruction like and, or and xor modifies exactly same flags | ||
| // as "test" instruction. | ||
| // They reset OF and CF to 0 and modifies SF, ZF and PF. | ||
| if (DoesResetOverflowAndCarryFlags(lastIns)) | ||
| { | ||
| case INS_adc: | ||
| case INS_add: | ||
| case INS_dec: | ||
| case INS_dec_l: | ||
| case INS_inc: | ||
| case INS_inc_l: | ||
| case INS_neg: | ||
| case INS_shr_1: | ||
| case INS_shl_1: | ||
| case INS_sar_1: | ||
| case INS_sbb: | ||
| case INS_sub: | ||
| case INS_xadd: | ||
| if (needsOCFlags) | ||
| { | ||
| return false; | ||
| } | ||
| FALLTHROUGH; | ||
| // these always set OC to 0 | ||
| case INS_and: | ||
| case INS_or: | ||
| case INS_xor: | ||
| return id->idOpSize() == opSize; | ||
| return id->idOpSize() == opSize; | ||
| } | ||
|
|
||
| default: | ||
| break; | ||
| if ((treeOps == GT_EQ) || (treeOps == GT_NE)) | ||
| { | ||
| if (DoesWriteZeroFlag(lastIns) && IsFlagsAlwaysModified(id)) | ||
| { | ||
| return id->idOpSize() == opSize; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.