Skip to content

Commit

Permalink
Add script to amalgamate the compiler into a single .c file
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sgraham committed Apr 15, 2023
1 parent e4fea99 commit f3d8719
Show file tree
Hide file tree
Showing 16 changed files with 378 additions and 292 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
out/
.vs/
embed/
tags
test/x.c
33 changes: 18 additions & 15 deletions src/alloc.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "dyibicc.h"
#include "dyibicc.h"

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

#define NUM_BUMP_HEAPS (AL_Link + 1)

UserContext* user_context;
jmp_buf toplevel_update_jmpbuf;
CompilerState compiler_state;
LinkerState linker_state;
IMPLSTATIC UserContext* user_context;
IMPLSTATIC jmp_buf toplevel_update_jmpbuf;
IMPLSTATIC CompilerState compiler_state;
IMPLSTATIC LinkerState linker_state;

typedef struct HeapData {
char* base;
Expand All @@ -42,7 +42,7 @@ static HeapData heap[NUM_BUMP_HEAPS] = {
{NULL, NULL, 128 << 20}, // AL_Link
};

void alloc_init(AllocLifetime lifetime) {
IMPLSTATIC void alloc_init(AllocLifetime lifetime) {
assert(lifetime < NUM_BUMP_HEAPS);
HeapData* hd = &heap[lifetime];

Expand All @@ -55,7 +55,7 @@ void alloc_init(AllocLifetime lifetime) {
}
}

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

void* bumpcalloc(size_t num, size_t size, AllocLifetime lifetime) {
IMPLSTATIC void* bumpcalloc(size_t num, size_t size, AllocLifetime lifetime) {
if (lifetime == AL_Manual) {
return calloc(num, size);
}
Expand All @@ -85,20 +85,23 @@ void* bumpcalloc(size_t num, size_t size, AllocLifetime lifetime) {
return ret;
}

void alloc_free(void* p, AllocLifetime lifetime) {
IMPLSTATIC void alloc_free(void* p, AllocLifetime lifetime) {
(void)lifetime;
assert(lifetime == AL_Manual);
free(p);
}

void* bumplamerealloc(void* old, size_t old_size, size_t new_size, AllocLifetime lifetime) {
IMPLSTATIC void* bumplamerealloc(void* old,
size_t old_size,
size_t new_size,
AllocLifetime lifetime) {
void* newptr = bumpcalloc(1, new_size, lifetime);
memcpy(newptr, old, MIN(old_size, new_size));
ASAN_POISON_MEMORY_REGION(old, old_size);
return newptr;
}

void* aligned_allocate(size_t size, size_t alignment) {
IMPLSTATIC void* aligned_allocate(size_t size, size_t alignment) {
size = align_to_u(size, alignment);
#if X64WIN
return _aligned_malloc(size, alignment);
Expand All @@ -107,7 +110,7 @@ void* aligned_allocate(size_t size, size_t alignment) {
#endif
}

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

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

void free_executable_memory(void* p, size_t size) {
IMPLSTATIC void free_executable_memory(void* p, size_t size) {
#if X64WIN
(void)size; // If |size| is passed, free will fail.
if (!VirtualFree(p, 0, MEM_RELEASE)) {
Expand Down
112 changes: 112 additions & 0 deletions src/build_amalg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env python3

import os
import shutil
import subprocess
import sys


# Update gen.py too, except entry.c.
#
# Order is important because of static initialized data that can't be forward
# declared.
FILELIST = [
'src/dyibicc.h',
'src/khash.h',
'src/dynasm/dasm_proto.h',
'src/dynasm/dasm_x86.h',
'src/type.c',
'src/alloc.c',
'src/util.c',
'src/hashmap.c',
'src/link.c',
'src/main.c',
'src/parse.c',
'src/preprocess.c',
'src/tokenize.c',
'src/unicode.c',
]


def push_disable_dynasm_warnings(f):
f.write('#ifdef _MSC_VER\n')
f.write('#pragma warning(push)\n')
f.write('#pragma warning(disable: 4127)\n')
f.write('#pragma warning(disable: 4242)\n')
f.write('#pragma warning(disable: 4244)\n')
f.write('#endif\n')


def pop_dynasm_warnings(f):
f.write('#ifdef _MSC_VER\n')
f.write('#pragma warning(pop)\n')
f.write('#endif\n')


def include_file(f, src):
with open(src, 'r') as i:
f.write('#undef C\n')
f.write('#undef L\n')
f.write('#undef VOID\n')
if 'dynasm/' in src:
push_disable_dynasm_warnings(f)
f.write('//\n// START OF %s\n//\n' % src)
for line in i.readlines():
if line.startswith('#include "dyibicc.h"'):
continue
if line.startswith('#include "khash.h"'):
continue
if line.startswith('#include "dynasm/dasm_proto.h"'):
continue
if line.startswith('#include "dynasm/dasm_x86.h"'):
continue
if line.startswith('IMPLEXTERN '):
continue
f.write(line.replace('IMPLSTATIC ', 'static ').replace('DASM_FDEF ', 'static '))
f.write('//\n// END OF %s\n//\n' % src)
if 'dynasm/' in src:
pop_dynasm_warnings(f)


def main():
if not os.path.isfile('out/wr/codegen.w.c') or not os.path.isfile('out/lr/codegen.l.c'):
print('Must first do both a `m.bat r test` and `./m r test`.')
return 1

if not os.path.isdir('embed'):
os.makedirs('embed')
with open('embed/libdyibicc.c', 'w', newline='\n') as f:
for src in FILELIST:
include_file(f, src)

f.write('#if X64WIN\n')
include_file(f, 'out/wr/codegen.w.c')
f.write('#else // ^^^ X64WIN / !X64WIN vvv\n')
include_file(f, 'out/lr/codegen.l.c')
f.write('#endif // !X64WIN\n')
shutil.copyfile('src/libdyibicc.h', 'embed/libdyibicc.h')
shutil.copyfile('LICENSE', 'embed/LICENSE')

# Smoke test that there's no warnings, etc. Currently only on host platform.
os.chdir('embed')
if sys.platform == 'win32':
subprocess.check_call(['cl', '/nologo', '/W4', '/Wall', '/WX', '/c', 'libdyibicc.c'])
elif sys.platform == 'linux':
subprocess.check_call(['gcc', '-Wall', '-Wextra', '-Werror', '-c', 'libdyibicc.c'])
subprocess.check_call(['clang', '-Wall', '-Wextra', '-Werror', '-c', 'libdyibicc.c'])
symsp = subprocess.run(['readelf', '-s', 'libdyibicc.o'], capture_output=True)
syms = str(symsp.stdout, encoding='utf-8').splitlines()
syms = [l for l in syms if 'GLOBAL ' in l]
syms = [l for l in syms if 'UND ' not in l]
print('These are the global exported symbols from libdyibicc.o,')
print('check that they match libdyibicc.h (only).')
print('-'*80)
for s in syms:
print(s)
print('-'*80)

return 0


if __name__ == '__main__':
sys.exit(main())
23 changes: 12 additions & 11 deletions src/codegen.in.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ static int dasmargreg[] = {REG_DI, REG_SI, REG_DX, REG_CX, REG_R8, REG_R9};
static void gen_expr(Node* node);
static void gen_stmt(Node* node);

int codegen_pclabel(void) {
IMPLSTATIC int codegen_pclabel(void) {
int ret = C(numlabels);
dasm_growpc(&C(dynasm), ++C(numlabels));
return ret;
Expand Down Expand Up @@ -579,7 +579,8 @@ static DynasmCastFunc dynasm_cast_table[][11] = {

// clang-format on

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

Expand Down Expand Up @@ -1225,7 +1226,7 @@ static void gen_expr(Node* node) {
return;
case ND_CAST:
gen_expr(node->lhs);
cast(node->lhs->ty, node->ty);
cg_cast(node->lhs->ty, node->ty);
return;
case ND_MEMZERO:
// `rep stosb` is equivalent to `memset(rdi, al, rcx)`.
Expand Down Expand Up @@ -2179,7 +2180,7 @@ static void assign_lvar_offsets(Obj* prog) {

#endif // SysV

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

UserContext* uc = user_context;
bool was_freed = false;
// bool was_freed = false;
size_t idx = var->is_static ? C(file_index) : uc->num_files;
void* prev = hashmap_get(&user_context->global_data[idx], var->name);
if (prev) {
if (var->is_rodata) {
aligned_free(prev);
was_freed = true;
// was_freed = true;
} else {
// data already created and initialized, don't reinit.
continue;
Expand Down Expand Up @@ -2342,7 +2343,7 @@ static void store_gp(int r, int offset, int sz) {
}

#if X64WIN
extern int __chkstk();
extern int __chkstk(void);
#endif

static void emit_text(Obj* prog) {
Expand Down Expand Up @@ -2537,7 +2538,7 @@ static void fill_out_text_exports(Obj* prog, char* codeseg_base_address) {
}
}

void free_link_fixups(FileLinkData* fld) {
IMPLSTATIC void free_link_fixups(FileLinkData* fld) {
for (int i = 0; i < fld->flen; ++i) {
free(fld->fixups[i].name);
}
Expand All @@ -2562,14 +2563,14 @@ static void fill_out_fixups(FileLinkData* fld) {
}
}

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

C(numlabels) = 1;
}

void codegen(Obj* prog, size_t file_index) {
IMPLSTATIC void codegen(Obj* prog, size_t file_index) {
C(file_index) = file_index;

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

// This can be called after a longjmp in update.
void codegen_free(void) {
IMPLSTATIC void codegen_free(void) {
if (C(dynasm)) {
dasm_free(&C(dynasm));
}
Expand Down
Loading

0 comments on commit f3d8719

Please sign in to comment.