/* Copyright (C) 2000 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA * * As a special exception, the Free Software Foundation gives permission * for additional uses of the text contained in its release of GUILE. * * The exception is that, if you link the GUILE library with other files * to produce an executable, this does not by itself cause the * resulting executable to be covered by the GNU General Public License. * Your use of that executable is in no way restricted on account of * linking the GUILE library code into it. * * This exception does not however invalidate any other reasons why * the executable file might be covered by the GNU General Public License. * * This exception applies only to the code released by the * Free Software Foundation under the name GUILE. If you copy * code from other Free Software Foundation releases into a copy of * GUILE, as the General Public License permits, the exception does * not apply to the code that you add in this way. To avoid misleading * anyone as to the status of such modified files, you must delete * this exception notice from them. * * If you write modifications of your own for GUILE, it is your choice * whether to permit this exception to apply to your modifications. * If you do not wish that, delete this exception notice. */ #define SCM_DEBUG_TYPING_STRICTNESS 0 #include "config.h" #include "vm.h" /* default stack size in the number of SCM */ #define VM_DEFAULT_STACK_SIZE (16 * 1024) /* = 64KB */ #define VM_MAXIMUM_STACK_SIZE (1024 * 1024) /* = 4MB */ /* I sometimes use this for debugging. */ #define vm_puts(OBJ) \ { \ scm_display (OBJ, scm_def_errp); \ scm_newline (scm_def_errp); \ } /* * Generic object name */ static SCM scm_name_property; SCM_DEFINE (scm_name, "name", 1, 0, 0, (SCM obj), "") #define FUNC_NAME s_scm_name { return scm_primitive_property_ref (scm_name_property, obj); } #undef FUNC_NAME SCM_DEFINE (scm_set_name_x, "set-name!", 2, 0, 0, (SCM obj, SCM name), "") #define FUNC_NAME s_scm_set_name_x { SCM_VALIDATE_SYMBOL (2, name); return scm_primitive_property_set_x (scm_name_property, obj, name); } #undef FUNC_NAME int scm_smob_print_with_name (SCM smob, SCM port, scm_print_state *pstate) { int n = SCM_SMOBNUM (smob); SCM name = scm_name (smob); scm_puts ("#<", port); scm_puts (SCM_SMOBNAME (n) ? SCM_SMOBNAME (n) : "smob", port); scm_putc (' ', port); if (SCM_FALSEP (name)) { scm_puts ("0x", port); scm_intprint (SCM_UNPACK (scm_smobs[n].size ? SCM_CDR (smob) : smob), 16, port); } else { scm_display (name, port); } scm_putc ('>', port); return 1; } static void init_name_property () { scm_name_property = scm_permanent_object (scm_primitive_make_property (SCM_BOOL_F)); } /* * Instruction */ static long scm_instruction_tag; static struct scm_instruction scm_instruction_table[] = { #include "vm_system.inst" #include "vm_scheme.inst" #include "vm_number.inst" {op_last} }; #define SCM_INSTRUCTION(OP) &scm_instruction_table[SCM_UNPACK (OP)] static SCM make_instruction (struct scm_instruction *instp) { SCM_RETURN_NEWSMOB (scm_instruction_tag, instp); } static int print_instruction (SCM obj, SCM port, scm_print_state *pstate) { scm_puts ("#name, port); scm_putc ('>', port); return 1; } static void init_instruction_type () { scm_instruction_tag = scm_make_smob_type ("instruction", 0); scm_set_smob_print (scm_instruction_tag, print_instruction); } /* C interface */ static struct scm_instruction * scm_lookup_instruction (const char *name) { struct scm_instruction *p; for (p = scm_instruction_table; p->opcode != op_last; p++) if (strcmp (name, p->name) == 0) return p; return 0; } /* Scheme interface */ SCM_DEFINE (scm_instruction_p, "instruction?", 1, 0, 0, (SCM obj), "") #define FUNC_NAME s_scm_instruction_p { return SCM_BOOL (SCM_INSTRUCTION_P (obj)); } #undef FUNC_NAME SCM_DEFINE (scm_system_instruction_p, "system-instruction?", 1, 0, 0, (SCM obj), "") #define FUNC_NAME s_scm_system_instruction_p { return SCM_BOOL (SCM_SYSTEM_INSTRUCTION_P (obj)); } #undef FUNC_NAME SCM_DEFINE (scm_functional_instruction_p, "functional-instruction?", 1, 0, 0, (SCM obj), "") #define FUNC_NAME s_scm_functional_instruction_p { return SCM_BOOL (SCM_FUNCTIONAL_INSTRUCTION_P (obj)); } #undef FUNC_NAME SCM_DEFINE (scm_instruction_name_p, "instruction-name?", 1, 0, 0, (SCM name), "") #define FUNC_NAME s_scm_instruction_name_p { SCM_VALIDATE_SYMBOL (1, name); return SCM_BOOL (scm_lookup_instruction (SCM_SYMBOL_CHARS (name))); } #undef FUNC_NAME SCM_DEFINE (scm_symbol_to_instruction, "symbol->instruction", 1, 0, 0, (SCM name), "") #define FUNC_NAME s_scm_symbol_to_instruction { struct scm_instruction *p; SCM_VALIDATE_SYMBOL (1, name); p = scm_lookup_instruction (SCM_SYMBOL_CHARS (name)); if (!p) SCM_MISC_ERROR ("No such instruction: ~S", SCM_LIST1 (name)); return p->obj; } #undef FUNC_NAME SCM_DEFINE (scm_instruction_list, "instruction-list", 0, 0, 0, (), "") #define FUNC_NAME s_scm_instruction_list { SCM list = SCM_EOL; struct scm_instruction *p; for (p = scm_instruction_table; p->opcode != op_last; p++) list = scm_cons (p->obj, list); return scm_reverse_x (list, SCM_EOL); } #undef FUNC_NAME SCM_DEFINE (scm_instruction_opcode, "instruction-opcode", 1, 0, 0, (SCM inst), "") #define FUNC_NAME s_scm_instruction_opcode { SCM_VALIDATE_INSTRUCTION (1, inst); return SCM_MAKINUM (SCM_INSTRUCTION_DATA (inst)->opcode); } #undef FUNC_NAME SCM_DEFINE (scm_instruction_name, "instruction-name", 1, 0, 0, (SCM inst), "") #define FUNC_NAME s_scm_instruction_name { SCM_VALIDATE_INSTRUCTION (1, inst); return SCM_CAR (scm_intern0 (SCM_INSTRUCTION_DATA (inst)->name)); } #undef FUNC_NAME SCM_DEFINE (scm_instruction_type, "instruction-type", 1, 0, 0, (SCM inst), "") #define FUNC_NAME s_scm_instruction_type { SCM_VALIDATE_INSTRUCTION (1, inst); return SCM_MAKINUM (SCM_INSTRUCTION_DATA (inst)->type); } #undef FUNC_NAME SCM_DEFINE (scm_instruction_scheme_name, "instruction-scheme-name", 1, 0, 0, (SCM inst), "") #define FUNC_NAME s_scm_instruction_scheme_name { SCM_VALIDATE_INSTRUCTION (1, inst); if (SCM_FUNCTIONAL_INSTRUCTION_P (inst)) return SCM_CAR (scm_intern0 (SCM_INSTRUCTION_DATA (inst)->sname)); else return SCM_BOOL_F; } #undef FUNC_NAME SCM_DEFINE (scm_instruction_arity, "instruction-arity", 1, 0, 0, (SCM inst), "") #define FUNC_NAME s_scm_instruction_arity { SCM_VALIDATE_INSTRUCTION (1, inst); if (SCM_FUNCTIONAL_INSTRUCTION_P (inst)) { struct scm_instruction *p = SCM_INSTRUCTION_DATA (inst); return SCM_LIST2 (SCM_MAKINUM (p->nargs), SCM_BOOL (p->restp)); } else return SCM_BOOL_F; } #undef FUNC_NAME /* * Bytecode */ static long scm_bytecode_tag; static SCM make_bytecode (int size) { struct scm_bytecode *p = scm_must_malloc (sizeof (*p) + (size * sizeof (SCM)), "make_bytecode"); p->size = size; SCM_RETURN_NEWSMOB (scm_bytecode_tag, p); } static SCM mark_bytecode (SCM bytecode) { int i; struct scm_instruction *p; int size = SCM_BYTECODE_SIZE (bytecode); SCM *base = SCM_BYTECODE_BASE (bytecode); for (i = 0; i < size; i++) { p = SCM_INSTRUCTION (base[i]); switch (p->type) { case INST_NONE: break; case INST_SCM: case INST_TOP: case INST_EXT: case INST_CODE: scm_gc_mark (base[++i]); break; case INST_INUM: /* a fixed integer; we don't need to mark it */ case INST_ADDR: /* real memory address; we shouldn't mark it! */ i++; } } return SCM_BOOL_F; } static int print_bytecode (SCM obj, SCM port, scm_print_state *pstate) { scm_puts ("#', port); return 1; } static scm_sizet free_bytecode (SCM bytecode) { int size = (sizeof (struct scm_bytecode) + (SCM_BYTECODE_SIZE (bytecode) * sizeof (SCM))); if (SCM_BYTECODE_EXTS (bytecode)) { size += (SCM_BYTECODE_EXTS (bytecode)[0] + 1) * sizeof (int); scm_must_free (SCM_BYTECODE_EXTS (bytecode)); } scm_must_free (SCM_BYTECODE_DATA (bytecode)); return size; } static void init_bytecode_type () { scm_bytecode_tag = scm_make_smob_type ("bytecode", 0); scm_set_smob_mark (scm_bytecode_tag, mark_bytecode); scm_set_smob_print (scm_bytecode_tag, print_bytecode); scm_set_smob_free (scm_bytecode_tag, free_bytecode); } /* Internal functions */ static SCM lookup_variable (SCM sym) { SCM eclo = scm_standard_eval_closure (scm_selected_module ()); SCM var = scm_eval_closure_lookup (eclo, sym, SCM_BOOL_F); if (SCM_FALSEP (var)) var = scm_eval_closure_lookup (eclo, sym, SCM_BOOL_T); return var; } /* Scheme interface */ SCM_DEFINE (scm_bytecode_p, "bytecode?", 1, 0, 0, (SCM obj), "") #define FUNC_NAME s_scm_bytecode_p { return SCM_BOOL (SCM_BYTECODE_P (obj)); } #undef FUNC_NAME SCM_DEFINE (scm_make_bytecode, "make-bytecode", 1, 0, 0, (SCM code), "") #define FUNC_NAME s_scm_make_bytecode { int i, size, len, offset; SCM header, body, nreqs, restp, nvars, nexts, exts, bytecode; SCM *old, *new, *address; /* Type check */ SCM_VALIDATE_VECTOR (1, code); SCM_ASSERT_RANGE (1, code, SCM_LENGTH (code) == 2); header = SCM_VELTS (code)[0]; body = SCM_VELTS (code)[1]; SCM_VALIDATE_VECTOR (1, header); SCM_VALIDATE_VECTOR (2, body); SCM_ASSERT_RANGE (1, header, SCM_LENGTH (header) == 5); nreqs = SCM_VELTS (header)[0]; restp = SCM_VELTS (header)[1]; nvars = SCM_VELTS (header)[2]; nexts = SCM_VELTS (header)[3]; exts = SCM_VELTS (header)[4]; SCM_VALIDATE_INUM (1, nreqs); SCM_VALIDATE_BOOL (2, restp); SCM_VALIDATE_INUM (3, nvars); SCM_VALIDATE_INUM (4, nexts); SCM_VALIDATE_VECTOR (5, exts); /* Create a new bytecode */ size = SCM_LENGTH (body); old = SCM_VELTS (body); bytecode = make_bytecode (size); new = SCM_BYTECODE_BASE (bytecode); /* Initialize the header */ SCM_BYTECODE_NREQS (bytecode) = SCM_INUM (nreqs); SCM_BYTECODE_RESTP (bytecode) = SCM_FALSEP (restp) ? 0 : 1; SCM_BYTECODE_NVARS (bytecode) = SCM_INUM (nvars); SCM_BYTECODE_NEXTS (bytecode) = SCM_INUM (nexts); len = SCM_LENGTH (exts); if (len == 0) { SCM_BYTECODE_EXTS (bytecode) = NULL; } else { SCM_BYTECODE_EXTS (bytecode) = scm_must_malloc ((len + 1) * sizeof (int), FUNC_NAME); SCM_BYTECODE_EXTS (bytecode)[0] = len; for (i = 0; i < len; i++) SCM_BYTECODE_EXTS (bytecode)[i + 1] = SCM_INUM (SCM_VELTS (exts)[i]); } /* Initialize the body */ for (i = 0; i < size; i++) { struct scm_instruction *p; /* Process instruction */ if (!SCM_SYMBOLP (old[i]) || !(p = scm_lookup_instruction (SCM_SYMBOL_CHARS (old[i])))) SCM_MISC_ERROR ("Invalid instruction: ~S", SCM_LIST1 (old[i])); new[i] = SCM_PACK (p->opcode); /* Process arguments */ if (p->type == INST_NONE) continue; if (++i >= size) SCM_MISC_ERROR ("Unexpected end of code", SCM_EOL); switch (p->type) { case INST_NONE: /* never come here */ case INST_INUM: SCM_VALIDATE_INUM (1, old[i]); /* fall through */ case INST_SCM: /* just copy */ new[i] = old[i]; break; case INST_TOP: /* top-level variable */ SCM_VALIDATE_SYMBOL (1, old[i]); new[i] = lookup_variable (old[i]); break; case INST_EXT: /* just copy for now */ SCM_VALIDATE_CONS (1, old[i]); SCM_VALIDATE_INUM (1, SCM_CAR (old[i])); SCM_VALIDATE_INUM (1, SCM_CDR (old[i])); new[i] = old[i]; break; case INST_CODE: /* another bytecode */ new[i] = scm_make_bytecode (old[i]); break; case INST_ADDR: /* real address */ SCM_VALIDATE_INUM (1, old[i]); /* Without the following intermediate variables, type conversion fails on my machine. Casting doesn't work well, why? */ offset = SCM_INUM (old[i]); address = new + offset; new[i] = SCM_VM_MAKE_ADDRESS (address); break; } } return bytecode; } #undef FUNC_NAME SCM_DEFINE (scm_bytecode_decode, "bytecode-decode", 1, 0, 0, (SCM bytecode), "") #define FUNC_NAME s_scm_bytecode_decode { int i, size, offset; SCM code, *old, *new; SCM_VALIDATE_BYTECODE (1, bytecode); size = SCM_BYTECODE_SIZE (bytecode); old = SCM_BYTECODE_BASE (bytecode); code = scm_make_vector (SCM_MAKINUM (size), SCM_BOOL_F); new = SCM_VELTS (code); for (i = 0; i < size; i++) { struct scm_instruction *p; /* Process instruction */ p = SCM_INSTRUCTION (old[i]); if (!p) { broken: SCM_MISC_ERROR ("Broken bytecode", SCM_EOL); } new[i] = scm_instruction_name (p->obj); /* Process arguments */ if (p->type == INST_NONE) continue; if (++i >= size) goto broken; switch (p->type) { case INST_NONE: /* never come here */ case INST_INUM: case INST_SCM: case INST_EXT: /* just copy */ new[i] = old[i]; break; case INST_TOP: /* top-level variable */ new[i] = SCM_CAR (old[i]); break; case INST_CODE: /* another bytecode */ new[i] = scm_bytecode_decode (old[i]); break; case INST_ADDR: /* program address */ offset = SCM_VM_ADDRESS (old[i]) - old; new[i] = SCM_MAKINUM (offset); break; } } return code; } #undef FUNC_NAME /* * Program */ static long scm_program_tag; static SCM make_program (SCM code, SCM env) { SCM_RETURN_NEWSMOB2 (scm_program_tag, SCM_UNPACK (code), SCM_UNPACK (env)); } static SCM mark_program (SCM program) { scm_gc_mark (SCM_PROGRAM_CODE (program)); return SCM_PROGRAM_ENV (program); } static SCM scm_vm_apply (SCM vm, SCM program, SCM args); static SCM make_vm (int stack_size); static SCM apply_program (SCM program, SCM args) { return scm_vm_apply (make_vm (VM_DEFAULT_STACK_SIZE), program, args); } static void init_program_type () { scm_program_tag = scm_make_smob_type ("program", 0); scm_set_smob_mark (scm_program_tag, mark_program); scm_set_smob_print (scm_program_tag, scm_smob_print_with_name); scm_set_smob_apply (scm_program_tag, apply_program, 0, 0, 1); } /* Scheme interface */ SCM_DEFINE (scm_program_p, "program?", 1, 0, 0, (SCM obj), "") #define FUNC_NAME s_scm_program_p { return SCM_BOOL (SCM_PROGRAM_P (obj)); } #undef FUNC_NAME SCM_DEFINE (scm_make_program, "make-program", 2, 0, 0, (SCM bytecode, SCM parent), "") #define FUNC_NAME s_scm_make_program { SCM_VALIDATE_BYTECODE (1, bytecode); return make_program (bytecode, parent); } #undef FUNC_NAME SCM_DEFINE (scm_program_code, "program-code", 1, 0, 0, (SCM program), "") #define FUNC_NAME s_scm_program_code { SCM_VALIDATE_PROGRAM (1, program); return SCM_PROGRAM_CODE (program); } #undef FUNC_NAME SCM_DEFINE (scm_program_base, "program-base", 1, 0, 0, (SCM program), "") #define FUNC_NAME s_scm_program_base { SCM_VALIDATE_PROGRAM (1, program); return SCM_VM_MAKE_ADDRESS (SCM_PROGRAM_BASE (program)); } #undef FUNC_NAME /* * VM Frame */ static long scm_vm_frame_tag; /* This is used for debugging */ struct scm_vm_frame { int size; SCM program; SCM variables; SCM dynamic_link; SCM external_link; SCM stack_pointer; SCM return_address; }; #define SCM_VM_FRAME_P(OBJ) SCM_SMOB_PREDICATE (scm_vm_frame_tag, OBJ) #define SCM_VM_FRAME_DATA(FR) ((struct scm_vm_frame *) SCM_SMOB_DATA (FR)) #define SCM_VALIDATE_VM_FRAME(POS,OBJ) SCM_MAKE_VALIDATE (POS, OBJ, VM_FRAME_P) static SCM make_vm_frame (SCM *fp) { int i; int size = SCM_INUM (SCM_VM_FRAME_SIZE (fp)); struct scm_vm_frame *p = scm_must_malloc (sizeof (*p), "make_vm_frame"); p->program = SCM_VM_FRAME_PROGRAM (fp); p->dynamic_link = SCM_VM_FRAME_DYNAMIC_LINK (fp); p->external_link = SCM_VM_FRAME_EXTERNAL_LINK (fp); p->stack_pointer = SCM_VM_FRAME_STACK_POINTER (fp); p->return_address = SCM_VM_FRAME_RETURN_ADDRESS (fp); if (!SCM_FALSEP (p->dynamic_link)) p->dynamic_link = make_vm_frame (SCM_VM_ADDRESS (p->dynamic_link)); size += SCM_PROGRAM_NREQS (p->program) + SCM_PROGRAM_RESTP (p->program); p->variables = scm_make_vector (SCM_MAKINUM (size), SCM_BOOL_F); for (i = 0; i < size; i++) SCM_VELTS (p->variables)[i] = SCM_VM_FRAME_VARIABLE (fp, i); SCM_RETURN_NEWSMOB (scm_vm_frame_tag, p); } static SCM mark_vm_frame (SCM frame) { struct scm_vm_frame *p = SCM_VM_FRAME_DATA (frame); scm_gc_mark (p->program); scm_gc_mark (p->dynamic_link); scm_gc_mark (p->external_link); return p->variables; } static void init_vm_frame_type () { scm_vm_frame_tag = scm_make_smob_type ("vm-frame", 0); scm_set_smob_mark (scm_vm_frame_tag, mark_vm_frame); } /* Scheme interface */ SCM_DEFINE (scm_frame_p, "frame?", 1, 0, 0, (SCM obj), "") #define FUNC_NAME s_scm_frame_p { return SCM_BOOL (SCM_VM_FRAME_P (obj)); } #undef FUNC_NAME SCM_DEFINE (scm_frame_program, "frame-program", 1, 0, 0, (SCM frame), "") #define FUNC_NAME s_scm_frame_program { SCM_VALIDATE_VM_FRAME (1, frame); return SCM_VM_FRAME_DATA (frame)->program; } #undef FUNC_NAME SCM_DEFINE (scm_frame_variables, "frame-variables", 1, 0, 0, (SCM frame), "") #define FUNC_NAME s_scm_frame_variables { SCM_VALIDATE_VM_FRAME (1, frame); return SCM_VM_FRAME_DATA (frame)->variables; } #undef FUNC_NAME SCM_DEFINE (scm_frame_dynamic_link, "frame-dynamic-link", 1, 0, 0, (SCM frame), "") #define FUNC_NAME s_scm_frame_dynamic_link { SCM_VALIDATE_VM_FRAME (1, frame); return SCM_VM_FRAME_DATA (frame)->dynamic_link; } #undef FUNC_NAME SCM_DEFINE (scm_frame_external_link, "frame-external-link", 1, 0, 0, (SCM frame), "") #define FUNC_NAME s_scm_frame_external_link { SCM_VALIDATE_VM_FRAME (1, frame); return SCM_VM_FRAME_DATA (frame)->external_link; } #undef FUNC_NAME SCM_DEFINE (scm_frame_stack_pointer, "frame-stack-pointer", 1, 0, 0, (SCM frame), "") #define FUNC_NAME s_scm_frame_stack_pointer { SCM_VALIDATE_VM_FRAME (1, frame); return SCM_VM_FRAME_DATA (frame)->stack_pointer; } #undef FUNC_NAME SCM_DEFINE (scm_frame_return_address, "frame-return-address", 1, 0, 0, (SCM frame), "") #define FUNC_NAME s_scm_frame_return_address { SCM_VALIDATE_VM_FRAME (1, frame); return SCM_VM_FRAME_DATA (frame)->return_address; } #undef FUNC_NAME /* * VM Continuation */ static long scm_vm_cont_tag; static SCM capture_vm_cont (struct scm_vm *vmp) { struct scm_vm *p = scm_must_malloc (sizeof (*p), "capture_vm_cont"); p->stack_size = vmp->stack_limit - vmp->sp; p->stack_base = scm_must_malloc (p->stack_size * sizeof (SCM), "capture_vm_cont"); p->stack_limit = p->stack_base + p->stack_size - 1; p->pc = vmp->pc; p->sp = (SCM *) (vmp->stack_limit - vmp->sp); p->fp = (SCM *) (vmp->stack_limit - vmp->fp); memcpy (p->stack_base, vmp->sp + 1, vmp->stack_size * sizeof (SCM)); SCM_RETURN_NEWSMOB (scm_vm_cont_tag, p); } static void reinstate_vm_cont (struct scm_vm *vmp, SCM cont) { struct scm_vm *p = SCM_VM_CONT_VMP (cont); if (vmp->stack_size < p->stack_size) { puts ("FIXME: Need to expand"); abort (); } vmp->pc = p->pc; vmp->sp = vmp->stack_limit - (int) p->sp; vmp->fp = vmp->stack_limit - (int) p->fp; memcpy (vmp->sp + 1, p->stack_base, p->stack_size * sizeof (SCM)); } static SCM mark_vm_cont (SCM cont) { SCM *p; struct scm_vm *vmp = SCM_VM_CONT_VMP (cont); for (p = vmp->stack_base; p <= vmp->stack_limit; p++) if (SCM_NIMP (*p)) scm_gc_mark (*p); return SCM_BOOL_F; } static scm_sizet free_vm_cont (SCM cont) { struct scm_vm *p = SCM_VM_CONT_VMP (cont); int size = sizeof (struct scm_vm) + p->stack_size * sizeof (SCM); scm_must_free (p->stack_base); scm_must_free (p); return size; } static void init_vm_cont_type () { scm_vm_cont_tag = scm_make_smob_type ("vm-cont", 0); scm_set_smob_mark (scm_vm_cont_tag, mark_vm_cont); scm_set_smob_free (scm_vm_cont_tag, free_vm_cont); } /* * VM */ static long scm_vm_tag; static SCM make_vm (int stack_size) { struct scm_vm *vmp = scm_must_malloc (sizeof (struct scm_vm), "make_vm"); vmp->stack_size = stack_size; vmp->stack_base = scm_must_malloc (stack_size * sizeof (SCM), "make_vm"); vmp->stack_limit = vmp->stack_base + vmp->stack_size - 1; vmp->sp = vmp->stack_limit; vmp->ac = SCM_BOOL_F; vmp->pc = NULL; vmp->fp = NULL; vmp->options = SCM_EOL; vmp->boot_hook = scm_make_hook (SCM_MAKINUM (1)); vmp->halt_hook = scm_make_hook (SCM_MAKINUM (1)); vmp->next_hook = scm_make_hook (SCM_MAKINUM (1)); vmp->call_hook = scm_make_hook (SCM_MAKINUM (1)); vmp->apply_hook = scm_make_hook (SCM_MAKINUM (1)); vmp->return_hook = scm_make_hook (SCM_MAKINUM (1)); SCM_RETURN_NEWSMOB (scm_vm_tag, vmp); } static SCM mark_vm (SCM vm) { SCM *p; struct scm_vm *vmp = SCM_VM_DATA (vm); for (p = vmp->sp + 1; p <= vmp->stack_limit; p++) if (SCM_NIMP (*p)) scm_gc_mark (*p); scm_gc_mark (vmp->ac); scm_gc_mark (vmp->boot_hook); scm_gc_mark (vmp->halt_hook); scm_gc_mark (vmp->next_hook); scm_gc_mark (vmp->call_hook); scm_gc_mark (vmp->apply_hook); scm_gc_mark (vmp->return_hook); return vmp->options; } static void init_vm_type () { scm_vm_tag = scm_make_smob_type ("vm", sizeof (struct scm_vm)); scm_set_smob_mark (scm_vm_tag, mark_vm); scm_set_smob_print (scm_vm_tag, scm_smob_print_with_name); } /* Scheme interface */ SCM_DEFINE (scm_vm_version, "vm-version", 0, 0, 0, (), "") #define FUNC_NAME s_scm_vm_version { return scm_makfrom0str (VERSION); } #undef FUNC_NAME SCM_DEFINE (scm_vm_p, "vm?", 1, 0, 0, (SCM obj), "") #define FUNC_NAME s_scm_vm_p { return SCM_BOOL (SCM_VM_P (obj)); } #undef FUNC_NAME SCM_DEFINE (scm_make_vm, "make-vm", 0, 0, 0, (), "") #define FUNC_NAME s_scm_make_vm { return make_vm (VM_DEFAULT_STACK_SIZE); } #undef FUNC_NAME SCM_DEFINE (scm_vm_ac, "vm:ac", 1, 0, 0, (SCM vm), "") #define FUNC_NAME s_scm_vm_ac { SCM_VALIDATE_VM (1, vm); return SCM_VM_DATA (vm)->ac; } #undef FUNC_NAME SCM_DEFINE (scm_vm_pc, "vm:pc", 1, 0, 0, (SCM vm), "") #define FUNC_NAME s_scm_vm_pc { SCM_VALIDATE_VM (1, vm); return SCM_VM_MAKE_ADDRESS (SCM_VM_DATA (vm)->pc); } #undef FUNC_NAME SCM_DEFINE (scm_vm_sp, "vm:sp", 1, 0, 0, (SCM vm), "") #define FUNC_NAME s_scm_vm_sp { SCM_VALIDATE_VM (1, vm); return SCM_VM_MAKE_ADDRESS (SCM_VM_DATA (vm)->sp); } #undef FUNC_NAME SCM_DEFINE (scm_vm_fp, "vm:fp", 1, 0, 0, (SCM vm), "") #define FUNC_NAME s_scm_vm_fp { SCM_VALIDATE_VM (1, vm); return SCM_VM_MAKE_ADDRESS (SCM_VM_DATA (vm)->fp); } #undef FUNC_NAME SCM_DEFINE (scm_vm_current_frame, "vm-current-frame", 1, 0, 0, (SCM vm), "") #define FUNC_NAME s_scm_vm_current_frame { SCM_VALIDATE_VM (1, vm); return make_vm_frame (SCM_VM_DATA (vm)->fp); } #undef FUNC_NAME SCM_DEFINE (scm_vm_fetch_code, "vm-fetch-code", 2, 0, 0, (SCM vm, SCM addr), "") #define FUNC_NAME s_scm_vm_fetch_code { SCM *p, list; struct scm_instruction *inst; SCM_VALIDATE_VM (1, vm); SCM_VALIDATE_INUM (2, addr); p = SCM_VM_ADDRESS (addr); inst = SCM_INSTRUCTION (*p); if (!inst) SCM_MISC_ERROR ("Broken bytecode", SCM_LIST1 (addr)); list = SCM_LIST1 (scm_instruction_name (inst->obj)); if (inst->type != INST_NONE) { if (inst->type == INST_ADDR) { p = SCM_CODE_TO_ADDR (p[1]); SCM_SETCDR (list, SCM_LIST1 (SCM_VM_MAKE_ADDRESS (p))); } else SCM_SETCDR (list, SCM_LIST1 (p[1])); } return list; } #undef FUNC_NAME SCM_DEFINE (scm_vm_stack_to_list, "vm-stack->list", 1, 0, 0, (SCM vm), "") #define FUNC_NAME s_scm_vm_stack_to_list { struct scm_vm *vmp; SCM *p, list = SCM_EOL; SCM_VALIDATE_VM (1, vm); vmp = SCM_VM_DATA (vm); for (p = vmp->sp + 1; p <= vmp->stack_limit; p++) list = scm_cons (*p, list); return list; } #undef FUNC_NAME SCM_DEFINE (scm_vm_option, "vm-option", 2, 0, 0, (SCM vm, SCM key), "") #define FUNC_NAME s_scm_vm_option { SCM_VALIDATE_VM (1, vm); SCM_VALIDATE_SYMBOL (2, key); return scm_assq_ref (SCM_VM_DATA (vm)->options, key); } #undef FUNC_NAME SCM_DEFINE (scm_vm_set_option_x, "vm-set-option!", 3, 0, 0, (SCM vm, SCM key, SCM val), "") #define FUNC_NAME s_scm_vm_set_option_x { SCM_VALIDATE_VM (1, vm); SCM_VALIDATE_SYMBOL (2, key); SCM_VM_DATA (vm)->options = scm_assq_set_x (SCM_VM_DATA (vm)->options, key, val); return SCM_UNSPECIFIED; } #undef FUNC_NAME SCM_DEFINE (scm_vm_boot_hook, "vm-boot-hook", 1, 0, 0, (SCM vm), "") #define FUNC_NAME s_scm_vm_boot_hook { SCM_VALIDATE_VM (1, vm); return SCM_VM_DATA (vm)->boot_hook; } #undef FUNC_NAME SCM_DEFINE (scm_vm_halt_hook, "vm-halt-hook", 1, 0, 0, (SCM vm), "") #define FUNC_NAME s_scm_vm_halt_hook { SCM_VALIDATE_VM (1, vm); return SCM_VM_DATA (vm)->halt_hook; } #undef FUNC_NAME SCM_DEFINE (scm_vm_next_hook, "vm-next-hook", 1, 0, 0, (SCM vm), "") #define FUNC_NAME s_scm_vm_next_hook { SCM_VALIDATE_VM (1, vm); return SCM_VM_DATA (vm)->next_hook; } #undef FUNC_NAME SCM_DEFINE (scm_vm_call_hook, "vm-call-hook", 1, 0, 0, (SCM vm), "") #define FUNC_NAME s_scm_vm_call_hook { SCM_VALIDATE_VM (1, vm); return SCM_VM_DATA (vm)->call_hook; } #undef FUNC_NAME SCM_DEFINE (scm_vm_apply_hook, "vm-apply-hook", 1, 0, 0, (SCM vm), "") #define FUNC_NAME s_scm_vm_apply_hook { SCM_VALIDATE_VM (1, vm); return SCM_VM_DATA (vm)->apply_hook; } #undef FUNC_NAME SCM_DEFINE (scm_vm_return_hook, "vm-return-hook", 1, 0, 0, (SCM vm), "") #define FUNC_NAME s_scm_vm_return_hook { SCM_VALIDATE_VM (1, vm); return SCM_VM_DATA (vm)->return_hook; } #undef FUNC_NAME SCM_SYMBOL (sym_debug, "debug"); static SCM scm_regular_vm (SCM vm, SCM program); static SCM scm_debug_vm (SCM vm, SCM program); #define VM_CODE(name) SCM_PACK (scm_lookup_instruction (name)->opcode) SCM_DEFINE (scm_vm_run, "vm-run", 2, 0, 0, (SCM vm, SCM program), "") #define FUNC_NAME s_scm_vm_run { SCM bootcode; static SCM template[5]; SCM_VALIDATE_VM (1, vm); SCM_VALIDATE_PROGRAM (2, program); if (SCM_EQ_P (template[0], SCM_PACK (0))) { template[0] = VM_CODE ("%loadc"); template[1] = SCM_BOOL_F; /* overwritten */ template[2] = VM_CODE ("%call"); template[3] = SCM_MAKINUM (0); template[4] = VM_CODE ("%halt"); } /* Create a boot program */ bootcode = make_bytecode (5); memcpy (SCM_BYTECODE_BASE (bootcode), template, sizeof (SCM) * 5); SCM_BYTECODE_BASE (bootcode)[1] = program; SCM_BYTECODE_SIZE (bootcode) = 5; SCM_BYTECODE_EXTS (bootcode) = NULL; SCM_BYTECODE_NREQS (bootcode) = 0; SCM_BYTECODE_RESTP (bootcode) = 0; SCM_BYTECODE_NVARS (bootcode) = 0; SCM_BYTECODE_NEXTS (bootcode) = 0; program = SCM_MAKE_PROGRAM (bootcode, SCM_BOOL_F); if (SCM_FALSEP (scm_vm_option (vm, sym_debug))) return scm_regular_vm (vm, program); else return scm_debug_vm (vm, program); } #undef FUNC_NAME SCM_DEFINE (scm_vm_apply, "vm-apply", 3, 0, 0, (SCM vm, SCM program, SCM args), "") #define FUNC_NAME s_scm_vm_apply { int len; SCM bootcode; static SCM template[7]; SCM_VALIDATE_VM (1, vm); SCM_VALIDATE_PROGRAM (2, program); SCM_VALIDATE_LIST_COPYLEN (3, args, len); if (SCM_EQ_P (template[0], SCM_PACK (0))) { template[0] = VM_CODE ("%push-list"); template[1] = SCM_EOL; /* overwritten */ template[2] = VM_CODE ("%loadc"); template[3] = SCM_BOOL_F; /* overwritten */ template[4] = VM_CODE ("%call"); template[5] = SCM_MAKINUM (0); /* overwritten */ template[6] = VM_CODE ("%halt"); } /* Create a boot program */ bootcode = make_bytecode (7); memcpy (SCM_BYTECODE_BASE (bootcode), template, sizeof (SCM) * 7); SCM_BYTECODE_BASE (bootcode)[1] = args; SCM_BYTECODE_BASE (bootcode)[3] = program; SCM_BYTECODE_BASE (bootcode)[5] = SCM_MAKINUM (len); SCM_BYTECODE_SIZE (bootcode) = 7; SCM_BYTECODE_EXTS (bootcode) = NULL; SCM_BYTECODE_NREQS (bootcode) = 0; SCM_BYTECODE_RESTP (bootcode) = 0; SCM_BYTECODE_NVARS (bootcode) = 0; SCM_BYTECODE_NEXTS (bootcode) = 0; program = SCM_MAKE_PROGRAM (bootcode, SCM_BOOL_F); if (SCM_FALSEP (scm_vm_option (vm, sym_debug))) return scm_regular_vm (vm, program); else return scm_debug_vm (vm, program); } #undef FUNC_NAME /* * The VM engines */ /* We don't want to snarf the engines */ #ifndef SCM_MAGIC_SNARFER /* the regular engine */ #define VM_ENGINE SCM_VM_REGULAR_ENGINE #include "vm_engine.c" #undef VM_ENGINE /* the debug engine */ #define VM_ENGINE SCM_VM_DEBUG_ENGINE #include "vm_engine.c" #undef VM_ENGINE #endif /* not SCM_MAGIC_SNARFER */ /* * Initialize */ static SCM scm_module_vm; void scm_init_vm () { SCM old_module; /* Initialize the module */ scm_module_vm = scm_make_module (scm_read_0str ("(vm vm)")); old_module = scm_select_module (scm_module_vm); init_name_property (); init_instruction_type (); init_bytecode_type (); init_program_type (); init_vm_frame_type (); init_vm_cont_type (); init_vm_type (); #include "vm.x" scm_select_module (old_module); { struct scm_instruction *p; for (p = scm_instruction_table; p->opcode != op_last; p++) { p->obj = scm_permanent_object (make_instruction (p)); if (p->restp) p->type = INST_INUM; } } } void scm_init_vm_vm_module () { scm_register_module_xxx ("vm vm", (void *) scm_init_vm); }