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