-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Replace a load with cheaper mov instruction when possible #83458
Changes from all commits
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 |
---|---|---|
|
@@ -16183,7 +16183,7 @@ bool emitter::IsRedundantLdStr( | |
// ins - The instruction code | ||
// reg1Attr - The emit attribute for register 1 | ||
// reg1 - Register 1 | ||
// reg2 - Encoded register 2 | ||
// reg2 - Register 2 | ||
// imm - Immediate offset, prior to scaling by operand size | ||
// size - Operand size | ||
// fmt - Instruction format | ||
|
@@ -16194,9 +16194,6 @@ bool emitter::IsRedundantLdStr( | |
bool emitter::ReplaceLdrStrWithPairInstr( | ||
instruction ins, emitAttr reg1Attr, regNumber reg1, regNumber reg2, ssize_t imm, emitAttr size, insFormat fmt) | ||
{ | ||
// Register 2 needs conversion to unencoded value. | ||
reg2 = encodingZRtoSP(reg2); | ||
|
||
RegisterOrder optimizationOrder = IsOptimizableLdrStrWithPair(ins, reg1, reg2, imm, size, fmt); | ||
|
||
if (optimizationOrder != eRO_none) | ||
|
@@ -16367,4 +16364,83 @@ emitter::RegisterOrder emitter::IsOptimizableLdrStrWithPair( | |
return optimisationOrder; | ||
} | ||
|
||
//----------------------------------------------------------------------------------- | ||
// IsOptimizableLdrToMov: Check if it is possible to optimize a second "ldr" | ||
// instruction into a cheaper "mov" instruction. | ||
// | ||
// Examples: ldr w1, [x20, #0x10] | ||
// ldr w2, [x20, #0x10] => mov w1, w2 | ||
// | ||
// Arguments: | ||
// ins - The instruction code | ||
// reg1 - Register 1 number | ||
// reg2 - Register 2 number | ||
// imm - Immediate offset, prior to scaling by operand size | ||
// size - Operand size | ||
// fmt - Instruction format | ||
// | ||
// Return Value: | ||
// true - Optimization of the second instruction is possible | ||
// | ||
bool emitter::IsOptimizableLdrToMov( | ||
instruction ins, regNumber reg1, regNumber reg2, ssize_t imm, emitAttr size, insFormat fmt) | ||
{ | ||
if (ins != INS_ldr) | ||
{ | ||
// This instruction is not an "ldr" instruction. | ||
return false; | ||
} | ||
|
||
if (ins != emitLastIns->idIns()) | ||
{ | ||
// Not successive "ldr" instructions. | ||
return false; | ||
} | ||
|
||
regNumber prevReg1 = emitLastIns->idReg1(); | ||
regNumber prevReg2 = emitLastIns->idReg2(); | ||
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.
(Did you see any diffs with a base register of 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. The check is pulled up in the 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. The check you refer to, 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. @BruceForstall - Does that mean that we would have lost some opportunities because of not encoding it to SP? 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. Actually, now I am confused - why we have to do the 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.
That's my thinking. However, when I add
The problem is that we store the encoded register value in the instrDesc -- and arm64 encodings sometimes encode SP as ZR in the instruction encoding bits. It's possibly unfortunate that we (long ago) decided to store the encoded form into igReg1()/idReg2()/etc. An alternative would be to store the "actual" register in the instrDesc and do the SP->ZR encoding when we're building the instruction bits. This would eliminate the need to "un-encode" which we do in the 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.
Unfortunately, REG_SP is 64 and REGNUM_BITS is 6, so we don't have enough bits currently to store REG_SP in idReg1/idReg2/etc. Bumping REGNUM_BITS to 7 just to allow this might have negative effects on memory usage or other instrDesc effects. 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. Thanks for the explanation @BruceForstall, I'll add a new PR addressing the comments 👍 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. Created #83886 |
||
insFormat lastInsFmt = emitLastIns->idInsFmt(); | ||
emitAttr prevSize = emitLastIns->idOpSize(); | ||
ssize_t prevImm = emitGetInsSC(emitLastIns); | ||
|
||
if ((reg2 != prevReg2) || !isGeneralRegisterOrSP(reg2)) | ||
{ | ||
// The "register 2" should be same as previous instruction and | ||
// should either be a general register or stack pointer. | ||
return false; | ||
} | ||
|
||
if (prevImm != imm) | ||
{ | ||
// Then we are loading from a different immediate offset. | ||
return false; | ||
} | ||
|
||
if (!isGeneralRegister(reg1) || !isGeneralRegister(prevReg1)) | ||
{ | ||
// Either register 1 or previous register 1 is not a general register | ||
// or the zero register, so we cannot optimise. | ||
Comment on lines
+16421
to
+16422
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. This comment is wrong (or confusing) since 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. Sure, I'll change this. Should I coalesce these changes with another PR? 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. If you have an additional PR coming, it's fine to add the comment changes to that PR. |
||
return false; | ||
} | ||
|
||
if (lastInsFmt != fmt) | ||
{ | ||
// The formats of the two instructions differ. | ||
return false; | ||
} | ||
|
||
if (prevReg1 == prevReg2) | ||
{ | ||
// Then the previous load overwrote the register that we are indexing against. | ||
return false; | ||
} | ||
|
||
if (prevSize != size) | ||
{ | ||
// Operand sizes differ. | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
#endif // defined(TARGET_ARM64) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -130,6 +130,7 @@ RegisterOrder IsOptimizableLdrStrWithPair( | |
instruction ins, regNumber reg1, regNumber reg2, ssize_t imm, emitAttr size, insFormat fmt); | ||
bool ReplaceLdrStrWithPairInstr( | ||
instruction ins, emitAttr reg1Attr, regNumber reg1, regNumber reg2, ssize_t imm, emitAttr size, insFormat fmt); | ||
bool IsOptimizableLdrToMov(instruction ins, regNumber reg1, regNumber reg2, ssize_t imm, emitAttr size, insFormat fmt); | ||
|
||
// Try to optimize a Ldr or Str with an alternative instruction. | ||
inline bool OptimizeLdrStr(instruction ins, | ||
|
@@ -156,6 +157,9 @@ inline bool OptimizeLdrStr(instruction ins, | |
return true; | ||
} | ||
|
||
// Register 2 needs conversion to unencoded value for following optimisation checks. | ||
reg2 = encodingZRtoSP(reg2); | ||
kunalspathak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// If the previous instruction was a matching load/store, then try to replace it instead of emitting. | ||
// Don't do this if either instruction had a local variable. | ||
if ((emitLastIns->idIns() == ins) && !localVar && !emitLastIns->idIsLclVar() && | ||
|
@@ -164,6 +168,13 @@ inline bool OptimizeLdrStr(instruction ins, | |
return true; | ||
} | ||
|
||
// If we have a second LDR instruction from the same source, then try to replace it with a MOV. | ||
if (IsOptimizableLdrToMov(ins, reg1, reg2, imm, size, fmt)) | ||
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'm wondering if this creates a GC hole? Should this be gated by a 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. 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. Also the gcstress pipelines are clean. |
||
{ | ||
emitIns_Mov(INS_mov, reg1Attr, reg1, emitLastIns->idReg1(), true); | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
|
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.
Shouldn't this be
mov w2, w1
?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.
True, I'll fix this.