Initial delegates implementation

This isn't very well tested yet, but it runs Rance 01 up to the first
HLL error.
This commit is contained in:
Nunuhara Cabbage
2021-10-18 10:45:10 -07:00
parent f53184d8f7
commit 5ff042738e
13 changed files with 298 additions and 45 deletions
+1
View File
@@ -62,6 +62,7 @@ bool page_index_valid(int index);
bool string_index_valid(int index);
struct page *heap_get_page(int index);
struct page *heap_get_delegate_page(int index);
struct string *heap_get_string(int index);
void heap_set_page(int slot, struct page *page);
void heap_string_assign(int slot, struct string *string);
+26 -4
View File
@@ -29,14 +29,15 @@ enum page_type {
GLOBAL_PAGE,
LOCAL_PAGE,
STRUCT_PAGE,
ARRAY_PAGE
ARRAY_PAGE,
DELEGATE_PAGE,
};
#define NR_PAGE_TYPES (ARRAY_PAGE+1)
/*
* A page is an ordered collection of variables. Pages are used to implement
* global and local variables, structures and arrays as follows,
* global and local variables, structures, arrays and delegates as follows:
*
* Global variables: all global variables are stored in a single global page.
*
@@ -48,15 +49,28 @@ enum page_type {
* Arrays: each array object is backed by a page storing its members.
* Multi-dimensional arrays are implemented as a tree of pages (meaning the
* whole array is NOT contiguous in memory).
*
* Delegates: each delegate object is backed by a page storing object/function
* pairs.
*/
struct page {
enum page_type type;
// Value of `index`:
// ----------------
// GLOBAL_PAGE: unused
// LOCAL_PAGE: function index
// STRUCT_PAGE: struct index
// ARRAY_PAGE: array data type (e.g. for AIN_ARRAY_STRING, AIN_STRING)
// DELEGATE_PAGE: unused
union {
int index;
enum ain_data_type a_type;
};
int struct_type; // for array pages
int rank; // for array pages
// array-specific metadata
struct {
int struct_type;
int rank;
} array;
int nr_vars;
union vm_value values[];
};
@@ -115,4 +129,12 @@ void array_sort(struct page *page, int compare_fno);
int array_find(struct page *page, int start, int end, union vm_value v, int compare_fno);
void array_reverse(struct page *page);
// delegates
struct page *delegate_new_from_method(int obj, int fun);
int delegate_numof(struct page *page);
struct page *delegate_plusa(struct page *dst, struct page *add);
struct page *delegate_minusa(struct page *dst, struct page *minus);
struct page *delegate_clear(struct page *page);
void delegate_get(struct page *page, int i, int *obj_out, int *fun_out);
#endif /* SYSTEM4_PAGE_H */
+8 -8
View File
@@ -162,16 +162,16 @@ sexp sexp_page_get_a_type (sexp ctx, sexp self, sexp_sint_t n, sexp x) {
return sexp_make_integer(ctx, ((struct page*)sexp_cpointer_value(x))->a_type);
}
sexp sexp_page_get_struct_type (sexp ctx, sexp self, sexp_sint_t n, sexp x) {
sexp sexp_page_get__page_struct_type (sexp ctx, sexp self, sexp_sint_t n, sexp x) {
if (! (sexp_pointerp(x) && (sexp_pointer_tag(x) == sexp_unbox_fixnum(sexp_opcode_arg1_type(self)))))
return sexp_type_exception(ctx, self, sexp_unbox_fixnum(sexp_opcode_arg1_type(self)), x);
return sexp_make_integer(ctx, ((struct page*)sexp_cpointer_value(x))->struct_type);
return sexp_make_integer(ctx, ((struct page*)sexp_cpointer_value(x))->_page_struct_type);
}
sexp sexp_page_get_rank (sexp ctx, sexp self, sexp_sint_t n, sexp x) {
sexp sexp_page_get__page_rank (sexp ctx, sexp self, sexp_sint_t n, sexp x) {
if (! (sexp_pointerp(x) && (sexp_pointer_tag(x) == sexp_unbox_fixnum(sexp_opcode_arg1_type(self)))))
return sexp_type_exception(ctx, self, sexp_unbox_fixnum(sexp_opcode_arg1_type(self)), x);
return sexp_make_integer(ctx, ((struct page*)sexp_cpointer_value(x))->rank);
return sexp_make_integer(ctx, ((struct page*)sexp_cpointer_value(x))->_page_rank);
}
@@ -290,8 +290,8 @@ sexp sexp_init_library (sexp ctx, sexp self, sexp_sint_t n, sexp env, const char
tmp = sexp_string_to_symbol(ctx, name);
sexp_env_define(ctx, env, tmp, sexp_page_type_obj);
sexp_type_slots(sexp_page_type_obj) = SEXP_NULL;
sexp_push(ctx, sexp_type_slots(sexp_page_type_obj), sexp_intern(ctx, "rank", -1));
sexp_push(ctx, sexp_type_slots(sexp_page_type_obj), sexp_intern(ctx, "struct_type", -1));
sexp_push(ctx, sexp_type_slots(sexp_page_type_obj), sexp_intern(ctx, "_page_rank", -1));
sexp_push(ctx, sexp_type_slots(sexp_page_type_obj), sexp_intern(ctx, "_page_struct_type", -1));
sexp_push(ctx, sexp_type_slots(sexp_page_type_obj), sexp_intern(ctx, "a_type", -1));
sexp_push(ctx, sexp_type_slots(sexp_page_type_obj), sexp_intern(ctx, "nr_vars", -1));
sexp_push(ctx, sexp_type_slots(sexp_page_type_obj), sexp_intern(ctx, "index", -1));
@@ -301,13 +301,13 @@ sexp sexp_init_library (sexp ctx, sexp self, sexp_sint_t n, sexp env, const char
tmp = sexp_make_type_predicate(ctx, name, sexp_page_type_obj);
name = sexp_intern(ctx, "page?", 5);
sexp_env_define(ctx, env, name, tmp);
op = sexp_define_foreign(ctx, env, "array-rank", 1, sexp_page_get_rank);
op = sexp_define_foreign(ctx, env, "array-rank", 1, sexp_page_get__page_rank);
if (sexp_opcodep(op)) {
sexp_opcode_return_type(op) = sexp_make_fixnum(SEXP_FIXNUM);
sexp_opcode_arg1_type(op) = sexp_make_fixnum(sexp_type_tag(sexp_page_type_obj));
}
if (sexp_vectorp(sexp_type_getters(sexp_page_type_obj))) sexp_vector_set(sexp_type_getters(sexp_page_type_obj), SEXP_FIVE, op);
op = sexp_define_foreign(ctx, env, "array-struct-type", 1, sexp_page_get_struct_type);
op = sexp_define_foreign(ctx, env, "array-struct-type", 1, sexp_page_get__page_struct_type);
if (sexp_opcodep(op)) {
sexp_opcode_return_type(op) = sexp_make_fixnum(SEXP_FIXNUM);
sexp_opcode_arg1_type(op) = sexp_make_fixnum(sexp_type_tag(sexp_page_type_obj));
+2 -2
View File
@@ -63,8 +63,8 @@
(int nr_vars page-nr-vars)
(int a_type array-data-type)
(int struct_type array-struct-type)
(int rank array-rank))
(int _page_struct_type array-struct-type)
(int _page_rank array-rank))
(define-c-union vm_value
predicate: vm-value?
+6
View File
@@ -111,6 +111,8 @@ static const char *variable_name(struct page *page, int varno)
return ain->structures[page->index].members[varno].name;
case ARRAY_PAGE:
return "array member";
case DELEGATE_PAGE:
return "delegate member";
}
return "unknown";
}
@@ -217,6 +219,10 @@ static struct page *frame_page(int i)
return get_page(call_stack[call_stack_ptr - (i+1)].page_slot);
}
// XXX: chibi-ffi doesn't support anonymous structs
#define _page_struct_type array.struct_type
#define _page_rank array.rank
// autogenerated with `chibi-ffi src/debug-ffi.stub`
#include "debug-ffi.c"
+1 -1
View File
@@ -356,7 +356,7 @@ static int find_walk_data(struct page *array, int map_no, struct dgn *dgn)
{
if (!array)
return WALK_DATA_BROKEN;
if (array->type != ARRAY_PAGE || array->a_type != AIN_ARRAY_INT || array->rank != 1)
if (array->type != ARRAY_PAGE || array->a_type != AIN_ARRAY_INT || array->array.rank != 1)
VM_ERROR("Not a flat integer array");
int ptr = 0;
+8
View File
@@ -222,6 +222,14 @@ struct string *heap_get_string(int index)
return heap[index].s;
}
struct page *heap_get_delegate_page(int index)
{
struct page *page = heap_get_page(index);
if (!page || page->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate page: %d", index);
return page;
}
void heap_set_page(int slot, struct page *page)
{
#ifdef DEBUG_HEAP
+2 -2
View File
@@ -99,7 +99,7 @@ static void Bitarray_GetArray(int bitSet, struct page **page)
return;
struct page *array = *page;
if (!array || array->type != ARRAY_PAGE || array->a_type != AIN_ARRAY_INT || array->rank != 1)
if (!array || array->type != ARRAY_PAGE || array->a_type != AIN_ARRAY_INT || array->array.rank != 1)
ERROR("Not an flat integer array");
int n = (arrays[bitSet].size + 31) / 32;
@@ -117,7 +117,7 @@ static void Bitarray_SetArray(int bitSet, struct page **page)
return;
struct page *array = *page;
if (!array || array->type != ARRAY_PAGE || array->a_type != AIN_ARRAY_INT || array->rank != 1)
if (!array || array->type != ARRAY_PAGE || array->a_type != AIN_ARRAY_INT || array->array.rank != 1)
ERROR("Not an flat integer array");
int n = (arrays[bitSet].size + 31) / 32;
+5 -5
View File
@@ -122,9 +122,9 @@ void iarray_write_array(struct iarray_writer *w, struct page *page)
assert(page->type == ARRAY_PAGE);
struct ain_type t = {
.data = page->rank > 1 ? page->a_type : array_type(page->a_type),
.struc = page->struct_type,
.rank = page->rank - 1
.data = page->array.rank > 1 ? page->a_type : array_type(page->a_type),
.struc = page->array.struct_type,
.rank = page->array.rank - 1
};
iarray_write(w, page->nr_vars);
@@ -230,8 +230,8 @@ struct page *iarray_read_array(struct iarray_reader *r, struct ain_type *t)
int nr_vars = iarray_read(r);
struct page *page = alloc_page(ARRAY_PAGE, t->data, nr_vars);
page->struct_type = t->struc;
page->rank = t->rank;
page->array.struct_type = t->struc;
page->array.rank = t->rank;
for (int i = 0; i < nr_vars; i++) {
page->values[i].i = iarray_read_member(r, &next_t);
+123 -17
View File
@@ -29,7 +29,8 @@ static const char *pagetype_strtab[] = {
[GLOBAL_PAGE] = "GLOBAL_PAGE",
[LOCAL_PAGE] = "LOCAL_PAGE",
[STRUCT_PAGE] = "STRUCT_PAGE",
[ARRAY_PAGE] = "ARRAY_PAGE"
[ARRAY_PAGE] = "ARRAY_PAGE",
[DELEGATE_PAGE] = "DELEGATE_PAGE",
};
const char *pagetype_string(enum page_type type)
@@ -168,10 +169,19 @@ enum ain_data_type variable_type(struct page *page, int varno, int *struct_type,
return ain->structures[page->index].members[varno].type.data;
case ARRAY_PAGE:
if (struct_type)
*struct_type = page->struct_type;
*struct_type = page->array.struct_type;
if (array_rank)
*array_rank = page->rank - 1;
return page->rank > 1 ? page->a_type : array_type(page->a_type);
*array_rank = page->array.rank - 1;
return page->array.rank > 1 ? page->a_type : array_type(page->a_type);
case DELEGATE_PAGE:
if (varno % 2 == 0) {
if (struct_type && page->values[varno].i >= 0)
*struct_type = heap_get_page(page->values[varno].i)->index;
if (array_rank)
*array_rank = -1;
return AIN_STRUCT;
}
return AIN_VOID;
}
return AIN_VOID;
}
@@ -207,8 +217,7 @@ struct page *copy_page(struct page *src)
if (!src)
return NULL;
struct page *dst = alloc_page(src->type, src->index, src->nr_vars);
dst->struct_type = src->struct_type;
dst->rank = src->rank;
dst->array = src->array;
for (int i = 0; i < src->nr_vars; i++) {
dst->values[i] = vm_copy(src->values[i], variable_type(src, i, NULL, NULL));
@@ -282,8 +291,8 @@ struct page *alloc_array(int rank, union vm_value *dimensions, enum ain_data_typ
data_type = unref_array_type(data_type);
enum ain_data_type type = array_type(data_type);
struct page *page = alloc_page(ARRAY_PAGE, data_type, dimensions->i);
page->struct_type = struct_type;
page->rank = rank;
page->array.struct_type = struct_type;
page->array.rank = rank;
for (int i = 0; i < dimensions->i; i++) {
if (rank == 1) {
@@ -311,7 +320,7 @@ struct page *realloc_array(struct page *src, int rank, union vm_value *dimension
return alloc_array(rank, dimensions, data_type, struct_type, init_structs);
if (src->type != ARRAY_PAGE)
VM_ERROR("Not an array");
if (src->rank != rank)
if (src->array.rank != rank)
VM_ERROR("Attempt to reallocate array with different rank");
if (!dimensions->i) {
delete_page_vars(src);
@@ -354,7 +363,7 @@ int array_numof(struct page *page, int rank)
{
if (!page)
return 0;
if (rank < 1 || rank > page->rank)
if (rank < 1 || rank > page->array.rank)
return 0;
if (rank == 1) {
return page->nr_vars;
@@ -379,7 +388,7 @@ void array_copy(struct page *dst, int dst_i, struct page *src, int src_i, int n)
VM_ERROR("Out of bounds array access");
if (!array_index_ok(dst, dst_i + n - 1) || !array_index_ok(src, src_i + n - 1))
VM_ERROR("Out of bounds array access");
if (dst->rank != 1 || src->rank != 1)
if (dst->array.rank != 1 || src->array.rank != 1)
VM_ERROR("Tried to copy to/from a multi-dimensional array");
if (dst->a_type != src->a_type)
VM_ERROR("Array types do not match");
@@ -420,12 +429,12 @@ struct page *array_pushback(struct page *dst, union vm_value v, enum ain_data_ty
if (dst) {
if (dst->type != ARRAY_PAGE)
VM_ERROR("Not an array");
if (dst->rank != 1)
if (dst->array.rank != 1)
VM_ERROR("Tried pushing to a multi-dimensional array");
int index = dst->nr_vars;
union vm_value dims[1] = { (union vm_value) { .i = index + 1 } };
dst = realloc_array(dst, 1, dims, dst->a_type, dst->struct_type, false);
dst = realloc_array(dst, 1, dims, dst->a_type, dst->array.struct_type, false);
variable_set(dst, index, array_type(data_type), v);
} else {
union vm_value dims[1] = { (union vm_value) { .i = 1 } };
@@ -441,11 +450,11 @@ struct page *array_popback(struct page *dst)
return NULL;
if (dst->type != ARRAY_PAGE)
VM_ERROR("Not an array");
if (dst->rank != 1)
if (dst->array.rank != 1)
VM_ERROR("Tried popping from a multi-dimensional array");
union vm_value dims[1] = { (union vm_value) { .i = dst->nr_vars - 1 } };
dst = realloc_array(dst, 1, dims, dst->a_type, dst->struct_type, false);
dst = realloc_array(dst, 1, dims, dst->a_type, dst->array.struct_type, false);
return dst;
}
@@ -456,7 +465,7 @@ struct page *array_erase(struct page *page, int i, bool *success)
return NULL;
if (page->type != ARRAY_PAGE)
VM_ERROR("Not an array");
if (page->rank != 1)
if (page->array.rank != 1)
VM_ERROR("Tried erasing from a multi-dimensional array");
if (!array_index_ok(page, i))
return page;
@@ -488,7 +497,7 @@ struct page *array_insert(struct page *page, int i, union vm_value v, enum ain_d
}
if (page->type != ARRAY_PAGE)
VM_ERROR("Not an array");
if (page->rank != 1)
if (page->array.rank != 1)
VM_ERROR("Tried inserting into a multi-dimensional array");
// NOTE: you cannot insert at the end of an array due to how i is clamped
@@ -576,3 +585,100 @@ void array_reverse(struct page *page)
page->values[end] = tmp;
}
}
struct page *delegate_new_from_method(int obj, int fun)
{
struct page *page = alloc_page(DELEGATE_PAGE, 0, 2);
page->values[0].i = obj;
page->values[1].i = fun;
if (obj >= 0) {
heap_ref(obj);
}
return page;
}
int delegate_numof(struct page *page)
{
if (page->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
return page->nr_vars / 2;
}
struct page *delegate_plusa(struct page *dst, struct page *add)
{
if (dst->type != DELEGATE_PAGE || add->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
// FIXME: duplicate checking needed?
int new_vars = dst->nr_vars + add->nr_vars;
dst = xrealloc(dst, sizeof(struct page) + sizeof(union vm_value) * new_vars);
for (int i = 0; i < add->nr_vars; i += 2) {
if (add->values[i].i >= 0) {
heap_ref(add->values[i].i);
}
dst->values[dst->nr_vars+i+0].i = add->values[i+0].i;
dst->values[dst->nr_vars+i+1].i = add->values[i+1].i;
}
dst->nr_vars = new_vars;
return dst;
}
struct page *delegate_minusa(struct page *dst, struct page *minus)
{
if (dst->type != DELEGATE_PAGE || minus->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
// set matching obj/fun pairs to -1/-1
int removed = 0;
for (int i = 0; i < minus->nr_vars; i += 2) {
for (int j = 0; j < dst->nr_vars; j += 2) {
if (minus->values[i].i == dst->values[j].i && minus->values[i+1].i == dst->values[j+1].i) {
if (dst->values[j].i >= 0)
heap_unref(dst->values[j].i);
dst->values[j].i = -1;
dst->values[j+1].i = -1;
removed++;
break;
}
}
}
// shift values to fill in gaps
for (int i = 0, p = 0; i < dst->nr_vars; i += 2) {
if (dst->values[i].i == -1 && dst->values[i+1].i == -1)
continue;
if (p < i) {
dst->values[p+0].i = dst->values[i+0].i;
dst->values[p+1].i = dst->values[i+1].i;
}
p += 2;
}
dst->nr_vars -= removed;
return dst;
}
struct page *delegate_clear(struct page *page)
{
if (page->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
for (int i = 0; i < page->nr_vars; i += 2) {
if (page->values[i].i >= 0)
heap_unref(page->values[i].i);
page->values[i].i = -1;
page->values[i+1].i = -1;
}
page->index = 0;
page->nr_vars = 0;
return page;
}
void delegate_get(struct page *page, int i, int *obj_out, int *fun_out)
{
if (page->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
if (i*2 >= page->nr_vars)
VM_ERROR("Invalid delegate index: %d", i);
*obj_out = page->values[i*2].i;
*fun_out = page->values[i*2+1].i;
}
+4 -4
View File
@@ -66,8 +66,8 @@ static cJSON *resume_page_to_json(struct page *page)
cJSON_AddStringToObject(json, "type", page_type_strtab[page->type]);
cJSON_AddNumberToObject(json, "subtype", page->index);
if (page->type == ARRAY_PAGE) {
cJSON_AddNumberToObject(json, "struct-type", page->struct_type);
cJSON_AddNumberToObject(json, "rank", page->rank);
cJSON_AddNumberToObject(json, "struct-type", page->array.struct_type);
cJSON_AddNumberToObject(json, "rank", page->array.rank);
}
cJSON *values = cJSON_CreateArray();
@@ -200,8 +200,8 @@ static void load_page(int slot, cJSON *json)
// allocate page
struct page *page = alloc_page(page_type, subtype->valueint, cJSON_GetArraySize(values));
page->struct_type = struct_type;
page->rank = rank;
page->array.struct_type = struct_type;
page->array.rank = rank;
// init page variables
int i = 0;
+111 -1
View File
@@ -78,6 +78,12 @@ int32_t call_stack_ptr = 0;
struct ain *ain;
size_t instr_ptr = 0;
static struct {
struct page *page;
int index;
int nr_functions;
} current_delegate;
// Read the opcode at ADDR.
static int16_t get_opcode(size_t addr)
{
@@ -2007,6 +2013,106 @@ static enum opcode execute_instruction(enum opcode opcode)
stack_push(i < get_argument(1) || i >= get_argument(2));
break;
}
//case DG_ADD:
//case DG_SET:
case DG_CALL: { // DG_TYPE, ADDR
int dg = get_argument(0);
if (dg < 0 || dg >= ain->nr_delegates)
VM_ERROR("Invalid delegate index");
int obj, fun;
delegate_get(current_delegate.page, current_delegate.index, &obj, &fun);
// pop previous return value
if (ain->delegates[dg].return_type.data != AIN_VOID) {
stack_pop();
}
// call function
vm_call(fun, obj);
if (++current_delegate.index >= current_delegate.nr_functions) {
// clear delegate state
current_delegate.page = NULL;
current_delegate.index = 0;
current_delegate.nr_functions = 0;
// jump to addr
instr_ptr = get_argument(1);
} else {
// continue to next instruction
instr_ptr += 10;
}
break;
}
case DG_NUMOF: {
stack_push(delegate_numof(heap_get_page(stack_pop().i)));
break;
}
//case DG_EXIST:
//case DG_ERASE:
case DG_CLEAR: {
int slot = stack_pop().i;
if (!slot)
break;
heap_set_page(slot, delegate_clear(heap_get_delegate_page(slot)));
break;
}
case DG_COPY: {
stack_push(vm_copy_page(heap_get_delegate_page(stack_pop().i)));
break;
}
case DG_ASSIGN: {
int set_i = stack_pop().i;
int dst_i = stack_pop().i;
struct page *set = heap_get_delegate_page(set_i);
delete_page(dst_i);
heap_set_page(dst_i, copy_page(set));
stack_push(set_i);
break;
}
case DG_PLUSA: {
int add_i = stack_pop().i;
int dst_i = stack_pop().i;
struct page *add = heap_get_delegate_page(add_i);
struct page *dst = heap_get_delegate_page(dst_i);
heap_set_page(dst_i, delegate_plusa(dst, add));
stack_push(add_i);
break;
}
case DG_MINUSA: {
int minus_i = stack_pop().i;
int dst_i = stack_pop().i;
struct page *minus = heap_get_delegate_page(minus_i);
struct page *dst = heap_get_delegate_page(dst_i);
heap_set_page(dst_i, delegate_minusa(dst, minus));
stack_push(minus_i);
break;
}
case DG_POP: {
heap_unref(stack_pop().i);
break;
}
case DG_NEW_FROM_METHOD: {
int fun = stack_pop().i;
int obj = stack_pop().i;
stack_push(heap_alloc_page(delegate_new_from_method(obj, fun)));
break;
}
case DG_CALLBEGIN: { // DG_TYPE
int dg = get_argument(0);
if (dg < 0 || dg >= ain->nr_delegates)
VM_ERROR("Invalid delegate index");
struct page *page = heap_get_page(stack_pop().i);
if (page->type != DELEGATE_PAGE)
VM_ERROR("Not a delegate");
current_delegate.page = page;
current_delegate.index = 0;
current_delegate.nr_functions = delegate_numof(page);
// XXX: If the delegate has a return value, we push a dummy value
// so that DG_CALL can replace it
if (ain->delegates[dg].return_type.data != AIN_VOID) {
stack_push(0);
}
break;
}
//case DG_NEW:
//case DG_STR_TO_METHOD:
// -- NOOPs ---
case FUNC:
break;
@@ -2168,10 +2274,14 @@ static void describe_page(struct page *page)
free(u);
break;
case ARRAY_PAGE:
u = sjis2utf(ain_strtype(ain, page->a_type, page->struct_type), 0);
u = sjis2utf(ain_strtype(ain, page->a_type, page->array.struct_type), 0);
sys_message("ARRAY_PAGE: %s\n", u);
free(u);
break;
case DELEGATE_PAGE:
// TODO: list function names
sys_message("DELEGATE_PAGE\n");
break;
}
}