Richard Sandiford <[email protected]> writes:
> Tamar Christina <[email protected]> writes:
>>> > For LE the first pattern stays the same. We also add these
>>> >
>>> > (define_insn "*aarch64_vec_concat_lane<mode>"
>>> > [(set (match_operand:VP_2E 0 "register_operand" "=w,w")
>>> > (vec_concat:VP_2E
>>> > (vec_select:<VEL>
>>> > (match_operand:VP_2E 1 "register_operand" "0,w")
>>> > (parallel
>>> > [(match_operand:SI 3 "immediate_operand" "i,i")]))
>>> > (vec_select:<VEL>
>>> > (match_operand:VP_2E 2 "register_operand" "w,0")
>>> > (parallel
>>> > [(match_operand:SI 4 "immediate_operand" "i,i")]))))]
>>> > "TARGET_SIMD
>>> > && INTVAL (operands[3]) == 0
>>> > && INTVAL (operands[4]) == 1"
>>> > {
>>> > ...
>>> >
>>> > Which essentially is saying concat two vectors together from a vec_select.
>>> > This then becomes your INS.
>>> >
>>> > Then for BE you rewrite the vec_merge into the vec_concat using e.g.
>>> >
>>> > (define_insn_and_split
>>> "*aarch64_simd_vec_copy_lane_same<mode>_be_lowpart"
>>> > [(set (match_operand:VP_2E 0 "register_operand" "=w,w")
>>> > (vec_merge:VP_2E
>>> > (vec_duplicate:VP_2E
>>> > (match_operand:<VEL> 2 "register_operand" "w,0"))
>>> > (match_operand:VP_2E 1 "register_operand" "0,w")
>>> > (match_operand:SI 3 "immediate_operand" "i,i")))]
>>> > "TARGET_SIMD
>>> > && BYTES_BIG_ENDIAN
>>> > && !reload_completed
>>> > && INTVAL (operands[3]) == 2
>>> > && SUBREG_P (operands[2])
>>> > && subreg_lowpart_p (operands[2])"
>>> > "#"
>>> > "&& true"
>>> > ...
>>> >
>>> > This changes the codegen to target
>>> >
>>> > (vec_concat:V2DF
>>> > (vec_select:DF (reg:V2DF x) [(const_int 0)])
>>> > (vec_select:DF (reg:V2DF y) [(const_int 1)]))
>>> >
>>> > Which represents the INS without needing the subreg lanes and the subreg
>>> > goes away way before reload.
>>> >
>>> > Hopefully Richard is happy with this one.
>>>
>>> But I'm not sure what this subreg stuff is trying to achieve. Are you
>>> trying to force the case where operand 2 is a "natural scalar" (handwavy
>>> term) through a different pattern? If so, which one?
>>>
>>> The existing vec_merge-of-vec_duplicate patterns don't seem to care
>>> where the scalar comes from. If the operand is a plain pseudo REG
>>> defined by a GPR operation then LRA will generate a GPR-to-FPR move.
>>> One of those is going to be needed somewhere in that case.
>>>
>>> So I was more wondering why your original suggestion needed the:
>>>
>>> && SUBREG_P (operands[2])
>>> && known_eq (SUBREG_BYTE (operands[2]), GET_MODE_SIZE
>>> (<VEL>mode))"
>>>
>>> and couldn't just be:
>>>
>>> "TARGET_SIMD && INTVAL (operands[3]) == 2"
>>>
>>
>> Because this pattern is *only* valid for upper inserts, lower inserts is
>> handled
>> by the first one, the aarch64_simd_vec_copy_lane<mode>.
>>
>> Operand two, since the vec_merge pattern is a bit mask, 2 means your
>> destination is lane 1, the
>> known_eq (SUBREG_BYTE (operands[2]), GET_MODE_SIZE> (<VEL>mode))
>>
>> comparison is only valid if the subreg is the high part of a 2 lane
>> vector. Or in
>> other words, lane 1. So this pattern is only handling ins[1], ins[1].
>
> Guess I'm just stating common ground in the first few paragraphs below, but:
>
> Subregs like that are only allowed in one special case: a 64-bit
> highpart of a 128-bit AdvSIMD register. The fact that that case is
> allowed is due to a defective definition of REGMODE_NATURAL_SIZE, which
> Robin is in the process of fixing.
>
> Once the 64-bit highpart of a 128-bit register case is used, we have
> already lost, since if we have:
>
> (subreg:DI (reg:V2DI R) 0) [big-endian]
> (subreg:DI (reg:V2DI R) 8) [little-endian]
>
> LRA will not allocate R to a SIMD register. (This is partly due to the
> simplifiable_subregs/valid_mode_changes_for_regno stuff.)
>
> So the subregs that I listed above wouldn't become references to architectural
> lane 1. Instead, the RAs would spill R and reload the high 64 bits into the
> lowpart of a SIMD register, which would become operand 2. I think this
> would normally happen through memory, but perhaps GPRs are also possible.
>
> So after RA, operand 2 would always be the lowpart of a SIMD register
> (lane 0), and would have the right contents, whatever the SUBREG_BYTE
> was before RA. The behaviour would be correct, but the spilling would
> make the subregs above very inefficient.
>
> Perhaps you were wanting to prevent the inefficient 64-bit highpart
> subregs from being formed. But I'm not sure the condition would do that.
> Even if it does, the same thing would happen for any other DImode SIMD
> register_operand that had one of the forms above, including the existing
> vec_merge-of-vec_duplicate patterns. The inefficiency comes from the
> spilling that LRA is bound to emit, and that inefficiency would apply
> wherever the subreg occurs, not just to this pattern. The pattern
> doesn't seem like a special case.
>
> So I still think we can ignore whether operand 2 is a subreg. It's the
> RA's job to ensure that operand 2 occupies the lowpart of a register.
I suppose it would be more constructive to sketch what I'm actually
suggesting. I still think your original idea of having a vec_select
pattern and a scalar pattern is the way to go. Due to the vec_select-to-
subreg simplificaton rule that you mentioned (and contributed), the
vec_select will always be for the high part. In other words, it will
always be for architectural lane 1. So for your suggestion:
(define_insn "@aarch64_simd_vec_copy_lane<mode>"
[(set (match_operand:VP_2E 0 "register_operand" "=w,w")
(vec_merge:VP_2E
(vec_duplicate:VP_2E
(vec_select:<VEL>
(match_operand:VP_2E 3 "register_operand" "w,0")
(parallel
[(match_operand:SI 4 "immediate_operand" "i,i")])))
(match_operand:VP_2E 1 "register_operand" "0,w")
(match_operand:SI 2 "immediate_operand" "i,i")))]
"TARGET_SIMD
&& exact_log2 (INTVAL (operands[2])) >= 0
&& INTVAL (operands[4]) == exact_log2 (INTVAL (operands[2]))"
{
operand 4 should in practice always be 0 for big-endian and 1 for
little-endian. The other case should be folded away. We could test
for that using:
"TARGET_SIMD
&& exact_log2 (INTVAL (operands[2])) >= 0
&& ENDIAN_LANE_N (2, INTVAL (operands[4])) == 1"
(i.e. just changing the last line).
This is then (as you say) a replacement for the existing VP_2E
subset of the aarch64_simd_vec_copy_lane<mode> pattern.
The scalar pattern will always be for the low part. In other words,
it will always be for architectural lane 0. So for your suggestion:
(define_insn "*aarch64_simd_vec_copy_lane_same<mode>_subreg"
[(set (match_operand:VP_2E 0 "register_operand" "=w,w")
(vec_merge:VP_2E
(vec_duplicate:VP_2E
(match_operand:<VEL> 2 "register_operand" "w,0"))
(match_operand:VP_2E 1 "register_operand" "0,w")
(match_operand:SI 3 "immediate_operand" "i,i")))]
"TARGET_SIMD
&& INTVAL (operands[3]) == 2
&& SUBREG_P (operands[2])
&& known_eq (SUBREG_BYTE (operands[2]), GET_MODE_SIZE (<VEL>mode))"
{
I think we should replace the condition with:
"TARGET_SIMD && exact_log2 (INTVAL (operands[2])) >= 0"
This pattern is then a replacement for the VP_2E subset of the existing
@aarch64_simd_vec_set<mode> pattern. Because of that, it would be good
to include the existing ?r and Utv alternatives too. The thing that
we're adding is alternative 1 above.
Thanks,
Richard