Skip to content

Commit a6ecbb7

Browse files
committed
Introduce libc/mem/tinymalloc.inc
This allocator shaves ~20kb off single-threaded tool programs and is slightly faster than proper malloc for simple non-demanding programs
1 parent 3bf95ae commit a6ecbb7

File tree

17 files changed

+201
-37
lines changed

17 files changed

+201
-37
lines changed

libc/mem/BUILD.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ LIBC_MEM = $(LIBC_MEM_A_DEPS) $(LIBC_MEM_A)
88
LIBC_MEM_A = o/$(MODE)/libc/mem/mem.a
99
LIBC_MEM_A_FILES := $(wildcard libc/mem/*)
1010
LIBC_MEM_A_HDRS = $(filter %.h,$(LIBC_MEM_A_FILES))
11+
LIBC_MEM_A_INCS = $(filter %.inc,$(LIBC_MEM_A_FILES))
1112
LIBC_MEM_A_SRCS = $(filter %.c,$(LIBC_MEM_A_FILES))
1213
LIBC_MEM_A_OBJS = $(LIBC_MEM_A_SRCS:%.c=o/$(MODE)/%.o)
1314

@@ -46,6 +47,7 @@ $(LIBC_MEM_A_OBJS): private \
4647
LIBC_MEM_LIBS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)))
4748
LIBC_MEM_SRCS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_SRCS))
4849
LIBC_MEM_HDRS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_HDRS))
50+
LIBC_MEM_INCS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_INCS))
4951
LIBC_MEM_BINS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_BINS))
5052
LIBC_MEM_CHECKS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_CHECKS))
5153
LIBC_MEM_OBJS = $(foreach x,$(LIBC_MEM_ARTIFACTS),$($(x)_OBJS))

libc/mem/tinymalloc.inc

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright 2024 Justine Alexandra Roberts Tunney
2+
//
3+
// Permission to use, copy, modify, and/or distribute this software for
4+
// any purpose with or without fee is hereby granted, provided that the
5+
// above copyright notice and this permission notice appear in all copies.
6+
//
7+
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
8+
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
9+
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
10+
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
11+
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
12+
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
13+
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14+
// PERFORMANCE OF THIS SOFTWARE.
15+
16+
#include "libc/assert.h"
17+
#include "libc/errno.h"
18+
#include "libc/mem/mem.h"
19+
#include "libc/stdckdint.h"
20+
#include "libc/str/str.h"
21+
22+
#ifndef MODE_DBG /* don't interfere with asan dlmalloc hooking */
23+
24+
_Alignas(65536) static struct {
25+
char memory[1024 * 1024 * 1024];
26+
unsigned used, last, free;
27+
} heap;
28+
29+
static inline bool isheap(char *mem) {
30+
return heap.memory <= mem && mem < heap.memory + heap.used;
31+
}
32+
33+
void free(void *ptr) {
34+
char *mem;
35+
unsigned base;
36+
if (ptr) {
37+
mem = (char *)ptr;
38+
unassert(isheap(mem));
39+
base = mem - heap.memory;
40+
*(unsigned *)mem = heap.free;
41+
heap.free = base;
42+
}
43+
}
44+
45+
size_t malloc_usable_size(void *ptr) {
46+
char *mem = (char *)ptr;
47+
unassert(isheap(mem));
48+
return ((unsigned *)mem)[-1];
49+
}
50+
51+
void *memalign(size_t align, size_t need) {
52+
char *res;
53+
unsigned next, next2, base, toto, *link, *link2;
54+
55+
// normalize arguments
56+
while (align & (align - 1))
57+
++align;
58+
if (need < sizeof(unsigned))
59+
need = sizeof(unsigned);
60+
if (align < sizeof(unsigned))
61+
align = sizeof(unsigned);
62+
if (align > 65536)
63+
goto InvalidArgument;
64+
if (ckd_add(&need, need, sizeof(unsigned) - 1))
65+
goto OutOfMemory;
66+
need &= -sizeof(unsigned);
67+
68+
// allocate from free list
69+
next = heap.free;
70+
link = &heap.free;
71+
while (next) {
72+
next2 = *(unsigned *)(heap.memory + next);
73+
link2 = (unsigned *)(heap.memory + next);
74+
if (need <= ((unsigned *)(heap.memory + next))[-1]) {
75+
*link = next2;
76+
return (void *)(heap.memory + next);
77+
}
78+
next = next2;
79+
link = link2;
80+
}
81+
82+
// allocate new static memory
83+
base = heap.used;
84+
base += sizeof(unsigned);
85+
base += align - 1;
86+
base &= -align;
87+
if (ckd_add(&toto, base, need))
88+
goto OutOfMemory;
89+
if (toto > sizeof(heap.memory))
90+
goto OutOfMemory;
91+
res = heap.memory + base;
92+
((unsigned *)res)[-1] = need;
93+
heap.used = toto;
94+
heap.last = base;
95+
return res;
96+
97+
// we require more vespene gas
98+
OutOfMemory:
99+
errno = ENOMEM;
100+
return 0;
101+
InvalidArgument:
102+
errno = EINVAL;
103+
return 0;
104+
}
105+
106+
void *malloc(size_t need) {
107+
return memalign(sizeof(max_align_t), need);
108+
}
109+
110+
void *calloc(size_t count, size_t size) {
111+
char *res;
112+
unsigned need, used;
113+
if (ckd_mul(&need, count, size))
114+
need = -1;
115+
used = heap.used;
116+
if ((res = (char *)malloc(need)))
117+
if (res - heap.memory < used)
118+
bzero(res, need);
119+
return res;
120+
}
121+
122+
void *realloc(void *ptr, size_t need) {
123+
char *res, *mem;
124+
unsigned base, have, toto;
125+
if (!ptr) {
126+
res = (char *)malloc(need);
127+
} else {
128+
mem = (char *)ptr;
129+
unassert(isheap(mem));
130+
have = ((unsigned *)mem)[-1];
131+
base = mem - heap.memory;
132+
if (need < have) {
133+
res = mem;
134+
} else if (base == heap.last) {
135+
if (need < sizeof(unsigned))
136+
need = sizeof(unsigned);
137+
if (ckd_add(&need, need, sizeof(unsigned) - 1))
138+
goto OutOfMemory;
139+
need &= -sizeof(unsigned);
140+
if (ckd_add(&toto, base, need))
141+
goto OutOfMemory;
142+
if (toto > sizeof(heap.memory))
143+
goto OutOfMemory;
144+
((unsigned *)mem)[-1] = need;
145+
heap.used = toto;
146+
res = mem;
147+
} else if ((res = (char *)malloc(need))) {
148+
if (have > need)
149+
have = need;
150+
memcpy(res, mem, have);
151+
free(mem);
152+
}
153+
}
154+
return res;
155+
OutOfMemory:
156+
errno = ENOMEM;
157+
return 0;
158+
}
159+
160+
#endif /* MODE_DBG */

tool/build/apelink.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ static Elf64_Xword notesize;
259259

260260
static char *r_off32_e_lfanew;
261261

262+
#include "libc/mem/tinymalloc.inc"
263+
262264
static wontreturn void Die(const char *thing, const char *reason) {
263265
tinyprint(2, thing, ": ", reason, "\n", NULL);
264266
exit(1);

tool/build/assimilate.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
#define FORMAT_MACHO 2
6868
#define FORMAT_PE 3
6969

70+
#include "libc/mem/tinymalloc.inc"
71+
7072
static int g_arch;
7173
static int g_format;
7274
static bool g_force;

tool/build/compile.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ const char *const kSafeEnv[] = {
226226
"SYSTEMROOT", // needed by socket()
227227
};
228228

229+
#include "libc/mem/tinymalloc.inc"
230+
229231
void OnAlrm(int sig) {
230232
++gotalrm;
231233
}

tool/build/cp.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ char linkbuf[PATH_MAX];
6969

7070
void Cp(char *, char *);
7171

72+
#include "libc/mem/tinymalloc.inc"
73+
7274
bool IsDirectory(const char *path) {
7375
int e;
7476
bool res;

tool/build/elf2pe.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ static const char *stubpath;
158158
static long FLAG_SizeOfStackCommit = 64 * 1024;
159159
static long FLAG_SizeOfStackReserve = 8 * 1024 * 1024;
160160

161+
#include "libc/mem/tinymalloc.inc"
162+
161163
static wontreturn void Die(const char *thing, const char *reason) {
162164
tinyprint(2, thing, ": ", reason, "\n", NULL);
163165
exit(1);

tool/build/fixupobj.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ static Elf64_Ehdr *elf;
6767
static const char *epath;
6868
static Elf64_Xword symcount;
6969

70+
#include "libc/mem/tinymalloc.inc"
71+
7072
static wontreturn void Die(const char *reason) {
7173
tinyprint(2, epath, ": ", reason, "\n", NULL);
7274
exit(1);

tool/build/gzip.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ const char *prog;
7171
char databuf[32768];
7272
char pathbuf[PATH_MAX];
7373

74+
#include "libc/mem/tinymalloc.inc"
75+
7476
wontreturn void PrintUsage(int rc, FILE *f) {
7577
fputs("usage: ", f);
7678
fputs(prog, f);

tool/build/killall.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ static const char *prog;
5050
static char16_t **filters;
5151
static uint32_t pids[10000];
5252

53+
#include "libc/mem/tinymalloc.inc"
54+
5355
static wontreturn void PrintUsage(int rc, FILE *f) {
5456
fprintf(f,
5557
"Usage: %s [-nshv] NAME...\n"

0 commit comments

Comments
 (0)