| // Copyright 2026 The IREE Authors |
| // |
| // Licensed under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| // String and memory functions for wasm32. |
| // |
| // Clang lowers many visible memory operations directly to wasm bulk-memory |
| // instructions (memory.copy, memory.fill) when compiled with -mbulk-memory. |
| // Out-of-line libc symbols are still required for calls that survive to link |
| // time. |
| |
| #include <errno.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| //===----------------------------------------------------------------------===// |
| // Memory comparison and search |
| //===----------------------------------------------------------------------===// |
| |
| void* memcpy(void* dest, const void* src, size_t count) { |
| unsigned char* d = (unsigned char*)dest; |
| const unsigned char* s = (const unsigned char*)src; |
| for (size_t i = 0; i < count; ++i) { |
| d[i] = s[i]; |
| } |
| return dest; |
| } |
| |
| void* memmove(void* dest, const void* src, size_t count) { |
| unsigned char* d = (unsigned char*)dest; |
| const unsigned char* s = (const unsigned char*)src; |
| if (d == s || count == 0) return dest; |
| if (d < s) { |
| for (size_t i = 0; i < count; ++i) { |
| d[i] = s[i]; |
| } |
| } else { |
| for (size_t i = count; i > 0; --i) { |
| d[i - 1] = s[i - 1]; |
| } |
| } |
| return dest; |
| } |
| |
| void* memset(void* dest, int value, size_t count) { |
| unsigned char* d = (unsigned char*)dest; |
| unsigned char v = (unsigned char)value; |
| for (size_t i = 0; i < count; ++i) { |
| d[i] = v; |
| } |
| return dest; |
| } |
| |
| int memcmp(const void* left, const void* right, size_t count) { |
| const unsigned char* a = (const unsigned char*)left; |
| const unsigned char* b = (const unsigned char*)right; |
| for (size_t i = 0; i < count; i++) { |
| if (a[i] != b[i]) return a[i] < b[i] ? -1 : 1; |
| } |
| return 0; |
| } |
| |
| void* memchr(const void* pointer, int value, size_t count) { |
| const unsigned char* p = (const unsigned char*)pointer; |
| unsigned char v = (unsigned char)value; |
| for (size_t i = 0; i < count; i++) { |
| if (p[i] == v) return (void*)(p + i); |
| } |
| return NULL; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // String length |
| //===----------------------------------------------------------------------===// |
| |
| size_t strlen(const char* string) { |
| const char* p = string; |
| while (*p) ++p; |
| return (size_t)(p - string); |
| } |
| |
| size_t strnlen(const char* string, size_t max_length) { |
| const char* p = string; |
| while (max_length-- && *p) ++p; |
| return (size_t)(p - string); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // String comparison |
| //===----------------------------------------------------------------------===// |
| |
| int strcmp(const char* left, const char* right) { |
| const unsigned char* a = (const unsigned char*)left; |
| const unsigned char* b = (const unsigned char*)right; |
| while (*a && *a == *b) { |
| ++a; |
| ++b; |
| } |
| return (int)*a - (int)*b; |
| } |
| |
| int strncmp(const char* left, const char* right, size_t count) { |
| const unsigned char* a = (const unsigned char*)left; |
| const unsigned char* b = (const unsigned char*)right; |
| while (count && *a && *a == *b) { |
| ++a; |
| ++b; |
| --count; |
| } |
| if (count == 0) return 0; |
| return (int)*a - (int)*b; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // String search |
| //===----------------------------------------------------------------------===// |
| |
| char* strchr(const char* string, int character) { |
| char c = (char)character; |
| while (*string) { |
| if (*string == c) return (char*)string; |
| ++string; |
| } |
| // strchr finds the null terminator when c == '\0'. |
| return c == '\0' ? (char*)string : NULL; |
| } |
| |
| char* strrchr(const char* string, int character) { |
| char c = (char)character; |
| const char* last = NULL; |
| while (*string) { |
| if (*string == c) last = string; |
| ++string; |
| } |
| if (c == '\0') return (char*)string; |
| return (char*)last; |
| } |
| |
| char* strstr(const char* haystack, const char* needle) { |
| if (!*needle) return (char*)haystack; |
| |
| for (; *haystack; ++haystack) { |
| const char* h = haystack; |
| const char* n = needle; |
| while (*h && *n && *h == *n) { |
| ++h; |
| ++n; |
| } |
| if (!*n) return (char*)haystack; |
| } |
| return NULL; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // String copying |
| //===----------------------------------------------------------------------===// |
| |
| char* strcpy(char* dest, const char* src) { |
| char* d = dest; |
| while ((*d++ = *src++)) { |
| } |
| return dest; |
| } |
| |
| char* strncpy(char* dest, const char* src, size_t count) { |
| char* d = dest; |
| while (count && (*d = *src)) { |
| ++d; |
| ++src; |
| --count; |
| } |
| // Pad remaining bytes with null. |
| while (count--) *d++ = '\0'; |
| return dest; |
| } |
| |
| char* strcat(char* dest, const char* src) { |
| char* d = dest; |
| while (*d) ++d; |
| while ((*d++ = *src++)) { |
| } |
| return dest; |
| } |
| |
| char* strncat(char* dest, const char* src, size_t count) { |
| char* d = dest; |
| while (*d) ++d; |
| while (count-- && (*d = *src)) { |
| ++d; |
| ++src; |
| } |
| *d = '\0'; |
| return dest; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Error string |
| //===----------------------------------------------------------------------===// |
| |
| // Minimal strerror — returns the error name rather than a human-readable |
| // message. IREE uses iree_status_code_string() for its own error reporting; |
| // this is only needed for code that calls strerror directly. |
| char* strerror(int error_number) { |
| switch (error_number) { |
| case 0: |
| return (char*)"Success"; |
| case EPERM: |
| return (char*)"EPERM"; |
| case ENOENT: |
| return (char*)"ENOENT"; |
| case EIO: |
| return (char*)"EIO"; |
| case EBADF: |
| return (char*)"EBADF"; |
| case ENOMEM: |
| return (char*)"ENOMEM"; |
| case EACCES: |
| return (char*)"EACCES"; |
| case EEXIST: |
| return (char*)"EEXIST"; |
| case EINVAL: |
| return (char*)"EINVAL"; |
| case ENOSYS: |
| return (char*)"ENOSYS"; |
| case ERANGE: |
| return (char*)"ERANGE"; |
| case EOVERFLOW: |
| return (char*)"EOVERFLOW"; |
| case ENOTSUP: |
| return (char*)"ENOTSUP"; |
| case ETIMEDOUT: |
| return (char*)"ETIMEDOUT"; |
| case ECANCELED: |
| return (char*)"ECANCELED"; |
| default: |
| return (char*)"Unknown error"; |
| } |
| } |