.
This commit is contained in:
13
Makefile
13
Makefile
@@ -3,7 +3,10 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
# install prefix
|
# install prefix
|
||||||
PREFIX=/usr/local
|
PREFIX ?= /usr/local
|
||||||
|
|
||||||
|
# add functions to debug assembly to console
|
||||||
|
DEBUG_ASSEMBLY ?= 0
|
||||||
|
|
||||||
#
|
#
|
||||||
# internal flags/options
|
# internal flags/options
|
||||||
@@ -48,6 +51,10 @@ RELEASE_LDFLAGS=-flto=auto
|
|||||||
CFLAGS+=-std=c99 -D_GNU_SOURCE -fPIC -fvisibility=hidden -isystem lib/contrib -MMD -MP
|
CFLAGS+=-std=c99 -D_GNU_SOURCE -fPIC -fvisibility=hidden -isystem lib/contrib -MMD -MP
|
||||||
LDFLAGS+=
|
LDFLAGS+=
|
||||||
|
|
||||||
|
ifeq ($(DEBUG_ASSEMBLY),1)
|
||||||
|
CFLAGS += -DDEBUG_ASSEMBLY
|
||||||
|
endif
|
||||||
|
|
||||||
#
|
#
|
||||||
# generic targets
|
# generic targets
|
||||||
#
|
#
|
||||||
@@ -58,7 +65,7 @@ check: tyche-test
|
|||||||
./tyche-test
|
./tyche-test
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f tyche libtyche.a libtyche.so* tyche-test **/*.o **/*.d lib/compiler/compiler.h
|
rm -f tyche libtyche.a libtyche.so* tyche-test **/*.o **/*.d lib/compiler/compiler.lua.h
|
||||||
|
|
||||||
install: tyche libtyche.a libtyche.so.${VERSION} lib/tyche.h
|
install: tyche libtyche.a libtyche.so.${VERSION} lib/tyche.h
|
||||||
install -m 644 libtyche.a libtyche.so.${VERSION} ${PREFIX}/lib
|
install -m 644 libtyche.a libtyche.so.${VERSION} ${PREFIX}/lib
|
||||||
@@ -95,7 +102,7 @@ tyche: src/tyche.o libtyche.a
|
|||||||
$(CC) -o $@ $^ ${LDFLAGS}
|
$(CC) -o $@ $^ ${LDFLAGS}
|
||||||
strip $@
|
strip $@
|
||||||
|
|
||||||
tyche-test: CFLAGS += ${DEBUG_CFLAGS}
|
tyche-test: CFLAGS += ${DEBUG_CFLAGS} -DDEBUG_ASSEMBLY
|
||||||
tyche-test: LDFLAGS += ${DEBUG_LDFLAGS}
|
tyche-test: LDFLAGS += ${DEBUG_LDFLAGS}
|
||||||
tyche-test: test/tests.o libtyche.a
|
tyche-test: test/tests.o libtyche.a
|
||||||
$(CC) -o $@ $^ ${LDFLAGS} -I../lib
|
$(CC) -o $@ $^ ${LDFLAGS} -I../lib
|
||||||
|
|||||||
5
TODO.md
5
TODO.md
@@ -24,8 +24,9 @@ Decisions:
|
|||||||
- [ ] VM operations
|
- [ ] VM operations
|
||||||
- [x] Expressions
|
- [x] Expressions
|
||||||
- [x] Local variables
|
- [x] Local variables
|
||||||
- [ ] Functions
|
- [x] Functions
|
||||||
- [ ] With parameters
|
- [x] With parameters
|
||||||
|
- [ ] Debug VM execution
|
||||||
- [ ] Control flow
|
- [ ] Control flow
|
||||||
- [ ] Recursion
|
- [ ] Recursion
|
||||||
- [ ] Strings
|
- [ ] Strings
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Stack operations:
|
|||||||
|
|
||||||
Local variables:
|
Local variables:
|
||||||
a3 c3 e3 pushv [int] Push n nil values into the stack (used to init local vars)
|
a3 c3 e3 pushv [int] Push n nil values into the stack (used to init local vars)
|
||||||
ab cb eb set [index] Set value in stack position (set local variable)
|
ae ce ee set [index] Set value in stack position (set local variable)
|
||||||
a4 c4 e4 dupv [index] Duplicate stack value (load local variable)
|
a4 c4 e4 dupv [index] Duplicate stack value (load local variable)
|
||||||
a5 c5 e5 setg [int] Set global variable
|
a5 c5 e5 setg [int] Set global variable
|
||||||
a6 c6 e6 getg [int] Get global variable
|
a6 c6 e6 getg [int] Get global variable
|
||||||
@@ -75,7 +75,7 @@ Logical/arithmetic:
|
|||||||
Other value operations:
|
Other value operations:
|
||||||
40 len Get table, array or string size
|
40 len Get table, array or string size
|
||||||
41 type Get type from value at the top of the stack
|
41 type Get type from value at the top of the stack
|
||||||
aa cast [type] Cast type to another type
|
ad cast [type] Cast type to another type
|
||||||
42 ver Return VM version
|
42 ver Return VM version
|
||||||
|
|
||||||
External code:
|
External code:
|
||||||
|
|||||||
111
lib/code.c
111
lib/code.c
@@ -25,6 +25,7 @@ struct Code {
|
|||||||
uint32_t* const_addr;
|
uint32_t* const_addr;
|
||||||
uint32_t fn_count;
|
uint32_t fn_count;
|
||||||
uint32_t* fn_addr;
|
uint32_t* fn_addr;
|
||||||
|
uint32_t* fn_sz;
|
||||||
};
|
};
|
||||||
|
|
||||||
Code* code_new(void)
|
Code* code_new(void)
|
||||||
@@ -37,6 +38,7 @@ void code_destroy(Code* code)
|
|||||||
{
|
{
|
||||||
free(code->const_addr);
|
free(code->const_addr);
|
||||||
free(code->fn_addr);
|
free(code->fn_addr);
|
||||||
|
free(code->fn_sz);
|
||||||
free(code);
|
free(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,12 +93,16 @@ TYC_RESULT code_load_bytecode(Code* code, uint8_t const* bytecode, size_t byteco
|
|||||||
addr += 4;
|
addr += 4;
|
||||||
|
|
||||||
code->fn_addr = xcalloc(code->fn_count, sizeof(uint32_t));
|
code->fn_addr = xcalloc(code->fn_count, sizeof(uint32_t));
|
||||||
|
code->fn_sz = xcalloc(code->fn_count, sizeof(uint32_t));
|
||||||
code->fn_addr[0] = addr;
|
code->fn_addr[0] = addr;
|
||||||
for (size_t i = 1; i < code->fn_count; ++i) {
|
|
||||||
uint32_t addr_next;
|
uint32_t addr_next;
|
||||||
|
for (size_t i = 1; i < code->fn_count; ++i) {
|
||||||
memcpy(&addr_next, &bytecode[addr], sizeof(uint32_t));
|
memcpy(&addr_next, &bytecode[addr], sizeof(uint32_t));
|
||||||
|
code->fn_sz[i-1] = addr_next - addr - 4;
|
||||||
addr = code->fn_addr[i] = addr_next;
|
addr = code->fn_addr[i] = addr_next;
|
||||||
}
|
}
|
||||||
|
memcpy(&addr_next, &bytecode[addr], sizeof(uint32_t));
|
||||||
|
code->fn_sz[code->fn_count-1] = addr_next - addr - 4;
|
||||||
|
|
||||||
return T_OK;
|
return T_OK;
|
||||||
}
|
}
|
||||||
@@ -132,6 +138,11 @@ uint32_t code_n_functions(Code const* code)
|
|||||||
return code->fn_count;
|
return code->fn_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t code_function_sz(Code const* code, uint32_t f_id)
|
||||||
|
{
|
||||||
|
return code->fn_sz[f_id];
|
||||||
|
}
|
||||||
|
|
||||||
Instruction code_next_instruction(Code const* code, uint32_t function_id, uint32_t pc)
|
Instruction code_next_instruction(Code const* code, uint32_t function_id, uint32_t pc)
|
||||||
{
|
{
|
||||||
uint32_t addr = code->fn_addr[function_id] + 4 + pc;
|
uint32_t addr = code->fn_addr[function_id] + 4 + pc;
|
||||||
@@ -162,3 +173,101 @@ Instruction code_next_instruction(Code const* code, uint32_t function_id, uint32
|
|||||||
.sz = sz,
|
.sz = sz,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ASSEMBLY
|
||||||
|
|
||||||
|
void code_decompile(Code const* code)
|
||||||
|
{
|
||||||
|
if (code_n_consts(code) > 0)
|
||||||
|
printf(".const\n");
|
||||||
|
|
||||||
|
for (size_t const_id = 0; const_id < code_n_consts(code); ++const_id) {
|
||||||
|
TYC_CONST_TYPE type = code_const_type(code, const_id);
|
||||||
|
if (type == TC_STRING)
|
||||||
|
printf(" %03zu: \"%s\"\n", const_id, code_const_string(code, const_id));
|
||||||
|
else if (type == TC_REAL)
|
||||||
|
printf(" %03zu: %f\n", const_id, (double) code_const_real(code, const_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t f_id = 0; f_id < code_n_functions(code); ++f_id) {
|
||||||
|
printf(".func %d\n", f_id);
|
||||||
|
uint32_t pc = 0;
|
||||||
|
while (pc < code_function_sz(code, f_id)) {
|
||||||
|
Instruction inst = code_next_instruction(code, f_id, pc);
|
||||||
|
char buf[50];
|
||||||
|
code_parse_instruction(inst, buf, sizeof buf);
|
||||||
|
printf(" %s ; %d\n", buf, pc);
|
||||||
|
pc += inst.sz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void code_parse_instruction(Instruction inst, char* outbuf, size_t sz)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
switch (inst.operator) {
|
||||||
|
case TO_PUSHI: n = snprintf(outbuf, sz, "pushi "); break;
|
||||||
|
case TO_PUSHC: n = snprintf(outbuf, sz, "pushc "); break;
|
||||||
|
case TO_PUSHF: n = snprintf(outbuf, sz, "pushf "); break;
|
||||||
|
case TO_PUSHN: n = snprintf(outbuf, sz, "pushn "); break;
|
||||||
|
case TO_PUSHZ: n = snprintf(outbuf, sz, "pushz "); break;
|
||||||
|
case TO_PUSHT: n = snprintf(outbuf, sz, "pusht "); break;
|
||||||
|
case TO_NEWA: n = snprintf(outbuf, sz, "newa "); break;
|
||||||
|
case TO_NEWT: n = snprintf(outbuf, sz, "newt "); break;
|
||||||
|
case TO_POP: n = snprintf(outbuf, sz, "pop "); break;
|
||||||
|
case TO_DUP: n = snprintf(outbuf, sz, "dup "); break;
|
||||||
|
case TO_PUSHV: n = snprintf(outbuf, sz, "pushv "); break;
|
||||||
|
case TO_SET: n = snprintf(outbuf, sz, "set "); break;
|
||||||
|
case TO_DUPV: n = snprintf(outbuf, sz, "dupv "); break;
|
||||||
|
case TO_SETG: n = snprintf(outbuf, sz, "setg "); break;
|
||||||
|
case TO_GETG: n = snprintf(outbuf, sz, "getg "); break;
|
||||||
|
case TO_CALL: n = snprintf(outbuf, sz, "call "); break;
|
||||||
|
case TO_RET: n = snprintf(outbuf, sz, "ret "); break;
|
||||||
|
case TO_RETI: n = snprintf(outbuf, sz, "reti "); break;
|
||||||
|
case TO_GETKV: n = snprintf(outbuf, sz, "getkv "); break;
|
||||||
|
case TO_SETKV: n = snprintf(outbuf, sz, "setkv "); break;
|
||||||
|
case TO_GETI: n = snprintf(outbuf, sz, "geti "); break;
|
||||||
|
case TO_SETI: n = snprintf(outbuf, sz, "seti "); break;
|
||||||
|
case TO_APPND: n = snprintf(outbuf, sz, "appnd "); break;
|
||||||
|
case TO_NEXT: n = snprintf(outbuf, sz, "next "); break;
|
||||||
|
case TO_SMT: n = snprintf(outbuf, sz, "smt "); break;
|
||||||
|
case TO_MT: n = snprintf(outbuf, sz, "mt "); break;
|
||||||
|
case TO_SUM: n = snprintf(outbuf, sz, "sum "); break;
|
||||||
|
case TO_SUB: n = snprintf(outbuf, sz, "sub "); break;
|
||||||
|
case TO_MUL: n = snprintf(outbuf, sz, "mul "); break;
|
||||||
|
case TO_DIV: n = snprintf(outbuf, sz, "div "); break;
|
||||||
|
case TO_IDIV: n = snprintf(outbuf, sz, "idiv "); break;
|
||||||
|
case TO_MOD: n = snprintf(outbuf, sz, "mod "); break;
|
||||||
|
case TO_EQ: n = snprintf(outbuf, sz, "eq "); break;
|
||||||
|
case TO_NEQ: n = snprintf(outbuf, sz, "neq "); break;
|
||||||
|
case TO_LT: n = snprintf(outbuf, sz, "lt "); break;
|
||||||
|
case TO_LTE: n = snprintf(outbuf, sz, "lte "); break;
|
||||||
|
case TO_GT: n = snprintf(outbuf, sz, "gt "); break;
|
||||||
|
case TO_GTE: n = snprintf(outbuf, sz, "gte "); break;
|
||||||
|
case TO_AND: n = snprintf(outbuf, sz, "and "); break;
|
||||||
|
case TO_OR: n = snprintf(outbuf, sz, "or "); break;
|
||||||
|
case TO_XOR: n = snprintf(outbuf, sz, "xor "); break;
|
||||||
|
case TO_POW: n = snprintf(outbuf, sz, "pow "); break;
|
||||||
|
case TO_SHL: n = snprintf(outbuf, sz, "shl "); break;
|
||||||
|
case TO_SHR: n = snprintf(outbuf, sz, "shr "); break;
|
||||||
|
case TO_LEN: n = snprintf(outbuf, sz, "len "); break;
|
||||||
|
case TO_TYPE: n = snprintf(outbuf, sz, "type "); break;
|
||||||
|
case TO_CAST: n = snprintf(outbuf, sz, "cast "); break;
|
||||||
|
case TO_VER: n = snprintf(outbuf, sz, "ver "); break;
|
||||||
|
case TO_CMPL: n = snprintf(outbuf, sz, "cmpl "); break;
|
||||||
|
case TO_ASMBL: n = snprintf(outbuf, sz, "asmbl "); break;
|
||||||
|
case TO_LOAD: n = snprintf(outbuf, sz, "load "); break;
|
||||||
|
case TO_BZ: n = snprintf(outbuf, sz, "bz "); break;
|
||||||
|
case TO_BNZ: n = snprintf(outbuf, sz, "bnz "); break;
|
||||||
|
case TO_JMP: n = snprintf(outbuf, sz, "jmp "); break;
|
||||||
|
case TO_GC: n = snprintf(outbuf, sz, "gc "); break;
|
||||||
|
default: n = snprintf(outbuf, sz, "??? "); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inst.operator >= OP_8BIT_OPERAND)
|
||||||
|
snprintf(&outbuf[n], sz + (size_t) n, "%2d", inst.operand);
|
||||||
|
else
|
||||||
|
snprintf(&outbuf[n], sz + (size_t) n, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -95,10 +95,10 @@ local instructions = {
|
|||||||
|
|
||||||
-- local variables
|
-- local variables
|
||||||
pushv = 0xa3,
|
pushv = 0xa3,
|
||||||
set = 0xa4,
|
set = 0xae,
|
||||||
dupv = 0xa5,
|
dupv = 0xa4,
|
||||||
setg = 0xa6,
|
setg = 0xa5,
|
||||||
getg = 0xa7,
|
getg = 0xa6,
|
||||||
|
|
||||||
-- function operations
|
-- function operations
|
||||||
call = 0xa7,
|
call = 0xa7,
|
||||||
@@ -138,7 +138,7 @@ local instructions = {
|
|||||||
-- other value operations
|
-- other value operations
|
||||||
len = 0x40,
|
len = 0x40,
|
||||||
type = 0x41,
|
type = 0x41,
|
||||||
cast = 0xaa,
|
cast = 0xad,
|
||||||
ver = 0x42,
|
ver = 0x42,
|
||||||
|
|
||||||
-- external code
|
-- external code
|
||||||
@@ -244,7 +244,7 @@ local function assemble(proto)
|
|||||||
-- add labels
|
-- add labels
|
||||||
if inst.labels then
|
if inst.labels then
|
||||||
for _, lbl in ipairs(inst.labels) do
|
for _, lbl in ipairs(inst.labels) do
|
||||||
labels[lbl] = #bin - function_start - 1
|
labels[lbl] = #bin - function_start
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
16
lib/priv.h
16
lib/priv.h
@@ -26,10 +26,10 @@ typedef enum {
|
|||||||
|
|
||||||
// LOCAL VARIABLES
|
// LOCAL VARIABLES
|
||||||
TO_PUSHV = 0XA3,
|
TO_PUSHV = 0XA3,
|
||||||
TO_SET = 0XA4,
|
TO_SET = 0XAE,
|
||||||
TO_DUPV = 0XA5,
|
TO_DUPV = 0XA4,
|
||||||
TO_SETG = 0XA6,
|
TO_SETG = 0XA5,
|
||||||
TO_GETG = 0XA7,
|
TO_GETG = 0XA6,
|
||||||
|
|
||||||
// FUNCTION OPERATIONS
|
// FUNCTION OPERATIONS
|
||||||
TO_CALL = 0XA7,
|
TO_CALL = 0XA7,
|
||||||
@@ -69,7 +69,7 @@ typedef enum {
|
|||||||
// OTHER VALUE OPERATIONS
|
// OTHER VALUE OPERATIONS
|
||||||
TO_LEN = 0X40,
|
TO_LEN = 0X40,
|
||||||
TO_TYPE = 0X41,
|
TO_TYPE = 0X41,
|
||||||
TO_CAST = 0XAA,
|
TO_CAST = 0XAD,
|
||||||
TO_VER = 0X42,
|
TO_VER = 0X42,
|
||||||
|
|
||||||
// EXTERNAL CODE
|
// EXTERNAL CODE
|
||||||
@@ -156,7 +156,7 @@ TYC_RESULT stack_push(Stack* s, VALUE v);
|
|||||||
TYC_RESULT stack_peek(Stack const* s, VALUE* v_out);
|
TYC_RESULT stack_peek(Stack const* s, VALUE* v_out);
|
||||||
TYC_RESULT stack_pop(Stack* s, VALUE* v_out);
|
TYC_RESULT stack_pop(Stack* s, VALUE* v_out);
|
||||||
|
|
||||||
size_t stack_len(Stack const* s);
|
size_t stack_size(Stack const* s);
|
||||||
|
|
||||||
TYC_RESULT stack_at(Stack const* s, int32_t key, VALUE* v);
|
TYC_RESULT stack_at(Stack const* s, int32_t key, VALUE* v);
|
||||||
TYC_RESULT stack_set(Stack* s, int32_t key, VALUE v);
|
TYC_RESULT stack_set(Stack* s, int32_t key, VALUE v);
|
||||||
@@ -224,8 +224,12 @@ T_REAL code_const_real(Code const* code, size_t n);
|
|||||||
const char* code_const_string(Code const* code, size_t n);
|
const char* code_const_string(Code const* code, size_t n);
|
||||||
|
|
||||||
uint32_t code_n_functions(Code const* code);
|
uint32_t code_n_functions(Code const* code);
|
||||||
|
uint32_t code_function_sz(Code const* code, uint32_t f_id);
|
||||||
Instruction code_next_instruction(Code const* code, uint32_t function_id, uint32_t pc);
|
Instruction code_next_instruction(Code const* code, uint32_t function_id, uint32_t pc);
|
||||||
|
|
||||||
|
void code_decompile(Code const* code);
|
||||||
|
void code_parse_instruction(Instruction inst, char* outbuf, size_t sz);
|
||||||
|
|
||||||
//
|
//
|
||||||
// EXPRESSIONS
|
// EXPRESSIONS
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ TYC_RESULT stack_pop(Stack* s, VALUE* v_out)
|
|||||||
return T_OK;
|
return T_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t stack_len(Stack const* s)
|
size_t stack_size(Stack const* s)
|
||||||
{
|
{
|
||||||
return s->stack_n - stack_top_fp(s);
|
return s->stack_n - stack_top_fp(s);
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ size_t stack_fp_level(Stack const* s)
|
|||||||
size_t stack_collectable_array(Stack const* s, VALUE** values)
|
size_t stack_collectable_array(Stack const* s, VALUE** values)
|
||||||
{
|
{
|
||||||
size_t j = 0;
|
size_t j = 0;
|
||||||
*values = xmalloc(stack_len(s) * sizeof(VALUE));
|
*values = xmalloc(stack_size(s) * sizeof(VALUE));
|
||||||
|
|
||||||
for (size_t i = 0; i < s->stack_n; ++i)
|
for (size_t i = 0; i < s->stack_n; ++i)
|
||||||
if (type_is_collectable(s->stack[i].type))
|
if (type_is_collectable(s->stack[i].type))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#ifndef TYCHE_TYCHE_H
|
#ifndef TYCHE_TYCHE_H
|
||||||
#define TYCHE_TYCHE_H
|
#define TYCHE_TYCHE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
@@ -33,6 +34,10 @@ typedef struct TycheVM TycheVM;
|
|||||||
TycheVM* tyc_new(void);
|
TycheVM* tyc_new(void);
|
||||||
void tyc_destroy(TycheVM* t);
|
void tyc_destroy(TycheVM* t);
|
||||||
|
|
||||||
|
// debugging (DEBUG_ASSEMBLY needs to be setup in compilation options)
|
||||||
|
void tyc_debug_to_console(TycheVM* T, bool activate);
|
||||||
|
void tyc_assembly_decompile(TycheVM* T);
|
||||||
|
|
||||||
// code loading and execution
|
// code loading and execution
|
||||||
TYC_RESULT tyc_load_bytecode(TycheVM* T, uint8_t const* bytecode, size_t bytecode_sz);
|
TYC_RESULT tyc_load_bytecode(TycheVM* T, uint8_t const* bytecode, size_t bytecode_sz);
|
||||||
TYC_RESULT tyc_call(TycheVM* t, uint16_t n_pars);
|
TYC_RESULT tyc_call(TycheVM* t, uint16_t n_pars);
|
||||||
|
|||||||
135
lib/vm.c
135
lib/vm.c
@@ -1,6 +1,7 @@
|
|||||||
#include "priv.h"
|
#include "priv.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
typedef struct Location {
|
typedef struct Location {
|
||||||
uint32_t function_id;
|
uint32_t function_id;
|
||||||
@@ -18,6 +19,7 @@ struct TycheVM {
|
|||||||
Heap* heap;
|
Heap* heap;
|
||||||
Code* code;
|
Code* code;
|
||||||
LocationStack location_stack;
|
LocationStack location_stack;
|
||||||
|
bool debug;
|
||||||
};
|
};
|
||||||
|
|
||||||
static TYC_RESULT step(TycheVM* T);
|
static TYC_RESULT step(TycheVM* T);
|
||||||
@@ -39,6 +41,7 @@ TycheVM* tyc_new(void)
|
|||||||
.cap = 4,
|
.cap = 4,
|
||||||
.sz = 0,
|
.sz = 0,
|
||||||
};
|
};
|
||||||
|
t->debug = false;
|
||||||
|
|
||||||
expr_init();
|
expr_init();
|
||||||
|
|
||||||
@@ -54,6 +57,95 @@ void tyc_destroy(TycheVM* t)
|
|||||||
free(t);
|
free(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// DEBUGGING
|
||||||
|
//
|
||||||
|
|
||||||
|
void tyc_debug_to_console(TycheVM* T, bool activate)
|
||||||
|
{
|
||||||
|
T->debug = activate;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ASSEMBLY
|
||||||
|
|
||||||
|
static void debug_instruction(TycheVM* T, Location* loc, Instruction inst)
|
||||||
|
{
|
||||||
|
if (!T->debug)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char buf[50];
|
||||||
|
code_parse_instruction(inst, buf, sizeof(buf));
|
||||||
|
printf(": %02d-%04d %s ", loc->function_id, loc->pc, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void debug_value(TycheVM* T, VALUE a)
|
||||||
|
{
|
||||||
|
switch (value_type(a)) {
|
||||||
|
case TT_NIL:
|
||||||
|
printf("[nil]");
|
||||||
|
break;
|
||||||
|
case TT_INTEGER:
|
||||||
|
printf("[%d]", value_integer(a));
|
||||||
|
break;
|
||||||
|
case TT_REAL:
|
||||||
|
printf("[%f]", (double) value_real(a));
|
||||||
|
break;
|
||||||
|
case TT_STRING: {
|
||||||
|
const char* str;
|
||||||
|
if (heap_get_string(T->heap, value_idx(a), &str) == T_OK)
|
||||||
|
printf("[\"%s\"]", str);
|
||||||
|
else
|
||||||
|
printf("[\"(not found)\"]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TT_STRING_CONST: {
|
||||||
|
if (code_const_type(T->code, value_idx(a)) != TC_STRING)
|
||||||
|
printf("[\"(const not a string)\"]");
|
||||||
|
else
|
||||||
|
printf("[\"%s\"]", code_const_string(T->code, value_idx(a)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TT_ARRAY:
|
||||||
|
printf("[(not implemented)]\n");
|
||||||
|
abort();
|
||||||
|
case TT_TABLE:
|
||||||
|
printf("[(not implemented )]\n");
|
||||||
|
abort();
|
||||||
|
case TT_FUNCTION:
|
||||||
|
printf("[func %d]", value_idx(a));
|
||||||
|
break;
|
||||||
|
case TT_NATIVE_PTR:
|
||||||
|
printf("[ptr %p]", (void *) (intptr_t) value_idx(a));
|
||||||
|
break;
|
||||||
|
case TT_COUNT__:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void debug_stack(TycheVM* T)
|
||||||
|
{
|
||||||
|
if (!T->debug)
|
||||||
|
return;
|
||||||
|
if (stack_size(T->stack) == 0) {
|
||||||
|
printf("|empty|\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < stack_size(T->stack); ++i) {
|
||||||
|
VALUE a;
|
||||||
|
stack_at(T->stack, (int32_t) i, &a);
|
||||||
|
debug_value(T, a);
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tyc_assembly_decompile(TycheVM* T)
|
||||||
|
{
|
||||||
|
code_decompile(T->code);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// LOCATION STACK
|
// LOCATION STACK
|
||||||
//
|
//
|
||||||
@@ -152,7 +244,7 @@ TYC_RESULT tyc_call(TycheVM* T, uint16_t n_pars)
|
|||||||
|
|
||||||
size_t tyc_stack_size(TycheVM* T)
|
size_t tyc_stack_size(TycheVM* T)
|
||||||
{
|
{
|
||||||
return stack_len(T->stack);
|
return stack_size(T->stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tyc_pushnil(TycheVM* T)
|
void tyc_pushnil(TycheVM* T)
|
||||||
@@ -211,7 +303,9 @@ static TYC_RESULT step(TycheVM* T)
|
|||||||
Location* loc = location_top(T);
|
Location* loc = location_top(T);
|
||||||
Instruction inst = code_next_instruction(T->code, loc->function_id, loc->pc);
|
Instruction inst = code_next_instruction(T->code, loc->function_id, loc->pc);
|
||||||
|
|
||||||
// TODO - debug instruction
|
#ifdef DEBUG_ASSEMBLY
|
||||||
|
debug_instruction(T, loc, inst);
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (inst.operator) {
|
switch (inst.operator) {
|
||||||
|
|
||||||
@@ -299,8 +393,37 @@ static TYC_RESULT step(TycheVM* T)
|
|||||||
TRY(stack_pop_fp(T->stack))
|
TRY(stack_pop_fp(T->stack))
|
||||||
TRY(stack_push(T->stack, a))
|
TRY(stack_push(T->stack, a))
|
||||||
location_pop(T);
|
location_pop(T);
|
||||||
// TODO - print stack
|
goto dont_update_pc;
|
||||||
return T_OK;
|
|
||||||
|
//
|
||||||
|
// jumps/branching
|
||||||
|
//
|
||||||
|
|
||||||
|
case TO_JMP:
|
||||||
|
if (inst.operand < 0)
|
||||||
|
return T_ERR_VALUE_OUT_OF_RANGE; // TODO - also check function size
|
||||||
|
loc->pc = (uint32_t) inst.operand;
|
||||||
|
goto dont_update_pc;
|
||||||
|
|
||||||
|
case TO_BZ:
|
||||||
|
if (inst.operand < 0)
|
||||||
|
return T_ERR_VALUE_OUT_OF_RANGE; // TODO - also check function size
|
||||||
|
TRY(stack_pop(T->stack, &a))
|
||||||
|
if (value_is_zero(a)) {
|
||||||
|
loc->pc = (uint32_t) inst.operand;
|
||||||
|
goto dont_update_pc;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TO_BNZ:
|
||||||
|
if (inst.operand < 0)
|
||||||
|
return T_ERR_VALUE_OUT_OF_RANGE; // TODO - also check function size
|
||||||
|
TRY(stack_pop(T->stack, &a))
|
||||||
|
if (!value_is_zero(a)) {
|
||||||
|
loc->pc = (uint32_t) inst.operand;
|
||||||
|
goto dont_update_pc;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return T_ERR_INVALID_OPCODE;
|
return T_ERR_INVALID_OPCODE;
|
||||||
@@ -309,5 +432,9 @@ static TYC_RESULT step(TycheVM* T)
|
|||||||
// TODO - print stack
|
// TODO - print stack
|
||||||
loc->pc += inst.sz;
|
loc->pc += inst.sz;
|
||||||
|
|
||||||
|
dont_update_pc:
|
||||||
|
#ifdef DEBUG_ASSEMBLY
|
||||||
|
debug_stack(T);
|
||||||
|
#endif
|
||||||
return T_OK;
|
return T_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,4 +75,26 @@ return {
|
|||||||
expected_stack_size = 1,
|
expected_stack_size = 1,
|
||||||
expected_stack_top = -1,
|
expected_stack_top = -1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name = "VM: jumps (jmp + bnz)",
|
||||||
|
code = [[
|
||||||
|
.func 0
|
||||||
|
jmp @x1 ; 0
|
||||||
|
pushi 5 ; 3
|
||||||
|
@x1:
|
||||||
|
pushi 1 ; 5
|
||||||
|
bnz @x2 ; 7
|
||||||
|
pushi 1 ; 10
|
||||||
|
bz @x3 ; 12
|
||||||
|
@x2:
|
||||||
|
pushi 6 ; 15
|
||||||
|
ret ; 17
|
||||||
|
@x3:
|
||||||
|
pushi 7 ; 18
|
||||||
|
ret ; 20
|
||||||
|
]],
|
||||||
|
decompile = true,
|
||||||
|
debug = true,
|
||||||
|
expected_stack_top = 6,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
40
test/tests.c
40
test/tests.c
@@ -14,8 +14,8 @@
|
|||||||
|
|
||||||
static void run_assembly_tests(void);
|
static void run_assembly_tests(void);
|
||||||
static void run_assembly_test(lua_State* L);
|
static void run_assembly_test(lua_State* L);
|
||||||
static void run_assembly_test_code(lua_State* L);
|
static void run_assembly_test_code(lua_State* L, bool debug, bool decompile);
|
||||||
static void run_assembly_test_template(lua_State* L);
|
static void run_assembly_test_template(lua_State* L, bool debug, bool decompile);
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
@@ -37,7 +37,7 @@ int main(void)
|
|||||||
stack_push(s, create_value_integer(30));
|
stack_push(s, create_value_integer(30));
|
||||||
|
|
||||||
VALUE v;
|
VALUE v;
|
||||||
assert(stack_len(s) == 3);
|
assert(stack_size(s) == 3);
|
||||||
assert(stack_at(s, 0, &v) == T_OK); assert(value_integer(v) == 10);
|
assert(stack_at(s, 0, &v) == T_OK); assert(value_integer(v) == 10);
|
||||||
assert(stack_at(s, 1, &v) == T_OK); assert(value_integer(v) == 20);
|
assert(stack_at(s, 1, &v) == T_OK); assert(value_integer(v) == 20);
|
||||||
assert(stack_at(s, -1, &v) == T_OK); assert(value_integer(v) == 30);
|
assert(stack_at(s, -1, &v) == T_OK); assert(value_integer(v) == 30);
|
||||||
@@ -54,7 +54,7 @@ int main(void)
|
|||||||
assert(stack_pop(s, NULL) == T_OK);
|
assert(stack_pop(s, NULL) == T_OK);
|
||||||
assert(stack_at(s, -1, &v) == T_OK); assert(value_integer(v) == 10);
|
assert(stack_at(s, -1, &v) == T_OK); assert(value_integer(v) == 10);
|
||||||
assert(stack_pop(s, NULL) == T_OK);
|
assert(stack_pop(s, NULL) == T_OK);
|
||||||
assert(stack_len(s) == 0);
|
assert(stack_size(s) == 0);
|
||||||
|
|
||||||
assert(stack_pop(s, NULL) == T_ERR_STACK_UNDERFLOW);
|
assert(stack_pop(s, NULL) == T_ERR_STACK_UNDERFLOW);
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ int main(void)
|
|||||||
stack_push(s, create_value_integer(50));
|
stack_push(s, create_value_integer(50));
|
||||||
|
|
||||||
VALUE v;
|
VALUE v;
|
||||||
assert(stack_len(s) == 3);
|
assert(stack_size(s) == 3);
|
||||||
assert(stack_at(s, 0, &v) == T_OK); assert(value_integer(v) == 30);
|
assert(stack_at(s, 0, &v) == T_OK); assert(value_integer(v) == 30);
|
||||||
assert(stack_at(s, 1, &v) == T_OK); assert(value_integer(v) == 40);
|
assert(stack_at(s, 1, &v) == T_OK); assert(value_integer(v) == 40);
|
||||||
assert(stack_at(s, -1, &v) == T_OK); assert(value_integer(v) == 50);
|
assert(stack_at(s, -1, &v) == T_OK); assert(value_integer(v) == 50);
|
||||||
@@ -88,7 +88,7 @@ int main(void)
|
|||||||
|
|
||||||
stack_pop_fp(s);
|
stack_pop_fp(s);
|
||||||
|
|
||||||
assert(stack_len(s) == 2);
|
assert(stack_size(s) == 2);
|
||||||
assert(stack_at(s, 0, &v) == T_OK); assert(value_integer(v) == 10);
|
assert(stack_at(s, 0, &v) == T_OK); assert(value_integer(v) == 10);
|
||||||
assert(stack_at(s, 1, &v) == T_OK); assert(value_integer(v) == 20);
|
assert(stack_at(s, 1, &v) == T_OK); assert(value_integer(v) == 20);
|
||||||
assert(stack_at(s, -1, &v) == T_OK); assert(value_integer(v) == 20);
|
assert(stack_at(s, -1, &v) == T_OK); assert(value_integer(v) == 20);
|
||||||
@@ -252,6 +252,8 @@ int main(void)
|
|||||||
assert(code_const_real(code, 0) > 3.13f && code_const_real(code, 0) < 3.15f);
|
assert(code_const_real(code, 0) > 3.13f && code_const_real(code, 0) < 3.15f);
|
||||||
assert(strcmp(code_const_string(code, 1), "Hello world") == 0);
|
assert(strcmp(code_const_string(code, 1), "Hello world") == 0);
|
||||||
assert(code_n_functions(code) == 2);
|
assert(code_n_functions(code) == 2);
|
||||||
|
assert(code_function_sz(code, 0) == 6);
|
||||||
|
assert(code_function_sz(code, 1) == 4);
|
||||||
|
|
||||||
uint32_t addr = 0;
|
uint32_t addr = 0;
|
||||||
Instruction inst = code_next_instruction(code, 0, addr);
|
Instruction inst = code_next_instruction(code, 0, addr);
|
||||||
@@ -296,7 +298,7 @@ int main(void)
|
|||||||
|
|
||||||
Instruction inst = code_next_instruction(code, 0, 0);
|
Instruction inst = code_next_instruction(code, 0, 0);
|
||||||
assert(inst.operator == TO_JMP);
|
assert(inst.operator == TO_JMP);
|
||||||
assert(inst.operand == 3);
|
assert(inst.operand == 4);
|
||||||
assert(inst.sz == 3);
|
assert(inst.sz == 3);
|
||||||
|
|
||||||
code_destroy(code);
|
code_destroy(code);
|
||||||
@@ -350,11 +352,21 @@ static void run_assembly_test(lua_State* L)
|
|||||||
printf(" - %s\n", lua_tostring(L, -1));
|
printf(" - %s\n", lua_tostring(L, -1));
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// debug?
|
||||||
|
lua_getfield(L, -1, "debug");
|
||||||
|
bool debug = lua_isboolean(L, -1) && lua_toboolean(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
// decompile
|
||||||
|
lua_getfield(L, -1, "decompile");
|
||||||
|
bool decompile = lua_isboolean(L, -1) && lua_toboolean(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
// has code?
|
// has code?
|
||||||
lua_getfield(L, -1, "code");
|
lua_getfield(L, -1, "code");
|
||||||
if (!lua_isnil(L, -1)) {
|
if (!lua_isnil(L, -1)) {
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
run_assembly_test_code(L);
|
run_assembly_test_code(L, debug, decompile);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
@@ -364,15 +376,16 @@ static void run_assembly_test(lua_State* L)
|
|||||||
lua_getfield(L, -1, "template");
|
lua_getfield(L, -1, "template");
|
||||||
if (!lua_isnil(L, -1)) {
|
if (!lua_isnil(L, -1)) {
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
run_assembly_test_template(L);
|
run_assembly_test_template(L, debug, decompile);
|
||||||
} else {
|
} else {
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run_assembly_test_code(lua_State* L)
|
static void run_assembly_test_code(lua_State* L, bool debug, bool decompile)
|
||||||
{
|
{
|
||||||
TycheVM* T = tyc_new();
|
TycheVM* T = tyc_new();
|
||||||
|
tyc_debug_to_console(T, debug);
|
||||||
|
|
||||||
// load code
|
// load code
|
||||||
uint8_t* bytecode; size_t bytecode_sz;
|
uint8_t* bytecode; size_t bytecode_sz;
|
||||||
@@ -382,6 +395,8 @@ static void run_assembly_test_code(lua_State* L)
|
|||||||
|
|
||||||
// run code
|
// run code
|
||||||
assert(tyc_load_bytecode(T, bytecode, bytecode_sz) == T_OK);
|
assert(tyc_load_bytecode(T, bytecode, bytecode_sz) == T_OK);
|
||||||
|
if (decompile)
|
||||||
|
tyc_assembly_decompile(T);
|
||||||
assert(tyc_call(T, 0) == T_OK);
|
assert(tyc_call(T, 0) == T_OK);
|
||||||
|
|
||||||
// check stack size
|
// check stack size
|
||||||
@@ -405,7 +420,7 @@ static void run_assembly_test_code(lua_State* L)
|
|||||||
tyc_destroy(T);
|
tyc_destroy(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run_assembly_test_template(lua_State* L)
|
static void run_assembly_test_template(lua_State* L, bool debug, bool decompile)
|
||||||
{
|
{
|
||||||
lua_getfield(L, -1, "template");
|
lua_getfield(L, -1, "template");
|
||||||
char* template = strdup(lua_tostring(L, -1));
|
char* template = strdup(lua_tostring(L, -1));
|
||||||
@@ -440,9 +455,12 @@ static void run_assembly_test_template(lua_State* L)
|
|||||||
|
|
||||||
// run code
|
// run code
|
||||||
TycheVM* T = tyc_new();
|
TycheVM* T = tyc_new();
|
||||||
|
tyc_debug_to_console(T, debug);
|
||||||
uint8_t* bytecode; size_t bytecode_sz;
|
uint8_t* bytecode; size_t bytecode_sz;
|
||||||
assert(code_assemble(formatted_code, &bytecode, &bytecode_sz) == T_OK);
|
assert(code_assemble(formatted_code, &bytecode, &bytecode_sz) == T_OK);
|
||||||
assert(tyc_load_bytecode(T, bytecode, bytecode_sz) == T_OK);
|
assert(tyc_load_bytecode(T, bytecode, bytecode_sz) == T_OK);
|
||||||
|
if (decompile)
|
||||||
|
tyc_assembly_decompile(T);
|
||||||
assert(tyc_call(T, 0) == T_OK);
|
assert(tyc_call(T, 0) == T_OK);
|
||||||
|
|
||||||
// check stack top
|
// check stack top
|
||||||
|
|||||||
Reference in New Issue
Block a user