From 413d2ddbc32abcde25ade549960809d6fcf4d7d3 Mon Sep 17 00:00:00 2001 From: zhangyuan29 Date: Wed, 8 Jul 2026 15:52:54 +0800 Subject: [PATCH] testing/libc/arch_libc: Cover unaligned src/dst copy paths. test_strcpy(), test_strncpy() and test_stpcpy() applied the same offset to the source and to the destination, so both pointers always shared the same word congruence. Architecture optimized copy routines take a different code path when the two offsets differ: a byte prologue to align the destination, then either a byte fallback or a shift-merge loop that recombines two source words per store. None of that was reached by the test. Vary the source and destination offsets independently over 0..7 in those three tests, so both the equal congruence (aligned word copy) and the unequal congruence (shift-merge) paths are covered, and report both offsets on failure so a regression points at the offending combination. Also drop the ARCH_TOOLCHAIN_GNU dependency from TESTING_ARCH_LIBC. The test only uses standard C string functions and perf_gettime(), with no GNU specific construct, so it builds with non GNU toolchains such as TASKING as well. Impact: test only, selected by CONFIG_TESTING_ARCH_LIBC (default n). Dropping the ARCH_TOOLCHAIN_GNU dependency only widens the set of toolchains that may select the test, no existing configuration changes. Testing: built and ran sim:nsh on Linux x86_64 (Ubuntu 24.04, gcc 13.3.0) with CONFIG_TESTING_ARCH_LIBC=y. strcpy, strncpy and stpcpy report PASSED for all 64 offset combinations, and "arch_libc_test Passed". Assisted-by: Claude:claude-opus-5 Signed-off-by: zhangyuan29 --- testing/libc/arch_libc/Kconfig | 1 - testing/libc/arch_libc/arch_libc_test_main.c | 86 +++++++++++++------- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/testing/libc/arch_libc/Kconfig b/testing/libc/arch_libc/Kconfig index 97885ea40..05ccacdd8 100644 --- a/testing/libc/arch_libc/Kconfig +++ b/testing/libc/arch_libc/Kconfig @@ -6,7 +6,6 @@ config TESTING_ARCH_LIBC tristate "arch-specific libc function test" default n - depends on ARCH_TOOLCHAIN_GNU ---help--- Enable the arch libc test diff --git a/testing/libc/arch_libc/arch_libc_test_main.c b/testing/libc/arch_libc/arch_libc_test_main.c index f1ff3dc05..1a5e8bbf1 100644 --- a/testing/libc/arch_libc/arch_libc_test_main.c +++ b/testing/libc/arch_libc/arch_libc_test_main.c @@ -618,22 +618,32 @@ static void speed_strcmp(void) #ifdef CONFIG_TESTING_ARCH_LIBC_STRCPY static int test_strcpy(void) { - int align; int size; int fail = 0; + int ai; printf("Testing strcpy...\n"); - for (align = 0; align < 8; align++) + + /* ai encodes da*8+sa: sa==da keeps src/dst in the same 4-byte congruence + * (aligned word-copy path), sa!=da forces different congruence and + * exercises the shift-merge path; any nonzero da also hits the dst byte + * prologue. + */ + + for (ai = 0; ai < 64; ai++) { + int da = ai / 8; + int sa = ai % 8; + for (size = 1; size <= 64; size++) { - fill_pattern(g_buf1 + align, size); - g_buf1[align + size] = '\0'; + fill_pattern(g_buf1 + sa, size); + g_buf1[sa + size] = '\0'; memset(g_buf2, 0, sizeof(g_buf2)); - strcpy(g_buf2 + align, g_buf1 + align); - if (strcmp(g_buf2 + align, g_buf1 + align) != 0) + strcpy(g_buf2 + da, g_buf1 + sa); + if (strcmp(g_buf2 + da, g_buf1 + sa) != 0) { - printf(" FAIL: align=%d size=%d\n", align, size); + printf(" FAIL: sa=%d da=%d size=%d\n", sa, da, size); fail++; } } @@ -946,26 +956,34 @@ static void speed_strnlen(void) #ifdef CONFIG_TESTING_ARCH_LIBC_STRNCPY static int test_strncpy(void) { - int align; int size; int i; int fail = 0; + int ai; printf("Testing strncpy...\n"); - for (align = 0; align < 8; align++) + + /* ai encodes da*8+sa so src/dst offsets vary independently: sa!=da forces + * different congruence and exercises the shift-merge path. + */ + + for (ai = 0; ai < 64; ai++) { + int da = ai / 8; + int sa = ai % 8; + for (size = 1; size <= 64; size++) { - fill_pattern(g_buf1 + align, size); - g_buf1[align + size] = '\0'; + fill_pattern(g_buf1 + sa, size); + g_buf1[sa + size] = '\0'; /* n > strlen: should copy and zero-fill */ memset(g_buf2, 0xaa, sizeof(g_buf2)); - strncpy(g_buf2 + align, g_buf1 + align, size + 4); - if (strcmp(g_buf2 + align, g_buf1 + align) != 0) + strncpy(g_buf2 + da, g_buf1 + sa, size + 4); + if (strcmp(g_buf2 + da, g_buf1 + sa) != 0) { - printf(" FAIL copy: align=%d size=%d\n", align, size); + printf(" FAIL copy: sa=%d da=%d size=%d\n", sa, da, size); fail++; } @@ -973,9 +991,10 @@ static int test_strncpy(void) for (i = 0; i < 4; i++) { - if (g_buf2[align + size + i] != '\0') + if (g_buf2[da + size + i] != '\0') { - printf(" FAIL zfill: align=%d size=%d\n", align, size); + printf(" FAIL zfill: sa=%d da=%d size=%d\n", + sa, da, size); fail++; break; } @@ -986,10 +1005,11 @@ static int test_strncpy(void) if (size > 1) { memset(g_buf2, 0xaa, sizeof(g_buf2)); - strncpy(g_buf2 + align, g_buf1 + align, size - 1); - if (memcmp(g_buf2 + align, g_buf1 + align, size - 1) != 0) + strncpy(g_buf2 + da, g_buf1 + sa, size - 1); + if (memcmp(g_buf2 + da, g_buf1 + sa, size - 1) != 0) { - printf(" FAIL trunc: align=%d size=%d\n", align, size); + printf(" FAIL trunc: sa=%d da=%d size=%d\n", + sa, da, size); fail++; } } @@ -1027,40 +1047,48 @@ static void speed_strncpy(void) #ifdef CONFIG_TESTING_ARCH_LIBC_STPCPY static int test_stpcpy(void) { - int align; int size; int fail = 0; + int ai; FAR char *p; printf("Testing stpcpy...\n"); - for (align = 0; align < 8; align++) + + /* ai encodes da*8+sa so src/dst offsets vary independently: sa!=da forces + * different congruence and exercises the shift-merge path. + */ + + for (ai = 0; ai < 64; ai++) { + int da = ai / 8; + int sa = ai % 8; + for (size = 1; size <= 64; size++) { - fill_pattern(g_buf1 + align, size); - g_buf1[align + size] = '\0'; + fill_pattern(g_buf1 + sa, size); + g_buf1[sa + size] = '\0'; memset(g_buf2, 0, sizeof(g_buf2)); - p = stpcpy(g_buf2 + align, g_buf1 + align); + p = stpcpy(g_buf2 + da, g_buf1 + sa); /* Check content */ - if (strcmp(g_buf2 + align, g_buf1 + align) != 0) + if (strcmp(g_buf2 + da, g_buf1 + sa) != 0) { - printf(" FAIL copy: align=%d size=%d\n", align, size); + printf(" FAIL copy: sa=%d da=%d size=%d\n", sa, da, size); fail++; } /* Check return value points to NUL */ - if (p != g_buf2 + align + size) + if (p != g_buf2 + da + size) { - printf(" FAIL retval: align=%d size=%d\n", align, size); + printf(" FAIL retval: sa=%d da=%d size=%d\n", sa, da, size); fail++; } if (*p != '\0') { - printf(" FAIL nul: align=%d size=%d\n", align, size); + printf(" FAIL nul: sa=%d da=%d size=%d\n", sa, da, size); fail++; } }