# include "comp.h" # include "str.h" # include "array.h" # include "object.h" # include "xfloat.h" # include "interpret.h" # include "data.h" # include "table.h" # include "node.h" # include "control.h" # include "compile.h" # include "codegen.h" # define PUSH 0 # define POP 1 # define INTVAL 2 # define TRUTHVAL 3 # define TOPTRUTHVAL 4 static void output(); static void cg_iexpr P((node*, int)); static void cg_expr P((node*, int)); static void cg_stmt P((node*)); static int vars[MAX_LOCALS]; /* local variable types */ static int nvars; /* number of local variables */ static int nparam; /* how many of those are arguments */ static int tvc; /* tmpval count */ static int catch_level; /* level of nested catches */ static Int kf_call_trace, kf_call_other, kf_clone_object, kf_editor; /* * NAME: tmpval() * DESCRIPTION: return a new temporary value index */ static int tmpval() { if (tvc == NTMPVAL - 1) { c_error("out of temporary values (%d)", NTMPVAL); } return tvc++; } /* * NAME: local() * DESCRIPTION: output a local or argument */ static char *local(n) register int n; { static char buffer[16]; n = nparam - n - 1; if (n < 0) { sprintf(buffer, "(f->fp - %d)", -n); } else { sprintf(buffer, "(f->argp + %d)", n); } return buffer; } /* * NAME: comma() * DESCRIPTION: output a comma */ static void comma() { output(",\n"); } /* * NAME: kfun() * DESCRIPTION: output a kfun call */ static void kfun(func) char *func; { output("kf_%s(f)", func); } /* * NAME: store() * DESCRIPTION: output store code */ static void store() { output(", store()"); } /* * NAME: codegen->cast() * DESCRIPTION: generate code for a cast */ static void cg_cast(what, type) char *what; unsigned short type; { if ((type & T_REF) != 0) { type = T_ARRAY; } output("i_cast(%s, %u)", what, type); } /* * NAME: codegen->lvalue() * DESCRIPTION: generate code for an lvalue */ static void cg_lvalue(n, type) register node *n; register int type; { register node *m; if (type & T_REF) { type = T_ARRAY; } if (n->type == N_CAST) { n = n->l.left; } switch (n->type) { case N_LOCAL: output("push_lvalue(%s, %d)", local((int) n->r.number), type); break; case N_GLOBAL: output("i_global_lvalue(f, %d, %d, %d)", ((int) n->r.number >> 8) & 0xff, ((int) n->r.number) & 0xff, type); break; case N_INDEX: m = n->l.left; if (m->type == N_CAST) { m = m->l.left; } switch (m->type) { case N_LOCAL: output("push_lvalue(%s, 0)", local((int) m->r.number)); break; case N_GLOBAL: output("i_global_lvalue(f, %d, %d, 0)", ((int) m->r.number >> 8) & 0xff, ((int) m->r.number) & 0xff); break; case N_INDEX: cg_expr(m->l.left, PUSH); comma(); cg_expr(m->r.right, PUSH); output(", i_index_lvalue(f, 0)"); break; default: cg_expr(m, PUSH); break; } comma(); cg_expr(n->r.right, PUSH); output(", i_index_lvalue(f, %d)", type); break; } } /* * NAME: codegen->fetch() * DESCRIPTION: generate code for a fetched lvalue */ static void cg_fetch(n) node *n; { cg_lvalue(n, 0); output(", i_fetch(f), "); if (n->type == N_CAST) { cg_cast("f->sp", n->mod); comma(); } } /* * NAME: codegen->iasgn() * DESCRIPTION: handle general integer assignment (operator) case */ static void cg_iasgn(n, op, i, direct) register node *n; char *op; register int i; bool direct; { if (i < 0) { /* assignment on stack */ if (n->type == N_INT) { output("f->sp->u.number %s ", op); cg_iexpr(n, TRUE); } else { i = tmpval(); output("tv[%d] = ", i); cg_iexpr(n, TRUE); output(", f->sp->u.number %s tv[%d]", op, i); } } else { /* assignment to register var */ if (catch_level != 0) { output("%s->u.number = ", local(i)); } output("ivar%d %s ", vars[i], op); cg_iexpr(n, direct); } } /* * NAME: codegen->iasgnop() * DESCRIPTION: handle an integer assignment operator */ static void cg_iasgnop(n, op, direct) register node *n; char *op; bool direct; { if (n->l.left->type == N_LOCAL) { cg_iasgn(n->r.right, op, (int) n->l.left->r.number, direct); } else { cg_fetch(n->l.left); cg_iasgn(n->r.right, op, -1, TRUE); output(", store_int()"); } } /* * NAME: codegen->uasgnop() * DESCRIPTION: handle an unsigned integer assignment operator */ static void cg_uasgnop(n, op, direct) register node *n; char *op; bool direct; { register int i; if (n->l.left->type == N_LOCAL) { /* * local variable */ i = n->l.left->r.number; n = n->r.right; if (catch_level != 0) { output("%s->u.number = ", local(i)); } output("ivar%d = ((Uint) ivar%d) %s ", vars[i], vars[i], op); cg_iexpr(n, direct); } else { cg_fetch(n->l.left); n = n->r.right; if (n->type == N_INT) { output("f->sp->u.number = ((Uint) f->sp->u.number) %s ", op); cg_iexpr(n, TRUE); } else { i = tmpval(); output("tv[%d] = ", i); cg_iexpr(n, TRUE); output(", f->sp->u.number = ((Uint) f->sp->u.number) %s tv[%d]", op, i); } output(", store_int()"); } } /* * NAME: codegen->ifasgnop() * DESCRIPTION: handle a function integer assignment operator */ static void cg_ifasgnop(n, op, direct) register node *n; char *op; bool direct; { register int i; if (n->l.left->type == N_LOCAL) { i = n->l.left->r.number; if(catch_level != 0) { output("%s->u.number = ", local(i)); } output("ivar%d = %s(ivar%d, ", vars[i], op, vars[i]); cg_iexpr(n->r.right, direct); output(")"); } else { cg_fetch(n->l.left); n = n->r.right; if (n->type == N_INT) { output("f->sp->u.number = %s(f->sp->u.number, ", op); cg_iexpr(n, TRUE); output("), store_int()"); } else { i = tmpval(); output("tv[%d] = %s(f->sp->u.number, ", i, op); cg_iexpr(n, TRUE); output("), f->sp->u.number = tv[%d], store_int()", op, i); } } } /* * NAME: codegen->ibinop() * DESCRIPTION: generate code for an integer binary operator */ static void cg_ibinop(n, op, direct) register node *n; char *op; bool direct; { if (!((n->l.left->flags | n->r.right->flags) & F_CONST) && n->l.left->type != N_LOCAL && n->r.right->type != N_LOCAL) { /* neither is a constant or a local intvar */ direct = FALSE; } cg_iexpr(n->l.left, direct); output(" %s ", op); cg_iexpr(n->r.right, direct); } /* * NAME: codegen->iexpr() * DESCRIPTION: generate code for an integer expression */ static void cg_iexpr(n, direct) register node *n; int direct; { register int i; output("("); switch (n->type) { case N_ADD: case N_ADD_EQ: case N_ADD_EQ_1: case N_AGGR: case N_AND: case N_AND_EQ: case N_ASSIGN: case N_CATCH: case N_DIV: case N_DIV_EQ: case N_EQ: case N_FLOAT: case N_FUNC: case N_GE: case N_GLOBAL: case N_GT: case N_INDEX: case N_LE: case N_LSHIFT: case N_LSHIFT_EQ: case N_LT: case N_LVALUE: case N_MOD: case N_MOD_EQ: case N_MULT: case N_MULT_EQ: case N_NE: case N_OR: case N_OR_EQ: case N_RANGE: case N_RSHIFT: case N_RSHIFT_EQ: case N_STR: case N_SUB: case N_SUB_EQ: case N_SUB_EQ_1: case N_SUM: case N_SUM_EQ: case N_TOFLOAT: case N_TOINT: case N_TOSTRING: case N_XOR: case N_XOR_EQ: case N_MIN_MIN: case N_PLUS_PLUS: if (direct) { cg_expr(n, INTVAL); } else { i = tmpval(); output("tv[%d] = (", i); cg_expr(n, INTVAL); output("), tv[%d]", i); } break; case N_ADD_INT: cg_ibinop(n, "+", direct); break; case N_ADD_EQ_INT: cg_iasgnop(n, "+=", direct); break; case N_ADD_EQ_1_INT: if (n->l.left->type == N_LOCAL) { if (catch_level != 0) { output("%s->u.number = ", local((int) n->l.left->r.number)); } output("++ivar%d", vars[n->l.left->r.number]); } else { cg_fetch(n->l.left); output("++f->sp->u.number, store_int()"); } break; case N_AND_INT: cg_ibinop(n, "&", direct); break; case N_AND_EQ_INT: cg_iasgnop(n, "&=", direct); break; case N_CAST: if (n->l.left->type == N_LOCAL) { cg_cast(local((int) n->l.left->r.number), T_INT); output(", %s->u.number", local((int) n->l.left->r.number)); } else { cg_expr(n->l.left, PUSH); comma(); cg_cast("f->sp", T_INT); if (direct) { output(", (f->sp++)->u.number"); } else { i = tmpval(); output(", tv[%d] = (f->sp++)->u.number, tv[%d]", i, i); } } break; case N_COMMA: cg_expr(n->l.left, POP); comma(); cg_iexpr(n->r.right, direct); break; case N_DIV_INT: output("xdiv("); cg_ibinop(n, ",", direct); output(")"); break; case N_DIV_EQ_INT: cg_ifasgnop(n, "xdiv", direct); break; case N_EQ_INT: cg_ibinop(n, "==", direct); break; case N_GE_INT: cg_ibinop(n, ">=", direct); break; case N_GT_INT: cg_ibinop(n, ">", direct); break; case N_INT: output("(Int) 0x%lxL", (long) n->l.number); break; case N_LAND: output("("); cg_expr(n->l.left, TOPTRUTHVAL); output(") && ("); cg_expr(n->r.right, (direct) ? TOPTRUTHVAL : TRUTHVAL); output(")"); break; case N_LE_INT: cg_ibinop(n, "<=", direct); break; case N_LOCAL: output("ivar%d", vars[n->r.number]); break; case N_LOR: output("("); cg_expr(n->l.left, TOPTRUTHVAL); output(") || ("); cg_expr(n->r.right, (direct) ? TOPTRUTHVAL : TRUTHVAL); output(")"); break; case N_LSHIFT_INT: output("(Uint) ("); cg_ibinop(n, ") <<", direct); break; case N_LSHIFT_EQ_INT: cg_uasgnop(n, "<<", direct); break; case N_LT_INT: cg_ibinop(n, "<", direct); break; case N_MOD_INT: output("xmod("); cg_ibinop(n, ",", direct); output(")"); break; case N_MOD_EQ_INT: cg_ifasgnop(n, "xmod", direct); break; case N_MULT_INT: cg_ibinop(n, "*", direct); break; case N_MULT_EQ_INT: cg_iasgnop(n, "*=", direct); break; case N_NE_INT: cg_ibinop(n, "!=", direct); break; case N_NOT: output("!"); if (n->l.left->mod == T_INT) { cg_iexpr(n->l.left, direct); } else { output("("); cg_expr(n->l.left, (direct) ? TOPTRUTHVAL : TRUTHVAL); output(")"); } break; case N_OR_INT: cg_ibinop(n, "|", direct); break; case N_OR_EQ_INT: cg_iasgnop(n, "|=", direct); break; case N_QUEST: output("("); cg_expr(n->l.left, TOPTRUTHVAL); output(") ? "); if (n->r.right->l.left != (node *) NULL) { cg_iexpr(n->r.right->l.left, direct); } else { output("0"); } output(" : "); if (n->r.right->r.right != (node *) NULL) { cg_iexpr(n->r.right->r.right, direct); } else { output("0"); } break; case N_RSHIFT_INT: output("(Uint) ("); cg_ibinop(n, ") >>", direct); break; case N_RSHIFT_EQ_INT: cg_uasgnop(n, ">>", direct); break; case N_SUB_INT: if (n->l.left->type == N_INT && n->l.left->l.number == 0) { output("-"); cg_iexpr(n->r.right, direct); } else { cg_ibinop(n, "-", direct); } break; case N_SUB_EQ_INT: cg_iasgnop(n, "-=", direct); break; case N_SUB_EQ_1_INT: if (n->l.left->type == N_LOCAL) { if (catch_level != 0) { output("%s->u.number = ", local((int) n->l.left->r.number)); } output("--ivar%d", vars[n->l.left->r.number]); } else { cg_fetch(n->l.left); output("--f->sp->u.number, store_int()"); } break; case N_TST: if (n->l.left->mod == T_INT) { output("!!"); cg_iexpr(n->l.left, direct); } else { cg_expr(n->l.left, (direct) ? TOPTRUTHVAL : TRUTHVAL); } break; case N_UPLUS: cg_iexpr(n->l.left, direct); break; case N_XOR_INT: if (n->r.right->type == N_INT && n->r.right->l.number == -1) { output("~"); cg_iexpr(n->l.left, direct); } else { cg_ibinop(n, "^", direct); } break; case N_XOR_EQ_INT: cg_iasgnop(n, "^=", direct); break; case N_MIN_MIN_INT: if (n->l.left->type == N_LOCAL) { if (catch_level != 0) { output("%s->u.number--, ", local((int) n->l.left->r.number)); } output("ivar%d--", vars[n->l.left->r.number]); } else { cg_fetch(n->l.left); output("f->sp->u.number--, store_int() + 1"); } break; case N_PLUS_PLUS_INT: if (n->l.left->type == N_LOCAL) { if (catch_level != 0) { output("%s->u.number++, ", local((int) n->l.left->r.number)); } output("ivar%d++", vars[n->l.left->r.number]); } else { cg_fetch(n->l.left); output("f->sp->u.number++, store_int() - 1"); } break; } output(")"); } /* * NAME: codegen->asgnop() * DESCRIPTION: generate code for an assignment operator */ static void cg_asgnop(n, op) register node *n; char *op; { if (n->l.left->type == N_LOCAL && vars[n->l.left->r.number] != 0) { output("PUSH_NUMBER ivar%d, ", vars[n->l.left->r.number]); cg_expr(n->r.right, PUSH); comma(); kfun(op); comma(); cg_cast("f->sp", T_INT); comma(); if (catch_level != 0) { output("%s->u.number = ", local((int) n->l.left->r.number)); } output("ivar%d = f->sp->u.number", vars[n->l.left->r.number]); } else { cg_fetch(n->l.left); cg_expr(n->r.right, PUSH); comma(); kfun(op); store(); } } /* * NAME: codegen->aggr() * DESCRIPTION: generate code for an aggregate */ static int cg_aggr(n) register node *n; { register int i; if (n == (node *) NULL) { return 0; } for (i = 1; n->type == N_PAIR; i++) { cg_expr(n->l.left, PUSH); comma(); n = n->r.right; } cg_expr(n, PUSH); comma(); return i; } /* * NAME: codegen->map_aggr() * DESCRIPTION: generate code for a mapping aggregate */ static int cg_map_aggr(n) register node *n; { register int i; if (n == (node *) NULL) { return 0; } for (i = 2; n->type == N_PAIR; i += 2) { cg_expr(n->l.left->l.left, PUSH); comma(); cg_expr(n->l.left->r.right, PUSH); comma(); n = n->r.right; } cg_expr(n->l.left, PUSH); comma(); cg_expr(n->r.right, PUSH); comma(); return i; } /* * NAME: codegen->sumargs() * DESCRIPTION: generate code for summand arguments */ static int cg_sumargs(n) register node *n; { int i; if (n->type == N_SUM) { i = cg_sumargs(n->l.left); n = n->r.right; } else { i = 0; } if (n->type == N_AGGR) { output("PUSH_NUMBER %d", -3 - cg_aggr(n->l.left)); } else if (n->type == N_RANGE) { cg_expr(n->l.left, PUSH); comma(); n = n->r.right; if (n->l.left != (node *) NULL) { cg_expr(n->l.left, PUSH); comma(); if (n->r.right != (node *) NULL) { cg_expr(n->r.right, PUSH); comma(); kfun("ckrangeft"); } else { kfun("ckrangef"); } } else if (n->r.right != (node *) NULL) { cg_expr(n->r.right, PUSH); comma(); kfun("ckranget"); } else { kfun("range"); output(", PUSH_NUMBER -2"); } } else { cg_expr(n, PUSH); output(", PUSH_NUMBER -2"); } comma(); return i + 1; } /* * NAME: codegen->funargs() * DESCRIPTION: generate code for function arguments */ static char *cg_funargs(n, lv) register node *n; bool lv; { static char buffer[32]; register int i; if (n == (node *) NULL) { return "0"; } for (i = 1; n->type == N_PAIR; i++) { cg_expr(n->l.left, PUSH); comma(); n = n->r.right; } if (n->type == N_SPREAD) { int type; if (!lv || (type=n->l.left->mod & ~(1 << REFSHIFT)) == T_MIXED) { type = 0; } cg_expr(n->l.left, PUSH); comma(); sprintf(buffer, "%d + i_spread(f, %d, %d)", i, (short) n->mod, type); } else { cg_expr(n, PUSH); comma(); sprintf(buffer, "%d", i); } return buffer; } /* * NAME: codegen->locals() * DESCRIPTION: propagate values between local variables and ivars */ static void cg_locals(n, vtoi) register node *n; bool vtoi; { if (n != (node *) NULL) { register node *m; register int i; /* skip non-lvalue arguments */ while (n->type == N_PAIR && n->l.left->type != N_LVALUE) { n = n->r.right; } while (n->type == N_PAIR) { m = n->l.left; if (m->l.left->type == N_LOCAL && vars[i = m->l.left->r.number] != 0) { if (vtoi) { output("%s->u.number = ivar%d, ", local(i), vars[i]); } else { output(", ivar%d = %s->u.number", vars[i], local(i)); } } n = n->r.right; } /* last one can be lvalue or spread array */ m = n->l.left; if (n->type == N_LVALUE && m->type == N_LOCAL && vars[i = m->r.number] != 0) { if (vtoi) { output("%s->u.number = ivar%d, ", local(i), vars[i]); } else { output(", ivar%d = %s->u.number", vars[i], local(i)); } } } } /* * NAME: codegen->binop() * DESCRIPTION: generate code for a binary operator */ static void cg_binop(n) node *n; { cg_expr(n->l.left, PUSH); comma(); cg_expr(n->r.right, PUSH); comma(); } /* * NAME: codegen->expr() * DESCRIPTION: generate code for an expression */ static void cg_expr(n, state) register node *n; register int state; { register int i; long l; char *p; switch (n->type) { case N_ADD_INT: case N_ADD_EQ_INT: case N_ADD_EQ_1_INT: case N_AND_INT: case N_AND_EQ_INT: case N_DIV_INT: case N_DIV_EQ_INT: case N_EQ_INT: case N_GE_INT: case N_GT_INT: case N_LAND: case N_LE_INT: case N_LOR: case N_LSHIFT_INT: case N_LSHIFT_EQ_INT: case N_LT_INT: case N_MOD_INT: case N_MOD_EQ_INT: case N_MULT_INT: case N_MULT_EQ_INT: case N_NE_INT: case N_OR_INT: case N_OR_EQ_INT: case N_RSHIFT_INT: case N_RSHIFT_EQ_INT: case N_SUB_INT: case N_SUB_EQ_INT: case N_SUB_EQ_1_INT: case N_XOR_INT: case N_XOR_EQ_INT: case N_MIN_MIN_INT: case N_PLUS_PLUS_INT: if (state == PUSH) { i = tmpval(); output("tv[%d] = ", i); cg_iexpr(n, TRUE); output(", PUSH_NUMBER tv[%d]", i); } else { cg_iexpr(n, (state != TRUTHVAL)); } return; case N_ADD: cg_expr(n->l.left, PUSH); comma(); if (n->r.right->type == N_FLOAT) { if (NFLT_ISONE(n->r.right)) { kfun("add1"); break; } if (NFLT_ISMONE(n->r.right)) { kfun("sub1"); break; } } cg_expr(n->r.right, PUSH); comma(); kfun("add"); break; case N_ADD_EQ: cg_asgnop(n, "add"); break; case N_ADD_EQ_1: cg_fetch(n->l.left); kfun("add1"); store(); break; case N_AGGR: if (n->mod == T_MAPPING) { output("i_map_aggregate(f, %u)", cg_map_aggr(n->l.left)); } else { output("i_aggregate(f, %u)", cg_aggr(n->l.left)); } break; case N_AND: cg_binop(n); kfun("and"); break; case N_AND_EQ: cg_asgnop(n, "and"); break; case N_ASSIGN: if (n->l.left->type == N_LOCAL && vars[n->l.left->r.number] != 0) { cg_iasgn(n->r.right, "=", (int) n->l.left->r.number, (state != PUSH && state != TRUTHVAL)); if (state == PUSH) { output(", PUSH_NUMBER ivar%d", vars[n->l.left->r.number]); } return; } if (n->r.right->type == N_CAST) { cg_lvalue(n->l.left, n->r.right->mod); n->r.right = n->r.right->l.left; } else { cg_lvalue(n->l.left, 0); } comma(); cg_expr(n->r.right, PUSH); store(); break; case N_CAST: cg_expr(n->l.left, PUSH); comma(); cg_cast("f->sp", n->mod); break; case N_CATCH: output("(pre_catch(f), "); if (catch_level == 0) { for (i = nvars; i > 0; ) { if (vars[--i] != 0) { output("%s->u.number = ivar%d, ", local(i), vars[i]); } } } output("!ec_push((ec_ftn) i_catcherr) ? ("); catch_level++; cg_expr(n->l.left, POP); --catch_level; if (state == PUSH) { output(", ec_pop(), PUSH_NUMBER 0) : (p = errormesg(), "); output("(--f->sp)->type = T_STRING, str_ref(f->sp->u.string = "); output("str_new(p, (long) strlen(p)))"); if (catch_level == 0) { for (i = nvars; i > 0; ) { if (vars[--i] != 0) { output(", ivar%d = %s->u.number", vars[i], local(i)); } } } output("), post_catch(f, 0))"); } else { output(", ec_pop(), post_catch(f, 0), FALSE) : ("); if (catch_level == 0) { for (i = nvars; i > 0; ) { if (vars[--i] != 0) { output("ivar%d = %s->u.number, ", vars[i], local(i)); } } } output("post_catch(f, 0), TRUE))"); } return; case N_COMMA: cg_expr(n->l.left, POP); comma(); cg_expr(n->r.right, state); return; case N_DIV: cg_binop(n); kfun("div"); break; case N_DIV_EQ: cg_asgnop(n, "div"); break; case N_EQ: cg_binop(n); kfun("eq"); break; case N_FLOAT: output("(--f->sp)->type = T_FLOAT, f->sp->oindex = 0x%04x, ", n->l.fhigh); output("f->sp->u.objcnt = 0x%08XL", (long) n->r.flow); break; case N_FUNC: p = cg_funargs(n->l.left, (n->r.number >> 24) & KFCALL_LVAL); switch (n->r.number >> 24) { case KFCALL: if (catch_level == 0 && (n->r.number == kf_call_trace || n->r.number == kf_call_other || n->r.number == kf_clone_object || n->r.number == kf_editor)) { for (i = nparam; i > 0; ) { if (vars[--i] != 0) { output("%s->u.number = ivar%d, ", local(i), vars[i]); } } } case KFCALL_LVAL: if (PROTO_NARGS(KFUN((short) n->r.number).proto) == 0) { /* kfun without arguments won't do argument checking */ kfun(KFUN((short) n->r.number).name); } else { if (catch_level == 0 && ((n->r.number >> 24) & KFCALL_LVAL)) { cg_locals(n->l.left, TRUE); } if (PROTO_CLASS(KFUN((short) n->r.number).proto) & C_VARARGS) { output("call_kfun_arg(f, %d/*%s*/, %s)", &KFUN((short) n->r.number) - kftab, KFUN((short) n->r.number).name, p); } else { output("call_kfun(f, %d/*%s*/)", &KFUN((short) n->r.number) - kftab, KFUN((short) n->r.number).name); } if ((n->r.number >> 24) & KFCALL_LVAL) { cg_locals(n->l.left, FALSE); } } break; case DFCALL: if (catch_level == 0) { for (i = nparam; i > 0; ) { if (vars[--i] != 0) { output("%s->u.number = ivar%d, ", local(i), vars[i]); } } } if (((n->r.number >> 8) & 0xff) == 0) { output("i_funcall(f, (object *) NULL, 0, %d, %s)", ((int) n->r.number) & 0xff, p); } else { output("i_funcall(f, (object *) NULL, f->p_index-%d, %d, %s)", ((int) n->r.number >> 8) & 0xff, ((int) n->r.number) & 0xff, p); } break; case FCALL: if (catch_level == 0) { for (i = nparam; i > 0; ) { if (vars[--i] != 0) { output("%s->u.number = ivar%d, ", local(i), vars[i]); } } } output("p = i_foffset(%u), ", ctrl_gencall((long) n->r.number)); output("i_funcall(f, (object *) NULL, UCHAR(p[0]), UCHAR(p[1]), %s)", p); break; } break; case N_GE: cg_binop(n); kfun("ge"); break; case N_GLOBAL: output("i_global(f, %d, %d)", ((int) n->r.number >> 8) & 0xff, ((int) n->r.number) & 0xff); break; case N_GT: cg_binop(n); kfun("gt"); break; case N_INDEX: cg_binop(n); output("i_index(f)"); break; case N_INT: if (state == PUSH) { output("PUSH_NUMBER "); } cg_iexpr(n, TRUE); return; case N_LE: cg_binop(n); kfun("le"); break; case N_LOCAL: if (vars[n->r.number] != 0) { if (state == PUSH) { output("PUSH_NUMBER "); } output("ivar%d", vars[n->r.number]); return; } if (state == TRUTHVAL || state == TOPTRUTHVAL) { p = local((int) n->r.number); switch (n->mod) { case T_FLOAT: output("VFLT_ISZERO(%s)", p); break; case T_STRING: output("%s->type == T_STRING", p); break; case T_OBJECT: output("%s->type == T_OBJECT", p); break; case T_ARRAY: case T_MAPPING: output("T_INDEXED(%s->type)", p); break; default: output("truthval(%s)", p); break; } return; } output((n->mod == T_FLOAT) ? "*--f->sp = *%s" : "i_push_value(f, %s)", local((int) n->r.number)); break; case N_LSHIFT: cg_binop(n); kfun("lshift"); break; case N_LSHIFT_EQ: cg_asgnop(n, "lshift"); break; case N_LT: cg_binop(n); kfun("lt"); break; case N_LVALUE: cg_lvalue(n->l.left, (n->l.left->mod != T_MIXED) ? n->l.left->mod : 0); break; case N_MOD: cg_binop(n); kfun("mod"); break; case N_MOD_EQ: cg_asgnop(n, "mod"); break; case N_MULT: cg_binop(n); kfun("mult"); break; case N_MULT_EQ: cg_asgnop(n, "mult"); break; case N_NE: cg_binop(n); kfun("ne"); break; case N_NOT: if (state == PUSH) { i = tmpval(); output("tv[%d] = ", i); cg_iexpr(n, TRUE); output(", PUSH_NUMBER tv[%d]", i); } else { output("!"); n = n->l.left; if (n->mod == T_INT) { cg_iexpr(n, (state != TRUTHVAL)); } else { output("("); cg_expr(n, (state != TRUTHVAL) ? TOPTRUTHVAL : TRUTHVAL); output(")"); } } return; case N_OR: cg_expr(n->l.left, PUSH); comma(); cg_expr(n->r.right, PUSH); comma(); kfun("or"); break; case N_OR_EQ: cg_asgnop(n, "or"); break; case N_QUEST: output("("); cg_expr(n->l.left, TOPTRUTHVAL); output(") ? ("); if (n->r.right->l.left != (node *) NULL) { cg_expr(n->r.right->l.left, state); if (state == PUSH || state == POP) { output(", 0"); } } else { output("0"); } output(") : ("); if (n->r.right->r.right != (node *) NULL) { cg_expr(n->r.right->r.right, state); if (state == PUSH || state == POP) { output(", 0"); } } else { output("0"); } output(")"); return; case N_RANGE: cg_expr(n->l.left, PUSH); comma(); n = n->r.right; if (n->l.left != (node *) NULL) { cg_expr(n->l.left, PUSH); comma(); if (n->r.right != (node *) NULL) { cg_expr(n->r.right, PUSH); comma(); kfun("rangeft"); } else { kfun("rangef"); } } else if (n->r.right != (node *) NULL) { cg_expr(n->r.right, PUSH); comma(); kfun("ranget"); } else { kfun("range"); } break; case N_RSHIFT: cg_expr(n->l.left, PUSH); comma(); cg_expr(n->r.right, PUSH); comma(); kfun("rshift"); break; case N_RSHIFT_EQ: cg_asgnop(n, "rshift"); break; case N_STR: l = ctrl_dstring(n->l.string); output("i_string(f, %d, %u)", ((int) (l >> 16)) & 0xff, (unsigned short) l); break; case N_SUB: if ((n->l.left->type == N_INT && n->l.left->l.number == 0) || (n->l.left->type == N_FLOAT && NFLT_ISZERO(n->l.left))) { cg_expr(n->r.right, PUSH); comma(); kfun("umin"); } else { cg_expr(n->l.left, PUSH); comma(); if (n->r.right->type == N_FLOAT) { if (NFLT_ISONE(n->r.right)) { kfun("sub1"); break; } if (NFLT_ISMONE(n->r.right)) { kfun("add1"); break; } } cg_expr(n->r.right, PUSH); comma(); kfun("sub"); } break; case N_SUB_EQ: cg_asgnop(n, "sub"); break; case N_SUB_EQ_1: cg_fetch(n->l.left); kfun("sub1"); store(); break; case N_SUM: output("kf_sum(f, %d)", cg_sumargs(n)); break; case N_SUM_EQ: cg_fetch(n->l.left); output("PUSH_NUMBER -2,\n"); output("kf_sum(f, %d), store()", cg_sumargs(n->r.right) + 1); break; case N_TOFLOAT: cg_expr(n->l.left, PUSH); comma(); kfun("tofloat"); break; case N_TST: if (state == PUSH) { i = tmpval(); output("tv[%d] = ", i); cg_iexpr(n, TRUE); output(", PUSH_NUMBER tv[%d]", i); } else { output("!!"); n = n->l.left; if (n->mod == T_INT) { cg_iexpr(n, (state != TRUTHVAL)); } else { output("("); cg_expr(n, (state != TRUTHVAL) ? TOPTRUTHVAL : TRUTHVAL); output(")"); } } return; case N_TOINT: cg_expr(n->l.left, PUSH); comma(); kfun("toint"); break; case N_TOSTRING: cg_expr(n->l.left, PUSH); comma(); kfun("tostring"); break; case N_UPLUS: cg_expr(n->l.left, state); return; case N_XOR: if (n->r.right->type == N_INT && n->r.right->l.number == -1) { cg_expr(n->l.left, PUSH); comma(); kfun("neg"); } else { cg_expr(n->l.left, PUSH); comma(); cg_expr(n->r.right, PUSH); comma(); kfun("xor"); } break; case N_XOR_EQ: if (n->r.right->type == N_INT && n->r.right->l.number == -1) { cg_fetch(n->l.left); kfun("neg"); store(); } else { cg_asgnop(n, "xor"); } break; case N_MIN_MIN: cg_fetch(n->l.left); kfun("sub1"); store(); comma(); if (n->mod == T_INT) { output("f->sp->u.number++"); } else { kfun("add1"); } break; case N_PLUS_PLUS: cg_fetch(n->l.left); kfun("add1"); store(); comma(); if (n->mod == T_INT) { output("f->sp->u.number--"); } else { kfun("sub1"); } break; # ifdef DEBUG default: fatal("unknown expression type %d", n->type); # endif } switch (state) { case POP: if ((n->type != N_FUNC || n->r.number >> 24 != FCALL) && (n->mod == T_INT || n->mod == T_FLOAT || n->mod == T_OBJECT || n->mod == T_VOID)) { output(", f->sp++"); } else { output(", i_del_value(f->sp++)"); } break; case INTVAL: output(", (f->sp++)->u.number"); break; case TRUTHVAL: if (n->mod == T_INT) { i = tmpval(); output(", tv[%d] = (f->sp++)->u.number, tv[%d]", i, i); } else { output(", poptruthval(f)"); } break; case TOPTRUTHVAL: switch (n->mod) { case T_INT: output(", (f->sp++)->u.number"); break; case T_FLOAT: output(", f->sp++, VFLT_ISZERO(f->sp - 1)"); break; case T_OBJECT: output(", (f->sp++)->type == T_OBJECT"); break; default: output(", poptruthval(f)"); break; } break; } } static Int *switch_table; /* label table for current switch */ static int swcount; /* current switch number */ static int outcount; /* switch table element count */ /* * NAME: outint() * DESCRIPTION: output an integer */ static void outint(i) Int i; { if (outcount == 8) { output("\n"); outcount = 0; } output("%ld, ", (long) i); outcount++; } /* * NAME: outchar() * DESCRIPTION: output a character */ static void outchar(c) char c; { if (outcount == 16) { output("\n"); outcount = 0; } output("%d, ", c); outcount++; } /* * NAME: codegen->switch_int() * DESCRIPTION: generate single label code for a switch statement */ static void cg_switch_int(n) register node *n; { register node *m; register int i, size; Int *table; /* * switch table */ m = n->l.left; size = n->mod; if (m->l.left == (node *) NULL) { /* explicit default */ m = m->r.right; } else { /* implicit default */ size++; } table = switch_table; switch_table = ALLOCA(Int, size); i = 1; do { switch_table[i++] = m->l.left->l.number; m = m->r.right; } while (i < size); /* * switch expression */ if (n->r.right->l.left->mod == T_INT) { output("switch ("); cg_expr(n->r.right->l.left, INTVAL); output(") {\n"); switch_table[0] = 0; } else { cg_expr(n->r.right->l.left, PUSH); output(";\nif (f->sp->type != T_INT) { i_del_value(f->sp++);"); output(" goto sw%d; }", ++swcount); output("\nswitch ((f->sp++)->u.number) {\n"); switch_table[0] = swcount; } /* * generate code for body */ cg_stmt(n->r.right->r.right); output("}\n"); if (switch_table[0] > 0) { output("sw%d: ;\n", (int) switch_table[0]); } AFREE(switch_table); switch_table = table; } /* * NAME: codegen->switch_range() * DESCRIPTION: generate range label code for a switch statement */ static void cg_switch_range(n) register node *n; { register node *m; register int i, size; Int *table; /* * switch table */ m = n->l.left; size = n->mod; if (m->l.left == (node *) NULL) { /* explicit default */ m = m->r.right; } else { /* implicit default */ size++; } table = switch_table; switch_table = ALLOCA(Int, size); output("{\nstatic Int swtab[] = {\n"); outcount = 0; i = 1; do { switch_table[i] = i; outint(m->l.left->l.number); outint(m->l.left->r.number); m = m->r.right; } while (++i < size); output("\n};\n"); /* * switch expression */ if (n->r.right->l.left->mod == T_INT) { output("switch (switch_range(("); cg_expr(n->r.right->l.left, INTVAL); output("), swtab, %d)) {\n", size - 1); switch_table[0] = 0; } else { cg_expr(n->r.right->l.left, PUSH); output(";\nif (f->sp->type != T_INT) { i_del_value(f->sp++);"); output(" goto sw%d; }", ++swcount); output("\nswitch (switch_range((f->sp++)->u.number, swtab, %d)) {\n", size - 1); switch_table[0] = swcount; } /* * generate code for body */ cg_stmt(n->r.right->r.right); output("}\n}\n"); if (switch_table[0] > 0) { output("sw%d: ;\n", (int) switch_table[0]); } AFREE(switch_table); switch_table = table; } /* * NAME: codegen->switch_str() * DESCRIPTION: generate code for a string switch statement */ static void cg_switch_str(n) register node *n; { register node *m; register int i, size; Int *table; /* * switch table */ m = n->l.left; size = n->mod; if (m->l.left == (node *) NULL) { /* explicit default */ m = m->r.right; } else { /* implicit default */ size++; } table = switch_table; switch_table = ALLOCA(Int, size); output("{\nstatic char swtab[] = {\n"); outcount = 0; i = 1; if (m->l.left->type == N_INT) { /* * 0 */ outchar(0); switch_table[i++] = 1; m = m->r.right; } else { /* no 0 case */ outchar(1); } do { register long l; switch_table[i] = i; l = ctrl_dstring(m->l.left->l.string); outchar((char) (l >> 16)); outchar((char) (l >> 8)); outchar((char) l); m = m->r.right; } while (++i < size); output("\n};\n"); /* * switch expression */ cg_expr(n->r.right->l.left, PUSH); output(";\nswitch (switch_str(f->sp++, f->p_ctrl, swtab, %d)) {\n", size - 1); switch_table[0] = 0; /* * generate code for body */ cg_stmt(n->r.right->r.right); output("}\n}\n"); AFREE(switch_table); switch_table = table; } typedef struct _rclink_ { int type; /* rlimits or catch */ struct _rclink_ *next; /* next in linked list */ } rclink; static rclink *rclist; /* * NAME: breakout() * DESCRIPTION: break out of a catch or rlimits */ static void breakout(n) register int n; { register int c, r; register rclink *link; c = r = 0; link = rclist; do { if (link->type == N_CATCH) { c++; r = 0; } else { r++; } link = link->next; } while (--n != 0); if (c != 0) { output("post_catch(f, %d);", c); } if (r != 0) { output("i_set_rllevel(f, -%u);", r); } } /* * NAME: codegen->stmt() * DESCRIPTION: generate code for a statement */ static void cg_stmt(n) register node *n; { rclink rcstart; register node *m; register int i; while (n != (node *) NULL) { if (n->type == N_PAIR) { m = n->l.left; n = n->r.right; } else { m = n; n = (node *) NULL; } if (catch_level == 0) { tvc = 0; } switch (m->type) { case N_BLOCK: cg_stmt(m->l.left); break; case N_BREAK: if (m->mod != 0) { breakout(m->mod); } output("break;\n"); break; case N_CASE: if (m->mod == 0) { if (switch_table[0] > 0) { output("sw%d:\n", (int) switch_table[0]); switch_table[0] = 0; } output("default:\n"); } else { output("case %ld:\n", (long) switch_table[m->mod]); } cg_stmt(m->l.left); break; case N_CONTINUE: if (m->mod != 0) { breakout(m->mod); } output("continue;\n"); break; case N_DO: output("do {\ni_add_ticks(f, 1);\n"); cg_stmt(m->r.right); output("} while ("); tvc = 0; cg_expr(m->l.left, TOPTRUTHVAL); output(");\n"); break; case N_FOR: output("for (;"); cg_expr(m->l.left, TOPTRUTHVAL); output(";"); m = m->r.right; if (m != (node *) NULL) { if (m->type == N_PAIR && m->l.left->type == N_BLOCK && m->l.left->mod == N_CONTINUE) { cg_expr(m->r.right->l.left, POP); output(") {\ni_add_ticks(f, 1);\n"); cg_stmt(m->l.left->l.left); } else { output(") {\ni_add_ticks(f, 1);\n"); cg_stmt(m); } } else { output(") {\ni_add_ticks(f, 1);\n"); } output("}\n"); break; case N_FOREVER: output("for ("); if (m->l.left != (node *) NULL) { cg_expr(m->l.left, POP); } output(";;) {\ni_add_ticks(f, 1);\n"); cg_stmt(m->r.right); output("}\n"); break; case N_RLIMITS: cg_expr(m->l.left->l.left, PUSH); comma(); cg_expr(m->l.left->r.right, PUSH); rcstart.type = N_RLIMITS; rcstart.next = rclist; rclist = &rcstart; output(";\npre_rlimits(f);\n"); cg_stmt(m->r.right); if (!(m->r.right->flags & F_END)) { output("i_set_rllevel(f, -1);\n"); } rclist = rcstart.next; break; case N_CATCH: rcstart.type = N_CATCH; rcstart.next = rclist; rclist = &rcstart; output("pre_catch(f);\n"); if (catch_level == 0) { for (i = nvars; i > 0; ) { if (vars[--i] != 0) { output("%s->u.number = ivar%d;\n", local(i), vars[i]); } } } output("if (!ec_push((ec_ftn) i_catcherr)) {\n"); catch_level++; cg_stmt(m->l.left); --catch_level; if (!(m->l.left->flags & F_END)) { output("ec_pop(); post_catch(f, 0);"); } output("} else {\n"); if (catch_level == 0) { for (i = nvars; i > 0; ) { if (vars[--i] != 0) { output("ivar%d = %s->u.number;\n", vars[i], local(i)); } } } output("post_catch(f, 0);\n"); rclist = rcstart.next; if (m->r.right != (node *) NULL) { cg_stmt(m->r.right); } output("}\n"); break; case N_IF: output("if ("); cg_expr(m->l.left, TOPTRUTHVAL); output(") {\n"); cg_stmt(m->r.right->l.left); if (m->r.right->r.right != (node *) NULL) { output("} else {\n"); cg_stmt(m->r.right->r.right); } output("}\n"); break; case N_PAIR: cg_stmt(m); break; case N_POP: cg_expr(m->l.left, POP); output(";\n"); break; case N_RETURN: cg_expr(m->l.left, PUSH); output(";\n"); if (m->mod != 0) { breakout(m->mod); } output("return;\n"); break; case N_SWITCH_INT: cg_switch_int(m); break; case N_SWITCH_RANGE: cg_switch_range(m); break; case N_SWITCH_STR: cg_switch_str(m); break; } } } static bool inherited; static unsigned short nfuncs; static string *funcnames[255]; /* * NAME: codegen->init() * DESCRIPTION: initialize the code generator */ void cg_init(flag) bool flag; { inherited = flag; nfuncs = 0; kf_call_trace = ((long) KFCALL << 24) | kf_func("call_trace"); kf_call_other = ((long) KFCALL << 24) | kf_func("call_other"); kf_clone_object = ((long) KFCALL << 24) | kf_func("clone_object"); kf_editor = ((long) KFCALL << 24) | kf_func("editor"); } /* * NAME: codegen->compiled() * DESCRIPTION: return TRUE to signal that the code is compiled, and not * interpreted */ bool cg_compiled() { return TRUE; } /* * NAME: codegen->function() * DESCRIPTION: generate code for a function */ char *cg_function(fname, n, nvar, npar, depth, size) string *fname; node *n; int nvar, npar; unsigned int depth; unsigned short *size; { register int i, j; char *prog; depth += nvar; prog = ALLOC(char, *size = 6); prog[0] = depth >> 8; prog[1] = depth; prog[2] = nvar - npar; nvars = nvar; nparam = npar; if (!inherited) { str_ref(funcnames[nfuncs] = fname); output("\nstatic void LPC_%s(f)\nregister frame *f;\n{\n", fname->text); output("char *p; Int tv[%d];\n", NTMPVAL); } j = 0; for (i = 0; i < nvar; i++) { if (c_vtype(i) == T_INT) { vars[i] = ++j; output("register Int ivar%d = ", j); if (i < npar) { output("%s->u.number;\n", local(i)); } else { output("0;\n"); } } else { vars[i] = 0; } } output("\n"); swcount = 0; cg_stmt(n); output("}\n"); prog[3] = nfuncs >> 16; prog[4] = nfuncs >> 8; prog[5] = nfuncs++; return prog; } /* * NAME: codegen->nfuncs() * DESCRIPTION: return the number of functions generated */ int cg_nfuncs() { return nfuncs; } /* * NAME: codegen->clear() * DESCRIPTION: clean up code generator */ void cg_clear() { if (!inherited && nfuncs != 0) { register int i; register string **f; output("\nstatic pcfunc functions[] = {\n"); for (i = nfuncs, f = funcnames; i != 0; --i, f++) { output("LPC_%s,\n", (*f)->text); str_del(*f); } output("};\n"); } } /* * NAME: output() * DESCRIPTION: output a formatted string */ static void output(format, arg1, arg2, arg3, arg4) char *format, *arg1, *arg2, *arg3, *arg4; { if (!inherited) { printf(format, arg1, arg2, arg3, arg4); } }