📄 idelvm.c
字号:
/* * Virtual machine interpreter. * Copyright (C) 2001-2002 Darius Bacon */#include <errno.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include "idel_private.h"/* Table mapping Idel opcodes to internal labels. */const cplabel *opcode_labels = NULL;/* Verify that e is a valid Entry*. */static voidentry_check (const Entry *e){ assert (e != NULL); assert (0 <= e->popping); assert (0 <= e->pushing); if (e->address != NULL) assert (e->refs == NULL); /* TODO: check that address & refs point to safe & valid stuff */}/* Verify that vm is a valid VM*. */voidvm_check (const VM *vm){ int i; assert (vm != NULL); assert (0 < vm->stack_size); assert (vm->stack != NULL); assert (vm->stack <= vm->sp && vm->sp <= vm->rp && vm->rp <= vm->stack + vm->stack_size - 1); assert (*(i32 *)(vm->stack[vm->stack_size - 1]) == (i32) opcode_labels[HALT]); /* TODO: frames_check() */ assert (0 < vm->num_chunks && vm->num_chunks <= vm->chunks_size); assert (vm->chunks != NULL); for (i = 0; i < vm->num_chunks; ++i) assert (vm->chunks[i] != NULL); assert (vm->pc != NULL); /* TODO: check that it points into the code space or a stub */ assert (0 < vm->data_size); assert (0 == vm->data_size % word_size); /* data ends on a word boundary */ assert (vm->data != NULL); assert (0 <= vm->data_ptr && vm->data_ptr <= vm->data_size); assert (false == vm->profiling || true == vm->profiling); assert (0 <= vm->tallies_size); assert (0 == vm->tallies_size || vm->tallies != NULL); assert (0 <= vm->num_tallies && vm->num_tallies <= vm->tallies_size); /* TODO: check that each tally entry is reasonable */ assert (0 < vm->ns_size); assert (vm->nesting != NULL); assert (vm->nesting <= vm->nsp && vm->nsp <= vm->nesting + vm->ns_size); /* TODO: nest_check() */ assert (0 <= vm->height); assert (0 <= vm->locals); assert (vm->height + vm->locals <= vm->max_width); assert (-1 <= vm->returning); assert (false == vm->at_jump_target || true == vm->at_jump_target); assert (0 <= vm->block_start && vm->block_start <= vm->block_end); assert (vm->block_start <= vm->defn_number && vm->defn_number <= vm->block_end); assert (0 <= vm->entries_size); assert (vm->entries != NULL); for (i = 0; i < vm->block_end; ++i) entry_check (vm->entries + i); assert (vm->there != NULL); assert (vm->chunks[vm->num_chunks - 1] <= vm->there && vm->there <= vm->chunks[vm->num_chunks - 1] + chunk_size);}/* Return a new VM (but with uninitialized codegen fields). */VM *vm_sort_of_make (int stack_size, int data_size){ static i32 halt_stub; if (stack_size <= 0 || data_size <= 0 || data_size % bytes_per_word != 0) die ("Bad argument to vm_make()"); { VM *vm = allot (sizeof (*vm)); vm->stack_size = stack_size; vm->stack = allot (stack_size * sizeof vm->stack[0]); vm->sp = vm->stack; vm->rp = vm->stack + stack_size; vm->num_chunks = 1; vm->chunks_size = 1; vm->chunks = allot (vm->chunks_size * sizeof vm->chunks[0]); vm->chunks[0] = allot (chunk_size * sizeof vm->chunks[0][0]); vm->data_size = data_size; vm->data = allot (data_size * sizeof vm->data[0]); memset (vm->data, 0, data_size * sizeof vm->data[0]); vm->obj_code = NULL; vm->fm_size = 64; vm->num_fms = 0; vm->fm = allot (vm->fm_size * sizeof vm->fm[0]); /* set up the label table */ if (opcode_labels == NULL) { vm_run (NULL, 0); halt_stub = (i32) opcode_labels [HALT]; } /* Push a sentinel on the return stack. */ *--(vm->rp) = (i32) &halt_stub; vm->pc = &halt_stub; return vm; }}/* Destroy a vm. */voidvm_unmake (VM *vm){ /* FIXME: free codegen vars & obj_code */ int i; free (vm->fm); free (vm->data); for (i = 0; i < vm->num_chunks; ++i) free (vm->chunks [i]); free (vm->chunks); free (vm->stack); free (vm);}/* State saving *//* Push onto `ow' the contents of `or'. */static voidcopy_subfile (OW *ow, const OR *or){ int n = or->limit - or->ptr; while (ow->ptr - ow->buffer < n) ow_grow (ow); ow->ptr -= n; memcpy (ow->ptr, or->ptr, n);}/* Push onto `ow' the defns that make up vm's code. */static voidpush_defns (OW *ow, const VM *vm){ copy_subfile (ow, vm->obj_code);}/* Push onto `ow' a data section with the contents of vm's data space. */static voidpush_data (OW *ow, const VM *vm){ int g = vm->data_size - word_size; for (; 0 <= g; g -= word_size) if (0 != *(i32 *)(vm->data + g)) break; if (0 <= g) { int mark = start_subfile (ow); for (; 0 <= g; g -= word_size) push_i32 (ow, *(i32 *)(vm->data + g)); end_subfile (ow, mark); push_u32 (ow, tag_ints); }}/* Return vm's frame map for the return address `code'. */static const Frame_map *frame_map_find (const VM *vm, const i32 *code){ int i; /* TODO: binary search or something */ for (i = 0; i < vm->num_fms; ++i) if (vm->fm [i].code == code) return vm->fm + i; die ("Unmapped return pointer"); return NULL;}/* Push onto `ow' the stack frame at (sp,rp) described by `m'. Pre: sp, rp, and m designate a genuine stack frame in vm. */static voidpush_frame (OW *ow, const VM *vm, const Frame_map *m, const i32 *sp, const i32 *rp){ int i; for (i = 0; i < m->height; ++i) push_i32 (ow, sp [-1 - i]); for (i = 0; i < m->locals; ++i) push_i32 (ow, rp [i]); push_u32 (ow, m - vm->fm);}/* Push onto `ow' all of vm's stack frames from (sp,rp) back to the bottom of the stacks (except for the sentinel at the very bottom). Pre: (sp, rp) designates a genuine stack frame in vm. */static voidpush_frames (OW *ow, const VM *vm, const i32 *sp, const i32 *rp){ if (rp < vm->stack + vm->stack_size - 1) { const Frame_map *m = frame_map_find (vm, (const i32 *) rp [0]); push_frames (ow, vm, sp - m->height, rp + (m->locals + 1)); push_frame (ow, vm, m, sp, rp); }}/* Push onto `ow' all of vm's stack frames. Pre: vm's program counter is at a program point with a frame map. */static voidpush_stack (OW *ow, const VM *vm){ int mark = start_subfile (ow); push_frames (ow, vm, vm->sp, vm->rp); end_subfile (ow, mark); push_u32 (ow, tag_stack);}/* Push onto `ow' the data needed to recreate vm's state. Pre: vm's program counter is at a program point with a frame map. */static voidvm_freeze (OW *ow, const VM *vm){ push_data (ow, vm); push_stack (ow, vm); push_defns (ow, vm); push_header (ow, NULL);}/* Write the contents of `ow' to `filename'. */static voidow_write_file (const char *filename, OW *ow){ FILE *out = open_file (filename, "wb"); ow_write (out, ow); fclose (out);}/* Save vm's state in object-file format to the file "frozen". */static voidsave_vm_state (const VM *vm){ OW *ow = ow_make (); vm_freeze (ow, vm); ow_write_file ("frozen", ow); ow_unmake (ow);}/* The inner interpreter */enum { tracing = 0 /* set to 1 to print instructions executed */};/* Print a data stack to stdout. */static voidprint_stack (const i32 *stack, const i32 *sp){ int i; for (i = 0; stack + i < sp; ++i) printf ("%d ", stack[i]); printf ("\n");}/* Print current instruction and stack (for tracing) *//* TODO: show locals too */static voidtrace_op (const i32 *pc, const i32 *stack, const i32 *sp){ printf ("%-30s | ", format_op (pc)); print_stack (stack, sp);}/* If vm is non-NULL: Execute vm's code, starting at `pc', until completion, error, or exhaustion of fuel. Return a status code. (Pre: `pc' points to valid code consistent with the vm state.) else: Initialize the opcode_labels table. (Blame GNU C's scope rules for this ugly disjunctive specification...) */intvm_run (VM *vm, int fuel){ static const cplabel table[] = {#include "labels.inc" }; if (vm == NULL) { opcode_labels = table; return 0; } else { const i32 *pc = vm->pc; i32 *sp = vm->sp - 1, *rp = vm->rp; i32 *stack = vm->stack; i8 *data = vm->data; unsigned data_size = vm->data_size; if (paranoid) vm_check (vm); if (0) vm_dump_code (vm); /* Now run it */#include "interp.inc" }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -