-
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
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 |
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. 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 |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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, |
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. Added some questions before we merge.
@@ -1012,32 +1012,32 @@ void emitter::emitInsSanityCheck(instrDesc* id) | |||
// (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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hhm, so turns out, we won't be able to do the validation in emitInsSanityCheck()
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.
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.
src/coreclr/jit/emitarm64.cpp
Outdated
assert(isVectorRegister(reg1)); | ||
assert(isLowPredicateRegister(reg2)); | ||
assert(isVectorRegister(reg3)); | ||
assert(insOptsScalableWithSimdVector(opt)); | ||
assert(insOptsScalable(opt)); | ||
assert(sopt == INS_SCALABLE_OPTS_NONE); |
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 INS_SCALABLE_OPTS_WITH_SIMD_SCALAR
?
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.
likewise for few below where previously we had assert(insOptsScalableWithSimdVector(opt))
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.
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_SCALAR
to any instruction with a SIMD scalar. - Only pass
_WITH_SIMD_SCALAR
to 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wondering why we need a different method for INS_OPTS_SCALABLE_Q
?
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, alright that makes sense then.
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.
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). |
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
* 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.