Add coverage for the MSA LD/ST.B/H/W/D instructions at every source and destination byte offset within a vector-sized block, including accesses that cross a page boundary. Verify per-element endian ordering, destination contents, and source register preservation.
Test both little-endian (P5600, MIPS32R5) and big-endian (I6400, MIPS64R6). AI-used-for: tests Signed-off-by: Philippe Mathieu-Daudé <[email protected]> --- Since this is a TCG test ran under linux-user, ignore checkpatch: ERROR: use qemu_real_host_page_size() instead of sysconf(_SC_PAGESIZE) #138: FILE: tests/tcg/mips/test_msa_load_store.c:110: + long page_size = sysconf(_SC_PAGESIZE); --- tests/tcg/mips/test_msa_load_store.c | 166 +++++++++++++++++++++++++++ tests/tcg/mips64/meson.build | 1 + tests/tcg/mipsel/meson.build | 1 + 3 files changed, 168 insertions(+) create mode 100644 tests/tcg/mips/test_msa_load_store.c diff --git a/tests/tcg/mips/test_msa_load_store.c b/tests/tcg/mips/test_msa_load_store.c new file mode 100644 index 000000000000..50e60bb44ff5 --- /dev/null +++ b/tests/tcg/mips/test_msa_load_store.c @@ -0,0 +1,166 @@ +/* + * MIPS MSA tests: Load / Store instructions + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> + +enum { + VECTOR_BYTES = 16, + BUFFER_BYTES = 64, +}; + +#define DEFINE_MSA_COPY(format) \ +static void copy_##format(const uint8_t *source, uint8_t *destination, \ + uint8_t *loaded, uint8_t *after_store) \ +{ \ + __asm__ volatile( \ + "ld." #format " $w0, 0(%[source])\n\t" \ + "st.b $w0, 0(%[loaded])\n\t" \ + "st." #format " $w0, 0(%[destination])\n\t" \ + "st.b $w0, 0(%[after_store])\n\t" \ + : \ + : [source] "r" (source), [destination] "r" (destination), \ + [loaded] "r" (loaded), [after_store] "r" (after_store) \ + : "f0", "memory"); \ +} + +DEFINE_MSA_COPY(b) +DEFINE_MSA_COPY(h) +DEFINE_MSA_COPY(w) +DEFINE_MSA_COPY(d) + +typedef void (*MsaCopy)(const uint8_t *source, uint8_t *destination, + uint8_t *loaded, uint8_t *after_store); + +static const struct { + const char *name; + unsigned element_bytes; + MsaCopy copy; +} formats[] = { + { "b", 1, copy_b }, + { "h", 2, copy_h }, + { "w", 4, copy_w }, + { "d", 8, copy_d }, +}; + +static bool check_format(size_t format_index, const uint8_t *source, + uint8_t *destination, size_t first_offset, + const char *region) +{ + _Alignas(VECTOR_BYTES) uint8_t loaded[VECTOR_BYTES]; + _Alignas(VECTOR_BYTES) uint8_t after_store[VECTOR_BYTES]; + uint8_t expected_vector[VECTOR_BYTES]; + uint8_t expected_memory[BUFFER_BYTES]; + const uint16_t endian_probe = 1; + unsigned endian_mask = *(const uint8_t *)&endian_probe ? 0 : + formats[format_index].element_bytes - 1; + + for (size_t source_offset = first_offset; + source_offset < first_offset + VECTOR_BYTES; source_offset++) { + for (size_t destination_offset = first_offset; + destination_offset < first_offset + VECTOR_BYTES; + destination_offset++) { + memset(destination, 0xa5, BUFFER_BYTES); + memset(expected_memory, 0xa5, BUFFER_BYTES); + memcpy(expected_memory + destination_offset, + source + source_offset, VECTOR_BYTES); + + for (size_t byte = 0; byte < VECTOR_BYTES; byte++) { + expected_vector[byte] = + source[source_offset + (byte ^ endian_mask)]; + } + + formats[format_index].copy(source + source_offset, + destination + destination_offset, + loaded, after_store); + + if (memcmp(loaded, expected_vector, VECTOR_BYTES)) { + fprintf(stderr, "ld.%s endian mismatch (%s, offset %zu)\n", + formats[format_index].name, region, source_offset); + return false; + } + if (memcmp(destination, expected_memory, BUFFER_BYTES)) { + fprintf(stderr, "st.%s memory mismatch (%s, offset %zu)\n", + formats[format_index].name, region, + destination_offset); + return false; + } + if (memcmp(after_store, loaded, VECTOR_BYTES)) { + fprintf(stderr, "st.%s changed its source register (%s)\n", + formats[format_index].name, region); + return false; + } + } + } + + return true; +} + +int main(void) +{ + _Alignas(VECTOR_BYTES) uint8_t source[BUFFER_BYTES]; + _Alignas(VECTOR_BYTES) uint8_t destination[BUFFER_BYTES]; + long page_size = sysconf(_SC_PAGESIZE); + size_t mapping_bytes; + uint8_t *source_mapping; + uint8_t *destination_mapping; + uint8_t *page_source; + uint8_t *page_destination; + bool success = true; + + if (page_size < BUFFER_BYTES) { + fprintf(stderr, "Invalid guest page size: %ld\n", page_size); + return 1; + } + mapping_bytes = 4 * (size_t)page_size; + source_mapping = mmap(NULL, mapping_bytes, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + destination_mapping = mmap(NULL, mapping_bytes, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (source_mapping == MAP_FAILED || destination_mapping == MAP_FAILED) { + perror("mmap"); + return 1; + } + if (mprotect(source_mapping + page_size, 2 * page_size, + PROT_READ | PROT_WRITE) != 0 || + mprotect(destination_mapping + page_size, 2 * page_size, + PROT_READ | PROT_WRITE) != 0) { + perror("mprotect"); + return 1; + } + + page_source = source_mapping + 2 * page_size - BUFFER_BYTES / 2; + page_destination = destination_mapping + 2 * page_size - BUFFER_BYTES / 2; + for (size_t byte = 0; byte < BUFFER_BYTES; byte++) { + source[byte] = page_source[byte] = byte * 13 + 7; + } + + printf("TAP version 14\n" + "1..%zu\n", + sizeof(formats) / sizeof(formats[0])); + for (size_t format_index = 0; + format_index < sizeof(formats) / sizeof(formats[0]); format_index++) { + bool passed = check_format(format_index, source, destination, 0, + "buffer") && + check_format(format_index, page_source, page_destination, + VECTOR_BYTES, "page boundary"); + + printf("%s %zu - MSA ld/st.%s\n", passed ? "ok" : "not ok", + format_index + 1, formats[format_index].name); + success &= passed; + } + + if (munmap(source_mapping, mapping_bytes) != 0 || + munmap(destination_mapping, mapping_bytes) != 0) { + perror("munmap"); + return 1; + } + return success ? 0 : 1; +} diff --git a/tests/tcg/mips64/meson.build b/tests/tcg/mips64/meson.build index d1d4bf6b5a24..f2e507a47d88 100644 --- a/tests/tcg/mips64/meson.build +++ b/tests/tcg/mips64/meson.build @@ -54,6 +54,7 @@ tests_mips_msa_groups = [ '../mips/test_msa_int_multiply.c', '../mips/test_msa_int_subtract.c', '../mips/test_msa_interleave.c', + '../mips/test_msa_load_store.c', '../mips/test_msa_logic.c', '../mips/test_msa_pack.c', '../mips/test_msa_shift.c', diff --git a/tests/tcg/mipsel/meson.build b/tests/tcg/mipsel/meson.build index fcf764ea8df6..f2642e1b20f8 100644 --- a/tests/tcg/mipsel/meson.build +++ b/tests/tcg/mipsel/meson.build @@ -55,6 +55,7 @@ tests_mips_msa_groups = [ '../mips/test_msa_int_multiply.c', '../mips/test_msa_int_subtract.c', '../mips/test_msa_interleave.c', + '../mips/test_msa_load_store.c', '../mips/test_msa_logic.c', '../mips/test_msa_pack.c', '../mips/test_msa_shift.c', -- 2.55.0
