summaryrefslogtreecommitdiffhomepage
path: root/src/codegen.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/codegen.c')
-rw-r--r--src/codegen.c2273
1 files changed, 2273 insertions, 0 deletions
diff --git a/src/codegen.c b/src/codegen.c
new file mode 100644
index 000000000..c44e619e9
--- /dev/null
+++ b/src/codegen.c
@@ -0,0 +1,2273 @@
+#undef CODEGEN_TEST
+#define CODEGEN_DUMP
+
+#include "mruby.h"
+#include "irep.h"
+#include "compile.h"
+#include "mruby/proc.h"
+#include "opcode.h"
+#include "mruby/string.h"
+#include <string.h>
+#include <stdlib.h>
+
+typedef mrb_ast_node node;
+typedef struct mrb_parser_state parser_state;
+
+struct loopinfo {
+ enum looptype {
+ LOOP_NORMAL,
+ LOOP_BLOCK,
+ LOOP_FOR,
+ LOOP_BEGIN,
+ LOOP_RESCUE,
+ } type;
+ int pc1, pc2, pc3, acc;
+ int ensure_level;
+ struct loopinfo *prev;
+};
+
+typedef struct scope {
+ mrb_state *mrb;
+ mrb_pool *mpool;
+ jmp_buf jmp;
+
+ struct scope *prev;
+
+ node *lv;
+
+ int sp;
+ int pc;
+ int lastlabel;
+ int ainfo;
+
+ struct loopinfo *loop;
+ int ensure_level;
+
+ mrb_code *iseq;
+ int icapa;
+
+ mrb_value *pool;
+ int plen;
+ int pcapa;
+
+ mrb_sym *syms;
+ int slen;
+
+ int nlocals;
+ int nregs;
+
+ int idx;
+} codegen_scope;
+
+static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv);
+static void scope_finish(codegen_scope *s, int idx);
+static struct loopinfo *loop_push(codegen_scope *s, enum looptype t);
+static void loop_break(codegen_scope *s, node *tree);
+static void loop_pop(codegen_scope *s, int val);
+
+static void gen_assignment(codegen_scope *s, node *node, int sp, int val);
+static void gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val);
+
+static void codegen(codegen_scope *s, node *tree, int val);
+
+static void
+codegen_error(codegen_scope *s, const char *message)
+{
+ if (!s) return;
+ while (s->prev) {
+ mrb_pool_close(s->mpool);
+ s = s->prev;
+ }
+ mrb_pool_close(s->mpool);
+ fprintf(stderr, "codegen error: %s\n", message);
+ longjmp(s->jmp, 1);
+}
+
+static void*
+codegen_palloc(codegen_scope *s, size_t len)
+{
+ void *p = mrb_pool_alloc(s->mpool, len);
+
+ if (!p) codegen_error(s, "pool memory allocation");
+ return p;
+}
+
+void*
+codegen_malloc(codegen_scope *s, size_t len)
+{
+ void *p = mrb_malloc(s->mrb, len);
+
+ if (!p) codegen_error(s, "mrb_malloc");
+ return p;
+}
+
+void*
+codegen_realloc(codegen_scope *s, void *p, size_t len)
+{
+ p = mrb_realloc(s->mrb, p, len);
+
+ if (!p && len > 0) codegen_error(s, "mrb_realloc");
+ return p;
+}
+
+static int
+new_label(codegen_scope *s)
+{
+ s->lastlabel = s->pc;
+ return s->pc;
+}
+
+static inline void
+genop(codegen_scope *s, mrb_code i)
+{
+ if (s->pc == s->icapa) {
+ s->icapa *= 2;
+ s->iseq = codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa);
+ }
+ s->iseq[s->pc] = i;
+ s->pc++;
+}
+
+static void
+genop_peep(codegen_scope *s, mrb_code i, int val)
+{
+ // peephole optimization
+ if (!val && s->lastlabel != s->pc && s->pc > 0) {
+ mrb_code i0 = s->iseq[s->pc-1];
+ int c1 = GET_OPCODE(i);
+ int c0 = GET_OPCODE(i0);
+
+ switch (c1) {
+ case OP_MOVE:
+ switch (c0) {
+ case OP_MOVE:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0) && GETARG_A(i) >= s->nlocals) {
+ // skip swapping OP_MOVE
+ return;
+ }
+ break;
+ case OP_LOADI:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_AsBx(OP_LOADI, GETARG_A(i), GETARG_sBx(i0));
+ return;
+ }
+ break;
+ case OP_ARRAY:
+ case OP_HASH:
+ case OP_RANGE:
+ case OP_AREF:
+ case OP_GETUPVAR:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_ABC(c0, GETARG_A(i), GETARG_B(i0), GETARG_C(i0));
+ return;
+ }
+ break;
+ case OP_LOADSYM:
+ case OP_GETGLOBAL:
+ case OP_GETIV:
+ case OP_GETCV:
+ case OP_GETCONST:
+ case OP_GETSPECIAL:
+ case OP_LOADL:
+ case OP_STRING:
+ case OP_GETMCNST:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_ABx(c0, GETARG_A(i), GETARG_Bx(i0));
+ return;
+ }
+ break;
+ case OP_SCLASS:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_AB(c0, GETARG_A(i), GETARG_B(i0));
+ return;
+ }
+ break;
+ case OP_LOADNIL:
+ case OP_LOADSELF:
+ case OP_LOADT:
+ case OP_LOADF:
+ case OP_OCLASS:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_A(c0, GETARG_A(i));
+ return;
+ }
+ break;
+ }
+ break;
+ case OP_SETIV:
+ case OP_SETCV:
+ case OP_SETCONST:
+ case OP_SETMCNST:
+ switch (c0) {
+ case OP_MOVE:
+ if (GETARG_A(i) == GETARG_A(i0)) {
+ s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i));
+ return;
+ }
+ break;
+ }
+ break;
+ case OP_SETUPVAR:
+ switch (c0) {
+ case OP_MOVE:
+ if (GETARG_A(i) == GETARG_A(i0)) {
+ s->iseq[s->pc-1] = MKOP_ABC(c1, GETARG_B(i0), GETARG_B(i), GETARG_C(i));
+ return;
+ }
+ break;
+ }
+ break;
+ case OP_EPOP:
+ if (c0 == OP_EPOP) {
+ s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i));
+ return;
+ }
+ break;
+ case OP_POPERR:
+ if (c0 == OP_POPERR) {
+ s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i));
+ return;
+ }
+ break;
+ }
+ }
+ genop(s, i);
+}
+
+static void
+scope_error(codegen_scope *s)
+{
+ exit(1);
+}
+
+static inline void
+dispatch(codegen_scope *s, int pc)
+{
+ int diff = s->pc - pc;
+ mrb_code i = s->iseq[pc];
+ int c = GET_OPCODE(i);
+
+ s->lastlabel = s->pc;
+ switch (c) {
+ case OP_JMP:
+ case OP_JMPIF:
+ case OP_JMPNOT:
+ case OP_ONERR:
+ break;
+ default:
+ fprintf(stderr, "bug: dispatch on non JMP op\n");
+ scope_error(s);
+ }
+ s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff);
+}
+
+static void
+dispatch_linked(codegen_scope *s, int pc)
+{
+ mrb_code i;
+ int pos;
+
+ if (!pc) return;
+ for (;;) {
+ i = s->iseq[pc];
+ pos = GETARG_sBx(i);
+ dispatch(s, pc);
+ if (!pos) break;
+ pc = pos;
+ }
+}
+
+#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0)
+static void
+push_(codegen_scope *s)
+{
+ if (s->sp > 511) {
+ codegen_error(s, "too complex expression");
+ }
+ s->sp++;
+ nregs_update;
+}
+#if 0
+static void
+push_n_(codegen_scope *s, int n)
+{
+ if (s->sp + n > 511) {
+ codegen_error(s, "too complex expression");
+ }
+ s->sp += n;
+ nregs_update;
+}
+#endif
+
+#define push() push_(s)
+#define push_n(n) push_n_(s, n)
+#define pop() (s->sp--)
+#define pop_n(n) (s->sp-=(n))
+#define cursp() (s->sp)
+
+static inline int
+new_lit(codegen_scope *s, mrb_value val)
+{
+ int i;
+
+ for (i=0; i<s->plen; i++) {
+ if (memcmp(&s->pool[i], &val, sizeof(mrb_value)) == 0) return i;
+ }
+ if (s->plen == s->pcapa) {
+ s->pcapa *= 2;
+ s->pool = codegen_realloc(s, s->pool, sizeof(mrb_value)*s->pcapa);
+ }
+ s->pool[s->plen] = val;
+ return s->plen++;
+}
+
+static inline int
+new_msym(codegen_scope *s, mrb_sym sym)
+{
+ int i, len;
+
+ len = s->slen;
+ if (len > 255) len = 255;
+ for (i=0; i<len; i++) {
+ if (s->syms[i] == sym) return i;
+ if (s->syms[i] == 0) break;
+ }
+ if (i > 255) {
+ codegen_error(s, "too many symbols (max 256)");
+ }
+ s->syms[i] = sym;
+ if (i == s->slen) s->slen++;
+ return i;
+}
+
+static inline int
+new_sym(codegen_scope *s, mrb_sym sym)
+{
+ int i;
+
+ for (i=0; i<s->slen; i++) {
+ if (s->syms[i] == sym) return i;
+ }
+ if (s->slen > 125 && s->slen < 256) {
+ s->syms = codegen_realloc(s, s->syms, sizeof(mrb_sym)*65536);
+ memset(s->syms+s->slen, 0, sizeof(mrb_sym)*(256-s->slen));
+ s->slen = 256;
+ }
+ s->syms[s->slen] = sym;
+ return s->slen++;
+}
+
+static int
+node_len(node *tree)
+{
+ int n = 0;
+
+ while (tree) {
+ n++;
+ tree = tree->cdr;
+ }
+ return n;
+}
+
+#define lv_name(lv) ((mrb_sym)(lv)->car)
+static int
+lv_idx(codegen_scope *s, mrb_sym id)
+{
+ node *lv = s->lv;
+ int n = 1;
+
+ while (lv) {
+ if (lv_name(lv) == id) return n;
+ n++;
+ lv = lv->cdr;
+ }
+ return 0;
+}
+
+#define NOVAL 0
+#define VAL 1
+
+static void
+for_body(codegen_scope *s, node *tree)
+{
+ codegen_scope *prev = s;
+ int idx, base = s->idx;
+ struct loopinfo *lp;
+ node *n2;
+ mrb_code c;
+
+ // generate receiver
+ codegen(s, tree->cdr->car, VAL);
+ // generate loop-block
+ s = scope_new(s->mrb, s, tree->car);
+ idx = s->idx;
+
+ lp = loop_push(s, LOOP_FOR);
+ lp->pc1 = new_label(s);
+
+ // generate loop variable
+ n2 = tree->car;
+ if (n2->car && !n2->car->cdr && !n2->cdr) {
+ genop(s, MKOP_Ax(OP_ENTER, 1<<18));
+ gen_assignment(s, n2->car->car, 1, NOVAL);
+ }
+ else {
+ genop(s, MKOP_Ax(OP_ENTER, 1<<18));
+ gen_vmassignment(s, n2, 1, VAL);
+ }
+ codegen(s, tree->cdr->cdr->car, VAL);
+ pop();
+ c = s->iseq[s->pc-1];
+ if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) {
+ genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL));
+ }
+ loop_pop(s, NOVAL);
+ scope_finish(s, idx);
+ s = prev;
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx - base, OP_L_BLOCK));
+ pop();
+ idx = new_msym(s, mrb_intern(s->mrb, "each"));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 0));
+}
+
+static int
+lambda_body(codegen_scope *s, node *tree, int blk)
+{
+ int idx, base = s->idx;
+ mrb_code c;
+
+ s = scope_new(s->mrb, s, tree->car);
+ idx = s->idx;
+
+ if (blk) {
+ struct loopinfo *lp = loop_push(s, LOOP_BLOCK);
+ lp->pc1 = new_label(s);
+ }
+ tree = tree->cdr;
+ if (tree->car) {
+ int ma, oa, ra, pa, ka, kd, ba, a;
+ int pos, i;
+ node *n, *opt;
+
+ ma = node_len(tree->car->car);
+ n = tree->car->car;
+ while (n) {
+ n = n->cdr;
+ }
+ oa = node_len(tree->car->cdr->car);
+ ra = tree->car->cdr->cdr->car ? 1 : 0;
+ pa = node_len(tree->car->cdr->cdr->cdr->car);
+ ka = kd = 0;
+ ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
+
+ a = ((ma & 0x1f) << 18)
+ | ((oa & 0x1f) << 13)
+ | ((ra & 1) << 12)
+ | ((pa & 0x1f) << 7)
+ | ((ka & 0x1f) << 2)
+ | ((kd & 1)<< 1)
+ | (ba & 1);
+ s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */
+ | ((ra & 1) << 5)
+ | (pa & 0x1f);
+ genop(s, MKOP_Ax(OP_ENTER, a));
+ pos = new_label(s);
+ for (i=0; i<oa; i++) {
+ new_label(s);
+ genop(s, MKOP_Ax(OP_JMP, 0));
+ }
+ if (oa > 0) {
+ genop(s, MKOP_Ax(OP_JMP, 0));
+ }
+ opt = tree->car->cdr->car;
+ i = 0;
+ while (opt) {
+ int idx;
+
+ dispatch(s, pos+i);
+ codegen(s, opt->car->cdr, VAL);
+ idx = lv_idx(s, (mrb_sym)opt->car->car);
+ pop();
+ genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL);
+ i++;
+ opt = opt->cdr;
+ }
+ if (oa > 0) {
+ dispatch(s, pos+i);
+ }
+ }
+ codegen(s, tree->cdr->car, VAL);
+ pop();
+ c = s->iseq[s->pc-1];
+ if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) {
+ genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL));
+ }
+ if (blk) {
+ loop_pop(s, NOVAL);
+ }
+ scope_finish(s, idx);
+
+ return idx - base;
+}
+
+static int
+scope_body(codegen_scope *s, node *tree)
+{
+ codegen_scope *scope = scope_new(s->mrb, s, tree->car);
+ int idx = scope->idx;
+
+ if (!s->iseq) {
+ codegen(scope, tree->cdr, NOVAL);
+ genop(scope, MKOP_A(OP_STOP, 0));
+ }
+ else {
+ codegen(scope, tree->cdr, VAL);
+ genop(scope, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL));
+ }
+ scope_finish(scope, idx);
+
+ return idx - s->idx;
+}
+
+static int
+nosplat(node *t)
+{
+ while (t) {
+ if ((intptr_t)t->car->car == NODE_SPLAT) return 0;
+ t = t->cdr;
+ }
+ return 1;
+}
+
+static mrb_sym
+attrsym(codegen_scope *s, mrb_sym a)
+{
+ const char *name = mrb_sym2name(s->mrb, a);
+ char *name2;
+ size_t len = strlen(name);
+
+ name2 = codegen_palloc(s, len+1);
+ strcpy(name2, name);
+ name2[len] = '=';
+ name2[len+1] = '\0';
+
+ return mrb_intern(s->mrb, name2);
+}
+
+static int
+gen_values(codegen_scope *s, node *t)
+{
+ int n = 0;
+
+ while (t) {
+ if ((intptr_t)t->car->car == NODE_SPLAT) { // splat mode
+ pop_n(n);
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+ push();
+ codegen(s, t->car, VAL);
+ pop(); pop();
+ genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+ t = t->cdr;
+ while (t) {
+ push();
+ codegen(s, t->car, VAL);
+ pop(); pop();
+ if ((intptr_t)t->car->car == NODE_SPLAT) {
+ genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+ }
+ else {
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ }
+ t = t->cdr;
+ }
+ return -1;
+ }
+ // normal (no splat) mode
+ codegen(s, t->car, VAL);
+ n++;
+ t = t->cdr;
+ }
+ return n;
+}
+
+#define CALL_MAXARGS 127
+
+static void
+gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val)
+{
+ mrb_sym sym = name ? name : (mrb_sym)tree->cdr->car;
+ int idx;
+ int n = 0, noop = 0, sendv = 0;
+
+ codegen(s, tree->car, VAL); /* receiver */
+ idx = new_msym(s, sym);
+ tree = tree->cdr->cdr->car;
+ if (tree) {
+ n = gen_values(s, tree->car);
+ if (n < 0) {
+ n = noop = sendv = 1;
+ push();
+ }
+ }
+ if (sp) {
+ if (sendv) {
+ pop();
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp));
+ push();
+ }
+ else {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), sp));
+ push();
+ n++;
+ }
+ }
+ if (tree && tree->cdr) {
+ noop = 1;
+ codegen(s, tree->cdr, VAL);
+ pop();
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ pop_n(n+1);
+ {
+ const char *name = mrb_sym2name(s->mrb, sym);
+
+ if (!noop && name[0] == '+' && strlen(name) == 1) {
+ genop(s, MKOP_ABC(OP_ADD, cursp(), idx, n));
+ }
+ else if (!noop && name[0] == '-' && strlen(name) == 1) {
+ genop(s, MKOP_ABC(OP_SUB, cursp(), idx, n));
+ }
+ else if (!noop && name[0] == '<' && strlen(name) == 1) {
+ genop(s, MKOP_ABC(OP_LT, cursp(), idx, n));
+ }
+ else if (!noop && name[0] == '<' && strlen(name) == 2 && name[1] == '=') {
+ genop(s, MKOP_ABC(OP_LE, cursp(), idx, n));
+ }
+ else if (!noop && name[0] == '>' && strlen(name) == 1) {
+ genop(s, MKOP_ABC(OP_GT, cursp(), idx, n));
+ }
+ else if (!noop && name[0] == '>' && strlen(name) == 2 && name[1] == '=') {
+ genop(s, MKOP_ABC(OP_GE, cursp(), idx, n));
+ }
+ else {
+ if (sendv) n = CALL_MAXARGS;
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n));
+ }
+ }
+ if (val) {
+ push();
+ }
+}
+
+static void
+gen_assignment(codegen_scope *s, node *node, int sp, int val)
+{
+ int idx;
+ int type = (intptr_t)node->car;
+
+ node = node->cdr;
+ switch ((intptr_t)type) {
+ case NODE_GVAR:
+ idx = new_sym(s, (mrb_sym)node);
+ genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val);
+ break;
+ case NODE_LVAR:
+ idx = lv_idx(s, (mrb_sym)node);
+ if (idx > 0) {
+ if (idx != sp) {
+ genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val);
+ }
+ break;
+ }
+ else { /* upvar */
+ int lv = 0;
+ codegen_scope *up = s->prev;
+
+ while (up) {
+ idx = lv_idx(up, (mrb_sym)node);
+ if (idx > 0) {
+ genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val);
+ break;
+ }
+ lv++;
+ up = up->prev;
+ }
+ // assert(up!=0);
+ }
+ break;
+ case NODE_IVAR:
+ idx = new_sym(s, (mrb_sym)node);
+ genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val);
+ break;
+ case NODE_CVAR:
+ idx = new_sym(s, (mrb_sym)node);
+ genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val);
+ break;
+ case NODE_CONST:
+ idx = new_sym(s, (mrb_sym)node);
+ genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val);
+ break;
+ case NODE_COLON2:
+ idx = new_sym(s, (mrb_sym)node->cdr);
+ genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL);
+ push();
+ codegen(s, node->car, VAL);
+ pop_n(2);
+ genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val);
+ break;
+
+ case NODE_CALL:
+ push();
+ gen_call(s, node, attrsym(s, (mrb_sym)node->cdr->car), sp, val);
+ val = NOVAL; /* push should have done in gen_call() */
+ break;
+
+ default:
+ printf("unknown lhs %d\n", type);
+ break;
+ }
+ if (val) push();
+}
+
+static void
+gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
+{
+ int n = 0, post = 0;
+ node *t, *p;
+
+ if (tree->car) { /* pre */
+ t = tree->car;
+ n = 0;
+ while (t) {
+ genop(s, MKOP_ABC(OP_AREF, cursp(), rhs, n));
+ gen_assignment(s, t->car, cursp(), NOVAL);
+ n++;
+ t = t->cdr;
+ }
+ }
+ t = tree->cdr;
+ if (t) {
+ if (t->cdr) { /* post count */
+ p = t->cdr->car;
+ while (p) {
+ post++;
+ p = p->cdr;
+ }
+ }
+ if (val) {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), rhs));
+ push();
+ }
+ pop();
+ genop(s, MKOP_ABC(OP_APOST, cursp(), n, post));
+ n = 1;
+ if (t->car) { /* rest */
+ gen_assignment(s, t->car, cursp(), NOVAL);
+ }
+ if (t->cdr && t->cdr->car) {
+ t = t->cdr->car;
+ while (t) {
+ gen_assignment(s, t->car, cursp()+n, NOVAL);
+ t = t->cdr;
+ n++;
+ }
+ }
+ }
+}
+
+static void
+raise_error(codegen_scope *s, const char *msg)
+{
+ int idx = new_lit(s, mrb_str_new_cstr(s->mrb, msg));
+
+ genop(s, MKOP_ABx(OP_ERR, 0, idx));
+}
+
+static void
+codegen(codegen_scope *s, node *tree, int val)
+{
+ int nt;
+
+ if (!tree) return;
+ nt = (intptr_t)tree->car;
+ tree = tree->cdr;
+ switch (nt) {
+ case NODE_BEGIN:
+ while (tree) {
+ codegen(s, tree->car, tree->cdr ? NOVAL : val);
+ tree = tree->cdr;
+ }
+ break;
+
+ case NODE_RESCUE:
+ {
+ int onerr, noexc, exend, pos1, pos2, tmp;
+ struct loopinfo *lp;
+
+ onerr = new_label(s);
+ genop(s, MKOP_Bx(OP_ONERR, 0));
+ lp = loop_push(s, LOOP_BEGIN);
+ lp->pc1 = onerr;
+ if (tree->car) {
+ codegen(s, tree->car, val);
+ }
+ lp->type = LOOP_RESCUE;
+ noexc = new_label(s);
+ genop(s, MKOP_Bx(OP_JMP, 0));
+ dispatch(s, onerr);
+ tree = tree->cdr;
+ exend = 0;
+ pos1 = 0;
+ if (tree->car) {
+ node *n2 = tree->car;
+ int exc = cursp();
+
+ genop(s, MKOP_A(OP_RESCUE, exc));
+ push();
+ while (n2) {
+ node *n3 = n2->car;
+
+ if (pos1) dispatch(s, pos1);
+ if (n3->car) {
+ node *n4 = n3->car;
+
+ pos2 = 0;
+ while (n4) {
+ codegen(s, n4->car, VAL);
+ genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
+ push();
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ pop(); pop();
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern(s->mrb, "===")), 1));
+ tmp = new_label(s);
+ genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+ pos2 = tmp;
+ n4 = n4->cdr;
+ }
+ pos1 = new_label(s);
+ genop(s, MKOP_Bx(OP_JMP, 0));
+ dispatch_linked(s, pos2);
+ }
+ pop();
+ if (n3->cdr->car) {
+ gen_assignment(s, n3->cdr->car, exc, NOVAL);
+ }
+ if (n3->cdr->cdr->car) {
+ codegen(s, n3->cdr->cdr->car, val);
+ }
+ tmp = new_label(s);
+ genop(s, MKOP_AsBx(OP_JMP, cursp(), exend));
+ exend = tmp;
+ n2 = n2->cdr;
+ push();
+ }
+ if (pos1) {
+ dispatch(s, pos1);
+ genop(s, MKOP_A(OP_RAISE, exc));
+ }
+ }
+ tree = tree->cdr;
+ dispatch(s, noexc);
+ genop(s, MKOP_A(OP_POPERR, 1));
+ if (tree->car) {
+ codegen(s, tree->car, val);
+ }
+ dispatch_linked(s, exend);
+ loop_pop(s, NOVAL);
+ }
+ break;
+
+ case NODE_ENSURE:
+ {
+ int idx;
+ int epush = s->pc;
+
+ genop(s, MKOP_Bx(OP_EPUSH, 0));
+ s->ensure_level++;
+ codegen(s, tree->car, val);
+ idx = scope_body(s, tree->cdr);
+ s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx);
+ s->ensure_level--;
+ genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL);
+ }
+ break;
+
+ case NODE_LAMBDA:
+ {
+ int idx = lambda_body(s, tree, 1);
+
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA));
+ push();
+ }
+ break;
+
+ case NODE_BLOCK:
+ {
+ int idx = lambda_body(s, tree, 1);
+
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK));
+ push();
+ }
+ break;
+
+ case NODE_IF:
+ {
+ int pos1, pos2;
+ node *e = tree->cdr->cdr->car;
+
+ codegen(s, tree->car, VAL);
+ pop();
+ pos1 = new_label(s);
+ genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0));
+
+ codegen(s, tree->cdr->car, val);
+ if (e) {
+ if (val) pop();
+ pos2 = new_label(s);
+ genop(s, MKOP_sBx(OP_JMP, 0));
+ dispatch(s, pos1);
+ codegen(s, e, val);
+ dispatch(s, pos2);
+ }
+ else {
+ if (val) {
+ pop();
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ dispatch(s, pos1);
+ }
+ }
+ break;
+
+ case NODE_AND:
+ {
+ int pos;
+
+ codegen(s, tree->car, VAL);
+ pos = new_label(s);
+ pop();
+ genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0));
+ codegen(s, tree->cdr, val);
+ dispatch(s, pos);
+ }
+ break;
+
+ case NODE_OR:
+ {
+ int pos;
+
+ codegen(s, tree->car, VAL);
+ pos = new_label(s);
+ pop();
+ genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+ codegen(s, tree->cdr, val);
+ dispatch(s, pos);
+ }
+ break;
+
+ case NODE_WHILE:
+ {
+ struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
+
+ lp->pc1 = new_label(s);
+ codegen(s, tree->car, VAL);
+ pop();
+ lp->pc2 = new_label(s);
+ genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0));
+ codegen(s, tree->cdr, NOVAL);
+ genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
+ dispatch(s, lp->pc2);
+ loop_pop(s, val);
+ }
+ break;
+
+ case NODE_UNTIL:
+ {
+ struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
+
+ lp->pc1 = new_label(s);
+ codegen(s, tree->car, VAL);
+ pop();
+ lp->pc2 = new_label(s);
+ genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+ codegen(s, tree->cdr, NOVAL);
+ genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
+ dispatch(s, lp->pc2);
+ loop_pop(s, val);
+ }
+ break;
+
+ case NODE_FOR:
+ for_body(s, tree);
+ if (val) push();
+ break;
+
+ case NODE_CASE:
+ {
+ int head = 0;
+ int pos1, pos2, pos3, tmp;
+ node *n;
+
+ pos3 = 0;
+ if (tree->car) {
+ head = cursp();
+ codegen(s, tree->car, VAL);
+ }
+ tree = tree->cdr;
+ while (tree) {
+ n = tree->car->car;
+ pos1 = pos2 = 0;
+ while (n) {
+ codegen(s, n->car, VAL);
+ if (head) {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), head));
+ push();
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ pop(); pop();
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern(s->mrb, "===")), 1));
+ }
+ tmp = new_label(s);
+ genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+ pos2 = tmp;
+ n = n->cdr;
+ }
+ if (tree->car->car) {
+ pos1 = new_label(s);
+ genop(s, MKOP_AsBx(OP_JMP, cursp(), 0));
+ dispatch_linked(s, pos2);
+ }
+ pop(); pop();
+ codegen(s, tree->car->cdr, val);
+ tmp = new_label(s);
+ genop(s, MKOP_AsBx(OP_JMP, cursp(), pos3));
+ pos3 = tmp;
+ if (pos1) dispatch(s, pos1);
+ tree = tree->cdr;
+ push(); push();
+ }
+ pop();
+ if (pos3) dispatch_linked(s, pos3);
+ if (val) push();
+ }
+ break;
+
+ case NODE_SCOPE:
+ scope_body(s, tree);
+ break;
+
+ case NODE_FCALL:
+ case NODE_CALL:
+ gen_call(s, tree, 0, 0, val);
+ break;
+
+ case NODE_DOT2:
+ codegen(s, tree->car, VAL);
+ codegen(s, tree->cdr, VAL);
+ pop(); pop();
+ if (val) {
+ genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), 0));
+ push();
+ }
+ break;
+
+ case NODE_DOT3:
+ codegen(s, tree->car, VAL);
+ codegen(s, tree->cdr, VAL);
+ pop(); pop();
+ if (val) {
+ genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), 1));
+ push();
+ }
+ break;
+
+ case NODE_COLON2:
+ {
+ int sym = new_sym(s, (mrb_sym)tree->cdr);
+
+ codegen(s, tree->car, VAL);
+ pop();
+ genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_COLON3:
+ {
+ int sym = new_sym(s, (mrb_sym)tree);
+
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_ARRAY:
+ {
+ int n;
+
+ n = gen_values(s, tree);
+ if (n >= 0) {
+ pop_n(n);
+ if (val) {
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+ push();
+ }
+ }
+ else if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_HASH:
+ {
+ int len = 0;
+
+ while (tree) {
+ codegen(s, tree->car->car, VAL);
+ codegen(s, tree->car->cdr, VAL);
+ len++;
+ tree = tree->cdr;
+ }
+ pop_n(len*2);
+ if (val) {
+ genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
+ push();
+ }
+ }
+ break;
+
+ case NODE_SPLAT:
+ codegen(s, tree, VAL);
+ break;
+
+ case NODE_ASGN:
+ codegen(s, tree->cdr, VAL);
+ pop();
+ gen_assignment(s, tree->car, cursp(), val);
+ break;
+
+ case NODE_MASGN:
+ {
+ int len = 0, n = 0, post = 0;
+ node *t = tree->cdr, *p;
+ int rhs = cursp();
+
+ if ((intptr_t)t->car == NODE_ARRAY && nosplat(t->cdr)) {
+ // fixed rhs
+ t = t->cdr;
+ while (t) {
+ codegen(s, t->car, VAL);
+ len++;
+ t = t->cdr;
+ }
+ tree = tree->car;
+ if (tree->car) { /* pre */
+ t = tree->car;
+ n = 0;
+ while (t) {
+ gen_assignment(s, t->car, rhs+n, NOVAL);
+ n++;
+ t = t->cdr;
+ }
+ }
+ t = tree->cdr;
+ if (t) {
+ if (t->cdr) { /* post count */
+ p = t->cdr->car;
+ while (p) {
+ post++;
+ p = p->cdr;
+ }
+ }
+ if (t->car) { /* rest (len - pre - post) */
+ int rn = len - post - n;
+
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn));
+ gen_assignment(s, t->car, cursp(), NOVAL);
+ n += rn;
+ }
+ if (t->cdr && t->cdr->car) {
+ t = t->cdr->car;
+ while (n<len) {
+ gen_assignment(s, t->car, rhs+n, NOVAL);
+ t = t->cdr;
+ n++;
+ }
+ }
+ }
+ pop_n(len);
+ if (val) {
+ genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len));
+ push();
+ }
+ }
+ else {
+ // variable rhs
+ codegen(s, t, VAL);
+ gen_vmassignment(s, tree->car, rhs, val);
+ if (!val) pop();
+ }
+ }
+ break;
+
+ case NODE_OP_ASGN:
+ codegen(s, tree->car, VAL);
+ codegen(s, tree->cdr->cdr->car, VAL);
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ pop(); pop();
+ {
+ mrb_sym sym = (mrb_sym)tree->cdr->car;
+ const char *name = mrb_sym2name(s->mrb, sym);
+ int idx = new_msym(s, sym);
+
+ if (name[0] == '+' && strlen(name) == 1) {
+ genop(s, MKOP_ABC(OP_ADD, cursp(), idx, 2));
+ }
+ else if (name[0] == '-' && strlen(name) == 1) {
+ genop(s, MKOP_ABC(OP_SUB, cursp(), idx, 2));
+ }
+ else if (name[0] == '<' && strlen(name) == 1) {
+ genop(s, MKOP_ABC(OP_LT, cursp(), idx, 2));
+ }
+ else if (name[0] == '<' && strlen(name) == 2 && name[1] == '=') {
+ genop(s, MKOP_ABC(OP_LE, cursp(), idx, 2));
+ }
+ else if (name[0] == '>' && strlen(name) == 1) {
+ genop(s, MKOP_ABC(OP_GT, cursp(), idx, 2));
+ }
+ else if (name[0] == '>' && strlen(name) == 2 && name[1] == '=') {
+ genop(s, MKOP_ABC(OP_GE, cursp(), idx, 2));
+ }
+ else {
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 2));
+ }
+ }
+ gen_assignment(s, tree->car, cursp(), val);
+ break;
+
+ case NODE_SUPER:
+ {
+ int n = 0;
+
+ push();
+ if (tree) {
+ node *args = tree->car;
+ while (args) {
+ codegen(s, args->car, VAL);
+ n++;
+ args = args->cdr;
+ }
+ }
+ if (tree && tree->cdr) {
+ codegen(s, tree->cdr, VAL);
+ pop();
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ pop_n(n+1);
+ genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n));
+ if (val) push();
+ }
+ break;
+
+ case NODE_ZSUPER:
+ {
+ codegen_scope *s2 = s;
+ int lv = 0, ainfo = 0;
+
+ while (s2->ainfo < 0) {
+ lv++;
+ s2 = s2->prev;
+ if (!s2) break;
+ }
+ if (s2) ainfo = s2->ainfo;
+ push();
+ genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf)));
+ pop();
+ genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS));
+ if (val) push();
+ }
+ break;
+
+ case NODE_RETURN:
+ codegen(s, tree, VAL);
+ pop();
+ if (s->loop && s->loop->type != LOOP_NORMAL) {
+ genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN));
+ }
+ else {
+ genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL));
+ }
+ break;
+
+ case NODE_YIELD:
+ {
+ codegen_scope *s2 = s;
+ int lv = 0, ainfo = 0;
+ int n = 0, sendv = 0;
+
+ while (s2->ainfo < 0) {
+ lv++;
+ s2 = s2->prev;
+ if (!s2) break;
+ }
+ if (s2) ainfo = s2->ainfo;
+ genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)));
+ push();
+ if (tree) {
+ n = gen_values(s, tree);
+ if (n < 0) {
+ n = sendv = 1;
+ push();
+ }
+ }
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ pop_n(n+1);
+ if (sendv) n = CALL_MAXARGS;
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern(s->mrb, "call")), n));
+ if (val) push();
+ }
+ break;
+
+ case NODE_BREAK:
+ loop_break(s, tree);
+ if (val) push();
+ break;
+
+ case NODE_NEXT:
+ if (!s->loop) {
+ raise_error(s, "unexpected next");
+ }
+ else if (s->loop->type == LOOP_NORMAL) {
+ if (s->ensure_level > s->loop->ensure_level) {
+ genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ }
+ codegen(s, tree, NOVAL);
+ genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc));
+ }
+ else {
+ codegen(s, tree, VAL);
+ pop();
+ genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL));
+ }
+ if (val) push();
+ break;
+
+ case NODE_REDO:
+ if (!s->loop) {
+ raise_error(s, "unexpected redo");
+ }
+ else {
+ if (s->ensure_level > s->loop->ensure_level) {
+ genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ }
+ genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
+ }
+ break;
+
+ case NODE_RETRY:
+ {
+ const char *msg = "unexpected retry";
+
+ if (!s->loop) {
+ raise_error(s, msg);
+ }
+ else {
+ struct loopinfo *lp = s->loop;
+ int n = 0;
+
+ while (lp && lp->type != LOOP_RESCUE) {
+ if (lp->type == LOOP_BEGIN) {
+ n++;
+ }
+ lp = lp->prev;
+ }
+ if (!lp) {
+ raise_error(s, msg);
+ }
+ else {
+ if (n > 0) {
+ while (n--) {
+ genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL);
+ }
+ }
+ if (s->ensure_level > lp->ensure_level) {
+ genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL);
+ }
+ genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
+ }
+ }
+ }
+ break;
+
+ case NODE_LVAR:
+ if (val) {
+ int idx = lv_idx(s, (mrb_sym)tree);
+
+ if (idx > 0) {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), idx));
+ }
+ else {
+ int lv = 0;
+ codegen_scope *up = s->prev;
+
+ while (up) {
+ idx = lv_idx(up, (mrb_sym)tree);
+ if (idx > 0) {
+ genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv));
+ break;
+ }
+ lv++;
+ up = up->prev;
+ }
+ }
+ push();
+ }
+ break;
+
+ case NODE_GVAR:
+ {
+ int sym = new_sym(s, (mrb_sym)tree);
+
+ genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_IVAR:
+ {
+ int sym = new_sym(s, (mrb_sym)tree);
+
+ genop(s, MKOP_ABx(OP_GETIV, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_CVAR:
+ {
+ int sym = new_sym(s, (mrb_sym)tree);
+
+ genop(s, MKOP_ABx(OP_GETCV, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_CONST:
+ {
+ int sym = new_sym(s, (mrb_sym)tree);
+
+ genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_DEFINED:
+ codegen(s, tree, VAL);
+ break;
+
+ case NODE_BACK_REF:
+ codegen(s, tree, VAL);
+ break;
+
+ case NODE_NTH_REF:
+ codegen(s, tree, VAL);
+ break;
+
+ case NODE_ARG:
+ // should not happen
+ break;
+
+ case NODE_BLOCK_ARG:
+ codegen(s, tree, VAL);
+ break;
+
+ case NODE_INT:
+ if (val) {
+ char *p = (char*)tree->car;
+ int base = (intptr_t)tree->cdr->car;
+ int i = readint(p, base);
+ mrb_code co;
+
+ if (i < MAXARG_sBx && i > -MAXARG_sBx) {
+ co = MKOP_AsBx(OP_LOADI, cursp(), i);
+ }
+ else {
+ int off = new_lit(s, mrb_fixnum_value(i));
+ co = MKOP_ABx(OP_LOADL, cursp(), off);
+ }
+ genop(s, co);
+ push();
+ }
+ break;
+
+ case NODE_FLOAT:
+ if (val) {
+ char *p = (char*)tree;
+ mrb_float f = readfloat(p);
+ int off = new_lit(s, mrb_float_value(f));
+
+ genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ push();
+ }
+ break;
+
+ case NODE_NEGATE:
+ {
+ nt = (intptr_t)tree->car;
+ tree = tree->cdr;
+ switch (nt) {
+ case NODE_FLOAT:
+ {
+ char *p = (char*)tree;
+ mrb_float f = readfloat(p);
+ int off = new_lit(s, mrb_float_value(-f));
+
+ genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ push();
+ }
+ break;
+
+ case NODE_INT:
+ {
+ char *p = (char*)tree->car;
+ int base = (intptr_t)tree->cdr->car;
+ int i = readint(p, base);
+ mrb_code co;
+
+ i = -i;
+ if (i < MAXARG_sBx && i > -MAXARG_sBx) {
+ co = MKOP_AsBx(OP_LOADI, cursp(), i);
+ }
+ else {
+ int off = new_lit(s, mrb_fixnum_value(i));
+ co = MKOP_ABx(OP_LOADL, cursp(), off);
+ }
+ genop(s, co);
+ push();
+ }
+ break;
+
+ default:
+ {
+ int sym = new_msym(s, mrb_intern(s->mrb, "-"));
+
+ genop(s, MKOP_ABx(OP_LOADI, cursp(), 0));
+ push();
+ codegen(s, tree, VAL);
+ pop(); pop();
+ genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2));
+ }
+ break;
+ }
+ }
+ break;
+
+ case NODE_STR:
+ if (val) {
+ char *p = (char*)tree->car;
+ size_t len = (intptr_t)tree->cdr;
+ int off = new_lit(s, mrb_str_new(s->mrb, p, len));
+
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push();
+ }
+ break;
+
+ case NODE_DSTR:
+ if (val) {
+ node *n = tree;
+
+ codegen(s, n->car, VAL);
+ n = n->cdr;
+ while (n) {
+ codegen(s, n->car, VAL);
+ pop(); pop();
+ genop(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1));
+ push();
+ n = n->cdr;
+ }
+ }
+ else {
+ node *n = tree;
+
+ while (n) {
+ if ((intptr_t)n->car->car != NODE_STR) {
+ codegen(s, n->car, NOVAL);
+ }
+ n = n->cdr;
+ }
+ }
+ break;
+
+ case NODE_SYM:
+ if (val) {
+ int sym = new_sym(s, (mrb_sym)tree);
+
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_SELF:
+ if (val) {
+ genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ push();
+ }
+ break;
+
+ case NODE_NIL:
+ if (val) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ break;
+
+ case NODE_TRUE:
+ if (val) {
+ genop(s, MKOP_A(OP_LOADT, cursp()));
+ push();
+ }
+ break;
+
+ case NODE_FALSE:
+ if (val) {
+ genop(s, MKOP_A(OP_LOADF, cursp()));
+ push();
+ }
+ break;
+
+ case NODE_ALIAS:
+ {
+ int a = new_msym(s, (mrb_sym)tree->car);
+ int b = new_msym(s, (mrb_sym)tree->cdr);
+ int c = new_msym(s, mrb_intern(s->mrb, "alias_method"));
+
+ genop(s, MKOP_A(OP_TCLASS, cursp()));
+ push();
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), a));
+ push();
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b));
+ push();
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ pop_n(3);
+ genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_UNDEF:
+ {
+ int sym = new_msym(s, (mrb_sym)tree);
+ int undef = new_msym(s, mrb_intern(s->mrb, "undef_method"));
+
+ genop(s, MKOP_A(OP_TCLASS, cursp()));
+ push();
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ push();
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ pop_n(2);
+ genop(s, MKOP_ABC(OP_SEND, cursp(), undef, 2));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_CLASS:
+ {
+ int idx;
+
+ if (tree->car->car == (node*)0) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ else if (tree->car->car == (node*)1) {
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ push();
+ }
+ else {
+ codegen(s, tree->car->car, VAL);
+ }
+ if (tree->cdr->car) {
+ codegen(s, tree->cdr->car, VAL);
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ pop(); pop();
+ idx = new_msym(s, (mrb_sym)tree->car->cdr);
+ genop(s, MKOP_AB(OP_CLASS, cursp(), idx));
+ idx = scope_body(s, tree->cdr->cdr->car);
+ genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_MODULE:
+ {
+ int idx;
+
+ if (tree->car->car == (node*)0) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ else if (tree->car->car == (node*)1) {
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ push();
+ }
+ else {
+ codegen(s, tree->car->car, VAL);
+ }
+ pop();
+ idx = new_msym(s, (mrb_sym)tree->car->cdr);
+ genop(s, MKOP_AB(OP_MODULE, cursp(), idx));
+ idx = scope_body(s, tree->cdr->car);
+ genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_SCLASS:
+ {
+ int idx;
+
+ codegen(s, tree->car, VAL);
+ pop();
+ genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
+ idx = scope_body(s, tree->cdr->car);
+ genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_DEF:
+ {
+ int sym = new_msym(s, (mrb_sym)tree->car);
+ int idx = lambda_body(s, tree->cdr, 0);
+
+ genop(s, MKOP_A(OP_TCLASS, cursp()));
+ push();
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
+ pop();
+ genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
+ if (val) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ }
+ break;
+
+ case NODE_SDEF:
+ {
+ node *recv = tree->car;
+ int sym = new_msym(s, (mrb_sym)tree->cdr->car);
+ int idx = lambda_body(s, tree->cdr->cdr, 0);
+
+ codegen(s, recv, VAL);
+ pop();
+ genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
+ push();
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
+ pop();
+ genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
+ if (val) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ }
+ break;
+
+ case NODE_POSTEXE:
+ codegen(s, tree, NOVAL);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static codegen_scope*
+scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
+{
+ mrb_pool *pool = mrb_pool_open(mrb);
+ codegen_scope *p = mrb_pool_alloc(pool, sizeof(codegen_scope));
+ if (!p) return 0;
+
+ memset(p, 0, sizeof(codegen_scope));
+ p->mrb = mrb;
+ p->mpool = pool;
+ if (!prev) return p;
+ p->prev = prev;
+ p->ainfo = -1;
+
+ p->mrb = prev->mrb;
+ p->icapa = 1024;
+ p->iseq = mrb_malloc(mrb, sizeof(mrb_code)*p->icapa);
+
+ p->pcapa = 32;
+ p->pool = mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa);
+
+ p->syms = mrb_malloc(mrb, sizeof(mrb_sym)*256);
+
+ p->lv = lv;
+ p->sp += node_len(lv)+2;
+ p->nlocals = p->sp;
+
+ p->idx = mrb->irep_len++;
+
+ return p;
+}
+
+static void
+scope_finish(codegen_scope *s, int idx)
+{
+ mrb_state *mrb = s->mrb;
+ mrb_irep *irep;
+
+ mrb_add_irep(mrb, idx);
+ irep = mrb->irep[idx] = mrb_malloc(mrb, sizeof(mrb_irep));
+
+ irep->idx = idx;
+ irep->flags = 0;
+ if (s->iseq) {
+ irep->iseq = codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc);
+ irep->ilen = s->pc;
+ }
+ if (s->pool) {
+ irep->pool = codegen_realloc(s, s->pool, sizeof(mrb_value)*s->plen);
+ irep->plen = s->plen;
+ }
+ if (s->syms) {
+ irep->syms = codegen_realloc(s, s->syms, sizeof(mrb_sym)*s->slen);
+ irep->slen = s->slen;
+ }
+
+ irep->nlocals = s->nlocals;
+ irep->nregs = s->nregs;
+
+ mrb_pool_close(s->mpool);
+}
+
+static struct loopinfo*
+loop_push(codegen_scope *s, enum looptype t)
+{
+ struct loopinfo *p = codegen_palloc(s, sizeof(struct loopinfo));
+
+ p->type = t;
+ p->pc1 = p->pc2 = p->pc3 = 0;
+ p->prev = s->loop;
+ p->ensure_level = s->ensure_level;
+ p->acc = cursp();
+ s->loop = p;
+
+ return p;
+}
+
+static void
+loop_break(codegen_scope *s, node *tree)
+{
+ if (!s->loop) {
+ codegen(s, tree, NOVAL);
+ raise_error(s, "unexpected break");
+ }
+ else {
+ struct loopinfo *loop;
+
+ if (tree) {
+ codegen(s, tree, VAL);
+ printf("break op %d\n", cursp());
+ pop();
+ }
+
+ loop = s->loop;
+ while (loop->type == LOOP_BEGIN) {
+ genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL);
+ loop = loop->prev;
+ }
+ if (loop->type == LOOP_NORMAL) {
+ int tmp;
+
+ if (s->ensure_level > s->loop->ensure_level) {
+ genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ }
+ if (tree) {
+ genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL);
+ }
+ tmp = new_label(s);
+ genop(s, MKOP_sBx(OP_JMP, loop->pc3));
+ loop->pc3 = tmp;
+ }
+ else {
+ genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK));
+ }
+ }
+}
+
+static void
+loop_pop(codegen_scope *s, int val)
+{
+ if (val) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ dispatch_linked(s, s->loop->pc3);
+ s->loop = s->loop->prev;
+ if (val) push();
+}
+
+static void
+codedump(mrb_state *mrb, int n)
+{
+ mrb_irep *irep = mrb->irep[n];
+ int i;
+ mrb_code c;
+
+ if (!irep) return;
+ printf("irep %d nregs=%d nlocals=%d pools=%d syms=%d\n", n,
+ irep->nregs, irep->nlocals, irep->plen, irep->slen);
+ for (i=0; i<irep->ilen; i++) {
+ printf("%03d ", i);
+ c = irep->iseq[i];
+ switch (GET_OPCODE(c)) {
+ case OP_NOP:
+ printf("OP_NOP\n");
+ break;
+ case OP_MOVE:
+ printf("OP_MOVE\tR%d\tR%d\n", GETARG_A(c), GETARG_B(c));
+ break;
+ case OP_LOADL:
+ printf("OP_LOADL\tR%d\tL(%d)\n", GETARG_A(c), GETARG_Bx(c));
+ break;
+ case OP_LOADI:
+ printf("OP_LOADI\tR%d\t%d\n", GETARG_A(c), GETARG_sBx(c));
+ break;
+ case OP_LOADSYM:
+ printf("OP_LOADSYM\tR%d\t'%s'\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
+ break;
+ case OP_LOADNIL:
+ printf("OP_LOADNIL\tR%d\n", GETARG_A(c));
+ break;
+ case OP_LOADSELF:
+ printf("OP_LOADSELF\tR%d\n", GETARG_A(c));
+ break;
+ case OP_LOADT:
+ printf("OP_LOADT\tR%d\n", GETARG_A(c));
+ break;
+ case OP_LOADF:
+ printf("OP_LOADF\tR%d\n", GETARG_A(c));
+ break;
+ case OP_GETGLOBAL:
+ printf("OP_GETGLOBAL\tR%d\t'%s'\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
+ break;
+ case OP_SETGLOBAL:
+ printf("OP_SETGLOBAL\t'%s'\tR%d\n",
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
+ GETARG_A(c));
+ break;
+ case OP_GETCONST:
+ printf("OP_GETCONST\tR%d\t'%s'\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
+ break;
+ case OP_SETCONST:
+ printf("OP_SETCONST\t'%s'\tR%d\n",
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
+ GETARG_A(c));
+ break;
+ case OP_GETMCNST:
+ printf("OP_GETMCNST\tR%d\tR%d::%s\n", GETARG_A(c), GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
+ break;
+ case OP_SETMCNST:
+ printf("OP_SETMCNST\tR%d::%s\tR%d\n", GETARG_A(c)+1,
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
+ GETARG_A(c));
+ break;
+ case OP_GETIV:
+ printf("OP_GETIV\tR%d\t%s\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
+ break;
+ case OP_SETIV:
+ printf("OP_SETIV\t%s\tR%d\n",
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
+ GETARG_A(c));
+ break;
+ case OP_GETUPVAR:
+ printf("OP_GETUPVAR\tR%d\t%d\t%d\n",
+ GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ break;
+ case OP_SETUPVAR:
+ printf("OP_SETUPVAR\tR%d\t%d\t%d\n",
+ GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ break;
+ case OP_GETCV:
+ printf("OP_GETCV\tR%d\t%s\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
+ break;
+ case OP_SETCV:
+ printf("OP_SETCV\t%s\tR%d\n",
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
+ GETARG_A(c));
+ break;
+ case OP_JMP:
+ printf("OP_JMP\t\t%03d\n", i+GETARG_sBx(c));
+ break;
+ case OP_JMPIF:
+ printf("OP_JMPIF\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
+ break;
+ case OP_JMPNOT:
+ printf("OP_JMPNOT\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
+ break;
+ case OP_SEND:
+ printf("OP_SEND\tR%d\t'%s'\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_SUPER:
+ printf("OP_SUPER\tR%d\t%d\n", GETARG_A(c),
+ GETARG_C(c));
+ break;
+ case OP_ARGARY:
+ printf("OP_ARGARY\tR%d\t%d:%d:%d:%d\n", GETARG_A(c),
+ (GETARG_Bx(c)>>10)&0x3f,
+ (GETARG_Bx(c)>>9)&0x1,
+ (GETARG_Bx(c)>>4)&0x1f,
+ (GETARG_Bx(c)>>0)&0xf);
+ break;
+
+ case OP_ENTER:
+ printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n",
+ (GETARG_Ax(c)>>18)&0x1f,
+ (GETARG_Ax(c)>>13)&0x1f,
+ (GETARG_Ax(c)>>12)&0x1,
+ (GETARG_Ax(c)>>7)&0x1f,
+ (GETARG_Ax(c)>>2)&0x1f,
+ (GETARG_Ax(c)>>1)&0x1,
+ GETARG_Ax(c) & 0x1);
+ break;
+ case OP_RETURN:
+ printf("OP_RETURN\tR%d", GETARG_A(c));
+ switch (GETARG_B(c)) {
+ case OP_R_NORMAL:
+ printf("\n"); break;
+ case OP_R_RETURN:
+ printf("\treturn\n"); break;
+ case OP_R_BREAK:
+ printf("\tbreak\n"); break;
+ default:
+ printf("\tbroken\n"); break;
+ break;
+ }
+ break;
+ case OP_BLKPUSH:
+ printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d\n", GETARG_A(c),
+ (GETARG_Bx(c)>>10)&0x3f,
+ (GETARG_Bx(c)>>9)&0x1,
+ (GETARG_Bx(c)>>4)&0x1f,
+ (GETARG_Bx(c)>>0)&0xf);
+ break;
+
+ case OP_LAMBDA:
+ printf("OP_LAMBDA\tR%d\tI(%d)\t%d\n", GETARG_A(c), n+GETARG_b(c), GETARG_c(c));
+ break;
+ case OP_RANGE:
+ printf("OP_RANGE\tR%d\tR%d\t%d\n", GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ break;
+ case OP_METHOD:
+ printf("OP_METHOD\tR%d\t'%s'\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
+ break;
+
+ case OP_ADD:
+ printf("OP_ADD\tR%d\t'%s'\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_SUB:
+ printf("OP_SUB\tR%d\t'%s'\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_LT:
+ printf("OP_LT\tR%d\t'%s'\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_LE:
+ printf("OP_LE\tR%d\t'%s'\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_GT:
+ printf("OP_GT\tR%d\t'%s'\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_GE:
+ printf("OP_GE\tR%d\t'%s'\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+
+ case OP_STOP:
+ printf("OP_STOP\n");
+ break;
+
+ case OP_ARRAY:
+ printf("OP_ARRAY\tR%d\tR%d\t%d\n", GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ break;
+ case OP_ARYCAT:
+ printf("OP_ARYCAT\tR%d\tR%d\n", GETARG_A(c), GETARG_B(c));
+ break;
+ case OP_ARYPUSH:
+ printf("OP_ARYPUSH\tR%d\tR%d\n", GETARG_A(c), GETARG_B(c));
+ break;
+ case OP_AREF:
+ printf("OP_AREF\tR%d\tR%d\t%d\n", GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ break;
+ case OP_APOST:
+ printf("OP_APOST\tR%d\t%d\t%d\n", GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ break;
+ case OP_STRING:
+ printf("OP_STRING\tR%d\t'%s'\n", GETARG_A(c), RSTRING_PTR(irep->pool[GETARG_Bx(c)]));
+ break;
+ case OP_STRCAT:
+ printf("OP_STRCAT\tR%d\tR%d\n", GETARG_A(c), GETARG_B(c));
+ break;
+ case OP_HASH:
+ printf("OP_HASH\tR%d\tR%d\t%d\n", GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ break;
+
+ case OP_OCLASS:
+ printf("OP_OCLASS\tR%d\n", GETARG_A(c));
+ break;
+ case OP_CLASS:
+ printf("OP_CLASS\tR%d\t'%s'\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
+ break;
+ case OP_MODULE:
+ printf("OP_MODULE\tR%d\t'%s'\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
+ break;
+ case OP_EXEC:
+ printf("OP_EXEC\tR%d\tI(%d)\n", GETARG_A(c), n+GETARG_Bx(c));
+ break;
+ case OP_SCLASS:
+ printf("OP_SCLASS\tR%d\tR%d\n", GETARG_A(c), GETARG_B(c));
+ break;
+ case OP_TCLASS:
+ printf("OP_TCLASS\tR%d\n", GETARG_A(c));
+ break;
+ case OP_ERR:
+ printf("OP_ERR\t:L(%d)\n", GETARG_Bx(c));
+ break;
+ case OP_EPUSH:
+ printf("OP_EPUSH\t:I(%d)\n", n+GETARG_Bx(c));
+ break;
+ case OP_ONERR:
+ printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c));
+ break;
+ case OP_RESCUE:
+ printf("OP_RESCUE\tR%d\n", GETARG_A(c));
+ break;
+ case OP_RAISE:
+ printf("OP_RAISE\tR%d\n", GETARG_A(c));
+ break;
+ case OP_POPERR:
+ printf("OP_POPERR\t%d\n", GETARG_A(c));
+ break;
+ case OP_EPOP:
+ printf("OP_EPOP\t%d\n", GETARG_A(c));
+ break;
+
+ default:
+ printf("OP_unknown %d\t%d\t%d\t%d\n", GET_OPCODE(c),
+ GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ break;
+ }
+ }
+ printf("\n");
+}
+
+void
+codedump_all(mrb_state *mrb, int start)
+{
+ int i;
+
+ for (i=start; i<mrb->irep_len; i++) {
+ codedump(mrb, i);
+ }
+}
+
+static int
+codegen_start(mrb_state *mrb, node *tree)
+{
+ codegen_scope *scope = scope_new(mrb, 0, 0);
+
+ if (!scope) {
+ return -1;
+ }
+ scope->mrb = mrb;
+
+ if (setjmp(scope->jmp) != 0) {
+ return -1;
+ }
+ // prepare irep
+ codegen(scope, tree, NOVAL);
+ return 0;
+}
+
+int
+mrb_generate_code(mrb_state *mrb, node *tree)
+{
+ int start = mrb->irep_len;
+ int n;
+
+ n = codegen_start(mrb, tree);
+ if (n < 0) return n;
+
+ return start;
+}
+
+#ifdef CODEGEN_TEST
+int
+main()
+{
+ mrb_state *mrb = mrb_open();
+ int n;
+
+#if 1
+ n = mrb_compile_string(mrb, "p(__FILE__)\np(__LINE__)");
+#else
+ n = mrb_compile_string(mrb, "\
+def fib(n)\n\
+ if n<2\n\
+ n\n\
+ else\n\
+ fib(n-2)+fib(n-1)\n\
+ end\n\
+end\n\
+p(fib(30), \"\\n\")\n\
+");
+#endif
+ printf("ret: %d\n", n);
+#ifdef CODEGEN_DUMP
+ codedump_all(mrb, n);
+#endif
+ mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[0]), mrb_nil_value());
+
+ return 0;
+}
+#endif