Skip to content

Commit f3d8719

Browse files
committed
Add script to amalgamate the compiler into a single .c file
src/build_amalg.py generates embed/libdyibicc.{c,h} which are a standalone copy of the compiler for simple embedding. There's some clutter added to the normal source build (which is defined away) in order to make all functions (and data) except the four exported in libdyibicc.h be static.
1 parent e4fea99 commit f3d8719

16 files changed

Lines changed: 378 additions & 292 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
out/
22
.vs/
3+
embed/
34
tags
45
test/x.c

src/alloc.c

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include "dyibicc.h"
1+
#include "dyibicc.h"
22

33
#if X64WIN
44
#include <windows.h>
@@ -25,10 +25,10 @@ void __asan_unpoison_memory_region(void const volatile* addr, size_t size);
2525

2626
#define NUM_BUMP_HEAPS (AL_Link + 1)
2727

28-
UserContext* user_context;
29-
jmp_buf toplevel_update_jmpbuf;
30-
CompilerState compiler_state;
31-
LinkerState linker_state;
28+
IMPLSTATIC UserContext* user_context;
29+
IMPLSTATIC jmp_buf toplevel_update_jmpbuf;
30+
IMPLSTATIC CompilerState compiler_state;
31+
IMPLSTATIC LinkerState linker_state;
3232

