-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Refactor codegen and emit to centralize mov reg, reg
handling
#52661
Refactor codegen and emit to centralize mov reg, reg
handling
#52661
Conversation
This might also make it easier to do optimizations for certain scenarios and to add more interesting kinds of move elimination (such as we have basic support for on Arm64 via |
838b22e
to
02606db
Compare
@@ -203,15 +203,15 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, | |||
|
|||
if (GetEmitter()->isLowRegister(reg) && (imm_hi16 == 0xffff) && ((imm_lo16 & 0x8000) == 0x8000)) | |||
{ | |||
GetEmitter()->emitIns_R_R(INS_sxth, EA_4BYTE, reg, reg); | |||
GetEmitter()->emitIns_Mov(INS_sxth, EA_4BYTE, reg, reg, /* canSkip */ 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.
Most of these emitIns_Mov
calls should "probably" be inst_Mov
or inst_Move_Extend
calls instead, where the instruction is picked based on the register and type.
I tried, for the most part, to minimize the diff from this change however, and so I kept existing calls directly to emitIns_*
where possible.
@@ -550,7 +550,7 @@ void CodeGen::genLclHeap(GenTree* tree) | |||
inst_JMP(EJ_lo, done); | |||
|
|||
// Update SP to be at the next page of stack that we will tickle | |||
GetEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_SPBASE, regTmp); | |||
GetEmitter()->emitIns_Mov(INS_mov, EA_PTRSIZE, REG_SPBASE, regTmp, /* canSkip */ 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.
Many of these are currently /* canSkip */ false
because that's how the existing code handled it.
However, almost all of them except for genIntToIntCast
are technically elidable (assuming the registers are the same) as they don't do have any special semantics.
254653b
to
82ac186
Compare
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 I've said elsewhere I don't think this really addresses the heart of the issue.
However, it makes the jit's behavior more consistent, and the centralizing of the move elision logic is a nice improvement. It will be interesting to see if removing some of the unneded canSkips
leads to interesting diffs.
Can you verify that when the problematic assert is restored, it no longer fires..?
82ac186
to
4c1b097
Compare
@AndyAyersMS, validated that there are no diffs with the first commit and the second commit allows the assert to no longer fire.
I'm certain we can also do this higher in the JIT (possibly by retyping things), but the "root" issue today is that we have a pointer to byref assignment and the assignment is elided so the emitter never marks the register as becoming a live
Right, I think the first commit is worth taking even if we decide the second is not. This at least allows us to centrally manage the logic and allows easier introduction of other kinds of move elision. ARM64 for example has IsRedundantMov which avoids the Likewise, it looks like almost all cases where |
ARM64 isn't quite zero diffs. It looks like there is some scenario where Edit: This is an existing elided sign extension on ARM64: https://github.com/dotnet/runtime/pull/52661/files#diff-abc75a0357f9afd3dbad2d0f5ed03e13aa04c4e6c6c69118014558f3c7f61f61R2356-R2357 |
31e3298
to
519a7d0
Compare
mov reg, reg
handlingmov reg, reg
handling
@AndyAyersMS, to simplify things (and since there is still some issue with the second commit I'm working on), I've limited this to just centralizing the move elision support. I got PMI diffs for |
Thanks. I appreciate all the work you're putting into this. I'll approve, but I'd like to get one other approver from @dotnet/jit-contrib |
I will review it later today too. |
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. Thanks for the cleanup!
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.
Some comments. Overall, good approach and thanks for doing this.
assert(size <= EA_32BYTE); | ||
noway_assert(emitVerifyEncodable(ins, size, dstReg, srcReg)); | ||
|
||
if (canSkip && (dstReg == srcReg)) |
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.
what are some examples where canSkip == false
but dstReg == srcReg
?
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.
This mostly comes down to inst_Mov_Extend
where extensions should not be elided.
The point of this initial PR is to be a refactoring with zero-diffs. We need to audit/cleanup the callers in a secondary PR so that all copy moves
can be elided when the registers are the same and all extension moves
are not elided (outside the logic that checks if the value was already extended).
// Ensure we aren't overwriting op2 | ||
assert(op2Reg != targetReg); | ||
// Ensure we aren't overwriting op2 | ||
assert((op2Reg != targetReg) || (op1Reg == targetReg)); |
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 the assert be just assert(op2 != targetReg)
? It doesn't matter if op1Reg == targetReg
or not, right?
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.
This assert is validating that the move won't overwrite op2
.
This requires that either op2Reg != targetReg
and so therefore mov tgtReg, op1Reg
is always safe or op1Reg == tgtReg
in which case mov tgtReg, op1Reg
is a "nop" and it doesn't matter if op2Reg
is tgtReg
.
Hello @tannergooding! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
/azp run runtime-coreclr jitstress, runtime-coreclr outerloop |
Azure Pipelines successfully started running 2 pipeline(s). |
Only failure is |
This centralizes the handling of moves so that move elision can be centrally managed.
This results in zero diffs for the x64, arm32, and arm64 frameworks, benchmarks, and tests PMI disassembly.