diff options
Diffstat (limited to 'lisp/bytecode.c')
-rw-r--r-- | lisp/bytecode.c | 3707 |
1 files changed, 3707 insertions, 0 deletions
diff --git a/lisp/bytecode.c b/lisp/bytecode.c new file mode 100644 index 0000000..39667b0 --- /dev/null +++ b/lisp/bytecode.c @@ -0,0 +1,3707 @@ +/* + * Copyright (c) 2002 by The XFree86 Project, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of the XFree86 Project shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from the + * XFree86 Project. + * + * Author: Paulo César Pereira de Andrade + */ + +/* $XFree86: xc/programs/xedit/lisp/bytecode.c,v 1.15 2003/01/29 03:05:53 paulo Exp $ */ + + +/* +somethings TODO: + + o Write code for allowing storing the bytecode on disk. Basically + write a section of the bytecode with the package name of the symbol + pointers, and after that, the symbols used. At load time just put + the pointers in the bytecode. Constants can be stored as the string + representation. Probably just storing the gc protected code as a + string is enough to rebuild it. + + o Write code to store tags of BLOCK/CATCH and setjump buffer stacks, + and, only keep track of this if non byte-compiled code is called, + as after byte-compilation RETURN and THROW are just jumps. + + o Remove not reliable "optmization code" code from Com_XXX functions + and do it later, removing dead code, tests with a constant argument, + etc, in the "link time". Frequently joining sequential opcodes to a + compound version. + + o Write an optimizer to do code transformation. + + o Write code to know when variables can be changed in place, this + can save a huge time in loop counters. + + o Write code for fast garbage collection of objects that can be + safely collected. + + o Cleanup of interpreted code. Having bytecode mean that the interpreter + now is better having a clean and small code. If speed is important, + it should be byte compiled. + + o Limit the stacks length. So that instead of using an index, use the + pointer where an object value should be read/stored as the stack address + would not change during the program execution. + + o Optimize jump to jump. Common in code like: + (IF test + (GO somewhere) + (GO elsewhere) + ) + (GO there) + that generates a bytecode like: + <code to evaluate test> + JUMPNIL :NIL-RESULT + :T-RESULT + JUMP :SOMEWHERE + JUMP :END-OF-IF ;; <- this is not required, or even + :NIL-RESULT ;; better, notice the jump after + JUMP :ELSEWHERE ;; the if and transform it into + :END-OF-IF ;; a JUMP :THERE (assuming there + JUMP :THERE ;; (was no jump in the T code). + + o Optimize variables that are known to not change it's value, i.e. pseudo + constants. Loading the value of a constant should be faster than loading + the current value of a variable; the constant table could fit in the + processor cache line and needs less calculation to find the object address. + + o Fix some known problems, like when calling return or return-from while + building the argument list to a builtin function, or inline of recursive + functions. + */ + + +#include "bytecode.h" +#include "write.h" + +#define SYMBOL_KEYWORD -1 /* A keyword, load as constant */ +#define SYMBOL_CONSTANT -2 /* Defined as constant at compile time */ +#define SYMBOL_UNBOUND -3 /* Not a local variable */ + +#define NEW_TREE(type) CompileNewTree(com, type) + +/* If in tagbody, ignore anything that is not code */ +#define IN_TAGBODY() (com->block->type == LispBlockBody && \ + com->level == com->tagbody) +#define FORM_ENTER() ++com->level +#define FORM_LEAVE() --com->level + +#define COMPILE_FAILURE(message) \ + LispMessage("COMPILE: %s", message); \ + longjmp(com->jmp, 1) + +/* + * Types + */ +typedef struct _CodeTree CodeTree; +typedef struct _CodeBlock CodeBlock; + +typedef enum { + CodeTreeBytecode, + CodeTreeLabel, + CodeTreeGo, + CodeTreeJump, + CodeTreeJumpIf, + CodeTreeCond, + CodeTreeBlock, + CodeTreeReturn +} CodeTreeType; + +struct _CodeTree { + CodeTreeType type; + + /* Resolved when linking, may be adjusted while optimizing */ + long offset; + + LispByteOpcode code; + + union { + signed char signed_char; + signed short signed_short; + signed int signed_int; + LispAtom *atom; + LispObj *object; + CodeTree *tree; + CodeBlock *block; + struct { + unsigned char num_arguments; + LispBuiltin *builtin; + signed short offset; /* Used if opcode is XBC_CALL_SET */ + } builtin; + struct { + unsigned char num_arguments; + LispObj *name; + LispObj *lambda; + } call; + struct { + unsigned char num_arguments; + LispObj *code; + } bytecall; + struct { + short offset; + LispAtom *name; + } let; + struct { + LispAtom *symbol; + LispAtom *name; + } let_sym; + struct { + LispObj *object; + LispAtom *name; + } let_con; + struct { + signed short load; + signed short set; + } load_set; + struct { + LispObj *object; + signed short offset; + } load_con_set; + struct { + LispObj *car; + LispObj *cdr; + } cons; + struct { + short offset; + LispObj *definition; + } struc; + } data; + + CodeTree *next; + CodeTree *group; + CodeBlock *block; +}; + +struct _CodeBlock { + LispBlockType type; + LispObj *tag; + + struct { + LispObj **labels; + CodeTree **codes; /* Filled at link time */ + int length; + int space; + } tagbody; + + struct { + LispAtom **symbols; /* Identifiers of variables in a block */ + int *flags; /* Information about usage of the variable */ + int length; + } variables; + + int bind; /* Used in case of RETURN from LET */ + int level; /* Nesting level block was created */ + + CodeTree *tree, *tail; + CodeBlock *prev; /* Linked list as a stack */ + CodeTree *parent; /* Back reference */ +}; + +struct _LispCom { + unsigned char *bytecode; /* Bytecode generated so far */ + long length; + + CodeBlock *block, *toplevel; + + int tagbody; /* Inside a tagbody block? */ + int level; /* Nesting level */ + int macro; /* Expanding a macro? */ + + int lex; + + int warnings; + + LispObj *form, *plist; + + jmp_buf jmp; /* Used if compilation cannot be finished */ + + struct { + int cstack; /* Current number of objects in forms evaluation */ + int cbstack; + int cpstack; + int stack; /* max number of objects will be loaded in stack */ + int bstack; + int pstack; + } stack; + + struct { + /* Constant table */ + LispObj **constants; + int num_constants; + /* Symbol table */ + LispAtom **symbols; + int num_symbols; + /* Builtin table */ + LispBuiltin **builtins; + int num_builtins; + /* Bytecode table */ + LispObj **bytecodes; + int num_bytecodes; + } table; +}; + +/* + * Prototypes + */ +static LispObj *MakeBytecodeObject(LispCom*, LispObj*, LispObj*); + +static CodeTree *CompileNewTree(LispCom*, CodeTreeType); +static void CompileFreeState(LispCom*); +static void CompileFreeBlock(CodeBlock*); +static void CompileFreeTree(CodeTree*); + +static void CompileIniBlock(LispCom*, LispBlockType, LispObj*); +static void CompileFiniBlock(LispCom*); + +static void com_BytecodeChar(LispCom*, LispByteOpcode, char); +static void com_BytecodeShort(LispCom*, LispByteOpcode, short); +static void com_BytecodeObject(LispCom*, LispByteOpcode, LispObj*); +static void com_BytecodeCons(LispCom*, LispByteOpcode, LispObj*, LispObj*); + +static void com_BytecodeAtom(LispCom*, LispByteOpcode, LispAtom*); + +static void com_Bytecode(LispCom*, LispByteOpcode); + +static void com_Load(LispCom*, short); +static void com_LoadLet(LispCom*, short, LispAtom*); +static void com_LoadPush(LispCom*, short); + +static void com_Let(LispCom*, LispAtom*); + +static void com_Bind(LispCom*, short); +static void com_Unbind(LispCom*, short); + +static void com_LoadSym(LispCom*, LispAtom*); +static void com_LoadSymLet(LispCom*, LispAtom*, LispAtom*); +static void com_LoadSymPush(LispCom*, LispAtom*); + +static void com_LoadCon(LispCom*, LispObj*); +static void com_LoadConLet(LispCom*, LispObj*, LispAtom*); +static void com_LoadConPush(LispCom*, LispObj*); + +static void com_Set(LispCom*, short); +static void com_SetSym(LispCom*, LispAtom*); + +static void com_Struct(LispCom*, short, LispObj*); +static void com_Structp(LispCom*, LispObj*); + +static void com_Call(LispCom*, unsigned char, LispBuiltin*); +static void com_Bytecall(LispCom*, unsigned char, LispObj*); +static void com_Funcall(LispCom*, LispObj*, LispObj*); + +static void CompileStackEnter(LispCom*, int, int); +static void CompileStackLeave(LispCom*, int, int); + +static void LinkBytecode(LispCom*); + +static LispObj *ExecuteBytecode(unsigned char*); + + +/* Defined in lisp.c */ +void LispMoreStack(void); +void LispMoreEnvironment(void); +void LispMoreGlobals(LispPackage*); +LispObj *LispEvalBackquote(LispObj*, int); +void LispSetAtomObjectProperty(LispAtom*, LispObj*); + +/* + * Initialization + */ +extern int pagesize; + +LispObj x_cons[8]; +static LispObj *cons, *cons1, *cons2, *cons3, *cons4, *cons5, *cons6, *cons7; + +/* + * Implementation + */ +#include "compile.c" + +void +LispBytecodeInit(void) +{ + cons = &x_cons[7]; + cons->type = LispCons_t; + CDR(cons) = NIL; + cons1 = &x_cons[6]; + cons1->type = LispCons_t; + CDR(cons1) = cons; + cons2 = &x_cons[5]; + cons2->type = LispCons_t; + CDR(cons2) = cons1; + cons3 = &x_cons[4]; + cons3->type = LispCons_t; + CDR(cons3) = cons2; + cons4 = &x_cons[3]; + cons4->type = LispCons_t; + CDR(cons4) = cons3; + cons5 = &x_cons[2]; + cons5->type = LispCons_t; + CDR(cons5) = cons4; + cons6 = &x_cons[1]; + cons6->type = LispCons_t; + CDR(cons6) = cons5; + cons7 = &x_cons[0]; + cons7->type = LispCons_t; + CDR(cons7) = cons6; +} + +LispObj * +Lisp_Compile(LispBuiltin *builtin) +/* + compile name &optional definition + */ +{ + GC_ENTER(); + LispObj *result, *warnings_p, *failure_p; + + LispObj *name, *definition; + + definition = ARGUMENT(1); + name = ARGUMENT(0); + + result = name; + warnings_p = NIL; + failure_p = T; + + if (name != NIL) { + LispAtom *atom; + + CHECK_SYMBOL(name); + atom = name->data.atom; + if (atom->a_builtin || atom->a_compiled) + goto finished_compilation; + else if (atom->a_function) { + LispCom com; + int failed, *pfailed; + int lex, base, *plex, *pbase; + LispArgList *alist; + LispObj *lambda, *form, *arguments, **parguments; + LispObj **presult, **pwarnings_p, **pfailure_p, **pform; + + lambda = atom->property->fun.function; + if (definition != UNSPEC || lambda->funtype != LispFunction) + /* XXX TODO replace definition etc. */ + goto finished_compilation; + alist = atom->property->alist; + + memset(&com, 0, sizeof(LispCom)); + com.toplevel = com.block = LispCalloc(1, sizeof(CodeBlock)); + com.block->type = LispBlockClosure; + com.block->tag = name; + + /* Create a fake argument list to avoid yet another flag + * for ComCall. The value does not matter, just the fact + * that the symbol will be bound or not in the implicit + * PROGN of the function body. */ + base = alist->num_arguments - alist->auxs.num_symbols; + if (base) { + LispObj *argument; + int i, sforms; + + for (i = sforms = 0; i < alist->optionals.num_symbols; i++) + if (alist->optionals.sforms[i]) + ++sforms; + + arguments = form = NIL; + i = sforms + + alist->normals.num_symbols + alist->optionals.num_symbols; + + if (i) { + arguments = form = CONS(NIL, NIL); + GC_PROTECT(arguments); + for (--i; i > 0; i--) { + RPLACD(form, CONS(NIL, NIL)); + form = CDR(form); + } + } + + for (i = 0; i < alist->keys.num_symbols; i++) { + /* key symbol */ + if (alist->keys.keys[i]) + argument = QUOTE(alist->keys.keys[i]); + else + argument = alist->keys.symbols[i]; + + /* add key */ + if (arguments == NIL) { + arguments = form = CONS(argument, NIL); + GC_PROTECT(arguments); + } + else { + RPLACD(form, CONS(argument, NIL)); + form = CDR(form); + } + + /* add value */ + RPLACD(form, CONS(NIL, NIL)); + form = CDR(form); + + if (alist->keys.sforms[i]) { + RPLACD(form, CONS(NIL, NIL)); + form = CDR(form); + } + } + + if (alist->rest) { + if (arguments == NIL) { + arguments = form = CONS(NIL, NIL); + GC_PROTECT(arguments); + } + else { + RPLACD(form, CONS(NIL, NIL)); + form = CDR(form); + } + } + } + else + arguments = NIL; + + form = CONS(lambda->data.lambda.code, NIL); + GC_PROTECT(form); + com.form = form; + com.plist = CONS(NIL, NIL); + GC_PROTECT(com.plist); + + pfailed = &failed; + plex = &lex; + pbase = &base; + pform = &form; + presult = &result; + pwarnings_p = &warnings_p; + pfailure_p = &failure_p; + parguments = &arguments; + failed = 1; + if (setjmp(com.jmp) == 0) { + /* Save interpreter state */ + lex = com.lex = lisp__data.env.lex; + base = ComCall(&com, alist, name, arguments, 1, 0, 1); + + /* Generate code tree */ + lisp__data.env.lex = base; + ComProgn(&com, CAR(form)); + failed = 0; + } + + /* Restore interpreter state */ + lisp__data.env.lex = lex; + lisp__data.env.head = lisp__data.env.length = base; + + if (!failed) { + failure_p = NIL; + result = MakeBytecodeObject(&com, name, + lambda->data.lambda.data); + LispSetAtomCompiledProperty(atom, result); + result = name; + } + if (com.warnings) + warnings_p = FIXNUM(com.warnings); + goto finished_compilation; + } + else + goto undefined_function; + } + +undefined_function: + LispDestroy("%s: the function %s is undefined", + STRFUN(builtin), STROBJ(name)); + +finished_compilation: + RETURN(0) = warnings_p; + RETURN(1) = failure_p; + RETURN_COUNT = 2; + GC_LEAVE(); + + return (result); +} + +LispObj * +Lisp_Disassemble(LispBuiltin *builtin) +/* + disassemble function + */ +{ + int macro; + char buffer[128]; + LispAtom *atom; + LispArgList *alist; + LispBuiltin *xbuiltin; + LispObj *name, *lambda, *bytecode; + + LispObj *function; + + function = ARGUMENT(0); + + macro = 0; + alist = NULL; + xbuiltin = NULL; + name = bytecode = NULL; + + switch (OBJECT_TYPE(function)) { + case LispAtom_t: + name = function; + atom = function->data.atom; + alist = atom->property->alist; + if (atom->a_builtin) { + xbuiltin = atom->property->fun.builtin; + macro = xbuiltin->type == LispMacro; + } + else if (atom->a_compiled) + bytecode = atom->property->fun.function; + else if (atom->a_function) { + lambda = atom->property->fun.function; + macro = lambda->funtype == LispMacro; + } + else if (atom->a_defstruct && + atom->property->structure.function != STRUCT_NAME) { + if (atom->property->structure.function == STRUCT_CONSTRUCTOR) + atom = Omake_struct->data.atom; + else if (atom->property->structure.function == STRUCT_CHECK) + atom = Ostruct_type->data.atom; + else + atom = Ostruct_access->data.atom; + xbuiltin = atom->property->fun.builtin; + } + else + LispDestroy("%s: the function %s is not defined", + STRFUN(builtin), STROBJ(function)); + break; + case LispBytecode_t: + name = Olambda; + bytecode = function; + break; + case LispLambda_t: + name = Olambda; + alist = (LispArgList*)function->data.lambda.name->data.opaque.data; + break; + case LispCons_t: + if (CAR(function) == Olambda) { + function = EVAL(function); + if (OBJECT_TYPE(function) == LispLambda_t) { + name = Olambda; + alist = (LispArgList*) + function->data.lambda.name->data.opaque.data; + break; + } + } + default: + LispDestroy("%s: %s is not a function", + STRFUN(builtin), STROBJ(function)); + break; + } + + if (xbuiltin) { + LispWriteStr(NIL, "Builtin ", 8); + if (macro) + LispWriteStr(NIL, "macro ", 6); + else + LispWriteStr(NIL, "function ", 9); + } + else if (macro) + LispWriteStr(NIL, "Macro ", 6); + else + LispWriteStr(NIL, "Function ", 9); + LispWriteObject(NIL, name); + LispWriteStr(NIL, ":\n", 2); + + if (alist) { + int i; + + sprintf(buffer, "%d required argument%s", + alist->normals.num_symbols, + alist->normals.num_symbols != 1 ? "s" : ""); + LispWriteStr(NIL, buffer, strlen(buffer)); + for (i = 0; i < alist->normals.num_symbols; i++) { + LispWriteChar(NIL, i ? ',' : ':'); + LispWriteChar(NIL, ' '); + LispWriteStr(NIL, ATOMID(alist->normals.symbols[i]), + strlen(ATOMID(alist->normals.symbols[i]))); + } + LispWriteChar(NIL, '\n'); + + sprintf(buffer, "%d optional argument%s", + alist->optionals.num_symbols, + alist->optionals.num_symbols != 1 ? "s" : ""); + LispWriteStr(NIL, buffer, strlen(buffer)); + for (i = 0; i < alist->optionals.num_symbols; i++) { + LispWriteChar(NIL, i ? ',' : ':'); + LispWriteChar(NIL, ' '); + LispWriteStr(NIL, ATOMID(alist->optionals.symbols[i]), + strlen(ATOMID(alist->optionals.symbols[i]))); + } + LispWriteChar(NIL, '\n'); + + sprintf(buffer, "%d keyword parameter%s", + alist->keys.num_symbols, + alist->keys.num_symbols != 1 ? "s" : ""); + LispWriteStr(NIL, buffer, strlen(buffer)); + for (i = 0; i < alist->keys.num_symbols; i++) { + LispWriteChar(NIL, i ? ',' : ':'); + LispWriteChar(NIL, ' '); + LispWriteObject(NIL, alist->keys.symbols[i]); + } + LispWriteChar(NIL, '\n'); + + if (alist->rest) { + LispWriteStr(NIL, "Rest argument: ", 15); + LispWriteStr(NIL, ATOMID(alist->rest), + strlen(ATOMID(alist->rest))); + LispWriteChar(NIL, '\n'); + } + else + LispWriteStr(NIL, "No rest argument\n", 17); + } + + if (bytecode) { + char *ptr; + int *offsets[4]; + int i, done, j, sym0, sym1, con0, con1, bui0, byt0, strd, strf; + LispObj **constants; + LispAtom **symbols; + LispBuiltin **builtins; + unsigned char **codes; + LispObj **names; + short stack, num_constants, num_symbols, num_builtins, num_bytecodes; + unsigned char *base, *stream = bytecode->data.bytecode.bytecode->code; + + LispWriteStr(NIL, "\nBytecode header:\n", 18); + + /* Header information */ + stack = *(short*)stream; + stream += sizeof(short); + sprintf(buffer, "%d element%s used in the stack\n", + stack, stack != 1 ? "s" : ""); + LispWriteStr(NIL, buffer, strlen(buffer)); + stack = *(short*)stream; + stream += sizeof(short); + sprintf(buffer, "%d element%s used in the builtin stack\n", + stack, stack != 1 ? "s" : ""); + LispWriteStr(NIL, buffer, strlen(buffer)); + stack = *(short*)stream; + stream += sizeof(short); + sprintf(buffer, "%d element%s used in the protected stack\n", + stack, stack != 1 ? "s" : ""); + LispWriteStr(NIL, buffer, strlen(buffer)); + + num_constants = *(short*)stream; + stream += sizeof(short); + num_symbols = *(short*)stream; + stream += sizeof(short); + num_builtins = *(short*)stream; + stream += sizeof(short); + num_bytecodes = *(short*)stream; + stream += sizeof(short); + + constants = (LispObj**)stream; + stream += num_constants * sizeof(LispObj*); + symbols = (LispAtom**)stream; + stream += num_symbols * sizeof(LispAtom*); + builtins = (LispBuiltin**)stream; + stream += num_builtins * sizeof(LispBuiltin*); + codes = (unsigned char**)stream; + stream += num_bytecodes * sizeof(unsigned char*); + names = (LispObj**)stream; + stream += num_bytecodes * sizeof(LispObj*); + + for (i = 0; i < num_constants; i++) { + sprintf(buffer, "Constant %d = %s\n", i, STROBJ(constants[i])); + LispWriteStr(NIL, buffer, strlen(buffer)); + } + +/* Macro XSTRING avoids some noisy in the output, if it were defined as + * #define XSTRING(object) object ? STROBJ(object) : #<UNBOUND> + * and called as XSTRING(atom->object) + * it would also print the package name were the symbol was first defined, + * but for local variables, only the symbol string is important. */ +#define XSTRING(string) string ? string : "#<UNBOUND>" + + for (i = 0; i < num_symbols; i++) { + sprintf(buffer, "Symbol %d = %s\n", + i, XSTRING(symbols[i]->string)); + LispWriteStr(NIL, buffer, strlen(buffer)); + } + for (i = 0; i < num_builtins; i++) { + sprintf(buffer, "Builtin %d = %s\n", + i, STROBJ(builtins[i]->symbol)); + LispWriteStr(NIL, buffer, strlen(buffer)); + } + for (i = 0; i < num_bytecodes; i++) { + sprintf(buffer, "Bytecode %d = %s\n", + i, STROBJ(names[i])); + LispWriteStr(NIL, buffer, strlen(buffer)); + } + + /* Make readability slightly easier printing the names of local + * variables where it's offset is known, i.e. function arguments. */ + if (alist) { + if (alist->num_arguments == 0) + LispWriteStr(NIL, "\nNo initial stack\n", 18); + else { + int len1, len2; + + j = 0; + LispWriteStr(NIL, "\nInitial stack:\n", 16); + + for (i = 0; i < alist->normals.num_symbols; i++, j++) { + sprintf(buffer, "%d = ", j); + LispWriteStr(NIL, buffer, strlen(buffer)); + ptr = alist->normals.symbols[i]->data.atom->string; + LispWriteStr(NIL, ptr, strlen(ptr)); + LispWriteChar(NIL, '\n'); + } + + for (i = 0; i < alist->optionals.num_symbols; i++, j++) { + sprintf(buffer, "%d = ", j); + LispWriteStr(NIL, buffer, strlen(buffer)); + ptr = alist->optionals.symbols[i]->data.atom->string; + LispWriteStr(NIL, ptr, strlen(ptr)); + LispWriteChar(NIL, '\n'); + if (alist->optionals.sforms[i]) { + sprintf(buffer, "%d = ", j); + len1 = strlen(buffer); + LispWriteStr(NIL, buffer, len1); + ptr = alist->optionals.sforms[i]->data.atom->string; + len2 = strlen(ptr); + LispWriteStr(NIL, ptr, len2); + LispWriteChars(NIL, ' ', 28 - (len1 + len2)); + LispWriteStr(NIL, "; sform\n", 9); + j++; + } + } + + for (i = 0; i < alist->keys.num_symbols; i++, j++) { + sprintf(buffer, "%d = ", j); + len1 = strlen(buffer); + LispWriteStr(NIL, buffer, len1); + if (alist->keys.keys[i]) { + ptr = alist->keys.keys[i]->data.atom->string; + len2 = strlen(ptr); + LispWriteStr(NIL, ptr, strlen(ptr)); + LispWriteChars(NIL, ' ', 28 - (len1 + len2)); + LispWriteStr(NIL, "; special key", 14); + } + else { + ptr = alist->keys.symbols[i]->data.atom->string; + LispWriteStr(NIL, ptr, strlen(ptr)); + } + LispWriteChar(NIL, '\n'); + if (alist->keys.sforms[i]) { + sprintf(buffer, "%d = ", j); + len1 = strlen(buffer); + LispWriteStr(NIL, buffer, len1); + ptr = alist->keys.sforms[i]->data.atom->string; + len2 = strlen(ptr); + LispWriteStr(NIL, ptr, len2); + LispWriteChars(NIL, ' ', 28 - (len1 + len2)); + LispWriteStr(NIL, "; sform\n", 9); + j++; + } + } + + if (alist->rest) { + sprintf(buffer, "%d = ", j); + len1 = strlen(buffer); + LispWriteStr(NIL, buffer, len1); + ptr = alist->rest->data.atom->string; + len2 = strlen(ptr); + LispWriteStr(NIL, ptr, len2); + LispWriteChar(NIL, '\n'); + j++; + } + + for (i = 0; i < alist->auxs.num_symbols; i++, j++) { + sprintf(buffer, "%d = ", j); + len1 = strlen(buffer); + LispWriteStr(NIL, buffer, len1); + ptr = alist->auxs.symbols[i]->data.atom->string; + len2 = strlen(ptr); + LispWriteStr(NIL, ptr, len2); + LispWriteChars(NIL, ' ', 28 - (len1 + len2)); + LispWriteStr(NIL, "; aux\n", 7); + } + } + } + + LispWriteStr(NIL, "\nBytecode stream:\n", 18); + + base = stream; + for (done = j = 0; !done; j = 0) { + sym0 = sym1 = con0 = con1 = bui0 = byt0 = strd = strf = -1; + sprintf(buffer, "%4ld ", (long)(stream - base)); + ptr = buffer + strlen(buffer); + switch (*stream++) { + case XBC_NOOP: strcpy(ptr, "NOOP"); break; + case XBC_PRED: + strcpy(ptr, "PRED:"); + ptr += strlen(ptr); + goto predicate; + case XBC_INV: strcpy(ptr, "INV"); break; + case XBC_NIL: strcpy(ptr, "NIL"); break; + case XBC_T: strcpy(ptr, "T"); break; + case XBC_CAR: strcpy(ptr, "CAR"); break; + case XBC_CDR: strcpy(ptr, "CDR"); break; + case XBC_RPLACA:strcpy(ptr, "RPLACA"); break; + case XBC_RPLACD:strcpy(ptr, "RPLACD"); break; + case XBC_EQ: strcpy(ptr, "EQ"); break; + case XBC_EQL: strcpy(ptr, "EQL"); break; + case XBC_EQUAL: strcpy(ptr, "EQUAL"); break; + case XBC_EQUALP:strcpy(ptr, "EQUALP"); break; + case XBC_LENGTH:strcpy(ptr, "LENGTH"); break; + case XBC_LAST: strcpy(ptr, "LAST"); break; + case XBC_NTHCDR:strcpy(ptr, "NTHCDR"); break; + case XBC_PUSH: strcpy(ptr, "PUSH"); break; + case XBC_CAR_PUSH: + strcpy(ptr, "CAR&PUSH"); + break; + case XBC_CDR_PUSH: + strcpy(ptr, "CDR&PUSH"); + break; + case XBC_PUSH_NIL: + strcpy(ptr, "PUSH NIL"); + break; + case XBC_PUSH_UNSPEC: + strcpy(ptr, "PUSH #<UNSPEC>"); + break; + case XBC_PUSH_T: + strcpy(ptr, "PUSH T"); + break; + case XBC_PUSH_NIL_N: + strcpy(ptr, "PUSH NIL "); + ptr += strlen(ptr); + sprintf(ptr, "%d", (int)(*stream++)); + break; + case XBC_PUSH_UNSPEC_N: + strcpy(ptr, "PUSH #<UNSPEC> "); + ptr += strlen(ptr); + sprintf(ptr, "%d", (int)(*stream++)); + break; + case XBC_LET: + strcpy(ptr, "LET"); +/* update sym0 */ +symbol: + offsets[j++] = &sym0; +/* update <offsets> - print [byte] */ +offset: + ptr += strlen(ptr); + i = *stream++; + *(offsets[j - 1]) = i; + sprintf(ptr, " [%d]", i); + break; + case XBC_LETX: + strcpy(ptr, "LET*"); + goto symbol; + case XBC_LET_NIL: + strcpy(ptr, "LET NIL"); + goto symbol; + case XBC_LETX_NIL: + strcpy(ptr, "LET* NIL"); + goto symbol; + case XBC_LETBIND: + strcpy(ptr, "LETBIND"); +/* print byte */ +value: + ptr += strlen(ptr); + sprintf(ptr, " %d", (int)(*stream++)); + break; + case XBC_UNLET:strcpy(ptr, "UNLET"); goto value; + case XBC_LOAD: + strcpy(ptr, "LOAD"); +/* print (byte) */ +reference: + ptr += strlen(ptr); + i = *stream++; + sprintf(ptr, " (%d)", i); + break; + case XBC_LOAD_CAR: + strcpy(ptr, "LOAD&CAR"); + goto reference; + case XBC_LOAD_CDR: + strcpy(ptr, "LOAD&CDR"); + goto reference; + case XBC_LOAD_CAR_STORE: + strcpy(ptr, "LOAD&CAR&STORE"); + goto reference; + case XBC_LOAD_CDR_STORE: + strcpy(ptr, "LOAD&CDR&STORE"); + goto reference; + case XBC_LOAD_LET: + strcpy(ptr, "LOAD&LET"); +load_let: + offsets[j++] = &sym0; + i = *stream++; + ptr += strlen(ptr); + sprintf(ptr, " (%d)", i); + goto offset; + case XBC_LOAD_LETX: + strcpy(ptr, "LOAD&LET*"); + goto load_let; + case XBC_STRUCT: + strcpy(ptr, "STRUCT"); + offsets[j++] = &strf; + offsets[j++] = &strd; +/* update <offsets> - print [byte] - update <offsets> - print [byte] */ +offset_offset: + ptr += strlen(ptr); + i = *stream++; + *(offsets[j - 2]) = i; + sprintf(ptr, " [%d]", i); + goto offset; + case XBC_LOAD_PUSH: + strcpy(ptr, "LOAD&PUSH"); + goto reference; + case XBC_LOADCON: + strcpy(ptr, "LOADCON"); +constant: + offsets[j++] = &con0; + goto offset; + case XBC_LOADCON_SET: + strcpy(ptr, "LOADCON&SET"); + offsets[j++] = &con0; +/* update <offsets> - print [byte] - print (byte) */ +offset_reference: + i = *stream++; + *(offsets[j - 1]) = i; + ptr += strlen(ptr); + sprintf(ptr, " [%d]", i); + goto reference; + case XBC_STRUCTP: + strcpy(ptr, "STRUCTP"); + offsets[j++] = &strd; + goto offset; + case XBC_LOADCON_LET: + strcpy(ptr, "LOADCON&LET"); +loadcon_let: + offsets[j++] = &con0; + offsets[j++] = &sym0; + goto offset_offset; + case XBC_LOADCON_LETX: + strcpy(ptr, "LOADCON&LET*"); + goto loadcon_let; + case XBC_LOADCON_PUSH: + strcpy(ptr, "LOADCON&PUSH"); + goto constant; + case XBC_LOADSYM: + strcpy(ptr, "LOADSYM"); + goto symbol; + case XBC_LOADSYM_LET: + strcpy(ptr, "LOADSYM&LET"); +loadsym_let: + offsets[j++] = &sym0; + offsets[j++] = &sym1; + goto offset_offset; + case XBC_LOADSYM_LETX: + strcpy(ptr, "LOADSYM&LET*"); + goto loadsym_let; + case XBC_LOADSYM_PUSH: + strcpy(ptr, "LOADSYM&PUSH"); + goto symbol; + case XBC_LOAD_SET: + strcpy(ptr, "LOAD&SET"); +/* print (byte) - print (byte) */ +reference_reference: + ptr += strlen(ptr); + i = *stream++; + sprintf(ptr, " (%d)", i); + goto reference; + case XBC_LOAD_CAR_SET: + strcpy(ptr, "LOAD&CAR&SET"); + goto reference_reference; + case XBC_LOAD_CDR_SET: + strcpy(ptr, "LOAD&CDR&SET"); + goto reference_reference; + case XBC_CAR_SET: + strcpy(ptr, "CAR&SET"); + goto reference; + case XBC_CDR_SET: + strcpy(ptr, "CDR&SET"); + goto reference; + case XBC_SET: + strcpy(ptr, "SET"); + goto reference; + case XBC_SETSYM: + strcpy(ptr, "SETSYM"); + goto symbol; + case XBC_SET_NIL: + strcpy(ptr, "SET NIL"); + goto reference; + case XBC_CALL: + strcpy(ptr, "CALL"); + ptr += strlen(ptr); + sprintf(ptr, " %d", (int)(*stream++)); + offsets[j++] = &bui0; + goto offset; + case XBC_CALL_SET: + strcpy(ptr, "CALL&SET"); + ptr += strlen(ptr); + sprintf(ptr, " %d", (int)(*stream++)); + offsets[j++] = &bui0; + goto offset_reference; + case XBC_BYTECALL: + strcpy(ptr, "BYTECALL"); + ptr += strlen(ptr); + sprintf(ptr, " %d", (int)(*stream++)); + offsets[j++] = &byt0; + goto offset; + case XBC_FUNCALL: + strcpy(ptr, "FUNCALL"); +constant_constant: + offsets[j++] = &con0; + offsets[j++] = &con1; + goto offset_offset; + case XBC_CCONS: + strcpy(ptr, "CCONS"); + goto constant_constant; + case XBC_CSTAR: strcpy(ptr, "CSTAR"); break; + case XBC_CFINI: strcpy(ptr, "CFINI"); break; + case XBC_LSTAR: strcpy(ptr, "LSTAR"); break; + case XBC_LCONS: strcpy(ptr, "LCONS"); break; + case XBC_LFINI: strcpy(ptr, "LFINI"); break; + case XBC_BCONS: strcpy(ptr, "BCONS"); break; + case XBC_BCONS1: case XBC_BCONS2: case XBC_BCONS3: + case XBC_BCONS4: case XBC_BCONS5: case XBC_BCONS6: + case XBC_BCONS7: + strcpy(ptr, "BCONS"); + ptr += strlen(ptr); + sprintf(ptr, "%d", (int)(stream[-1] - XBC_BCONS)); + break; + case XBC_JUMP: + strcpy(ptr, "JUMP"); +integer: + ptr += strlen(ptr); + sprintf(ptr, " %d", *(signed short*)stream); + stream += sizeof(short); + break; + case XBC_JUMPT: + strcpy(ptr, "JUMPT"); + goto integer; + case XBC_JUMPNIL: + strcpy(ptr, "JUMPNIL"); + goto integer; + case XBC_LETREC: + strcpy(ptr, "LETREC"); + ptr += strlen(ptr); + sprintf(ptr, " %d", (int)*stream++); + break; + case XBC_RETURN: + strcpy(ptr, "RETURN"); + done = 1; + break; + } + i = ptr - buffer + strlen(ptr); + LispWriteStr(NIL, buffer, i); + if (j) { + + /* Pad */ + LispWriteChars(NIL, ' ', 28 - i); + LispWriteChar(NIL, ';'); + + ptr = buffer; + + /* Structure */ + if (strf >= 0) { + /* strd is valid if strf set */ + LispObj *fields = constants[strd]; + + for (; strf >= 0; strf--) + fields = CDR(fields); + strcpy(ptr, " "); ptr += 2; + strcpy(ptr, CAR(fields)->data.atom->string); + ptr += strlen(ptr); + } + if (strd >= 0) { + strcpy(ptr, " "); ptr += 2; + strcpy(ptr, STROBJ(CAR(constants[strd]))); + ptr += strlen(ptr); + } + + /* Constants */ + if (con0 >= 0) { + strcpy(ptr, " "); ptr += 2; + strcpy(ptr, STROBJ(constants[con0])); + ptr += strlen(ptr); + if (con1 >= 0) { + strcpy(ptr, " "); ptr += 2; + strcpy(ptr, STROBJ(constants[con1])); + ptr += strlen(ptr); + } + } + + /* Builtin */ + if (bui0 >= 0) { + strcpy(ptr, " "); ptr += 2; + strcpy(ptr, STROBJ(builtins[bui0]->symbol)); + ptr += strlen(ptr); + } + + /* Bytecode */ + if (byt0 >= 0) { + strcpy(ptr, " "); ptr += 2; + strcpy(ptr, STROBJ(names[byt0])); + ptr += strlen(ptr); + } + + /* Symbols */ + if (sym0 >= 0) { + strcpy(ptr, " "); ptr += 2; + strcpy(ptr, XSTRING(symbols[sym0]->string)); + ptr += strlen(ptr); + if (sym1 >= 0) { + strcpy(ptr, " "); ptr += 2; + strcpy(ptr, XSTRING(symbols[sym1]->string)); + ptr += strlen(ptr); + } + } + + i = ptr - buffer; + LispWriteStr(NIL, buffer, i); + } + LispWriteChar(NIL, '\n'); + continue; +predicate: + switch (*stream++) { + case XBP_CONSP: strcpy(ptr, "CONSP"); break; + case XBP_LISTP: strcpy(ptr, "LISTP"); break; + case XBP_NUMBERP: strcpy(ptr, "NUMBERP"); break; + } + LispWriteStr(NIL, buffer, ptr - buffer + strlen(ptr)); + LispWriteChar(NIL, '\n'); + } +#undef XSTRING + } + + return (function); +} + + + +LispObj * +LispCompileForm(LispObj *form) +{ + GC_ENTER(); + int failed, *pfailed; + LispCom com; + LispObj *code, **pform; + + if (!CONSP(form)) + /* Incorrect call or NIL */ + return (form); + + memset(&com, 0, sizeof(LispCom)); + + com.toplevel = com.block = LispCalloc(1, sizeof(CodeBlock)); + com.block->type = LispBlockNone; + com.lex = lisp__data.env.lex; + + com.plist = CONS(NIL, NIL); + GC_PROTECT(com.plist); + + pfailed = &failed; + pform = &form; + failed = 1; + if (setjmp(com.jmp) == 0) { + for (code = form; CONSP(form); form = CDR(form)) { + com.form = form; + ComEval(&com, CAR(form)); + } + failed = 0; + } + GC_LEAVE(); + + return (failed ? NIL : MakeBytecodeObject(&com, NIL, NIL)); +} + +LispObj * +LispExecuteBytecode(LispObj *object) +{ + if (!BYTECODEP(object)) + return (EVAL(object)); + + return (ExecuteBytecode(object->data.bytecode.bytecode->code)); +} + +static LispObj * +MakeBytecodeObject(LispCom *com, LispObj *name, LispObj *plist) +{ + LispObj *object; + LispBytecode *bytecode; + + GC_ENTER(); + unsigned char *stream; + short i, num_constants; + LispObj **constants, *code, *cons, *prev; + + /* Resolve dependencies, optimize and create byte stream */ + LinkBytecode(com); + + object = LispNew(NIL, NIL); + GC_PROTECT(object); + bytecode = LispMalloc(sizeof(LispBytecode)); + bytecode->code = com->bytecode; + bytecode->length = com->length; + + + stream = bytecode->code; + + /* Skip stack information */ + stream += sizeof(short) * 3; + + /* Get information */ + num_constants = *(short*)stream; + stream += sizeof(short) * 4; + constants = (LispObj**)stream; + + GC_PROTECT(plist); + code = cons = prev = NIL; + for (i = 0; i < num_constants; i++) { + if (POINTERP(constants[i]) && !XSYMBOLP(constants[i])) { + if (code == NIL) { + code = cons = prev = CONS(constants[i], NIL); + GC_PROTECT(code); + } + else { + RPLACD(cons, CONS(constants[i], NIL)); + prev = cons; + cons = CDR(cons); + } + } + } + + /* Protect this in case the function is redefined */ + for (i = 0; i < com->table.num_bytecodes; i++) { + if (code == NIL) { + code = cons = prev = CONS(com->table.bytecodes[i], NIL); + GC_PROTECT(code); + } + else { + RPLACD(cons, CONS(com->table.bytecodes[i], NIL)); + prev = cons; + cons = CDR(cons); + } + } + + /* Free everything, but the LispCom structure and the generated bytecode */ + CompileFreeState(com); + + /* Allocate the minimum required number of cons cells to protect objects */ + if (!CONSP(code)) + code = plist; + else if (CONSP(plist)) { + if (code == cons) + RPLACD(code, plist); + else + RPLACD(cons, plist); + } + else { + if (code == cons) + code = CAR(code); + else + CDR(prev) = CAR(cons); + } + + object->data.bytecode.bytecode = bytecode; + /* Byte code references this object, so it cannot be garbage collected */ + object->data.bytecode.code = code; + object->data.bytecode.name = name; + object->type = LispBytecode_t; + + LispMused(bytecode); + LispMused(bytecode->code); + GC_LEAVE(); + + return (object); +} + +static void +CompileFreeTree(CodeTree *tree) +{ + if (tree->type == CodeTreeBlock) + CompileFreeBlock(tree->data.block); + LispFree(tree); +} + +static void +CompileFreeBlock(CodeBlock *block) +{ + CodeTree *tree = block->tree, *next; + + while (tree) { + next = tree->next; + CompileFreeTree(tree); + tree = next; + } + if (block->type == LispBlockBody) { + LispFree(block->tagbody.labels); + LispFree(block->tagbody.codes); + } + LispFree(block->variables.symbols); + LispFree(block->variables.flags); + LispFree(block); +} + +static void +CompileFreeState(LispCom *com) +{ + CompileFreeBlock(com->block); + LispFree(com->table.constants); + LispFree(com->table.symbols); + LispFree(com->table.builtins); + LispFree(com->table.bytecodes); +} + +/* XXX Put a breakpoint here when changing the macro expansion code. + * No opcodes should be generated during macro expansion. */ +static CodeTree * +CompileNewTree(LispCom *com, CodeTreeType type) +{ + CodeTree *tree = LispMalloc(sizeof(CodeTree)); + + tree->type = type; + tree->next = NULL; + tree->block = com->block; + if (com->block->tree == NULL) + com->block->tree = tree; + else + com->block->tail->next = tree; + com->block->tail = tree; + + return (tree); +} + +static void +CompileIniBlock(LispCom *com, LispBlockType type, LispObj *tag) +{ + CodeTree *tree = NEW_TREE(CodeTreeBlock); + CodeBlock *block = LispCalloc(1, sizeof(CodeBlock)); + + tree->data.block = block; + + block->type = type; + block->tag = tag; + block->prev = com->block; + block->parent = tree; + block->level = com->level; + com->block = block; + + if (type == LispBlockBody) + com->tagbody = com->level; +} + +static void +CompileFiniBlock(LispCom *com) +{ + com->block = com->block->prev; + if (com->block && com->block->type == LispBlockBody) + com->tagbody = com->block->level; +} + +static void +com_BytecodeChar(LispCom *com, LispByteOpcode code, char value) +{ + CodeTree *tree = NEW_TREE(CodeTreeBytecode); + + tree->code = code; + tree->data.signed_char = value; +} + +static void +com_BytecodeShort(LispCom *com, LispByteOpcode code, short value) +{ + CodeTree *tree = NEW_TREE(CodeTreeBytecode); + + tree->code = code; + tree->data.signed_short = value; +} + +static void +com_BytecodeAtom(LispCom *com, LispByteOpcode code, LispAtom *atom) +{ + CodeTree *tree = NEW_TREE(CodeTreeBytecode); + + tree->code = code; + tree->data.atom = atom; +} + +static void +com_BytecodeObject(LispCom *com, LispByteOpcode code, LispObj *object) +{ + CodeTree *tree = NEW_TREE(CodeTreeBytecode); + + tree->code = code; + tree->data.object = object; +} + +static void +com_BytecodeCons(LispCom *com, LispByteOpcode code, LispObj *car, LispObj *cdr) +{ + CodeTree *tree = NEW_TREE(CodeTreeBytecode); + + tree->code = code; + tree->data.cons.car = car; + tree->data.cons.cdr = cdr; +} + +static void +com_Bytecode(LispCom *com, LispByteOpcode code) +{ + CodeTree *tree = NEW_TREE(CodeTreeBytecode); + + tree->code = code; +} + +static void +com_Load(LispCom *com, short offset) +{ + com_BytecodeShort(com, XBC_LOAD, offset); +} + +static void +com_LoadLet(LispCom *com, short offset, LispAtom *name) +{ + CodeTree *tree = NEW_TREE(CodeTreeBytecode); + + tree->code = XBC_LOAD_LET; + tree->data.let.offset = offset; + tree->data.let.name = name; +} + +static void +com_LoadPush(LispCom *com, short offset) +{ + com_BytecodeShort(com, XBC_LOAD_PUSH, offset); +} + +static void +com_Let(LispCom *com, LispAtom *name) +{ + com_BytecodeAtom(com, XBC_LET, name); +} + +static void +com_Bind(LispCom *com, short count) +{ + if (count) + com_BytecodeShort(com, XBC_LETBIND, count); +} + +static void +com_Unbind(LispCom *com, short count) +{ + if (count) + com_BytecodeShort(com, XBC_UNLET, count); +} + +static void +com_LoadSym(LispCom *com, LispAtom *atom) +{ + com_BytecodeAtom(com, XBC_LOADSYM, atom); +} + +static void +com_LoadSymLet(LispCom *com, LispAtom *symbol, LispAtom *name) +{ + CodeTree *tree = NEW_TREE(CodeTreeBytecode); + + tree->code = XBC_LOADSYM_LET; + tree->data.let_sym.symbol = symbol; + tree->data.let_sym.name = name; +} + +static void +com_LoadSymPush(LispCom *com, LispAtom *name) +{ + com_BytecodeAtom(com, XBC_LOADSYM_PUSH, name); +} + +static void +com_LoadCon(LispCom *com, LispObj *constant) +{ + if (constant == NIL) + com_Bytecode(com, XBC_NIL); + else if (constant == T) + com_Bytecode(com, XBC_T); + else if (constant == UNSPEC) { + COMPILE_FAILURE("internal error: loading #<UNSPEC>"); + } + else + com_BytecodeObject(com, XBC_LOADCON, constant); +} + +static void +com_LoadConLet(LispCom *com, LispObj *constant, LispAtom *name) +{ + if (constant == NIL) + com_BytecodeAtom(com, XBC_LET_NIL, name); + else { + CodeTree *tree = NEW_TREE(CodeTreeBytecode); + + tree->code = XBC_LOADCON_LET; + tree->data.let_con.object = constant; + tree->data.let_con.name = name; + } +} + +static void +com_LoadConPush(LispCom *com, LispObj *constant) +{ + if (constant == NIL) + com_Bytecode(com, XBC_PUSH_NIL); + else if (constant == T) + com_Bytecode(com, XBC_PUSH_T); + else if (constant == UNSPEC) + com_Bytecode(com, XBC_PUSH_UNSPEC); + else + com_BytecodeObject(com, XBC_LOADCON_PUSH, constant); +} + +static void +com_Set(LispCom *com, short offset) +{ + com_BytecodeShort(com, XBC_SET, offset); +} + +static void +com_SetSym(LispCom *com, LispAtom *symbol) +{ + com_BytecodeAtom(com, XBC_SETSYM, symbol); +} + +static void +com_Struct(LispCom *com, short offset, LispObj *definition) +{ + CodeTree *tree = NEW_TREE(CodeTreeBytecode); + + tree->code = XBC_STRUCT; + tree->data.struc.offset = offset; + tree->data.struc.definition = definition; +} + +static void +com_Structp(LispCom *com, LispObj *definition) +{ + com_BytecodeObject(com, XBC_STRUCTP, definition); +} + +static void +com_Call(LispCom *com, unsigned char num_arguments, LispBuiltin *builtin) +{ + CodeTree *tree = NEW_TREE(CodeTreeBytecode); + + tree->code = XBC_CALL; + tree->data.builtin.num_arguments = num_arguments; + tree->data.builtin.builtin = builtin; +} + +static void +com_Bytecall(LispCom *com, unsigned char num_arguments, LispObj *code) +{ + CodeTree *tree = NEW_TREE(CodeTreeBytecode); + + tree->code = XBC_BYTECALL; + tree->data.bytecall.num_arguments = num_arguments; + tree->data.bytecall.code = code; +} + +static void +com_Funcall(LispCom *com, LispObj *function, LispObj *arguments) +{ + com_BytecodeCons(com, XBC_FUNCALL, function, arguments); +} + +static void +CompileStackEnter(LispCom *com, int count, int builtin) +{ + if (!com->macro) { + if (builtin) { + com->stack.cbstack += count; + if (com->stack.bstack < com->stack.cbstack) + com->stack.bstack = com->stack.cbstack; + } + else { + com->stack.cstack += count; + if (com->stack.stack < com->stack.cstack) + com->stack.stack = com->stack.cstack; + } + } +} + +static void +CompileStackLeave(LispCom *com, int count, int builtin) +{ + if (!com->macro) { + if (builtin) + com->stack.cbstack -= count; + else + com->stack.cstack -= count; + } +} + +static void +LinkWarnUnused(LispCom *com, CodeBlock *block) +{ + int i; + CodeTree *tree; + + for (tree = block->tree; tree; tree = tree->next) { + if (tree->type == CodeTreeBlock) + LinkWarnUnused(com, tree->data.block); + } + + for (i = 0; i < block->variables.length; i++) + if (!(block->variables.flags[i] & (VARIABLE_USED | VARIABLE_ARGUMENT))) { + ++com->warnings; + LispWarning("the variable %s is unused", + block->variables.symbols[i]->string); + } +} + +#define INTERNAL_ERROR_STRING "COMPILE: internal error #%d" +#define INTERNAL_ERROR(value) LispDestroy(INTERNAL_ERROR_STRING, value) +static long +LinkBuildOffsets(LispCom *com, CodeTree *tree, long offset) +{ + for (; tree; tree = tree->next) { + tree->offset = offset; + switch (tree->type) { + case CodeTreeBytecode: + switch (tree->code) { + case XBC_NOOP: + INTERNAL_ERROR(__LINE__); + break; + + /* byte */ + case XBC_BCONS: + case XBC_BCONS1: + case XBC_BCONS2: + case XBC_BCONS3: + case XBC_BCONS4: + case XBC_BCONS5: + case XBC_BCONS6: + case XBC_BCONS7: + case XBC_INV: + case XBC_NIL: + case XBC_T: + case XBC_PUSH: + case XBC_CAR_PUSH: + case XBC_CDR_PUSH: + case XBC_PUSH_NIL: + case XBC_PUSH_UNSPEC: + case XBC_PUSH_T: + case XBC_LSTAR: + case XBC_LCONS: + case XBC_LFINI: + case XBC_RETURN: + case XBC_CSTAR: + case XBC_CFINI: + case XBC_CAR: + case XBC_CDR: + case XBC_RPLACA: + case XBC_RPLACD: + case XBC_EQ: + case XBC_EQL: + case XBC_EQUAL: + case XBC_EQUALP: + case XBC_LENGTH: + case XBC_LAST: + case XBC_NTHCDR: + ++offset; + break; + + /* byte + byte */ + case XBC_PUSH_NIL_N: + case XBC_PUSH_UNSPEC_N: + case XBC_PRED: + case XBC_LETREC: + case XBC_LOAD_PUSH: + case XBC_CAR_SET: + case XBC_CDR_SET: + case XBC_SET: + case XBC_SET_NIL: + case XBC_LETBIND: + case XBC_UNLET: + case XBC_LOAD: + case XBC_LOAD_CAR: + case XBC_LOAD_CDR: + case XBC_LOAD_CAR_STORE: + case XBC_LOAD_CDR_STORE: + case XBC_LET: + case XBC_LETX: + case XBC_LET_NIL: + case XBC_LETX_NIL: + case XBC_STRUCTP: + case XBC_SETSYM: + case XBC_LOADCON_PUSH: + case XBC_LOADSYM_PUSH: + case XBC_LOADCON: + case XBC_LOADSYM: + offset += 2; + break; + + /* byte + byte + byte */ + case XBC_CALL: + case XBC_BYTECALL: + case XBC_LOAD_SET: + case XBC_LOAD_CAR_SET: + case XBC_LOAD_CDR_SET: + case XBC_LOADCON_SET: + case XBC_LOAD_LET: + case XBC_LOAD_LETX: + case XBC_STRUCT: + case XBC_LOADCON_LET: + case XBC_LOADCON_LETX: + case XBC_LOADSYM_LET: + case XBC_LOADSYM_LETX: + case XBC_CCONS: + case XBC_FUNCALL: + offset += 3; + break; + + /* byte + short */ + case XBC_JUMP: + case XBC_JUMPT: + case XBC_JUMPNIL: + /* XXX this is likely a jump to random address here */ + INTERNAL_ERROR(__LINE__); + offset += sizeof(short) + 1; + break; + + /* byte + byte + byte + byte */ + case XBC_CALL_SET: + offset += 4; + break; + } + break; + case CodeTreeLabel: + /* Labels are not loaded */ + break; + case CodeTreeJump: + case CodeTreeJumpIf: + case CodeTreeCond: + /* If not the point where the conditional block finishes */ + if (tree->code != XBC_NOOP) + /* Reserve space for the jump opcode */ + offset += sizeof(short) + 1; + break; + case CodeTreeGo: + case CodeTreeReturn: + /* Reserve space for the jump opcode */ + offset += sizeof(short) + 1; + break; + case CodeTreeBlock: + offset = LinkBuildOffsets(com, tree->data.block->tree, offset); + break; + } + } + + return (offset); +} + +static void +LinkDoOptimize_0(LispCom *com, CodeBlock *block) +{ + CodeTree *tree, *prev, *next; + + /* Remove redundant or join opcodes that can be joined. Do it here + * because some of these are hard to detect earlier, and/or would + * require a lot of duplicated code or more time. */ + tree = prev = block->tree; + while (tree) { + next = tree->next; + + /* LET -> LET* */ + if (next && + next->type == CodeTreeBytecode && + next->code == XBC_LETBIND && + next->data.signed_short == 1) { + switch (tree->code) { + case XBC_LET: + tree->code = XBC_LETX; + goto remove_next_label; + case XBC_LET_NIL: + tree->code = XBC_LETX_NIL; + goto remove_next_label; + case XBC_LOAD_LET: + tree->code = XBC_LOAD_LETX; + goto remove_next_label; + case XBC_LOADCON_LET: + tree->code = XBC_LOADCON_LETX; + goto remove_next_label; + case XBC_LOADSYM_LET: + tree->code = XBC_LOADSYM_LETX; + goto remove_next_label; + default: + break; + } + } + + switch (tree->type) { + case CodeTreeBytecode: + switch (tree->code) { + case XBC_LOADCON: + if (next && next->type == CodeTreeBytecode) { + switch (next->code) { + case XBC_LET: + next->code = XBC_LOADCON_LET; + next->data.let_con.name = + next->data.atom; + next->data.let_con.object = + tree->data.object; + goto remove_label; + case XBC_PUSH: + next->code = XBC_LOADCON_PUSH; + next->data.object = tree->data.object; + goto remove_label; + case XBC_CAR: + if (tree->data.object != NIL) { + if (!CONSP(tree->data.object)) + LispDestroy("CAR: %s is not a list", + STROBJ( + tree->data.object)); + next->code = XBC_LOADCON; + next->data.object = + CAR(tree->data.object); + } + goto remove_label; + case XBC_CDR: + if (tree->data.object != NIL) { + if (!CONSP(tree->data.object)) + LispDestroy("CAR: %s is not a list", + STROBJ( + tree->data.object)); + next->code = XBC_LOADCON; + next->data.object = + CDR(tree->data.object); + } + goto remove_label; + case XBC_SET: + next->code = XBC_LOADCON_SET; + next->data.load_con_set.offset = + next->data.signed_short; + next->data.load_con_set.object = + tree->data.object; + goto remove_label; + default: + break; + } + } + break; + case XBC_LOADSYM: + if (next && next->type == CodeTreeBytecode) { + switch (next->code) { + case XBC_LET: + next->code = XBC_LOADSYM_LET; + next->data.let_sym.name = + next->data.atom; + next->data.let_sym.symbol = + tree->data.atom; + goto remove_label; + case XBC_PUSH: + next->code = XBC_LOADSYM_PUSH; + next->data.atom = tree->data.atom; + goto remove_label; + default: + break; + } + } + break; + case XBC_LOAD: + if (next && next->type == CodeTreeBytecode) { + switch (next->code) { + case XBC_SET: + next->code = XBC_LOAD_SET; + next->data.load_set.set = + next->data.signed_short; + next->data.load_set.load = + tree->data.signed_short; + goto remove_label; + /* TODO add XBC_LOAD_SETSYM */ + case XBC_CAR: + next->code = XBC_LOAD_CAR; + next->data.signed_short = + tree->data.signed_short; + goto remove_label; + case XBC_CDR: + next->code = XBC_LOAD_CDR; + next->data.signed_short = + tree->data.signed_short; + goto remove_label; + case XBC_PUSH: + tree->code = XBC_LOAD_PUSH; + goto remove_next_label; + case XBC_LET: + next->code = XBC_LOAD_LET; + next->data.let.name = next->data.atom; + next->data.let.offset = + tree->data.signed_short; + goto remove_label; + default: + break; + } + } + break; + case XBC_LOAD_CAR: + if (next && next->type == CodeTreeBytecode && + next->code == XBC_SET) { + if (next->data.signed_short == + tree->data.signed_short) + next->code = XBC_LOAD_CAR_STORE; + else { + next->code = XBC_LOAD_CAR_SET; + next->data.load_set.set = + next->data.signed_short; + next->data.load_set.load = + tree->data.signed_short; + } + goto remove_label; + } + break; + case XBC_LOAD_CDR: + if (next && next->type == CodeTreeBytecode && + next->code == XBC_SET) { + if (next->data.signed_short == + tree->data.signed_short) + next->code = XBC_LOAD_CDR_STORE; + else { + next->code = XBC_LOAD_CDR_SET; + next->data.load_set.set = + next->data.signed_short; + next->data.load_set.load = + tree->data.signed_short; + } + goto remove_label; + } + break; + case XBC_CALL: + if (next && next->type == CodeTreeBytecode) { + switch (next->code) { + case XBC_SET: + next->code = XBC_CALL_SET; + next->data.builtin.offset = + next->data.signed_short; + next->data.builtin.num_arguments = + tree->data.builtin.num_arguments; + next->data.builtin.builtin = + tree->data.builtin.builtin; + goto remove_label; + /* TODO add XBC_CALL_SETSYM */ + default: + break; + } + } + break; + case XBC_CAR: + if (next && next->type == CodeTreeBytecode) { + switch (next->code) { + case XBC_SET: + next->code = XBC_CAR_SET; + goto remove_label; + /* TODO add XBC_CAR_SETSYM */ + case XBC_PUSH: + next->code = XBC_CAR_PUSH; + goto remove_label; + default: + break; + } + } + break; + case XBC_CDR: + if (next && next->type == CodeTreeBytecode) { + switch (next->code) { + case XBC_SET: + next->code = XBC_CDR_SET; + goto remove_label; + /* TODO add XBC_CDR_SETSYM */ + case XBC_PUSH: + next->code = XBC_CDR_PUSH; + goto remove_label; + default: + break; + } + } + break; + case XBC_NIL: + if (next && next->type == CodeTreeBytecode) { + switch (next->code) { + case XBC_SET: + next->code = XBC_SET_NIL; + goto remove_label; + /* TODO add XBC_SETSYM_NIL */ + default: + break; + } + } + break; + case XBC_PUSH_NIL: + if (next && next->type == CodeTreeBytecode && + next->code == XBC_PUSH_NIL) { + next->code = XBC_PUSH_NIL_N; + next->data.signed_char = 2; + goto remove_label; + } + break; + case XBC_PUSH_NIL_N: + if (next && next->type == CodeTreeBytecode && + next->code == XBC_PUSH_NIL) { + next->code = XBC_PUSH_NIL_N; + next->data.signed_char = tree->data.signed_char + 1; + goto remove_label; + } + break; + case XBC_PUSH_UNSPEC: + if (next && next->type == CodeTreeBytecode && + next->code == XBC_PUSH_UNSPEC) { + next->code = XBC_PUSH_UNSPEC_N; + next->data.signed_char = 2; + goto remove_label; + } + break; + case XBC_PUSH_UNSPEC_N: + if (next && next->type == CodeTreeBytecode && + next->code == XBC_PUSH_UNSPEC) { + next->code = XBC_PUSH_UNSPEC_N; + next->data.signed_char = tree->data.signed_char + 1; + goto remove_label; + } + break; + default: + break; + } + break; + case CodeTreeBlock: + LinkDoOptimize_0(com, tree->data.block); + break; + default: + break; + } + goto update_label; +remove_label: + if (tree == block->tree) { + block->tree = prev = next; + if (tree == block->tail) + block->tail = tree; + } + else + prev->next = next; + CompileFreeTree(tree); + tree = next; + continue; +remove_next_label: + tree->next = next->next; + CompileFreeTree(next); + continue; +update_label: + prev = tree; + tree = tree->next; + } +} + +static void +LinkOptimize_0(LispCom *com) +{ + /* Recursive */ + LinkDoOptimize_0(com, com->block); +} + +static void +LinkResolveLabels(LispCom *com, CodeBlock *block) +{ + int i; + CodeTree *tree = block->tree; + + for (; tree; tree = tree->next) { + if (tree->type == CodeTreeBlock) + LinkResolveLabels(com, tree->data.block); + else if (tree->type == CodeTreeLabel) { + for (i = 0; i < block->tagbody.length; i++) + if (tree->data.object == block->tagbody.labels[i]) { + block->tagbody.codes[i] = tree; + break; + } + } + } +} + +static void +LinkResolveJumps(LispCom *com, CodeBlock *block) +{ + int i; + CodeBlock *body = block; + CodeTree *ptr, *tree = block->tree; + + /* Check if there is a tagbody. Error checking already done */ + while (body && body->type != LispBlockBody) + body = body->prev; + + for (; tree; tree = tree->next) { + switch (tree->type) { + case CodeTreeBytecode: + case CodeTreeLabel: + break; + + case CodeTreeBlock: + LinkResolveJumps(com, tree->data.block); + break; + + case CodeTreeGo: + for (i = 0; i < body->tagbody.length; i++) + if (tree->data.object == body->tagbody.labels[i]) + break; + if (i == body->tagbody.length) + LispDestroy("COMPILE: no visible tag %s to GO", + STROBJ(tree->data.object)); + /* Now the jump code is known */ + tree->data.tree = body->tagbody.codes[i]; + break; + + case CodeTreeCond: + if (tree->code == XBC_JUMPNIL) + /* If test is NIL, go to next test */ + tree->data.tree = tree->group->next; + else if (tree->code == XBC_JUMPT) { + /* After executing code, test was T */ + for (ptr = tree->group; + ptr->code != XBC_NOOP; + ptr = ptr->group) + ; + tree->data.tree = ptr; + } + break; + + case CodeTreeJumpIf: + if (tree->code != XBC_NOOP) { + for (ptr = tree->group; + ptr->code != XBC_NOOP; + ptr = ptr->group) { + if (ptr->type == CodeTreeJump) { + /* ELSE code of IF */ + ptr = ptr->next; + /* Skip inconditional jump node */ + break; + } + } + tree->data.tree = ptr; + } + break; + + case CodeTreeJump: + if (tree->code != XBC_NOOP) + tree->data.tree = tree->group; + break; + + case CodeTreeReturn: + /* One bytecode is guaranteed to exist in the code tree */ + if (tree->data.block->parent == NULL) + /* Returning from the function or toplevel form */ + tree->data.tree = tree->data.block->tail; + else { + for (;;) { + ptr = tree->data.block->parent->next; + if (ptr) { + tree->data.tree = ptr; + break; + } + else + /* Move one BLOCK up */ + tree->data.block = tree->data.block->prev; + } + } + break; + } + } +} + +static long +LinkPad(long offset, long adjust, int preffix, int datalen) +{ + /* If byte or aligned data */ + if (datalen <= preffix || ((offset + adjust + preffix) % datalen) == 0) + return (adjust); + + return (adjust + (datalen - ((offset + adjust + preffix) % datalen))); +} + +static long +LinkFixupOffsets(LispCom *com, CodeTree *tree, long adjust) +{ + for (; tree; tree = tree->next) { + switch (tree->type) { + case CodeTreeBytecode: + switch (tree->code) { + /* byte + short */ + case XBC_JUMP: + case XBC_JUMPT: + case XBC_JUMPNIL: + adjust = LinkPad(tree->offset, adjust, 1, + sizeof(short)); + /*FALLTROUGH*/ + default: + tree->offset += adjust; + break; + } + break; + case CodeTreeLabel: + /* Labels are not loaded, just adjust offset */ + tree->offset += adjust; + break; + case CodeTreeJump: + case CodeTreeCond: + case CodeTreeJumpIf: + /* If an opcode will be generated. */ + if (tree->code != XBC_NOOP) + adjust = LinkPad(tree->offset, adjust, 1, sizeof(short)); + tree->offset += adjust; + break; + case CodeTreeGo: + case CodeTreeReturn: + adjust = LinkPad(tree->offset, adjust, 1, sizeof(short)); + tree->offset += adjust; + break; + case CodeTreeBlock: + adjust = LinkFixupOffsets(com, tree->data.block->tree, adjust); + break; + } + } + + return (adjust); +} + +static void +LinkSkipPadding(LispCom *com, CodeTree *tree) +{ + int found; + CodeTree *ptr; + + /* Recurse to adjust forward jumps or jumps to the start of the block */ + for (ptr = tree; ptr; ptr = ptr->next) { + if (ptr->type == CodeTreeBlock) { + LinkSkipPadding(com, ptr->data.block->tree); + ptr->offset = ptr->data.block->tree->offset; + } + } + + /* Adjust the nodes offsets */ + for (; tree; tree = tree->next) { + switch (tree->type) { + case CodeTreeBytecode: + case CodeTreeBlock: + case CodeTreeGo: + case CodeTreeReturn: + break; + case CodeTreeJump: + case CodeTreeCond: + case CodeTreeJumpIf: + if (tree->code != XBC_NOOP) + /* If code will be generated */ + break; + case CodeTreeLabel: + /* This should be done in reversed order, but to avoid + * the requirement of a prev pointer, do the job in a + * harder way here. */ + for (found = 0, ptr = tree->next; ptr; ptr = ptr->next) { + switch (ptr->type) { + case CodeTreeBytecode: + case CodeTreeBlock: + case CodeTreeGo: + case CodeTreeReturn: + found = 1; + break; + case CodeTreeJump: + case CodeTreeCond: + case CodeTreeJumpIf: + if (ptr->code != XBC_NOOP) + found = 1; + break; + case CodeTreeLabel: + break; + } + if (found) + break; + } + if (found) + tree->offset = ptr->offset; + break; + } + } +} + +static void +LinkCalculateJump(LispCom *com, CodeTree *tree, LispByteOpcode code) +{ + long jumpto, offset, distance; + + tree->type = CodeTreeBytecode; + /* After the opcode */ + offset = tree->offset + 1; + jumpto = tree->data.tree->offset; + /* Effective distance */ + distance = jumpto - offset; + tree->code = code; + if (distance < -32768 || distance > 32767) { + COMPILE_FAILURE("jump too long"); + } + tree->data.signed_int = distance; +} + +static void +LinkFixupJumps(LispCom *com, CodeTree *tree) +{ + for (; tree; tree = tree->next) { + switch (tree->type) { + case CodeTreeBytecode: + case CodeTreeLabel: + break; + case CodeTreeCond: + if (tree->code == XBC_JUMPNIL) + /* Go to next test if NIL */ + LinkCalculateJump(com, tree, XBC_JUMPNIL); + else if (tree->code == XBC_JUMPT) + /* After executing T code */ + LinkCalculateJump(com, tree, XBC_JUMP); + break; + case CodeTreeJumpIf: + if (tree->code != XBC_NOOP) + LinkCalculateJump(com, tree, tree->code); + break; + case CodeTreeGo: + /* Inconditional jump */ + LinkCalculateJump(com, tree, XBC_JUMP); + break; + case CodeTreeReturn: + /* Inconditional jump */ + if (tree->data.tree != tree) + /* If need to skip something */ + LinkCalculateJump(com, tree, XBC_JUMP); + break; + case CodeTreeBlock: + LinkFixupJumps(com, tree->data.block->tree); + break; + case CodeTreeJump: + if (tree->code != XBC_NOOP) + LinkCalculateJump(com, tree, tree->code); + } + } +} + +static void +LinkBuildTableSymbol(LispCom *com, LispAtom *symbol) +{ + if (BuildTablePointer(symbol, (void***)&com->table.symbols, + &com->table.num_symbols) > 0xff) { + COMPILE_FAILURE("more than 256 symbols"); + } +} + +static void +LinkBuildTableConstant(LispCom *com, LispObj *constant) +{ + if (BuildTablePointer(constant, (void***)&com->table.constants, + &com->table.num_constants) > 0xff) { + COMPILE_FAILURE("more than 256 constants"); + } +} + +static void +LinkBuildTableBuiltin(LispCom *com, LispBuiltin *builtin) +{ + if (BuildTablePointer(builtin, (void***)&com->table.builtins, + &com->table.num_builtins) > 0xff) { + COMPILE_FAILURE("more than 256 functions"); + } +} + +static void +LinkBuildTableBytecode(LispCom *com, LispObj *bytecode) +{ + if (BuildTablePointer(bytecode, (void***)&com->table.bytecodes, + &com->table.num_bytecodes) > 0xff) { + COMPILE_FAILURE("more than 256 bytecode functions"); + } +} + +static void +LinkBuildTables(LispCom *com, CodeBlock *block) +{ + CodeTree *tree; + + for (tree = block->tree; tree; tree = tree->next) { + switch (tree->type) { + case CodeTreeBytecode: + switch (tree->code) { + case XBC_LET: + case XBC_LETX: + case XBC_LET_NIL: + case XBC_LETX_NIL: + case XBC_SETSYM: + case XBC_LOADSYM: + case XBC_LOADSYM_PUSH: + LinkBuildTableSymbol(com, tree->data.atom); + break; + case XBC_STRUCTP: + case XBC_LOADCON: + case XBC_LOADCON_PUSH: + LinkBuildTableConstant(com, tree->data.object); + break; + case XBC_LOADCON_SET: + LinkBuildTableConstant(com, tree->data.load_con_set.object); + break; + case XBC_CALL: + case XBC_CALL_SET: + LinkBuildTableBuiltin(com, tree->data.builtin.builtin); + break; + case XBC_BYTECALL: + LinkBuildTableBytecode(com, tree->data.bytecall.code); + break; + case XBC_LOAD_LET: + case XBC_LOAD_LETX: + LinkBuildTableSymbol(com, tree->data.let.name); + break; + case XBC_STRUCT: + LinkBuildTableConstant(com, tree->data.struc.definition); + break; + case XBC_LOADSYM_LET: + case XBC_LOADSYM_LETX: + LinkBuildTableSymbol(com, tree->data.let_sym.symbol); + LinkBuildTableSymbol(com, tree->data.let_sym.name); + break; + case XBC_LOADCON_LET: + case XBC_LOADCON_LETX: + LinkBuildTableConstant(com, tree->data.let_con.object); + LinkBuildTableSymbol(com, tree->data.let_con.name); + break; + case XBC_CCONS: + case XBC_FUNCALL: + LinkBuildTableConstant(com, tree->data.cons.car); + LinkBuildTableConstant(com, tree->data.cons.cdr); + break; + default: + break; + } + break; + case CodeTreeBlock: + LinkBuildTables(com, tree->data.block); + break; + default: + break; + } + } +} + +static long +LinkEmmitBytecode(LispCom *com, CodeTree *tree, + unsigned char *bytecode, long offset) +{ + short i; + + for (; tree; tree = tree->next) { + /* Fill padding */ + while (offset < tree->offset) + bytecode[offset++] = XBC_NOOP; + + switch (tree->type) { + case CodeTreeBytecode: + bytecode[offset++] = tree->code; + switch (tree->code) { + /* Noop should not enter the CodeTree */ + case XBC_NOOP: + INTERNAL_ERROR(__LINE__); + break; + + /* byte */ + case XBC_BCONS: + case XBC_BCONS1: + case XBC_BCONS2: + case XBC_BCONS3: + case XBC_BCONS4: + case XBC_BCONS5: + case XBC_BCONS6: + case XBC_BCONS7: + case XBC_INV: + case XBC_NIL: + case XBC_T: + case XBC_PUSH_NIL: + case XBC_PUSH_UNSPEC: + case XBC_PUSH_T: + case XBC_CAR_PUSH: + case XBC_CDR_PUSH: + case XBC_PUSH: + case XBC_LSTAR: + case XBC_LCONS: + case XBC_LFINI: + case XBC_RETURN: + case XBC_CSTAR: + case XBC_CFINI: + case XBC_CAR: + case XBC_CDR: + case XBC_RPLACA: + case XBC_RPLACD: + case XBC_EQ: + case XBC_EQL: + case XBC_EQUAL: + case XBC_EQUALP: + case XBC_LENGTH: + case XBC_LAST: + case XBC_NTHCDR: + break; + + /* byte + byte */ + case XBC_LETREC: + case XBC_PRED: + case XBC_PUSH_NIL_N: + case XBC_PUSH_UNSPEC_N: + bytecode[offset++] = tree->data.signed_char; + break; + + /* byte + byte */ + case XBC_CAR_SET: + case XBC_CDR_SET: + case XBC_SET: + case XBC_SET_NIL: + case XBC_LETBIND: + case XBC_UNLET: + case XBC_LOAD_PUSH: + case XBC_LOAD: + case XBC_LOAD_CAR: + case XBC_LOAD_CDR: + case XBC_LOAD_CAR_STORE: + case XBC_LOAD_CDR_STORE: + bytecode[offset++] = tree->data.signed_short; + break; + + /* byte + byte + byte */ + case XBC_LOAD_SET: + case XBC_LOAD_CAR_SET: + case XBC_LOAD_CDR_SET: + bytecode[offset++] = tree->data.load_set.load; + bytecode[offset++] = tree->data.load_set.set; + break; + + /* byte + short */ + case XBC_JUMP: + case XBC_JUMPT: + case XBC_JUMPNIL: + *(short*)(bytecode + offset) = tree->data.signed_int; + offset += sizeof(short); + break; + + /* byte + byte */ + case XBC_LET: + case XBC_LETX: + case XBC_LET_NIL: + case XBC_LETX_NIL: + case XBC_SETSYM: + case XBC_LOADSYM: + case XBC_LOADSYM_PUSH: + i = FindIndex(tree->data.atom, + (void**)com->table.symbols, + com->table.num_symbols); + bytecode[offset++] = i; + break; + + /* byte + byte */ + case XBC_STRUCTP: + case XBC_LOADCON: + case XBC_LOADCON_PUSH: + i = FindIndex(tree->data.object, + (void**)com->table.constants, + com->table.num_constants); + bytecode[offset++] = i; + break; + + /* byte + byte + byte */ + case XBC_LOADCON_SET: + i = FindIndex(tree->data.load_con_set.object, + (void**)com->table.constants, + com->table.num_constants); + bytecode[offset++] = i; + bytecode[offset++] = tree->data.load_con_set.offset; + break; + + /* byte + byte + byte */ + case XBC_CALL: + bytecode[offset++] = tree->data.builtin.num_arguments; + i = FindIndex(tree->data.builtin.builtin, + (void**)com->table.builtins, + com->table.num_builtins); + bytecode[offset++] = i; + break; + + /* byte + byte + byte */ + case XBC_BYTECALL: + bytecode[offset++] = tree->data.bytecall.num_arguments; + i = FindIndex(tree->data.bytecall.code, + (void**)com->table.bytecodes, + com->table.num_bytecodes); + bytecode[offset++] = i; + break; + + /* byte + byte + byte + byte */ + case XBC_CALL_SET: + bytecode[offset++] = tree->data.builtin.num_arguments; + i = FindIndex(tree->data.builtin.builtin, + (void**)com->table.builtins, + com->table.num_builtins); + bytecode[offset++] = i; + bytecode[offset++] = tree->data.builtin.offset; + break; + + /* byte + byte + byte */ + case XBC_LOAD_LET: + case XBC_LOAD_LETX: + bytecode[offset++] = tree->data.let.offset; + i = FindIndex(tree->data.let.name, + (void**)com->table.symbols, + com->table.num_symbols); + bytecode[offset++] = i; + break; + + /* byte + byte + byte */ + case XBC_STRUCT: + bytecode[offset++] = tree->data.struc.offset; + i = FindIndex(tree->data.struc.definition, + (void**)com->table.constants, + com->table.num_constants); + bytecode[offset++] = i; + break; + + /* byte + byte + byte */ + case XBC_LOADSYM_LET: + case XBC_LOADSYM_LETX: + i = FindIndex(tree->data.let_sym.symbol, + (void**)com->table.symbols, + com->table.num_symbols); + bytecode[offset++] = i; + i = FindIndex(tree->data.let_sym.name, + (void**)com->table.symbols, + com->table.num_symbols); + bytecode[offset++] = i; + break; + + /* byte + byte + byte */ + case XBC_LOADCON_LET: + case XBC_LOADCON_LETX: + i = FindIndex(tree->data.let_con.object, + (void**)com->table.constants, + com->table.num_constants); + bytecode[offset++] = i; + i = FindIndex(tree->data.let_con.name, + (void**)com->table.symbols, + com->table.num_symbols); + bytecode[offset++] = i; + break; + + /* byte + byte + byte */ + case XBC_CCONS: + case XBC_FUNCALL: + i = FindIndex(tree->data.cons.car, + (void**)com->table.constants, + com->table.num_constants); + bytecode[offset++] = i; + i = FindIndex(tree->data.cons.cdr, + (void**)com->table.constants, + com->table.num_constants); + bytecode[offset++] = i; + break; + } + break; + case CodeTreeLabel: + /* Labels are not loaded */ + break; + case CodeTreeCond: + case CodeTreeJump: + case CodeTreeJumpIf: + if (tree->code != XBC_NOOP) + INTERNAL_ERROR(__LINE__); + break; + case CodeTreeGo: + INTERNAL_ERROR(__LINE__); + break; + case CodeTreeReturn: + if (tree->data.tree != tree) + INTERNAL_ERROR(__LINE__); + break; + case CodeTreeBlock: + offset = LinkEmmitBytecode(com, tree->data.block->tree, + bytecode, offset); + break; + } + } + + return (offset); +} + +static void +LinkBytecode(LispCom *com) +{ + long offset, count; + unsigned char **codes; + LispObj **names; + + /* Close bytecode */ + com_Bytecode(com, XBC_RETURN); + + /* The only usage of this information for now, and still may generate + * false positives because arguments to unamed functions are not being + * parsed as well as arguments to yet undefined function/maros. + * XXX should also add declaim/declare to let the code specify that + * the argument is unused */ + LinkWarnUnused(com, com->block); + + /* First level optimization */ + LinkOptimize_0(com); + + /* Resolve tagbody labels */ + LinkResolveLabels(com, com->block); + + /* Resolve any pending jumps */ + LinkResolveJumps(com, com->block); + + /* Calculate unpadded offsets */ + LinkBuildOffsets(com, com->block->tree, 0); + + /* Do padding for aligned memory reads */ + LinkFixupOffsets(com, com->block->tree, 0); + + /* Jumps normally are to a node that does not generate code, + * and due to padding, the jump may go to a address with a + * XBC_NOOP, so adjust the jump to the next useful opcode. */ + LinkSkipPadding(com, com->block->tree); + + /* Now addresses are known */ + LinkFixupJumps(com, com->block->tree); + + /* Build symbol, constant and builtin tables */ + LinkBuildTables(com, com->block); + + /* Stack info */ + com->length = sizeof(short) * 3; + /* Tables info */ + com->length += sizeof(short) * 4; + com->length += com->table.num_constants * sizeof(LispObj*); + com->length += com->table.num_symbols * sizeof(LispAtom*); + com->length += com->table.num_builtins * sizeof(LispBuiltin*); + com->length += com->table.num_bytecodes * sizeof(unsigned char*); + com->length += com->table.num_bytecodes * sizeof(LispObj*); + + /* Allocate space for the bytecode stream */ + com->length += com->block->tail->offset + 1; + com->bytecode = LispMalloc(com->length); + + /* Add header */ + offset = 0; + *(short*)(com->bytecode + offset) = com->stack.stack; + offset += sizeof(short); + *(short*)(com->bytecode + offset) = com->stack.bstack; + offset += sizeof(short); + *(short*)(com->bytecode + offset) = com->stack.pstack; + offset += sizeof(short); + + *(short*)(com->bytecode + offset) = com->table.num_constants; + offset += sizeof(short); + *(short*)(com->bytecode + offset) = com->table.num_symbols; + offset += sizeof(short); + *(short*)(com->bytecode + offset) = com->table.num_builtins; + offset += sizeof(short); + *(short*)(com->bytecode + offset) = com->table.num_bytecodes; + offset += sizeof(short); + + count = sizeof(LispObj*) * com->table.num_constants; + memcpy(com->bytecode + offset, com->table.constants, count); + offset += count; + count = sizeof(LispAtom*) * com->table.num_symbols; + memcpy(com->bytecode + offset, com->table.symbols, count); + offset += count; + count = sizeof(LispBuiltin*) * com->table.num_builtins; + memcpy(com->bytecode + offset, com->table.builtins, count); + offset += count; + + /* Store bytecode information */ + for (count = 0, codes = (unsigned char**)(com->bytecode + offset); + count < com->table.num_bytecodes; count++, codes++) + *codes = com->table.bytecodes[count]->data.bytecode.bytecode->code; + offset += com->table.num_bytecodes * sizeof(unsigned char*); + /* Store names, only useful for disassemble but may also be used + * to check if a function was redefined, and the bytecode is referencing + * the older version, the current version can be checked looking at + * <name>->data.atom */ + for (count = 0, names = (LispObj**)(com->bytecode + offset); + count < com->table.num_bytecodes; count++, names++) + *names = com->table.bytecodes[count]->data.bytecode.name; + offset += com->table.num_bytecodes * sizeof(LispObj*); + + /* Generate it */ + LinkEmmitBytecode(com, com->block->tree, com->bytecode + offset, 0); +} + +static LispObj * +ExecuteBytecode(register unsigned char *stream) +{ + register LispObj *reg0; + register LispAtom *atom; + register short offset; + LispObj *reg1; + LispBuiltin *builtin; + LispObj *lambda; + LispObj *arguments; + unsigned char *bytecode; + + LispObj **constants; + LispAtom **symbols; + LispBuiltin **builtins; + unsigned char **codes; + short num_constants, num_symbols, num_builtins, num_codes; + + int lex, len; + + /* To control gc protected slots */ + int phead, pbase; + + long fixnum = 0; + +#if defined(__GNUC__) && !defined(ANSI_SOURCE) +#define ALLOW_GOTO_ADDRESS +#endif + +#ifdef ALLOW_GOTO_ADDRESS +#define JUMP_ADDRESS(label) &&label + static const void *opcode_labels[] = { + JUMP_ADDRESS(XBC_NOOP), + JUMP_ADDRESS(XBC_INV), + JUMP_ADDRESS(XBC_NIL), + JUMP_ADDRESS(XBC_T), + JUMP_ADDRESS(XBC_PRED), + JUMP_ADDRESS(XBC_CAR), + JUMP_ADDRESS(XBC_CDR), + JUMP_ADDRESS(XBC_CAR_SET), + JUMP_ADDRESS(XBC_CDR_SET), + JUMP_ADDRESS(XBC_RPLACA), + JUMP_ADDRESS(XBC_RPLACD), + JUMP_ADDRESS(XBC_EQ), + JUMP_ADDRESS(XBC_EQL), + JUMP_ADDRESS(XBC_EQUAL), + JUMP_ADDRESS(XBC_EQUALP), + JUMP_ADDRESS(XBC_LENGTH), + JUMP_ADDRESS(XBC_LAST), + JUMP_ADDRESS(XBC_NTHCDR), + JUMP_ADDRESS(XBC_CAR_PUSH), + JUMP_ADDRESS(XBC_CDR_PUSH), + JUMP_ADDRESS(XBC_PUSH), + JUMP_ADDRESS(XBC_PUSH_NIL), + JUMP_ADDRESS(XBC_PUSH_UNSPEC), + JUMP_ADDRESS(XBC_PUSH_T), + JUMP_ADDRESS(XBC_PUSH_NIL_N), + JUMP_ADDRESS(XBC_PUSH_UNSPEC_N), + JUMP_ADDRESS(XBC_LET), + JUMP_ADDRESS(XBC_LETX), + JUMP_ADDRESS(XBC_LET_NIL), + JUMP_ADDRESS(XBC_LETX_NIL), + JUMP_ADDRESS(XBC_LETBIND), + JUMP_ADDRESS(XBC_UNLET), + JUMP_ADDRESS(XBC_LOAD), + JUMP_ADDRESS(XBC_LOAD_LET), + JUMP_ADDRESS(XBC_LOAD_LETX), + JUMP_ADDRESS(XBC_LOAD_PUSH), + JUMP_ADDRESS(XBC_LOADCON), + JUMP_ADDRESS(XBC_LOADCON_LET), + JUMP_ADDRESS(XBC_LOADCON_LETX), + JUMP_ADDRESS(XBC_LOADCON_PUSH), + JUMP_ADDRESS(XBC_LOAD_CAR), + JUMP_ADDRESS(XBC_LOAD_CDR), + JUMP_ADDRESS(XBC_LOAD_CAR_STORE), + JUMP_ADDRESS(XBC_LOAD_CDR_STORE), + JUMP_ADDRESS(XBC_LOADCON_SET), + JUMP_ADDRESS(XBC_LOADSYM), + JUMP_ADDRESS(XBC_LOADSYM_LET), + JUMP_ADDRESS(XBC_LOADSYM_LETX), + JUMP_ADDRESS(XBC_LOADSYM_PUSH), + JUMP_ADDRESS(XBC_LOAD_SET), + JUMP_ADDRESS(XBC_LOAD_CAR_SET), + JUMP_ADDRESS(XBC_LOAD_CDR_SET), + JUMP_ADDRESS(XBC_SET), + JUMP_ADDRESS(XBC_SETSYM), + JUMP_ADDRESS(XBC_SET_NIL), + JUMP_ADDRESS(XBC_CALL), + JUMP_ADDRESS(XBC_CALL_SET), + JUMP_ADDRESS(XBC_BYTECALL), + JUMP_ADDRESS(XBC_FUNCALL), + JUMP_ADDRESS(XBC_LETREC), + JUMP_ADDRESS(XBC_BCONS), + JUMP_ADDRESS(XBC_BCONS1), + JUMP_ADDRESS(XBC_BCONS2), + JUMP_ADDRESS(XBC_BCONS3), + JUMP_ADDRESS(XBC_BCONS4), + JUMP_ADDRESS(XBC_BCONS5), + JUMP_ADDRESS(XBC_BCONS6), + JUMP_ADDRESS(XBC_BCONS7), + JUMP_ADDRESS(XBC_CCONS), + JUMP_ADDRESS(XBC_CSTAR), + JUMP_ADDRESS(XBC_CFINI), + JUMP_ADDRESS(XBC_LSTAR), + JUMP_ADDRESS(XBC_LCONS), + JUMP_ADDRESS(XBC_LFINI), + JUMP_ADDRESS(XBC_JUMP), + JUMP_ADDRESS(XBC_JUMPT), + JUMP_ADDRESS(XBC_JUMPNIL), + JUMP_ADDRESS(XBC_STRUCT), + JUMP_ADDRESS(XBC_STRUCTP), + JUMP_ADDRESS(XBC_RETURN) + }; + static const void *predicate_opcode_labels[] = { + JUMP_ADDRESS(XBP_CONSP), + JUMP_ADDRESS(XBP_LISTP), + JUMP_ADDRESS(XBP_NUMBERP) + }; +#endif + + reg0 = NIL; + + bytecode = stream; + pbase = lisp__data.protect.length; + + /* stack */ + offset = *(short*)stream; + stream += sizeof(short); + if (lisp__data.env.length + offset > lisp__data.env.space) { + do + LispMoreEnvironment(); + while (lisp__data.env.length + offset >= lisp__data.env.space); + } + /* builtin stack */ + offset = *(short*)stream; + stream += sizeof(short); + if (lisp__data.stack.length + offset >= lisp__data.stack.space) { + do + LispMoreStack(); + while (lisp__data.stack.length + offset >= lisp__data.stack.space); + } + /* protect stack */ + phead = *(short*)stream; + stream += sizeof(short); + if (lisp__data.protect.length + phead > lisp__data.protect.space) { + do + LispMoreProtects(); + while (lisp__data.protect.length + phead >= lisp__data.protect.space); + } + + num_constants = *(short*)stream; + stream += sizeof(short); + num_symbols = *(short*)stream; + stream += sizeof(short); + num_builtins = *(short*)stream; + stream += sizeof(short); + num_codes = *(short*)stream; + stream += sizeof(short); + + constants = (LispObj**)stream; + stream += num_constants * sizeof(LispObj*); + symbols = (LispAtom**)stream; + stream += num_symbols * sizeof(LispAtom*); + builtins = (LispBuiltin**)stream; + stream += num_builtins * sizeof(LispBuiltin*); + codes = (unsigned char**)stream; + stream += num_codes * (sizeof(unsigned char*) + sizeof(LispObj*)); + + for (; phead > 0; phead--) + lisp__data.protect.objects[lisp__data.protect.length++] = NIL; + phead = pbase; + +#ifdef ALLOW_GOTO_ADDRESS +#define OPCODE_LABEL(label) label +#define NEXT_OPCODE() goto *opcode_labels[*stream++] +#define GOTO_PREDICATE() goto *predicate_opcode_labels[*stream++] +#else +#define OPCODE_LABEL(label) case label +#define NEXT_OPCODE() goto next_opcode +#define GOTO_PREDICATE() goto predicate_label + for (;;) { +next_opcode: + switch (*stream++) { +#endif /* ALLOW_GOTO_ADDRESS */ + +OPCODE_LABEL(XBC_NOOP): + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_PRED): + GOTO_PREDICATE(); + +OPCODE_LABEL(XBC_INV): + reg0 = reg0 == NIL ? T : NIL; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_NIL): + reg0 = NIL; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_T): + reg0 = T; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_CAR): +car: + if (reg0 != NIL) { + if (!CONSP(reg0)) + LispDestroy("CAR: %s is not a list", STROBJ(reg0)); + reg0 = CAR(reg0); + } + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_CDR): +cdr: + if (reg0 != NIL) { + if (!CONSP(reg0)) + LispDestroy("CDR: %s is not a list", STROBJ(reg0)); + reg0 = CDR(reg0); + } + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_RPLACA): + reg1 = lisp__data.stack.values[--lisp__data.stack.length]; + if (!CONSP(reg1)) + LispDestroy("RPLACA: %s is not a cons", STROBJ(reg1)); + RPLACA(reg1, reg0); + reg0 = reg1; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_RPLACD): + reg1 = lisp__data.stack.values[--lisp__data.stack.length]; + if (!CONSP(reg1)) + LispDestroy("RPLACD: %s is not a cons", STROBJ(reg1)); + RPLACD(reg1, reg0); + reg0 = reg1; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_BCONS): + CAR(cons) = reg0; + lisp__data.stack.values[lisp__data.stack.length++] = cons; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_BCONS1): + offset = lisp__data.stack.length - 1; + CAR(cons) = reg0; + CAR(cons1) = lisp__data.stack.values[offset]; + lisp__data.stack.values[offset] = cons1; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_BCONS2): + offset = lisp__data.stack.length; + CAR(cons) = reg0; + CAR(cons1) = lisp__data.stack.values[--offset]; + CAR(cons2) = lisp__data.stack.values[--offset]; + lisp__data.stack.values[offset] = cons2; + lisp__data.stack.length = offset + 1; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_BCONS3): + offset = lisp__data.stack.length; + CAR(cons) = reg0; + CAR(cons1) = lisp__data.stack.values[--offset]; + CAR(cons2) = lisp__data.stack.values[--offset]; + CAR(cons3) = lisp__data.stack.values[--offset]; + lisp__data.stack.values[offset] = cons3; + lisp__data.stack.length = offset + 1; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_BCONS4): + offset = lisp__data.stack.length; + CAR(cons) = reg0; + CAR(cons1) = lisp__data.stack.values[--offset]; + CAR(cons2) = lisp__data.stack.values[--offset]; + CAR(cons3) = lisp__data.stack.values[--offset]; + CAR(cons4) = lisp__data.stack.values[--offset]; + lisp__data.stack.values[offset] = cons4; + lisp__data.stack.length = offset + 1; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_BCONS5): + offset = lisp__data.stack.length; + CAR(cons) = reg0; + CAR(cons1) = lisp__data.stack.values[--offset]; + CAR(cons2) = lisp__data.stack.values[--offset]; + CAR(cons3) = lisp__data.stack.values[--offset]; + CAR(cons4) = lisp__data.stack.values[--offset]; + CAR(cons5) = lisp__data.stack.values[--offset]; + lisp__data.stack.values[offset] = cons5; + lisp__data.stack.length = offset + 1; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_BCONS6): + offset = lisp__data.stack.length; + CAR(cons) = reg0; + CAR(cons1) = lisp__data.stack.values[--offset]; + CAR(cons2) = lisp__data.stack.values[--offset]; + CAR(cons3) = lisp__data.stack.values[--offset]; + CAR(cons4) = lisp__data.stack.values[--offset]; + CAR(cons5) = lisp__data.stack.values[--offset]; + CAR(cons6) = lisp__data.stack.values[--offset]; + lisp__data.stack.values[offset] = cons6; + lisp__data.stack.length = offset + 1; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_BCONS7): + offset = lisp__data.stack.length; + CAR(cons) = reg0; + CAR(cons1) = lisp__data.stack.values[--offset]; + CAR(cons2) = lisp__data.stack.values[--offset]; + CAR(cons3) = lisp__data.stack.values[--offset]; + CAR(cons4) = lisp__data.stack.values[--offset]; + CAR(cons5) = lisp__data.stack.values[--offset]; + CAR(cons6) = lisp__data.stack.values[--offset]; + CAR(cons7) = lisp__data.stack.values[--offset]; + lisp__data.stack.values[offset] = cons7; + lisp__data.stack.length = offset + 1; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_EQ): + reg0 = reg0 == lisp__data.stack.values[--lisp__data.stack.length] ? T : NIL; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_EQL): + reg1 = lisp__data.stack.values[--lisp__data.stack.length]; + reg0 = XEQL(reg1, reg0); + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_EQUAL): + reg1 = lisp__data.stack.values[--lisp__data.stack.length]; + reg0 = XEQUAL(reg1, reg0); + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_EQUALP): + reg1 = lisp__data.stack.values[--lisp__data.stack.length]; + reg0 = XEQUALP(reg1, reg0); + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LENGTH): + reg0 = FIXNUM(LispLength(reg0)); + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LAST): + { + long length; + + reg1 = lisp__data.stack.values[--lisp__data.stack.length]; + if (CONSP(reg1)) { + if (reg0 != NIL) { + if (!FIXNUMP(reg0) || (fixnum = FIXNUM_VALUE(reg0)) < 0) + LispDestroy("LAST: %s is not a positive fixnum", + STROBJ(reg0)); + } + else + fixnum = 1; + reg0 = reg1; + for (reg0 = reg1, length = 0; + CONSP(reg0); + reg0 = CDR(reg0), length++) + ; + for (length -= fixnum, reg0 = reg1; length > 0; length--) + reg0 = CDR(reg0); + } + else + reg0 = reg1; + } NEXT_OPCODE(); + +OPCODE_LABEL(XBC_NTHCDR): + reg1 = lisp__data.stack.values[--lisp__data.stack.length]; + if (!FIXNUMP(reg1) || (fixnum = FIXNUM_VALUE(reg1)) < 0) + LispDestroy("NTHCDR: %s is not a positive fixnum", + STROBJ(reg1)); + if (reg0 != NIL) { + if (!CONSP(reg0)) + LispDestroy("NTHCDR: %s is not a list", STROBJ(reg0)); + for (; fixnum > 0; fixnum--) { + if (!CONSP(reg0)) + break; + reg0 = CDR(reg0); + } + } + NEXT_OPCODE(); + + /* Push to builtin stack */ +OPCODE_LABEL(XBC_CAR_PUSH): + if (reg0 != NIL) { + if (!CONSP(reg0)) + LispDestroy("CAR: %s is not a list", STROBJ(reg0)); + reg0 = CAR(reg0); + } + goto push_builtin; + +OPCODE_LABEL(XBC_CDR_PUSH): + if (reg0 != NIL) { + if (!CONSP(reg0)) + LispDestroy("CDR: %s is not a list", STROBJ(reg0)); + reg0 = CDR(reg0); + } + /*FALLTROUGH*/ + +OPCODE_LABEL(XBC_PUSH): +push_builtin: + lisp__data.stack.values[lisp__data.stack.length++] = reg0; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_PUSH_NIL): + lisp__data.stack.values[lisp__data.stack.length++] = NIL; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_PUSH_UNSPEC): + lisp__data.stack.values[lisp__data.stack.length++] = UNSPEC; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_PUSH_T): + lisp__data.stack.values[lisp__data.stack.length++] = T; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_PUSH_NIL_N): + for (offset = *stream++; offset > 0; offset--) + lisp__data.stack.values[lisp__data.stack.length++] = NIL; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_PUSH_UNSPEC_N): + for (offset = *stream++; offset > 0; offset--) + lisp__data.stack.values[lisp__data.stack.length++] = UNSPEC; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LET): +let_argument: + /* The global object value is not changed, so it does not + * matter if it is a constant symbol. An error would be + * generated if it was declared as constant at the time of + * bytecode generation. Check can be done looking at the + * atom->constant field. */ + atom = symbols[*stream++]; + atom->offset = lisp__data.env.length; + lisp__data.env.names[lisp__data.env.length] = atom->string; + lisp__data.env.values[lisp__data.env.length++] = reg0; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LETX): +letx_argument: + atom = symbols[*stream++]; + atom->offset = lisp__data.env.length; + lisp__data.env.names[lisp__data.env.length] = atom->string; + lisp__data.env.values[lisp__data.env.length++] = reg0; + lisp__data.env.head++; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LET_NIL): + atom = symbols[*stream++]; + atom->offset = lisp__data.env.length; + lisp__data.env.names[lisp__data.env.length] = atom->string; + lisp__data.env.values[lisp__data.env.length++] = NIL; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LETX_NIL): + atom = symbols[*stream++]; + atom->offset = lisp__data.env.length; + lisp__data.env.names[lisp__data.env.length] = atom->string; + lisp__data.env.values[lisp__data.env.length++] = NIL; + lisp__data.env.head++; + NEXT_OPCODE(); + + /* Bind locally added variables to a block */ +OPCODE_LABEL(XBC_LETBIND): + offset = *stream++; + lisp__data.env.head += offset; + NEXT_OPCODE(); + + /* Unbind locally added variables to a block */ +OPCODE_LABEL(XBC_UNLET): + offset = *stream++; + lisp__data.env.head -= offset; + lisp__data.env.length -= offset; + NEXT_OPCODE(); + + /* Load value from stack */ +OPCODE_LABEL(XBC_LOAD): + offset = *stream++; + reg0 = lisp__data.env.values[lisp__data.env.lex + offset]; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LOAD_CAR): + offset = *stream++; + reg0 = lisp__data.env.values[lisp__data.env.lex + offset]; + goto car; + +OPCODE_LABEL(XBC_LOAD_CDR): + offset = *stream++; + reg0 = lisp__data.env.values[lisp__data.env.lex + offset]; + goto cdr; + +OPCODE_LABEL(XBC_LOAD_CAR_STORE): + offset = *stream++; + reg0 = lisp__data.env.values[lisp__data.env.lex + offset]; + if (reg0 != NIL) { + if (!CONSP(reg0)) + LispDestroy("CAR: %s is not a list", STROBJ(reg0)); + reg0 = CAR(reg0); + lisp__data.env.values[lisp__data.env.lex + offset] = reg0; + } + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LOAD_CDR_STORE): + offset = *stream++; + reg0 = lisp__data.env.values[lisp__data.env.lex + offset]; + if (reg0 != NIL) { + if (!CONSP(reg0)) + LispDestroy("CDR: %s is not a list", STROBJ(reg0)); + reg0 = CDR(reg0); + lisp__data.env.values[lisp__data.env.lex + offset] = reg0; + } + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LOAD_LET): + offset = *stream++; + reg0 = lisp__data.env.values[lisp__data.env.lex + offset]; + goto let_argument; + +OPCODE_LABEL(XBC_LOAD_LETX): + offset = *stream++; + reg0 = lisp__data.env.values[lisp__data.env.lex + offset]; + goto letx_argument; + +OPCODE_LABEL(XBC_LOAD_PUSH): + offset = *stream++; + reg0 = lisp__data.env.values[lisp__data.env.lex + offset]; + lisp__data.stack.values[lisp__data.stack.length++] = reg0; + NEXT_OPCODE(); + + /* Load pointer to constant */ +OPCODE_LABEL(XBC_LOADCON): + reg0 = constants[*stream++]; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LOADCON_LET): + reg0 = constants[*stream++]; + goto let_argument; + +OPCODE_LABEL(XBC_LOADCON_LETX): + reg0 = constants[*stream++]; + goto letx_argument; + +OPCODE_LABEL(XBC_LOADCON_PUSH): + reg0 = constants[*stream++]; + lisp__data.stack.values[lisp__data.stack.length++] = reg0; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LOADCON_SET): + reg0 = constants[*stream++]; + offset = *stream++; + lisp__data.env.values[lisp__data.env.lex + offset] = reg0; + NEXT_OPCODE(); + + /* Change value of local variable */ +OPCODE_LABEL(XBC_CAR_SET): +car_set: + if (reg0 != NIL) { + if (!CONSP(reg0)) + LispDestroy("CAR: %s is not a list", STROBJ(reg0)); + reg0 = CAR(reg0); + } + goto set_local_variable; + +OPCODE_LABEL(XBC_CDR_SET): +cdr_set: + if (reg0 != NIL) { + if (!CONSP(reg0)) + LispDestroy("CDR: %s is not a list", STROBJ(reg0)); + reg0 = CDR(reg0); + } + goto set_local_variable; + +OPCODE_LABEL(XBC_LOAD_CAR_SET): + offset = *stream++; + reg0 = lisp__data.env.values[lisp__data.env.lex + offset]; + goto car_set; + +OPCODE_LABEL(XBC_LOAD_CDR_SET): + offset = *stream++; + reg0 = lisp__data.env.values[lisp__data.env.lex + offset]; + goto cdr_set; + +OPCODE_LABEL(XBC_LOAD_SET): + offset = *stream++; + reg0 = lisp__data.env.values[lisp__data.env.lex + offset]; + /*FALLTROUGH*/ + +OPCODE_LABEL(XBC_SET): +set_local_variable: + offset = *stream++; + lisp__data.env.values[lisp__data.env.lex + offset] = reg0; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_SET_NIL): + offset = *stream++; + lisp__data.env.values[lisp__data.env.lex + offset] = NIL; + NEXT_OPCODE(); + + /* Change value of a global/special variable */ +OPCODE_LABEL(XBC_SETSYM): + atom = symbols[*stream++]; + if (atom->dyn) { + /* atom->dyn and atom->constant are exclusive, no + * need to check if variable declared as constant. */ + if (atom->offset < lisp__data.env.head && + lisp__data.env.names[atom->offset] == atom->string) + lisp__data.env.values[atom->offset] = reg0; + else { + if (atom->watch) + LispSetAtomObjectProperty(atom, reg0); + else + SETVALUE(atom, reg0); + } + } + else if (atom->a_object) { + if (atom->constant) + LispDestroy("EVAL: %s is a constant", + STROBJ(atom->object)); + else if (atom->watch) + LispSetAtomObjectProperty(atom, reg0); + else + SETVALUE(atom, reg0); + } + else { + /* Create new global variable */ + LispPackage *pack; + + LispWarning("the variable %s was not declared", + atom->string); + LispSetAtomObjectProperty(atom, reg0); + pack = atom->package->data.package.package; + if (pack->glb.length >= pack->glb.space) + LispMoreGlobals(pack); + pack->glb.pairs[pack->glb.length++] = atom->object; + } + NEXT_OPCODE(); + +/* Resolve symbol value at runtime */ +#define LOAD_SYMBOL_VALUE() \ + atom = symbols[*stream++]; \ + if (atom->dyn) { \ + if (atom->offset < lisp__data.env.head && \ + lisp__data.env.names[atom->offset] == atom->string) \ + reg0 = lisp__data.env.values[atom->offset]; \ + else { \ + reg0 = atom->property->value; \ + if (reg0 == UNBOUND) \ + LispDestroy("EVAL: the symbol %s is unbound", \ + STROBJ(atom->object)); \ + } \ + } \ + else { \ + if (atom->a_object) \ + reg0 = atom->property->value; \ + else \ + LispDestroy("EVAL: the symbol %s is unbound", \ + STROBJ(atom->object)); \ + } + +OPCODE_LABEL(XBC_LOADSYM): + LOAD_SYMBOL_VALUE(); + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LOADSYM_LET): + LOAD_SYMBOL_VALUE(); + goto let_argument; + +OPCODE_LABEL(XBC_LOADSYM_LETX): + LOAD_SYMBOL_VALUE(); + goto letx_argument; + +OPCODE_LABEL(XBC_LOADSYM_PUSH): + LOAD_SYMBOL_VALUE(); + lisp__data.stack.values[lisp__data.stack.length++] = reg0; + NEXT_OPCODE(); + + /* Builtin function */ +OPCODE_LABEL(XBC_CALL): + offset = *stream++; + lisp__data.stack.base = lisp__data.stack.length - offset; + builtin = builtins[*stream++]; + if (builtin->multiple_values) { + RETURN_COUNT = 0; + reg0 = builtin->function(builtin); + } + else { + reg0 = builtin->function(builtin); + RETURN_COUNT = 0; + } + lisp__data.stack.length -= offset; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_CALL_SET): + offset = *stream++; + lisp__data.stack.base = lisp__data.stack.length - offset; + builtin = builtins[*stream++]; + if (builtin->multiple_values) { + RETURN_COUNT = 0; + reg0 = builtin->function(builtin); + } + else { + reg0 = builtin->function(builtin); + RETURN_COUNT = 0; + } + lisp__data.stack.length -= offset; + offset = *stream++; + lisp__data.env.values[lisp__data.env.lex + offset] = reg0; + NEXT_OPCODE(); + + /* Bytecode call */ +OPCODE_LABEL(XBC_BYTECALL): + lex = lisp__data.env.lex; + offset = *stream++; + lisp__data.env.head = lisp__data.env.length; + len = lisp__data.env.lex = lisp__data.env.length - offset; + reg0 = ExecuteBytecode(codes[*stream++]); + lisp__data.env.length = lisp__data.env.head = len; + lisp__data.env.lex = lex; + NEXT_OPCODE(); + + /* Unimplemented function/macro call */ +OPCODE_LABEL(XBC_FUNCALL): + lambda = constants[*stream++]; + arguments = constants[*stream++]; + reg0 = LispFuncall(lambda, arguments, 1); + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_JUMP): + stream += *(signed short*)stream; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_JUMPT): + if (reg0 != NIL) + stream += *(signed short*)stream; + else + /* skip jump relative offset */ + stream += sizeof(signed short); + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_JUMPNIL): + if (reg0 == NIL) + stream += *(signed short*)stream; + else + /* skip jump relative offset */ + stream += sizeof(signed short); + NEXT_OPCODE(); + + /* Build CONS of two constant arguments */ +OPCODE_LABEL(XBC_CCONS): + reg0 = constants[*stream++]; + reg1 = constants[*stream++]; + reg0 = CONS(reg0, reg1); + NEXT_OPCODE(); + + /* Start CONS */ +OPCODE_LABEL(XBC_CSTAR): + /* This the CAR of the CONS */ + lisp__data.protect.objects[phead++] = reg0; + NEXT_OPCODE(); + + /* Finish CONS */ +OPCODE_LABEL(XBC_CFINI): + reg0 = CONS(lisp__data.protect.objects[--phead], reg0); + NEXT_OPCODE(); + + /* Start building list */ +OPCODE_LABEL(XBC_LSTAR): + reg1 = CONS(reg0, NIL); + /* Start of list stored here */ + lisp__data.protect.objects[phead++] = reg1; + /* Tail of list stored here */ + lisp__data.protect.objects[phead++] = reg1; + NEXT_OPCODE(); + + /* Add to list */ +OPCODE_LABEL(XBC_LCONS): + reg1 = lisp__data.protect.objects[phead - 2]; + RPLACD(reg1, CONS(reg0, NIL)); + lisp__data.protect.objects[phead - 2] = CDR(reg1); + NEXT_OPCODE(); + + /* Finish list */ +OPCODE_LABEL(XBC_LFINI): + phead -= 2; + reg0 = lisp__data.protect.objects[phead + 1]; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_STRUCT): + offset = *stream++; + reg1 = constants[*stream++]; + if (!STRUCTP(reg0) || reg0->data.struc.def != reg1) { + char *name = ATOMID(CAR(reg1)); + + for (reg1 = CDR(reg1); offset; offset--) + reg1 = CDR(reg1); + LispDestroy("%s-%s: %s is not a %s", + name, ATOMID(CAR(reg1)), STROBJ(reg0), name); + } + for (reg0 = reg0->data.struc.fields; offset; offset--) + reg0 = CDR(reg0); + reg0 = CAR(reg0); + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_STRUCTP): + reg1 = constants[*stream++]; + reg0 = STRUCTP(reg0) && reg0->data.struc.def == reg1 ? T : NIL; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_LETREC): + /* XXX could/should optimize, shouldn't need to parse + * the bytecode header again */ + lex = lisp__data.env.lex; + offset = *stream++; + lisp__data.env.head = lisp__data.env.length; + len = lisp__data.env.lex = lisp__data.env.length - offset; + reg0 = ExecuteBytecode(bytecode); + lisp__data.env.length = lisp__data.env.head = len; + lisp__data.env.lex = lex; + NEXT_OPCODE(); + +OPCODE_LABEL(XBC_RETURN): + lisp__data.protect.length = pbase; + return (reg0); + +#ifndef ALLOW_GOTO_ADDRESS + } /* end of switch */ + +predicate_label: + switch (*stream++) { +#endif + +OPCODE_LABEL(XBP_CONSP): + reg0 = CONSP(reg0) ? T : NIL; + NEXT_OPCODE(); + +OPCODE_LABEL(XBP_LISTP): + reg0 = LISTP(reg0) ? T : NIL; + NEXT_OPCODE(); + +OPCODE_LABEL(XBP_NUMBERP): + reg0 = NUMBERP(reg0) ? T : NIL; + NEXT_OPCODE(); + +#ifndef ALLOW_GOTO_ADDRESS + } /* end of switch */ + } +#endif + + /*NOTREACHED*/ + return (reg0); +} |