Implement C standard function strlen as RISC-V.
Prototype: size_t rvv_strlen(const char* str)
Requirements:
Return the length of the given null-terminated byte string, that is, the number of characters in a character array whose first element is pointed to by str up to and not including the first null character. You don't have to handle illegal input.
Special Requirement:
You should use Vector Extension. The test is trying to check whether you really use Vector Extension.
As of October 2023, Codewars RISC-V RV64 supports Vector Extension 1.0 with VLEN = 128
.
.section .text
.global rvv_strlen
# size_t rvv_strlen(const char* str)
rvv_strlen:
mv a1, a0
li a0, 0
.Loop:
vsetvli t0, zero, e8, m8, ta, ma
vle8ff.v v16, (a1)
vmseq.vi v1, v16, 0
vfirst.m t0, v1
vmsbf.m v0, v1
vcpop.m t1, v0
add a0, a0, t1
add a1, a1, t1
bltz t0, .Loop
ret
// Tests for RISC-V are written in C with Cgreen.
// See <https://cgreen-devs.github.io/cgreen/cgreen-guide-en.html>.
#include <cgreen/cgreen.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/mman.h>
size_t rvv_strlen(const char* str);
// `Describe`, `BeforeEach`, and `AfterEach` are required.
Describe(Example);
BeforeEach(Example) {}
AfterEach(Example) {}
Ensure(Example, works_for_fixed_tests) {
assert_that(rvv_strlen("Hello"), is_equal_to(5));
assert_that(rvv_strlen("This\0is\0fake"), is_equal_to(4));
assert_that(rvv_strlen("Hello, RISC-V vector world!"), is_equal_to(27));
assert_that(rvv_strlen(""), is_equal_to(0));
}
Ensure(Example, works_for_random_tests) {
char buf[1024];
// use fixed seed to generate test case consistently
srand(36);
for (int i = 0; i < 100; i++) {
int len = rand() % 900 + 10;
for (int j = 0; j < len; j++) {
buf[j] = rand() % 94 + 32;
}
buf[len] = 0;
assert_that(rvv_strlen(buf), is_equal_to(len));
}
}
Ensure(Example, works_near_page_bound) {
long page_size = sysconf(_SC_PAGE_SIZE);
char *p = mmap(0, page_size * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
munmap(p + page_size, page_size);
p[page_size - 4] = 'H';
p[page_size - 3] = 'i';
p[page_size - 2] = '!';
p[page_size - 1] = 0;
assert_that(rvv_strlen(p + page_size - 4), is_equal_to(3));
munmap(p, page_size);
}
Ensure(Example, uses_vector) {
int64_t vtype;
asm __volatile__ (".word 0b11111111111111111111000001010111"); // vtype = "invalid"
rvv_strlen("Hello, RISC-V vector world!");
asm __volatile__ ("csrr %0, 0xc21" : "=r" (vtype));
// if rvv_strlen uses vector, vtype will hold valid value
assert_true_with_message(vtype >= 0, "it seems that the program didn't use Vector Extension");
}
// `solution_tests` to create a test suite is required.
TestSuite *solution_tests() {
TestSuite *suite = create_test_suite();
add_test_with_context(suite, Example, works_for_fixed_tests);
add_test_with_context(suite, Example, works_near_page_bound);
add_test_with_context(suite, Example, uses_vector);
add_test_with_context(suite, Example, works_for_random_tests);
return suite;
}
The original code used 32-bit system call.
I refactored the code to use 64-bit system call.
global say_hello section .text say_hello: mov rax, 1 ; system call for write (for Linux 64) mov rdi, 1 ; file handle 1 is STDOUT lea rsi, [rel message] ; memory address of output buffer mov rdx, msgLen ; size of output buffer in bytes syscall ; invoke OS to do the write ret ; Return to the caller section .data ; Read-only data message db "Hello World!", 10 ; message = "Hello World!\n" msgLen equ $-message ; msgLen = strlen(message)
- global say_hello
- section .text
- say_hello:
mov eax, 4 ; system call for write (for Linux)mov ebx, 1 ; file handle 1 is STDOUTmov ecx, message ; memory address of output buffermov edx, msgLen ; size of output buffer in bytesint 0x80 ; invoke OS to do the write- mov rax, 1 ; system call for write (for Linux 64)
- mov rdi, 1 ; file handle 1 is STDOUT
- lea rsi, [rel message] ; memory address of output buffer
- mov rdx, msgLen ; size of output buffer in bytes
- syscall ; invoke OS to do the write
- ret ; Return to the caller
- section .data ; Read-only data
- message db "Hello World!", 10 ; message = "Hello World!\n"
- msgLen equ $-message ; msgLen = strlen(message)