mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 07:33:01 +00:00 
			
		
		
		
	Compiler is coming along. Work on Slot system and general compiler strategy.
This commit is contained in:
		
							
								
								
									
										230
									
								
								3
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								3
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,230 @@ | ||||
| /* | ||||
| * Copyright (c) 2017 Calvin Rose | ||||
| * | ||||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| * of this software and associated documentation files (the "Software"), to | ||||
| * deal in the Software without restriction, including without limitation the | ||||
| * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| * sell copies of the Software, and to permit persons to whom the Software is | ||||
| * furnished to do so, subject to the following conditions: | ||||
| * | ||||
| * The above copyright notice and this permission notice shall be included in | ||||
| * all copies or substantial portions of the Software. | ||||
| * | ||||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| * IN THE SOFTWARE. | ||||
| */ | ||||
|  | ||||
| #ifndef DST_COMPILE_H | ||||
| #define DST_COMPILE_H | ||||
|  | ||||
| #include <dst/dst.h> | ||||
| #include <setjmp.h> | ||||
|  | ||||
| /* Compiler typedefs */ | ||||
| typedef struct DstCompiler DstCompiler; | ||||
| typedef struct FormOptions FormOptions; | ||||
| typedef struct SlotTracker SlotTracker; | ||||
| typedef struct DstScope DstScope; | ||||
| typedef struct DstCFunctionOptimizer DstCFunctionOptimizer; | ||||
|  | ||||
| #define DST_SLOT_CONSTANT 0x10000 | ||||
| #define DST_SLOT_NAMED 0x20000 | ||||
| #define DST_SLOT_RETURNED 0x40000 | ||||
| #define DST_SLOT_NIL 0x80000 | ||||
| #define DST_SLOT_MUTABLE 0x100000 | ||||
|  | ||||
| #define DST_SLOTTYPE_ANY 0xFFFF | ||||
|  | ||||
| /* A stack slot */ | ||||
| struct DstSlot { | ||||
|     int32_t index; | ||||
|     int32_t envindex; /* 0 is local, positive number is an upvalue */ | ||||
|     uint32_t flags; | ||||
|     DstValue constant; /* If the slot has a constant value */ | ||||
| } | ||||
|  | ||||
| /* Most forms that return a constant will not generate any bytecode */ | ||||
|  | ||||
| /* Special forms that need support */ | ||||
| /* cond | ||||
|  * while (continue, break) | ||||
|  * quote | ||||
|  * fn | ||||
|  * def | ||||
|  * var | ||||
|  * varset | ||||
|  * do | ||||
|  */ | ||||
|  | ||||
| #define DST_OPTIMIZER_CONSTANTS 0x10000 | ||||
| #define DST_OPTIMIZER_BYTECODE 0x20000 | ||||
| #define DST_OPTIMIZER_PARTIAL_CONSTANTS 0x40000 | ||||
| #define DST_OPTIMIZER_SYSCALL 0x80000 | ||||
|  | ||||
| /* A grouping of optimization on a cfunction given certain conditions | ||||
|  * on the arguments (such as all constants, or some known types). The appropriate | ||||
|  * optimizations should be tried before compiling a normal function call. */ | ||||
| struct DstCFunctionOptimizer { | ||||
|     uint32_t flags; /* Indicate what kind of optimizations can be performed. */ | ||||
|         /*Also what kind of types can be returned*/ | ||||
|     int32_t syscall; | ||||
| } | ||||
|  | ||||
| #define DST_SCOPE_FUNCTION 1 | ||||
| #define DST_SCOPE_LASTSLOT 2 | ||||
| #define DST_SCOPE_FIRSTSLOT 4 | ||||
| #define DST_SCOPE_ENV | ||||
|  | ||||
| /* A lexical scope during compilation */ | ||||
| struct DstScope { | ||||
|     DstArray constants; /* Constants for the funcdef */ | ||||
|     DstTable symbols; /* Map symbols -> Slot inidices */ | ||||
|  | ||||
|     /* Hold all slots in use. Data structures that store | ||||
|      * slots should link them to this datatstructure */ | ||||
|     DstSlot *slots; | ||||
|     int32_t slotcount; | ||||
|     int32_t slotcap; | ||||
|  | ||||
|     /* A vector of freed slots. */ | ||||
|     int32_t *freeslots; | ||||
|     int32_t freeslotcount; | ||||
|     int32_t freeslotcap; | ||||
|  | ||||
|     int32_t lastslot; | ||||
|     int32_t nextslot; | ||||
|  | ||||
|     /* Referenced closure environemts. The values at each index correspond | ||||
|      * to which index to get the environment from in the parent. The enironment | ||||
|      * that corresponds to the direct parent's stack will always have value 0. */ | ||||
|     int32_t *envs; | ||||
|     int32_t envcount; | ||||
|     int32_t envcap; | ||||
|  | ||||
|     int32_t buffer_offset; /* Where the bytecode for this scope begins */ | ||||
|  | ||||
|     uint32_t flags; | ||||
| } | ||||
|  | ||||
| /* Compilation state */ | ||||
| struct DstCompiler { | ||||
|     jump_buf on_error; | ||||
|     int32_t scopecount; | ||||
|     int32_t scopecap; | ||||
|     DstScope *scopes; | ||||
|     DstBuffer buffer; | ||||
|     DstBuffer mapbuffer; | ||||
|     int32_t error_start; | ||||
|     int32_t error_end; | ||||
|     DstValue error; | ||||
|     int recursion_guard; | ||||
| }; | ||||
|  | ||||
| #define DST_FOPTS_TAIL 0x10000 | ||||
| #define DST_FOPTS_FORCESLOT 0x20000 | ||||
|  | ||||
| /* Compiler state */ | ||||
| struct DstFormOptions { | ||||
|     DstCompiler *compiler; | ||||
|     DstValue x; | ||||
|     const DstValue *sourcemap; | ||||
|     uint32_t flags; /* bit set of accepted primitive types */ | ||||
| }; | ||||
|  | ||||
| typedef enum DstCompileStatus { | ||||
|     DST_COMPILE_OK, | ||||
|     DST_COMPILE_ERROR | ||||
| } DstCompileStatus; | ||||
|  | ||||
| /* Results of compilation */ | ||||
| typedef struct DstCompileResults { | ||||
|     DstCompileStatus status; | ||||
|     DstFuncDef *funcdef; | ||||
|     const uint8_t *error; | ||||
| } DstCompileResults; | ||||
|  | ||||
| typedef struct DstCompileOptions { | ||||
|     uint32_t flags; | ||||
|     const DstValue *sourcemap; | ||||
|     DstValue src; | ||||
|     int32_t target; | ||||
| }; | ||||
|  | ||||
| /* Compiler handlers. Used to compile different kinds of expressions. */ | ||||
| typedef DstSlot (*DstFormCompiler)(DstFormOptions opts); | ||||
|  | ||||
| /* Dispatch to correct form compiler */ | ||||
| DstSlot dst_compile_value(DstFormOptions opts); | ||||
|  | ||||
| /* Compile basic types */ | ||||
| DstSlot dst_compile_constant(DstFormOptions opts); | ||||
| DstSlot dst_compile_symbol(DstFormOptions opts); | ||||
| DstSlot dst_copmile_array(DstFormOptions opts); | ||||
| DstSlot dst_copmile_struct(DstFormOptions opts); | ||||
| DstSlot dst_copmile_table(DstFormOptions opts); | ||||
|  | ||||
| /* Tuple compiliation will handle much of the work */ | ||||
| DstSlot dst_compile_tuple(DstFormOptions opts); | ||||
|  | ||||
| /* Compile special forms */ | ||||
| DstSlot dst_compile_do(DstFormOptions opts); | ||||
| DstSlot dst_compile_fn(DstFormOptions opts); | ||||
| DstSlot dst_compile_cond(DstFormOptions opts); | ||||
| DstSlot dst_compile_while(DstFormOptions opts); | ||||
| DstSlot dst_compile_quote(DstFormOptions opts); | ||||
| DstSlot dst_compile_def(DstFormOptions opts); | ||||
| DstSlot dst_compile_var(DstFormOptions opts); | ||||
| DstSlot dst_compile_varset(DstFormOptions opts); | ||||
|  | ||||
| /* Compile source code into FuncDef. */ | ||||
| DstCompileResults dst_compile(DstCompileOptions opts); | ||||
|  | ||||
| /****************************************************/ | ||||
|  | ||||
| DstSlot dst_compile_error(DstCompiler *c, const DstValue *sourcemap, const uint8_t *m); | ||||
| DstSlot dst_compile_cerror(DstCompiler *c, const DstValue *sourcemap, const char *m); | ||||
|  | ||||
| /* Use these to get sub options. They will traverse the source map so | ||||
|  * compiler errors make sense. Then modify the returned options. */ | ||||
| DstFormOptions dst_compile_getopts_index(DstFormOptions opts, int32_t index); | ||||
| DstFormOptions dst_compile_getopts_key(DstFormOptions opts, DstValue key); | ||||
| DstFormOptions dst_compile_getopts_value(DstFormOptions opts, DstValue key); | ||||
|  | ||||
| void dst_compile_scope(DstCompiler *c, int newfn); | ||||
| DstSlot dst_compile_popscope(DstCompiler *c); | ||||
|  | ||||
| int dst_compile_slotmatch(DstFormOptions opts, DstSlot slot); | ||||
| DstSlot dst_compile_normalslot(DstCompiler *c, uint32_t types); | ||||
| DstSlot dst_compile_constantslot(DstCompiler *c, DstValue x); | ||||
| void dst_compile_freeslot(DstCompiler *c, DstSlot slot); | ||||
| void dst_compile_freeslotarray(DstCompiler *c, DstArray *slots); | ||||
|  | ||||
| /* Search for a symbol */ | ||||
| DstSlot dst_compile_resolve(DstCompiler *c, const DstValue *sourcemap, const uint8_t *sym); | ||||
|  | ||||
| /* Get a local slot that can be used as the desination for whatever is compiling. */ | ||||
| DstSlot dst_compile_targetslot(DstFormOptions opts, DstSlot s); | ||||
|  | ||||
| /* Coerce any slot into the target slot. If no target is specified, return | ||||
|  * the slot unaltered. Otherwise, move and load upvalues as necesarry to set the slot. */ | ||||
| DstSlot dst_compile_coercetargetslot(DstFormOptions opts, DstSlot s); | ||||
|  | ||||
| DstSlot dst_compile_realizeslot(DstCompiler *c, DstSlot s); | ||||
| DstSlot dst_compile_returnslot(DstCompiler *c, DstSlot s); | ||||
|  | ||||
| /* Emit instructions. */ | ||||
|  | ||||
| DstSlot dst_compile_emit_movenear(DstCompiler *c, DstSlot slot); | ||||
| void dst_compile_emit_movefar(DstCompiler *c, DstSlot near, DstSlot orig); | ||||
|  | ||||
| void dst_compile_emit_ss(DstCompiler *c, DstOpCode op, DstSlot dest, DstSlot src); | ||||
| void dst_compile_emit_sss(DstCompiler *c, DstOpCode op, DstSlot dest, DstSlot s1, DstSlot s2); | ||||
| void dst_compile_emit_sss_src(DstCompiler *c, DstOpCode op, DstSlot s1, DstSlot s2, DstSlot s3); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							| @@ -26,7 +26,7 @@ PREFIX?=/usr/local | ||||
| BINDIR=$(PREFIX)/bin | ||||
| VERSION=\"0.0.0-beta\" | ||||
|  | ||||
| CFLAGS=-std=c99 -Wall -Wextra -I./include -I./libs -g -DDST_VERSION=$(VERSION) | ||||
| CFLAGS=-std=c99 -Wall -Wextra -Wfatal-errors -I./include -I./libs -g -DDST_VERSION=$(VERSION) | ||||
| PREFIX=/usr/local | ||||
| DST_TARGET=dst | ||||
| DST_XXD=xxd | ||||
| @@ -59,7 +59,7 @@ $(DST_XXD): libs/xxd.c | ||||
| ################################### | ||||
|  | ||||
| DST_CORE_SOURCES=$(addprefix core/,\ | ||||
| 				 array.c asm.c buffer.c fiber.c func.c gc.c parse.c string.c strtod.c\ | ||||
| 				 array.c asm.c buffer.c compile.c fiber.c func.c gc.c parse.c string.c strtod.c\ | ||||
| 				 struct.c symcache.c syscalls.c table.c tuple.c userdata.c util.c\ | ||||
| 				 value.c vm.c wrap.c) | ||||
| DST_CORE_OBJECTS=$(patsubst %.c,%.o,$(DST_CORE_SOURCES)) | ||||
|   | ||||
| @@ -133,13 +133,16 @@ static const DstInstructionDef dst_ops[] = { | ||||
|     {"jump-if", DIT_SL, DOP_JUMP_IF}, | ||||
|     {"jump-if-not", DIT_SL, DOP_JUMP_IF_NOT}, | ||||
|     {"less-than", DIT_SSS, DOP_LESS_THAN}, | ||||
|     {"load-boolean", DIT_S, DOP_LOAD_BOOLEAN}, | ||||
|     {"load-constant", DIT_SC, DOP_LOAD_CONSTANT}, | ||||
|     {"load-false", DIT_S, DOP_LOAD_FALSE}, | ||||
|     {"load-integer", DIT_SI, DOP_LOAD_INTEGER}, | ||||
|     {"load-nil", DIT_S, DOP_LOAD_NIL}, | ||||
|     {"load-self", DIT_S, DOP_LOAD_SELF}, | ||||
|     {"load-syscall", DIT_SU, DOP_LOAD_SYSCALL}, | ||||
|     {"load-true", DIT_S, DOP_LOAD_TRUE}, | ||||
|     {"load-upvalue", DIT_SES, DOP_LOAD_UPVALUE}, | ||||
|     {"move", DIT_SS, DOP_MOVE}, | ||||
|     {"move-far", DIT_SS, DOP_MOVE_FAR}, | ||||
|     {"move-near", DIT_SS, DOP_MOVE_NEAR}, | ||||
|     {"multiply", DIT_SSS, DOP_MULTIPLY}, | ||||
|     {"multiply-immediate", DIT_SSI, DOP_MULTIPLY_IMMEDIATE}, | ||||
|     {"multiply-integer", DIT_SSS, DOP_MULTIPLY_INTEGER}, | ||||
|   | ||||
							
								
								
									
										595
									
								
								core/compile.c
									
									
									
									
									
								
							
							
						
						
									
										595
									
								
								core/compile.c
									
									
									
									
									
								
							| @@ -23,46 +23,54 @@ | ||||
| #include <dst/dst.h> | ||||
| #include "compile.h" | ||||
|  | ||||
| static void dst_compile_cleanup(DstCompiler *c) { | ||||
| /* Lazily sort the optimizers */ | ||||
| static int optimizers_sorted = 0; | ||||
|  | ||||
| /* Lookups for specials and optimizable c functions. */ | ||||
| DstCFunctionOptimizer dst_compiler_optimizers[255]; | ||||
| DstSpecial dst_compiler_specials[16]; | ||||
|  | ||||
| /* Deinitialize a compiler struct */ | ||||
| static void dst_compile_cleanup(DstCompiler *c) { | ||||
|     while (c->scopecount) | ||||
|         dst_compile_popscope(c); | ||||
|     free(c->scopes); | ||||
|     free(c->buffer); | ||||
|     free(c->mapbuffer); | ||||
|     c->buffer = NULL; | ||||
|     c->mapbuffer = NULL; | ||||
|     c->scopes = NULL; | ||||
| } | ||||
|  | ||||
| DstSlot dst_compile_error(DstCompiler *c, const DstValue *sourcemap, const uint8_t *m) { | ||||
|     DstSlot ret; | ||||
| /* Throw an error with a dst string */ | ||||
| void dst_compile_error(DstCompiler *c, const DstValue *sourcemap, const uint8_t *m) { | ||||
|     c->error_start = dst_unwrap_integer(sourcemap[0]); | ||||
|     c->error_end = dst_unwrap_integer(sourcemap[1]); | ||||
|     c->error = m; | ||||
|     ret.flags = DST_SLOT_ERROR; | ||||
|     ret.index = 0; | ||||
|     ret.constant = dst_wrap_nil(); | ||||
|     return ret; | ||||
|     c->error = dst_wrap_string(m); | ||||
|     longjmp(c->on_error, 1); | ||||
| } | ||||
|  | ||||
| DstSlot dst_compile_cerror(DstCompiler *c, const DstValue *sourcemap, const char *m) { | ||||
|     return dst_compile_error(c, sourcemap, dst_cstring(m)); | ||||
| /* Throw an error with a message in a cstring */ | ||||
| void dst_compile_cerror(DstCompiler *c, const DstValue *sourcemap, const char *m) { | ||||
|     dst_compile_error(c, sourcemap, dst_cstring(m)); | ||||
| } | ||||
|  | ||||
| /* Use these to get sub options. They will traverse the source map so | ||||
|  * compiler errors make sense. Then modify the returned options. */ | ||||
| DstFormOptions dst_compile_getopts_index(DstFormOptions opts, int32_t index) { | ||||
|     DstCompiler *c = opts.compiler; | ||||
|     const DstValue *sourcemap = dst_parse_submap_index(opts.sourcemap, index); | ||||
|     DstValue nextval = dst_getindex(opts.x, index); | ||||
|     opts.x = nextval; | ||||
|     opts.sourcemap = sourcemap; | ||||
|     return opts; | ||||
| } | ||||
|  | ||||
| DstFormOptions dst_compile_getopts_key(DstFormOptions opts, DstValue key) { | ||||
|     DstCompiler *c = opts.compiler; | ||||
|     const DstValue *sourcemap = dst_parse_submap_key(opts.sourcemap, key); | ||||
|     opts.x = key; | ||||
|     opts.sourcemap = sourcemap; | ||||
|     return opts; | ||||
| } | ||||
|  | ||||
| DstFormOptions dst_compile_getopts_value(DstFormOptions opts, DstValue key) { | ||||
|     DstCompiler *c = opts.compiler; | ||||
|     const DstValue *sourcemap = dst_parse_submap_value(opts.sourcemap, key); | ||||
|     DstValue nextval = dst_get(opts.x, key); | ||||
|     opts.x = nextval; | ||||
| @@ -70,142 +78,152 @@ DstFormOptions dst_compile_getopts_value(DstFormOptions opts, DstValue key) { | ||||
|     return opts; | ||||
| } | ||||
|  | ||||
| /* Eneter a new scope */ | ||||
| void dst_compile_scope(DstCompiler *c, int newfn) { | ||||
|     DstScope *scope; | ||||
|     if (c->scopecap < c->scopecount) { | ||||
|         c->scopes = realloc(c->scopes, 2 * sizeof(DstScope) * c->scopecount + 2); | ||||
|         if (NULL == c->scope) { | ||||
| void dst_compile_slotpool_init(DstSlotPool *pool) { | ||||
|     pool->s = NULL; | ||||
|     pool->count = 0; | ||||
|     pool->free = 0; | ||||
|     pool->cap = 0; | ||||
| } | ||||
|  | ||||
| void dst_compile_slotpool_deinit(DstSlotPool *pool) { | ||||
|     free(pool->s); | ||||
|     pool->s = NULL; | ||||
|     pool->cap = 0; | ||||
|     pool->count = 0; | ||||
| } | ||||
|  | ||||
| void dst_compile_slotpool_extend(DstSlotPool *pool, int32_t extra) { | ||||
|     int32_t i; | ||||
|     int32_t newcount = pool->count + extra; | ||||
|     if (newcount > pool->cap) { | ||||
|         int32_t newcap = 2 * newcount; | ||||
|         pool->s = realloc(pool->s, newcap * sizeof(DstSlot)); | ||||
|         if (NULL == pool->s) { | ||||
|             DST_OUT_OF_MEMORY; | ||||
|         } | ||||
|         pool->cap = newcap; | ||||
|     } | ||||
|     scope = c->scopes + c->scopecount++; | ||||
|     /* Mark all slots as free */ | ||||
|     for (i = pool->count; i < newcount; i++) { | ||||
|         pool->s[i].flags = 0; | ||||
|     } | ||||
|     pool->count = newcount; | ||||
| } | ||||
|  | ||||
| DstSlot *dst_compile_slotpool_alloc(DstSlotPool *pool) { | ||||
|     int32_t oldcount = pool->count; | ||||
|     int32_t newcount = oldcount == 0xF0 ? 0x101 : oldcount + 1; | ||||
|     while (pool->free < pool->count) { | ||||
|         if (!(pool->s[pool->free].flags & DST_SLOT_NOTEMPTY)) { | ||||
|             return pool->s + pool->free;  | ||||
|         } | ||||
|         pool->free++; | ||||
|     } | ||||
|     dst_compile_slotpool_extend(pool, newcount - oldcount); | ||||
|     pool->s[oldcount].flags = DST_SLOT_NOTEMPTY; | ||||
|     pool->s[oldcount].index = newcount - 1; | ||||
|     return pool->s + newcount - 1; | ||||
| } | ||||
|  | ||||
| void dst_compile_slotpool_freeindex(DstSlotPool *pool, int32_t index) { | ||||
|     if (index > 0 && index < pool->count) { | ||||
|         pool->s[index].flags = 0; | ||||
|         if (index < pool->free) | ||||
|             pool->free = index; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void dst_compile_slotpool_free(DstSlotPool *pool, DstSlot *s) { | ||||
|     DstSlot *oldfree = pool->s + pool->free; | ||||
|     if (s >= pool->s && s < (pool->s + pool->count)) { | ||||
|         if (s < oldfree) { | ||||
|             pool->free = s - pool->s; | ||||
|         } | ||||
|         s->flags = 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Eneter a new scope */ | ||||
| void dst_compile_scope(DstCompiler *c, int newfn) { | ||||
|     int32_t newcount, oldcount; | ||||
|     int32_t newlevel, oldlevel; | ||||
|     DstScope *scope; | ||||
|     oldcount = c->scopecount; | ||||
|     newcount = oldcount + 1; | ||||
|     oldlevel = c->scopecount | ||||
|         ? c->scopes[c->scopecount - 1].level | ||||
|         : 0; | ||||
|     newlevel = oldlevel + newfn; | ||||
|     if (newcount < c->scopecap) { | ||||
|         int32_t newcap = 2 * newcount; | ||||
|         c->scopes = realloc(c->scopes, newcap * sizeof(DstScope)); | ||||
|         if (NULL == c->scopes) { | ||||
|             DST_OUT_OF_MEMORY; | ||||
|         } | ||||
|         c->scopecap = newcap; | ||||
|     } | ||||
|     scope = c->scopes + oldcount; | ||||
|     c->scopecount = newcount; | ||||
|     dst_array_init(&scope->constants, 0); | ||||
|     dst_table_init(&scope->symbols, 4); | ||||
|     dst_table_init(&scope->constantrev, 4); | ||||
|  | ||||
|     scope->envs = NULL; | ||||
|     scope->envcount = 0; | ||||
|     scope->envcap = 0; | ||||
|      | ||||
|     scope->slots = NULL; | ||||
|     scope->slotcount = 0; | ||||
|     scope->slotcap = 0; | ||||
|     scope->bytecode_start = c->buffercount; | ||||
|  | ||||
|     scope->freeslots = NULL; | ||||
|     scope->freeslotcount = 0; | ||||
|     scope->freeslotcap = 0; | ||||
|     dst_compile_slotpool_init(&scope->slots); | ||||
|     dst_compile_slotpool_init(&scope->unorderedslots); | ||||
|  | ||||
|     scope->buffer_offset = c->buffer.count; | ||||
|     scope->nextslot = 0; | ||||
|     scope->lastslot = -1; | ||||
|     scope->level = newlevel; | ||||
|     scope->flags = newfn ? DST_SCOPE_FUNCTION : 0; | ||||
| } | ||||
|  | ||||
| DstSlot dst_slot_nil() { | ||||
|     DstSlot ret; | ||||
|     ret.index = 0; | ||||
|     ret.flags = (1 << DST_TYPE_NIL) | DST_SLOT_CONSTANT; | ||||
|     ret.constant = dst_wrap_nil(); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* Leave a scope.  Does not build closure*/ | ||||
| /* Leave a scope. */ | ||||
| void dst_compile_popscope(DstCompiler *c) { | ||||
|     DstScope *scope; | ||||
|     DstSlot ret; | ||||
|     dst_assert(c->scopecount, "could not pop scope"); | ||||
|     scope = c->scopes + --c->scopecount; | ||||
|     /* Move free slots to parent scope if not a new function */ | ||||
|     /* Move free slots to parent scope if not a new function. | ||||
|      * We need to know the total number of slots used when compiling the function. */ | ||||
|     if (!(scope->flags & DST_SCOPE_FUNCTION) && c->scopecount) { | ||||
|         int32_t i; | ||||
|         int32_t newcount; | ||||
|         DstScope *topscope = c->scopes + c->scopecount - 1; | ||||
|         topscope->nextslot = scope->nextslot;  | ||||
|         newcount = topscope->freeslotcount + scope->freeslotcount; | ||||
|         if (topscope->freeslotcap < newcount) { | ||||
|             topscope->freeslots = realloc(topscope->freeslot, sizeof(int32_t) * newcount); | ||||
|             if (NULL == topscope->freeslots) { | ||||
|                 DST_OUT_OF_MEMORY; | ||||
|             } | ||||
|             topscope->freeslotcap = newcount; | ||||
|         } | ||||
|         memcpy( | ||||
|             topscope->freeslots + topescope->freeslotcount, | ||||
|             scope->freeslots, | ||||
|             sizeof(int32_t) * scope->freeslotcount); | ||||
|         topscope->freeslotcount = newcount; | ||||
|         DstScope *newscope = dst_compile_topscope(c); | ||||
|         dst_compile_slotpool_extend(&newscope->slots, scope->slots.count); | ||||
|     } | ||||
|     dst_table_deinit(&scope->symbols); | ||||
|     dst_table_deinit(&scope->constantrev); | ||||
|     dst_array_deinit(&scope->constants); | ||||
|     free(scope->slots); | ||||
|     free(scope->freeslots); | ||||
|     dst_compile_slotpool_deinit(&scope->slots); | ||||
|     dst_compile_slotpool_deinit(&scope->unorderedslots); | ||||
|     free(scope->envs); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| #define dst_compile_topscope(c) ((c)->scopes + (c)->scopecount - 1) | ||||
|  | ||||
| /* Allocate a slot space */ | ||||
| static int32_t dst_compile_slotalloc(DstCompiler *c) { | ||||
| DstSlot *dst_compile_constantslot(DstCompiler *c, DstValue x) { | ||||
|     DstScope *scope = dst_compile_topscope(c); | ||||
|     if (scope->freeslotcount == 0) { | ||||
|         return scope->nextslot++; | ||||
|     } else { | ||||
|         return scope->freeslots[--scope->freeslotcount];  | ||||
|     } | ||||
| } | ||||
|  | ||||
| int dst_compile_slotmatch(DstFormOptions opts, DstSlot slot) { | ||||
|     return opts.type & slot.type; | ||||
| } | ||||
|  | ||||
| DstSlot dst_compile_normalslot(DstCompiler *c, uint32_t flags) { | ||||
|     DstSlot ret; | ||||
|     int32_t index = dst_compile_slotalloc(c); | ||||
|     ret.flags = flags; | ||||
|     ret.constant = dst_wrap_nil(); | ||||
|     ret.index = index; | ||||
|     ret.envindex = 0; | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| DstSlot dst_compile_constantslot(DstCompiler *c, DstValue x) { | ||||
|     DstSlot ret; | ||||
|     ret.flags = (1 << dst_type(x)) | DST_SLOT_CONSTANT; | ||||
|     ret.index = -1; | ||||
|     ret.constant = x; | ||||
|     ret.envindex = 0; | ||||
|     DstSlot *ret = dst_compile_slotpool_alloc(&scope->unorderedslots); | ||||
|     ret->flags = (1 << dst_type(x)) | DST_SLOT_CONSTANT | DST_SLOT_NOTEMPTY; | ||||
|     ret->index = -1; | ||||
|     ret->constant = x; | ||||
|     ret->envindex = scope->level; | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* Free a single slot */ | ||||
| void dst_compile_freeslot(DstCompiler *c, DstSlot slot) { | ||||
| void dst_compile_freeslot(DstCompiler *c, DstSlot *slot) { | ||||
|     DstScope *scope = dst_compile_topscope(c); | ||||
|     int32_t newcount = scope->freeslotcount + 1; | ||||
|     if (slot.flags & (DST_SLOT_CONSTANT | DST_SLOT_ERROR)) | ||||
|     if (slot->flags & (DST_SLOT_CONSTANT)) { | ||||
|         return; | ||||
|     if (scope->freeslotcap < newcount) { | ||||
|         int32_t newcap = 2 * newcount; | ||||
|         scope->freeslots = realloc(scope->freeslots, sizeof(int32_t) * newcap); | ||||
|         if (NULL == scope->freeslots) { | ||||
|             DST_OUT_OF_MEMORY; | ||||
|         } | ||||
|         scope->freeslotcap = newcap; | ||||
|     } | ||||
|     scope->freeslots[scope->freeslotcount] = slot.index; | ||||
|     scope->freeslotcount = newcount; | ||||
| } | ||||
|  | ||||
| /* Free an array of slots */ | ||||
| void dst_compile_freeslotarray(DstCompiler *c, DstArray *slots) { | ||||
|     int32_t i; | ||||
|     for (i = 0; i < slots->count; i++) { | ||||
|         dst_compile_freeslot(c, slots->data[i]); | ||||
|     if (slot->envindex != 0) { | ||||
|         return; | ||||
|     } | ||||
|     dst_compile_slotpool_free(&scope->slots, slot); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * The mechanism for passing environments to to closures is a bit complicated, | ||||
|  * The mechanism for passing environments to closures is a bit complicated, | ||||
|  * but ensures a few properties. | ||||
|  * * Environments are on the stack unless they need to be closurized | ||||
|  * * Environments can be shared between closures | ||||
| @@ -223,38 +241,39 @@ void dst_compile_freeslotarray(DstCompiler *c, DstArray *slots) { | ||||
|  */ | ||||
|  | ||||
| /* Allow searching for symbols. Return information about the symbol */ | ||||
| DstSlot dst_compile_resolve( | ||||
| DstSlot *dst_compile_resolve( | ||||
|         DstCompiler *c, | ||||
|         const DstValue *sourcemap, | ||||
|         const uint8_t *sym) { | ||||
|  | ||||
|     DstSlot ret; | ||||
|     DstSlot *ret = NULL; | ||||
|     DstScope *scope = dst_compile_topscope(c); | ||||
|     int32_t env_index = 0; | ||||
|     int foundlocal; | ||||
|     int foundlocal = 1; | ||||
|  | ||||
|     /* Search scopes for symbol, starting from top */ | ||||
|     while (scope >= c->scopes) { | ||||
|         DstValue check = dst_table_get(scope->symbols, dst_wrap_symbol(sym)); | ||||
|         if (dst_checktype(check, DST_INTEGER)) { | ||||
|             ret = scope->slots[dst_unwrap_integer(check)]; | ||||
|         DstValue check = dst_table_get(&scope->symbols, dst_wrap_symbol(sym)); | ||||
|         if (dst_checktype(check, DST_USERDATA)) { | ||||
|             ret = dst_unwrap_pointer(check); | ||||
|             goto found; | ||||
|         } | ||||
|         scope--; | ||||
|         if (scope->flags & DST_SCOPE_FUNCTION) | ||||
|             foundlocal = 0; | ||||
|     } | ||||
|  | ||||
|     /* Symbol not found */ | ||||
|     return dst_compile_error(c, sourcemap, dst_formatc("unknown symbol %q", sym)); | ||||
|     dst_compile_error(c, sourcemap, dst_formatc("unknown symbol %q", sym)); | ||||
|  | ||||
|     /* Symbol was found */ | ||||
|     found: | ||||
|  | ||||
|     /* Constants and errors can be returned immediately (they are stateless) */ | ||||
|     if (ret.flags & (DST_SLOT_CONSTANT | DST_SLOT_ERROR)) | ||||
|     /* Constants can be returned immediately (they are stateless) */ | ||||
|     if (ret->flags & DST_SLOT_CONSTANT) | ||||
|         return ret; | ||||
|  | ||||
|     /* non-local scope needs to expose its environment */ | ||||
|     foundlocal = scope == dst_compile_topscope(c); | ||||
|     if (!foundlocal) { | ||||
|         scope->flags |= DST_SCOPE_ENV; | ||||
|         if (scope->envcount < 1) { | ||||
| @@ -264,61 +283,86 @@ DstSlot dst_compile_resolve( | ||||
|                 DST_OUT_OF_MEMORY; | ||||
|             } | ||||
|             scope->envcap = 10; | ||||
|             scope->envs[0] = -1; | ||||
|             scope->envs[0] = 0; | ||||
|         } | ||||
|         scope++; | ||||
|     } | ||||
|  | ||||
|     /* Propogate env up to current scope */ | ||||
|     while (scope <= dst_compile_topscope(c)) { | ||||
|         int32_t j; | ||||
|         int32_t newcount = scope->envcount + 1; | ||||
|         int scopefound = 0; | ||||
|         /* Check if scope already has env. If so, break */ | ||||
|         for (j = 1; j < scope->envcount; j++) { | ||||
|             if (scope->envs[j] == env_index) { | ||||
|                 scopefound = 1; | ||||
|                 env_index = j; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         if (!scopefound) { | ||||
|             env_index = scope->envcount; | ||||
|             /* Ensure capacity for adding scope */ | ||||
|             if (newcount > scope->envcap) { | ||||
|                 int32_t newcap = 2 * newcount; | ||||
|                 scope->envs = realloc(scope->envs, sizeof(int32_t) * newcap); | ||||
|                 if (NULL == scope->envs) { | ||||
|                     DST_OUT_OF_MEMORY; | ||||
|         if (scope->flags & DST_SCOPE_FUNCTION) { | ||||
|             int32_t j; | ||||
|             int32_t newcount = scope->envcount + 1; | ||||
|             int scopefound = 0; | ||||
|             /* Check if scope already has env. If so, break */ | ||||
|             for (j = 1; j < scope->envcount; j++) { | ||||
|                 if (scope->envs[j] == env_index) { | ||||
|                     scopefound = 1; | ||||
|                     env_index = j; | ||||
|                     break; | ||||
|                 } | ||||
|                 scope->envcap = newcap; | ||||
|             } | ||||
|             scope->envs[scope->envcount] = env_index; | ||||
|             scope->envcount = newcount; | ||||
|             if (!scopefound) { | ||||
|                 env_index = scope->envcount; | ||||
|                 /* Ensure capacity for adding scope */ | ||||
|                 if (newcount > scope->envcap) { | ||||
|                     int32_t newcap = 2 * newcount; | ||||
|                     scope->envs = realloc(scope->envs, sizeof(int32_t) * newcap); | ||||
|                     if (NULL == scope->envs) { | ||||
|                         DST_OUT_OF_MEMORY; | ||||
|                     } | ||||
|                     scope->envcap = newcap; | ||||
|                 } | ||||
|                 scope->envs[scope->envcount] = env_index; | ||||
|                 scope->envcount = newcount; | ||||
|             } | ||||
|         } | ||||
|         scope++; | ||||
|     } | ||||
|      | ||||
|     /* Take the slot from the upper scope, and set its envindex before returning. */ | ||||
|     /* Store in the unordered slots so we don't modify the original slot. */ | ||||
|     if (!foundlocal) { | ||||
|         ret.envindex = env_index; | ||||
|         DstSlot *newret = dst_compile_slotpool_alloc(&scope->unorderedslots); | ||||
|         *newret = *ret; | ||||
|         newret->envindex = env_index; | ||||
|         ret = newret; | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| DstSlot *dst_compile_def(DstFormOptions opts, int32_t argn, const DstValue *argv) { | ||||
|     DstScope *scope; | ||||
|     DstSlot *rvalue; | ||||
|     DstFormOptions subopts; | ||||
|     DstValue check; | ||||
|     if (argn != 2) | ||||
|         dst_compile_cerror(opts.compiler, opts.sourcemap, "expected 2 arguments"); | ||||
|     if (!dst_checktype(argv[0], DST_SYMBOL)) | ||||
|         dst_compile_cerror(opts.compiler, opts.sourcemap, "expected symbol"); | ||||
|     scope = dst_compile_topscope(opts.compiler); | ||||
|     check = dst_table_get(&scope->symbols, argv[0]); | ||||
|     if (dst_checktype(check, DST_INTEGER)) { | ||||
|         dst_compile_cerror(opts.compiler, opts.sourcemap, "cannot redefine symbol"); | ||||
|     } | ||||
|     subopts = dst_compile_getopts_index(opts, 1); | ||||
|     rvalue = dst_compile_value(subopts); | ||||
|     dst_table_put(&scope->symbols, argv[0], dst_wrap_userdata(rvalue)); | ||||
|     return rvalue; | ||||
| } | ||||
|  | ||||
| /* Compile an array */ | ||||
|  | ||||
| /* Compile a single value */ | ||||
| DstSlot dst_compile_value(DstFormOptions opts) { | ||||
|     DstSlot ret; | ||||
| DstSlot *dst_compile_value(DstFormOptions opts) { | ||||
|     DstSlot *ret; | ||||
|     if (opts.compiler->recursion_guard <= 0) { | ||||
|         return dst_compiler_cerror(opts.compiler, opts.sourcemap, "recursed too deeply"); | ||||
|         dst_compile_cerror(opts.compiler, opts.sourcemap, "recursed too deeply"); | ||||
|     } | ||||
|     opts.compiler->recursion_guard--; | ||||
|     switch (dst_type(opts.x)) { | ||||
|         default: | ||||
|             ret = dst_compile_constantslot(opts.x); | ||||
|             ret = dst_compile_constantslot(opts.compiler, opts.x); | ||||
|             break; | ||||
|         case DST_SYMBOL: | ||||
|             { | ||||
| @@ -326,28 +370,241 @@ DstSlot dst_compile_value(DstFormOptions opts) { | ||||
|                 if (dst_string_length(sym) > 0 && sym[0] != ':') | ||||
|                     ret = dst_compile_resolve(opts.compiler, opts.sourcemap, sym); | ||||
|                 else | ||||
|                     ret = dst_compile_constantslot(opts.x); | ||||
|                     ret = dst_compile_constantslot(opts.compiler, opts.x); | ||||
|                 break; | ||||
|             } | ||||
|         case DST_TUPLE: | ||||
|             ret = dst_compile_tuple(opts);  | ||||
|             break; | ||||
|         case DST_ARRAY: | ||||
|             ret = dst_compile_array(opts);  | ||||
|             break; | ||||
|         case DST_STRUCT: | ||||
|             ret = dst_compile_struct(opts);  | ||||
|             break; | ||||
|         case DST_TABLE: | ||||
|             ret = dst_compile_table(opts); | ||||
|             break; | ||||
|         /*case DST_TUPLE:*/ | ||||
|             /*ret = dst_compile_tuple(opts); */ | ||||
|             /*break;*/ | ||||
|         /*case DST_ARRAY:*/ | ||||
|             /*ret = dst_compile_array(opts); */ | ||||
|             /*break;*/ | ||||
|         /*case DST_STRUCT:*/ | ||||
|             /*ret = dst_compile_struct(opts); */ | ||||
|             /*break;*/ | ||||
|         /*case DST_TABLE:*/ | ||||
|             /*ret = dst_compile_table(opts);*/ | ||||
|             /*break;*/ | ||||
|     } | ||||
|     opts.compiler->recursion_guard++; | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| DstSlot dst_compile_targetslot(DstFormOptions opts, DstSlot s); | ||||
| /* Emit a raw instruction with source mapping. */ | ||||
| void dst_compile_emit(DstCompiler *c, const DstValue *sourcemap, uint32_t instr) { | ||||
|     int32_t index = c->buffercount; | ||||
|     int32_t newcount = index + 1; | ||||
|     if (newcount > c->buffercap) { | ||||
|         int32_t newcap = 2 * newcount; | ||||
|         c->buffer = realloc(c->buffer, newcap * sizeof(int32_t)); | ||||
|         c->mapbuffer = realloc(c->mapbuffer, newcap * sizeof(int32_t) * 2); | ||||
|         if (NULL == c->buffer || NULL == c->mapbuffer) { | ||||
|             DST_OUT_OF_MEMORY; | ||||
|         } | ||||
|         c->buffercap = newcap; | ||||
|     } | ||||
|     c->buffercount = newcount; | ||||
|     if (NULL != sourcemap) { | ||||
|         c->mapbuffer[index][0] = dst_unwrap_integer(sourcemap[0]); | ||||
|         c->mapbuffer[index][1] = dst_unwrap_integer(sourcemap[1]); | ||||
|     } | ||||
|     c->buffer[index] = instr; | ||||
| } | ||||
|  | ||||
| /* Coerce any slot into the target slot. If no target is specified, return | ||||
|  * the slot unaltered. Otherwise, move and load upvalues as necesarry to set the slot. */ | ||||
| DstSlot dst_compile_coercetargetslot(DstFormOptions opts, DstSlot s); | ||||
| /* Represents a local slot - not a constant, and within a specified range. Also | ||||
|  * contains if it corresponds to a real slot. If temp, then the slot index | ||||
|  * should be free right after use */ | ||||
| typedef struct DstLocalSlot DstLocalSlot; | ||||
| struct DstLocalSlot { | ||||
|     DstSlot *orig; | ||||
|     int temp; | ||||
|     int dirty; | ||||
|     int32_t index; | ||||
| }; | ||||
|  | ||||
| /* Get the index of a constant */ | ||||
| static int32_t dst_compile_constant_index(DstCompiler *c, const DstValue *sourcemap, DstValue x) { | ||||
|     DstScope *scope = dst_compile_topscope(c); | ||||
|     DstValue check; | ||||
|     int32_t count = scope->constants.count; | ||||
|     check = dst_table_get(&scope->constantrev, x); | ||||
|     if (dst_checktype(check, DST_INTEGER)) { | ||||
|         return dst_unwrap_integer(check); | ||||
|     } | ||||
|     if (count >= 0xFFFF) { | ||||
|         dst_compile_cerror(c, sourcemap, "too many constants"); | ||||
|     } | ||||
|     dst_array_push(&scope->constants, x); | ||||
|     dst_table_put(&scope->constantrev, x, dst_wrap_integer(count)); | ||||
|     return count; | ||||
| } | ||||
|  | ||||
| /* Realize any slot to a local slot. Call this to get a slot index | ||||
|  * that can be used in an instruction. */ | ||||
| static DstLocalSlot dst_compile_slot_pre( | ||||
|         DstCompiler *c, | ||||
|         const DstValue *sourcemap, | ||||
|         int32_t max, | ||||
|         int32_t hint, | ||||
|         int isdest, | ||||
|         int nth, | ||||
|         DstSlot *s) { | ||||
|  | ||||
|     DstScope *scope = dst_compile_topscope(c); | ||||
|     DstLocalSlot ret; | ||||
|     ret.orig = s; | ||||
|     ret.dirty = isdest; | ||||
|     ret.temp = 0; | ||||
|  | ||||
|     if (s->flags & DST_SLOT_CONSTANT) { | ||||
|         int32_t cindex; | ||||
|         int32_t nextfree = dst_compile_slotpool_alloc(&scope->slots)->index; | ||||
|         if (hint >= 0 && hint <= 0xFF) { | ||||
|             ret.index = hint; | ||||
|         } else if (nextfree >= 0xF0) { | ||||
|             ret.index = 0xF0 + nth; | ||||
|             dst_compile_slotpool_freeindex(&scope->slots, nextfree); | ||||
|         } else { | ||||
|             ret.temp = 1; | ||||
|             ret.index = nextfree; | ||||
|         } | ||||
|         /* Use instructions for loading certain constants */ | ||||
|         switch (dst_type(s->constant)) { | ||||
|             case DST_NIL: | ||||
|                 dst_compile_emit(c, sourcemap, ((uint32_t)(ret.index) << 8) | DOP_LOAD_NIL); | ||||
|                 break; | ||||
|             case DST_TRUE: | ||||
|                 dst_compile_emit(c, sourcemap, ((uint32_t)(ret.index) << 8) | DOP_LOAD_TRUE); | ||||
|                 break; | ||||
|             case DST_FALSE: | ||||
|                 dst_compile_emit(c, sourcemap, ((uint32_t)(ret.index) << 8) | DOP_LOAD_FALSE); | ||||
|                 break; | ||||
|             case DST_INTEGER: | ||||
|                 { | ||||
|                     int32_t i = dst_unwrap_integer(s->constant); | ||||
|                     if (i <= INT16_MAX && i >= INT16_MIN) { | ||||
|                         dst_compile_emit(c, sourcemap,  | ||||
|                                 ((uint32_t)i << 16) | | ||||
|                                 ((uint32_t)(ret.index) << 8) | | ||||
|                                 DOP_LOAD_INTEGER); | ||||
|                         break; | ||||
|                     } | ||||
|                     /* fallthrough */ | ||||
|                 } | ||||
|             default: | ||||
|                 cindex = dst_compile_constant_index(c, sourcemap, s->constant); | ||||
|                 if (isdest) | ||||
|                     dst_compile_cerror(c, sourcemap, "cannot write to a constant"); | ||||
|                 dst_compile_emit(c, sourcemap,  | ||||
|                         ((uint32_t)cindex << 16) | | ||||
|                         ((uint32_t)(ret.index) << 8) | | ||||
|                         DOP_LOAD_CONSTANT); | ||||
|                 break; | ||||
|         } | ||||
|     } else if (s->envindex > 0 || s->index > max) { | ||||
|         /* Get a local slot to shadow the environment or far slot */ | ||||
|         int32_t nextfree = dst_compile_slotpool_alloc(&scope->slots)->index; | ||||
|         if (hint >= 0 && hint <= 0xFF) { | ||||
|             ret.index = hint; | ||||
|         } else if (nextfree >= 0xF0) { | ||||
|             ret.index = 0xF0 + nth; | ||||
|             dst_compile_slotpool_freeindex(&scope->slots, nextfree); | ||||
|         } else { | ||||
|             ret.temp = 1; | ||||
|             ret.index = nextfree; | ||||
|         } | ||||
|         if (!isdest) { | ||||
|             /* Move the remote slot into the local space */ | ||||
|             if (s->envindex > 0) { | ||||
|                 /* Load the higher slot */ | ||||
|                 dst_compile_emit(c, sourcemap,  | ||||
|                         ((uint32_t)(s->index) << 24) | | ||||
|                         ((uint32_t)(s->envindex) << 16) | | ||||
|                         ((uint32_t)(ret.index) << 8) | | ||||
|                         DOP_LOAD_UPVALUE); | ||||
|             } else { | ||||
|                 /* Slot is a far slot: greater than 0xFF. Get | ||||
|                  * the far data and bring it to the near slot. */ | ||||
|                 dst_compile_emit(c, sourcemap,  | ||||
|                         ((uint32_t)(s->index) << 16) | | ||||
|                         ((uint32_t)(ret.index) << 8) | | ||||
|                         DOP_MOVE_NEAR); | ||||
|             } | ||||
|         } | ||||
|     } else if (hint >= 0 && hint <= 0xFF && isdest) { | ||||
|         ret.index = hint; | ||||
|     } else { | ||||
|         /* We have a normal slot that fits in the required bit width */             | ||||
|         ret.index = s->index; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* Call this on a DstLocalSlot to free the slot or sync any changes | ||||
|  * made after the instruction has been emitted. */ | ||||
| static void dst_compile_slot_post( | ||||
|         DstCompiler *c, | ||||
|         const DstValue *sourcemap, | ||||
|         DstLocalSlot ls) { | ||||
|     DstSlot *s = ls.orig; | ||||
|     DstScope *scope = dst_compile_topscope(c); | ||||
|     if (ls.temp) | ||||
|         dst_compile_slotpool_freeindex(&scope->slots, ls.index); | ||||
|     if (ls.dirty) { | ||||
|         /* We need to save the data in the local slot to the original slot */ | ||||
|         if (s->envindex > 0) { | ||||
|             /* Load the higher slot */ | ||||
|             dst_compile_emit(c, sourcemap,  | ||||
|                     ((uint32_t)(s->index) << 24) | | ||||
|                     ((uint32_t)(s->envindex) << 16) | | ||||
|                     ((uint32_t)(ls.index) << 8) | | ||||
|                     DOP_SET_UPVALUE); | ||||
|         } else if (s->index != ls.index) { | ||||
|             /* There was a local remapping */ | ||||
|             dst_compile_emit(c, sourcemap,  | ||||
|                     ((uint32_t)(s->index) << 16) | | ||||
|                     ((uint32_t)(ls.index) << 8) | | ||||
|                     DOP_MOVE_FAR); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void dst_compile_pop_funcdef(DstCompiler *c) { | ||||
|     DstScope *scope = dst_compiler_topscope(c); | ||||
|     DstFuncDef *def; | ||||
|  | ||||
|     /* Initialize funcdef */ | ||||
|     def = dst_alloc(DST_MEMORY_FUNCDEF, sizeof(DstFuncDef)); | ||||
|     def->environments = NULL; | ||||
|     def->constants = NULL; | ||||
|     def->bytecode = NULL; | ||||
|     def->flags = 0; | ||||
|     def->slotcount = 0; | ||||
|     def->arity = 0; | ||||
|     def->constants_length = 0; | ||||
|     def->bytecode_length = 0; | ||||
|     def->environments_length = 1; | ||||
| } | ||||
|  | ||||
| DstCompileResults dst_compile(DstCompileOptions opts) { | ||||
|     DstCompiler c; | ||||
|  | ||||
|     if (setjmp(c.on_error)) { | ||||
|         c.results.status = DST_COMPILE_ERROR; | ||||
|         dst_compile_cleanup(&c); | ||||
|         results.funcdef = NULL; | ||||
|         return c.results; | ||||
|     } | ||||
|  | ||||
|     /* Initialize the compiler struct */ | ||||
|     c.scopecount = 0; | ||||
|     c.scopecap = 0; | ||||
|     c.scopes = NULL; | ||||
|     c.buffercap = 0; | ||||
|     c.buffercount = 0; | ||||
|     c->buffer = NULL; | ||||
|     c->mapbuffer; | ||||
|     c->recursion_guard = 1024; | ||||
|  | ||||
|      | ||||
| } | ||||
|   | ||||
							
								
								
									
										173
									
								
								core/compile.h
									
									
									
									
									
								
							
							
						
						
									
										173
									
								
								core/compile.h
									
									
									
									
									
								
							| @@ -24,20 +24,24 @@ | ||||
| #define DST_COMPILE_H | ||||
|  | ||||
| #include <dst/dst.h> | ||||
| #include <setjmp.h> | ||||
| #include "opcodes.h" | ||||
|  | ||||
| /* Compiler typedefs */ | ||||
| typedef struct DstCompiler DstCompiler; | ||||
| typedef struct FormOptions FormOptions; | ||||
| typedef struct SlotTracker SlotTracker; | ||||
| typedef struct DstScope DstScope; | ||||
| typedef struct DstSlot DstSlot; | ||||
| typedef struct DstFormOptions DstFormOptions; | ||||
| typedef struct DstCFunctionOptimizer DstCFunctionOptimizer; | ||||
|  | ||||
| #define DST_SLOT_CONSTANT 0x10000 | ||||
| #define DST_SLOT_TEMP 0x20000 | ||||
| #define DST_SLOT_NAMED 0x20000 | ||||
| #define DST_SLOT_RETURNED 0x40000 | ||||
| #define DST_SLOT_NIL 0x80000 | ||||
| #define DST_SLOT_MUTABLE 0x100000 | ||||
| #define DST_SLOT_ERROR 0x200000 | ||||
| #define DST_SLOT_NOTEMPTY 0x200000 | ||||
|  | ||||
| #define DST_SLOTTYPE_ANY 0xFFFF | ||||
|  | ||||
| @@ -47,7 +51,7 @@ struct DstSlot { | ||||
|     int32_t envindex; /* 0 is local, positive number is an upvalue */ | ||||
|     uint32_t flags; | ||||
|     DstValue constant; /* If the slot has a constant value */ | ||||
| } | ||||
| }; | ||||
|  | ||||
| /* Most forms that return a constant will not generate any bytecode */ | ||||
|  | ||||
| @@ -62,67 +66,59 @@ struct DstSlot { | ||||
|  * do | ||||
|  */ | ||||
|  | ||||
| #define DST_OPTIMIZER_CONSTANTS 0x10000 | ||||
| #define DST_OPTIMIZER_BYTECODE 0x20000 | ||||
| #define DST_OPTIMIZER_PARTIAL_CONSTANTS 0x40000 | ||||
| #define DST_OPTIMIZER_SYSCALL 0x80000 | ||||
|  | ||||
| /* A grouping of optimization on a cfunction given certain conditions | ||||
|  * on the arguments (such as all constants, or some known types). The appropriate | ||||
|  * optimizations should be tried before compiling a normal function call. */ | ||||
| struct DstCFunctionOptimizer { | ||||
|     uint32_t flags; /* Indicate what kind of optimizations can be performed. */ | ||||
|         /*Also what kind of types can be returned*/ | ||||
|     int32_t syscall; | ||||
| } | ||||
|  | ||||
| #define DST_SCOPE_FUNCTION 1 | ||||
| #define DST_SCOPE_LASTSLOT 2 | ||||
| #define DST_SCOPE_FIRSTSLOT 4 | ||||
| #define DST_SCOPE_ENV | ||||
| #define DST_SCOPE_ENV 8 | ||||
|  | ||||
| /* Hold a bunch of slots together */ | ||||
| typedef struct DstSlotPool DstSlotPool; | ||||
| struct DstSlotPool { | ||||
|     DstSlot *s; | ||||
|     int32_t count; | ||||
|     int32_t cap; | ||||
|     int32_t free; | ||||
| }; | ||||
|  | ||||
| /* A lexical scope during compilation */ | ||||
| struct DstScope { | ||||
|     int32_t level; | ||||
|     DstArray constants; /* Constants for the funcdef */ | ||||
|     DstTable symbols; /* Map symbols -> Slot inidices */ | ||||
|     DstTable constantrev; /* Map constants -> constant inidices */ | ||||
|     DstTable symbols; /* Map symbols -> Slot pointers */ | ||||
|  | ||||
|     /* Hold all slots in use. Data structures that store | ||||
|      * slots should link them to this datatstructure */ | ||||
|     DstSlot *slots; | ||||
|     int32_t slotcount; | ||||
|     int32_t slotcap; | ||||
|     DstSlotPool slots; | ||||
|     DstSlotPool unorderedslots; | ||||
|  | ||||
|     /* A vector of freed slots. */ | ||||
|     int32_t *freeslots; | ||||
|     int32_t freeslotcount; | ||||
|     int32_t freeslotcap; | ||||
|  | ||||
|     int32_t lastslot; | ||||
|     int32_t nextslot; | ||||
|  | ||||
|     /* Referenced closure environemts. The values at each index correspond | ||||
|     /* Referenced closure environents. The values at each index correspond | ||||
|      * to which index to get the environment from in the parent. The enironment | ||||
|      * that corresponds to the direct parent's stack will always have value 0. */ | ||||
|     int32_t *envs; | ||||
|     int32_t envcount; | ||||
|     int32_t envcap; | ||||
|  | ||||
|     int32_t buffer_offset; /* Where the bytecode for this scope begins */ | ||||
|  | ||||
|     int32_t bytecode_start; | ||||
|     uint32_t flags; | ||||
| } | ||||
| }; | ||||
|  | ||||
| #define dst_compile_topscope(c) ((c)->scopes + (c)->scopecount - 1) | ||||
|  | ||||
| /* Compilation state */ | ||||
| struct DstCompiler { | ||||
|     jmp_buf on_error; | ||||
|     int recursion_guard; | ||||
|     int32_t scopecount; | ||||
|     int32_t scopecap; | ||||
|     DstScope *scopes; | ||||
|     DstBuffer buffer; | ||||
|     DstBuffer mapbuffer; | ||||
|     int32_t error_start; | ||||
|     int32_t error_end; | ||||
|     DstValue error; | ||||
|     int recursion_guard; | ||||
|      | ||||
|     int32_t buffercap; | ||||
|     int32_t buffercount; | ||||
|     uint32_t *buffer; | ||||
|     int32_t (*mapbuffer)[2]; | ||||
|  | ||||
|     DstCompileResults results; | ||||
| }; | ||||
|  | ||||
| #define DST_FOPTS_TAIL 0x10000 | ||||
| @@ -136,58 +132,48 @@ struct DstFormOptions { | ||||
|     uint32_t flags; /* bit set of accepted primitive types */ | ||||
| }; | ||||
|  | ||||
| typedef enum DstCompileStatus { | ||||
|     DST_COMPILE_OK, | ||||
|     DST_COMPILE_ERROR | ||||
| } DstCompileStatus; | ||||
|  | ||||
| /* Results of compilation */ | ||||
| typedef struct DstCompileResults { | ||||
|     DstCompileStatus status; | ||||
|     DstFuncDef *funcdef; | ||||
|     const uint8_t *error; | ||||
| } DstCompileResults; | ||||
|  | ||||
| typedef struct DstCompileOptions { | ||||
|     uint32_t flags; | ||||
|     const DstValue *sourcemap; | ||||
|     DstValue src; | ||||
|     int32_t target; | ||||
| /* A grouping of optimizations on a cfunction given certain conditions | ||||
|  * on the arguments (such as all constants, or some known types). The appropriate | ||||
|  * optimizations should be tried before compiling a normal function call. */ | ||||
| struct DstCFunctionOptimizer { | ||||
|     DstCFunction cfun; | ||||
|     DstSlot (*optimize)(DstFormOptions opts, int32_t argn, const DstValue *argv); | ||||
| }; | ||||
| typedef struct DstSpecial { | ||||
|     const char *name; | ||||
|     DstSlot (*compile)(DstFormOptions opts, int32_t argn, const DstValue *argv); | ||||
| } DstSpecial; | ||||
|  | ||||
| /* Compiler handlers. Used to compile different kinds of expressions. */ | ||||
| typedef DstSlot (*DstFormCompiler)(DstFormOptions opts); | ||||
| /* An array of optimizers sorted by key */ | ||||
| extern DstCFunctionOptimizer dst_compiler_optimizers[255]; | ||||
|  | ||||
| /* An array of special forms */ | ||||
| extern DstSpecial dst_compiler_specials[16]; | ||||
|  | ||||
| void dst_compile_slotpool_init(DstSlotPool *pool); | ||||
| void dst_compile_slotpool_deinit(DstSlotPool *pool); | ||||
| DstSlot *dst_compile_slotpool_alloc(DstSlotPool *pool); | ||||
| void dst_compile_slotpool_extend(DstSlotPool *pool, int32_t extra); | ||||
| void dst_compile_slotpool_free(DstSlotPool *pool, DstSlot *s); | ||||
| void dst_compile_slotpool_freeindex(DstSlotPool *pool, int32_t index); | ||||
|  | ||||
| /* Dispatch to correct form compiler */ | ||||
| DstSlot dst_compile_value(DstFormOptions opts); | ||||
|  | ||||
| /* Compile basic types */ | ||||
| DstSlot dst_compile_constant(DstFormOptions opts); | ||||
| DstSlot dst_compile_symbol(DstFormOptions opts); | ||||
| DstSlot dst_copmile_array(DstFormOptions opts); | ||||
| DstSlot dst_copmile_struct(DstFormOptions opts); | ||||
| DstSlot dst_copmile_table(DstFormOptions opts); | ||||
|  | ||||
| /* Tuple compiliation will handle much of the work */ | ||||
| DstSlot dst_compile_tuple(DstFormOptions opts); | ||||
| DstSlot *dst_compile_value(DstFormOptions opts); | ||||
|  | ||||
| /* Compile special forms */ | ||||
| DstSlot dst_compile_do(DstFormOptions opts); | ||||
| DstSlot dst_compile_fn(DstFormOptions opts); | ||||
| DstSlot dst_compile_cond(DstFormOptions opts); | ||||
| DstSlot dst_compile_while(DstFormOptions opts); | ||||
| DstSlot dst_compile_quote(DstFormOptions opts); | ||||
| DstSlot dst_compile_def(DstFormOptions opts); | ||||
| DstSlot dst_compile_var(DstFormOptions opts); | ||||
| DstSlot dst_compile_varset(DstFormOptions opts); | ||||
|  | ||||
| /* Compile source code into FuncDef. */ | ||||
| DstCompileResults dst_compile(DstCompileOptions opts); | ||||
| DstSlot *dst_compile_do(DstFormOptions opts, int32_t argn, const DstValue *argv); | ||||
| DstSlot *dst_compile_fn(DstFormOptions opts, int32_t argn, const DstValue *argv); | ||||
| DstSlot *dst_compile_cond(DstFormOptions opts, int32_t argn, const DstValue *argv); | ||||
| DstSlot *dst_compile_while(DstFormOptions opts, int32_t argn, const DstValue *argv); | ||||
| DstSlot *dst_compile_quote(DstFormOptions opts, int32_t argn, const DstValue *argv); | ||||
| DstSlot *dst_compile_def(DstFormOptions opts, int32_t argn, const DstValue *argv); | ||||
| DstSlot *dst_compile_var(DstFormOptions opts, int32_t argn, const DstValue *argv); | ||||
| DstSlot *dst_compile_varset(DstFormOptions opts, int32_t argn, const DstValue *argv); | ||||
|  | ||||
| /****************************************************/ | ||||
|  | ||||
| DstSlot dst_compile_error(DstCompiler *c, const DstValue *sourcemap, const uint8_t *m); | ||||
| DstSlot dst_compile_cerror(DstCompiler *c, const DstValue *sourcemap, const char *m); | ||||
| void dst_compile_error(DstCompiler *c, const DstValue *sourcemap, const uint8_t *m); | ||||
| void dst_compile_cerror(DstCompiler *c, const DstValue *sourcemap, const char *m); | ||||
|  | ||||
| /* Use these to get sub options. They will traverse the source map so | ||||
|  * compiler errors make sense. Then modify the returned options. */ | ||||
| @@ -196,25 +182,16 @@ DstFormOptions dst_compile_getopts_key(DstFormOptions opts, DstValue key); | ||||
| DstFormOptions dst_compile_getopts_value(DstFormOptions opts, DstValue key); | ||||
|  | ||||
| void dst_compile_scope(DstCompiler *c, int newfn); | ||||
| DstSlot dst_compile_popscope(DstCompiler *c); | ||||
| void dst_compile_popscope(DstCompiler *c); | ||||
|  | ||||
| int dst_compile_slotmatch(DstFormOptions opts, DstSlot slot); | ||||
| DstSlot dst_compile_normalslot(DstCompiler *c, uint32_t types); | ||||
| DstSlot dst_compile_constantslot(DstCompiler *c, DstValue x); | ||||
| void dst_compile_freeslot(DstCompiler *c, DstSlot slot); | ||||
| void dst_compile_freeslotarray(DstCompiler *c, DstArray *slots); | ||||
| DstSlot *dst_compile_constantslot(DstCompiler *c, DstValue x); | ||||
| void dst_compile_freeslot(DstCompiler *c, DstSlot *slot); | ||||
|  | ||||
| /* Search for a symbol */ | ||||
| DstSlot dst_compile_resolve(DstCompiler *c, const DstValue *sourcemap, const uint8_t *sym); | ||||
| DstSlot *dst_compile_resolve(DstCompiler *c, const DstValue *sourcemap, const uint8_t *sym); | ||||
|  | ||||
| /* Get a local slot that can be used as the desination for whatever is compiling. */ | ||||
| DstSlot dst_compile_targetslot(DstFormOptions opts, DstSlot s); | ||||
| /* Emit instructions. */ | ||||
|  | ||||
| /* Coerce any slot into the target slot. If no target is specified, return | ||||
|  * the slot unaltered. Otherwise, move and load upvalues as necesarry to set the slot. */ | ||||
| DstSlot dst_compile_coercetargetslot(DstFormOptions opts, DstSlot s); | ||||
|  | ||||
| DstSlot dst_compile_realizeslot(DstCompiler *c, DstSlot s); | ||||
| DstSlot dst_compile_returnslot(DstCompiler *c, DstSlot s); | ||||
| void dst_compile_emit(DstCompiler *c, const DstValue *sourcemap, uint32_t instr); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -55,7 +55,8 @@ enum DstOpCode { | ||||
|     DOP_SHIFT_RIGHT_IMMEDIATE, | ||||
|     DOP_SHIFT_RIGHT_UNSIGNED, | ||||
|     DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE, | ||||
|     DOP_MOVE, | ||||
|     DOP_MOVE_FAR, | ||||
|     DOP_MOVE_NEAR, | ||||
|     DOP_JUMP, | ||||
|     DOP_JUMP_IF, | ||||
|     DOP_JUMP_IF_NOT, | ||||
| @@ -64,9 +65,11 @@ enum DstOpCode { | ||||
|     DOP_EQUALS, | ||||
|     DOP_COMPARE, | ||||
|     DOP_LOAD_NIL, | ||||
|     DOP_LOAD_BOOLEAN, | ||||
|     DOP_LOAD_TRUE, | ||||
|     DOP_LOAD_FALSE, | ||||
|     DOP_LOAD_INTEGER, | ||||
|     DOP_LOAD_CONSTANT, | ||||
|     DOP_LOAD_SELF, | ||||
|     DOP_LOAD_UPVALUE, | ||||
|     DOP_SET_UPVALUE, | ||||
|     DOP_CLOSURE, | ||||
|   | ||||
							
								
								
									
										21
									
								
								core/vm.c
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								core/vm.c
									
									
									
									
									
								
							| @@ -280,11 +280,16 @@ int dst_continue() { | ||||
|         pc++; | ||||
|         vm_next(); | ||||
|  | ||||
|         case DOP_MOVE: | ||||
|         case DOP_MOVE_NEAR: | ||||
|         stack[oparg(1, 0xFF)] = stack[oparg(2, 0xFFFF)]; | ||||
|         pc++; | ||||
|         vm_next(); | ||||
|  | ||||
|         case DOP_MOVE_FAR: | ||||
|         stack[oparg(2, 0xFFFF)] = stack[oparg(1, 0xFF)]; | ||||
|         pc++; | ||||
|         vm_next(); | ||||
|  | ||||
|         case DOP_JUMP: | ||||
|         pc += (*(int32_t *)pc) >> 8; | ||||
|         vm_next(); | ||||
| @@ -342,8 +347,13 @@ int dst_continue() { | ||||
|         pc++; | ||||
|         vm_next(); | ||||
|  | ||||
|         case DOP_LOAD_BOOLEAN: | ||||
|         stack[oparg(1, 0xFF)] = dst_wrap_boolean(oparg(2, 0xFFFF)); | ||||
|         case DOP_LOAD_TRUE: | ||||
|         stack[oparg(1, 0xFFFFFF)] = dst_wrap_boolean(1); | ||||
|         pc++; | ||||
|         vm_next(); | ||||
|  | ||||
|         case DOP_LOAD_FALSE: | ||||
|         stack[oparg(1, 0xFFFFFF)] = dst_wrap_boolean(0); | ||||
|         pc++; | ||||
|         vm_next(); | ||||
|  | ||||
| @@ -358,6 +368,11 @@ int dst_continue() { | ||||
|         pc++; | ||||
|         vm_next(); | ||||
|  | ||||
|         case DOP_LOAD_SELF: | ||||
|         stack[oparg(1, 0xFFFFFF)] = dst_wrap_function(func); | ||||
|         pc++; | ||||
|         vm_next(); | ||||
|  | ||||
|         case DOP_LOAD_UPVALUE: | ||||
|         { | ||||
|             int32_t eindex = oparg(2, 0xFF); | ||||
|   | ||||
| @@ -20,8 +20,8 @@ | ||||
| * IN THE SOFTWARE. | ||||
| */ | ||||
|  | ||||
| #ifndef DST_NANBOX_H_defined | ||||
| #define DST_NANBOX_H_defined | ||||
| #ifndef DST_H_defined | ||||
| #define DST_H_defined | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| @@ -680,6 +680,30 @@ int dst_continue(); | ||||
| int dst_run(DstValue callee); | ||||
| DstValue dst_transfer(DstFiber *fiber, DstValue x); | ||||
|  | ||||
| /* Compile */ | ||||
| typedef enum DstCompileStatus { | ||||
|     DST_COMPILE_OK, | ||||
|     DST_COMPILE_ERROR | ||||
| } DstCompileStatus; | ||||
|  | ||||
| /* Results of compilation */ | ||||
| typedef struct DstCompileResults { | ||||
|     DstCompileStatus status; | ||||
|     DstFuncDef *funcdef; | ||||
|     const uint8_t *error; | ||||
|     int32_t error_start; | ||||
|     int32_t error_end; | ||||
| } DstCompileResults; | ||||
|  | ||||
| typedef struct DstCompileOptions { | ||||
|     uint32_t flags; | ||||
|     const DstValue *sourcemap; | ||||
|     DstValue src; | ||||
| } DstCompileOptions; | ||||
|  | ||||
| /* Compile source code into FuncDef. */ | ||||
| DstCompileResults dst_compile(DstCompileOptions opts); | ||||
|  | ||||
| /* GC */ | ||||
|  | ||||
| /* The metadata header associated with an allocated block of memory */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 bakpakin
					bakpakin