Alfie Richards <[email protected]> writes:
> The first (question A) is versioning the whole body of the loop (as you 
> call the "classical idea") vs a "fixup block" like I suggest. The fixup 
> block keeps one loop body, but with the addition of the branch to the 
> fixup block to calculate anything required to deal with a partial load 
> (such as AND of ffr and the loop mask).

The branch would be needed for both approaches.  I think the question is
more how much code the fixup block duplicates (ranging from "none" with
your approach to "everything up to the latch test" with the classical
approach.)

> In testing I actually found the overhead to be pretty similar for having 
> a fixup block verses duplicating the entire body of the loop. Then as 
> you say, using the fixup block allows multiple FFR regions (which this 
> patch series implements, see ex 2) and it requires less code overall, so 
> I strongly prefer this.

Ah, interesting!  Thanks for running the numbers.

>> However, if I'm reading the above assembly correctly, it looks like:
>> 
>>    next_mask_ffr_81 = next_mask_79 & next_mask_ffr_80;
>> 
>> ends up using the RDFFR result even for the "good" path.  Is:
>> 
>>    # next_mask_ffr_80 = PHI <{ -1, ... }(3), _84(15)>
>> 
>> getting optimised to _84 based on:
>> 
>>    if (ffr_mask_83 == { -1, ... })
>> 
>> ?  That would be a legitimate optimisation as far as gimple is concerned,
>> but my understanding is that it would be harmful for performance.
>
> I think this is actually my mistake and the example I gave is missing 
> bits and misleading. If you see example 1 and 2 you can hopefully see in 
> the "good" full load case, we do not depend on the value from FFR in the 
> loop body.

Hmm, example 1 still seems similar to the original quote.  I.e.:

>       .arch armv8.2-a+crc+sve
>       .file   "example1.c"
>       .text
>       .align  2
>       .p2align 5,,15
>       .global foo
>       .type   foo, %function
> foo:
> .LFB13:
>       .cfi_startproc
>       cntb    x4
>       sub     x4, x4, #1
>       setffr
>       and     x4, x1, x4
>       mov     w3, 0
>       ptrue   p15.s, all
>       and     x2, x4, 17179869180
>       ubfx    x4, x4, 2, 32
>       sub     x0, x0, x2
>       whilelo p14.s, xzr, x4
>       sub     x1, x1, x2
>       mov     w2, 10000
>       add     x4, x4, x2
>       whilelo p7.s, xzr, x4
>       not     p7.b, p7/z, p14.b
>       b       .L4
>       .p2align 2,,3
> .L11:
>       add     x3, x3, x2
>       whilelo p7.s, x3, x4
>       add     x0, x0, x2, lsl 2
>       and     p7.b, p7/z, p14.b, p14.b

This is executed for both paths after the block at L2.  It uses p14.

>       add     x1, x1, x2, lsl 2
>       ptest   p15, p7.b
>       b.none  .L9
> .L4:
>       ldff1w  z30.s, p7/z, [x0]
>       ldff1w  z31.s, p7/z, [x1]
>       cntw    x2
>       rdffr   p14.b

RDFFR sets p14 here.

>       nots    p13.b, p15/z, p14.b
>       b.any   .L10
> .L2:

For this "good" fallthrough case, p14 is still the RDFFR result.

>       cmpeq   p7.s, p7/z, z30.s, z31.s
>       b.none  .L11
>       mov     w0, 1
>       ret
>       .p2align 2,,3
> .L9:
>       mov     w0, 0
>       ret
> .L10:
>       and     p7.b, p7/z, p14.b, p14.b
>       mov     w2, 0
>       setffr
>       mov     p14.b, p13.b

p14 is set up to the NOTS result here, for the faulting case.

Am I reading it wrongly?

Thanks,
Richard

>       b       .L2
>       .cfi_endproc
> .LFE13:
>       .size   foo, .-foo
>       .ident  "GCC: (GNU) 17.0.0 20260622 (experimental)"
>       .section        .note.GNU-stack,"",@progbits

Reply via email to