Skip to content
Browse files

Implement all the instructions

  • Loading branch information...
1 parent 15e9f25 commit 49f9eee67bf232619457e841d05235f1a3d4f132 @txus committed
View
9 src/forkix/darray.c
@@ -113,3 +113,12 @@ void *DArray_pop(DArray *array)
error:
return NULL;
}
+
+void DArray_set(DArray *array, int i, void *el)
+{
+ check(i < array->max, "darray attempt to set past max");
+ array->contents[i] = el;
+error:
+ return;
+}
+
View
8 src/forkix/darray.h
@@ -31,13 +31,7 @@ void DArray_clear_destroy(DArray *array);
#define DEFAULT_EXPAND_RATE 300
-static inline void DArray_set(DArray *array, int i, void *el)
-{
- check(i < array->max, "darray attempt to set past max");
- array->contents[i] = el;
-error:
- return;
-}
+void DArray_set(DArray *array, int i, void *el);
static inline void *DArray_get(DArray *array, int i)
{
View
15 src/forkix/opcode.h
@@ -5,11 +5,22 @@ typedef enum {
NOOP = 0,
PUSHSELF = 0x10, // 16
- PUSHINT, // 17
+ PUSH, // 17
+ PUSHTRUE, // 18
+ PUSHFALSE, // 19
+ PUSHNIL, // 20
PUSHLOCAL = 0x20, // 32
+ SETLOCAL, // 33
- ADD, // 33
+ ADD, // 34
+
+ JMP = 0x30, // 48
+ JIF, // 49
+ JIT, // 50
+
+ GETSLOT = 0x40, // 64
+ SETSLOT, // 65
POP,
View
23 src/forkix/runtime.c
@@ -0,0 +1,23 @@
+#include <forkix/runtime.h>
+
+VALUE TrueObject = NULL;
+VALUE FalseObject = NULL;
+VALUE NilObject = NULL;
+
+void Runtime_init() {
+ // Init extern constants
+ TrueObject = Value_new(TrueType);
+ FalseObject = Value_new(FalseType);
+ NilObject = Value_new(NilType);
+}
+
+void Runtime_destroy() {
+ Value_destroy(TrueObject);
+ TrueObject = NULL;
+
+ Value_destroy(FalseObject);
+ FalseObject = NULL;
+
+ Value_destroy(NilObject);
+ NilObject = NULL;
+}
View
13 src/forkix/runtime.h
@@ -0,0 +1,13 @@
+#ifndef _fx_runtime_h_
+#define _fx_runtime_h
+
+#include <forkix/value.h>
+
+extern VALUE TrueObject;
+extern VALUE FalseObject;
+extern VALUE NilObject;
+
+void Runtime_init();
+void Runtime_destroy();
+
+#endif
View
21 src/forkix/value.c
@@ -25,6 +25,26 @@ Value_print(VALUE o)
printf("#<Integer %p @value=%i>\n", o, VAL2INT(o));
break;
}
+ case StringType: {
+ printf("#<String %p @value=%s>\n", o, VAL2STR(o));
+ break;
+ }
+ case TrueType: {
+ printf("#<True %p>\n", o);
+ break;
+ }
+ case FalseType: {
+ printf("#<False %p>\n", o);
+ break;
+ }
+ case NilType: {
+ printf("#<Nil %p>\n", o);
+ break;
+ }
+ case MainType: {
+ printf("#<Main %p>\n", o);
+ break;
+ }
default: {
printf("#<Object %p>\n", o);
break;
@@ -47,4 +67,3 @@ String_new(char* value)
val->data.as_str = value;
return val;
}
-
View
8 src/forkix/value.h
@@ -29,7 +29,13 @@ VALUE Integer_new(int);
VALUE String_new(char*);
#define VAL2STR(o) (o->data.as_str)
-#define VALSET(S, K, V) Hashmap_set((S)->table, bfromcstr((K)), (V));
+#define VALSET(S, K, V) \
+ { \
+ bstring _slotname = bfromcstr((K)); \
+ Hashmap_delete((S)->table, _slotname); \
+ Hashmap_set((S)->table, _slotname, (V)); \
+ }
+
#define VALGET(S, K) (VALUE)Hashmap_get((S)->table, bfromcstr((K)))
#endif
View
105 src/forkix/vm.c
@@ -5,10 +5,16 @@
#include <forkix/vm.h>
#include <forkix/opcode.h>
#include <forkix/state.h>
+#include <forkix/runtime.h>
#include <forkix/bootstrap.h>
#include <forkix/bstrlib.h>
#include <forkix/function.h>
+// Extern global objects declared in runtime.h
+VALUE TrueObject;
+VALUE FalseObject;
+VALUE NilObject;
+
static inline void dump(Stack* stack)
{
#ifndef NDEBUG
@@ -27,6 +33,7 @@ static inline void dump(Stack* stack)
#define STATE_FN(A) (Function*)Hashmap_get(state->functions, bfromcstr((A)))
#define LITERAL(A) (VALUE)DArray_at(current_frame->fn->literals, (A))
#define LOCAL(A) (VALUE)DArray_at(current_frame->locals, (A))
+#define LOCALSET(A, B) DArray_set(current_frame->locals, (A), (B))
void VM_start(BytecodeFile *file)
{
@@ -34,15 +41,18 @@ void VM_start(BytecodeFile *file)
VALUE main = Value_new(MainType); // toplevel object
+ Runtime_init();
State_bootstrap(state);
Stack *frames = Stack_create();
CallFrame *top_frame = CallFrame_new(main, STATE_FN("main"), NULL);
Stack_push(frames, top_frame);
VM_run(state, frames);
+
+ Runtime_destroy();
}
-void VM_run(STATE state, Stack *frames)
+VALUE VM_run(STATE state, Stack *frames)
{
Stack *stack = Stack_create();
@@ -51,13 +61,91 @@ void VM_run(STATE state, Stack *frames)
while(1) {
switch(*ip) {
- case PUSHINT: {
+ case NOOP:
+ break;
+ case PUSH: {
ip++;
- debug("PUSHINT %i", *ip);
+ debug("PUSH %i", *ip);
VALUE value = LITERAL(*ip);
Stack_push(stack, value);
break;
}
+ case PUSHTRUE: {
+ debug("PUSHTRUE");
+ Stack_push(stack, TrueObject);
+ break;
+ }
+ case PUSHFALSE: {
+ debug("PUSHFALSE");
+ Stack_push(stack, FalseObject);
+ break;
+ }
+ case PUSHNIL: {
+ debug("PUSHNIL");
+ Stack_push(stack, NilObject);
+ break;
+ }
+ case JMP: {
+ ip++;
+ int jump = *ip;
+ debug("JMP %i", jump);
+ for(int i=1; i < jump; i++) {
+ ip++;
+ }
+ break;
+ }
+ case JIF: {
+ ip++;
+ int jump = *ip;
+ debug("JIF %i", jump);
+
+ VALUE value = Stack_peek(stack);
+ if (value == FalseObject || value == NilObject) {
+ for(int i=1; i < jump; i++) {
+ ip++;
+ }
+ }
+
+ break;
+ }
+ case JIT: {
+ ip++;
+ int jump = *ip;
+ debug("JIT %i", jump);
+
+ VALUE value = Stack_peek(stack);
+ if (value != FalseObject && value != NilObject) {
+ for(int i=1; i < jump; i++) {
+ ip++;
+ }
+ }
+
+ break;
+ }
+ case GETSLOT: {
+ ip++;
+ debug("GETSLOT %i", *ip);
+ VALUE receiver = Stack_pop(stack);
+ VALUE slot = LITERAL(*ip);
+
+ check(slot->type == StringType, "Slot name must be a String.");
+
+ Stack_push(stack, VALGET(receiver, VAL2STR(slot)));
+ break;
+ }
+ case SETSLOT: {
+ ip++;
+ debug("SETSLOT %i", *ip);
+ VALUE value = Stack_pop(stack);
+ VALUE receiver = Stack_pop(stack);
+ VALUE slot = LITERAL(*ip);
+
+ check(slot->type == StringType, "Slot name must be a String.");
+
+ VALSET(receiver, VAL2STR(slot), value);
+ Stack_push(stack, value); // push the rhs back to the stack
+ break;
+ }
case ADD: {
debug("ADD");
VALUE op1 = Stack_pop(stack);
@@ -105,6 +193,12 @@ void VM_run(STATE state, Stack *frames)
debug("PUSHLOCAL %i", *ip);
break;
}
+ case SETLOCAL: {
+ ip++;
+ debug("SETLOCAL %i", *ip);
+ LOCALSET(*ip, Stack_peek(stack));
+ break;
+ }
case POP: {
debug("POP");
Stack_pop(stack);
@@ -115,7 +209,7 @@ void VM_run(STATE state, Stack *frames)
CallFrame *old_frame = Stack_pop(frames);
ip = old_frame->ret;
- if (ip == NULL) return; // if there's nowhere to return, exit
+ if (ip == NULL) return Stack_pop(stack); // if there's nowhere to return, exit
current_frame = (CallFrame*)(Stack_peek(frames));
break;
@@ -128,4 +222,7 @@ void VM_run(STATE state, Stack *frames)
}
ip++;
}
+error:
+ debug("Aborted.");
+ return NULL;
}
View
3 src/forkix/vm.h
@@ -3,9 +3,10 @@
#include <forkix/stack.h>
#include <forkix/state.h>
+#include <forkix/value.h>
#include <forkix/input_reader.h>
-void VM_run(STATE state, Stack *frames);
+VALUE VM_run(STATE state, Stack *frames);
void VM_start(BytecodeFile *file);
#endif
View
2 tests/input_reader_tests.c
@@ -11,7 +11,7 @@ char *test_load()
Function *main = Hashmap_get(file->functions, bfromcstr("main"));
Function *add = Hashmap_get(file->functions, bfromcstr("add"));
- mu_assert(*main->code == PUSHINT, "error parsing main");
+ mu_assert(*main->code == PUSH, "error parsing main");
VALUE first_lit = DArray_first(main->literals);
mu_assert(strcmp(VAL2STR(first_lit), "add") == 0, "error parsing literal in main");
mu_assert(*add->code == PUSHSELF, "error parsing add");
View
36 tests/runtime_tests.c
@@ -0,0 +1,36 @@
+#include "minunit.h"
+#include <forkix/runtime.h>
+#include <assert.h>
+
+char *test_init()
+{
+ Runtime_init();
+
+ mu_assert(TrueObject->type == TrueType, "TrueObject didn't initialize");
+ mu_assert(FalseObject->type == FalseType, "FalseObject didn't initialize");
+ mu_assert(NilObject->type == NilType, "NilObject didn't initialize");
+
+ return NULL;
+}
+
+char *test_destroy()
+{
+ Runtime_destroy();
+
+ mu_assert(TrueObject == NULL, "TrueObject wasn't destroyed with the runtime");
+ mu_assert(FalseObject == NULL, "FalseObject wasn't destroyed with the runtime");
+ mu_assert(NilObject == NULL, "NilObject wasn't destroyed with the runtime");
+
+ return NULL;
+}
+
+char *all_tests() {
+ mu_suite_start();
+
+ mu_run_test(test_init);
+ mu_run_test(test_destroy);
+
+ return NULL;
+}
+
+RUN_TESTS(all_tests);
View
321 tests/vm_tests.c
@@ -0,0 +1,321 @@
+#include "minunit.h"
+#include <forkix/value.h>
+#include <forkix/opcode.h>
+#include <forkix/runtime.h>
+#include <forkix/call_frame.h>
+#include <forkix/darray.h>
+#include <forkix/state.h>
+#include <forkix/bootstrap.h>
+#include <forkix/vm.h>
+#include <assert.h>
+
+// Extern global objects declared in runtime.h
+VALUE TrueObject;
+VALUE FalseObject;
+VALUE NilObject;
+
+#define VM state
+#define STACK frames
+#define LOCAL(A) (VALUE)DArray_at(locals, (A))
+#define PUSH_LITERAL(A, N) VALUE (N) = (A); DArray_push(literals, (N))
+#define PUSH_LOCAL(A, N) VALUE (N) = (A); DArray_push(locals, (N))
+
+#define SETUP() \
+ DArray *literals = DArray_create(sizeof(VALUE), 10); \
+ Stack *frames = Stack_create(); \
+ Hashmap *fns = Hashmap_create(NULL, NULL); \
+ DArray *locals = DArray_create(sizeof(VALUE), 10); \
+ Runtime_init(); \
+
+#define TEARDOWN() \
+ Runtime_destroy(); \
+ return NULL; \
+
+#define DEFN(N, ...) \
+ Hashmap_set( \
+ fns, \
+ bfromcstr((N)), \
+ &(Function) { .code = (int[]){__VA_ARGS__} } \
+ )
+
+#define RUN(...) \
+ int code[] = (int[]){__VA_ARGS__}; \
+ \
+ Function *fn = calloc(1, sizeof(Function)); \
+ fn->code = code; \
+ fn->literals = literals; \
+ \
+ STATE state = State_new(fns); \
+ VALUE main = Value_new(MainType); \
+ State_bootstrap(state); \
+ CallFrame *top_frame = CallFrame_new(main, fn, NULL); \
+ top_frame->locals = locals; \
+ Stack_push(frames, top_frame); \
+ VALUE result = VM_run(state, frames); \
+
+char *test_pushself()
+{
+ SETUP();
+
+ RUN(
+ PUSHSELF,
+ RET
+ );
+
+ mu_assert(result == main, "Pushself failed.");
+
+ TEARDOWN();
+}
+
+char *test_push()
+{
+ SETUP();
+
+ PUSH_LITERAL(Integer_new(123), integer);
+ PUSH_LITERAL(String_new("foo"), string);
+
+ RUN(
+ PUSH, 0,
+ PUSH, 1,
+ POP,
+ RET
+ );
+
+ mu_assert(result == integer, "Push failed.");
+
+ TEARDOWN();
+}
+
+char *test_pushtrue()
+{
+ SETUP();
+
+ RUN(
+ PUSHTRUE,
+ RET
+ );
+
+ mu_assert(result == TrueObject, "Pushtrue failed.");
+
+ TEARDOWN();
+}
+
+char *test_pushfalse()
+{
+ SETUP();
+
+ RUN(
+ PUSHFALSE,
+ RET
+ );
+
+ mu_assert(result == FalseObject, "Pushfalse failed.");
+
+ TEARDOWN();
+}
+
+char *test_pushnil()
+{
+ SETUP();
+
+ RUN(
+ PUSHNIL,
+ RET
+ );
+
+ mu_assert(result == NilObject, "Pushnil failed.");
+
+ TEARDOWN();
+}
+
+char *test_pushlocal()
+{
+ SETUP();
+
+ PUSH_LOCAL(Integer_new(123), integer);
+
+ RUN(
+ PUSHLOCAL, 0,
+ RET
+ );
+
+ mu_assert(result == integer, "Pushlocal failed.");
+
+ TEARDOWN();
+}
+
+char *test_setlocal()
+{
+ SETUP();
+
+ RUN(
+ PUSHTRUE,
+ SETLOCAL, 0,
+ RET
+ );
+
+ mu_assert(result == TrueObject, "Setlocal didn't respect stack size.");
+ mu_assert(LOCAL(0) == TrueObject, "Setlocal didn't set the local.");
+
+ TEARDOWN();
+}
+
+char *test_jmp()
+{
+ SETUP();
+
+ RUN(
+ PUSHTRUE,
+ JMP, 2,
+ PUSHFALSE,
+ RET
+ );
+
+ mu_assert(result == TrueObject, "Jmp failed.");
+
+ TEARDOWN();
+}
+
+char *test_jif()
+{
+ SETUP();
+
+ RUN(
+ PUSHFALSE,
+ JIF, 2,
+ PUSHTRUE,
+ RET
+ );
+
+ mu_assert(result == FalseObject, "Jif failed.");
+
+ TEARDOWN();
+}
+
+char *test_jit()
+{
+ SETUP();
+
+ RUN(
+ PUSHTRUE,
+ JIT, 2,
+ PUSHFALSE,
+ RET
+ );
+
+ mu_assert(result == TrueObject, "Jit failed.");
+
+ TEARDOWN();
+}
+
+char *test_getslot()
+{
+ SETUP();
+
+ VALUE receiver = String_new("foo");
+ VALSET(receiver, "tainted", TrueObject);
+ DArray_push(literals, receiver);
+
+ PUSH_LITERAL(String_new("tainted"), slot);
+
+ RUN(
+ PUSH, 0, // receiver, the "foo" string
+ GETSLOT, 1,
+ RET
+ );
+
+ mu_assert(result == TrueObject, "Getslot failed.");
+
+ TEARDOWN();
+}
+
+char *test_setslot()
+{
+ SETUP();
+
+ VALUE receiver = String_new("foo");
+ VALSET(receiver, "tainted", TrueObject);
+ DArray_push(literals, receiver);
+
+ PUSH_LITERAL(String_new("tainted"), slot);
+
+ RUN(
+ PUSH, 0, // receiver, the "foo" string
+ PUSHFALSE,
+ SETSLOT, 1,
+ RET
+ );
+
+ mu_assert(result == FalseObject, "Setslot didn't push the rhs back to the stack.");
+ mu_assert(VALGET(receiver, "tainted") == FalseObject, "Setslot didn't set the slot.");
+
+ TEARDOWN();
+}
+
+char *test_pop()
+{
+ SETUP();
+
+ RUN(
+ PUSHTRUE,
+ PUSHFALSE,
+ POP,
+ RET
+ );
+
+ mu_assert(result == TrueObject, "Pop failed.");
+
+ TEARDOWN();
+}
+
+char *test_send()
+{
+ SETUP();
+
+ DEFN(
+ "add",
+ PUSHSELF,
+ PUSHLOCAL, 0,
+ ADD,
+ RET
+ );
+
+ PUSH_LITERAL(Integer_new(1), a);
+ PUSH_LITERAL(Integer_new(2), b);
+ PUSH_LITERAL(String_new("add"), method);
+
+ RUN(
+ PUSH, 0,
+ PUSH, 1,
+ SEND, 2, 1,
+ RET
+ );
+
+ mu_assert(VAL2INT(result) == 3, "Send failed.");
+
+ TEARDOWN();
+}
+
+char *all_tests() {
+ mu_suite_start();
+
+ // Instructions
+ mu_run_test(test_pushself);
+ mu_run_test(test_push);
+ mu_run_test(test_pushtrue);
+ mu_run_test(test_pushfalse);
+ mu_run_test(test_pushnil);
+ mu_run_test(test_pushlocal);
+ mu_run_test(test_setlocal);
+ mu_run_test(test_jmp);
+ mu_run_test(test_jif);
+ mu_run_test(test_jit);
+ mu_run_test(test_getslot);
+ mu_run_test(test_setslot);
+ mu_run_test(test_pop);
+ mu_run_test(test_send);
+
+ return NULL;
+}
+
+RUN_TESTS(all_tests);

0 comments on commit 49f9eee

Please sign in to comment.
Something went wrong with that request. Please try again.