Modularize compiler.

This commit is contained in:
Calvin Rose 2018-07-01 11:52:15 -04:00
parent fde9751eab
commit f4fc4a0bcc
13 changed files with 1055 additions and 679 deletions

View File

@ -37,12 +37,16 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(COMPILER_SOURCES
src/compiler/compile.c
src/compiler/cfuns.c
src/compiler/emit.c
src/compiler/regalloc.c
src/compiler/run.c
src/compiler/specials.c
src/compiler/stl.c
dststlbootstrap.h
src/compiler/compile.h
src/compiler/emit.h
src/compiler/regalloc.h
)
set(CORE_SOURCES

View File

@ -50,10 +50,12 @@ endif
# Source headers
DST_HEADERS=$(sort $(wildcard src/include/dst/*.h))
DST_LOCAL_HEADERS=$(sort $(wildcard src/*/*.h))
DST_LIBHEADERS=$(sort $(wildcard src/include/headerlibs/*.h))
DST_GENERATED_HEADERS=src/mainclient/clientinit.gen.h \
src/compiler/dststlbootstrap.gen.h
DST_ALL_HEADERS=$(DST_HEADERS) \
DST_ALL_HEADERS=$(DST_LOCAL_HEADERS) \
$(DST_HEADERS) \
$(DST_LIB_HEADERS) \
$(DST_GENERATED_HEADERS)

View File

@ -7,7 +7,7 @@
(loop [k :range [0 len]]
(put tab (get s k) k))
(loop [i :range [0 len], j :range [0 len]]
(def k (get tab (- 0 (get s i) (get s j))))
(when (and k (not= k i) (not= k j) (not= i j))
(put solutions {i true j true k true} true)))
(def k (get tab (- 0 (get s i) (get s j))))
(when (and k (not= k i) (not= k j) (not= i j))
(put solutions {i true j true k true} true)))
(map keys (keys solution)))

View File

@ -1,11 +1,10 @@
# Example of dst bytecode assembly
# Fibonacci sequence, implemented with naive recursion.
(def fibasm (asm.asm '{
(def fibasm (asm '{
arity 1
bytecode [
(ldi 1 0x2) # $1 = 2
(lt 1 0 1) # $1 = $0 < $1
(ltim 1 0 0x2) # $1 = $0 < 2
(jmpif 1 :done) # if ($1) goto :done
(lds 1) # $1 = self
(addim 0 0 -0x1) # $0 = $0 - 1

View File

@ -13,7 +13,7 @@
(def defn :macro
"Define a function. Equivalent to (def name (fn name [args] ...))."
(fn [name & more]
(fn defn [name & more]
(def len (length more))
(def fstart (fn recur [i]
(def {i ith} more)
@ -30,9 +30,8 @@
(def defmacro :macro
"Define a macro."
(do
(def defn* (get (get _env 'defn) :value))
(fn [name & more]
(apply1 defn* (array.concat
(fn defmacro [name & more]
(apply1 defn (array.concat
@[name :macro] more)))))
(defmacro defmacro-

View File

@ -24,7 +24,7 @@
#include <dst/dstcorelib.h>
#include "compile.h"
#include <headerlibs/vector.h>
#undef DST_V_NODEF_GROW
#include "emit.h"
/* This logic needs to be expanded for more types */
@ -76,7 +76,6 @@ static DstSlot *foldc(DstSlot *slots, Dst (*fn)(Dst lhs, Dst rhs)) {
static DstSlot opreduce(DstFopts opts, DstSlot *args, int op) {
DstCompiler *c = opts.compiler;
int32_t i, len;
int32_t op1, op2;
len = dst_v_count(args);
DstSlot t;
if (len == 0) {
@ -86,15 +85,9 @@ static DstSlot opreduce(DstFopts opts, DstSlot *args, int op) {
}
t = dstc_gettarget(opts);
/* Compile initial two arguments */
op1 = dstc_preread(c, 0xFF, 1, args[0]);
op2 = dstc_preread(c, 0xFF, 2, args[1]);
dstc_emit(c, (t.index << 8) | (op1 << 16) | (op2 << 24) | op);
dstc_postread(c, args[0], op1);
dstc_postread(c, args[1], op2);
dstc_emit_sss(c, op, t, args[0], args[1]);
for (i = 2; i < len; i++) {
op1 = dstc_preread(c, 0xFF, 1, args[i]);
dstc_emit(c, (t.index << 8) | (t.index << 16) | (op1 << 24) | op);
dstc_postread(c, args[i], op1);
dstc_emit_sss(c, op, t, t, args[i]);
}
return t;
}

View File

@ -23,42 +23,12 @@
#include <dst/dst.h>
#include <dst/dstcorelib.h>
#include "compile.h"
#include "emit.h"
#define DST_V_DEF_COPYMEM
#define DST_V_DEF_FLATTENMEM
#include <headerlibs/vector.h>
#undef DST_V_DEF_COPYMEM
#undef DST_V_DEF_FLATTENMEM
static void dstc_ast_push(DstCompiler *c, const Dst *tup) {
DstSourceMapping mapping;
if (c->result.status == DST_COMPILE_ERROR) {
return;
}
mapping.line = dst_tuple_sm_line(tup);
mapping.column = dst_tuple_sm_col(tup);
if (!mapping.line) {
/* Reuse previous mapping */
mapping = c->current_mapping;
} else {
c->current_mapping = mapping;
}
dst_v_push(c->ast_stack, mapping);
}
static void dstc_ast_pop(DstCompiler *c) {
if (c->result.status == DST_COMPILE_ERROR) {
return;
}
dst_v_pop(c->ast_stack);
if (dst_v_count(c->ast_stack)) {
c->current_mapping = dst_v_last(c->ast_stack);
} else {
c->current_mapping.line = 0;
c->current_mapping.column = 0;
}
}
DstFopts dstc_fopts_default(DstCompiler *c) {
DstFopts ret;
ret.compiler = c;
@ -100,151 +70,21 @@ const DstKV *dstc_next(Dst ds, const DstKV *kv) {
}
}
/* Allocate a slot index */
int32_t dstc_lsloti(DstCompiler *c) {
DstScope *scope = &dst_v_last(c->scopes);
/* Get the nth bit in the array */
int32_t i, biti, len;
biti = -1;
len = dst_v_count(scope->slots);
for (i = 0; i < len; i++) {
uint32_t block = scope->slots[i];
if (block == 0xFFFFFFFF) continue;
biti = i << 5; /* + clz(block) */
while (block & 1) {
biti++;
block >>= 1;
}
break;
}
if (biti == -1) {
/* Extend bit vector for slots */
dst_v_push(scope->slots, len == 7 ? 0xFFFF0000 : 0);
biti = len << 5;
}
/* set the bit at index biti */
scope->slots[biti >> 5] |= 1 << (biti & 0x1F);
if (biti > scope->smax)
scope->smax = biti;
return biti;
}
/* Allocate a given slot index */
static void slotalloci(DstCompiler *c, int32_t index) {
int32_t count;
int32_t block = index >> 5;
DstScope *scope = &dst_v_last(c->scopes);
if (index < 0) return;
while ((count = dst_v_count(scope->slots)) <= block) {
/* Extend bit vector for slots */
dst_v_push(scope->slots, count == 7 ? 0xFFFF0000 : 0);
}
scope->slots[block] |= 1 << (index & 0x1F);
}
/* Free a slot index */
void dstc_sfreei(DstCompiler *c, int32_t index) {
DstScope *scope = &dst_v_last(c->scopes);
/* Don't free the pre allocated slots */
if (index >= 0 && (index < 0xF0 || index > 0xFF) &&
index < (dst_v_count(scope->slots) << 5))
scope->slots[index >> 5] &= ~(1 << (index & 0x1F));
}
/* Allocate a local near (n) slot and return its index. Slot
* has maximum index max. Common value for max would be 0xFF,
* the highest slot index representable with one byte. */
int32_t dstc_lslotn(DstCompiler *c, int32_t max, int32_t nth) {
int32_t ret = dstc_lsloti(c);
if (ret > max) {
dstc_sfreei(c, ret);
ret = 0xF0 + nth;
}
return ret;
}
/* Free a slot */
void dstc_freeslot(DstCompiler *c, DstSlot s) {
if (s.flags & (DST_SLOT_CONSTANT | DST_SLOT_REF | DST_SLOT_NAMED)) return;
if (s.envindex >= 0) return;
dstc_sfreei(c, s.index);
dstc_regalloc_free(&c->scope->ra, s.index);
}
/* Add a slot to a scope with a symbol associated with it (def or var). */
void dstc_nameslot(DstCompiler *c, const uint8_t *sym, DstSlot s) {
DstScope *scope = &dst_v_last(c->scopes);
SymPair sp;
sp.sym = sym;
sp.slot = s;
sp.keep = 0;
sp.slot.flags |= DST_SLOT_NAMED;
dst_v_push(scope->syms, sp);
}
/* Enter a new scope */
void dstc_scope(DstCompiler *c, int flags) {
DstScope scope;
scope.consts = NULL;
scope.syms = NULL;
scope.envs = NULL;
scope.defs = NULL;
scope.slots = NULL;
scope.smax = -1;
scope.selfconst = -1;
scope.bytecode_start = dst_v_count(c->buffer);
scope.flags = flags;
/* Inherit slots */
if ((!(flags & DST_SCOPE_FUNCTION)) && dst_v_count(c->scopes)) {
DstScope *oldscope = &dst_v_last(c->scopes);
scope.smax = oldscope->smax;
scope.slots = dst_v_copy(oldscope->slots);
}
dst_v_push(c->scopes, scope);
}
/* Leave a scope. */
void dstc_popscope(DstCompiler *c) {
DstScope scope;
int32_t oldcount = dst_v_count(c->scopes);
dst_assert(oldcount, "could not pop scope");
scope = dst_v_last(c->scopes);
dst_v_pop(c->scopes);
/* 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 | DST_SCOPE_UNUSED)) && oldcount > 1) {
int32_t i;
DstScope *newscope = &dst_v_last(c->scopes);
if (newscope->smax < scope.smax)
newscope->smax = scope.smax;
/* Keep upvalue slots */
for (i = 0; i < dst_v_count(scope.syms); i++) {
SymPair pair = scope.syms[i];
if (pair.keep) {
/* The variable should not be lexically accessible */
pair.sym = NULL;
dst_v_push(newscope->syms, pair);
slotalloci(c, pair.slot.index);
}
}
}
/* Free the scope */
dst_v_free(scope.consts);
dst_v_free(scope.syms);
dst_v_free(scope.envs);
dst_v_free(scope.defs);
dst_v_free(scope.slots);
}
/* Leave a scope but keep a slot allocated. */
void dstc_popscope_keepslot(DstCompiler *c, DstSlot retslot) {
dstc_popscope(c);
if (retslot.envindex < 0 && retslot.index >= 0) {
slotalloci(c, retslot.index);
}
dst_v_push(c->scope->syms, sp);
}
/* Create a slot with a constant */
@ -257,20 +97,109 @@ DstSlot dstc_cslot(Dst x) {
return ret;
}
/* Get a temp near slot */
DstSlot dstc_nearslot(DstCompiler *c, DstcRegisterTemp tag) {
DstSlot ret;
ret.flags = DST_SLOTTYPE_ANY;
ret.index = dstc_getreg_temp(c, tag);
ret.constant = dst_wrap_nil();
ret.envindex = -1;
return ret;
}
/* Get a temp near slot */
DstSlot dstc_farslot(DstCompiler *c) {
DstSlot ret;
ret.flags = DST_SLOTTYPE_ANY;
ret.index = dstc_getreg(c);
ret.constant = dst_wrap_nil();
ret.envindex = -1;
return ret;
}
/* Enter a new scope */
void dstc_scope(DstScope *s, DstCompiler *c, int flags, const char *name) {
DstScope scope;
scope.name = name;
scope.child = NULL;
scope.consts = NULL;
scope.syms = NULL;
scope.envs = NULL;
scope.defs = NULL;
scope.selfconst = -1;
scope.bytecode_start = dst_v_count(c->buffer);
scope.flags = flags;
*s = scope;
/* Inherit slots */
if ((!(flags & DST_SCOPE_FUNCTION)) && c->scope) {
dstc_regalloc_clone(&s->ra, &(c->scope->ra));
} else {
dstc_regalloc_init(&s->ra);
}
/* Link parent and child and update pointer */
s->parent = c->scope;
if (c->scope)
c->scope->child = s;
c->scope = s;
}
/* Leave a scope. */
void dstc_popscope(DstCompiler *c) {
DstScope *oldscope = c->scope;
DstScope *newscope = oldscope->parent;
/* 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 (!(oldscope->flags & (DST_SCOPE_FUNCTION | DST_SCOPE_UNUSED)) && newscope) {
if (newscope->ra.max < oldscope->ra.max)
newscope->ra.max = oldscope->ra.max;
/* Keep upvalue slots */
for (int32_t i = 0; i < dst_v_count(oldscope->syms); i++) {
SymPair pair = oldscope->syms[i];
if (pair.keep) {
/* The variable should not be lexically accessible */
pair.sym = NULL;
dst_v_push(newscope->syms, pair);
dstc_regalloc_touch(&newscope->ra, pair.slot.index);
}
}
}
/* Free the old scope */
dst_v_free(oldscope->consts);
dst_v_free(oldscope->syms);
dst_v_free(oldscope->envs);
dst_v_free(oldscope->defs);
dstc_regalloc_deinit(&oldscope->ra);
/* Update pointer */
if (newscope)
newscope->child = NULL;
c->scope = newscope;
}
/* Leave a scope but keep a slot allocated. */
void dstc_popscope_keepslot(DstCompiler *c, DstSlot retslot) {
DstScope *scope;
dstc_popscope(c);
scope = c->scope;
if (scope && retslot.envindex < 0 && retslot.index >= 0) {
dstc_regalloc_touch(&scope->ra, retslot.index);
}
}
/* Allow searching for symbols. Return information about the symbol */
DstSlot dstc_resolve(
DstCompiler *c,
const uint8_t *sym) {
DstSlot ret = dstc_cslot(dst_wrap_nil());
DstScope *top = &dst_v_last(c->scopes);
DstScope *scope = top;
DstScope *scope = c->scope;
SymPair *pair;
int foundlocal = 1;
int unused = 0;
/* Search scopes for symbol, starting from top */
while (scope >= c->scopes) {
while (scope) {
int32_t i, len;
if (scope->flags & DST_SCOPE_UNUSED)
unused = 1;
@ -285,7 +214,7 @@ DstSlot dstc_resolve(
}
if (scope->flags & DST_SCOPE_FUNCTION)
foundlocal = 0;
scope--;
scope = scope->parent;
}
/* Symbol not found - check for global */
@ -326,14 +255,15 @@ DstSlot dstc_resolve(
/* non-local scope needs to expose its environment */
pair->keep = 1;
while (scope >= c->scopes && !(scope->flags & DST_SCOPE_FUNCTION)) scope--;
dst_assert(scope >= c->scopes, "invalid scopes");
while (scope && !(scope->flags & DST_SCOPE_FUNCTION))
scope = scope->parent;
dst_assert(scope, "invalid scopes");
scope->flags |= DST_SCOPE_ENV;
scope++;
scope = scope->child;
/* Propogate env up to current scope */
int32_t envindex = -1;
while (scope <= top) {
while (scope) {
if (scope->flags & DST_SCOPE_FUNCTION) {
int32_t j, len;
int scopefound = 0;
@ -353,272 +283,20 @@ DstSlot dstc_resolve(
envindex = len;
}
}
scope++;
scope = scope->child;
}
ret.envindex = envindex;
return ret;
}
/* Emit a raw instruction with source mapping. */
void dstc_emit(DstCompiler *c, uint32_t instr) {
dst_v_push(c->buffer, instr);
dst_v_push(c->mapbuffer, c->current_mapping);
}
/* Add a constant to the current scope. Return the index of the constant. */
static int32_t dstc_const(DstCompiler *c, Dst x) {
DstScope *scope = &dst_v_last(c->scopes);
int32_t i, len;
/* Get the topmost function scope */
while (scope > c->scopes) {
if (scope->flags & DST_SCOPE_FUNCTION)
break;
scope--;
}
/* Check if already added */
len = dst_v_count(scope->consts);
for (i = 0; i < len; i++) {
if (dst_equals(x, scope->consts[i]))
return i;
}
/* Ensure not too many constsants. */
if (len >= 0xFFFF) {
dstc_cerror(c, "too many constants");
return 0;
}
dst_v_push(scope->consts, x);
return len;
}
/* Load a constant into a local slot */
static void dstc_loadconst(DstCompiler *c, Dst k, int32_t dest) {
switch (dst_type(k)) {
case DST_NIL:
dstc_emit(c, (dest << 8) | DOP_LOAD_NIL);
break;
case DST_TRUE:
dstc_emit(c, (dest << 8) | DOP_LOAD_TRUE);
break;
case DST_FALSE:
dstc_emit(c, (dest << 8) | DOP_LOAD_FALSE);
break;
case DST_INTEGER:
{
int32_t i = dst_unwrap_integer(k);
if (i <= INT16_MAX && i >= INT16_MIN) {
dstc_emit(c,
(i << 16) |
(dest << 8) |
DOP_LOAD_INTEGER);
break;
}
goto do_constant;
}
default:
do_constant:
{
int32_t cindex = dstc_const(c, k);
dstc_emit(c,
(cindex << 16) |
(dest << 8) |
DOP_LOAD_CONSTANT);
break;
}
}
}
/* Realize any slot to a local slot. Call this to get a slot index
* that can be used in an instruction. */
int32_t dstc_preread(
DstCompiler *c,
int32_t max,
int nth,
DstSlot s) {
int32_t ret;
if (s.flags & DST_SLOT_REF)
max = 0xFF;
if (s.flags & (DST_SLOT_CONSTANT | DST_SLOT_REF)) {
ret = dstc_lslotn(c, 0xFF, nth);
dstc_loadconst(c, s.constant, ret);
/* If we also are a reference, deref the one element array */
if (s.flags & DST_SLOT_REF) {
dstc_emit(c,
(ret << 16) |
(ret << 8) |
DOP_GET_INDEX);
}
} else if (s.envindex >= 0 || s.index > max) {
ret = dstc_lslotn(c, max, nth);
dstc_emit(c,
((uint32_t)(s.index) << 24) |
((uint32_t)(s.envindex) << 16) |
((uint32_t)(ret) << 8) |
DOP_LOAD_UPVALUE);
} else if (s.index > max) {
ret = dstc_lslotn(c, max, nth);
dstc_emit(c,
((uint32_t)(s.index) << 16) |
((uint32_t)(ret) << 8) |
DOP_MOVE_NEAR);
} else {
/* We have a normal slot that fits in the required bit width */
ret = s.index;
}
return ret;
}
/* Call this to release a read handle after emitting the instruction. */
void dstc_postread(DstCompiler *c, DstSlot s, int32_t index) {
if (index != s.index || s.envindex >= 0 || s.flags & DST_SLOT_CONSTANT) {
/* We need to free the temporary slot */
dstc_sfreei(c, index);
}
}
/* Check if two slots are equal */
int dstc_sequal(DstSlot lhs, DstSlot rhs) {
if (lhs.flags == rhs.flags &&
lhs.index == rhs.index &&
lhs.envindex == rhs.envindex) {
if (lhs.flags & (DST_SLOT_REF | DST_SLOT_CONSTANT)) {
return dst_equals(lhs.constant, rhs.constant);
} else {
return 1;
}
}
return 0;
}
/* Move values from one slot to another. The destination must
* be writeable (not a literal). */
void dstc_copy(
DstCompiler *c,
DstSlot dest,
DstSlot src) {
int writeback = 0;
int32_t destlocal = -1;
int32_t srclocal = -1;
int32_t reflocal = -1;
/* Can't write to constants */
if (dest.flags & DST_SLOT_CONSTANT) {
dstc_cerror(c, "cannot write to constant");
return;
}
/* Short circuit if dest and source are equal */
if (dstc_sequal(dest, src)) return;
/* Types of slots - src */
/* constants */
/* upvalues */
/* refs */
/* near index */
/* far index */
/* Types of slots - dest */
/* upvalues */
/* refs */
/* near index */
/* far index */
/* If dest is a near index, do some optimization */
if (dest.envindex < 0 && dest.index >= 0 && dest.index <= 0xFF) {
if (src.flags & DST_SLOT_CONSTANT) {
dstc_loadconst(c, src.constant, dest.index);
} else if (src.flags & DST_SLOT_REF) {
dstc_loadconst(c, src.constant, dest.index);
dstc_emit(c,
(dest.index << 16) |
(dest.index << 8) |
DOP_GET_INDEX);
} else if (src.envindex >= 0) {
dstc_emit(c,
(src.index << 24) |
(src.envindex << 16) |
(dest.index << 8) |
DOP_LOAD_UPVALUE);
} else {
dstc_emit(c,
(src.index << 16) |
(dest.index << 8) |
DOP_MOVE_NEAR);
}
return;
}
/* Process: src -> srclocal -> destlocal -> dest */
/* src -> srclocal */
srclocal = dstc_preread(c, 0xFF, 1, src);
/* Pull down dest (find destlocal) */
if (dest.flags & DST_SLOT_REF) {
writeback = 1;
destlocal = srclocal;
reflocal = dstc_lslotn(c, 0xFF, 2);
dstc_emit(c,
(dstc_const(c, dest.constant) << 16) |
(reflocal << 8) |
DOP_LOAD_CONSTANT);
} else if (dest.envindex >= 0) {
writeback = 2;
destlocal = srclocal;
} else if (dest.index > 0xFF) {
writeback = 3;
destlocal = srclocal;
} else {
destlocal = dest.index;
}
/* srclocal -> destlocal */
if (srclocal != destlocal) {
dstc_emit(c,
((uint32_t)(srclocal) << 16) |
((uint32_t)(destlocal) << 8) |
DOP_MOVE_NEAR);
}
/* destlocal -> dest */
if (writeback == 1) {
dstc_emit(c,
(destlocal << 16) |
(reflocal << 8) |
DOP_PUT_INDEX);
} else if (writeback == 2) {
dstc_emit(c,
((uint32_t)(dest.index) << 24) |
((uint32_t)(dest.envindex) << 16) |
((uint32_t)(destlocal) << 8) |
DOP_SET_UPVALUE);
} else if (writeback == 3) {
dstc_emit(c,
((uint32_t)(dest.index) << 16) |
((uint32_t)(destlocal) << 8) |
DOP_MOVE_FAR);
}
/* Cleanup */
if (reflocal >= 0) {
dstc_sfreei(c, reflocal);
}
dstc_postread(c, src, srclocal);
}
/* Generate the return instruction for a slot. */
DstSlot dstc_return(DstCompiler *c, DstSlot s) {
if (!(s.flags & DST_SLOT_RETURNED)) {
if (s.flags & DST_SLOT_CONSTANT && dst_checktype(s.constant, DST_NIL)) {
if (s.flags & DST_SLOT_CONSTANT && dst_checktype(s.constant, DST_NIL))
dstc_emit(c, DOP_RETURN_NIL);
} else {
int32_t ls = dstc_preread(c, 0xFFFF, 1, s);
dstc_emit(c, DOP_RETURN | (ls << 8));
dstc_postread(c, s, ls);
}
else
dstc_emit_s(c, DOP_RETURN, s);
s.flags |= DST_SLOT_RETURNED;
}
return s;
@ -636,7 +314,7 @@ DstSlot dstc_gettarget(DstFopts opts) {
slot.envindex = -1;
slot.constant = dst_wrap_nil();
slot.flags = 0;
slot.index = dstc_lslotn(opts.compiler, 0xFF, 4);
slot.index = dstc_getreg_temp(opts.compiler, DSTC_REGTEMP_3);
}
return slot;
}
@ -667,35 +345,12 @@ DstSlot *dstc_toslotskv(DstCompiler *c, Dst ds) {
/* Push slots load via dstc_toslots. */
void dstc_pushslots(DstCompiler *c, DstSlot *slots) {
int32_t i;
for (i = 0; i < dst_v_count(slots) - 2; i += 3) {
int32_t ls1 = dstc_preread(c, 0xFF, 1, slots[i]);
int32_t ls2 = dstc_preread(c, 0xFF, 2, slots[i + 1]);
int32_t ls3 = dstc_preread(c, 0xFF, 3, slots[i + 2]);
dstc_emit(c,
(ls3 << 24) |
(ls2 << 16) |
(ls1 << 8) |
DOP_PUSH_3);
dstc_postread(c, slots[i], ls1);
dstc_postread(c, slots[i + 1], ls2);
dstc_postread(c, slots[i + 2], ls3);
}
if (i == dst_v_count(slots) - 2) {
int32_t ls1 = dstc_preread(c, 0xFF, 1, slots[i]);
int32_t ls2 = dstc_preread(c, 0xFFFF, 2, slots[i + 1]);
dstc_emit(c,
(ls2 << 16) |
(ls1 << 8) |
DOP_PUSH_2);
dstc_postread(c, slots[i], ls1);
dstc_postread(c, slots[i + 1], ls2);
} else if (i == dst_v_count(slots) - 1) {
int32_t ls1 = dstc_preread(c, 0xFFFFFF, 1, slots[i]);
dstc_emit(c,
(ls1 << 8) |
DOP_PUSH);
dstc_postread(c, slots[i], ls1);
}
for (i = 0; i < dst_v_count(slots) - 2; i += 3)
dstc_emit_sss(c, DOP_PUSH_3, slots[i], slots[i+1], slots[i+2]);
if (i == dst_v_count(slots) - 2)
dstc_emit_ss(c, DOP_PUSH_2, slots[i], slots[i+1]);
else if (i == dst_v_count(slots) - 1)
dstc_emit_s(c, DOP_PUSH, slots[i]);
}
/* Free slots loaded via dstc_toslots */
@ -712,14 +367,15 @@ void dstc_freeslots(DstCompiler *c, DstSlot *slots) {
* bytecode. */
void dstc_throwaway(DstFopts opts, Dst x) {
DstCompiler *c = opts.compiler;
DstScope unusedScope;
int32_t bufstart = dst_v_count(c->buffer);
int32_t mapbufstart = dst_v_count(c->mapbuffer);
dstc_scope(c, DST_SCOPE_UNUSED);
dstc_scope(&unusedScope, c, DST_SCOPE_UNUSED, "unusued");
dstc_value(opts, x);
dstc_popscope(c);
if (NULL != c->buffer) {
if (c->buffer) {
dst_v__cnt(c->buffer) = bufstart;
if (NULL != c->mapbuffer)
if (c->mapbuffer)
dst_v__cnt(c->mapbuffer) = mapbufstart;
}
}
@ -727,7 +383,6 @@ void dstc_throwaway(DstFopts opts, Dst x) {
/* Compile a call or tailcall instruction */
static DstSlot dstc_call(DstFopts opts, DstSlot *slots, DstSlot fun) {
DstSlot retslot;
int32_t localindex;
DstCompiler *c = opts.compiler;
int specialized = 0;
if (fun.flags & DST_SLOT_CONSTANT) {
@ -742,16 +397,21 @@ static DstSlot dstc_call(DstFopts opts, DstSlot *slots, DstSlot fun) {
}
if (!specialized) {
dstc_pushslots(c, slots);
localindex = dstc_preread(c, 0xFF, 1, fun);
int32_t fun_register;
if (opts.flags & DST_FOPTS_TAIL) {
dstc_emit(c, (localindex << 8) | DOP_TAILCALL);
fun_register = dstc_to_reg(c, fun);
dstc_emit(c, DOP_TAILCALL | (fun_register << 8));
retslot = dstc_cslot(dst_wrap_nil());
retslot.flags = DST_SLOT_RETURNED;
} else {
retslot = dstc_gettarget(opts);
dstc_emit(c, (localindex << 16) | (retslot.index << 8) | DOP_CALL);
fun_register = dstc_to_tempreg(c, fun, DSTC_REGTEMP_0);
dstc_emit(c, DOP_CALL |
(retslot.index << 8) |
(fun_register << 16));
/* Don't free ret register */
}
dstc_postread(c, fun, localindex);
dstc_free_reg(c, fun, fun_register);
}
dstc_freeslots(c, slots);
return retslot;
@ -795,6 +455,7 @@ DstSlot dstc_value(DstFopts opts, Dst x) {
DstSlot ret;
DstCompiler *c = opts.compiler;
int macrorecur = 0;
DstSourceMapping last_mapping = c->current_mapping;
opts.compiler->recursion_guard--;
recur:
if (dstc_iserr(&opts)) {
@ -821,7 +482,11 @@ recur:
DstSlot head;
DstFopts subopts = dstc_fopts_default(c);
const Dst *tup = dst_unwrap_tuple(x);
dstc_ast_push(c, tup);
/* Get ast mapping */
if (dst_tuple_sm_line(tup) > 0) {
c->current_mapping.line = dst_tuple_sm_line(tup);
c->current_mapping.column = dst_tuple_sm_col(tup);
}
/* Empty tuple is tuple literal */
if (dst_tuple_length(tup) == 0) {
compiled = 1;
@ -867,9 +532,9 @@ recur:
ret = dstc_call(opts, dstc_toslots(c, tup + 1, dst_tuple_length(tup) - 1), head);
}
}
/* Pop source mapping for tuple - macrorecur+1 times */
for (int i = 0; i <= macrorecur; i++)
dstc_ast_pop(c);
/* Pop source mapping */
if (c->result.status != DST_COMPILE_ERROR)
c->current_mapping = last_mapping;
}
break;
case DST_ARRAY:
@ -891,7 +556,7 @@ recur:
if (opts.flags & DST_FOPTS_TAIL) {
ret = dstc_return(opts.compiler, ret);
}
if (opts.flags & DST_FOPTS_HINT && !dstc_sequal(opts.hint, ret)) {
if (opts.flags & DST_FOPTS_HINT) {
dstc_copy(opts.compiler, opts.hint, ret);
ret = opts.hint;
}
@ -901,40 +566,40 @@ recur:
/* Compile a funcdef */
DstFuncDef *dstc_pop_funcdef(DstCompiler *c) {
DstScope scope = dst_v_last(c->scopes);
DstScope *scope = c->scope;
DstFuncDef *def = dst_funcdef_alloc();
def->slotcount = scope.smax + 1;
def->slotcount = scope->ra.max + 1;
dst_assert(scope.flags & DST_SCOPE_FUNCTION, "expected function scope");
dst_assert(scope->flags & DST_SCOPE_FUNCTION, "expected function scope");
/* Copy envs */
def->environments_length = dst_v_count(scope.envs);
def->environments = dst_v_flatten(scope.envs);
def->environments_length = dst_v_count(scope->envs);
def->environments = dst_v_flatten(scope->envs);
def->constants_length = dst_v_count(scope.consts);
def->constants = dst_v_flatten(scope.consts);
def->constants_length = dst_v_count(scope->consts);
def->constants = dst_v_flatten(scope->consts);
def->defs_length = dst_v_count(scope.defs);
def->defs = dst_v_flatten(scope.defs);
def->defs_length = dst_v_count(scope->defs);
def->defs = dst_v_flatten(scope->defs);
/* Copy bytecode (only last chunk) */
def->bytecode_length = dst_v_count(c->buffer) - scope.bytecode_start;
def->bytecode_length = dst_v_count(c->buffer) - scope->bytecode_start;
if (def->bytecode_length) {
size_t s = sizeof(int32_t) * def->bytecode_length;
def->bytecode = malloc(s);
if (NULL == def->bytecode) {
DST_OUT_OF_MEMORY;
}
memcpy(def->bytecode, c->buffer + scope.bytecode_start, s);
dst_v__cnt(c->buffer) = scope.bytecode_start;
memcpy(def->bytecode, c->buffer + scope->bytecode_start, s);
dst_v__cnt(c->buffer) = scope->bytecode_start;
if (NULL != c->mapbuffer) {
size_t s = sizeof(DstSourceMapping) * def->bytecode_length;
def->sourcemap = malloc(s);
if (NULL == def->sourcemap) {
DST_OUT_OF_MEMORY;
}
memcpy(def->sourcemap, c->mapbuffer + scope.bytecode_start, s);
dst_v__cnt(c->mapbuffer) = scope.bytecode_start;
memcpy(def->sourcemap, c->mapbuffer + scope->bytecode_start, s);
dst_v__cnt(c->mapbuffer) = scope->bytecode_start;
}
}
@ -943,7 +608,7 @@ DstFuncDef *dstc_pop_funcdef(DstCompiler *c) {
def->arity = 0;
def->flags = 0;
if (scope.flags & DST_SCOPE_ENV) {
if (scope->flags & DST_SCOPE_ENV) {
def->flags |= DST_FUNCDEF_FLAG_NEEDSENV;
}
@ -955,13 +620,12 @@ DstFuncDef *dstc_pop_funcdef(DstCompiler *c) {
/* Initialize a compiler */
static void dstc_init(DstCompiler *c, DstTable *env, const uint8_t *where) {
c->scopes = NULL;
c->scope = NULL;
c->buffer = NULL;
c->mapbuffer = NULL;
c->recursion_guard = DST_RECURSION_GUARD;
c->env = env;
c->source = where;
c->ast_stack = NULL;
c->current_mapping.line = 0;
c->current_mapping.column = 0;
/* Init result */
@ -974,23 +638,21 @@ static void dstc_init(DstCompiler *c, DstTable *env, const uint8_t *where) {
/* Deinitialize a compiler struct */
static void dstc_deinit(DstCompiler *c) {
while (dst_v_count(c->scopes)) dstc_popscope(c);
dst_v_free(c->scopes);
dst_v_free(c->buffer);
dst_v_free(c->mapbuffer);
dst_v_free(c->ast_stack);
c->env = NULL;
}
/* Compile a form. */
DstCompileResult dst_compile(Dst source, DstTable *env, const uint8_t *where) {
DstCompiler c;
DstScope rootscope;
DstFopts fopts;
dstc_init(&c, env, where);
/* Push a function scope */
dstc_scope(&c, DST_SCOPE_FUNCTION | DST_SCOPE_TOP);
dstc_scope(&rootscope, &c, DST_SCOPE_FUNCTION | DST_SCOPE_TOP, "root");
/* Set initial form options */
fopts.compiler = &c;
@ -1006,6 +668,7 @@ DstCompileResult dst_compile(Dst source, DstTable *env, const uint8_t *where) {
c.result.funcdef = def;
} else {
c.result.error_mapping = c.current_mapping;
dstc_popscope(&c);
}
dstc_deinit(&c);

View File

@ -26,6 +26,7 @@
#include <dst/dst.h>
#include <dst/dstcompile.h>
#include <dst/dstopcodes.h>
#include "regalloc.h"
/* Compiler typedefs */
typedef struct DstCompiler DstCompiler;
@ -69,15 +70,21 @@ typedef struct SymPair {
/* A lexical scope during compilation */
struct DstScope {
/* For debugging */
const char *name;
/* Scopes are doubly linked list */
DstScope *parent;
DstScope *child;
/* Constants for this funcdef */
Dst *consts;
/* Map of symbols to slots. Use a simple linear scan for symbols. */
SymPair *syms;
/* Bit vector with allocated slot indices. Used to allocate new slots */
uint32_t *slots;
int32_t smax;
/* Regsiter allocator */
DstcRegisterAllocator ra;
/* FuncDefs */
DstFuncDef **defs;
@ -97,13 +104,14 @@ struct DstScope {
/* Compilation state */
struct DstCompiler {
int recursion_guard;
DstScope *scopes;
/* Pointer to current scope */
DstScope *scope;
uint32_t *buffer;
DstSourceMapping *mapbuffer;
/* Keep track of where we are in the source */
DstSourceMapping *ast_stack;
DstSourceMapping current_mapping;
/* Hold the environment */
@ -158,46 +166,14 @@ int dstc_iserr(DstFopts *opts);
/* Helper for iterating tables and structs */
const DstKV *dstc_next(Dst ds, const DstKV *kv);
/* Allocate a slot index */
int32_t dstc_lsloti(DstCompiler *c);
/* Free a slot index */
void dstc_sfreei(DstCompiler *c, int32_t index);
/* Allocate a local near (n) slot and return its index. Slot
* has maximum index max. Common value for max would be 0xFF,
* the highest slot index representable with one byte. */
int32_t dstc_lslotn(DstCompiler *c, int32_t max, int32_t nth);
/* Free a slot */
void dstc_freeslot(DstCompiler *c, DstSlot s);
/* Add a slot to a scope with a symbol associated with it (def or var). */
void dstc_nameslot(DstCompiler *c, const uint8_t *sym, DstSlot s);
/* Realize any slot to a local slot. Call this to get a slot index
* that can be used in an instruction. */
int32_t dstc_preread(
DstCompiler *c,
int32_t max,
int nth,
DstSlot s);
/* Call this to release a read handle after emitting the instruction. */
void dstc_postread(DstCompiler *c, DstSlot s, int32_t index);
/* Move value from one slot to another. Cannot copy to constant slots. */
void dstc_copy(
DstCompiler *c,
DstSlot dest,
DstSlot src);
DstSlot dstc_nearslot(DstCompiler *c, DstcRegisterTemp tag);
DstSlot dstc_farslot(DstCompiler *c);
/* Throw away some code after checking that it is well formed. */
void dstc_throwaway(DstFopts opts, Dst x);
/* Generate the return instruction for a slot. */
DstSlot dstc_return(DstCompiler *c, DstSlot s);
/* Get a target slot for emitting an instruction. Will always return
* a local slot. */
DstSlot dstc_gettarget(DstFopts opts);
@ -214,6 +190,9 @@ void dstc_pushslots(DstCompiler *c, DstSlot *slots);
/* Free slots loaded via dstc_toslots */
void dstc_freeslots(DstCompiler *c, DstSlot *slots);
/* Generate the return instruction for a slot. */
DstSlot dstc_return(DstCompiler *c, DstSlot s);
/* Store an error */
void dstc_error(DstCompiler *c, const uint8_t *m);
void dstc_cerror(DstCompiler *c, const char *m);
@ -221,11 +200,8 @@ void dstc_cerror(DstCompiler *c, const char *m);
/* Dispatch to correct form compiler */
DstSlot dstc_value(DstFopts opts, Dst x);
/* Check if two slots are equal */
int dstc_sequal(DstSlot lhs, DstSlot rhs);
/* Push and pop from the scope stack */
void dstc_scope(DstCompiler *c, int newfn);
void dstc_scope(DstScope *s, DstCompiler *c, int flags, const char *name);
void dstc_popscope(DstCompiler *c);
void dstc_popscope_keepslot(DstCompiler *c, DstSlot retslot);
DstFuncDef *dstc_pop_funcdef(DstCompiler *c);
@ -236,7 +212,4 @@ DstSlot dstc_cslot(Dst x);
/* Search for a symbol */
DstSlot dstc_resolve(DstCompiler *c, const uint8_t *sym);
/* Emit instructions. */
void dstc_emit(DstCompiler *c, uint32_t instr);
#endif

385
src/compiler/emit.c Normal file
View File

@ -0,0 +1,385 @@
/*
* Copyright (c) 2018 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.
*/
#include <dst/dstcompile.h>
#include <headerlibs/vector.h>
#include "emit.h"
/* Get a register */
int32_t dstc_getreg(DstCompiler *c) {
int32_t reg = dstc_regalloc_1(&c->scope->ra);
if (reg > 0xFFFF) {
dstc_cerror(c, "ran out of internal registers");
}
return reg;
}
/* Get a register less than 256 */
int32_t dstc_getreg_temp(DstCompiler *c, DstcRegisterTemp tag) {
return dstc_regalloc_temp(&c->scope->ra, tag);
}
/* Emit a raw instruction with source mapping. */
void dstc_emit(DstCompiler *c, uint32_t instr) {
dst_v_push(c->buffer, instr);
dst_v_push(c->mapbuffer, c->current_mapping);
}
/* Add a constant to the current scope. Return the index of the constant. */
static int32_t dstc_const(DstCompiler *c, Dst x) {
DstScope *scope = c->scope;
int32_t i, len;
/* Get the topmost function scope */
while (scope) {
if (scope->flags & DST_SCOPE_FUNCTION)
break;
scope = scope->parent;
}
/* Check if already added */
len = dst_v_count(scope->consts);
for (i = 0; i < len; i++) {
if (dst_equals(x, scope->consts[i]))
return i;
}
/* Ensure not too many constsants. */
if (len >= 0xFFFF) {
dstc_cerror(c, "too many constants");
return 0;
}
dst_v_push(scope->consts, x);
return len;
}
/* Load a constant into a local register */
static void dstc_loadconst(DstCompiler *c, Dst k, int32_t reg) {
switch (dst_type(k)) {
case DST_NIL:
dstc_emit(c, (reg << 8) | DOP_LOAD_NIL);
break;
case DST_TRUE:
dstc_emit(c, (reg << 8) | DOP_LOAD_TRUE);
break;
case DST_FALSE:
dstc_emit(c, (reg << 8) | DOP_LOAD_FALSE);
break;
case DST_INTEGER:
{
int32_t i = dst_unwrap_integer(k);
if (i <= INT16_MAX && i >= INT16_MIN) {
dstc_emit(c,
(i << 16) |
(reg << 8) |
DOP_LOAD_INTEGER);
break;
}
goto do_constant;
}
default:
do_constant:
{
int32_t cindex = dstc_const(c, k);
dstc_emit(c,
(cindex << 16) |
(reg << 8) |
DOP_LOAD_CONSTANT);
break;
}
}
}
/* Convert a slot to a two byte register */
int32_t dstc_to_reg(DstCompiler *c, DstSlot s) {
int32_t reg;
if (s.flags & (DST_SLOT_CONSTANT | DST_SLOT_REF)) {
reg = dstc_getreg(c);
dstc_loadconst(c, s.constant, reg);
/* If we also are a reference, deref the one element array */
if (s.flags & DST_SLOT_REF) {
dstc_emit(c,
(reg << 16) |
(reg << 8) |
DOP_GET_INDEX);
}
} else if (s.envindex >= 0 || s.index > 0xFF) {
reg = dstc_getreg(c);
dstc_emit(c,
((uint32_t)(s.index) << 24) |
((uint32_t)(s.envindex) << 16) |
((uint32_t)(reg) << 8) |
DOP_LOAD_UPVALUE);
} else {
/* We have a normal slot that fits in the required bit width */
reg = s.index;
}
return reg;
}
/* Convert a slot to a temporary 1 byte register */
int32_t dstc_to_tempreg(DstCompiler *c, DstSlot s, DstcRegisterTemp tag) {
int32_t reg;
if (s.flags & (DST_SLOT_CONSTANT | DST_SLOT_REF)) {
reg = dstc_getreg_temp(c, tag);
dstc_loadconst(c, s.constant, reg);
/* If we also are a reference, deref the one element array */
if (s.flags & DST_SLOT_REF) {
dstc_emit(c,
(reg << 16) |
(reg << 8) |
DOP_GET_INDEX);
}
} else if (s.envindex >= 0 || s.index > 0xFF) {
reg = dstc_getreg_temp(c, tag);
dstc_emit(c,
((uint32_t)(s.index) << 24) |
((uint32_t)(s.envindex) << 16) |
((uint32_t)(reg) << 8) |
DOP_LOAD_UPVALUE);
} else if (s.index > 0xFF) {
reg = dstc_getreg_temp(c, tag);
dstc_emit(c,
((uint32_t)(s.index) << 16) |
((uint32_t)(reg) << 8) |
DOP_MOVE_NEAR);
} else {
/* We have a normal slot that fits in the required bit width */
reg = s.index;
}
return reg;
}
/* Call this to release a register after emitting the instruction. */
void dstc_free_reg(DstCompiler *c, DstSlot s, int32_t reg) {
if (reg != s.index || s.envindex >= 0 || s.flags & DST_SLOT_CONSTANT) {
/* We need to free the temporary slot */
dstc_regalloc_free(&c->scope->ra, reg);
}
}
/* Check if two slots are equal */
int dstc_sequal(DstSlot lhs, DstSlot rhs) {
if (lhs.flags == rhs.flags &&
lhs.index == rhs.index &&
lhs.envindex == rhs.envindex) {
if (lhs.flags & (DST_SLOT_REF | DST_SLOT_CONSTANT)) {
return dst_equals(lhs.constant, rhs.constant);
} else {
return 1;
}
}
return 0;
}
/* Move values from one slot to another. The destination must
* be writeable (not a literal). */
int dstc_copy(
DstCompiler *c,
DstSlot dest,
DstSlot src) {
int writeback = 0;
int32_t destlocal = -1;
int32_t srclocal = -1;
int32_t reflocal = -1;
/* Can't write to constants */
if (dest.flags & DST_SLOT_CONSTANT) {
dstc_cerror(c, "cannot write to constant");
return 0;
}
/* Short circuit if dest and source are equal */
if (dstc_sequal(dest, src)) return 0;
/* Types of slots - src */
/* constants */
/* upvalues */
/* refs */
/* near index */
/* far index */
/* Types of slots - dest */
/* upvalues */
/* refs */
/* near index */
/* far index */
/* If dest is a near index, do some optimization */
if (dest.envindex < 0 && dest.index >= 0 && dest.index <= 0xFF) {
if (src.flags & DST_SLOT_CONSTANT) {
dstc_loadconst(c, src.constant, dest.index);
} else if (src.flags & DST_SLOT_REF) {
dstc_loadconst(c, src.constant, dest.index);
dstc_emit(c,
(dest.index << 16) |
(dest.index << 8) |
DOP_GET_INDEX);
} else if (src.envindex >= 0) {
dstc_emit(c,
(src.index << 24) |
(src.envindex << 16) |
(dest.index << 8) |
DOP_LOAD_UPVALUE);
} else {
dstc_emit(c,
(src.index << 16) |
(dest.index << 8) |
DOP_MOVE_NEAR);
}
return 1;
}
/* Process: src -> srclocal -> destlocal -> dest */
/* src -> srclocal */
srclocal = dstc_to_tempreg(c, src, DSTC_REGTEMP_0);
/* Pull down dest (find destlocal) */
if (dest.flags & DST_SLOT_REF) {
writeback = 1;
destlocal = srclocal;
reflocal = dstc_getreg_temp(c, DSTC_REGTEMP_1);
dstc_emit(c,
(dstc_const(c, dest.constant) << 16) |
(reflocal << 8) |
DOP_LOAD_CONSTANT);
} else if (dest.envindex >= 0) {
writeback = 2;
destlocal = srclocal;
} else if (dest.index > 0xFF) {
writeback = 3;
destlocal = srclocal;
} else {
destlocal = dest.index;
}
/* srclocal -> destlocal */
if (srclocal != destlocal) {
dstc_emit(c,
((uint32_t)(srclocal) << 16) |
((uint32_t)(destlocal) << 8) |
DOP_MOVE_NEAR);
}
/* destlocal -> dest */
if (writeback == 1) {
dstc_emit(c,
(destlocal << 16) |
(reflocal << 8) |
DOP_PUT_INDEX);
} else if (writeback == 2) {
dstc_emit(c,
((uint32_t)(dest.index) << 24) |
((uint32_t)(dest.envindex) << 16) |
((uint32_t)(destlocal) << 8) |
DOP_SET_UPVALUE);
} else if (writeback == 3) {
dstc_emit(c,
((uint32_t)(dest.index) << 16) |
((uint32_t)(destlocal) << 8) |
DOP_MOVE_FAR);
}
/* Cleanup */
if (reflocal >= 0) {
dstc_regalloc_free(&c->scope->ra, reflocal);
}
dstc_free_reg(c, src, srclocal);
return 1;
}
/* Instruction templated emitters */
static int32_t emit1s(DstCompiler *c, uint8_t op, DstSlot s, int32_t rest) {
int32_t reg = dstc_to_tempreg(c, s, DSTC_REGTEMP_0);
int32_t label = dst_v_count(c->buffer);
dstc_emit(c, op | (reg << 8) | (rest << 16));
dstc_free_reg(c, s, reg);
return label;
}
int32_t dstc_emit_s(DstCompiler *c, uint8_t op, DstSlot s) {
int32_t reg = dstc_to_reg(c, s);
int32_t label = dst_v_count(c->buffer);
dstc_emit(c, op | (reg << 8));
dstc_free_reg(c, s, reg);
return label;
}
int32_t dstc_emit_sl(DstCompiler *c, uint8_t op, DstSlot s, int32_t label) {
int32_t current = dst_v_count(c->buffer) - 1;
int32_t jump = label - current;
if (jump < INT16_MIN || jump > INT16_MAX) {
dstc_cerror(c, "jump is too far");
}
return emit1s(c, op, s, jump);
}
int32_t dstc_emit_st(DstCompiler *c, uint8_t op, DstSlot s, int32_t tflags) {
return emit1s(c, op, s, tflags);
}
int32_t dstc_emit_si(DstCompiler *c, uint8_t op, DstSlot s, int16_t immediate) {
return emit1s(c, op, s, immediate);
}
int32_t dstc_emit_su(DstCompiler *c, uint8_t op, DstSlot s, uint16_t immediate) {
return emit1s(c, op, s, (int32_t) immediate);
}
static int32_t emit2s(DstCompiler *c, uint8_t op, DstSlot s1, DstSlot s2, int32_t rest) {
int32_t reg1 = dstc_to_tempreg(c, s1, DSTC_REGTEMP_0);
int32_t reg2 = dstc_to_tempreg(c, s2, DSTC_REGTEMP_1);
int32_t label = dst_v_count(c->buffer);
dstc_emit(c, op | (reg1 << 8) | (reg2 << 16) | (rest << 24));
dstc_free_reg(c, s1, reg1);
dstc_free_reg(c, s2, reg2);
return label;
}
int32_t dstc_emit_ss(DstCompiler *c, uint8_t op, DstSlot s1, DstSlot s2) {
int32_t reg1 = dstc_to_tempreg(c, s1, DSTC_REGTEMP_0);
int32_t reg2 = dstc_to_reg(c, s2);
int32_t label = dst_v_count(c->buffer);
dstc_emit(c, op | (reg1 << 8) | (reg2 << 16));
dstc_free_reg(c, s1, reg1);
dstc_free_reg(c, s2, reg2);
return label;
}
int32_t dstc_emit_ssi(DstCompiler *c, uint8_t op, DstSlot s1, DstSlot s2, int8_t immediate) {
return emit2s(c, op, s1, s2, immediate);
}
int32_t dstc_emit_ssu(DstCompiler *c, uint8_t op, DstSlot s1, DstSlot s2, uint8_t immediate) {
return emit2s(c, op, s1, s2, (int32_t) immediate);
}
int32_t dstc_emit_sss(DstCompiler *c, uint8_t op, DstSlot s1, DstSlot s2, DstSlot s3) {
int32_t reg1 = dstc_to_tempreg(c, s1, DSTC_REGTEMP_0);
int32_t reg2 = dstc_to_tempreg(c, s2, DSTC_REGTEMP_1);
int32_t reg3 = dstc_to_tempreg(c, s3, DSTC_REGTEMP_2);
int32_t label = dst_v_count(c->buffer);
dstc_emit(c, op | (reg1 << 8) | (reg2 << 16) | (reg3 << 24));
dstc_free_reg(c, s1, reg1);
dstc_free_reg(c, s2, reg2);
dstc_free_reg(c, s3, reg3);
return label;
}

50
src/compiler/emit.h Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2018 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_EMIT_H
#define DST_EMIT_H
#include "compile.h"
void dstc_emit(DstCompiler *c, uint32_t instr);
int32_t dstc_getreg(DstCompiler *c);
int32_t dstc_getreg_temp(DstCompiler *c, DstcRegisterTemp);
int32_t dstc_to_reg(DstCompiler *c, DstSlot s);
int32_t dstc_to_tempreg(DstCompiler *c, DstSlot s, DstcRegisterTemp tag);
void dstc_free_reg(DstCompiler *c, DstSlot s, int32_t reg);
int32_t dstc_emit_s(DstCompiler *c, uint8_t op, DstSlot s);
int32_t dstc_emit_sl(DstCompiler *c, uint8_t op, DstSlot s, int32_t label);
int32_t dstc_emit_st(DstCompiler *c, uint8_t op, DstSlot s, int32_t tflags);
int32_t dstc_emit_si(DstCompiler *c, uint8_t op, DstSlot s, int16_t immediate);
int32_t dstc_emit_su(DstCompiler *c, uint8_t op, DstSlot s, uint16_t immediate);
int32_t dstc_emit_ss(DstCompiler *c, uint8_t op, DstSlot s1, DstSlot s2);
int32_t dstc_emit_ssi(DstCompiler *c, uint8_t op, DstSlot s1, DstSlot s2, int8_t immediate);
int32_t dstc_emit_ssu(DstCompiler *c, uint8_t op, DstSlot s1, DstSlot s2, uint8_t immediate);
int32_t dstc_emit_sss(DstCompiler *c, uint8_t op, DstSlot s1, DstSlot s2, DstSlot s3);
/* Move value from one slot to another. Cannot copy to constant slots. */
int dstc_copy(DstCompiler *c, DstSlot dest, DstSlot src);
#endif

229
src/compiler/regalloc.c Normal file
View File

@ -0,0 +1,229 @@
/*
* Copyright (c) 2018 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.
*/
#include "regalloc.h"
#include <stdlib.h>
#include <dst/dst.h>
void dstc_regalloc_init(DstcRegisterAllocator *ra) {
ra->chunks = NULL;
ra->count = 0;
ra->capacity = 0;
ra->max = 0;
}
void dstc_regalloc_deinit(DstcRegisterAllocator *ra) {
free(ra->chunks);
}
/* Fallbacks for when ctz not available */
#ifdef __GNUC__
#define count_trailing_zeros(x) __builtin_ctz(x)
#define count_trailing_ones(x) __builtin_ctz(~(x))
#else
static int32_t count_trailing_ones(uint32_t x) {
int32_t ret = 0;
while (x & 1) {
ret++;
x >>= 1;
}
return ret;
}
#define count_trailing_zeros(x) count_trailing_ones(~(x))
#endif
/* Get ith bit */
#define ithbit(I) ((uint32_t)1 << (I))
/* Get N bits */
#define nbits(N) (ithbit(N) - 1)
/* Copy a regsiter allocator */
void dstc_regalloc_clone(DstcRegisterAllocator *dest, DstcRegisterAllocator *src) {
size_t size;
dest->count = src->count;
dest->capacity = src->capacity;
dest->max = src->max;
size = sizeof(uint32_t) * dest->capacity;
dest->chunks = malloc(size);
if (!dest->chunks) {
DST_OUT_OF_MEMORY;
}
memcpy(dest->chunks, src->chunks, size);
}
/* Allocate one more chunk in chunks */
static void pushchunk(DstcRegisterAllocator *ra) {
/* Registers 240-255 are always allocated (reserved) */
uint32_t chunk = ra->count == 7 ? 0xFFFF0000 : 0;
int32_t newcount = ra->count + 1;
if (newcount > ra->capacity) {
int32_t newcapacity = newcount * 2;
ra->chunks = realloc(ra->chunks, newcapacity * sizeof(uint32_t));
if (!ra->chunks) {
DST_OUT_OF_MEMORY;
}
ra->capacity = newcapacity;
}
ra->chunks[ra->count] = chunk;
ra->count = newcount;
}
/* Reallocate a given register */
void dstc_regalloc_touch(DstcRegisterAllocator *ra, int32_t reg) {
int32_t chunk = reg >> 5;
int32_t bit = reg & 0x1F;
while (chunk >= ra->count) pushchunk(ra);
ra->chunks[chunk] |= ithbit(bit);
}
/* Allocate one register. */
int32_t dstc_regalloc_1(DstcRegisterAllocator *ra) {
/* Get the nth bit in the array */
int32_t bit, chunk, nchunks, reg;
bit = -1;
nchunks = ra->count;
for (chunk = 0; chunk < nchunks; chunk++) {
uint32_t block = ra->chunks[chunk];
if (block == 0xFFFFFFFF) continue;
bit = count_trailing_ones(block);
break;
}
/* No reg found */
if (bit == -1) {
pushchunk(ra);
bit = 0;
chunk = nchunks;
}
/* set the bit at index bit in chunk */
ra->chunks[chunk] |= ithbit(bit);
reg = (chunk << 5) + bit;
if (reg > ra->max)
ra->max = reg;
return reg;
}
/* Free a register. The register must have been previously allocated
* without being freed. */
void dstc_regalloc_free(DstcRegisterAllocator *ra, int32_t reg) {
/* We cannot free reserved slots */
if (reg < 0 || (reg >= 240 && reg <= 255))
return;
int32_t chunk = reg >> 5;
int32_t bit = reg & 0x1F;
ra->chunks[chunk] &= ~ithbit(bit);
}
/* Get a register that will fit in 8 bits (< 256). Do not call this
* twice with the same value of nth without calling dstc_regalloc_free
* on the returned register before. */
int32_t dstc_regalloc_temp(DstcRegisterAllocator *ra, DstcRegisterTemp nth) {
int32_t oldmax = ra->max;
int32_t reg = dstc_regalloc_1(ra);
if (reg > 0xFF) {
reg = 0xF0 + nth;
ra->max = (reg > oldmax) ? reg : oldmax;
}
return reg;
}
/* Check if a range is free. Returns the next index to check if not free,
* -1 if free. */
static int32_t checkrange(DstcRegisterAllocator *ra, int32_t start, int32_t end) {
int32_t startchunk = start / 32;
int32_t endchunk = end / 32;
for (int32_t chunk = startchunk; chunk <= endchunk; chunk++) {
while (ra->count <= chunk) pushchunk(ra);
uint32_t mask = 0xFFFFFFFF;
if (chunk == startchunk)
mask &= ~nbits(start & 0x1F);
if (chunk == endchunk)
mask &= nbits(end & 0x1F);
uint32_t block = ra->chunks[chunk];
uint32_t masking = mask & block;
if (masking) {
/* If block is full, skip it completely. */
int32_t nextbit = (block == 0xFFFFFFFF)
? 32
: count_trailing_zeros(masking) + 1;
return chunk * 32 + nextbit;
}
}
return -1;
}
/* Mark a range */
static void markrange(DstcRegisterAllocator *ra, int32_t start, int32_t end) {
int32_t startchunk = start / 32;
int32_t endchunk = end / 32;
for (int32_t chunk = startchunk; chunk <= endchunk; chunk++) {
uint32_t mask = 0xFFFFFFFF;
if (chunk == startchunk)
mask &= ~nbits(start & 0x1F);
if (chunk == endchunk)
mask &= nbits(end & 0x1F);
ra->chunks[chunk] |= mask;
}
}
/* Free a range of registers. */
void dstc_regalloc_freerange(DstcRegisterAllocator *ra, int32_t start, int32_t n) {
int32_t end = start + n - 1;
int32_t startchunk = start / 32;
int32_t endchunk = end / 32;
for (int32_t chunk = startchunk; chunk <= endchunk; chunk++) {
uint32_t mask = 0;
if (chunk == startchunk)
mask |= nbits(start & 0x1F);
if (chunk == endchunk)
mask |= ~nbits(end & 0x1F);
ra->chunks[chunk] &= mask;
}
}
/* Allocate n contiguous registers. Returns the first register
* in the range allocated. */
int32_t dstc_regalloc_n(DstcRegisterAllocator *ra, int32_t n) {
int32_t start = 0, end = 0, next = 0;
while (next >= 0) {
start = next;
end = start + n - 1;
next = checkrange(ra, start, end);
}
markrange(ra, start, end);
/* Set max */
if (end > ra->max)
ra->max = end;
return start;
}
/* Allocates registers for a function call. Tries to not move the callee,
* but will find nargs + 1 other contiguous registers if there is not enough
* space after the callee. */
int32_t dstc_regalloc_call(DstcRegisterAllocator *ra, int32_t callee, int32_t nargs) {
if (checkrange(ra, callee, callee + nargs) < 0) {
markrange(ra, callee + 1, callee + nargs);
return callee;
}
return dstc_regalloc_n(ra, nargs + 1);
}

106
src/compiler/regalloc.h Normal file
View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2018 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.
*/
/* Implements a simple first fit register allocator for the compiler. */
#ifndef DST_REGALLOC_H
#define DST_REGALLOC_H
#include <stdint.h>
/* Placeholder for allocating temporary registers */
typedef enum {
DSTC_REGTEMP_0,
DSTC_REGTEMP_1,
DSTC_REGTEMP_2,
DSTC_REGTEMP_3,
DSTC_REGTEMP_4,
DSTC_REGTEMP_5,
DSTC_REGTEMP_6,
DSTC_REGTEMP_7
} DstcRegisterTemp;
typedef struct {
uint32_t *chunks;
int32_t count; /* number of chunks in chunks */
int32_t capacity; /* amount allocated for chunks */
int32_t max; /* The maximum allocated register so far */
} DstcRegisterAllocator;
void dstc_regalloc_init(DstcRegisterAllocator *ra);
void dstc_regalloc_deinit(DstcRegisterAllocator *ra);
int32_t dstc_regalloc_1(DstcRegisterAllocator *ra);
void dstc_regalloc_free(DstcRegisterAllocator *ra, int32_t reg);
void dstc_regalloc_freerange(DstcRegisterAllocator *ra, int32_t regstart, int32_t n);
int32_t dstc_regalloc_temp(DstcRegisterAllocator *ra, DstcRegisterTemp nth);
int32_t dstc_regalloc_n(DstcRegisterAllocator *ra, int32_t n);
int32_t dstc_regalloc_call(DstcRegisterAllocator *ra, int32_t callee, int32_t nargs);
void dstc_regalloc_clone(DstcRegisterAllocator *dest, DstcRegisterAllocator *src);
void dstc_regalloc_touch(DstcRegisterAllocator *ra, int32_t reg);
/* Test code */
/*
#include <stdio.h>
static void printreg(DstcRegisterAllocator *ra) {
printf("count=%d, cap=%d, max=%d\n", ra->count, ra->capacity, ra->max);
for (int row = 0; row < ra->count; row++) {
uint32_t chunk = ra->chunks[row];
putc('[', stdout);
for (int i = 0; i < 32; i++) {
putc(
(chunk & (1 << i))
? '*'
: '.', stdout);
}
putc(']', stdout);
putc('\n', stdout);
}
putc('\n', stdout);
}
static void runtest(void) {
DstcRegisterAllocator ra, rb;
dstc_regalloc_init(&ra);
int32_t a = dstc_regalloc_1(&ra);
int32_t b = dstc_regalloc_1(&ra);
int32_t c = dstc_regalloc_1(&ra);
int32_t d = dstc_regalloc_1(&ra);
int32_t e = dstc_regalloc_1(&ra);
printreg(&ra);
dstc_regalloc_free(&ra, b);
dstc_regalloc_free(&ra, d);
printreg(&ra);
int32_t x = dstc_regalloc_n(&ra, 32);
printreg(&ra);
dstc_regalloc_1(&ra);
printreg(&ra);
int32_t y = dstc_regalloc_n(&ra, 101);
printreg(&ra);
dstc_regalloc_clone(&rb, &ra);
printreg(&rb);
dstc_regalloc_deinit(&ra);
dstc_regalloc_deinit(&rb);
}
*/
#endif

View File

@ -26,6 +26,7 @@
#include "compile.h"
#include <headerlibs/strbinsearch.h>
#include <headerlibs/vector.h>
#include "emit.h"
DstSlot dstc_quote(DstFopts opts, int32_t argn, const Dst *argv) {
if (argn != 1) {
@ -35,7 +36,11 @@ DstSlot dstc_quote(DstFopts opts, int32_t argn, const Dst *argv) {
return dstc_cslot(argv[0]);
}
static void destructure(DstCompiler *c, Dst left, DstSlot right,
/* Preform destructuring. Be careful to
* keep the order registers are freed. */
static void destructure(DstCompiler *c,
Dst left,
DstSlot right,
void (*leaf)(DstCompiler *c,
const uint8_t *sym,
DstSlot s,
@ -52,37 +57,35 @@ static void destructure(DstCompiler *c, Dst left, DstSlot right,
case DST_TUPLE:
case DST_ARRAY:
{
int32_t i, len, localright, localsub;
int32_t i, len, right_register, subval_register;
const Dst *values;
dst_seq_view(left, &values, &len);
for (i = 0; i < len; i++) {
DstSlot newright;
DstSlot nextright;
Dst subval = values[i];
localright = dstc_preread(c, 0xFF, 1, right);
localsub = dstc_lslotn(c, 0xFF, 3);
right_register = dstc_to_tempreg(c, right, DSTC_REGTEMP_0);
subval_register = dstc_getreg_temp(c, DSTC_REGTEMP_1);
if (i < 0x100) {
dstc_emit(c,
(i << 24) |
(localright << 16) |
(localsub << 8) |
DOP_GET_INDEX);
dstc_emit(c, DOP_GET_INDEX |
(subval_register << 8) |
(right_register << 16) |
(i << 24));
} else {
DstSlot islot = dstc_cslot(dst_wrap_integer(i));
int32_t locali = dstc_preread(c, 0xFF, 2, islot);
dstc_emit(c,
(locali << 24) |
(localright << 16) |
(localsub << 8) |
DOP_GET);
dstc_postread(c, islot, locali);
int32_t i_register = dstc_to_tempreg(c, islot, DSTC_REGTEMP_2);
dstc_emit(c, DOP_GET_INDEX |
(subval_register << 8) |
(right_register << 16) |
(i_register << 24));
dstc_free_reg(c, islot, i_register);
}
newright.index = localsub;
newright.envindex = -1;
newright.constant = dst_wrap_nil();
newright.flags = DST_SLOTTYPE_ANY;
/* Traverse into the structure */
destructure(c, subval, newright, leaf, attr);
dstc_postread(c, right, localright);
nextright.index = subval_register;
nextright.envindex = -1;
nextright.constant = dst_wrap_nil();
nextright.flags = DST_SLOTTYPE_ANY;
destructure(c, subval, nextright, leaf, attr);
/* Free right_register AFTER sub destructuring */
dstc_free_reg(c, right, right_register);
}
}
/* Free right */
@ -91,28 +94,27 @@ static void destructure(DstCompiler *c, Dst left, DstSlot right,
case DST_TABLE:
case DST_STRUCT:
{
int32_t localright, localsub;
int32_t right_register, subval_register, k_register;
const DstKV *kv = NULL;
while ((kv = dstc_next(left, kv))) {
DstSlot newright;
DstSlot nextright;
DstSlot kslot = dstc_value(dstc_fopts_default(c), kv->key);
Dst subval = kv->value;
localright = dstc_preread(c, 0xFF, 1, right);
localsub = dstc_lslotn(c, 0xFF, 3);
int32_t localk = dstc_preread(c, 0xFF, 2, kslot);
dstc_emit(c,
(localk << 24) |
(localright << 16) |
(localsub << 8) |
DOP_GET);
dstc_postread(c, kslot, localk);
newright.index = localsub;
newright.envindex = -1;
newright.constant = dst_wrap_nil();
newright.flags = DST_SLOTTYPE_ANY;
/* Traverse into the structure */
destructure(c, subval, newright, leaf, attr);
dstc_postread(c, right, localright);
right_register = dstc_to_tempreg(c, right, DSTC_REGTEMP_0);
subval_register = dstc_getreg_temp(c, DSTC_REGTEMP_1);
k_register = dstc_to_tempreg(c, kslot, DSTC_REGTEMP_2);
dstc_emit(c, DOP_GET |
(subval_register << 8) |
(right_register << 16) |
(k_register << 24));
dstc_free_reg(c, kslot, k_register);
nextright.index = subval_register;
nextright.envindex = -1;
nextright.constant = dst_wrap_nil();
nextright.flags = DST_SLOTTYPE_ANY;
destructure(c, kv->value, nextright, leaf, attr);
/* Free right_register AFTER sub destructuring */
dstc_free_reg(c, right, right_register);
}
}
/* Free right */
@ -191,7 +193,7 @@ static DstSlot namelocal(DstCompiler *c, Dst head, int32_t flags, DstSlot ret) {
ret.index > 0xFF) {
/* Slot is not able to be named */
DstSlot localslot;
localslot.index = dstc_lsloti(c);
localslot.index = dstc_getreg(c);
/* infer type? */
localslot.flags = flags;
localslot.envindex = -1;
@ -209,9 +211,9 @@ static void varleaf(
const uint8_t *sym,
DstSlot s,
DstTable *attr) {
if (dst_v_last(c->scopes).flags & DST_SCOPE_TOP) {
DstSlot refslot, refarrayslot;
if (c->scope->flags & DST_SCOPE_TOP) {
/* Global var, generate var */
DstSlot refslot;
DstTable *reftab = dst_table(1);
reftab->proto = attr;
DstArray *ref = dst_array(1);
@ -219,17 +221,7 @@ static void varleaf(
dst_table_put(reftab, dst_csymbolv(":ref"), dst_wrap_array(ref));
dst_table_put(c->env, dst_wrap_symbol(sym), dst_wrap_table(reftab));
refslot = dstc_cslot(dst_wrap_array(ref));
refarrayslot = refslot;
refslot.flags |= DST_SLOT_REF | DST_SLOT_NAMED | DST_SLOT_MUTABLE;
/* Generate code to set ref */
int32_t refarrayindex = dstc_preread(c, 0xFF, 1, refarrayslot);
int32_t retindex = dstc_preread(c, 0xFF, 2, s);
dstc_emit(c,
(retindex << 16) |
(refarrayindex << 8) |
DOP_PUT_INDEX);
dstc_postread(c, refarrayslot, refarrayindex);
dstc_postread(c, s, retindex);
dstc_emit_ssu(c, DOP_PUT_INDEX, refslot, s, 0);
} else {
namelocal(c, dst_wrap_symbol(sym), DST_SLOT_NAMED | DST_SLOT_MUTABLE, s) ;
}
@ -249,10 +241,9 @@ static void defleaf(
const uint8_t *sym,
DstSlot s,
DstTable *attr) {
if (dst_v_last(c->scopes).flags & DST_SCOPE_TOP) {
if (c->scope->flags & DST_SCOPE_TOP) {
DstTable *tab = dst_table(2);
tab->proto = attr;
int32_t tableindex, valsymindex, valueindex;
DstSlot valsym = dstc_cslot(dst_csymbolv(":value"));
DstSlot tabslot = dstc_cslot(dst_wrap_table(tab));
@ -260,17 +251,7 @@ static void defleaf(
dst_table_put(c->env, dst_wrap_symbol(sym), dst_wrap_table(tab));
/* Put value in table when evaulated */
tableindex = dstc_preread(c, 0xFF, 1, tabslot);
valsymindex = dstc_preread(c, 0xFF, 2, valsym);
valueindex = dstc_preread(c, 0xFF, 3, s);
dstc_emit(c,
(valueindex << 24) |
(valsymindex << 16) |
(tableindex << 8) |
DOP_PUT);
dstc_postread(c, tabslot, tableindex);
dstc_postread(c, valsym, valsymindex);
dstc_postread(c, s, valueindex);
dstc_emit_sss(c, DOP_PUT, tabslot, valsym, s);
} else {
namelocal(c, dst_wrap_symbol(sym), DST_SLOT_NAMED, s);
}
@ -299,10 +280,11 @@ DstSlot dstc_def(DstFopts opts, int32_t argn, const Dst *argv) {
*/
DstSlot dstc_if(DstFopts opts, int32_t argn, const Dst *argv) {
DstCompiler *c = opts.compiler;
int32_t labelr, labeljr, labeld, labeljd, condlocal;
int32_t labelr, labeljr, labeld, labeljd;
DstFopts condopts, bodyopts;
DstSlot cond, left, right, target;
Dst truebody, falsebody;
DstScope tempscope;
const int tail = opts.flags & DST_FOPTS_TAIL;
const int drop = opts.flags & DST_FOPTS_DROP;
@ -331,7 +313,7 @@ DstSlot dstc_if(DstFopts opts, int32_t argn, const Dst *argv) {
falsebody = truebody;
truebody = temp;
}
dstc_scope(c, 0);
dstc_scope(&tempscope, c, 0, "if-body");
target = dstc_value(bodyopts, truebody);
dstc_popscope(c);
dstc_throwaway(bodyopts, falsebody);
@ -344,14 +326,10 @@ DstSlot dstc_if(DstFopts opts, int32_t argn, const Dst *argv) {
: dstc_gettarget(opts);
/* Compile jump to right */
condlocal = dstc_preread(c, 0xFF, 1, cond);
labeljr = dst_v_count(c->buffer);
dstc_emit(c, DOP_JUMP_IF_NOT | (condlocal << 8));
dstc_postread(c, cond, condlocal);
dstc_freeslot(c, cond);
labeljr = dstc_emit_si(c, DOP_JUMP_IF_NOT, cond, 0);
/* Condition left body */
dstc_scope(c, 0);
dstc_scope(&tempscope, c, 0, "if-true");
left = dstc_value(bodyopts, truebody);
if (!drop && !tail) dstc_copy(c, target, left);
dstc_popscope(c);
@ -362,7 +340,7 @@ DstSlot dstc_if(DstFopts opts, int32_t argn, const Dst *argv) {
/* Compile right body */
labelr = dst_v_count(c->buffer);
dstc_scope(c, 0);
dstc_scope(&tempscope, c, 0, "if-false");
right = dstc_value(bodyopts, falsebody);
if (!drop && !tail) dstc_copy(c, target, right);
dstc_popscope(c);
@ -383,7 +361,8 @@ DstSlot dstc_do(DstFopts opts, int32_t argn, const Dst *argv) {
DstSlot ret = dstc_cslot(dst_wrap_nil());
DstCompiler *c = opts.compiler;
DstFopts subopts = dstc_fopts_default(c);
dstc_scope(c, 0);
DstScope tempscope;
dstc_scope(&tempscope, c, 0, "do");
for (i = 0; i < argn; i++) {
if (i != argn - 1) {
subopts.flags = DST_FOPTS_DROP;
@ -412,7 +391,8 @@ DstSlot dstc_while(DstFopts opts, int32_t argn, const Dst *argv) {
DstCompiler *c = opts.compiler;
DstSlot cond;
DstFopts subopts = dstc_fopts_default(c);
int32_t condlocal, labelwt, labeld, labeljt, labelc, i;
DstScope tempscope;
int32_t labelwt, labeld, labeljt, labelc, i;
int infinite = 0;
if (argn < 2) {
@ -435,14 +415,11 @@ DstSlot dstc_while(DstFopts opts, int32_t argn, const Dst *argv) {
infinite = 1;
}
dstc_scope(c, 0);
dstc_scope(&tempscope, c, 0, "while");
/* Infinite loop does not need to check condition */
if (!infinite) {
condlocal = dstc_preread(c, 0xFF, 1, cond);
labelc = dst_v_count(c->buffer);
dstc_emit(c, DOP_JUMP_IF_NOT | (condlocal << 8));
dstc_postread(c, cond, condlocal);
labelc = dstc_emit_si(c, DOP_JUMP_IF_NOT, cond, 0);
} else {
labelc = 0;
}
@ -470,13 +447,13 @@ DstSlot dstc_while(DstFopts opts, int32_t argn, const Dst *argv) {
/* Add a funcdef to the top most function scope */
static int32_t dstc_addfuncdef(DstCompiler *c, DstFuncDef *def) {
DstScope *scope = &dst_v_last(c->scopes);
while (scope >= c->scopes) {
DstScope *scope = c->scope;
while (scope) {
if (scope->flags & DST_SCOPE_FUNCTION)
break;
scope--;
scope = scope->parent;
}
dst_assert(scope >= c->scopes, "could not add funcdef");
dst_assert(scope, "could not add funcdef");
dst_v_push(scope->defs, def);
return dst_v_count(scope->defs) - 1;
}
@ -486,19 +463,21 @@ DstSlot dstc_fn(DstFopts opts, int32_t argn, const Dst *argv) {
DstFuncDef *def;
DstSlot ret;
Dst head, paramv;
DstScope fnscope;
int32_t paramcount, argi, parami, arity, localslot, defindex;
DstFopts subopts = dstc_fopts_default(c);
const Dst *params;
const char *errmsg = NULL;
int varargs = 0;
int selfref = 0;
if (argn < 2) {
dstc_cerror(c, "expected at least 2 arguments to function literal");
return dstc_cslot(dst_wrap_nil());
}
/* Begin function */
dstc_scope(c, DST_SCOPE_FUNCTION);
dstc_scope(&fnscope, c, DST_SCOPE_FUNCTION, "function");
if (argn < 2) {
errmsg = "expected at least 2 arguments to function literal";
goto error;
}
/* Read function parameters */
parami = 0;
@ -509,8 +488,8 @@ DstSlot dstc_fn(DstFopts opts, int32_t argn, const Dst *argv) {
parami = 1;
}
if (parami >= argn) {
dstc_cerror(c, "expected function parameters");
return dstc_cslot(dst_wrap_nil());
errmsg = "expected function parameters";
goto error;
}
paramv = argv[parami];
if (dst_seq_view(paramv, &params, &paramcount)) {
@ -518,45 +497,32 @@ DstSlot dstc_fn(DstFopts opts, int32_t argn, const Dst *argv) {
for (i = 0; i < paramcount; i++) {
Dst param = params[i];
if (dst_checktype(param, DST_SYMBOL)) {
DstSlot slot;
/* Check for varargs */
if (0 == dst_cstrcmp(dst_unwrap_symbol(param), "&")) {
if (i != paramcount - 2) {
dstc_cerror(c, "variable argument symbol in unexpected location");
return dstc_cslot(dst_wrap_nil());
errmsg = "variable argument symbol in unexpected location";
goto error;
}
varargs = 1;
arity--;
continue;
}
slot.flags = DST_SLOT_NAMED;
slot.envindex = -1;
slot.constant = dst_wrap_nil();
slot.index = dstc_lsloti(c);
dstc_nameslot(c, dst_unwrap_symbol(param), slot);
dstc_nameslot(c, dst_unwrap_symbol(param), dstc_farslot(c));
} else {
DstSlot s;
s.envindex = -1;
s.flags = DST_SLOTTYPE_ANY;
s.constant = dst_wrap_nil();
s.index = dstc_lsloti(c);
destructure(c, param, s, defleaf, NULL);
destructure(c, param, dstc_farslot(c), defleaf, NULL);
}
arity++;
}
} else {
dstc_cerror(c, "expected function parameters");
return dstc_cslot(dst_wrap_nil());
errmsg = "expected function parameters";
goto error;
}
/* Check for self ref */
if (selfref) {
DstSlot slot;
slot.envindex = -1;
DstSlot slot = dstc_farslot(c);
slot.flags = DST_SLOT_NAMED | DST_FUNCTION;
slot.constant = dst_wrap_nil();
slot.index = dstc_lsloti(c);
dstc_emit(c, (slot.index << 8) | DOP_LOAD_SELF);
dstc_emit_s(c, DOP_LOAD_SELF, slot);
dstc_nameslot(c, dst_unwrap_symbol(head), slot);
}
@ -565,10 +531,11 @@ DstSlot dstc_fn(DstFopts opts, int32_t argn, const Dst *argv) {
dstc_emit(c, DOP_RETURN_NIL);
} else for (argi = parami + 1; argi < argn; argi++) {
DstSlot s;
subopts.flags = argi == (argn - 1) ? DST_FOPTS_TAIL : DST_FOPTS_DROP;
subopts.flags = (argi == (argn - 1)) ? DST_FOPTS_TAIL : DST_FOPTS_DROP;
s = dstc_value(subopts, argv[argi]);
dstc_freeslot(c, s);
if (dstc_iserr(&opts)) return dstc_cslot(dst_wrap_nil());
if (dstc_iserr(&opts))
goto error2;
}
/* Build function */
@ -598,6 +565,12 @@ DstSlot dstc_fn(DstFopts opts, int32_t argn, const Dst *argv) {
}
return ret;
error:
dstc_cerror(c, errmsg);
error2:
dstc_popscope(c);
return dstc_cslot(dst_wrap_nil());
}
/* Keep in lexicographic order */