> -----Original Message----- > From: Richard Biener <rguent...@suse.de> > Sent: Monday, June 13, 2022 9:38 AM > To: Richard Sandiford <richard.sandif...@arm.com> > Cc: Tamar Christina <tamar.christ...@arm.com>; gcc-patches@gcc.gnu.org; > nd <n...@arm.com>; Richard Earnshaw <richard.earns...@arm.com>; > Marcus Shawcroft <marcus.shawcr...@arm.com>; Kyrylo Tkachov > <kyrylo.tkac...@arm.com>; ro...@eyesopen.com > Subject: Re: [PATCH]AArch64 relax predicate on load structure load > instructions > > On Mon, 13 Jun 2022, Richard Sandiford wrote: > > > Richard Biener <rguent...@suse.de> writes: > > > On Wed, 8 Jun 2022, Richard Sandiford wrote: > > >> Tamar Christina <tamar.christ...@arm.com> writes: > > >> >> -----Original Message----- > > >> >> From: Richard Sandiford <richard.sandif...@arm.com> > > >> >> Sent: Wednesday, June 8, 2022 11:31 AM > > >> >> To: Tamar Christina <tamar.christ...@arm.com> > > >> >> Cc: gcc-patches@gcc.gnu.org; nd <n...@arm.com>; Richard Earnshaw > > >> >> <richard.earns...@arm.com>; Marcus Shawcroft > > >> >> <marcus.shawcr...@arm.com>; Kyrylo Tkachov > > >> >> <kyrylo.tkac...@arm.com> > > >> >> Subject: Re: [PATCH]AArch64 relax predicate on load structure > > >> >> load instructions > > >> >> > > >> >> Tamar Christina <tamar.christ...@arm.com> writes: > > >> >> > Hi All, > > >> >> > > > >> >> > At some point in time we started lowering the ld1r instructions in > gimple. > > >> >> > > > >> >> > That is: > > >> >> > > > >> >> > uint8x8_t f1(const uint8_t *in) { > > >> >> > return vld1_dup_u8(&in[1]); } > > >> >> > > > >> >> > generates at gimple: > > >> >> > > > >> >> > _3 = MEM[(const uint8_t *)in_1(D) + 1B]; > > >> >> > _4 = {_3, _3, _3, _3, _3, _3, _3, _3}; > > >> >> > > > >> >> > Which is good, but we then generate: > > >> >> > > > >> >> > f1: > > >> >> > ldr b0, [x0, 1] > > >> >> > dup v0.8b, v0.b[0] > > >> >> > ret > > >> >> > > > >> >> > instead of ld1r. > > >> >> > > > >> >> > The reason for this is because the load instructions have a > > >> >> > too restrictive predicate on them which causes combine not to > > >> >> > be able to combine the instructions due to the predicate only > > >> >> > accepting simple > > >> >> addressing modes. > > >> >> > > > >> >> > This patch relaxes the predicate to accept any memory operand > > >> >> > and relies on LRA to legitimize the address when it needs to > > >> >> > as the constraint still only allows the simple addressing > > >> >> > mode. Reload is always able to legitimize to these. > > >> >> > > > >> >> > Secondly since we are now actually generating more ld1r it > > >> >> > became clear that the lane instructions suffer from a similar issue. > > >> >> > > > >> >> > i.e. > > >> >> > > > >> >> > float32x4_t f2(const float32_t *in, float32x4_t a) { > > >> >> > float32x4_t dup = vld1q_dup_f32(&in[1]); > > >> >> > return vfmaq_laneq_f32 (a, a, dup, 1); } > > >> >> > > > >> >> > would generate ld1r + vector fmla instead of ldr + lane fmla. > > >> >> > > > >> >> > The reason for this is similar to the ld1r issue. The > > >> >> > predicate is too restrictive in only acception register operands but > not memory. > > >> >> > > > >> >> > This relaxes it to accept register and/or memory while leaving > > >> >> > the constraint to only accept registers. This will have LRA > > >> >> > generate a reload if needed forcing the memory to registers > > >> >> > using the standard > > >> >> patterns. > > >> >> > > > >> >> > These two changes allow combine and reload to generate the > > >> >> > right > > >> >> sequences. > > >> >> > > > >> >> > Bootstrapped Regtested on aarch64-none-linux-gnu and no issues. > > >> >> > > >> >> This is going against the general direction of travel, which is > > >> >> to make the instruction's predicates and conditions enforce the > > >> >> constraints as much as possible (making optimistic assumptions > about pseudo registers). > > >> >> > > >> >> The RA *can* deal with things like: > > >> >> > > >> >> (match_operand:M N "general_operand" "r") > > >> >> > > >> >> but it's best avoided, for a few reasons: > > >> >> > > >> >> (1) The fix-up will be done in LRA, so IRA will not see the temporary > > >> >> registers. This can make the allocation of those temporaries > > >> >> suboptimal but (more importantly) it might require other > > >> >> previously-allocated registers to be spilled late due to the > > >> >> unexpected increase in register pressure. > > >> >> > > >> >> (2) It ends up hiding instructions from the pre-RA optimisers. > > >> >> > > >> >> (3) It can also prevent combine opportunities (as well as create > them), > > >> >> unless the loose predicates in an insn I are propagated to all > > >> >> patterns that might result from combining I with something else. > > >> >> > > >> >> It sounds like the first problem (not generating ld1r) could be > > >> >> fixed by (a) combining aarch64_simd_dup<mode> and > > >> >> *aarch64_simd_ld1r<mode>, so that the register and memory > > >> >> alternatives are in the same pattern and (b) using the merged > instruction(s) to implement the vec_duplicate optab. > > >> >> Target-independent code should then make the address satisfy the > > >> >> predicate, simplifying the address where necessary. > > >> >> > > >> > > > >> > I think I am likely missing something here. I would assume that > > >> > you wanted to use the optab to split the addressing off from the > > >> > mem expression so the combined insn matches. > > >> > > > >> > But in that case, why do you need to combine the two instructions? > > >> > I've tried and it doesn't work since the vec_duplicate optab > > >> > doesn't see the mem as op1, because in gimple the mem is not part of > the duplicate. > > >> > > > >> > So you still just see: > > >> > > > >> >>>> dbgrtx (ops[1].value) > > >> > (subreg/s/v:QI (reg:SI 92 [ _3 ]) 0) > > >> > > > >> > As the operand as the argument to the dup is just an SSA_NAME. > > >> > > >> Ah, yeah, I'd forgotten that fixed-length vec_duplicates would come > > >> from a constructor rather than a vec_duplicate_expr, so we don't > > >> get the usual benefit of folding single-use mems during expand. > > >> > > >> https://gcc.gnu.org/pipermail/gcc-patches/2022-May/595362.html > > >> moves towards using vec_duplicate even for fixed-length vectors. > > >> If we take that approach, then I suppose a plain constructor should > > >> be folded to a vec_duplicate where possible. > > >> > > >> (Alternatively, we could use an extended vec_perm_expr with scalar > > >> inputs, as Richi suggested in that thread.) > > >> > > >> If we don't do that, or don't do it yet, then… > > > > > > I suppose since we alrady have vec_duplicate we can just use it ... > > > what was the reason to not do this originally? > > > > There just wasn't any specific benefit for fixed-length vectors at the > > time, and obvious potential problems -- introducing > > VEC_DUPLICATE_EXPRs too early would lose out on existing > CONSTRUCTOR-based folds. > > > > Also, isel didn't exist at the time that vec_duplicate was added, but > > it seems like it might be a good place to do the replacement. > > > > Match rules that want to test for a uniform vector operand can already > > use vec_same_elem_p to handle all representations, but perhaps we also > > need a way of generating the “right” form of duplicate for the current > > stage in the pass pipeline? > > I think we can have vec_duplicate without native target support by > expanding via CONSTRUCTOR, so vec_duplicate would be the correct one at > all stages and we fixup during RTL expansion directly. > > As you noted most targets don't implement vec_duplicate yet. > > > > I suppose the > > > vec_duplicate expander has a fallback via store_constuctor? > > > > > > Originally I wanted to avoid multiple ways to express the same thing > > > but vec_duplicate is a common enough special-case and it also > > > usually maps to a special instruction in vector ISAs. > > > There's VIEW_CONVERT vs. vec_duplicate for V1m modes then, I > suppose > > > VIEW_CONVERT is more canonical here. > > > > Is that already true for V1m constructors? (view_convert being > > canonical and constructors not, I mean.) > > I think so, yes. > > > What do you think about the suggestion in the other thread of making > > VEC_PERM_EXPR take an arbitrary number of inputs, with (as you > > suggested) the inputs allowed to be scalars rather than vectors? > > VEC_PERM_EXPR could then replace both CONSTRUCTOR and > VEC_DUPLICATE_EXPR and “optimising” > > a normal constructor to a duplicate would just be a case of removing > > repeated scalar inputs. > > It's indeed somewhat appealing to make VEC_PERM a swiss army knife. > I'm not sure about making it a VL tree though, currently it's a nice GIMPLE > ternary while VL would make it a SINGLE RHS with a GENERIC tree (unless we > introduce a gimple_vec_perm special gimple node). That said, allowing > scalars as VEC_PERM inputs to get rid of VEC_DUPLICATE only will still leave > us with the VIEW_CONVERT special case. > > At some point we might want to help targets with "interesting" > ISAs by lowering VEC_PERM to supported .VEC_PERM_CONSTs and relaxing > what permutes we allow earlier in the pipeline (I'm thinking of x86 with its > many special permutation ops and the open-coded vec-perm-const > expander). > > So not sure what to do, but I'm happy to widen VEC_DUPLICATE_EXPR use.
Just to check, this means detect VEC_DUPLICATE_EXPR during isel and convert the CONSTRUCTOR to it? Cheers, Tamar > > Richard.