Skip to content

Commit 76b60ae

Browse files
committed
Make embedding more useful
Two major changes: - Errors (syntax, etc.) longjmp and return to the caller fairly cleanly. There seems to be some private bytes growth in normal usage over a 30min test, but no huge leaks, and no file handle leaks on "normal" failure paths. - Support taking updates directly from the embedder. By accepting the contents of a file, the embedder (https://github.com/sgraham/rdy is a test) can directly pass from the editor to the compiler rather than having to write to and re-read from disk. This is less for efficiency than to avoid the messy state that files can be in while the text editor is in the middle of (re)writing them, e.g. not there, locked, zero byte long, etc. "rdy" hooks directly to rpc change events in nvim and passes the updated contents directly.
1 parent 0ad67e8 commit 76b60ae

File tree

7 files changed

+55
-37
lines changed

7 files changed

+55
-37
lines changed

alloc.c

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ void __asan_unpoison_memory_region(void const volatile* addr, size_t size);
2626
#define NUM_BUMP_HEAPS (AL_Link + 1)
2727

2828
UserContext* user_context;
29+
jmp_buf toplevel_update_jmpbuf;
2930
CompilerState compiler_state;
3031
LinkerState linker_state;
3132

@@ -41,7 +42,7 @@ static HeapData heap[NUM_BUMP_HEAPS] = {
4142
{NULL, NULL, 128 << 20}, // AL_Link
4243
};
4344

44-
// Reports an error and exit.
45+
// Reports an error and exit update.
4546
void error(char* fmt, ...) {
4647
va_list ap;
4748
va_start(ap, fmt);
@@ -51,7 +52,7 @@ void error(char* fmt, ...) {
5152
user_context->output_function(2, fmt, ap);
5253
}
5354
logerr("\n");
54-
exit(1);
55+
longjmp(toplevel_update_jmpbuf, 1);
5556
}
5657

5758
void alloc_init(AllocLifetime lifetime) {
@@ -70,9 +71,14 @@ void alloc_init(AllocLifetime lifetime) {
7071
void alloc_reset(AllocLifetime lifetime) {
7172
assert(lifetime < NUM_BUMP_HEAPS);
7273
HeapData* hd = &heap[lifetime];
73-
ASAN_POISON_MEMORY_REGION(hd->base, hd->size);
74-
free_executable_memory(hd->base, hd->size);
75-
hd->alloc_pointer = NULL;
74+
// We allow double resets because we may longjmp out during error handling,
75+
// and don't know which heaps are initialized at that point.
76+
if (hd->base) {
77+
ASAN_POISON_MEMORY_REGION(hd->base, hd->size);
78+
free_executable_memory(hd->base, hd->size);
79+
hd->alloc_pointer = NULL;
80+
hd->base = NULL;
81+
}
7682
}
7783

7884
void* bumpcalloc(size_t num, size_t size, AllocLifetime lifetime) {
@@ -129,7 +135,7 @@ void* allocate_writable_memory(size_t size) {
129135
#if X64WIN
130136
void* p = VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
131137
if (!p) {
132-
error("VirtualAlloc failed: 0x%x\n", GetLastError());
138+
error("VirtualAlloc of %zu failed: 0x%x\n", size, GetLastError());
133139
}
134140
ASAN_UNPOISON_MEMORY_REGION(p, size);
135141
return p;
@@ -150,7 +156,7 @@ bool make_memory_executable(void* m, size_t size) {
150156
#if X64WIN
151157
DWORD old_protect;
152158
if (!VirtualProtect(m, size, PAGE_EXECUTE_READ, &old_protect)) {
153-
error("VirtualProtect failed: 0x%x\n", GetLastError());
159+
error("VirtualProtect %p %zu failed: 0x%x\n", m, size, GetLastError());
154160
}
155161
return true;
156162
#else
@@ -166,7 +172,7 @@ void free_executable_memory(void* p, size_t size) {
166172
#if X64WIN
167173
(void)size; // If |size| is passed, free will fail.
168174
if (!VirtualFree(p, 0, MEM_RELEASE)) {
169-
error("VirtualFree failed: 0x%x\n", GetLastError());
175+
error("VirtualFree %p %zu failed: 0x%x\n", p, size, GetLastError());
170176
}
171177
ASAN_POISON_MEMORY_REGION(p, size);
172178
#else

dyibicc.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <ctype.h>
55
#include <errno.h>
66
#include <limits.h>
7+
#include <setjmp.h>
78
#include <stdarg.h>
89
#include <stdbool.h>
910
#include <stdint.h>
@@ -173,6 +174,7 @@ File* new_file(char* name, char* contents);
173174
Token* tokenize_string_literal(Token* tok, Type* basety);
174175
Token* tokenize(File* file);
175176
Token* tokenize_file(char* filename);
177+
Token* tokenize_filecontents(char* path, char* contents);
176178

177179
#define unreachable() error("internal error at %s:%d", __FILE__, __LINE__)
178180

@@ -610,7 +612,6 @@ struct Scope {
610612
typedef struct DyoLinkData {
611613
char* source_name;
612614
char* output_dyo_name;
613-
int64_t last_compiled_timestamp;
614615
char* codeseg_base_address; // Just the address, not a string.
615616
size_t codeseg_size;
616617
} DyoLinkData;
@@ -692,10 +693,9 @@ typedef struct CompilerState {
692693
typedef struct LinkerState {
693694
// link.c
694695
HashMap link__runtime_function_map;
695-
696-
// main.c
697696
} LinkerState;
698697

699698
extern UserContext* user_context;
699+
extern jmp_buf toplevel_update_jmpbuf;
700700
extern CompilerState compiler_state;
701701
extern LinkerState linker_state;

entry.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ int main(int argc, char** argv) {
100100

101101
int result = 0;
102102

103-
if (dyibicc_update(ctx)) {
103+
if (dyibicc_update(ctx, NULL, NULL)) {
104104
if (ctx->entry_point) {
105105
int myargc = 1;
106106
char* myargv[] = {"prog", NULL};

libdyibicc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ typedef struct DyibiccContext {
3030

3131
DyibiccContext* dyibicc_set_environment(DyibiccEnviromentData* env_data);
3232

33-
bool dyibicc_update(DyibiccContext* context);
33+
bool dyibicc_update(DyibiccContext* context, char* file, char* contents);
3434

3535
void dyibicc_free(DyibiccContext* context);

main.c

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,6 @@
152152
#define C(x) compiler_state.main__##x
153153
#define L(x) linker_state.main__##x
154154

155-
static Token* must_tokenize_file(char* path) {
156-
Token* tok = tokenize_file(path);
157-
if (!tok)
158-
error("%s: %s", path, strerror(errno));
159-
return tok;
160-
}
161-
162155
#if 0 // for -E call after preprocess().
163156
static void print_tokens(Token* tok) {
164157
int line = 1;
@@ -384,7 +377,18 @@ static FILE* open_file(char* path) {
384377
return out;
385378
}
386379

387-
bool dyibicc_update(DyibiccContext* context) {
380+
bool dyibicc_update(DyibiccContext* context, char* filename, char* contents) {
381+
if (setjmp(toplevel_update_jmpbuf) != 0) {
382+
alloc_reset(AL_Compile);
383+
alloc_reset(AL_Temp);
384+
alloc_reset(AL_Link);
385+
memset(&compiler_state, 0, sizeof(compiler_state));
386+
memset(&linker_state, 0, sizeof(linker_state));
387+
// TODO: files are being left open
388+
// TODO: AL_Manual memory or other mallocs may be leaked
389+
return false;
390+
}
391+
388392
UserContext* ctx = (UserContext*)context;
389393
bool link_result = true;
390394

@@ -395,19 +399,24 @@ bool dyibicc_update(DyibiccContext* context) {
395399
for (size_t i = 0; i < ctx->num_files; ++i) {
396400
DyoLinkData* dld = &ctx->files[i];
397401

398-
{
399-
int64_t cur_mtime = stat_single_file(dld->source_name);
400-
if (cur_mtime == dld->last_compiled_timestamp) {
401-
continue;
402-
}
402+
if (filename && strcmp(dld->source_name, filename) != 0) {
403+
// If a specific update is provided, we only compile that one.
404+
continue;
405+
}
403406

407+
{
404408
alloc_init(AL_Compile);
405409

406-
// logdbg("%s => %s\n", dld->source_name, dld->output_dyo_name);
407-
408410
init_macros();
409411
C(base_file) = dld->source_name;
410-
Token* tok = must_tokenize_file(C(base_file));
412+
Token* tok;
413+
if (filename) {
414+
tok = tokenize_filecontents(filename, contents);
415+
} else {
416+
tok = tokenize_file(C(base_file));
417+
}
418+
if (!tok)
419+
error("%s: %s", filename, strerror(errno));
411420
tok = preprocess(tok);
412421

413422
codegen_init(); // Initializes dynasm so that parse() can assign labels.
@@ -420,7 +429,6 @@ bool dyibicc_update(DyibiccContext* context) {
420429
codegen(prog, dyo_out);
421430
fclose(dyo_out);
422431

423-
dld->last_compiled_timestamp = cur_mtime;
424432
compiled_any = true;
425433

426434
alloc_reset(AL_Compile);

makefile.win

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CC=@cl
66
#CFLAGS=/nologo /Od /Zi /D_CRT_SECURE_NO_DEPRECATE /DX64WIN=1 /W4 /WX
77
CFLAGS=/nologo /Ox /GL /Zi /D_CRT_SECURE_NO_DEPRECATE /DX64WIN=1 /W4 /WX
88
LDFLAGS=gdi32.lib user32.lib onecore.lib /LTCG /OPT:REF /OPT:ICF
9+
#LDFLAGS=gdi32.lib user32.lib onecore.lib
910

1011
#CC=@"C:\Program Files\LLVM\bin\clang-cl.exe"
1112
#CFLAGS=/nologo /Od /fsanitize=address /Zi /D_CRT_SECURE_NO_DEPRECATE /DX64WIN=1 /W4 /WX -Wno-missing-field-initializers -Wno-switch -Wno-unused-function

tokenize.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ void error_at(char* loc, char* fmt, ...) {
4242
va_list ap;
4343
va_start(ap, fmt);
4444
verror_at(C(current_file)->name, C(current_file)->contents, line_no, loc, fmt, ap);
45-
exit(1);
45+
longjmp(toplevel_update_jmpbuf, 1);
4646
}
4747

4848
void error_tok(Token* tok, char* fmt, ...) {
4949
va_list ap;
5050
va_start(ap, fmt);
5151
verror_at(tok->file->name, tok->file->contents, tok->line_no, tok->loc, fmt, ap);
52-
exit(1);
52+
longjmp(toplevel_update_jmpbuf, 1);
5353
}
5454

5555
void warn_tok(Token* tok, char* fmt, ...) {
@@ -763,11 +763,7 @@ static void convert_universal_chars(char* p) {
763763
*q = '\0';
764764
}
765765

766-
Token* tokenize_file(char* path) {
767-
char* p = read_file(path, AL_Compile);
768-
if (!p)
769-
return NULL;
770-
766+
Token* tokenize_filecontents(char* path, char* p) {
771767
// UTF-8 texts may start with a 3-byte "BOM" marker sequence.
772768
// If exists, just skip them because they are useless bytes.
773769
// (It is actually not recommended to add BOM markers to UTF-8
@@ -782,3 +778,10 @@ Token* tokenize_file(char* path) {
782778
File* file = new_file(path, p);
783779
return tokenize(file);
784780
}
781+
782+
Token* tokenize_file(char* path) {
783+
char* p = read_file(path, AL_Compile);
784+
if (!p)
785+
return NULL;
786+
return tokenize_filecontents(path, p);
787+
}

0 commit comments

Comments
 (0)