Skip to content

Commit 299c57d

Browse files
committed
nostdlib.
1 parent 501f863 commit 299c57d

36 files changed

Lines changed: 786 additions & 213 deletions

libc/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# SQLite Libc
2+
3+
This is a minimal libc that offers just enough
4+
to compile SQLite for wasm32 with nostdlib.
5+
6+
The allocator is currently a bump allocator,
7+
and math is provided by the host side.

libc/assert.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#ifndef assert
2+
#define assert(ignore) ((void)0)
3+
#endif

libc/ctype.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <ctype.h>
2+
3+
int(isalnum)(int c) { return isalpha(c) || isdigit(c); }
4+
int(isalpha)(int c) { return ((unsigned)c | 32) - 'a' < 26; }
5+
int(isascii)(int c) { return !(c & ~0x7f); }
6+
int(isblank)(int c) { return (c == ' ' || c == '\t'); }
7+
int(iscntrl)(int c) { return (unsigned)c < 0x20 || c == 0x7f; }
8+
int(isdigit)(int c) { return (unsigned)c - '0' < 10; }
9+
int(isgraph)(int c) { return (unsigned)c - 0x21 < 0x5e; }
10+
int(islower)(int c) { return (unsigned)c - 'a' < 26; }
11+
int(isprint)(int c) { return (unsigned)c - 0x20 < 0x5f; }
12+
int(ispunct)(int c) { return isgraph(c) && !isalnum(c); }
13+
int(isspace)(int c) { return c == ' ' || (unsigned)c - '\t' < 5; }
14+
int(isupper)(int c) { return (unsigned)c - 'A' < 26; }
15+
int(isxdigit)(int c) { return isdigit(c) || ((unsigned)c | 32) - 'a' < 6; }
16+
17+
int(tolower)(int c) { return isupper(c) ? c | 32 : c; }
18+
int(toupper)(int c) { return islower(c) ? c & 0x5f : c; }

libc/ctype.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
3+
int isalnum(int);
4+
int isalpha(int);
5+
int isascii(int);
6+
int isblank(int);
7+
int iscntrl(int);
8+
int isdigit(int);
9+
int isgraph(int);
10+
int islower(int);
11+
int isprint(int);
12+
int ispunct(int);
13+
int isspace(int);
14+
int isupper(int);
15+
int isxdigit(int);
16+
17+
int tolower(int);
18+
int toupper(int);
19+
20+
#define isalpha(a) (0 ? isalpha(a) : (((unsigned)(a) | 32) - 'a') < 26)
21+
#define isdigit(a) (0 ? isdigit(a) : ((unsigned)(a) - '0') < 10)
22+
#define isgraph(a) (0 ? isgraph(a) : ((unsigned)(a) - 0x21) < 0x5e)
23+
#define islower(a) (0 ? islower(a) : ((unsigned)(a) - 'a') < 26)
24+
#define isprint(a) (0 ? isprint(a) : ((unsigned)(a) - 0x20) < 0x5f)
25+
#define isupper(a) (0 ? isupper(a) : ((unsigned)(a) - 'A') < 26)

libc/format.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
cd -P -- "$(dirname -- "$0")"
5+
6+
ROOT=../
7+
WASI_SDK="$ROOT/tools/wasi-sdk/bin"
8+
9+
"$WASI_SDK/clang-format" --style=Google -i *.c *.h

libc/libc.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#include "ctype.c"
2+
#include "math.c"
3+
#include "stdlib.c"
4+
#include "string.c"

libc/math.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include <math.h>
2+
3+
double(sqrt)(double x) { return __builtin_sqrt(x); }

libc/math.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#pragma once
2+
3+
double acos(double);
4+
double acosh(double);
5+
double asin(double);
6+
double asinh(double);
7+
double atan(double);
8+
double atan2(double, double);
9+
double atanh(double);
10+
double ceil(double);
11+
double cos(double);
12+
double cosh(double);
13+
double exp(double);
14+
double fabs(double);
15+
double floor(double);
16+
double fmod(double, double);
17+
double log(double);
18+
double log10(double);
19+
double log2(double);
20+
double pow(double, double);
21+
double sin(double);
22+
double sinh(double);
23+
double tan(double);
24+
double tanh(double);
25+
double trunc(double);
26+
double sqrt(double);
27+
28+
#define sqrt(x) (__builtin_sqrt(x))
29+
#define isnan(x) (__builtin_isnan(x))

