riscv: Fix undefined behavior in `png_read_filter_row_paeth_rvv`
The existing implementation produced incorrect output at -O2/-O3 due
to reliance on RVV mask-agnostic element preservation.
The old code computed absolute values using masked operations:
vbool8_t p_neg_mask = __riscv_vmslt_vx_i16m2_b8(p, 0, vl);
vint16m2_t pa = __riscv_vrsub_vx_i16m2_m(p_neg_mask, p, 0, vl);
Per RVV 1.0 specification (section 5.4), the elements where the mask
bit is 0 have agnostic tail policy: implementations may either preserve
the original value or may set all bits to 1. This is explicitly
implementation-defined and not guaranteed.
When `p >= 0`, the mask bit is 0, so the "preserved" value of `pa` may
be clobbered to 0xFFFF depending on microarchitecture and optimization
level. This causes silent data corruption in decoded PNG images.
Fix by eliminating all masked operations in favour of unconditional
vector operations:
1. Compute the absolute value via `min(x, -x)` in unsigned arithmetic:
vuint16m2_t tmp = __riscv_vrsub_vx_u16m2(p, 0, vl);
vuint16m2_t pa = __riscv_vminu_vv_u16m2(p, tmp, vl);
This works because operands are u8 values widened to u16.
For any difference `d = b - c` where `b` and `c` are in `[0...255]`:
- If `b >= c`:
`d` is in [0...255], and `-d (mod 2^16)` is in `[65281...65535]`.
- If `b < c`:
`d` is in `[65281...65535]`, and `-d (mod 2^16)` is in `[1...255]`.
In both cases, `min(d, -d)` yields `|b - c|`.
2. Select the Paeth predictor via iterative min-tracking with vmerge:
vbool8_t m1 = __riscv_vmsltu_vv_u16m2_b8(pb, pa, vl);
pa = __riscv_vmerge_vvm_u16m2(pa, pb, m1, vl);
a = __riscv_vmerge_vvm_u8m1(a, b, m1, vl);
The vmerge instruction explicitly defines all lanes (no agnostic
elements), and the strict less-than comparison preserves correct
tie-breaking per the PNG specification (prefer `a` over `b` over
`c` when equal).
The new implementation is also simpler (with fewer instructions) and
provides ~14% speedup over scalar on SpacemiT K1.
Reported-by: Filip Wasil <f.wasil@samsung.com>
Reviewed-by: Cosmin Truta <ctruta@gmail.com>
Signed-off-by: Cosmin Truta <ctruta@gmail.com>
1 file changed