3333
typedef struct HeapData {
3434
char* base;
@@ -42,7 +42,7 @@ static HeapData heap[NUM_BUMP_HEAPS] = {
4242
{NULL, NULL, 128 << 20}, // AL_Link
4343
};
4444

45-
void alloc_init(AllocLifetime lifetime) {
45+
IMPLSTATIC void alloc_init(AllocLifetime lifetime) {
4646
assert(lifetime < NUM_BUMP_HEAPS);
4747
HeapData* hd = &heap[lifetime];
4848

@@ -55,7 +55,7 @@ void alloc_init(AllocLifetime lifetime) {
5555
}
5656
}
5757

58-
void alloc_reset(AllocLifetime lifetime) {
58+
IMPLSTATIC void alloc_reset(AllocLifetime lifetime) {
5959
assert(lifetime < NUM_BUMP_HEAPS);
6060
HeapData* hd = &heap[lifetime];
6161
// We allow double resets because we may longjmp out during error handling,
@@ -68,7 +68,7 @@ void alloc_reset(AllocLifetime lifetime) {
6868
}
6969
}
7070

71-
void* bumpcalloc(size_t num, size_t size, AllocLifetime lifetime) {
71+
IMPLSTATIC void* bumpcalloc(size_t num, size_t size, AllocLifetime lifetime) {
7272
if (lifetime == AL_Manual) {
7373
return calloc(num, size);
7474
}
@@ -85,20 +85,23 @@ void* bumpcalloc(size_t num, size_t size, AllocLifetime lifetime) {
8585
return ret;
8686
}
8787

88-
void alloc_free(void* p, AllocLifetime lifetime) {
88+
IMPLSTATIC void alloc_free(void* p, AllocLifetime lifetime) {
8989
(void)lifetime;
9090
assert(lifetime == AL_Manual);
9191
free(p);
9292
}
9393

94-
void* bumplamerealloc(void* old, size_t old_size, size_t new_size, AllocLifetime lifetime) {
94+
IMPLSTATIC void* bumplamerealloc(void* old,
95+
size_t old_size,
96+
size_t new_size,
97+
AllocLifetime lifetime) {
9598
void* newptr = bumpcalloc(1, new_size, lifetime);
9699
memcpy(newptr, old, MIN(old_size, new_size));
97100
ASAN_POISON_MEMORY_REGION(old, old_size);
98101
return newptr;
99102
}
100103

101-
void* aligned_allocate(size_t size, size_t alignment) {
104+
IMPLSTATIC void* aligned_allocate(size_t size, size_t alignment) {
102105
size = align_to_u(size, alignment);
103106
#if X64WIN
104107
return _aligned_malloc(size, alignment);
@@ -107,7 +110,7 @@ void* aligned_allocate(size_t size, size_t alignment) {
107110
#endif
108111
}
109112

110-
void aligned_free(void* p) {
113+
IMPLSTATIC void aligned_free(void* p) {
111114
#if X64WIN
112115
_aligned_free(p);
113116
#else
@@ -118,7 +121,7 @@ void aligned_free(void* p) {
118121
// Allocates RW memory of given size and returns a pointer to it. On failure,
119122
// prints out the error and returns NULL. Unlike malloc, the memory is allocated
120123
// on a page boundary so it's suitable for calling mprotect.
121-
void* allocate_writable_memory(size_t size) {
124+
IMPLSTATIC void* allocate_writable_memory(size_t size) {
122125
#if X64WIN
123126
void* p = VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
124127
if (!p) {
@@ -139,7 +142,7 @@ void* allocate_writable_memory(size_t size) {
139142

140143
// Sets a RX permission on the given memory, which must be page-aligned. Returns
141144
// 0 on success. On failure, prints out the error and returns -1.
142-
bool make_memory_executable(void* m, size_t size) {
145+
IMPLSTATIC bool make_memory_executable(void* m, size_t size) {
143146
#if X64WIN
144147
DWORD old_protect;
145148
if (!VirtualProtect(m, size, PAGE_EXECUTE_READ, &old_protect)) {
@@ -155,7 +158,7 @@ bool make_memory_executable(void* m, size_t size) {
155158
#endif
156159
}
157160

158-
void free_executable_memory(void* p, size_t size) {
161+
IMPLSTATIC void free_executable_memory(void* p, size_t size) {
159162
#if X64WIN
160163
(void)size; // If |size| is passed, free will fail.
161164
if (!VirtualFree(p, 0, MEM_RELEASE)) {

src/build_amalg.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import shutil
5+
import subprocess
6+
import sys
7+
8+
9+
# Update gen.py too, except entry.c.
10+
#
11+
# Order is important because of static initialized data that can't be forward
12+
# declared.
13+
FILELIST = [
14+
'src/dyibicc.h',
15+
'src/khash.h',
16+
'src/dynasm/dasm_proto.h',
17+
'src/dynasm/dasm_x86.h',
18+
'src/type.c',
19+
'src/alloc.c',
20+
'src/util.c',
21+
'src/hashmap.c',
22+
'src/link.c',
23+
'src/main.c',
24+
'src/parse.c',
25+
'src/preprocess.c',
26+
'src/tokenize.c',
27+
'src/unicode.c',
28+
]
29+
30+
31+
def push_disable_dynasm_warnings(f):
32+
f.write('#ifdef _MSC_VER\n')
33+
f.write('#pragma warning(push)\n')
34+
f.write('#pragma warning(disable: 4127)\n')
35+
f.write('#pragma warning(disable: 4242)\n')
36+
f.write('#pragma warning(disable: 4244)\n')
37+
f.write('#endif\n')
38+
39+
40+
def pop_dynasm_warnings(f):
41+
f.write('#ifdef _MSC_VER\n')
42+
f.write('#pragma warning(pop)\n')
43+
f.write('#endif\n')
44+
45+
46+
def include_file(f, src):
47+
with open(src, 'r') as i:
48+
f.write('#undef C\n')
49+
f.write('#undef L\n')
50+
f.write('#undef VOID\n')
51+
if 'dynasm/' in src:
52+
push_disable_dynasm_warnings(f)
53+
f.write('//\n// START OF %s\n//\n' % src)
54+
for line in i.readlines():
55+
if line.startswith('#include "dyibicc.h"'):
56+
continue
57+
if line.startswith('#include "khash.h"'):
58+
continue
59+
if line.startswith('#include "dynasm/dasm_proto.h"'):
60+
continue
61+
if line.startswith('#include "dynasm/dasm_x86.h"'):
62+
continue
63+
if line.startswith('IMPLEXTERN '):
64+
continue
65+
f.write(line.replace('IMPLSTATIC ', 'static ').replace('DASM_FDEF ', 'static '))
66+
f.write('//\n// END OF %s\n//\n' % src)
67+
if 'dynasm/' in src:
68+
pop_dynasm_warnings(f)
69+
70+
71+
def main():
72+
if not os.path.isfile('out/wr/codegen.w.c') or not os.path.isfile('out/lr/codegen.l.c'):
73+
print('Must first do both a `m.bat r test` and `./m r test`.')
74+
return 1
75+
76+
if not os.path.isdir('embed'):
77+
os.makedirs('embed')
78+
with open('embed/libdyibicc.c', 'w', newline='\n') as f:
79+
for src in FILELIST:
80+
include_file(f, src)
81+
82+
f.write('#if X64WIN\n')
83+
include_file(f, 'out/wr/codegen.w.c')
84+
f.write('#else // ^^^ X64WIN / !X64WIN vvv\n')
85+
include_file(f, 'out/lr/codegen.l.c')
86+
f.write('#endif // !X64WIN\n')
87+
shutil.copyfile('src/libdyibicc.h', 'embed/libdyibicc.h')
88+
shutil.copyfile('LICENSE', 'embed/LICENSE')
89+
90+
# Smoke test that there's no warnings, etc. Currently only on host platform.
91+
os.chdir('embed')
92+
if sys.platform == 'win32':
93+
subprocess.check_call(['cl', '/nologo', '/W4', '/Wall', '/WX', '/c', 'libdyibicc.c'])
94+
elif sys.platform == 'linux':
95+
subprocess.check_call(['gcc', '-Wall', '-Wextra', '-Werror', '-c', 'libdyibicc.c'])
96+
subprocess.check_call(['clang', '-Wall', '-Wextra', '-Werror', '-c', 'libdyibicc.c'])
97+
symsp = subprocess.run(['readelf', '-s', 'libdyibicc.o'], capture_output=True)
98+
syms = str(symsp.stdout, encoding='utf-8').splitlines()
99+
syms = [l for l in syms if 'GLOBAL ' in l]
100+
syms = [l for l in syms if 'UND ' not in l]
101+
print('These are the global exported symbols from libdyibicc.o,')
102+
print('check that they match libdyibicc.h (only).')
103+
print('-'*80)
104+
for s in syms:
105+
print(s)
106+
print('-'*80)
107+
108+
return 0
109+
110+
111+
if __name__ == '__main__':
112+
sys.exit(main())

src/codegen.in.c

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ static int dasmargreg[] = {REG_DI, REG_SI, REG_DX, REG_CX, REG_R8, REG_R9};
6969
static void gen_expr(Node* node);
7070
static void gen_stmt(Node* node);
7171

72-
int codegen_pclabel(void) {
72+
IMPLSTATIC int codegen_pclabel(void) {
7373
int ret = C(numlabels);
7474
dasm_growpc(&C(dynasm), ++C(numlabels));
7575
return ret;
@@ -579,7 +579,8 @@ static DynasmCastFunc dynasm_cast_table[][11] = {
579579

580580
// clang-format on
581581

582-
static void cast(Type* from, Type* to) {
582+
// This can't be "cast()" when amalgamated because parse has a cast() as well.
583+
static void cg_cast(Type* from, Type* to) {
583584
if (to->kind == TY_VOID)
584585
return;
585586

@@ -1225,7 +1226,7 @@ static void gen_expr(Node* node) {
12251226
return;
12261227
case ND_CAST:
12271228
gen_expr(node->lhs);
1228-
cast(node->lhs->ty, node->ty);
1229+
cg_cast(node->lhs->ty, node->ty);
12291230
return;
12301231
case ND_MEMZERO:
12311232
// `rep stosb` is equivalent to `memset(rdi, al, rcx)`.
@@ -2179,7 +2180,7 @@ static void assign_lvar_offsets(Obj* prog) {
21792180

21802181
#endif // SysV
21812182

2182-
void linkfixup_push(FileLinkData* fld, char* target, char* fixup, int addend) {
2183+
static void linkfixup_push(FileLinkData* fld, char* target, char* fixup, int addend) {
21832184
if (!fld->fixups) {
21842185
fld->fixups = calloc(8, sizeof(LinkFixup));
21852186
fld->fcap = 8;
@@ -2237,13 +2238,13 @@ static void emit_data(Obj* prog) {
22372238
// than deferred to a relocation
22382239

22392240
UserContext* uc = user_context;
2240-
bool was_freed = false;
2241+
// bool was_freed = false;
22412242
size_t idx = var->is_static ? C(file_index) : uc->num_files;
22422243
void* prev = hashmap_get(&user_context->global_data[idx], var->name);
22432244
if (prev) {
22442245
if (var->is_rodata) {
22452246
aligned_free(prev);
2246-
was_freed = true;
2247+
// was_freed = true;
22472248
} else {
22482249
// data already created and initialized, don't reinit.
22492250
continue;
@@ -2342,7 +2343,7 @@ static void store_gp(int r, int offset, int sz) {
23422343
}
23432344

23442345
#if X64WIN
2345-
extern int __chkstk();
2346+
extern int __chkstk(void);
23462347
#endif
23472348

23482349
static void emit_text(Obj* prog) {
@@ -2537,7 +2538,7 @@ static void fill_out_text_exports(Obj* prog, char* codeseg_base_address) {
25372538
}
25382539
}
25392540

2540-
void free_link_fixups(FileLinkData* fld) {
2541+
IMPLSTATIC void free_link_fixups(FileLinkData* fld) {
25412542
for (int i = 0; i < fld->flen; ++i) {
25422543
free(fld->fixups[i].name);
25432544
}
@@ -2562,14 +2563,14 @@ static void fill_out_fixups(FileLinkData* fld) {
25622563
}
25632564
}
25642565

2565-
void codegen_init(void) {
2566+
IMPLSTATIC void codegen_init(void) {
25662567
dasm_init(&C(dynasm), DASM_MAXSECTION);
25672568
dasm_growpc(&C(dynasm), 1 << 16); // Arbitrary number to avoid lots of reallocs of that array.
25682569

25692570
C(numlabels) = 1;
25702571
}
25712572

2572-
void codegen(Obj* prog, size_t file_index) {
2573+
IMPLSTATIC void codegen(Obj* prog, size_t file_index) {
25732574
C(file_index) = file_index;
25742575

25752576
void* globals[dynasm_globals_MAX + 1];
@@ -2613,7 +2614,7 @@ void codegen(Obj* prog, size_t file_index) {
26132614
}
26142615

26152616
// This can be called after a longjmp in update.
2616-
void codegen_free(void) {
2617+
IMPLSTATIC void codegen_free(void) {
26172618
if (C(dynasm)) {
26182619
dasm_free(&C(dynasm));
26192620
}

0 commit comments

Comments
 (0)