libc/stdio.h

Whitespace-only changes.

libc/stdlib.c

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#include <stdint.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
5+
extern char __heap_base[];
6+
extern char __heap_end[];
7+
8+
static void* __arena_beg = __heap_base;
9+
static void* __arena_end = __heap_end;
10+
11+
static void __arena_grow(size_t size) {
12+
size_t avail = __arena_end - __arena_beg;
13+
if (size > avail) {
14+
size_t npages = (size - avail + 0xfffful) >> 16;
15+
size_t old = __builtin_wasm_memory_grow(0, npages);
16+
if (old == ~0ul) __builtin_trap();
17+
__arena_end += npages << 16;
18+
}
19+
}
20+
21+
__attribute__((always_inline)) void free(void*) {}
22+
23+
__attribute__((malloc)) void* malloc(size_t size) {
24+
if (size == 0) return NULL;
25+
size = (size + 15) & ~15;
26+
void* res = __arena_beg;
27+
__arena_grow(size);
28+
__arena_beg += size;
29+
return res;
30+
}
31+
32+
__attribute__((malloc)) void* calloc(size_t nelem, size_t elsize) {
33+
return malloc(nelem * elsize);
34+
}
35+
36+
__attribute__((malloc)) void* aligned_alloc(size_t align, size_t size) {
37+
if ((align & -align) != align) __builtin_trap();
38+
align = (align - ((uintptr_t)__arena_beg & (align - 1))) & (align - 1);
39+
return malloc(size + align) + align;
40+
}
41+
42+
void* realloc(void* ptr, size_t size) {
43+
size_t copy = __arena_beg - ptr;
44+
void* res = malloc(size);
45+
if (ptr != NULL && res != NULL) {
46+
if (size < copy) copy = size;
47+
memcpy(res, ptr, copy);
48+
}
49+
return res;
50+
}
51+
52+
__attribute__((noreturn)) void abort(void) { __builtin_trap(); }
53+
54+
// Shellsort with Gonnet & Baeza-Yates gap sequence.
55+
// Simple, no recursion, doesn't use the C stack.
56+
// Clang auto-vectorizes the inner loop.
57+
58+
void qsort(void* base, size_t nel, size_t width,
59+
int (*comp)(const void*, const void*)) {
60+
// If nel is zero, we're required to do nothing.
61+
// If it's one, the array is already sorted.
62+
size_t wnel = width * nel;
63+
size_t gap = nel;
64+
while (gap > 1) {
65+
// Use 64-bit unsigned arithmetic to avoid intermediate overflow.
66+
// Absent overflow, gap will be strictly less than its previous value.
67+
// Once it is one or zero, set it to one: do a final pass, and stop.
68+
gap = (5ull * gap - 1) / 11;
69+
if (gap == 0) gap = 1;
70+
71+
// It'd be undefined behavior for wnel to overflow a size_t;
72+
// or if width is zero: the base pointer would be invalid.
73+
// Since gap is stricly less than nel, we can assume
74+
// wgap is strictly less than wnel.
75+
size_t wgap = width * gap;
76+
__builtin_assume(wgap < wnel);
77+
for (size_t i = wgap; i < wnel; i += width) {
78+
// Even without overflow flags, the overflow builtin helps the compiler.
79+
for (size_t j = i; !__builtin_sub_overflow(j, wgap, &j);) {
80+
char* a = j + (char*)base;
81+
char* b = a + wgap;
82+
if (comp(a, b) <= 0) break;
83+
84+
// This well known loop is automatically vectorized.
85+
size_t s = width;
86+
do {
87+
char tmp = *a;
88+
*a++ = *b;
89+
*b++ = tmp;
90+
} while (--s);
91+
}
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)