2017-11-06 03:05:47 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2017 Calvin Rose
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to
|
|
|
|
* deal in the Software without restriction, including without limitation the
|
|
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
* IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <setjmp.h>
|
|
|
|
|
|
|
|
#include <dst/dst.h>
|
2018-01-19 21:43:19 +00:00
|
|
|
#include <dst/dstasm.h>
|
|
|
|
#include <dst/dstopcodes.h>
|
|
|
|
#include <headerlibs/strbinsearch.h>
|
2017-11-06 03:05:47 +00:00
|
|
|
|
|
|
|
/* Convert a slot to to an integer for bytecode */
|
|
|
|
|
2018-01-04 02:36:10 +00:00
|
|
|
/* Types of instructions (some of them) */
|
2017-11-06 03:05:47 +00:00
|
|
|
/* _0arg - op.---.--.-- (return-nil, noop, vararg arguments)
|
|
|
|
* _s - op.src.--.-- (push1)
|
|
|
|
* _l - op.XX.XX.XX (jump)
|
|
|
|
* _ss - op.dest.XX.XX (move, swap)
|
|
|
|
* _sl - op.check.XX.XX (jump-if)
|
|
|
|
* _st - op.check.TT.TT (typecheck)
|
|
|
|
* _si - op.dest.XX.XX (load-integer)
|
|
|
|
* _sss - op.dest.op1.op2 (add, subtract, arithmetic, comparison)
|
|
|
|
* _ses - op.dest.up.which (load-upvalue, save-upvalue)
|
|
|
|
* _sc - op.dest.CC.CC (load-constant, closure)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Definition for an instruction in the assembler */
|
|
|
|
typedef struct DstInstructionDef DstInstructionDef;
|
|
|
|
struct DstInstructionDef {
|
|
|
|
const char *name;
|
2018-01-29 20:46:26 +00:00
|
|
|
enum DstOpCode opcode;
|
2017-11-06 03:05:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Hold all state needed during assembly */
|
|
|
|
typedef struct DstAssembler DstAssembler;
|
|
|
|
struct DstAssembler {
|
|
|
|
DstAssembler *parent;
|
|
|
|
DstFuncDef *def;
|
|
|
|
jmp_buf on_error;
|
2017-11-21 02:39:44 +00:00
|
|
|
const uint8_t *errmessage;
|
2018-03-16 18:34:48 +00:00
|
|
|
int32_t errindex;
|
2017-11-06 03:05:47 +00:00
|
|
|
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t environments_capacity;
|
2018-01-05 21:17:55 +00:00
|
|
|
int32_t defs_capacity;
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t bytecode_count; /* Used for calculating labels */
|
2017-11-06 03:05:47 +00:00
|
|
|
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst name;
|
2017-11-06 03:05:47 +00:00
|
|
|
DstTable labels; /* symbol -> bytecode index */
|
|
|
|
DstTable constants; /* symbol -> constant index */
|
|
|
|
DstTable slots; /* symbol -> slot index */
|
|
|
|
DstTable envs; /* symbol -> environment index */
|
2018-01-04 02:36:10 +00:00
|
|
|
DstTable defs; /* symbol -> funcdefs index */
|
2017-11-06 03:05:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Dst opcode descriptions in lexographic order. This
|
|
|
|
* allows a binary search over the elements to find the
|
|
|
|
* correct opcode given a name. This works in reasonable
|
|
|
|
* time and is easier to setup statically than a hash table or
|
|
|
|
* prefix tree. */
|
|
|
|
static const DstInstructionDef dst_ops[] = {
|
2018-01-21 19:39:32 +00:00
|
|
|
{"add", DOP_ADD},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"addi", DOP_ADD_INTEGER},
|
2018-03-16 18:34:48 +00:00
|
|
|
{"addim", DOP_ADD_IMMEDIATE},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"addr", DOP_ADD_REAL},
|
2018-01-21 19:39:32 +00:00
|
|
|
{"band", DOP_BAND},
|
|
|
|
{"bnot", DOP_BNOT},
|
|
|
|
{"bor", DOP_BOR},
|
|
|
|
{"bxor", DOP_BXOR},
|
|
|
|
{"call", DOP_CALL},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"clo", DOP_CLOSURE},
|
|
|
|
{"cmp", DOP_COMPARE},
|
2018-03-09 22:14:26 +00:00
|
|
|
{"debug", DOP_DEBUG},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"div", DOP_DIVIDE},
|
|
|
|
{"divi", DOP_DIVIDE_INTEGER},
|
2018-03-16 18:34:48 +00:00
|
|
|
{"divim", DOP_DIVIDE_IMMEDIATE},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"divr", DOP_DIVIDE_REAL},
|
|
|
|
{"eq", DOP_EQUALS},
|
2018-05-07 16:34:04 +00:00
|
|
|
{"eqi", DOP_EQUALS_INTEGER},
|
|
|
|
{"eqim", DOP_EQUALS_IMMEDIATE},
|
|
|
|
{"eqr", DOP_EQUALS_REAL},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"err", DOP_ERROR},
|
2018-01-21 19:39:32 +00:00
|
|
|
{"get", DOP_GET},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"geti", DOP_GET_INDEX},
|
|
|
|
{"gt", DOP_GREATER_THAN},
|
2018-05-07 16:34:04 +00:00
|
|
|
{"gti", DOP_GREATER_THAN_INTEGER},
|
|
|
|
{"gtim", DOP_GREATER_THAN_IMMEDIATE},
|
|
|
|
{"gtr", DOP_GREATER_THAN_REAL},
|
|
|
|
{"gter", DOP_GREATER_THAN_EQUAL_REAL},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"jmp", DOP_JUMP},
|
2018-03-30 16:17:03 +00:00
|
|
|
{"jmpif", DOP_JUMP_IF},
|
|
|
|
{"jmpno", DOP_JUMP_IF_NOT},
|
2018-03-16 18:34:48 +00:00
|
|
|
{"ldc", DOP_LOAD_CONSTANT},
|
|
|
|
{"ldf", DOP_LOAD_FALSE},
|
|
|
|
{"ldi", DOP_LOAD_INTEGER},
|
|
|
|
{"ldn", DOP_LOAD_NIL},
|
|
|
|
{"lds", DOP_LOAD_SELF},
|
|
|
|
{"ldt", DOP_LOAD_TRUE},
|
|
|
|
{"ldu", DOP_LOAD_UPVALUE},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"lt", DOP_LESS_THAN},
|
2018-05-07 16:34:04 +00:00
|
|
|
{"lti", DOP_LESS_THAN_INTEGER},
|
|
|
|
{"ltim", DOP_LESS_THAN_IMMEDIATE},
|
|
|
|
{"ltr", DOP_LESS_THAN_REAL},
|
|
|
|
{"lter", DOP_LESS_THAN_EQUAL_REAL},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"movf", DOP_MOVE_FAR},
|
|
|
|
{"movn", DOP_MOVE_NEAR},
|
|
|
|
{"mul", DOP_MULTIPLY},
|
|
|
|
{"muli", DOP_MULTIPLY_INTEGER},
|
2018-03-16 18:34:48 +00:00
|
|
|
{"mulim", DOP_MULTIPLY_IMMEDIATE},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"mulr", DOP_MULTIPLY_REAL},
|
2018-01-21 19:39:32 +00:00
|
|
|
{"noop", DOP_NOOP},
|
|
|
|
{"push", DOP_PUSH},
|
|
|
|
{"push2", DOP_PUSH_2},
|
|
|
|
{"push3", DOP_PUSH_3},
|
2018-03-16 18:34:48 +00:00
|
|
|
{"pusha", DOP_PUSH_ARRAY},
|
2018-01-21 19:39:32 +00:00
|
|
|
{"put", DOP_PUT},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"puti", DOP_PUT_INDEX},
|
2018-03-11 19:35:23 +00:00
|
|
|
{"res", DOP_RESUME},
|
2018-02-12 17:28:58 +00:00
|
|
|
{"ret", DOP_RETURN},
|
|
|
|
{"retn", DOP_RETURN_NIL},
|
|
|
|
{"setu", DOP_SET_UPVALUE},
|
|
|
|
{"sl", DOP_SHIFT_LEFT},
|
|
|
|
{"slim", DOP_SHIFT_LEFT_IMMEDIATE},
|
|
|
|
{"sr", DOP_SHIFT_RIGHT},
|
|
|
|
{"srim", DOP_SHIFT_RIGHT_IMMEDIATE},
|
|
|
|
{"sru", DOP_SHIFT_RIGHT_UNSIGNED},
|
|
|
|
{"sruim", DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE},
|
|
|
|
{"sub", DOP_SUBTRACT},
|
|
|
|
{"tcall", DOP_TAILCALL},
|
2018-03-11 19:35:23 +00:00
|
|
|
{"tchck", DOP_TYPECHECK},
|
|
|
|
{"yield", DOP_YIELD}
|
2017-11-06 03:05:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Check a dst string against a bunch of test_strings. Return the
|
|
|
|
* index of the matching test_string, or -1 if not found. */
|
2018-03-31 20:40:36 +00:00
|
|
|
static int32_t strsearch(const uint8_t *str, const char *const *test_strings) {
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t len = dst_string_length(str);
|
2017-11-06 03:05:47 +00:00
|
|
|
int index;
|
|
|
|
for (index = 0; ; index++) {
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t i;
|
2017-11-06 03:05:47 +00:00
|
|
|
const char *testword = test_strings[index];
|
|
|
|
if (NULL == testword)
|
|
|
|
break;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
if (testword[i] != str[i])
|
|
|
|
goto nextword;
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
nextword:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deinitialize an Assembler. Does not deinitialize the parents. */
|
|
|
|
static void dst_asm_deinit(DstAssembler *a) {
|
|
|
|
dst_table_deinit(&a->slots);
|
|
|
|
dst_table_deinit(&a->labels);
|
|
|
|
dst_table_deinit(&a->envs);
|
|
|
|
dst_table_deinit(&a->constants);
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_table_deinit(&a->defs);
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Throw some kind of assembly error */
|
2018-01-04 02:36:10 +00:00
|
|
|
static void dst_asm_error(DstAssembler *a, const char *message) {
|
2018-03-16 18:34:48 +00:00
|
|
|
a->errmessage = dst_formatc("%s, instruction %d", message, a->errindex);
|
2017-11-06 03:05:47 +00:00
|
|
|
longjmp(a->on_error, 1);
|
|
|
|
}
|
2018-01-04 02:36:10 +00:00
|
|
|
#define dst_asm_assert(a, c, m) do { if (!(c)) dst_asm_error((a), (m)); } while (0)
|
2017-11-06 03:05:47 +00:00
|
|
|
|
2017-11-21 02:39:44 +00:00
|
|
|
/* Throw some kind of assembly error */
|
2018-01-04 02:36:10 +00:00
|
|
|
static void dst_asm_errorv(DstAssembler *a, const uint8_t *m) {
|
2017-11-21 02:39:44 +00:00
|
|
|
a->errmessage = m;
|
|
|
|
longjmp(a->on_error, 1);
|
|
|
|
}
|
|
|
|
|
2018-01-04 02:36:10 +00:00
|
|
|
/* Add a closure environment to the assembler. Sub funcdefs may need
|
|
|
|
* to reference outer function environments, and may change the outer environment.
|
|
|
|
* Returns the index of the environment in the assembler's environments, or -1
|
|
|
|
* if not found. */
|
2018-01-06 16:09:15 +00:00
|
|
|
static int32_t dst_asm_addenv(DstAssembler *a, Dst envname) {
|
|
|
|
Dst check;
|
2018-01-05 21:17:55 +00:00
|
|
|
DstFuncDef *def = a->def;
|
|
|
|
int32_t envindex;
|
|
|
|
int32_t res;
|
|
|
|
if (dst_equals(a->name, envname)) {
|
2018-02-12 21:43:59 +00:00
|
|
|
return -1;
|
2018-01-05 21:17:55 +00:00
|
|
|
}
|
|
|
|
/* Check for memoized value */
|
|
|
|
check = dst_table_get(&a->envs, envname);
|
2018-02-12 21:43:59 +00:00
|
|
|
if (dst_checktype(check, DST_INTEGER)) {
|
|
|
|
return dst_unwrap_integer(check);
|
|
|
|
}
|
|
|
|
if (NULL == a->parent) return -2;
|
2018-01-05 21:17:55 +00:00
|
|
|
res = dst_asm_addenv(a->parent, envname);
|
2018-02-12 21:43:59 +00:00
|
|
|
if (res < -1) {
|
2018-01-05 21:17:55 +00:00
|
|
|
return res;
|
2018-02-12 21:43:59 +00:00
|
|
|
}
|
2018-01-05 21:17:55 +00:00
|
|
|
envindex = def->environments_length;
|
|
|
|
dst_table_put(&a->envs, envname, dst_wrap_integer(envindex));
|
|
|
|
if (envindex >= a->environments_capacity) {
|
|
|
|
int32_t newcap = 2 * envindex;
|
|
|
|
def->environments = realloc(def->environments, newcap * sizeof(int32_t));
|
|
|
|
if (NULL == def->environments) {
|
|
|
|
DST_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
a->environments_capacity = newcap;
|
|
|
|
}
|
|
|
|
def->environments[envindex] = (int32_t) res;
|
|
|
|
def->environments_length = envindex + 1;
|
|
|
|
return envindex;
|
|
|
|
}
|
2018-01-04 02:36:10 +00:00
|
|
|
|
2017-11-06 03:05:47 +00:00
|
|
|
/* Parse an argument to an assembly instruction, and return the result as an
|
2018-01-04 02:36:10 +00:00
|
|
|
* integer. This integer will need to be bounds checked. */
|
2017-12-08 20:57:02 +00:00
|
|
|
static int32_t doarg_1(
|
|
|
|
DstAssembler *a,
|
2018-01-29 20:46:26 +00:00
|
|
|
enum DstOpArgType argtype,
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst x) {
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t ret = -1;
|
2017-11-06 03:05:47 +00:00
|
|
|
DstTable *c;
|
|
|
|
switch (argtype) {
|
|
|
|
case DST_OAT_SLOT:
|
|
|
|
c = &a->slots;
|
|
|
|
break;
|
|
|
|
case DST_OAT_ENVIRONMENT:
|
|
|
|
c = &a->envs;
|
|
|
|
break;
|
|
|
|
case DST_OAT_CONSTANT:
|
|
|
|
c = &a->constants;
|
|
|
|
break;
|
|
|
|
case DST_OAT_INTEGER:
|
|
|
|
c = NULL;
|
|
|
|
break;
|
|
|
|
case DST_OAT_TYPE:
|
|
|
|
case DST_OAT_SIMPLETYPE:
|
|
|
|
c = NULL;
|
|
|
|
break;
|
|
|
|
case DST_OAT_LABEL:
|
|
|
|
c = &a->labels;
|
|
|
|
break;
|
2018-01-04 02:36:10 +00:00
|
|
|
case DST_OAT_FUNCDEF:
|
|
|
|
c = &a->defs;
|
|
|
|
break;
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
2017-11-28 23:27:55 +00:00
|
|
|
switch (dst_type(x)) {
|
2017-11-06 03:05:47 +00:00
|
|
|
default:
|
2017-11-25 04:17:04 +00:00
|
|
|
goto error;
|
2017-11-06 03:05:47 +00:00
|
|
|
break;
|
|
|
|
case DST_INTEGER:
|
2017-11-28 23:27:55 +00:00
|
|
|
ret = dst_unwrap_integer(x);
|
2017-11-25 04:17:04 +00:00
|
|
|
break;
|
2017-11-06 03:05:47 +00:00
|
|
|
case DST_TUPLE:
|
|
|
|
{
|
2018-01-06 16:09:15 +00:00
|
|
|
const Dst *t = dst_unwrap_tuple(x);
|
2017-11-06 03:05:47 +00:00
|
|
|
if (argtype == DST_OAT_TYPE) {
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t i = 0;
|
2017-11-25 04:17:04 +00:00
|
|
|
ret = 0;
|
2017-11-28 23:27:55 +00:00
|
|
|
for (i = 0; i < dst_tuple_length(t); i++) {
|
2018-01-04 02:36:10 +00:00
|
|
|
ret |= doarg_1(a, DST_OAT_SIMPLETYPE, t[i]);
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
2017-11-25 04:17:04 +00:00
|
|
|
} else {
|
|
|
|
goto error;
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DST_SYMBOL:
|
|
|
|
{
|
|
|
|
if (NULL != c) {
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst result = dst_table_get(c, x);
|
2017-11-28 23:27:55 +00:00
|
|
|
if (dst_checktype(result, DST_INTEGER)) {
|
2017-11-25 04:17:04 +00:00
|
|
|
if (argtype == DST_OAT_LABEL) {
|
2017-11-28 23:27:55 +00:00
|
|
|
ret = dst_unwrap_integer(result) - a->bytecode_count;
|
2017-11-25 04:17:04 +00:00
|
|
|
} else {
|
2017-11-28 23:27:55 +00:00
|
|
|
ret = dst_unwrap_integer(result);
|
2017-11-25 04:17:04 +00:00
|
|
|
}
|
2017-11-06 03:05:47 +00:00
|
|
|
} else {
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_errorv(a, dst_formatc("unknown name %q", x));
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
|
|
|
} else if (argtype == DST_OAT_TYPE || argtype == DST_OAT_SIMPLETYPE) {
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t index = strsearch(dst_unwrap_symbol(x), dst_type_names);
|
2017-11-06 03:05:47 +00:00
|
|
|
if (index != -1) {
|
2017-11-28 23:27:55 +00:00
|
|
|
ret = index;
|
2017-11-06 03:05:47 +00:00
|
|
|
} else {
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_errorv(a, dst_formatc("unknown type %q", x));
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
2017-11-25 04:17:04 +00:00
|
|
|
} else {
|
|
|
|
goto error;
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
2018-01-05 21:17:55 +00:00
|
|
|
if (argtype == DST_OAT_ENVIRONMENT && ret == -1) {
|
|
|
|
/* Add a new env */
|
|
|
|
ret = dst_asm_addenv(a, x);
|
2018-02-12 21:43:59 +00:00
|
|
|
if (ret < -1) {
|
2018-01-05 21:17:55 +00:00
|
|
|
dst_asm_errorv(a, dst_formatc("unknown environment %q", x));
|
|
|
|
}
|
|
|
|
}
|
2017-11-06 03:05:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-11-25 04:17:04 +00:00
|
|
|
if (argtype == DST_OAT_SLOT && ret >= a->def->slotcount)
|
2017-11-28 23:27:55 +00:00
|
|
|
a->def->slotcount = (int32_t) ret + 1;
|
2017-11-25 04:17:04 +00:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
error:
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_errorv(a, dst_formatc("error parsing instruction argument %v", x));
|
2017-11-06 03:05:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-11-21 02:39:44 +00:00
|
|
|
/* Parse a single argument to an instruction. Trims it as well as
|
|
|
|
* try to convert arguments to bit patterns */
|
|
|
|
static uint32_t doarg(
|
|
|
|
DstAssembler *a,
|
2018-01-29 20:46:26 +00:00
|
|
|
enum DstOpArgType argtype,
|
2017-11-21 02:39:44 +00:00
|
|
|
int nth,
|
|
|
|
int nbytes,
|
|
|
|
int hassign,
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst x) {
|
2018-01-04 02:36:10 +00:00
|
|
|
int32_t arg = doarg_1(a, argtype, x);
|
2017-11-06 03:05:47 +00:00
|
|
|
/* Calculate the min and max values that can be stored given
|
|
|
|
* nbytes, and whether or not the storage is signed */
|
2018-03-18 13:13:21 +00:00
|
|
|
int32_t max = (1 << ((nbytes << 3) - hassign)) - 1;
|
|
|
|
int32_t min = hassign ? -max - 1 : 0;
|
2017-11-06 03:05:47 +00:00
|
|
|
if (arg < min)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_errorv(a, dst_formatc("instruction argument %v is too small, must be %d byte%s",
|
2017-11-21 02:39:44 +00:00
|
|
|
x, nbytes, nbytes > 1 ? "s" : ""));
|
2017-11-06 03:05:47 +00:00
|
|
|
if (arg > max)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_errorv(a, dst_formatc("instruction argument %v is too large, must be %d byte%s",
|
2017-11-21 02:39:44 +00:00
|
|
|
x, nbytes, nbytes > 1 ? "s" : ""));
|
2017-11-28 23:27:55 +00:00
|
|
|
return ((uint32_t) arg) << (nth << 3);
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Provide parsing methods for the different kinds of arguments */
|
2017-12-08 20:57:02 +00:00
|
|
|
static uint32_t read_instruction(
|
|
|
|
DstAssembler *a,
|
|
|
|
const DstInstructionDef *idef,
|
2018-01-06 16:09:15 +00:00
|
|
|
const Dst *argt) {
|
2017-11-06 03:05:47 +00:00
|
|
|
uint32_t instr = idef->opcode;
|
2018-01-29 20:46:26 +00:00
|
|
|
enum DstInstructionType type = dst_instructions[idef->opcode];
|
2018-01-21 19:39:32 +00:00
|
|
|
switch (type) {
|
2017-11-06 03:05:47 +00:00
|
|
|
case DIT_0:
|
|
|
|
{
|
|
|
|
if (dst_tuple_length(argt) != 1)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(a, "expected 0 arguments: (op)");
|
2017-11-06 03:05:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DIT_S:
|
|
|
|
{
|
|
|
|
if (dst_tuple_length(argt) != 2)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(a, "expected 1 argument: (op, slot)");
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 1, 3, 0, argt[1]);
|
2017-11-06 03:05:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DIT_L:
|
|
|
|
{
|
|
|
|
if (dst_tuple_length(argt) != 2)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(a, "expected 1 argument: (op, label)");
|
|
|
|
instr |= doarg(a, DST_OAT_LABEL, 1, 3, 1, argt[1]);
|
2017-11-06 03:05:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DIT_SS:
|
|
|
|
{
|
|
|
|
if (dst_tuple_length(argt) != 3)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(a, "expected 2 arguments: (op, slot, slot)");
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 2, 2, 0, argt[2]);
|
2017-11-06 03:05:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DIT_SL:
|
|
|
|
{
|
|
|
|
if (dst_tuple_length(argt) != 3)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(a, "expected 2 arguments: (op, slot, label)");
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
|
|
|
|
instr |= doarg(a, DST_OAT_LABEL, 2, 2, 1, argt[2]);
|
2017-11-06 03:05:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DIT_ST:
|
|
|
|
{
|
|
|
|
if (dst_tuple_length(argt) != 3)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(a, "expected 2 arguments: (op, slot, type)");
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
|
|
|
|
instr |= doarg(a, DST_OAT_TYPE, 2, 2, 0, argt[2]);
|
2017-11-06 03:05:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DIT_SI:
|
|
|
|
case DIT_SU:
|
|
|
|
{
|
|
|
|
if (dst_tuple_length(argt) != 3)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(a, "expected 2 arguments: (op, slot, integer)");
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
|
2018-01-21 19:39:32 +00:00
|
|
|
instr |= doarg(a, DST_OAT_INTEGER, 2, 2, type == DIT_SI, argt[2]);
|
2018-01-04 02:36:10 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DIT_SD:
|
|
|
|
{
|
|
|
|
if (dst_tuple_length(argt) != 3)
|
|
|
|
dst_asm_error(a, "expected 2 arguments: (op, slot, funcdef)");
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
|
|
|
|
instr |= doarg(a, DST_OAT_FUNCDEF, 2, 2, 0, argt[2]);
|
2017-11-06 03:05:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DIT_SSS:
|
|
|
|
{
|
|
|
|
if (dst_tuple_length(argt) != 4)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(a, "expected 3 arguments: (op, slot, slot, slot)");
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 2, 1, 0, argt[2]);
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 3, 1, 0, argt[3]);
|
2017-11-06 03:05:47 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-11-21 02:39:44 +00:00
|
|
|
case DIT_SSI:
|
2017-11-25 04:17:04 +00:00
|
|
|
case DIT_SSU:
|
2017-11-21 02:39:44 +00:00
|
|
|
{
|
|
|
|
if (dst_tuple_length(argt) != 4)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(a, "expected 3 arguments: (op, slot, slot, integer)");
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 2, 1, 0, argt[2]);
|
2018-01-21 19:39:32 +00:00
|
|
|
instr |= doarg(a, DST_OAT_INTEGER, 3, 1, type == DIT_SSI, argt[3]);
|
2017-11-21 02:39:44 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-11-06 03:05:47 +00:00
|
|
|
case DIT_SES:
|
|
|
|
{
|
|
|
|
DstAssembler *b = a;
|
|
|
|
uint32_t env;
|
|
|
|
if (dst_tuple_length(argt) != 4)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(a, "expected 3 arguments: (op, slot, environment, envslot)");
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
|
|
|
|
env = doarg(a, DST_OAT_ENVIRONMENT, 0, 1, 0, argt[2]);
|
2017-11-06 03:05:47 +00:00
|
|
|
instr |= env << 16;
|
|
|
|
for (env += 1; env > 0; env--) {
|
|
|
|
b = b->parent;
|
|
|
|
if (NULL == b)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(a, "invalid environment index");
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
2018-01-04 02:36:10 +00:00
|
|
|
instr |= doarg(b, DST_OAT_SLOT, 3, 1, 0, argt[3]);
|
2017-11-06 03:05:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DIT_SC:
|
|
|
|
{
|
|
|
|
if (dst_tuple_length(argt) != 3)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(a, "expected 2 arguments: (op, slot, constant)");
|
|
|
|
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
|
|
|
|
instr |= doarg(a, DST_OAT_CONSTANT, 2, 2, 0, argt[2]);
|
2017-11-06 03:05:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return instr;
|
|
|
|
}
|
|
|
|
|
2017-11-21 02:39:44 +00:00
|
|
|
/* Helper to assembly. Return the assembly result */
|
2018-01-18 22:25:45 +00:00
|
|
|
static DstAssembleResult dst_asm1(DstAssembler *parent, Dst source, int flags) {
|
2017-11-21 02:39:44 +00:00
|
|
|
DstAssembleResult result;
|
2017-11-06 03:05:47 +00:00
|
|
|
DstAssembler a;
|
2018-01-18 22:25:45 +00:00
|
|
|
Dst s = source;
|
2017-11-06 03:05:47 +00:00
|
|
|
DstFuncDef *def;
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t count, i;
|
2018-01-06 16:09:15 +00:00
|
|
|
const Dst *arr;
|
|
|
|
Dst x;
|
2018-01-18 22:25:45 +00:00
|
|
|
(void) flags;
|
2017-11-06 03:05:47 +00:00
|
|
|
|
|
|
|
/* Initialize funcdef */
|
2018-01-19 21:43:19 +00:00
|
|
|
def = dst_funcdef_alloc();
|
2017-11-06 03:05:47 +00:00
|
|
|
|
|
|
|
/* Initialize Assembler */
|
|
|
|
a.def = def;
|
|
|
|
a.parent = parent;
|
|
|
|
a.errmessage = NULL;
|
2018-03-16 18:34:48 +00:00
|
|
|
a.errindex = 0;
|
2017-11-06 03:05:47 +00:00
|
|
|
a.environments_capacity = 0;
|
|
|
|
a.bytecode_count = 0;
|
2018-01-05 21:17:55 +00:00
|
|
|
a.defs_capacity = 0;
|
|
|
|
a.name = dst_wrap_nil();
|
|
|
|
dst_table_init(&a.labels, 0);
|
|
|
|
dst_table_init(&a.constants, 0);
|
|
|
|
dst_table_init(&a.slots, 0);
|
|
|
|
dst_table_init(&a.envs, 0);
|
|
|
|
dst_table_init(&a.defs, 0);
|
2017-12-08 20:57:02 +00:00
|
|
|
|
2017-11-06 03:05:47 +00:00
|
|
|
/* Set error jump */
|
|
|
|
if (setjmp(a.on_error)) {
|
|
|
|
if (NULL != a.parent) {
|
2017-12-08 20:57:02 +00:00
|
|
|
dst_asm_deinit(&a);
|
2017-11-06 03:05:47 +00:00
|
|
|
longjmp(a.parent->on_error, 1);
|
|
|
|
}
|
2017-12-17 04:11:51 +00:00
|
|
|
result.error = a.errmessage;
|
2017-11-21 02:39:44 +00:00
|
|
|
result.status = DST_ASSEMBLE_ERROR;
|
2017-12-08 20:57:02 +00:00
|
|
|
dst_asm_deinit(&a);
|
2017-11-21 02:39:44 +00:00
|
|
|
return result;
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
|
|
|
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_assert(&a,
|
|
|
|
dst_checktype(s, DST_STRUCT) ||
|
|
|
|
dst_checktype(s, DST_TABLE),
|
|
|
|
"expected struct or table for assembly source");
|
|
|
|
|
|
|
|
/* Check for function name */
|
2018-01-05 21:17:55 +00:00
|
|
|
a.name = dst_get(s, dst_csymbolv("name"));
|
2017-11-06 03:05:47 +00:00
|
|
|
|
|
|
|
/* Set function arity */
|
2018-01-04 02:36:10 +00:00
|
|
|
x = dst_get(s, dst_csymbolv("arity"));
|
2017-11-28 23:27:55 +00:00
|
|
|
def->arity = dst_checktype(x, DST_INTEGER) ? dst_unwrap_integer(x) : 0;
|
2017-11-06 03:05:47 +00:00
|
|
|
|
2017-12-17 04:11:51 +00:00
|
|
|
/* Check vararg */
|
2018-01-04 02:36:10 +00:00
|
|
|
x = dst_get(s, dst_csymbolv("vararg"));
|
|
|
|
if (dst_truthy(x)) def->flags |= DST_FUNCDEF_FLAG_VARARG;
|
2017-12-17 04:11:51 +00:00
|
|
|
|
|
|
|
/* Check source */
|
2018-01-04 02:36:10 +00:00
|
|
|
x = dst_get(s, dst_csymbolv("source"));
|
|
|
|
if (dst_checktype(x, DST_STRING)) def->source = dst_unwrap_string(x);
|
2017-12-17 04:11:51 +00:00
|
|
|
|
|
|
|
/* Check source path */
|
2018-01-04 02:36:10 +00:00
|
|
|
x = dst_get(s, dst_csymbolv("sourcepath"));
|
|
|
|
if (dst_checktype(x, DST_STRING)) def->sourcepath = dst_unwrap_string(x);
|
2017-12-17 04:11:51 +00:00
|
|
|
|
2017-11-06 03:05:47 +00:00
|
|
|
/* Create slot aliases */
|
2018-01-04 02:36:10 +00:00
|
|
|
x = dst_get(s, dst_csymbolv("slots"));
|
2017-11-06 03:05:47 +00:00
|
|
|
if (dst_seq_view(x, &arr, &count)) {
|
|
|
|
for (i = 0; i < count; i++) {
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst v = arr[i];
|
2017-11-28 23:27:55 +00:00
|
|
|
if (dst_checktype(v, DST_TUPLE)) {
|
2018-01-06 16:09:15 +00:00
|
|
|
const Dst *t = dst_unwrap_tuple(v);
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t j;
|
|
|
|
for (j = 0; j < dst_tuple_length(t); j++) {
|
|
|
|
if (!dst_checktype(t[j], DST_SYMBOL))
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(&a, "slot names must be symbols");
|
2017-11-28 23:27:55 +00:00
|
|
|
dst_table_put(&a.slots, t[j], dst_wrap_integer(i));
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
2017-11-28 23:27:55 +00:00
|
|
|
} else if (dst_checktype(v, DST_SYMBOL)) {
|
2017-11-06 03:05:47 +00:00
|
|
|
dst_table_put(&a.slots, v, dst_wrap_integer(i));
|
|
|
|
} else {
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(&a, "slot names must be symbols or tuple of symbols");
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse constants */
|
2018-01-04 02:36:10 +00:00
|
|
|
x = dst_get(s, dst_csymbolv("constants"));
|
2017-11-06 03:05:47 +00:00
|
|
|
if (dst_seq_view(x, &arr, &count)) {
|
|
|
|
def->constants_length = count;
|
2018-01-06 16:09:15 +00:00
|
|
|
def->constants = malloc(sizeof(Dst) * count);
|
2017-11-06 03:05:47 +00:00
|
|
|
if (NULL == def->constants) {
|
|
|
|
DST_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
for (i = 0; i < count; i++) {
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst ct = arr[i];
|
2017-11-28 23:27:55 +00:00
|
|
|
if (dst_checktype(ct, DST_TUPLE) &&
|
|
|
|
dst_tuple_length(dst_unwrap_tuple(ct)) > 1 &&
|
|
|
|
dst_checktype(dst_unwrap_tuple(ct)[0], DST_SYMBOL)) {
|
2018-01-06 16:09:15 +00:00
|
|
|
const Dst *t = dst_unwrap_tuple(ct);
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t tcount = dst_tuple_length(t);
|
|
|
|
const uint8_t *macro = dst_unwrap_symbol(t[0]);
|
2018-01-05 21:17:55 +00:00
|
|
|
if (0 == dst_cstrcmp(macro, "quote")) {
|
2017-11-28 23:27:55 +00:00
|
|
|
def->constants[i] = t[1];
|
2017-11-06 03:05:47 +00:00
|
|
|
} else if (tcount == 3 &&
|
2017-11-28 23:27:55 +00:00
|
|
|
dst_checktype(t[1], DST_SYMBOL) &&
|
2018-01-05 21:17:55 +00:00
|
|
|
0 == dst_cstrcmp(macro, "def")) {
|
2017-11-28 23:27:55 +00:00
|
|
|
def->constants[i] = t[2];
|
|
|
|
dst_table_put(&a.constants, t[1], dst_wrap_integer(i));
|
2017-11-06 03:05:47 +00:00
|
|
|
} else {
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_errorv(&a, dst_formatc("could not parse constant \"%v\"", ct));
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
def->constants[i] = ct;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
def->constants = NULL;
|
|
|
|
def->constants_length = 0;
|
|
|
|
}
|
|
|
|
|
2018-01-04 02:36:10 +00:00
|
|
|
/* Parse sub funcdefs */
|
2018-01-05 21:17:55 +00:00
|
|
|
x = dst_get(s, dst_csymbolv("closures"));
|
|
|
|
if (dst_seq_view(x, &arr, &count)) {
|
|
|
|
int32_t i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
DstAssembleResult subres;
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst subname;
|
2018-01-05 21:17:55 +00:00
|
|
|
int32_t newlen;
|
2018-01-18 22:25:45 +00:00
|
|
|
subres = dst_asm1(&a, arr[i], flags);
|
2018-01-05 21:17:55 +00:00
|
|
|
if (subres.status != DST_ASSEMBLE_OK) {
|
|
|
|
dst_asm_errorv(&a, subres.error);
|
|
|
|
}
|
|
|
|
subname = dst_get(arr[i], dst_csymbolv("name"));
|
|
|
|
if (!dst_checktype(subname, DST_NIL)) {
|
|
|
|
dst_table_put(&a.defs, subname, dst_wrap_integer(def->defs_length));
|
|
|
|
}
|
|
|
|
newlen = def->defs_length + 1;
|
|
|
|
if (a.defs_capacity < newlen) {
|
|
|
|
int32_t newcap = newlen;
|
|
|
|
def->defs = realloc(def->defs, newcap * sizeof(DstFuncDef *));
|
|
|
|
if (NULL == def->defs) {
|
|
|
|
DST_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
a.defs_capacity = newcap;
|
|
|
|
}
|
|
|
|
def->defs[def->defs_length] = subres.funcdef;
|
|
|
|
def->defs_length = newlen;
|
|
|
|
}
|
|
|
|
}
|
2018-01-04 02:36:10 +00:00
|
|
|
|
2017-11-06 03:05:47 +00:00
|
|
|
/* Parse bytecode and labels */
|
2018-01-04 02:36:10 +00:00
|
|
|
x = dst_get(s, dst_csymbolv("bytecode"));
|
2017-11-06 03:05:47 +00:00
|
|
|
if (dst_seq_view(x, &arr, &count)) {
|
|
|
|
/* Do labels and find length */
|
2017-11-28 23:27:55 +00:00
|
|
|
int32_t blength = 0;
|
2017-11-06 03:05:47 +00:00
|
|
|
for (i = 0; i < count; ++i) {
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst instr = arr[i];
|
2017-11-28 23:27:55 +00:00
|
|
|
if (dst_checktype(instr, DST_SYMBOL)) {
|
2017-11-06 03:05:47 +00:00
|
|
|
dst_table_put(&a.labels, instr, dst_wrap_integer(blength));
|
2017-11-28 23:27:55 +00:00
|
|
|
} else if (dst_checktype(instr, DST_TUPLE)) {
|
2017-11-06 03:05:47 +00:00
|
|
|
blength++;
|
|
|
|
} else {
|
2018-03-16 18:34:48 +00:00
|
|
|
a.errindex = i;
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(&a, "expected assembly instruction");
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Allocate bytecode array */
|
|
|
|
def->bytecode_length = blength;
|
2017-11-28 23:27:55 +00:00
|
|
|
def->bytecode = malloc(sizeof(int32_t) * blength);
|
2017-11-06 03:05:47 +00:00
|
|
|
if (NULL == def->bytecode) {
|
|
|
|
DST_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
/* Do bytecode */
|
|
|
|
for (i = 0; i < count; ++i) {
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst instr = arr[i];
|
2017-11-28 23:27:55 +00:00
|
|
|
if (dst_checktype(instr, DST_SYMBOL)) {
|
2017-11-06 03:05:47 +00:00
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
uint32_t op;
|
|
|
|
const DstInstructionDef *idef;
|
2018-01-06 16:09:15 +00:00
|
|
|
const Dst *t;
|
2018-03-16 18:34:48 +00:00
|
|
|
a.errindex = i;
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_assert(&a, dst_checktype(instr, DST_TUPLE), "expected tuple");
|
2017-11-28 23:27:55 +00:00
|
|
|
t = dst_unwrap_tuple(instr);
|
|
|
|
if (dst_tuple_length(t) == 0) {
|
2017-11-06 03:05:47 +00:00
|
|
|
op = 0;
|
|
|
|
} else {
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_assert(&a, dst_checktype(t[0], DST_SYMBOL),
|
2017-11-06 03:05:47 +00:00
|
|
|
"expected symbol in assembly instruction");
|
2018-01-05 21:17:55 +00:00
|
|
|
idef = dst_strbinsearch(
|
|
|
|
&dst_ops,
|
|
|
|
sizeof(dst_ops)/sizeof(DstInstructionDef),
|
|
|
|
sizeof(DstInstructionDef),
|
|
|
|
dst_unwrap_symbol(t[0]));
|
2017-11-21 02:39:44 +00:00
|
|
|
if (NULL == idef)
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_errorv(&a, dst_formatc("unknown instruction %v", instr));
|
|
|
|
op = read_instruction(&a, idef, t);
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
|
|
|
def->bytecode[a.bytecode_count++] = op;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2018-01-04 02:36:10 +00:00
|
|
|
dst_asm_error(&a, "bytecode expected");
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
2018-03-16 18:34:48 +00:00
|
|
|
a.errindex = -1;
|
2017-12-17 04:11:51 +00:00
|
|
|
|
|
|
|
/* Check for source mapping */
|
2018-01-04 02:36:10 +00:00
|
|
|
x = dst_get(s, dst_csymbolv("sourcemap"));
|
2017-12-17 04:11:51 +00:00
|
|
|
if (dst_seq_view(x, &arr, &count)) {
|
2018-02-12 17:28:58 +00:00
|
|
|
dst_asm_assert(&a, count == def->bytecode_length, "sourcemap must have the same length as the bytecode");
|
2018-03-16 03:27:44 +00:00
|
|
|
def->sourcemap = malloc(sizeof(DstSourceMapping) * count);
|
2018-02-12 17:28:58 +00:00
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
const Dst *tup;
|
|
|
|
Dst entry = arr[i];
|
2018-03-16 03:27:44 +00:00
|
|
|
DstSourceMapping mapping;
|
2018-02-12 17:28:58 +00:00
|
|
|
if (!dst_checktype(entry, DST_TUPLE)) {
|
|
|
|
dst_asm_error(&a, "expected tuple");
|
|
|
|
}
|
|
|
|
tup = dst_unwrap_tuple(entry);
|
|
|
|
if (!dst_checktype(tup[0], DST_INTEGER)) {
|
|
|
|
dst_asm_error(&a, "expected integer");
|
2017-12-17 04:11:51 +00:00
|
|
|
}
|
2018-02-12 17:28:58 +00:00
|
|
|
if (!dst_checktype(tup[1], DST_INTEGER)) {
|
|
|
|
dst_asm_error(&a, "expected integer");
|
2017-12-17 04:11:51 +00:00
|
|
|
}
|
2018-03-16 03:27:44 +00:00
|
|
|
mapping.start = dst_unwrap_integer(tup[0]);
|
|
|
|
mapping.end = dst_unwrap_integer(tup[1]);
|
|
|
|
def->sourcemap[i] = mapping;
|
2017-12-17 04:11:51 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-06 03:05:47 +00:00
|
|
|
|
2018-01-20 22:19:47 +00:00
|
|
|
/* Set environments */
|
2017-11-06 03:05:47 +00:00
|
|
|
def->environments =
|
2017-11-28 23:27:55 +00:00
|
|
|
realloc(def->environments, def->environments_length * sizeof(int32_t));
|
2018-01-20 22:19:47 +00:00
|
|
|
|
|
|
|
/* Verify the func def */
|
|
|
|
if (dst_verify(def)) {
|
|
|
|
dst_asm_error(&a, "invalid assembly");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finish everything and return funcdef */
|
|
|
|
dst_asm_deinit(&a);
|
2017-12-17 04:11:51 +00:00
|
|
|
result.funcdef = def;
|
2017-11-21 02:39:44 +00:00
|
|
|
result.status = DST_ASSEMBLE_OK;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assemble a function */
|
2018-01-18 22:25:45 +00:00
|
|
|
DstAssembleResult dst_asm(Dst source, int flags) {
|
|
|
|
return dst_asm1(NULL, source, flags);
|
2017-11-06 03:05:47 +00:00
|
|
|
}
|
|
|
|
|
2017-12-17 04:11:51 +00:00
|
|
|
/* Disassembly */
|
|
|
|
|
|
|
|
/* Find the deinfintion of an instruction given the instruction word. Return
|
|
|
|
* NULL if not found. */
|
|
|
|
static const DstInstructionDef *dst_asm_reverse_lookup(uint32_t instr) {
|
|
|
|
size_t i;
|
2018-03-11 20:30:38 +00:00
|
|
|
uint32_t opcode = instr & 0x7F;
|
2017-12-17 04:11:51 +00:00
|
|
|
for (i = 0; i < sizeof(dst_ops)/sizeof(DstInstructionDef); i++) {
|
|
|
|
const DstInstructionDef *def = dst_ops + i;
|
|
|
|
if (def->opcode == opcode)
|
|
|
|
return def;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create some constant sized tuples */
|
2018-01-06 16:09:15 +00:00
|
|
|
static Dst tup1(Dst x) {
|
|
|
|
Dst *tup = dst_tuple_begin(1);
|
2017-12-17 04:11:51 +00:00
|
|
|
tup[0] = x;
|
|
|
|
return dst_wrap_tuple(dst_tuple_end(tup));
|
|
|
|
}
|
2018-01-06 16:09:15 +00:00
|
|
|
static Dst tup2(Dst x, Dst y) {
|
|
|
|
Dst *tup = dst_tuple_begin(2);
|
2017-12-17 04:11:51 +00:00
|
|
|
tup[0] = x;
|
|
|
|
tup[1] = y;
|
|
|
|
return dst_wrap_tuple(dst_tuple_end(tup));
|
|
|
|
}
|
2018-01-06 16:09:15 +00:00
|
|
|
static Dst tup3(Dst x, Dst y, Dst z) {
|
|
|
|
Dst *tup = dst_tuple_begin(3);
|
2017-12-17 04:11:51 +00:00
|
|
|
tup[0] = x;
|
|
|
|
tup[1] = y;
|
|
|
|
tup[2] = z;
|
|
|
|
return dst_wrap_tuple(dst_tuple_end(tup));
|
|
|
|
}
|
2018-01-06 16:09:15 +00:00
|
|
|
static Dst tup4(Dst w, Dst x, Dst y, Dst z) {
|
|
|
|
Dst *tup = dst_tuple_begin(4);
|
2017-12-17 04:11:51 +00:00
|
|
|
tup[0] = w;
|
|
|
|
tup[1] = x;
|
|
|
|
tup[2] = y;
|
|
|
|
tup[3] = z;
|
|
|
|
return dst_wrap_tuple(dst_tuple_end(tup));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Given an argument, convert it to the appriate integer or symbol */
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst dst_asm_decode_instruction(uint32_t instr) {
|
2017-12-17 04:11:51 +00:00
|
|
|
const DstInstructionDef *def = dst_asm_reverse_lookup(instr);
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst name;
|
2017-12-17 04:11:51 +00:00
|
|
|
if (NULL == def) {
|
|
|
|
return dst_wrap_integer((int32_t)instr);
|
|
|
|
}
|
|
|
|
name = dst_csymbolv(def->name);
|
|
|
|
#define oparg(shift, mask) ((instr >> ((shift) << 3)) & (mask))
|
2018-01-21 19:39:32 +00:00
|
|
|
switch (dst_instructions[def->opcode]) {
|
2017-12-17 04:11:51 +00:00
|
|
|
case DIT_0:
|
|
|
|
return tup1(name);
|
|
|
|
case DIT_S:
|
|
|
|
return tup2(name, dst_wrap_integer(oparg(1, 0xFFFFFF)));
|
|
|
|
case DIT_L:
|
|
|
|
return tup2(name, dst_wrap_integer((int32_t)instr >> 8));
|
|
|
|
case DIT_SS:
|
|
|
|
case DIT_ST:
|
|
|
|
case DIT_SC:
|
|
|
|
case DIT_SU:
|
2018-01-04 02:36:10 +00:00
|
|
|
case DIT_SD:
|
2017-12-17 04:11:51 +00:00
|
|
|
return tup3(name,
|
|
|
|
dst_wrap_integer(oparg(1, 0xFF)),
|
|
|
|
dst_wrap_integer(oparg(2, 0xFFFF)));
|
|
|
|
case DIT_SI:
|
|
|
|
case DIT_SL:
|
|
|
|
return tup3(name,
|
|
|
|
dst_wrap_integer(oparg(1, 0xFF)),
|
|
|
|
dst_wrap_integer((int32_t)instr >> 16));
|
|
|
|
case DIT_SSS:
|
|
|
|
case DIT_SES:
|
|
|
|
case DIT_SSU:
|
|
|
|
return tup4(name,
|
|
|
|
dst_wrap_integer(oparg(1, 0xFF)),
|
|
|
|
dst_wrap_integer(oparg(2, 0xFF)),
|
|
|
|
dst_wrap_integer(oparg(3, 0xFF)));
|
|
|
|
case DIT_SSI:
|
|
|
|
return tup4(name,
|
|
|
|
dst_wrap_integer(oparg(1, 0xFF)),
|
|
|
|
dst_wrap_integer(oparg(2, 0xFF)),
|
|
|
|
dst_wrap_integer((int32_t)instr >> 24));
|
|
|
|
}
|
|
|
|
#undef oparg
|
2018-01-12 22:38:06 +00:00
|
|
|
return dst_wrap_nil();
|
2017-12-17 04:11:51 +00:00
|
|
|
}
|
|
|
|
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst dst_disasm(DstFuncDef *def) {
|
2017-12-17 04:11:51 +00:00
|
|
|
int32_t i;
|
|
|
|
DstArray *bcode = dst_array(def->bytecode_length);
|
2017-12-30 21:46:59 +00:00
|
|
|
DstArray *constants;
|
2017-12-17 04:11:51 +00:00
|
|
|
DstTable *ret = dst_table(10);
|
2018-02-12 21:43:59 +00:00
|
|
|
dst_table_put(ret, dst_csymbolv("arity"), dst_wrap_integer(def->arity));
|
2017-12-17 04:11:51 +00:00
|
|
|
dst_table_put(ret, dst_csymbolv("bytecode"), dst_wrap_array(bcode));
|
2018-01-04 02:36:10 +00:00
|
|
|
if (NULL != def->sourcepath) {
|
|
|
|
dst_table_put(ret, dst_csymbolv("sourcepath"),
|
|
|
|
dst_wrap_string(def->sourcepath));
|
2017-12-17 04:11:51 +00:00
|
|
|
}
|
2018-01-04 02:36:10 +00:00
|
|
|
if (NULL != def->source) {
|
2017-12-17 04:11:51 +00:00
|
|
|
dst_table_put(ret, dst_csymbolv("source"), dst_wrap_string(def->source));
|
|
|
|
}
|
|
|
|
if (def->flags & DST_FUNCDEF_FLAG_VARARG) {
|
|
|
|
dst_table_put(ret, dst_csymbolv("vararg"), dst_wrap_true());
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add constants */
|
2017-12-30 21:46:59 +00:00
|
|
|
if (def->constants_length > 0) {
|
|
|
|
constants = dst_array(def->constants_length);
|
|
|
|
dst_table_put(ret, dst_csymbolv("constants"), dst_wrap_array(constants));
|
|
|
|
for (i = 0; i < def->constants_length; i++) {
|
2018-01-06 16:09:15 +00:00
|
|
|
Dst src = def->constants[i];
|
|
|
|
Dst dest;
|
2017-12-30 21:46:59 +00:00
|
|
|
if (dst_checktype(src, DST_TUPLE)) {
|
|
|
|
dest = tup2(dst_csymbolv("quote"), src);
|
|
|
|
} else {
|
|
|
|
dest = src;
|
|
|
|
}
|
|
|
|
constants->data[i] = dest;
|
2017-12-17 04:11:51 +00:00
|
|
|
}
|
2017-12-30 21:46:59 +00:00
|
|
|
constants->count = def->constants_length;
|
2017-12-17 04:11:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add bytecode */
|
|
|
|
for (i = 0; i < def->bytecode_length; i++) {
|
|
|
|
bcode->data[i] = dst_asm_decode_instruction(def->bytecode[i]);
|
|
|
|
}
|
|
|
|
bcode->count = def->bytecode_length;
|
|
|
|
|
|
|
|
/* Add source map */
|
2018-01-04 02:36:10 +00:00
|
|
|
if (NULL != def->sourcemap) {
|
2018-02-12 17:28:58 +00:00
|
|
|
DstArray *sourcemap = dst_array(def->bytecode_length);
|
2018-03-16 03:27:44 +00:00
|
|
|
for (i = 0; i < def->bytecode_length; i++) {
|
2018-02-12 17:28:58 +00:00
|
|
|
Dst *t = dst_tuple_begin(2);
|
2018-03-16 03:27:44 +00:00
|
|
|
DstSourceMapping mapping = def->sourcemap[i];
|
|
|
|
t[0] = dst_wrap_integer(mapping.start);
|
|
|
|
t[1] = dst_wrap_integer(mapping.end);
|
|
|
|
sourcemap->data[i] = dst_wrap_tuple(dst_tuple_end(t));
|
2017-12-17 04:11:51 +00:00
|
|
|
}
|
2018-02-12 17:28:58 +00:00
|
|
|
sourcemap->count = def->bytecode_length;
|
2017-12-17 04:11:51 +00:00
|
|
|
dst_table_put(ret, dst_csymbolv("sourcemap"), dst_wrap_array(sourcemap));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add environments */
|
2018-01-04 02:36:10 +00:00
|
|
|
if (NULL != def->environments) {
|
2017-12-17 04:11:51 +00:00
|
|
|
DstArray *envs = dst_array(def->environments_length);
|
|
|
|
for (i = 0; i < def->environments_length; i++) {
|
|
|
|
envs->data[i] = dst_wrap_integer(def->environments[i]);
|
|
|
|
}
|
|
|
|
envs->count = def->environments_length;
|
|
|
|
dst_table_put(ret, dst_csymbolv("environments"), dst_wrap_array(envs));
|
|
|
|
}
|
|
|
|
|
2018-01-04 02:36:10 +00:00
|
|
|
/* Add closures */
|
|
|
|
/* Funcdefs cannot be recursive */
|
|
|
|
if (NULL != def->defs) {
|
|
|
|
DstArray *defs = dst_array(def->defs_length);
|
|
|
|
for (i = 0; i < def->defs_length; i++) {
|
|
|
|
defs->data[i] = dst_disasm(def->defs[i]);
|
|
|
|
}
|
|
|
|
defs->count = def->defs_length;
|
|
|
|
dst_table_put(ret, dst_csymbolv("defs"), dst_wrap_array(defs));
|
|
|
|
}
|
|
|
|
|
2017-12-21 04:03:34 +00:00
|
|
|
/* Add slotcount */
|
|
|
|
dst_table_put(ret, dst_csymbolv("slotcount"), dst_wrap_integer(def->slotcount));
|
|
|
|
|
2017-12-17 04:11:51 +00:00
|
|
|
return dst_wrap_struct(dst_table_to_struct(ret));
|
|
|
|
}
|
2018-01-16 04:31:39 +00:00
|
|
|
|
|
|
|
/* C Function for assembly */
|
|
|
|
int dst_asm_cfun(DstArgs args) {
|
|
|
|
DstAssembleResult res;
|
|
|
|
if (args.n < 1) return dst_throw(args, "expected assembly source");
|
2018-01-18 22:25:45 +00:00
|
|
|
res = dst_asm(args.v[0], 0);
|
2018-01-16 04:31:39 +00:00
|
|
|
if (res.status == DST_ASSEMBLE_OK) {
|
2018-02-12 21:43:59 +00:00
|
|
|
return dst_return(args, dst_wrap_function(dst_thunk(res.funcdef)));
|
2018-01-16 04:31:39 +00:00
|
|
|
} else {
|
|
|
|
return dst_throwv(args, dst_wrap_string(res.error));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int dst_disasm_cfun(DstArgs args) {
|
|
|
|
DstFunction *f;
|
|
|
|
if (args.n < 1 || !dst_checktype(args.v[0], DST_FUNCTION))
|
|
|
|
return dst_throw(args, "expected function");
|
|
|
|
f = dst_unwrap_function(args.v[0]);
|
|
|
|
return dst_return(args, dst_disasm(f->def));
|
|
|
|
}
|
2018-01-30 04:38:49 +00:00
|
|
|
|
|
|
|
static const DstReg cfuns[] = {
|
|
|
|
{"asm", dst_asm_cfun},
|
|
|
|
{"disasm", dst_disasm_cfun},
|
|
|
|
{NULL, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Load the library */
|
|
|
|
int dst_lib_asm(DstArgs args) {
|
|
|
|
DstTable *env = dst_env_arg(args);
|
|
|
|
dst_env_cfuns(env, cfuns);
|
|
|
|
return 0;
|
|
|
|
}
|