Ensure that x64 disassembly names operands at the size actually encoded - #131428
Open
tannergooding wants to merge 4 commits into
Open
Ensure that x64 disassembly names operands at the size actually encoded#131428tannergooding wants to merge 4 commits into
tannergooding wants to merge 4 commits into
Conversation
`movzx` never encodes REX.W, and `emitOutputRR` drops it for `xor reg, reg`, but the display forced a pointer-sized destination in both cases -- the latter because a GC type overrode the narrowed operand size. This affected 33,035 instructions in a PMI run over System.Private.CoreLib. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…width
`v{extract,insert}{f,i}32x4` were caught by the pseudo-name immediate
suppression meant for `cmp*`/`vpcmp*`/`pclmulqdq`, which encode the immediate in
the mnemonic. Their pseudo-name is only the VEX-era alias, so the lane selector
was dropped -- `vextracti32x4` rendered all four lanes identically.
`movmskps`/`movmskpd` named the destination from the vector operand size, giving
`rcx` for an encoding that is `REX_WIG` and always writes 32 bits. `pmovmskb`
already handled this.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The narrowing converts only adjusted the destination size in the register form, so the memory forms named it at the source size instead. The scalar integer converts and `vmovd` likewise named their general purpose operand from the vector size. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The vector-source broadcasts fell to the default case, which names both operands at the destination size. The source is architecturally always xmm. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 5 pipeline(s). 11 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
Member
Author
|
This is just a fix to the disasm output, fixing some more of the edge cases that caused confusing or incorrect diffs. No changes to the actual codegen emitted. |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the xarch disassembly output formatting in emitDispIns / emitDispConstant so operand register names (and certain immediates) reflect the size actually encoded in the instruction bytes, avoiding misleading 64-bit register names and missing lane selectors in printed disassembly.
Changes:
- Teach
emitDispConstantto still print the imm8 lane selector for{vextract,vinsert}{f,i}32x4despite these instructions using pseudo-names. - Adjust operand-size selection in
emitDispInsso specific instructions display the correct GPR/vector operand width (e.g.,movzxdestination,movmskps/movmskpddestination, broadcast register-source forms). - Introduce
GetNarrowedDstAttrand apply it in the relevant display paths to correctly show narrowing-conversion destination sizes (including memory forms).
Show a summary per file
| File | Description |
|---|---|
src/coreclr/jit/emitxarch.cpp |
Disassembly-only fixes in emitDispIns/helpers to print operand widths and immediates consistent with the encoded instruction. |
Copilot's findings
- Files reviewed: 1/1 changed files
- Comments generated: 0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The x64 disassembler prints some operands at a size that doesn't match the bytes it just encoded. This makes the printed text actively misleading --
movzx rax, alandxor rax, raxboth suggest a 64-bit write where the encoder deliberately emitted a 32-bit one, and a dropped lane selector makes four differentvextracti32x4instructions render identically.Everything here is display-only, confined to
emitDispInsand its helpers. Codegen is unchanged.movzxprinted a 64-bit destination.emitOutputRRonly addsREX.Wwhen(size == EA_8BYTE) || (ins == INS_movsx), and formovzxthatsizeis the source width, soREX.Wis never set;TakesRexWPrefixexcludes it as well.movsxprinting 64-bit is correct -- all 5,263 in CoreLib genuinely carryREX.W-- so the two are split rather than sharing a branch. 4 sites (ARD/SRD/RRD/MRD).GC-typed self-zeroing
xorprinted 64-bit.emitOutputRRdropsREX.Wforxor reg, regand recordsid->idOpSize(EA_4BYTE), butemitDispInsderivedattrfromidGCref()first and so kept the pointer-sized name. Smoking gun in a single method:4533C0 xor r8, r8next to4533FF xor r15d, r15d.v{extract,insert}{f,i}32x4swallowed the imm8 lane selector.emitDispConstantearly-returns for everyINS_FLAGS_HasPseudoNameinstruction. That's right forvcmp*/vpcmp*/pclmulqdq, where the immediate is the name, but these four are only pseudo-named for the VEX-era alias and still need the operand. Proof of loss: CoreLib uses selectors 00/01/02/03 and all four rendered identically.movmskps/movmskpdprinted a 64-bit GPR.REX_WIGis#defined asREX_W0, soTakesRexWPrefixreturns false and the JIT never emitsREX.Wfor these -- the destination is alwaysr32.pmovmskbalready had the case; the other two were missing.cvtsi2*/vcvtusi2*/vmovdnamed their GPR operand from the vector size. Now taken fromGetInputSizeInBytes, which reads theInput_32Bit/Input_64Bitflag. Definitive display-only proof: the identical bytesC4C17A2AC0printedr8din one method andr8in another.Narrowing converts named the destination at the source size in the memory forms. Only the register form narrowed; the memory paths printed
emitRegName(idReg1(), attr)raw, givingvcvtpd2ps ymm0, ymmword ptr [...]. ExtractedGetNarrowedDstAttrand shared it across the register and ARD/SRD/MRD paths.Broadcasts printed the source at the destination width. Only the
_gprvariants had cases; the vector-source ones fell todefault:wheresrcAttr = attr. These are architecturallyymm1/zmm1, xmm2-- the source is the low element.C4E27D19C1isVEX.256.66.0F38.W0 19 /r=VBROADCASTSD ymm1, xmm2, but printedymm1, ymm2.Verification. Byte-accurate PMI corpus of
System.Private.CoreLib(--nodiffablewithDOTNET_JitDisasmWithCodeBytes=1), decoding the encoding prefixes mechanically and comparing encoded vs printed width for every instruction. Base and diff JITs built from the same source, differing only by this change:movzxwith 64-bit dstxorprinted 64-bitv?movmskp[sd]with 64-bit dstymm<-ymm/zmm<-zmmvextract/vinsert*32x4showing the selector1,134,687 instructions checked, 0 remaining width mismatches. Codegen is unchanged: 75,012 methods compared by (mnemonic, encoded length), and the only 4 differences are P/Invoke inline-frame calls whose helper target moves in and out of
rel32range with ASLR -- confirmed by a control run of the same JIT twice reproducing the same set.Broadcasts needed a targeted repro since CoreLib never emits the register form of
vpbroadcastb/w/d/qorvbroadcast{f,i}32x2at all. All eight flip correctly with identical encoded bytes:vbroadcastss xmm, xmm(236 in CoreLib) is correct and unchanged, as are the memory-source-onlyvbroadcast{f,i}32x4/64x2/32x8/64x4, which have no register form.On coverage.
GetNarrowedDstAttris inherently a hand-maintained list: metadata alone can't separate a narrowingINS_TT_FULLconvert from a same-size op, sincecvtpd2ps(Input_64Bit,KMask_Base2) andvaddps(Input_32Bit,KMask_Base4) both compute 8-vs-8 and 4-vs-4. So I audited it statically, decoding element sizes from the mnemonics rather than from what CoreLib happens to emit: 13/2entries and 3/4entries, no missing narrowing instructions and no dead entries. The widening direction needs no list --INS_TT_HALF/QUARTER_MEM/EIGHTH_MEMalready handle both directions generically.Known residual, not addressed here.
emitDispInsis also called fromemitDispIGduringJitDump, before output, where thexornarrowing hasn't happened yet; that path still prints the wide name. It degrades gracefully rather than misprinting --EA_SIZE(EA_GCREF) == idOpSize()there, so the new condition is simply false -- but the pre/post-encode difference is inherent to that call site.CC. @dotnet/jit-contrib
Note
This PR was authored with GitHub Copilot.