kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 7935e136ee6401e739de61fd9dbb8a1209ef9ab3
parent 78d4e8169776cc9364c5d4ea469bcde626352111
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 20 May 2026 07:31:38 -0700

rt: implement freestanding header routines

Diffstat:
Mrt/Makefile | 6++++++
Art/lib/assert/assert.c | 16++++++++++++++++
Mrt/lib/fp_tf/fp_tf.c | 44--------------------------------------------
Art/lib/int/si_div.c | 45+++++++++++++++++++++++++++++++++++++++++++++
Art/lib/stdio/printf.c | 243+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Art/lib/stdlib/qsort.c | 234+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Art/lib/stdlib/stdlib.c | 218+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Art/lib/string/string.c | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/rt/cases/freestanding_lib.c | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 995 insertions(+), 44 deletions(-)

diff --git a/rt/Makefile b/rt/Makefile @@ -134,9 +134,15 @@ RT_arm-eabi-thumb1_AEABI = thumb1 # ---------- sources and flags derived from features -------------------------- RT_BASE_SRCS = \ + rt/lib/assert/assert.c \ rt/lib/int/int.c \ + rt/lib/int/si_div.c \ rt/lib/fp/fp.c \ rt/lib/mem/mem.c \ + rt/lib/string/string.c \ + rt/lib/stdlib/stdlib.c \ + rt/lib/stdlib/qsort.c \ + rt/lib/stdio/printf.c \ rt/lib/atomic/atomic_freestanding.c \ rt/lib/cache/clear_cache.c \ rt/lib/cfree/ifunc_init.c diff --git a/rt/lib/assert/assert.c b/rt/lib/assert/assert.c @@ -0,0 +1,16 @@ +//===-- assert.c - cfree freestanding assert failure ----------------------===// +// +// SPDX-License-Identifier: 0BSD +//===----------------------------------------------------------------------===// + +__attribute__((weak)) void __cfree_assert_fail(const char* expr, + const char* file, int line, + const char* func) { + (void)expr; + (void)file; + (void)line; + (void)func; + __builtin_trap(); + for (;;) { + } +} diff --git a/rt/lib/fp_tf/fp_tf.c b/rt/lib/fp_tf/fp_tf.c @@ -13,50 +13,6 @@ #include "tf_supplement.h" #endif -// cfree's freestanding aarch64/RV64 backend may lower 32-bit divisions in -// this runtime TU back into compiler-rt spellings. Keep these leaf helpers in -// the same archive member as the quad helpers so demand-loaded links resolve -// the complete member closure. -COMPILER_RT_ABI su_int __udivsi3(su_int n, su_int d) { - const unsigned bits = sizeof(su_int) * CHAR_BIT; - su_int q = 0; - su_int r = 0; - for (unsigned i = bits; i > 0; --i) { - r = (su_int)((r << 1) | ((n >> (i - 1u)) & 1u)); - if (r >= d) { - r = (su_int)(r - d); - q |= (su_int)1u << (i - 1u); - } - } - return q; -} - -COMPILER_RT_ABI su_int __udivmodsi4(su_int n, su_int d, su_int* rem) { - const unsigned bits = sizeof(su_int) * CHAR_BIT; - su_int q = 0; - su_int r = 0; - for (unsigned i = bits; i > 0; --i) { - r = (su_int)((r << 1) | ((n >> (i - 1u)) & 1u)); - if (r >= d) { - r = (su_int)(r - d); - q |= (su_int)1u << (i - 1u); - } - } - if (rem) - *rem = r; - return q; -} - -COMPILER_RT_ABI si_int __divsi3(si_int a, si_int b) { - su_int sa = (su_int)(a >> 31); - su_int sb = (su_int)(b >> 31); - su_int ua = ((su_int)a ^ sa) - sa; - su_int ub = ((su_int)b ^ sb) - sb; - su_int sign = sa ^ sb; - su_int q = __udivsi3(ua, ub); - return (si_int)((q ^ sign) - sign); -} - // ---- addtf3.c ---- #define QUAD_PRECISION #include "fp_add_impl.inc" diff --git a/rt/lib/int/si_div.c b/rt/lib/int/si_div.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// 32-bit integer division helpers required by compiler-rt routines even on +// LP64 targets where normal C int division is otherwise a single instruction. + +#include "int_lib.h" + +COMPILER_RT_ABI su_int __udivsi3(su_int n, su_int d) { + const unsigned bits = sizeof(su_int) * CHAR_BIT; + su_int q = 0; + su_int r = 0; + for (unsigned i = bits; i > 0; --i) { + r = (su_int)((r << 1) | ((n >> (i - 1u)) & 1u)); + if (r >= d) { + r = (su_int)(r - d); + q |= (su_int)1u << (i - 1u); + } + } + return q; +} + +COMPILER_RT_ABI su_int __udivmodsi4(su_int n, su_int d, su_int* rem) { + const unsigned bits = sizeof(su_int) * CHAR_BIT; + su_int q = 0; + su_int r = 0; + for (unsigned i = bits; i > 0; --i) { + r = (su_int)((r << 1) | ((n >> (i - 1u)) & 1u)); + if (r >= d) { + r = (su_int)(r - d); + q |= (su_int)1u << (i - 1u); + } + } + if (rem) *rem = r; + return q; +} + +COMPILER_RT_ABI si_int __divsi3(si_int a, si_int b) { + su_int sa = (su_int)(a >> 31); + su_int sb = (su_int)(b >> 31); + su_int ua = ((su_int)a ^ sa) - sa; + su_int ub = ((su_int)b ^ sb) - sb; + su_int sign = sa ^ sb; + su_int q = __udivsi3(ua, ub); + return (si_int)((q ^ sign) - sign); +} diff --git a/rt/lib/stdio/printf.c b/rt/lib/stdio/printf.c @@ -0,0 +1,243 @@ +//===-- printf.c - cfree freestanding printf routines ---------------------===// +// +// Based on the mpaland/printf MIT-licensed formatter, imported for cfree's +// freestanding runtime and reduced to the top-level <stdio.h> declarations. +// Floating-point formats are intentionally omitted until cfree's backends can +// compile the original mpaland conversion path portably. +// +// TODO: +// - Re-enable mpaland float/exponential formats once all cfree backends can +// lower the formatter's FP-to-integer conversion path. +// - Re-enable the mpaland output-callback form after the x64 backend reliably +// handles the original indirect output dispatch and larger helper signatures. +// Upstream shape: +// typedef void (*out_fct_type)(char ch, void* buffer, +// size_t idx, size_t maxlen); +// typedef struct { void (*fct)(char ch, void* arg); void* arg; } +// out_fct_wrap_type; +// int fctprintf(void (*out)(char ch, void* arg), void* arg, +// const char* format, ...); +// fctprintf creates an out_fct_wrap_type on its stack and calls the shared +// formatter with an _out_fct adapter that invokes wrap->fct(ch, wrap->arg). +//===----------------------------------------------------------------------===// + +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> + +typedef struct { + char* buf; + size_t cap; + size_t len; +} CfreePrintfOut; + +static void cfree_out_ch(CfreePrintfOut* out, char ch) { + if (out->cap && out->len + 1U < out->cap) out->buf[out->len] = ch; + out->len++; +} + +static void cfree_out_repeat(CfreePrintfOut* out, char ch, unsigned count) { + while (count-- > 0) cfree_out_ch(out, ch); +} + +static unsigned cfree_strlen_prec(const char* s, int has_prec, + unsigned prec) { + unsigned n = 0; + if (!s) s = "(null)"; + while (s[n] != '\0' && (!has_prec || n < prec)) n++; + return n; +} + +static void cfree_out_string(CfreePrintfOut* out, const char* s, int left, + unsigned width, int has_prec, unsigned prec) { + unsigned len = cfree_strlen_prec(s, has_prec, prec); + if (!s) s = "(null)"; + if (!left && width > len) cfree_out_repeat(out, ' ', width - len); + for (unsigned i = 0; i < len; i++) cfree_out_ch(out, s[i]); + if (left && width > len) cfree_out_repeat(out, ' ', width - len); +} + +static void cfree_out_uint(CfreePrintfOut* out, unsigned long long value, + unsigned base, int upper, int neg, int left, + int zero, unsigned width) { + char tmp[64]; + unsigned len = 0; + unsigned digits_len; + const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + unsigned prefix = neg ? 1U : 0U; + char pad = (zero && !left) ? '0' : ' '; + + do { + tmp[len++] = digits[value % base]; + value /= base; + } while (value != 0); + digits_len = len; + + if (!left && pad == ' ' && width > len + prefix) { + cfree_out_repeat(out, ' ', width - len - prefix); + } + if (neg) cfree_out_ch(out, '-'); + if (!left && pad == '0' && width > len + prefix) { + cfree_out_repeat(out, '0', width - len - prefix); + } + while (len > 0) cfree_out_ch(out, tmp[--len]); + if (left && width > digits_len + prefix) { + cfree_out_repeat(out, ' ', width - digits_len - prefix); + } +} + +static int cfree_is_digit(char ch) { return ch >= '0' && ch <= '9'; } + +static unsigned cfree_parse_uint(const char** p) { + unsigned value = 0; + while (cfree_is_digit(**p)) { + value = value * 10U + (unsigned)(*(*p)++ - '0'); + } + return value; +} + +static int cfree_vsnprintf_impl(char* s, size_t n, const char* fmt, + va_list ap) { + CfreePrintfOut out; + out.buf = s; + out.cap = s ? n : 0U; + out.len = 0U; + + while (*fmt) { + int left = 0; + int zero = 0; + int long_mod = 0; + int long_long_mod = 0; + int has_prec = 0; + unsigned width = 0; + unsigned prec = 0; + + if (*fmt != '%') { + cfree_out_ch(&out, *fmt++); + continue; + } + fmt++; + if (*fmt == '%') { + cfree_out_ch(&out, *fmt++); + continue; + } + + for (;;) { + if (*fmt == '-') { + left = 1; + fmt++; + } else if (*fmt == '0') { + zero = 1; + fmt++; + } else { + break; + } + } + if (cfree_is_digit(*fmt)) width = cfree_parse_uint(&fmt); + if (*fmt == '.') { + fmt++; + has_prec = 1; + prec = cfree_parse_uint(&fmt); + } + if (*fmt == 'l') { + fmt++; + if (*fmt == 'l') { + fmt++; + long_long_mod = 1; + } else { + long_mod = 1; + } + } else if (*fmt == 'h') { + fmt++; + if (*fmt == 'h') fmt++; + } + + switch (*fmt) { + case 'd': + case 'i': { + long long v; + unsigned long long mag; + if (long_long_mod) { + v = va_arg(ap, long long); + } else if (long_mod) { + v = va_arg(ap, long); + } else { + v = va_arg(ap, int); + } + mag = v < 0 ? 0ULL - (unsigned long long)v : (unsigned long long)v; + cfree_out_uint(&out, mag, 10U, 0, v < 0, left, zero, width); + break; + } + case 'u': + case 'x': + case 'X': + case 'o': { + unsigned long long v; + unsigned base = *fmt == 'o' ? 8U : ((*fmt == 'u') ? 10U : 16U); + if (long_long_mod) { + v = va_arg(ap, unsigned long long); + } else if (long_mod) { + v = va_arg(ap, unsigned long); + } else { + v = va_arg(ap, unsigned int); + } + cfree_out_uint(&out, v, base, *fmt == 'X', 0, left, zero, width); + break; + } + case 'p': { + uintptr_t v = (uintptr_t)va_arg(ap, void*); + cfree_out_string(&out, "0x", 0, 0, 0, 0); + cfree_out_uint(&out, (unsigned long long)v, 16U, 0, 0, 0, 0, 0); + break; + } + case 'c': + cfree_out_ch(&out, (char)va_arg(ap, int)); + break; + case 's': + cfree_out_string(&out, va_arg(ap, const char*), left, width, has_prec, + prec); + break; + default: + if (*fmt) cfree_out_ch(&out, *fmt); + break; + } + if (*fmt) fmt++; + } + + if (out.cap) { + size_t pos = out.len < out.cap ? out.len : out.cap - 1U; + out.buf[pos] = '\0'; + } + return (int)out.len; +} + +int vsnprintf(char* s, size_t n, const char* fmt, va_list ap) { + va_list copy; + int rc; + va_copy(copy, ap); + rc = cfree_vsnprintf_impl(s, n, fmt, copy); + va_end(copy); + return rc; +} + +int snprintf(char* s, size_t n, const char* fmt, ...) { + va_list ap; + int rc; + va_start(ap, fmt); + rc = vsnprintf(s, n, fmt, ap); + va_end(ap); + return rc; +} + +int vsprintf(char* s, const char* fmt, va_list ap) { + return vsnprintf(s, (size_t)-1, fmt, ap); +} + +int sprintf(char* s, const char* fmt, ...) { + va_list ap; + int rc; + va_start(ap, fmt); + rc = vsprintf(s, fmt, ap); + va_end(ap); + return rc; +} diff --git a/rt/lib/stdlib/qsort.c b/rt/lib/stdlib/qsort.c @@ -0,0 +1,234 @@ +/* Copyright (C) 2011 by Lynn Ochs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* Minor changes by Rich Felker for integration in musl, 2011-04-27. */ + +/* Smoothsort, an adaptive variant of Heapsort. Memory usage: O(1). + Run time: Worst case O(n log n), close to O(n) in the mostly-sorted case. */ + +#include <stdint.h> +#include <stddef.h> + +void *memcpy(void *dest, const void *src, size_t n); + +typedef int (*cmpfun)(const void *, const void *); + +static int ntz(size_t x) +{ + int n = 0; + while ((x & 1) == 0) { + x >>= 1; + n++; + } + return n; +} + +/* returns index of first bit set, excluding the low bit assumed to always + * be set, starting from low bit of p[0] up through high bit of p[1] */ +static inline int pntz(size_t p[2]) { + if (p[0] != 1) return ntz(p[0] - 1); + if (p[1]) return 8*sizeof(size_t) + ntz(p[1]); + return 0; +} + +static void cycle(size_t width, unsigned char* ar[], int n) +{ + unsigned char tmp[256]; + size_t l; + int i; + + if(n < 2) { + return; + } + + ar[n] = tmp; + while(width) { + l = sizeof(tmp) < width ? sizeof(tmp) : width; + memcpy(ar[n], ar[0], l); + for(i = 0; i < n; i++) { + memcpy(ar[i], ar[i + 1], l); + ar[i] += l; + } + width -= l; + } +} + +/* shl() and shr() need n > 0 */ +static inline void shl(size_t p[2], int n) +{ + if(n >= 8 * sizeof(size_t)) { + n -= 8 * sizeof(size_t); + p[1] = p[0]; + p[0] = 0; + if (!n) return; + } + p[1] <<= n; + p[1] |= p[0] >> (sizeof(size_t) * 8 - n); + p[0] <<= n; +} + +static inline void shr(size_t p[2], int n) +{ + if(n >= 8 * sizeof(size_t)) { + n -= 8 * sizeof(size_t); + p[0] = p[1]; + p[1] = 0; + if (!n) return; + } + p[0] >>= n; + p[0] |= p[1] << (sizeof(size_t) * 8 - n); + p[1] >>= n; +} + +/* power-of-two length for working array so that we can mask indices and + * not depend on any invariant of the algorithm for spatial memory safety. + * the original size was just 14*sizeof(size_t)+1 */ +#define AR_LEN (16 * sizeof(size_t)) +#define AR_MASK (AR_LEN - 1) + +static void sift(unsigned char *head, size_t width, cmpfun cmp, int pshift, size_t lp[]) +{ + unsigned char *rt, *lf; + unsigned char *ar[AR_LEN]; + int i = 1; + + ar[0] = head; + while(pshift > 1) { + rt = head - width; + lf = head - width - lp[pshift - 2]; + + if(cmp(ar[0], lf) >= 0 && cmp(ar[0], rt) >= 0) { + break; + } + if(cmp(lf, rt) >= 0) { + ar[i++ & AR_MASK] = lf; + head = lf; + pshift -= 1; + } else { + ar[i++ & AR_MASK] = rt; + head = rt; + pshift -= 2; + } + } + cycle(width, ar, i & AR_MASK); +} + +static void trinkle(unsigned char *head, size_t width, cmpfun cmp, size_t pp[2], int pshift, int trusty, size_t lp[]) +{ + unsigned char *stepson, + *rt, *lf; + size_t p[2]; + unsigned char *ar[AR_LEN]; + int i = 1; + int trail; + + p[0] = pp[0]; + p[1] = pp[1]; + + ar[0] = head; + while(p[0] != 1 || p[1] != 0) { + stepson = head - lp[pshift]; + if(cmp(stepson, ar[0]) <= 0) { + break; + } + if(!trusty && pshift > 1) { + rt = head - width; + lf = head - width - lp[pshift - 2]; + if(cmp(rt, stepson) >= 0 || cmp(lf, stepson) >= 0) { + break; + } + } + + ar[i++ & AR_MASK] = stepson; + head = stepson; + trail = pntz(p); + shr(p, trail); + pshift += trail; + trusty = 0; + } + if(!trusty) { + cycle(width, ar, i & AR_MASK); + sift(head, width, cmp, pshift, lp); + } +} + +void qsort(void *base, size_t nel, size_t width, cmpfun cmp) +{ + size_t lp[12*sizeof(size_t)]; + size_t i, size = width * nel; + unsigned char *head, *high; + size_t p[2] = {1, 0}; + int pshift = 1; + int trail; + + if (!size) return; + + head = base; + high = head + size - width; + + /* Precompute Leonardo numbers, scaled by element width */ + for(lp[0]=lp[1]=width, i=2; (lp[i]=lp[i-2]+lp[i-1]+width) < size; i++); + + while(head < high) { + if((p[0] & 3) == 3) { + sift(head, width, cmp, pshift, lp); + shr(p, 2); + pshift += 2; + } else { + if(lp[pshift - 1] >= high - head) { + trinkle(head, width, cmp, p, pshift, 0, lp); + } else { + sift(head, width, cmp, pshift, lp); + } + + if(pshift == 1) { + shl(p, 1); + pshift = 0; + } else { + shl(p, pshift - 1); + pshift = 1; + } + } + + p[0] |= 1; + head += width; + } + + trinkle(head, width, cmp, p, pshift, 0, lp); + + while(pshift != 1 || p[0] != 1 || p[1] != 0) { + if(pshift <= 1) { + trail = pntz(p); + shr(p, trail); + pshift += trail; + } else { + shl(p, 2); + pshift -= 2; + p[0] ^= 7; + shr(p, 1); + trinkle(head - lp[pshift] - width, width, cmp, p, pshift + 1, 1, lp); + shl(p, 1); + p[0] |= 1; + trinkle(head - width, width, cmp, p, pshift, 1, lp); + } + head -= width; + } +} diff --git a/rt/lib/stdlib/stdlib.c b/rt/lib/stdlib/stdlib.c @@ -0,0 +1,218 @@ +//===-- stdlib.c - cfree freestanding stdlib primitives -------------------===// +// +// SPDX-License-Identifier: 0BSD +//===----------------------------------------------------------------------===// + +#include <stddef.h> + +typedef struct { + int quot; + int rem; +} div_t; + +typedef struct { + long quot; + long rem; +} ldiv_t; + +typedef struct { + long long quot; + long long rem; +} lldiv_t; + +static int cfree_isspace(char c) { + return c == ' ' || (c >= '\t' && c <= '\r'); +} + +static int cfree_digit_value(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'z') return c - 'a' + 10; + if (c >= 'A' && c <= 'Z') return c - 'A' + 10; + return -1; +} + +static int cfree_match_ci(const char* s, const char* word) { + while (*word != '\0') { + char a = *s++; + char b = *word++; + if (a >= 'A' && a <= 'Z') a = (char)(a - 'A' + 'a'); + if (a != b) return 0; + } + return 1; +} + +unsigned long long strtoull(const char* nptr, char** endptr, int base) { + const char* s = nptr; + int neg = 0; + unsigned long long value = 0; + int any = 0; + + while (cfree_isspace(*s)) s++; + if (*s == '+' || *s == '-') neg = *s++ == '-'; + + if ((base == 0 || base == 16) && s[0] == '0' && + (s[1] == 'x' || s[1] == 'X')) { + int d = cfree_digit_value(s[2]); + if (d >= 0 && d < 16) { + s += 2; + base = 16; + } + } + if (base == 0) base = (*s == '0') ? 8 : 10; + if (base < 2 || base > 36) { + if (endptr) *endptr = (char*)nptr; + return 0; + } + + for (;;) { + int d = cfree_digit_value(*s); + if (d < 0 || d >= base) break; + value = value * (unsigned)base + (unsigned)d; + s++; + any = 1; + } + + if (endptr) *endptr = (char*)(any ? s : nptr); + return neg ? 0 - value : value; +} + +unsigned long strtoul(const char* nptr, char** endptr, int base) { + return (unsigned long)strtoull(nptr, endptr, base); +} + +long long strtoll(const char* nptr, char** endptr, int base) { + return (long long)strtoull(nptr, endptr, base); +} + +long strtol(const char* nptr, char** endptr, int base) { + return (long)strtoull(nptr, endptr, base); +} + +long long atoll(const char* nptr) { return strtoll(nptr, NULL, 10); } +long atol(const char* nptr) { return strtol(nptr, NULL, 10); } +int atoi(const char* nptr) { return (int)strtol(nptr, NULL, 10); } + +static long double cfree_strtold_impl(const char* nptr, char** endptr) { + const char* s = nptr; + int neg = 0; + int any = 0; + long double value = 0.0L; + + while (cfree_isspace(*s)) s++; + if (*s == '+' || *s == '-') neg = *s++ == '-'; + + if (cfree_match_ci(s, "infinity")) { + if (endptr) *endptr = (char*)(s + 8); + return neg ? -(1.0L / 0.0L) : (1.0L / 0.0L); + } + if (cfree_match_ci(s, "inf")) { + if (endptr) *endptr = (char*)(s + 3); + return neg ? -(1.0L / 0.0L) : (1.0L / 0.0L); + } + if (cfree_match_ci(s, "nan")) { + if (endptr) *endptr = (char*)(s + 3); + return 0.0L / 0.0L; + } + + while (*s >= '0' && *s <= '9') { + value = value * 10.0L + (long double)(*s - '0'); + s++; + any = 1; + } + + if (*s == '.') { + long double place = 0.1L; + s++; + while (*s >= '0' && *s <= '9') { + value += (long double)(*s - '0') * place; + place *= 0.1L; + s++; + any = 1; + } + } + + if (!any) { + if (endptr) *endptr = (char*)nptr; + return 0.0L; + } + + if (*s == 'e' || *s == 'E') { + const char* exp_start = s; + int exp_neg = 0; + int exp_any = 0; + int exp = 0; + s++; + if (*s == '+' || *s == '-') exp_neg = *s++ == '-'; + while (*s >= '0' && *s <= '9') { + exp = exp * 10 + (*s - '0'); + s++; + exp_any = 1; + } + if (exp_any) { + while (exp-- > 0) value = exp_neg ? value / 10.0L : value * 10.0L; + } else { + s = exp_start; + } + } + + if (endptr) *endptr = (char*)s; + return neg ? -value : value; +} + +long double strtold(const char* nptr, char** endptr) { + return cfree_strtold_impl(nptr, endptr); +} + +double strtod(const char* nptr, char** endptr) { + return (double)cfree_strtold_impl(nptr, endptr); +} + +float strtof(const char* nptr, char** endptr) { + return (float)cfree_strtold_impl(nptr, endptr); +} + +int abs(int j) { return j < 0 ? -j : j; } +long labs(long j) { return j < 0 ? -j : j; } +long long llabs(long long j) { return j < 0 ? -j : j; } + +div_t div(int numer, int denom) { + div_t r; + r.quot = numer / denom; + r.rem = numer % denom; + return r; +} + +ldiv_t ldiv(long numer, long denom) { + ldiv_t r; + r.quot = numer / denom; + r.rem = numer % denom; + return r; +} + +lldiv_t lldiv(long long numer, long long denom) { + lldiv_t r; + r.quot = numer / denom; + r.rem = numer % denom; + return r; +} + +void* bsearch(const void* key, const void* base, size_t nmemb, size_t size, + int (*compar)(const void*, const void*)) { + const unsigned char* b = (const unsigned char*)base; + size_t low = 0; + size_t high = nmemb; + + while (low < high) { + size_t mid = low + (high - low) / 2; + const void* elem = b + mid * size; + int cmp = compar(key, elem); + if (cmp < 0) { + high = mid; + } else if (cmp > 0) { + low = mid + 1; + } else { + return (void*)elem; + } + } + return NULL; +} diff --git a/rt/lib/string/string.c b/rt/lib/string/string.c @@ -0,0 +1,109 @@ +//===-- string.c - cfree freestanding string primitives -------------------===// +// +// SPDX-License-Identifier: 0BSD +//===----------------------------------------------------------------------===// + +#include <stddef.h> + +__attribute__((weak)) void* memchr(const void* s, int c, size_t n) { + const unsigned char* p = (const unsigned char*)s; + unsigned char v = (unsigned char)c; + for (size_t i = 0; i < n; i++) { + if (p[i] == v) return (void*)(p + i); + } + return NULL; +} + +__attribute__((weak)) size_t strlen(const char* s) { + size_t n = 0; + while (s[n] != '\0') n++; + return n; +} + +__attribute__((weak)) size_t strnlen(const char* s, size_t maxlen) { + size_t n = 0; + while (n < maxlen && s[n] != '\0') n++; + return n; +} + +__attribute__((weak)) char* strcpy(char* dest, const char* src) { + char* d = dest; + while ((*d++ = *src++) != '\0') { + } + return dest; +} + +__attribute__((weak)) char* strncpy(char* dest, const char* src, size_t n) { + size_t i = 0; + for (; i < n && src[i] != '\0'; i++) dest[i] = src[i]; + for (; i < n; i++) dest[i] = '\0'; + return dest; +} + +__attribute__((weak)) char* strcat(char* dest, const char* src) { + strcpy(dest + strlen(dest), src); + return dest; +} + +__attribute__((weak)) char* strncat(char* dest, const char* src, size_t n) { + char* d = dest + strlen(dest); + size_t i = 0; + while (i < n && src[i] != '\0') { + d[i] = src[i]; + i++; + } + d[i] = '\0'; + return dest; +} + +__attribute__((weak)) int strcmp(const char* s1, const char* s2) { + const unsigned char* a = (const unsigned char*)s1; + const unsigned char* b = (const unsigned char*)s2; + while (*a != '\0' && *a == *b) { + a++; + b++; + } + return (int)*a - (int)*b; +} + +__attribute__((weak)) int strncmp(const char* s1, const char* s2, size_t n) { + const unsigned char* a = (const unsigned char*)s1; + const unsigned char* b = (const unsigned char*)s2; + for (size_t i = 0; i < n; i++) { + if (a[i] != b[i] || a[i] == '\0') return (int)a[i] - (int)b[i]; + } + return 0; +} + +__attribute__((weak)) char* strchr(const char* s, int c) { + char v = (char)c; + for (;;) { + if (*s == v) return (char*)s; + if (*s == '\0') return NULL; + s++; + } +} + +__attribute__((weak)) char* strrchr(const char* s, int c) { + char v = (char)c; + const char* last = NULL; + for (;;) { + if (*s == v) last = s; + if (*s == '\0') return (char*)last; + s++; + } +} + +__attribute__((weak)) char* strstr(const char* haystack, const char* needle) { + if (*needle == '\0') return (char*)haystack; + for (; *haystack != '\0'; haystack++) { + const char* h = haystack; + const char* n = needle; + while (*n != '\0' && *h == *n) { + h++; + n++; + } + if (*n == '\0') return (char*)haystack; + } + return NULL; +} diff --git a/test/rt/cases/freestanding_lib.c b/test/rt/cases/freestanding_lib.c @@ -0,0 +1,124 @@ +#include <assert.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static int cmp_int(const void* a, const void* b) { + int lhs = *(const int*)a; + int rhs = *(const int*)b; + return (lhs > rhs) - (lhs < rhs); +} + +static int check_vsnprintf(char* out, size_t n, const char* fmt, ...) { + va_list ap; + int rc; + va_start(ap, fmt); + rc = vsnprintf(out, n, fmt, ap); + va_end(ap); + return rc; +} + +static int check_vsprintf(char* out, const char* fmt, ...) { + va_list ap; + int rc; + va_start(ap, fmt); + rc = vsprintf(out, fmt, ap); + va_end(ap); + return rc; +} + +static int strings_ok(void) { + char buf[32]; + char small[8]; + const char* text = "alpha beta"; + + memset(buf, 0, sizeof(buf)); + memcpy(buf, "abc", 4); + memmove(buf + 1, buf, 3); + if (strcmp(buf, "aabc") != 0) return 0; + if (memcmp(buf, "aabc", 4) != 0) return 0; + if (*(char*)memchr(buf, 'b', 4) != 'b') return 0; + + strcpy(buf, "alpha"); + strcat(buf, " "); + strncat(buf, "beta!", 4); + if (strcmp(buf, text) != 0) return 0; + if (strncmp(buf, "alpha", 5) != 0) return 0; + if (strlen(buf) != 10) return 0; + if (strnlen(buf, 4) != 4) return 0; + if (strchr(buf, ' ') != buf + 5) return 0; + if (strrchr(buf, 'a') != buf + 9) return 0; + if (strstr(buf, "ha be") != buf + 3) return 0; + + strncpy(small, "xy", sizeof(small)); + if (small[0] != 'x' || small[1] != 'y' || small[2] != '\0') return 0; + return 1; +} + +static int stdlib_ok(void) { + char* end; + int values[6] = {5, 1, 4, 2, 3, 0}; + int key = 4; + int* found; + div_t d; + ldiv_t ld; + lldiv_t lld; + + if (atoi(" -42x") != -42) return 0; + if (atol("123") != 123L) return 0; + if (atoll("-456") != -456LL) return 0; + if (strtol("0x2a!", &end, 0) != 42 || *end != '!') return 0; + if (strtoll("-077", &end, 0) != -63 || *end != '\0') return 0; + if (strtoul("101", &end, 2) != 5UL || *end != '\0') return 0; + if (strtoull("ff", &end, 16) != 255ULL || *end != '\0') return 0; + if ((int)(strtod("12.5e1z", &end) + 0.5) != 125 || *end != 'z') return 0; + if ((int)(strtof("4.25", &end) * 4.0f) != 17 || *end != '\0') return 0; + if ((int)strtold("9.0", &end) != 9 || *end != '\0') return 0; + + if (abs(-7) != 7 || labs(-8L) != 8L || llabs(-9LL) != 9LL) return 0; + d = div(17, 5); + ld = ldiv(17L, 5L); + lld = lldiv(17LL, 5LL); + if (d.quot != 3 || d.rem != 2) return 0; + if (ld.quot != 3 || ld.rem != 2) return 0; + if (lld.quot != 3 || lld.rem != 2) return 0; + + qsort(values, 6, sizeof(values[0]), cmp_int); + for (int i = 0; i < 6; i++) { + if (values[i] != i) return 0; + } + found = bsearch(&key, values, 6, sizeof(values[0]), cmp_int); + if (found == NULL || *found != key) return 0; + return 1; +} + +static int stdio_ok(void) { + char buf[64]; + int rc; + + rc = snprintf(buf, sizeof(buf), "%s:%d:%04x:%c", "id", -7, 26, '!'); + if (rc != 12 || strcmp(buf, "id:-7:001a:!") != 0) return 0; + + rc = snprintf(buf, 6, "abcdef"); + if (rc != 6 || strcmp(buf, "abcde") != 0) return 0; + + rc = sprintf(buf, "%u/%ld/%p", 12U, 34L, (void*)0x1234); + if (rc <= 0 || strstr(buf, "12/34/") != buf) return 0; + + rc = check_vsnprintf(buf, sizeof(buf), "%s %02d", "v", 3); + if (rc != 4 || strcmp(buf, "v 03") != 0) return 0; + + rc = check_vsprintf(buf, "%lld", 123456789LL); + if (rc != 9 || strcmp(buf, "123456789") != 0) return 0; + return 1; +} + +int test_main(void) { + assert(1); + if (!strings_ok()) return 1; + if (!stdlib_ok()) return 2; + if (!stdio_ok()) return 3; + return 42; +}