/****************************************************************************** Copyright (c) 1994, 1995, 1996 Xerox Corporation. All rights reserved. Portions of this code were written by Stephen White, aka ghond. Use and copying of this software and preparation of derivative works based upon this software are permitted. Any distribution of this software or derivative works must comply with all applicable United States export control laws. This software is made available AS IS, and Xerox Corporation makes no warranty about the software, its performance or its conformity to any specification. Any person obtaining a copy of this software is requested to send their name and post office or electronic mail address to: Pavel Curtis Xerox PARC 3333 Coyote Hill Rd. Palo Alto, CA 94304 Pavel@Xerox.Com *****************************************************************************/ #include <limits.h> #include "ast.h" #include "exceptions.h" #include "opcode.h" #include "program.h" #include "storage.h" #include "structures.h" #include "utils.h" #include "version.h" /*** The reader will likely find it useful to consult the file *** `MOOCodeSequences.txt' in this directory while reading the code in this *** file. ***/ enum fixup_kind { FIXUP_LITERAL, FIXUP_FORK, FIXUP_LABEL, FIXUP_VAR_REF, FIXUP_STACK }; struct fixup { enum fixup_kind kind; unsigned pc; unsigned value; unsigned prev_literals, prev_forks, prev_var_refs, prev_labels, prev_stacks; int next; /* chain for compiling IF/ELSEIF arms */ }; typedef struct fixup Fixup; struct gstate { unsigned total_var_refs; /* For duplicating an old bug... */ unsigned num_literals, max_literals; Var *literals; unsigned num_fork_vectors, max_fork_vectors; Bytecodes *fork_vectors; }; typedef struct gstate GState; struct loop { int id; Fixup top_label; unsigned top_stack; int bottom_label; unsigned bottom_stack; }; typedef struct loop Loop; struct state { unsigned max_literal, max_fork, max_var_ref; /* For telling how big the refs must be */ unsigned num_literals, num_forks, num_var_refs, num_labels, num_stacks; /* For computing the final vector length */ unsigned num_fixups, max_fixups; Fixup *fixups; unsigned num_bytes, max_bytes; Byte *bytes; unsigned cur_stack, max_stack; unsigned saved_stack; unsigned num_loops, max_loops; Loop *loops; GState *gstate; }; typedef struct state State; static void init_gstate(GState *gstate) { gstate->total_var_refs = 0; gstate->num_literals = gstate->num_fork_vectors = 0; gstate->max_literals = gstate->max_fork_vectors = 0; gstate->fork_vectors = 0; gstate->literals = 0; } static void free_gstate(GState gstate) { if (gstate.literals) myfree(gstate.literals, M_CODE_GEN); if (gstate.fork_vectors) myfree(gstate.fork_vectors, M_CODE_GEN); } static void init_state(State *state, GState *gstate) { state->num_literals = state->num_forks = state->num_labels = 0; state->num_var_refs = state->num_stacks = 0; state->max_literal = state->max_fork = state->max_var_ref = 0; state->num_fixups = 0; state->max_fixups = 10; state->fixups = mymalloc(sizeof(Fixup) * state->max_fixups, M_CODE_GEN); state->num_bytes = 0; state->max_bytes = 50; state->bytes = mymalloc(sizeof(Byte) * state->max_bytes, M_BYTECODES); state->cur_stack = state->max_stack = 0; state->saved_stack = UINT_MAX; state->num_loops = 0; state->max_loops = 5; state->loops = mymalloc(sizeof(Loop) * state->max_loops, M_CODE_GEN); state->gstate = gstate; } static void free_state(State state) { myfree(state.fixups, M_CODE_GEN); myfree(state.bytes, M_BYTECODES); myfree(state.loops, M_CODE_GEN); } static void emit_byte(Byte b, State *state) { if (state->num_bytes == state->max_bytes) { unsigned new_max = 2 * state->max_bytes; Byte *new_bytes = mymalloc(sizeof(Byte) * new_max, M_BYTECODES); unsigned i; for (i = 0; i < state->num_bytes; i++) new_bytes[i] = state->bytes[i]; myfree(state->bytes, M_BYTECODES); state->bytes = new_bytes; state->max_bytes = new_max; } state->bytes[state->num_bytes++] = b; } static void emit_extended_byte(Byte b, State *state) { emit_byte(OP_EXTENDED, state); emit_byte(b, state); } static int add_known_fixup(Fixup f, State *state) { int i; if (state->num_fixups == state->max_fixups) { unsigned new_max = 2 * state->max_fixups; Fixup *new_fixups = mymalloc(sizeof(Fixup) * new_max, M_CODE_GEN); for (i = 0; i < state->num_fixups; i++) new_fixups[i] = state->fixups[i]; myfree(state->fixups, M_CODE_GEN); state->fixups = new_fixups; state->max_fixups = new_max; } f.pc = state->num_bytes; state->fixups[i = state->num_fixups++] = f; emit_byte(0, state); /* a placeholder for the eventual value */ return i; } static int add_linked_fixup(enum fixup_kind kind, unsigned value, int next, State *state) { Fixup f; f.kind = kind; f.value = value; f.prev_literals = state->num_literals; f.prev_forks = state->num_forks; f.prev_var_refs = state->num_var_refs; f.prev_labels = state->num_labels; f.prev_stacks = state->num_stacks; f.next = next; return add_known_fixup(f, state); } static int add_fixup(enum fixup_kind kind, unsigned value, State *state) { return add_linked_fixup(kind, value, -1, state); } static void add_literal(Var v, State *state) { GState *gstate = state->gstate; Var *literals = gstate->literals; unsigned i; for (i = 0; i < gstate->num_literals; i++) if (v.type == literals[i].type /* no int/float coercion here */ && equality(v, literals[i], 1)) break; if (i == gstate->num_literals) { /* New literal to intern */ if (gstate->num_literals == gstate->max_literals) { unsigned new_max = gstate->max_literals == 0 ? 5 : 2 * gstate->max_literals; Var *new_literals = mymalloc(sizeof(Var) * new_max, M_CODE_GEN); if (gstate->literals) { for (i = 0; i < gstate->num_literals; i++) new_literals[i] = literals[i]; myfree(literals, M_CODE_GEN); } gstate->literals = new_literals; gstate->max_literals = new_max; } gstate->literals[i = gstate->num_literals++] = var_ref(v); } add_fixup(FIXUP_LITERAL, i, state); state->num_literals++; if (i > state->max_literal) state->max_literal = i; } static void add_fork(Bytecodes b, State *state) { unsigned i; GState *gstate = state->gstate; if (gstate->num_fork_vectors == gstate->max_fork_vectors) { unsigned new_max = gstate->max_fork_vectors == 0 ? 1 : 2 * gstate->max_fork_vectors; Bytecodes *new_fv = mymalloc(sizeof(Bytecodes) * new_max, M_CODE_GEN); if (gstate->fork_vectors) { for (i = 0; i < gstate->num_fork_vectors; i++) new_fv[i] = gstate->fork_vectors[i]; myfree(gstate->fork_vectors, M_CODE_GEN); } gstate->fork_vectors = new_fv; gstate->max_fork_vectors = new_max; } gstate->fork_vectors[i = gstate->num_fork_vectors++] = b; add_fixup(FIXUP_FORK, i, state); state->num_forks++; if (i > state->max_fork) state->max_fork = i; } static void add_var_ref(unsigned slot, State *state) { add_fixup(FIXUP_VAR_REF, slot, state); state->num_var_refs++; if (slot > state->max_var_ref) state->max_var_ref = slot; state->gstate->total_var_refs++; } static int add_linked_label(int next, State *state) { int label = add_linked_fixup(FIXUP_LABEL, 0, next, state); state->num_labels++; return label; } static int add_label(State *state) { return add_linked_label(-1, state); } static void add_pseudo_label(unsigned value, State *state) { Fixup f; f.kind = FIXUP_LABEL; f.value = value; f.prev_literals = f.prev_forks = 0; f.prev_var_refs = f.prev_labels = 0; f.next = -1; add_known_fixup(f, state); state->num_labels++; } static int add_known_label(Fixup f, State *state) { int label = add_known_fixup(f, state); state->num_labels++; return label; } static Fixup capture_label(State *state) { Fixup f; f.kind = FIXUP_LABEL; f.value = state->num_bytes; f.prev_literals = state->num_literals; f.prev_forks = state->num_forks; f.prev_var_refs = state->num_var_refs; f.prev_labels = state->num_labels; f.next = -1; return f; } static void define_label(int label, State *state) { unsigned value = state->num_bytes; while (label != -1) { Fixup *fixup = &(state->fixups[label]); fixup->value = value; fixup->prev_literals = state->num_literals; fixup->prev_forks = state->num_forks; fixup->prev_var_refs = state->num_var_refs; fixup->prev_labels = state->num_labels; label = fixup->next; } } static void add_stack_ref(unsigned index, State *state) { add_fixup(FIXUP_STACK, index, state); } static void push_stack(unsigned n, State *state) { state->cur_stack += n; if (state->cur_stack > state->max_stack) state->max_stack = state->cur_stack; } static void pop_stack(unsigned n, State *state) { state->cur_stack -= n; } static unsigned save_stack_top(State *state) { unsigned old = state->saved_stack; state->saved_stack = state->cur_stack - 1; return old; } static unsigned saved_stack_top(State *state) { return state->saved_stack; } static void restore_stack_top(unsigned old, State *state) { state->saved_stack = old; } static void enter_loop(int id, Fixup top_label, unsigned top_stack, int bottom_label, unsigned bottom_stack, State *state) { int i; Loop *loop; if (state->num_loops == state->max_loops) { unsigned new_max = 2 * state->max_loops; Loop *new_loops = mymalloc(sizeof(Loop) * new_max, M_CODE_GEN); for (i = 0; i < state->num_loops; i++) new_loops[i] = state->loops[i]; myfree(state->loops, M_CODE_GEN); state->loops = new_loops; state->max_loops = new_max; } loop = &(state->loops[state->num_loops++]); loop->id = id; loop->top_label = top_label; loop->top_stack = top_stack; loop->bottom_label = bottom_label; loop->bottom_stack = bottom_stack; } static int exit_loop(State *state) { return state->loops[--state->num_loops].bottom_label; } static void emit_var_op(Opcode op, unsigned slot, State *state) { if (slot >= NUM_READY_VARS) { emit_byte(op + NUM_READY_VARS, state); add_var_ref(slot, state); } else emit_byte(op + slot, state); } static void generate_expr(Expr *, State *); static void generate_arg_list(Arg_List *args, State *state) { if (!args) { emit_byte(OP_MAKE_EMPTY_LIST, state); push_stack(1, state); } else { Opcode normal_op = OP_MAKE_SINGLETON_LIST, splice_op = OP_CHECK_LIST_FOR_SPLICE; unsigned pop = 0; for (; args; args = args->next) { generate_expr(args->expr, state); emit_byte(args->kind == ARG_NORMAL ? normal_op : splice_op, state); pop_stack(pop, state); normal_op = OP_LIST_ADD_TAIL; splice_op = OP_LIST_APPEND; pop = 1; } } } static void push_lvalue(Expr *expr, int indexed_above, State *state) { unsigned old; switch (expr->kind) { case EXPR_RANGE: push_lvalue(expr->e.range.base, 1, state); old = save_stack_top(state); generate_expr(expr->e.range.from, state); generate_expr(expr->e.range.to, state); restore_stack_top(old, state); break; case EXPR_INDEX: push_lvalue(expr->e.bin.lhs, 1, state); old = save_stack_top(state); generate_expr(expr->e.bin.rhs, state); restore_stack_top(old, state); if (indexed_above) { emit_byte(OP_PUSH_REF, state); push_stack(1, state); } break; case EXPR_ID: if (indexed_above) { emit_var_op(OP_PUSH, expr->e.id, state); push_stack(1, state); } break; case EXPR_PROP: generate_expr(expr->e.bin.lhs, state); generate_expr(expr->e.bin.rhs, state); if (indexed_above) { emit_byte(OP_PUSH_GET_PROP, state); push_stack(1, state); } break; default: panic("Bad lvalue in PUSH_LVALUE()"); } } static void generate_codes(Arg_List *codes, State *state) { if (codes) generate_arg_list(codes, state); else { emit_byte(OPTIM_NUM_TO_OPCODE(0), state); push_stack(1, state); } } static void generate_expr(Expr *expr, State *state) { switch (expr->kind) { case EXPR_VAR: { Var v; v = expr->e.var; if (v.type == TYPE_INT && IN_OPTIM_NUM_RANGE(v.v.num)) emit_byte(OPTIM_NUM_TO_OPCODE(v.v.num), state); else { emit_byte(OP_IMM, state); add_literal(v, state); } push_stack(1, state); } break; case EXPR_ID: emit_var_op(OP_PUSH, expr->e.id, state); push_stack(1, state); break; case EXPR_AND: case EXPR_OR: { int end_label; generate_expr(expr->e.bin.lhs, state); emit_byte(expr->kind == EXPR_AND ? OP_AND : OP_OR, state); end_label = add_label(state); pop_stack(1, state); generate_expr(expr->e.bin.rhs, state); define_label(end_label, state); } break; case EXPR_NEGATE: case EXPR_NOT: generate_expr(expr->e.expr, state); emit_byte(expr->kind == EXPR_NOT ? OP_NOT : OP_UNARY_MINUS, state); break; case EXPR_EQ: case EXPR_NE: case EXPR_GE: case EXPR_GT: case EXPR_LE: case EXPR_LT: case EXPR_IN: case EXPR_PLUS: case EXPR_MINUS: case EXPR_TIMES: case EXPR_DIVIDE: case EXPR_MOD: case EXPR_PROP: { Opcode op = OP_ADD; /* initialize to silence warning */ generate_expr(expr->e.bin.lhs, state); generate_expr(expr->e.bin.rhs, state); switch (expr->kind) { case EXPR_EQ: op = OP_EQ; break; case EXPR_NE: op = OP_NE; break; case EXPR_GE: op = OP_GE; break; case EXPR_GT: op = OP_GT; break; case EXPR_LE: op = OP_LE; break; case EXPR_LT: op = OP_LT; break; case EXPR_IN: op = OP_IN; break; case EXPR_PLUS: op = OP_ADD; break; case EXPR_MINUS: op = OP_MINUS; break; case EXPR_TIMES: op = OP_MULT; break; case EXPR_DIVIDE: op = OP_DIV; break; case EXPR_MOD: op = OP_MOD; break; case EXPR_PROP: op = OP_GET_PROP; break; case EXPR_INDEX: op = OP_REF; break; default: panic("Not a binary operator in GENERATE_EXPR()"); } emit_byte(op, state); pop_stack(1, state); } break; case EXPR_EXP: generate_expr(expr->e.bin.lhs, state); generate_expr(expr->e.bin.rhs, state); emit_extended_byte(EOP_EXP, state); pop_stack(1, state); break; case EXPR_INDEX: { unsigned old; generate_expr(expr->e.bin.lhs, state); old = save_stack_top(state); generate_expr(expr->e.bin.rhs, state); restore_stack_top(old, state); emit_byte(OP_REF, state); pop_stack(1, state); } break; case EXPR_RANGE: { unsigned old; generate_expr(expr->e.range.base, state); old = save_stack_top(state); generate_expr(expr->e.range.from, state); generate_expr(expr->e.range.to, state); restore_stack_top(old, state); emit_byte(OP_RANGE_REF, state); pop_stack(2, state); } break; case EXPR_LENGTH: { unsigned saved = saved_stack_top(state); if (saved != UINT_MAX) { emit_extended_byte(EOP_LENGTH, state); add_stack_ref(saved, state); push_stack(1, state); } else panic("Missing saved stack for `$' in GENERATE_EXPR()"); } break; case EXPR_LIST: generate_arg_list(expr->e.list, state); break; case EXPR_CALL: generate_arg_list(expr->e.call.args, state); emit_byte(OP_BI_FUNC_CALL, state); emit_byte(expr->e.call.func, state); break; case EXPR_VERB: generate_expr(expr->e.verb.obj, state); generate_expr(expr->e.verb.verb, state); generate_arg_list(expr->e.verb.args, state); emit_byte(OP_CALL_VERB, state); pop_stack(2, state); break; case EXPR_COND: { int else_label, end_label; generate_expr(expr->e.cond.condition, state); emit_byte(OP_IF_QUES, state); else_label = add_label(state); pop_stack(1, state); generate_expr(expr->e.cond.consequent, state); emit_byte(OP_JUMP, state); end_label = add_label(state); pop_stack(1, state); define_label(else_label, state); generate_expr(expr->e.cond.alternate, state); define_label(end_label, state); } break; case EXPR_ASGN: { Expr *e = expr->e.bin.lhs; if (e->kind == EXPR_SCATTER) { int nargs = 0, nreq = 0, rest = -1; unsigned done; Scatter *sc; generate_expr(expr->e.bin.rhs, state); for (sc = e->e.scatter; sc; sc = sc->next) { nargs++; if (sc->kind == SCAT_REQUIRED) nreq++; else if (sc->kind == SCAT_REST) rest = nargs; } if (rest == -1) rest = nargs + 1; emit_extended_byte(EOP_SCATTER, state); emit_byte(nargs, state); emit_byte(nreq, state); emit_byte(rest, state); for (sc = e->e.scatter; sc; sc = sc->next) { add_var_ref(sc->id, state); if (sc->kind != SCAT_OPTIONAL) add_pseudo_label(0, state); else if (!sc->expr) add_pseudo_label(1, state); else sc->label = add_label(state); } done = add_label(state); for (sc = e->e.scatter; sc; sc = sc->next) if (sc->kind == SCAT_OPTIONAL && sc->expr) { define_label(sc->label, state); generate_expr(sc->expr, state); emit_var_op(OP_PUT, sc->id, state); emit_byte(OP_POP, state); pop_stack(1, state); } define_label(done, state); } else { int is_indexed = 0; push_lvalue(e, 0, state); generate_expr(expr->e.bin.rhs, state); if (e->kind == EXPR_RANGE || e->kind == EXPR_INDEX) emit_byte(OP_PUT_TEMP, state); while (1) { switch (e->kind) { case EXPR_RANGE: emit_extended_byte(EOP_RANGESET, state); pop_stack(3, state); e = e->e.range.base; is_indexed = 1; continue; case EXPR_INDEX: emit_byte(OP_INDEXSET, state); pop_stack(2, state); e = e->e.bin.lhs; is_indexed = 1; continue; case EXPR_ID: emit_var_op(OP_PUT, e->e.id, state); break; case EXPR_PROP: emit_byte(OP_PUT_PROP, state); pop_stack(2, state); break; default: panic("Bad lvalue in GENERATE_EXPR()"); } break; } if (is_indexed) { emit_byte(OP_POP, state); emit_byte(OP_PUSH_TEMP, state); } } } break; case EXPR_CATCH: { int handler_label, end_label; generate_codes(expr->e.catch.codes, state); emit_extended_byte(EOP_PUSH_LABEL, state); handler_label = add_label(state); push_stack(1, state); emit_extended_byte(EOP_CATCH, state); push_stack(1, state); generate_expr(expr->e.expr, state); emit_extended_byte(EOP_END_CATCH, state); end_label = add_label(state); pop_stack(3, state); /* codes, label, catch */ define_label(handler_label, state); /* After this label, we still have a value on the stack, but now, * instead of it being the value of the main expression, we have * the exception tuple pushed before entering the handler. */ if (expr->e.catch.except) { emit_byte(OP_POP, state); pop_stack(1, state); generate_expr(expr->e.catch.except, state); } else { /* Select code from tuple */ emit_byte(OPTIM_NUM_TO_OPCODE(1), state); emit_byte(OP_REF, state); } define_label(end_label, state); } break; default: panic("Can't happen in GENERATE_EXPR()"); } } static Bytecodes stmt_to_code(Stmt *, GState *); static void generate_stmt(Stmt *stmt, State *state) { for (; stmt; stmt = stmt->next) { switch (stmt->kind) { case STMT_COND: { Opcode if_op = OP_IF; int end_label = -1; Cond_Arm *arms; for (arms = stmt->s.cond.arms; arms; arms = arms->next) { int else_label; generate_expr(arms->condition, state); emit_byte(if_op, state); else_label = add_label(state); pop_stack(1, state); generate_stmt(arms->stmt, state); emit_byte(OP_JUMP, state); end_label = add_linked_label(end_label, state); define_label(else_label, state); if_op = OP_EIF; } if (stmt->s.cond.otherwise) generate_stmt(stmt->s.cond.otherwise, state); define_label(end_label, state); } break; case STMT_LIST: { Fixup loop_top; int end_label; generate_expr(stmt->s.list.expr, state); emit_byte(OPTIM_NUM_TO_OPCODE(1), state); /* loop list index */ push_stack(1, state); loop_top = capture_label(state); emit_byte(OP_FOR_LIST, state); add_var_ref(stmt->s.list.id, state); end_label = add_label(state); enter_loop(stmt->s.list.id, loop_top, state->cur_stack, end_label, state->cur_stack - 2, state); generate_stmt(stmt->s.list.body, state); end_label = exit_loop(state); emit_byte(OP_JUMP, state); add_known_label(loop_top, state); define_label(end_label, state); pop_stack(2, state); } break; case STMT_RANGE: { Fixup loop_top; int end_label; generate_expr(stmt->s.range.from, state); generate_expr(stmt->s.range.to, state); loop_top = capture_label(state); emit_byte(OP_FOR_RANGE, state); add_var_ref(stmt->s.range.id, state); end_label = add_label(state); enter_loop(stmt->s.range.id, loop_top, state->cur_stack, end_label, state->cur_stack - 2, state); generate_stmt(stmt->s.range.body, state); end_label = exit_loop(state); emit_byte(OP_JUMP, state); add_known_label(loop_top, state); define_label(end_label, state); pop_stack(2, state); } break; case STMT_WHILE: { Fixup loop_top; int end_label; loop_top = capture_label(state); generate_expr(stmt->s.loop.condition, state); if (stmt->s.loop.id == -1) emit_byte(OP_WHILE, state); else { emit_extended_byte(EOP_WHILE_ID, state); add_var_ref(stmt->s.loop.id, state); } end_label = add_label(state); pop_stack(1, state); enter_loop(stmt->s.loop.id, loop_top, state->cur_stack, end_label, state->cur_stack, state); generate_stmt(stmt->s.loop.body, state); end_label = exit_loop(state); emit_byte(OP_JUMP, state); add_known_label(loop_top, state); define_label(end_label, state); } break; case STMT_FORK: generate_expr(stmt->s.fork.time, state); if (stmt->s.fork.id >= 0) emit_byte(OP_FORK_WITH_ID, state); else emit_byte(OP_FORK, state); add_fork(stmt_to_code(stmt->s.fork.body, state->gstate), state); if (stmt->s.fork.id >= 0) add_var_ref(stmt->s.fork.id, state); pop_stack(1, state); break; case STMT_EXPR: generate_expr(stmt->s.expr, state); emit_byte(OP_POP, state); pop_stack(1, state); break; case STMT_RETURN: if (stmt->s.expr) { generate_expr(stmt->s.expr, state); emit_byte(OP_RETURN, state); pop_stack(1, state); } else emit_byte(OP_RETURN0, state); break; case STMT_TRY_EXCEPT: { int end_label, arm_count = 0; Except_Arm *ex; for (ex = stmt->s.catch.excepts; ex; ex = ex->next) { generate_codes(ex->codes, state); emit_extended_byte(EOP_PUSH_LABEL, state); ex->label = add_label(state); push_stack(1, state); arm_count++; } emit_extended_byte(EOP_TRY_EXCEPT, state); emit_byte(arm_count, state); push_stack(1, state); generate_stmt(stmt->s.catch.body, state); emit_extended_byte(EOP_END_EXCEPT, state); end_label = add_label(state); pop_stack(2 * arm_count + 1, state); /* 2(codes,pc) + catch */ for (ex = stmt->s.catch.excepts; ex; ex = ex->next) { define_label(ex->label, state); push_stack(1, state); /* exception tuple */ if (ex->id >= 0) emit_var_op(OP_PUT, ex->id, state); emit_byte(OP_POP, state); pop_stack(1, state); generate_stmt(ex->stmt, state); if (ex->next) { emit_byte(OP_JUMP, state); end_label = add_linked_label(end_label, state); } } define_label(end_label, state); } break; case STMT_TRY_FINALLY: { int handler_label; emit_extended_byte(EOP_TRY_FINALLY, state); handler_label = add_label(state); push_stack(1, state); generate_stmt(stmt->s.finally.body, state); emit_extended_byte(EOP_END_FINALLY, state); pop_stack(1, state); /* FINALLY marker */ define_label(handler_label, state); push_stack(2, state); /* continuation value, reason */ generate_stmt(stmt->s.finally.handler, state); emit_extended_byte(EOP_CONTINUE, state); pop_stack(2, state); } break; case STMT_BREAK: case STMT_CONTINUE: { int i; Loop *loop = 0; /* silence warnings */ if (stmt->s.exit == -1) { emit_extended_byte(EOP_EXIT, state); if (state->num_loops == 0) panic("No loop to exit, in CODE_GEN!"); loop = &(state->loops[state->num_loops - 1]); } else { emit_extended_byte(EOP_EXIT_ID, state); add_var_ref(stmt->s.exit, state); for (i = state->num_loops - 1; i >= 0; i--) if (state->loops[i].id == stmt->s.exit) { loop = &(state->loops[i]); break; } if (i < 0) panic("Can't find loop in CONTINUE_LOOP!"); } if (stmt->kind == STMT_CONTINUE) { add_stack_ref(loop->top_stack, state); add_known_label(loop->top_label, state); } else { add_stack_ref(loop->bottom_stack, state); loop->bottom_label = add_linked_label(loop->bottom_label, state); } } break; default: panic("Can't happen in GENERATE_STMT()"); } } } static unsigned max(unsigned a, unsigned b) { return a > b ? a : b; } static unsigned ref_size(unsigned max) { if (max <= 256) return 1; else if (max <= 256 * 256) return 2; else return 4; } static Bytecodes stmt_to_code(Stmt *stmt, GState *gstate) { State state; Bytecodes bc; unsigned old_i, new_i, fix_i; Fixup *fixup; init_state(&state, gstate); generate_stmt(stmt, &state); emit_byte(OP_DONE, &state); if (state.cur_stack != 0) panic("Stack not entirely popped in STMT_TO_CODE()"); if (state.saved_stack != UINT_MAX) panic("Still a saved stack index in STMT_TO_CODE()"); /* The max()ing here with gstate->* is wrong (since that's a global * cumulative count, and thus unrelated to the local maximum), but required * in order to maintain the validity of old program counters stored for * suspended tasks... */ bc.numbytes_literal = ref_size(max(state.max_literal, gstate->num_literals)); bc.numbytes_fork = ref_size(max(state.max_fork, gstate->num_fork_vectors)); bc.numbytes_var_name = ref_size(max(state.max_var_ref, gstate->total_var_refs)); bc.size = state.num_bytes + (bc.numbytes_literal - 1) * state.num_literals + (bc.numbytes_fork - 1) * state.num_forks + (bc.numbytes_var_name - 1) * state.num_var_refs; if (bc.size <= 256) bc.numbytes_label = 1; else if (bc.size + state.num_labels <= 256 * 256) bc.numbytes_label = 2; else bc.numbytes_label = 4; bc.size += (bc.numbytes_label - 1) * state.num_labels; bc.max_stack = state.max_stack; bc.numbytes_stack = ref_size(state.max_stack); bc.vector = mymalloc(sizeof(Byte) * bc.size, M_BYTECODES); fixup = state.fixups; fix_i = 0; for (old_i = new_i = 0; old_i < state.num_bytes; old_i++) { if (fix_i < state.num_fixups && fixup->pc == old_i) { unsigned value, size = 0; /* initialized to silence warning */ value = fixup->value; switch (fixup->kind) { case FIXUP_LITERAL: size = bc.numbytes_literal; break; case FIXUP_FORK: size = bc.numbytes_fork; break; case FIXUP_VAR_REF: size = bc.numbytes_var_name; break; case FIXUP_STACK: size = bc.numbytes_stack; break; case FIXUP_LABEL: value += fixup->prev_literals * (bc.numbytes_literal - 1) + fixup->prev_forks * (bc.numbytes_fork - 1) + fixup->prev_var_refs * (bc.numbytes_var_name - 1) + fixup->prev_labels * (bc.numbytes_label - 1) + fixup->prev_stacks * (bc.numbytes_stack - 1); size = bc.numbytes_label; break; default: panic("Can't happen #1 in STMT_TO_CODE()"); } switch (size) { case 4: bc.vector[new_i++] = value >> 24; bc.vector[new_i++] = value >> 16; case 2: bc.vector[new_i++] = value >> 8; case 1: bc.vector[new_i++] = value; break; default: panic("Can't happen #2 in STMT_TO_CODE()"); } fixup++; fix_i++; } else bc.vector[new_i++] = state.bytes[old_i]; } free_state(state); return bc; } Program * generate_code(Stmt *stmt, DB_Version version) { Program *prog = new_program(); GState gstate; init_gstate(&gstate); prog->main_vector = stmt_to_code(stmt, &gstate); prog->version = version; if (gstate.literals) { unsigned i; prog->literals = mymalloc(sizeof(Var) * gstate.num_literals, M_LIT_LIST); prog->num_literals = gstate.num_literals; for (i = 0; i < gstate.num_literals; i++) prog->literals[i] = gstate.literals[i]; } else { prog->literals = 0; prog->num_literals = 0; } if (gstate.fork_vectors) { unsigned i; prog->fork_vectors = mymalloc(sizeof(Bytecodes) * gstate.num_fork_vectors, M_FORK_VECTORS); prog->fork_vectors_size = gstate.num_fork_vectors; for (i = 0; i < gstate.num_fork_vectors; i++) prog->fork_vectors[i] = gstate.fork_vectors[i]; } else { prog->fork_vectors = 0; prog->fork_vectors_size = 0; } free_gstate(gstate); return prog; } char rcsid_code_gen[] = "$Id: code_gen.c,v 2.4 1996/02/08 07:21:08 pavel Exp $"; /* $Log: code_gen.c,v $ * Revision 2.4 1996/02/08 07:21:08 pavel * Renamed TYPE_NUM to TYPE_INT. Added support for exponentiation expression, * named WHILE loop and BREAK and CONTINUE statement. Updated copyright * notice for 1996. Release 1.8.0beta1. * * Revision 2.3 1996/01/16 07:17:36 pavel * Add support for scattering assignment. Release 1.8.0alpha6. * * Revision 2.2 1995/12/31 03:10:47 pavel * Added general support for managing stack references as another kind of * fixup and for a single stack of remembered stack positions. Used that * stack for remembering the positions of indexed/subranged values and for * implementing the `$' expression. Release 1.8.0alpha4. * * Revision 2.1 1995/11/30 04:18:56 pavel * New baseline version, corresponding to release 1.8.0alpha1. * * Revision 2.0 1995/11/30 04:16:54 pavel * Initial RCS-controlled version. */