riscv: avoid vle8ff in vector strcmp loop
Checks
| Context |
Check |
Description |
| redhat-pt-bot/TryBot-apply_patch |
success
|
Patch applied to master at the time it was sent
|
| linaro-tcwg-bot/tcwg_glibc_build--master-arm |
success
|
Build passed
|
| redhat-pt-bot/TryBot-32bit |
success
|
Build for i686
|
| linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 |
success
|
Test passed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-arm |
success
|
Test passed
|
Commit Message
Limit vl by the minimum distance from the input pointers to the next 4K
boundary, and use normal vle8.v loads in the main loop. This keeps the
loads page-safe while avoiding the vl update dependency from vle8ff.v.
Signed-off-by: daichengrong <daichengrong@iscas.ac.cn>
---
sysdeps/riscv/rvv/strcmp.S | 110 +++++++++++++++++++++----------------
1 file changed, 64 insertions(+), 46 deletions(-)
Comments
On 6/14/26 6:55 AM, daichengrong wrote:
> Limit vl by the minimum distance from the input pointers to the next 4K
> boundary, and use normal vle8.v loads in the main loop. This keeps the
> loads page-safe while avoiding the vl update dependency from vle8ff.v.
Do you have microbenchmark data that supports this change?
Generally for such changes we would like to see "make bench" showing a difference.
> Signed-off-by: daichengrong <daichengrong@iscas.ac.cn>
> ---
> sysdeps/riscv/rvv/strcmp.S | 110 +++++++++++++++++++++----------------
> 1 file changed, 64 insertions(+), 46 deletions(-)
>
> diff --git a/sysdeps/riscv/rvv/strcmp.S b/sysdeps/riscv/rvv/strcmp.S
> index a2f6865581..d32d8f490a 100644
> --- a/sysdeps/riscv/rvv/strcmp.S
> +++ b/sysdeps/riscv/rvv/strcmp.S
> @@ -35,59 +35,77 @@
> #define vstr1 v0
> #define vstr2 v8
> #define vmask1 v16
> -#define vmask2 v17
> +#define vmask2 v24
>
> ENTRY (STRCMP)
> .option push
> .option arch, +v
> - /* lmul=1 */
> -L(Loop):
> - vsetvli ivl, zero, e8, m1, ta, ma
> - vle8ff.v vstr1, (str1)
> - /* Check if vstr1[i] == 0 */
> - vmseq.vx vmask1, vstr1, zero
> -
> - vle8ff.v vstr2, (str2)
> - /* Check if vstr1[i] != vstr2[i] */
> - vmsne.vv vmask2, vstr1, vstr2
> -
> - /* Find the index x for vstr1[x] == 0 */
> - vfirst.m temp1, vmask1
> - /* Find the index x for vstr1[x] != vstr2[x] */
> - vfirst.m temp2, vmask2
> -
> - bgez temp1, L(check1)
> - bgez temp2, L(check2)
> -
> - /* Get the current vl updated by vle8ff. */
> - csrr ivl, vl
> - add str1, str1, ivl
> - add str2, str2, ivl
> - j L(Loop)
> -
> - /* temp1 >= 0 */
> -L(check1):
> - bltz temp2, L(return_at_nul)
> - blt temp2, temp1, L(check2)
> -L(return_at_nul):
> - /* temp2 < 0 */
> - /* temp2 >= 0 && temp1 < temp2 */
> - add str1, str1, temp1
> - add str2, str2, temp1
> - lbu temp1, 0(str1)
> - lbu temp2, 0(str2)
> - sub result, temp1, temp2
> + li t0, 4095
> + li t3, 4096
> +L(loop):
> + and t1, str1, t0 /* t1 = str1 offset in 4K page */
> + and t2, str2, t0 /* t2 = str2 offset in 4K page */
> + sub t4, t3, t1 /* t4 = bytes from str1 to page end */
> + sub t5, t3, t2 /* t5 = bytes from str2 to page end */
> + /*
> + * t3 = min(dist1, dist2)
> + */
> + bltu t5, t4, 1f
> + mv t5, t4
> +1:
> + /*
> + * Set vl to min(dist, VLMAX).
> + *
> + * If far from page boundary, vl = VLMAX.
> + * If close to page boundary, vl = dist.
> + */
> +
> + vsetvli ivl, t5, e8, m4, ta, ma
> + /*
> + * This load will not cross a 4K boundary for either pointer.
> + */
> + vle8.v vstr1, (str1)
> + vle8.v vstr2, (str2)
> +
> + /*
> + * str1 == 0 means string ended.
> + * str1 != str2 means strcmp result is decided.
> + */
> + vmseq.vx vmask1, vstr1, zero
> + vmsne.vv vmask2, vstr1, vstr2
> +
> + vfirst.m temp2, vmask2
> + vfirst.m temp1, vmask1
> +
> + bgez temp2, L(check_nul)
> + bgez temp1, L(ret)
> +
> + /*
> + * No NUL and no difference in this chunk.
> + * Advance by actual vl.
> + *
> + * If this was a page-boundary-limited load, one of str1/str2 now
> + * reaches the next 4K boundary. The next loop recomputes distance.
> + */
> + add str1, str1, ivl
> + add str2, str2, ivl
> + j L(loop)
> +
> +L(check_nul):
> + bltz temp1, L(found)
> + blt temp1, temp2, L(ret)
> +L(found):
> + add str1, str1, temp2
> + add str2, str2, temp2
> + lbu temp1, 0(str1)
> + lbu temp2, 0(str2)
> + sub result, temp1, temp2
> ret
>
> - /* temp1 < 0 */
> - /* temp2 >= 0 */
> -L(check2):
> - add str1, str1, temp2
> - add str2, str2, temp2
> - lbu temp1, 0(str1)
> - lbu temp2, 0(str2)
> - sub result, temp1, temp2
> +L(ret):
> + li result, 0
> ret
> +
> .option pop
> END (STRCMP)
> libc_hidden_builtin_def (strcmp)
> Limit vl by the minimum distance from the input pointers to the next 4K
> boundary, and use normal vle8.v loads in the main loop. This keeps the
> loads page-safe while avoiding the vl update dependency from vle8ff.v.
Doesn't this make the implementation incompatible with the Zimt (memory
tagging) extension? Or isn't this a problem today?
Thanks,
Florian
* Carlos O'Donell:
> On 6/14/26 6:55 AM, daichengrong wrote:
>> Limit vl by the minimum distance from the input pointers to the next 4K
>> boundary, and use normal vle8.v loads in the main loop. This keeps the
>> loads page-safe while avoiding the vl update dependency from vle8ff.v.
>
> Do you have microbenchmark data that supports this change?
>
> Generally for such changes we would like to see "make bench" showing a
> difference.
I think asking for performance data from unreleased CPUs is unfair.
I don't know if this applies in this instance, though.
Thanks,
Florian
@@ -35,59 +35,77 @@
#define vstr1 v0
#define vstr2 v8
#define vmask1 v16
-#define vmask2 v17
+#define vmask2 v24
ENTRY (STRCMP)
.option push
.option arch, +v
- /* lmul=1 */
-L(Loop):
- vsetvli ivl, zero, e8, m1, ta, ma
- vle8ff.v vstr1, (str1)
- /* Check if vstr1[i] == 0 */
- vmseq.vx vmask1, vstr1, zero
-
- vle8ff.v vstr2, (str2)
- /* Check if vstr1[i] != vstr2[i] */
- vmsne.vv vmask2, vstr1, vstr2
-
- /* Find the index x for vstr1[x] == 0 */
- vfirst.m temp1, vmask1
- /* Find the index x for vstr1[x] != vstr2[x] */
- vfirst.m temp2, vmask2
-
- bgez temp1, L(check1)
- bgez temp2, L(check2)
-
- /* Get the current vl updated by vle8ff. */
- csrr ivl, vl
- add str1, str1, ivl
- add str2, str2, ivl
- j L(Loop)
-
- /* temp1 >= 0 */
-L(check1):
- bltz temp2, L(return_at_nul)
- blt temp2, temp1, L(check2)
-L(return_at_nul):
- /* temp2 < 0 */
- /* temp2 >= 0 && temp1 < temp2 */
- add str1, str1, temp1
- add str2, str2, temp1
- lbu temp1, 0(str1)
- lbu temp2, 0(str2)
- sub result, temp1, temp2
+ li t0, 4095
+ li t3, 4096
+L(loop):
+ and t1, str1, t0 /* t1 = str1 offset in 4K page */
+ and t2, str2, t0 /* t2 = str2 offset in 4K page */
+ sub t4, t3, t1 /* t4 = bytes from str1 to page end */
+ sub t5, t3, t2 /* t5 = bytes from str2 to page end */
+ /*
+ * t3 = min(dist1, dist2)
+ */
+ bltu t5, t4, 1f
+ mv t5, t4
+1:
+ /*
+ * Set vl to min(dist, VLMAX).
+ *
+ * If far from page boundary, vl = VLMAX.
+ * If close to page boundary, vl = dist.
+ */
+
+ vsetvli ivl, t5, e8, m4, ta, ma
+ /*
+ * This load will not cross a 4K boundary for either pointer.
+ */
+ vle8.v vstr1, (str1)
+ vle8.v vstr2, (str2)
+
+ /*
+ * str1 == 0 means string ended.
+ * str1 != str2 means strcmp result is decided.
+ */
+ vmseq.vx vmask1, vstr1, zero
+ vmsne.vv vmask2, vstr1, vstr2
+
+ vfirst.m temp2, vmask2
+ vfirst.m temp1, vmask1
+
+ bgez temp2, L(check_nul)
+ bgez temp1, L(ret)
+
+ /*
+ * No NUL and no difference in this chunk.
+ * Advance by actual vl.
+ *
+ * If this was a page-boundary-limited load, one of str1/str2 now
+ * reaches the next 4K boundary. The next loop recomputes distance.
+ */
+ add str1, str1, ivl
+ add str2, str2, ivl
+ j L(loop)
+
+L(check_nul):
+ bltz temp1, L(found)
+ blt temp1, temp2, L(ret)
+L(found):
+ add str1, str1, temp2
+ add str2, str2, temp2
+ lbu temp1, 0(str1)
+ lbu temp2, 0(str2)
+ sub result, temp1, temp2
ret
- /* temp1 < 0 */
- /* temp2 >= 0 */
-L(check2):
- add str1, str1, temp2
- add str2, str2, temp2
- lbu temp1, 0(str1)
- lbu temp2, 0(str2)
- sub result, temp1, temp2
+L(ret):
+ li result, 0
ret
+
.option pop
END (STRCMP)
libc_hidden_builtin_def (strcmp)