| // 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"; | |
| } | |
| } |