Refactoring emitInsBinary#15836
Conversation
|
FYI. @CarolEidt, @mikedn, @dotnet/jit-contrib |
|
|
||
| default: // Addressing mode [regBase + regIndex * Scale + const] | ||
| { | ||
| instrDesc* id = nullptr; |
There was a problem hiding this comment.
I want to refactor this code separately (as part of https://github.com/dotnet/coreclr/issues/15828).
There was a problem hiding this comment.
After this is refactored, we could also rename the method to genInsBinary and move it to Codegen, which may be a more appropriate place.
| // src can be anything but both src and dst cannot be addr modes | ||
| // or at least cannot be contained addr modes | ||
| if (dst->isUsedFromMemory()) | ||
| if (dst->isContained() || (dst->isLclField() && (dst->gtRegNum == REG_NA)) || dst->isUsedFromSpillTemp()) |
There was a problem hiding this comment.
I wasn't sure why dst isn't marked as contained for (dst->isLclField() && (dst->gtRegNum == REG_NA)) (see here for the old code), it seems to map to a GT_STORE_LCL_FLD and, being a store, I would have expected it to be containable...
There was a problem hiding this comment.
Hmm, a contained dst is likely rare in this code path. This might worth a separate investigation to avoid surprises.
| if (memOp->isIndir()) | ||
| { | ||
| GenTreeIndir* memIndir = memOp->AsIndir(); | ||
| GenTree* memBase = memIndir->gtOp1; |
There was a problem hiding this comment.
I kept the old name here, but I don't think memBase is appropriate.
There is also memIndir->Base() which returns something slightly different and which is used in emitHandleMemOp
| memBase = mem->gtOp1; | ||
| if (src->IsCnsIntOrI() || src->IsCnsFltOrDbl()) | ||
| { | ||
| cnsOp = src; |
There was a problem hiding this comment.
I originally had a assert(!src->isUsedFromMemory() || src->IsCnsFltOrDbl()); right above this. However, it appears that on x86 Checked builds, we will sometimes spill a GT_CNS_INT to the stack.
See the failure here which reported:
Assert failure(PID 4672 [0x00001240], Thread: 4644 [0x1224]): Assertion failed '!src->isUsedFromMemory() || src->IsCnsFltOrDbl()' in 'System.Diagnostics.Tracing.EventProvider:WriteEvent(byref,int,int,int,ref):bool:this' (IL size 1225)
There was a problem hiding this comment.
I was not seeing the same failure in x86 Debug or Release.
There was a problem hiding this comment.
Looks like x86 Checked is still failing due to the stack spill. Will investigate sometime tomorrow.
There was a problem hiding this comment.
isUsedFromSpillTemp needed to take precedence over everything else (including IsCns and IsIndir).
|
Hmm, I have to take a closer look when I have a bit more spare time. I had something a bit different in mind, it included moving this out of emitter to CodeGen. I wonder if I kept the branch where I experimented with this... |
I mentioned doing that above, but think it should wait until after the address mode refactoring (https://github.com/dotnet/coreclr/issues/15828) happens (so that the emitter specific code can be contained to the emitter) |
CarolEidt
left a comment
There was a problem hiding this comment.
LGTM Would love to get another review from @dotnet/jit-contrib
| // src can be anything but both src and dst cannot be addr modes | ||
| // or at least cannot be contained addr modes | ||
| if (dst->isUsedFromMemory()) | ||
| if (dst->isContained() || (dst->isLclField() && (dst->gtRegNum == REG_NA)) || dst->isUsedFromSpillTemp()) |
| if (dst->isUsedFromMemory()) | ||
| if (dst->isContained() || (dst->isLclField() && (dst->gtRegNum == REG_NA)) || dst->isUsedFromSpillTemp()) | ||
| { | ||
| // dst can only be a reg or modrm |
There was a problem hiding this comment.
At this point it can only be memory, given the condition above.
|
ping @dotnet/jit-contrib - This is broadly used for xarch codegen, so I think it would be good to get a second review on it. Any takers? |
|
@CarolEidt, which additional jobs should be run to validate this? I imagine we want to run all or most pri-1 tests, before merging, to get the additional validation (given the broad use). |
| // We can only have one memory operand and only src can be a constant operand | ||
| GenTree* memOp = nullptr; | ||
| GenTree* cnsOp = nullptr; | ||
| GenTree* otrOp = nullptr; |
There was a problem hiding this comment.
Yes (happy to change if its confusing, I forgot I named it this).
There was a problem hiding this comment.
I vote for a rename (IMO, "otherOp" is fine; no need for all vars to be 5 characters long)
There was a problem hiding this comment.
fixed to be otherOp
| tmpDsc = codeGen->getSpillTempDsc(src); | ||
| } | ||
| else if (dst->isUsedFromSpillTemp()) | ||
| if (memOp != nullptr) |
There was a problem hiding this comment.
It might have been better to first handle the simple cases to get those out of the way quicker. As is now you have to scroll through all the memOp case to reach the rest.
|
|
||
| case GT_LCL_VAR: | ||
| { | ||
| assert(memOp->IsRegOptional() || !emitComp->lvaTable[memOp->gtLclVar.gtLclNum].lvIsRegCandidate()); |
There was a problem hiding this comment.
It would be better to split this assert in two. The second condition should be moved after the assignment below so it can reuse varNum.
There was a problem hiding this comment.
It's an || condition, so it can't be split... No matter how you do the memOp->IsRegOptional() check, it can fail on its own.
There was a problem hiding this comment.
Unless I'm just too tired to remember how to do this 😄
|
If this is a pure refactoring, then you should verify the result is no-diff. You can use jit-diffs for this in Core but I'd also recommend running the SPMI based diffs over in desktop as they give better all around coverage of the various jit modes (jit-diffs will only test R2R codegen). |
Is there a doc on how to do this for Desktop? Is there an easy way to do this 'en-masse' for Core? |
|
Jit-diff is described here. I generally do this with two enlistments, one built vs master, the other vs my change. You need both check and release builds. For desktop I can send you pointers via email. |
|
Thanks @AndyAyersMS - I think that doing both coreclr and desktop diffs would be ideal validation (and is what I usually do for refactoring changes). |
|
Rebased onto HEAD for the jit-diff tests. |
|
(This is a poster child for CI-triggered all-platform all-architecture asm diffs, including superpmi asm diffs, which was once a goal but is not being worked on currently.) |
BruceForstall
left a comment
There was a problem hiding this comment.
- Make sure you trigger a bunch of x86/x64 stress mode jobs.
- Seems like there could be more comments, maybe including examples. I guess there weren't many before.
| @@ -2920,174 +2920,283 @@ void emitter::emitInsStoreLcl(instruction ins, emitAttr attr, GenTreeLclVarCommo | |||
|
|
|||
| regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src) | |||
There was a problem hiding this comment.
Since you've got this function in your head now, it would be useful to write a good (standard format) function header comment describing it.
There was a problem hiding this comment.
Added a function header.
| // We can only have one memory operand and only src can be a constant operand | ||
| GenTree* memOp = nullptr; | ||
| GenTree* cnsOp = nullptr; | ||
| GenTree* otrOp = nullptr; |
There was a problem hiding this comment.
I vote for a rename (IMO, "otherOp" is fine; no need for all vars to be 5 characters long)
Are you sure that the diffs you are doing have a proper baseline or whatever is what they are using? It seems strange for any change to |
Looks like |
|
FWIW I've got your branch and run jit-diff, there are no changes. |
|
Ah, I should have mentioned that you need to be careful about ensuring that your TFS enlistment is synced to the same point as your GitHub enlistment. They are usually close but the mirroring can lag or get blocked at times. |
|
Added a function header, some more detailed comments, and changed |
|
Ok. No diffs for CoreCLR (tested on Windows against x64 and x86 for Checked and Released builds). I think I'm doing something wrong for the Desktop diffs and am going to follow up to see if I can figure it out. |
| offset = 0; | ||
|
|
||
| // Ensure that all the GenTreeIndir values are set to their defaults. | ||
| assert(memBase->gtRegNum == REG_NA); |
There was a problem hiding this comment.
Assert was incorrect. Under JitStress=1 on Windows x64 Checked we had a few tests fail.
Validated that we still want to go the emit_R_S or emit_S_R route by checking the code-diff.
|
Okay:
|
|
Most of the Windows JITStress jobs are failing the |
|
The only non- |
|
https://github.com/dotnet/coreclr/issues/15924 is tracking the |
This finished. I had a few failures with a failure type of There were also no diffs (I did have a network outage that failed the amd64chk diff run, as it couldn't find the file share for the baseline, but I had already ran these manually beforehand and saw no failures). |
|
@CarolEidt, @AndyAyersMS, @BruceForstall Is there any additional validation needed? As a summary (assuming I read all the logs correctly):
|
Opened #15932 I don't see the need for any further validation. |
|
Is there any other feedback, or does this look good to merge (@AndyAyersMS, @BruceForstall, @mikedn)? |
BruceForstall
left a comment
There was a problem hiding this comment.
LGTM. Thanks for the additional comments.
|
Merging. |
Resolves https://github.com/dotnet/coreclr/issues/15829