Move arm64 insOpts entries into insScalableOpts#96692
Conversation
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch Issue DetailsinsOpts has a max size of 6 bits, and it is getting full. For some of the options, they are only required to specify the encoding group, after this only the lane size (_S etc) is needed. Move these to a new enum insGroupOpts.
|
|
This moves all the entries that can be moved. All the remaining insOpts values have additional use later.
|
insOpts has a max size of 6 bits, and it is getting full. For some of the options, they are only required to specify the encoding group, after this only the lane size (_S etc) is needed. Move these to a new enum insGroupOpts.
db72ceb to
92c3032
Compare
|
@dotnet/arm64-contrib |
BruceForstall
left a comment
There was a problem hiding this comment.
LGTM. I'll let someone more involved in coding these comment.
I was hoping to move all the |
|
I've managed to remove the I'll take a closer look at the rest. |
|
The only remaining additional Why was this needed? This has a single encoding group (IF_SVE_AH_3A). This option needs encoding somewhere in the
If the above is all ok, then there are some additional clean ups I can do with all the assert checks. |
I'm thinking at minimum we still need the 4 entires (_B, _H, _S, _D). The size of the lane needs storing somewhere. It's not available in |
kunalspathak
left a comment
There was a problem hiding this comment.
If the above is all ok, then there are some additional clean ups I can do with all the assert checks.
This looks fine to me.
| // IF_SVE_AJ_3A | ||
| #ifdef ALL_ARM64_EMITTER_UNIT_TESTS_SVE_UNSUPPORTED | ||
| theEmitter->emitIns_R_R_R(INS_sve_addqv, EA_8BYTE, REG_V21, REG_V7, REG_P22, INS_OPTS_SCALABLE_B_WITH_SIMD_VECTOR); | ||
| theEmitter->emitIns_R_R_R(INS_sve_addqv, EA_8BYTE, REG_V21, REG_P7, REG_V22, INS_OPTS_SCALABLE_B); |
There was a problem hiding this comment.
looks like there was a typo? This reminds me, that we should double check these instructions encoding with LATE_DISASM and mark them as supported wherever applicable.
There was a problem hiding this comment.
Yes, this was a typo.
Problem with adding them is that capstone exits when it doesn't recognise an instruction, so it breaks the diffs.
|
Thoughts about this:
The first is encoded as: The second currently requires a But, we could remove the |
I decided to do this as it simplifies the code and makes it less ambiguous. |
|
That's everything cleaned up. I've settled on For example, |
kunalspathak
left a comment
There was a problem hiding this comment.
LGTM. Added some questions before we merge.
| // (predicated) | ||
| case IF_SVE_CR_3A: // ........xx...... ...gggnnnnnddddd -- SVE extract element to SIMD&FP scalar register | ||
| elemsize = id->idOpSize(); | ||
| assert(insOptsScalableWithSimdScalar(id->idInsOpt())); // xx |
There was a problem hiding this comment.
Hhm, so turns out, we won't be able to do the validation in emitInsSanityCheck()
There was a problem hiding this comment.
No, we've lost the information by this point.
What would be useful here would be if we had REG_Z1 which is distinct from REG_V1. This way we could assert isSveVectorRegister().
AIUI, when predicate registers are added, REGNUM_BITS needs increasing from 6 bits to 7 bits. That would allow for 32 general + 32 neon + 16 SVE + 32 predicate.
There would then have to be a step somewhere around register allocations to convert SVE register numbers to/from NEON register numbers to ensure there is really only one set.
Alternatively, we could still have a distinct REG_Z1, but inside emitIns_R_R_R() it converts to REG_V1 before storing to _idReg1. That means you can assert in emitIns_R_R_R() but not in emitInsSanityCheck().
Either would allow for the removal of INS_SCALABLE_OPTS_WITH_SIMD_SCALAR too.
| assert(isVectorRegister(reg3)); | ||
| assert(insOptsScalableWithSimdVector(opt)); | ||
| assert(insOptsScalable(opt)); | ||
| assert(sopt == INS_SCALABLE_OPTS_NONE); |
There was a problem hiding this comment.
shouldn't this be INS_SCALABLE_OPTS_WITH_SIMD_SCALAR?
There was a problem hiding this comment.
likewise for few below where previously we had assert(insOptsScalableWithSimdVector(opt))
There was a problem hiding this comment.
There is only a single encoding for andqv. It always has a destination of a SIMD vector. This is the same for every instruction that uses a SIMD vector. In all these cases, the only use of setting sopt would be for an assert check. Removing the setting of sopt meant there were no uses of _WITH_SIMD_VECTOR and it could be removed.
Meanwhile, have a look above at andv. This always has a destination of a SIMD scalar. This is the same for some other instructions (eg saddv), but there are some instructions which can optionally have a destination of SVE Vector or SIMD scalar (eg clasta or mov). We have a choice here:
- Always pass
_WITH_SIMD_SCALARto any instruction with a SIMD scalar. - Only pass
_WITH_SIMD_SCALARto instructions where there is an option (clasta/mov). For instructions without a option (andv) do not use ansopt.
The PR goes with the second option. Why?....
Finally, have a look at movprfx. This can have optionally have zero or merge predicate, and uses _PREDICATE_MERGE to distinguish. If we were to insist on always passing _WITH_SIMD_SCALAR to andv, then technically we'd have to pass _PREDICATE_MERGE to every instruction that uses a predicate merge. Which is a lot of instructions, and becomes a pain to ensure we're always using it.
There was a problem hiding this comment.
That makes sense. I am wondering if we can capture this in a comment somewhere so the other developers can follow and reason about this design.
There was a problem hiding this comment.
I added this comment for the insScalableOpts definition in instr.h
| (opt == INS_OPTS_SCALABLE_D) || (opt == INS_OPTS_SCALABLE_Q)); | ||
| } | ||
|
|
||
| inline static bool insOptsScalableStandard(insOpts opt) |
There was a problem hiding this comment.
wondering why we need a different method for INS_OPTS_SCALABLE_Q?
There was a problem hiding this comment.
Most instructions don't allow Q sized registers. It's only a small few.
I think insOptsScalable() should still check for everything, even though that's not the default.
Prior to this PR, most instructions were using insOptsScalableSimple() but that vanished when I got rid of all the enum entries. For simplicity, I could rename insOptsScalableStandard() to insOptsScalableSimple() and that'd reduce the diff in this PR.
There was a problem hiding this comment.
I am thinking why not just have insOptsScalableStandard() (or insOptsScalableSimple()). Just have 1 method which is insOptsScalable() that takes care of _Q as well.
There was a problem hiding this comment.
That will mean for most instructions we will allow Q to be passed as a size.
Which would then probably assert when trying to encode the instruction, in insEncodeElemsize() (because it doesn't support Q)
There was a problem hiding this comment.
If an instruction wrongly passes _Q where it is not supposed to, insEncodeElemsize() will correctly assert, right? I am trying to understand why _Q needs special treatment than others.
There was a problem hiding this comment.
Only reason it would need special treatment is that we already have the various size checks for other instructions. insOptsScalableWide() is used for widening instructions (B,H,S only), insOptsScalableFloat() is used for float types (H,S,D), insOptsScalableWords() is just S and D, etc etc. Those are used throughout emitInsSanityCheck(), instead of using insOptsScalable(). It would seem odd to check for restricted element sizes for some instructions, and then for others allow Q (via use of insOptsScalable()).
There was a problem hiding this comment.
I see, alright that makes sense then.
There was a problem hiding this comment.
nit for next PR: we can rewrite it by:
inline static bool insOptsScalable(insOpts opt)
{
// `opt` is any of the scalable types.
return (insOptsScalableStandard(opt) || (opt == INS_OPTS_SCALABLE_Q));
}|
/azp run runtime-coreclr superpmi-diffs |
|
Azure Pipelines successfully started running 1 pipeline(s). |
* Move arm64 insOpts entries into insGroupOpts insOpts has a max size of 6 bits, and it is getting full. For some of the options, they are only required to specify the encoding group, after this only the lane size (_S etc) is needed. Move these to a new enum insGroupOpts. * Nits * Rename to insScalableOpts * Remove _WITH_SIMD_VECTOR * Remove _WITH_PREDICATE_MERGE. Reuse _idReg3Scaled. * Remove insOptsScalableSimple * Better insScalableOpts entry names * Add INS_SCALABLE_OPTS_NONE asserts * Remove INS_SCALABLE_OPTS_WITH_SCALAR * insScalableOpts descriptions * Remove uses of INS_SCALABLE_OPTS_WITH_SIMD_SCALAR for single variants * Remove unused sopt from emitIns_R_R * Add insScalableOptsNone * Restore unreached() for capstone unsupported * Add insOptsScalableStandard
insOpts has a max size of 6 bits, and it is getting full.
For some of the options, they are only required to specify the encoding group, after this only the lane size (_S etc) is needed. Move these to a new enum.