mirror of
https://github.com/janet-lang/janet
synced 2025-03-25 09:36:55 +00:00
Modularize compiler.
This commit is contained in:
parent
fde9751eab
commit
f4fc4a0bcc
@ -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
|
||||
|
4
Makefile
4
Makefile
@ -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)
|
||||
|
||||
|
@ -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)))
|
||||
|
@ -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
|
||||
|
@ -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-
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
385
src/compiler/emit.c
Normal 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
50
src/compiler/emit.h
Normal 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
229
src/compiler/regalloc.c
Normal 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
106
src/compiler/regalloc.h
Normal 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
|
@ -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, ¶ms, ¶mcount)) {
|
||||
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user