Fixed a bug caused by too much optimization; fix #4137
[mruby.git] / mrbgems / mruby-compiler / core / codegen.c
index 9d7392c..8ab1b9b 100644 (file)
@@ -23,6 +23,8 @@
 #define MRB_CODEGEN_LEVEL_MAX 1024
 #endif
 
+#define MAXARG_S (1<<16)
+
 typedef mrb_ast_node node;
 typedef struct mrb_parser_state parser_state;
 
@@ -36,7 +38,7 @@ enum looptype {
 
 struct loopinfo {
   enum looptype type;
-  int pc1, pc2, pc3, acc;
+  int pc0, pc1, pc2, pc3, acc;
   int ensure_level;
   struct loopinfo *prev;
 };
@@ -50,9 +52,10 @@ typedef struct scope {
 
   node *lv;
 
-  int sp;
-  int pc;
-  int lastlabel;
+  uint16_t sp;
+  uint16_t pc;
+  uint16_t lastpc;
+  uint16_t lastlabel;
   int ainfo:15;
   mrb_bool mscope:1;
 
@@ -63,10 +66,10 @@ typedef struct scope {
 
   mrb_code *iseq;
   uint16_t *lines;
-  int icapa;
+  uint32_t icapa;
 
   mrb_irep *irep;
-  int pcapa, scapa, rcapa;
+  uint32_t pcapa, scapa, rcapa;
 
   uint16_t nlocals;
   uint16_t nregs;
@@ -142,32 +145,140 @@ codegen_realloc(codegen_scope *s, void *p, size_t len)
 static int
 new_label(codegen_scope *s)
 {
-  s->lastlabel = s->pc;
-  return s->pc;
+  return s->lastlabel = s->pc;
 }
 
-static inline int
-genop(codegen_scope *s, mrb_code i)
+static void
+emit_B(codegen_scope *s, uint32_t pc, uint8_t i)
 {
-  if (s->pc == s->icapa) {
+  if (pc >= MAXARG_S || s->icapa >= MAXARG_S) {
+    codegen_error(s, "too big code block");
+  }
+  if (pc >= s->icapa) {
     s->icapa *= 2;
+    if (s->icapa > MAXARG_S) {
+      s->icapa = MAXARG_S;
+    }
     s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa);
     if (s->lines) {
-      s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa);
+      s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->icapa);
       s->irep->lines = s->lines;
     }
   }
-  s->iseq[s->pc] = i;
   if (s->lines) {
-    s->lines[s->pc] = s->lineno;
+    s->lines[pc] = s->lineno;
+  }
+  s->iseq[pc] = i;
+}
+
+static void
+emit_S(codegen_scope *s, int pc, uint16_t i)
+{
+  uint8_t hi = i>>8;
+  uint8_t lo = i&0xff;
+
+  emit_B(s, pc,   hi);
+  emit_B(s, pc+1, lo);
+}
+
+static void
+gen_B(codegen_scope *s, uint8_t i)
+{
+  emit_B(s, s->pc, i);
+  s->pc++;
+}
+
+static void
+gen_S(codegen_scope *s, uint16_t i)
+{
+  emit_S(s, s->pc, i);
+  s->pc += 2;
+}
+
+static void
+genop_0(codegen_scope *s, mrb_code i)
+{
+  s->lastpc = s->pc;
+  gen_B(s, i);
+}
+
+static void
+genop_1(codegen_scope *s, mrb_code i, uint16_t a)
+{
+  s->lastpc = s->pc;
+  if (a > 0xff) {
+    gen_B(s, OP_EXT1);
+    gen_B(s, i);
+    gen_S(s, a);
+  }
+  else {
+    gen_B(s, i);
+    gen_B(s, (uint8_t)a);
+  }
+}
+
+static void
+genop_2(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b)
+{
+  s->lastpc = s->pc;
+  if (a > 0xff && b > 0xff) {
+    gen_B(s, OP_EXT3);
+    gen_B(s, i);
+    gen_S(s, a);
+    gen_S(s, b);
+  }
+  else if (b > 0xff) {
+    gen_B(s, OP_EXT2);
+    gen_B(s, i);
+    gen_B(s, (uint8_t)a);
+    gen_S(s, b);
+  }
+  else if (a > 0xff) {
+    gen_B(s, OP_EXT1);
+    gen_B(s, i);
+    gen_S(s, a);
+    gen_B(s, (uint8_t)b);
   }
-  return s->pc++;
+  else {
+    gen_B(s, i);
+    gen_B(s, (uint8_t)a);
+    gen_B(s, (uint8_t)b);
+  }
+}
+
+static void
+genop_3(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b, uint8_t c)
+{
+  genop_2(s, i, a, b);
+  gen_B(s, c);
+}
+
+static void
+genop_2S(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b)
+{
+  genop_1(s, i, a);
+  gen_S(s, b);
+}
+
+static void
+genop_W(codegen_scope *s, mrb_code i, uint32_t a)
+{
+  uint8_t a1 = (a>>16) & 0xff;
+  uint8_t a2 = (a>>8) & 0xff;
+  uint8_t a3 = a & 0xff;
+
+  s->lastpc = s->pc;
+  gen_B(s, i);
+  gen_B(s, a1);
+  gen_B(s, a2);
+  gen_B(s, a3);
 }
 
 #define NOVAL  0
 #define VAL    1
 
-static mrb_bool
+//static
+mrb_bool
 no_optimize(codegen_scope *s)
 {
   if (s && s->parser && s->parser->no_optimize)
@@ -175,271 +286,270 @@ no_optimize(codegen_scope *s)
   return FALSE;
 }
 
-static int
-genop_peep(codegen_scope *s, mrb_code i, int val)
+static
+mrb_bool
+on_eval(codegen_scope *s)
 {
-  /* peephole optimization */
-  if (!no_optimize(s) && 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);
+  if (s && s->parser && s->parser->on_eval)
+    return TRUE;
+  return FALSE;
+}
 
-    switch (c1) {
-    case OP_MOVE:
-      if (GETARG_A(i) == GETARG_B(i)) {
-        /* skip useless OP_MOVE */
-        return 0;
-      }
-      if (val) break;
-      switch (c0) {
-      case OP_MOVE:
-        if (GETARG_A(i) == GETARG_A(i0)) {
-          /* skip overriden OP_MOVE */
-          s->pc--;
-          s->iseq[s->pc] = i;
-        }
-        if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0)) {
-          /* skip swapping OP_MOVE */
-          return 0;
-        }
-        if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
-          s->pc--;
-          return genop_peep(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)), val);
-        }
-        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 0;
-        }
-        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 0;
-        }
-        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:
-        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 0;
-        }
-        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 0;
-        }
-        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 0;
-        }
-        break;
-      default:
-        break;
-      }
-      break;
-    case OP_SETIV:
-    case OP_SETCV:
-    case OP_SETCONST:
-    case OP_SETMCNST:
-    case OP_SETGLOBAL:
-      if (val) break;
-      if (c0 == OP_MOVE) {
-        if (GETARG_A(i) == GETARG_A(i0)) {
-          s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i));
-          return 0;
-        }
-      }
-      break;
-    case OP_SETUPVAR:
-      if (val) break;
-      if (c0 == 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 0;
-        }
-      }
-      break;
-    case OP_EPOP:
-      if (c0 == OP_EPOP) {
-        s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i));
-        return 0;
-      }
-      break;
-    case OP_POPERR:
-      if (c0 == OP_POPERR) {
-        s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i));
-        return 0;
-      }
-      break;
-    case OP_RETURN:
-      switch (c0) {
-      case OP_RETURN:
-        return 0;
-      case OP_MOVE:
-        if (GETARG_A(i0) >= s->nlocals) {
-          s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL);
-          return 0;
-        }
-        break;
-      case OP_SETIV:
-      case OP_SETCV:
-      case OP_SETCONST:
-      case OP_SETMCNST:
-      case OP_SETUPVAR:
-      case OP_SETGLOBAL:
-        s->pc--;
-        genop_peep(s, i0, NOVAL);
-        i0 = s->iseq[s->pc-1];
-        return genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL));
-#if 0
-      case OP_SEND:
-        if (GETARG_B(i) == OP_R_NORMAL && GETARG_A(i) == GETARG_A(i0)) {
-          s->iseq[s->pc-1] = MKOP_ABC(OP_TAILCALL, GETARG_A(i0), GETARG_B(i0), GETARG_C(i0));
-          return;
-        }
-        break;
-#endif
-      default:
-        break;
-      }
-      break;
-    case OP_ADD:
-    case OP_SUB:
-      if (c0 == OP_LOADI) {
-        int c = GETARG_sBx(i0);
-
-        if (c1 == OP_SUB) c = -c;
-        if (c > 127 || c < -127) break;
-        if (0 <= c)
-          s->iseq[s->pc-1] = MKOP_ABC(OP_ADDI, GETARG_A(i), GETARG_B(i), c);
-        else
-          s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c);
-        return 0;
-      }
-    case OP_STRCAT:
-      if (c0 == OP_STRING) {
-        mrb_value v = s->irep->pool[GETARG_Bx(i0)];
+struct mrb_insn_data
+mrb_decode_insn(mrb_code *pc)
+{
+  struct mrb_insn_data data = { 0 };
+  mrb_code insn = READ_B();
+  uint16_t a = 0;
+  uint16_t b = 0;
+  uint8_t  c = 0;
+
+  switch (insn) {
+#define FETCH_Z() /* empty */
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+  }
+  switch (insn) {
+  case OP_EXT1:
+    insn = READ_B();
+    switch (insn) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+    }
+    break;
+  case OP_EXT2:
+    insn = READ_B();
+    switch (insn) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+    }
+    break;
+  case OP_EXT3:
+    insn = READ_B();
+    switch (insn) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+    }
+    break;
+  default:
+    break;
+  }
+  data.insn = insn;
+  data.a = a;
+  data.b = b;
+  data.c = c;
+  return data;
+}
 
-        if (mrb_string_p(v) && RSTRING_LEN(v) == 0) {
-          s->pc--;
-          return 0;
-        }
-      }
-      if (c0 == OP_LOADNIL) {
-        if (GETARG_B(i) == GETARG_A(i0)) {
-          s->pc--;
-          return 0;
-        }
-      }
+static struct mrb_insn_data
+mrb_last_insn(codegen_scope *s)
+{
+  if (s->pc == s->lastpc) {
+    struct mrb_insn_data data;
+
+    data.insn = OP_NOP;
+    return data;
+  }
+  return mrb_decode_insn(&s->iseq[s->lastpc]);
+}
+
+static mrb_bool
+no_peephole(codegen_scope *s)
+{
+  return no_optimize(s) || s->lastlabel == s->pc || s->pc == 0 || s->pc == s->lastpc;
+}
+
+static uint16_t
+genjmp(codegen_scope *s, mrb_code i, uint16_t pc)
+{
+  uint16_t pos;
+
+  s->lastpc = s->pc;
+  gen_B(s, i);
+  pos = s->pc;
+  gen_S(s, pc);
+  return pos;
+}
+
+static uint16_t
+genjmp2(codegen_scope *s, mrb_code i, uint16_t a, int pc, int val)
+{
+  uint16_t pos;
+
+  if (!no_peephole(s) && !val) {
+    struct mrb_insn_data data = mrb_last_insn(s);
+
+    if (data.insn == OP_MOVE && data.a == a) {
+      s->pc = s->lastpc;
+      a = data.b;
+    }
+  }
+
+  s->lastpc = s->pc;
+  if (a > 0xff) {
+    gen_B(s, OP_EXT1);
+    gen_B(s, i);
+    gen_S(s, a);
+    pos = s->pc;
+    gen_S(s, pc);
+  }
+  else {
+    gen_B(s, i);
+    gen_B(s, (uint8_t)a);
+    pos = s->pc;
+    gen_S(s, pc);
+  }
+  return pos;
+}
+
+static void
+gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep)
+{
+  if (no_peephole(s)) {
+  normal:
+    genop_2(s, OP_MOVE, dst, src);
+    if (on_eval(s)) {
+      genop_0(s, OP_NOP);
+    }
+    return;
+  }
+  else {
+    struct mrb_insn_data data = mrb_last_insn(s);
+
+    switch (data.insn) {
+    case OP_MOVE:
+      if (dst == src) return;             /* remove useless MOVE */
+      if (data.b == dst && data.a == src) /* skip swapping MOVE */
+        return;
+      goto normal;
+    case OP_LOADNIL: case OP_LOADSELF: case OP_LOADT: case OP_LOADF:
+    case OP_LOADI__1:
+    case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3:
+    case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7:
+      if (nopeep || data.a != src || data.a < s->nlocals) goto normal;
+      s->pc = s->lastpc;
+      genop_1(s, data.insn, dst);
       break;
-    case OP_JMPIF:
-    case OP_JMPNOT:
-      if (c0 == OP_MOVE && GETARG_A(i) == GETARG_A(i0)) {
-        s->iseq[s->pc-1] = MKOP_AsBx(c1, GETARG_B(i0), GETARG_sBx(i));
-        return s->pc-1;
-      }
+    case OP_LOADI: case OP_LOADINEG: case OP_LOADL: case OP_LOADSYM:
+    case OP_GETGV: case OP_GETSV: case OP_GETIV: case OP_GETCV:
+    case OP_GETCONST: case OP_STRING:
+    case OP_LAMBDA: case OP_BLOCK: case OP_METHOD: case OP_BLKPUSH:
+      if (nopeep || data.a != src || data.a < s->nlocals) goto normal;
+      s->pc = s->lastpc;
+      genop_2(s, data.insn, dst, data.b);
       break;
     default:
-      break;
+      goto normal;
     }
   }
-  return genop(s, i);
 }
 
 static void
-scope_error(codegen_scope *s)
+gen_return(codegen_scope *s, uint8_t op, uint16_t src)
 {
-  exit(EXIT_FAILURE);
+  if (no_peephole(s)) {
+    genop_1(s, op, src);
+  }
+  else {
+    struct mrb_insn_data data = mrb_last_insn(s);
+
+    if (data.insn == OP_MOVE && src == data.a) {
+      s->pc = s->lastpc;
+      genop_1(s, op, data.b);
+    }
+    else if (data.insn != OP_RETURN) {
+      genop_1(s, op, src);
+    }
+  }
 }
 
-static inline void
-dispatch(codegen_scope *s, int pc)
+static void
+gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst, uint16_t idx)
 {
-  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:
-#ifndef MRB_DISABLE_STDIO
-    fprintf(stderr, "bug: dispatch on non JMP op\n");
-#endif
-    scope_error(s);
-    break;
+  if (no_peephole(s)) {
+  normal:
+    genop_2(s, op, dst, idx);
+    return;
   }
-  if (diff > MAXARG_sBx) {
-    codegen_error(s, "too distant jump address");
+  else {
+    struct mrb_insn_data data = mrb_last_insn(s);
+
+    switch (data.insn) {
+    case OP_LOADI__1:
+      if (op == OP_ADD) op = OP_SUB;
+      else op = OP_ADD;
+      data.b = 1;
+      goto replace;
+    case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3:
+    case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7:
+      data.b = data.insn - OP_LOADI_0;
+      /* fall through */
+    case OP_LOADI:
+    replace:
+      if (data.b >= 128) goto normal;
+      s->pc = s->lastpc;
+      if (op == OP_ADD) {
+        genop_3(s, OP_ADDI, dst, idx, (uint8_t)data.b);
+      }
+      else {
+        genop_3(s, OP_SUBI, dst, idx, (uint8_t)data.b);
+      }
+      break;
+    default:
+      goto normal;
+    }
   }
-  s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff);
 }
 
-static void
-dispatch_linked(codegen_scope *s, int pc)
+static int
+dispatch(codegen_scope *s, uint16_t pos0)
 {
-  mrb_code i;
-  int pos;
+  uint16_t newpos;
+
+  s->lastlabel = s->pc;
+  newpos = PEEK_S(s->iseq+pos0);
+  emit_S(s, pos0, s->pc);
+  return newpos;
+}
 
-  if (!pc) return;
+static void
+dispatch_linked(codegen_scope *s, uint16_t pos)
+{
+  if (pos==0) return;
   for (;;) {
-    i = s->iseq[pc];
-    pos = GETARG_sBx(i);
-    dispatch(s, pc);
-    if (!pos) break;
-    pc = pos;
+    pos = dispatch(s, pos);
+    if (pos==0) break;
   }
 }
 
 #define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0)
 static void
-push_(codegen_scope *s)
+push_n_(codegen_scope *s, int n)
 {
-  if (s->sp > 511) {
+  if (s->sp+n >= 0xffff) {
     codegen_error(s, "too complex expression");
   }
-  s->sp++;
+  s->sp+=n;
   nregs_update;
 }
 
 static void
-push_n_(codegen_scope *s, int n)
+pop_n_(codegen_scope *s, int n)
 {
-  if (s->sp+n > 511) {
-    codegen_error(s, "too complex expression");
+  if ((int)s->sp-n < 0) {
+    codegen_error(s, "stack pointer underflow");
   }
-  s->sp+=n;
-  nregs_update;
+  s->sp-=n;
 }
 
-#define push() push_(s)
+#define push() push_n_(s,1)
 #define push_n(n) push_n_(s,n)
-#define pop_(s) ((s)->sp--)
-#define pop() pop_(s)
-#define pop_n(n) (s->sp-=(n))
+#define pop() pop_n_(s,1)
+#define pop_n(n) pop_n_(s,n)
 #define cursp() (s->sp)
 
 static inline int
@@ -460,6 +570,7 @@ new_lit(codegen_scope *s, mrb_value val)
         return i;
     }
     break;
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
     for (i=0; i<s->irep->plen; i++) {
       pv = &s->irep->pool[i];
@@ -467,6 +578,7 @@ new_lit(codegen_scope *s, mrb_value val)
       if (mrb_float(*pv) == mrb_float(val)) return i;
     }
     break;
+#endif
   case MRB_TT_FIXNUM:
     for (i=0; i<s->irep->plen; i++) {
       pv = &s->irep->pool[i];
@@ -492,11 +604,13 @@ new_lit(codegen_scope *s, mrb_value val)
     *pv = mrb_str_pool(s->mrb, val);
     break;
 
+#ifndef MRB_WITHOUT_FLOAT
   case MRB_TT_FLOAT:
 #ifdef MRB_WORD_BOXING
     *pv = mrb_float_pool(s->mrb, mrb_float(val));
     break;
 #endif
+#endif
   case MRB_TT_FIXNUM:
     *pv = val;
     break;
@@ -508,52 +622,23 @@ new_lit(codegen_scope *s, mrb_value val)
   return i;
 }
 
-/* method symbols should be fit in 9 bits */
-#define MAXMSYMLEN 512
 /* maximum symbol numbers */
-#define MAXSYMLEN 65536
+#define MAXSYMLEN 0x10000
 
 static int
-new_msym(codegen_scope *s, mrb_sym sym)
+new_sym(codegen_scope *s, mrb_sym sym)
 {
   int i, len;
 
   mrb_assert(s->irep);
 
   len = s->irep->slen;
-  if (len > MAXMSYMLEN) len = MAXMSYMLEN;
   for (i=0; i<len; i++) {
     if (s->irep->syms[i] == sym) return i;
-    if (s->irep->syms[i] == 0) break;
-  }
-  if (i == MAXMSYMLEN) {
-    codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXMSYMLEN) ")");
-  }
-  s->irep->syms[i] = sym;
-  if (i == s->irep->slen) s->irep->slen++;
-  return i;
-}
-
-static int
-new_sym(codegen_scope *s, mrb_sym sym)
-{
-  int i;
-
-  for (i=0; i<s->irep->slen; i++) {
-    if (s->irep->syms[i] == sym) return i;
   }
-  if (s->irep->slen == MAXSYMLEN) {
-    codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXSYMLEN) ")");
-  }
-
-  if (s->irep->slen > MAXMSYMLEN/2 && s->scapa == MAXMSYMLEN) {
-    s->scapa = MAXSYMLEN;
-    s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*MAXSYMLEN);
-    for (i = s->irep->slen; i < MAXMSYMLEN; i++) {
-      static const mrb_sym mrb_sym_zero = { 0 };
-      s->irep->syms[i] = mrb_sym_zero;
-    }
-    s->irep->slen = MAXMSYMLEN;
+  if (s->irep->slen >= s->scapa) {
+    s->scapa *= 2;
+    s->irep->syms = (mrb_sym*)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*s->scapa);
   }
   s->irep->syms[s->irep->slen] = sym;
   return s->irep->slen++;
@@ -571,8 +656,12 @@ node_len(node *tree)
   return n;
 }
 
+#define nint(x) ((int)(intptr_t)(x))
+#define nchar(x) ((char)(intptr_t)(x))
 #define nsym(x) ((mrb_sym)(intptr_t)(x))
+
 #define lv_name(lv) nsym((lv)->car)
+
 static int
 lv_idx(codegen_scope *s, mrb_sym id)
 {
@@ -594,7 +683,6 @@ for_body(codegen_scope *s, node *tree)
   int idx;
   struct loopinfo *lp;
   node *n2;
-  mrb_code c;
 
   /* generate receiver */
   codegen(s, tree->cdr->car, VAL);
@@ -608,7 +696,7 @@ for_body(codegen_scope *s, node *tree)
 
   /* generate loop variable */
   n2 = tree->car;
-  genop(s, MKOP_Ax(OP_ENTER, 0x40000));
+  genop_W(s, OP_ENTER, 0x40000);
   if (n2->car && !n2->car->cdr && !n2->cdr) {
     gen_assignment(s, n2->car->car, 1, NOVAL);
   }
@@ -622,25 +710,20 @@ for_body(codegen_scope *s, node *tree)
   /* loop body */
   codegen(s, tree->cdr->cdr->car, VAL);
   pop();
-  if (s->pc > 0) {
-    c = s->iseq[s->pc-1];
-    if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel)
-      genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
-  }
+  gen_return(s, OP_RETURN, cursp());
   loop_pop(s, NOVAL);
   scope_finish(s);
   s = prev;
-  genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK));
+  genop_2(s, OP_BLOCK, cursp(), s->irep->rlen-1);
   push();pop(); /* space for a block */
   pop();
-  idx = new_msym(s, mrb_intern_lit(s->mrb, "each"));
-  genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0));
+  idx = new_sym(s, mrb_intern_lit(s->mrb, "each"));
+  genop_3(s, OP_SENDB, cursp(), idx, 0);
 }
 
 static int
 lambda_body(codegen_scope *s, node *tree, int blk)
 {
-  mrb_code c;
   codegen_scope *parent = s;
   s = scope_new(s->mrb, s, tree->car);
   if (s == NULL) {
@@ -651,7 +734,7 @@ lambda_body(codegen_scope *s, node *tree, int blk)
 
   if (blk) {
     struct loopinfo *lp = loop_push(s, LOOP_BLOCK);
-    lp->pc1 = new_label(s);
+    lp->pc0 = new_label(s);
   }
   tree = tree->cdr;
   if (tree->car) {
@@ -659,70 +742,112 @@ lambda_body(codegen_scope *s, node *tree, int blk)
     int ma, oa, ra, pa, ka, kd, ba;
     int pos, i;
     node *n, *opt;
+    node *tail;
 
+    /* mandatory arguments */
     ma = node_len(tree->car->car);
     n = tree->car->car;
     while (n) {
       n = n->cdr;
     }
+    tail = tree->car->cdr->cdr->cdr->cdr;
+
+    /* optional arguments */
     oa = node_len(tree->car->cdr->car);
+    /* rest argument? */
     ra = tree->car->cdr->cdr->car ? 1 : 0;
+    /* mandatory arugments after rest argument */
     pa = node_len(tree->car->cdr->cdr->cdr->car);
-    ka = kd = 0;
-    ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
+    /* keyword arguments */
+    ka = tail? node_len(tail->cdr->car) : 0;
+    /* keyword dictionary? */
+    kd = tail && tail->cdr->cdr->car? 1 : 0;
+    /* block argument? */
+    ba = tail && tail->cdr->cdr->cdr->car ? 1 : 0;
 
     if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) {
       codegen_error(s, "too many formal arguments");
     }
-    a = ((mrb_aspec)(ma & 0x1f) << 18)
-      | ((mrb_aspec)(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));
+    a = MRB_ARGS_REQ(ma)
+      | MRB_ARGS_OPT(oa)
+      | (ra? MRB_ARGS_REST() : 0)
+      | MRB_ARGS_POST(pa)
+      | MRB_ARGS_KEY(ka, kd)
+      | (ba? MRB_ARGS_BLOCK() : 0);
+    s->ainfo = (((ma+oa) & 0x3f) << 7) /* (12bits = 5:1:5:1) */
+      | ((ra & 0x1) << 6)
+      | ((pa & 0x1f) << 1)
+      | (kd & 0x1);
+    genop_W(s, OP_ENTER, a);
+    /* generate jump table for optional arguments initializer */
     pos = new_label(s);
     for (i=0; i<oa; i++) {
       new_label(s);
-      genop(s, MKOP_sBx(OP_JMP, 0));
+      genjmp(s, OP_JMP, 0);
     }
     if (oa > 0) {
-      genop(s, MKOP_sBx(OP_JMP, 0));
+      genjmp(s, OP_JMP, 0);
     }
     opt = tree->car->cdr->car;
     i = 0;
     while (opt) {
       int idx;
 
-      dispatch(s, pos+i);
+      dispatch(s, pos+i*3+1);
       codegen(s, opt->car->cdr, VAL);
-      idx = lv_idx(s, nsym(opt->car->car));
       pop();
-      genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL);
+      idx = lv_idx(s, nsym(opt->car->car));
+      gen_move(s, idx, cursp(), 0);
       i++;
       opt = opt->cdr;
     }
     if (oa > 0) {
-      dispatch(s, pos+i);
+      dispatch(s, pos+i*3+1);
+    }
+
+    if (tail) {
+      node *kwds = tail->cdr->car;
+      int kwrest = 0;
+
+      if (tail->cdr->cdr->car) {
+        kwrest = 1;
+      }
+      mrb_assert(nint(tail->car) == NODE_ARGS_TAIL);
+      mrb_assert(node_len(tail) == 4);
+
+      while (kwds) {
+        int jmpif_key_p, jmp_def_set = -1;
+        node *kwd = kwds->car, *def_arg = kwd->cdr->cdr->car;
+        mrb_sym kwd_sym = nsym(kwd->cdr->car);
+
+        mrb_assert(nint(kwd->car) == NODE_KW_ARG);
+
+        if (def_arg) {
+          genop_2(s, OP_KEY_P, cursp(), new_sym(s, kwd_sym));
+          jmpif_key_p = genjmp2(s, OP_JMPIF, cursp(), 0, 0);
+          codegen(s, def_arg, VAL);
+          pop();
+          gen_move(s, lv_idx(s, kwd_sym), cursp(), 0);
+          jmp_def_set = genjmp(s, OP_JMP, 0);
+          dispatch(s, jmpif_key_p);
+        }
+        genop_2(s, OP_KARG, lv_idx(s, kwd_sym), new_sym(s, kwd_sym));
+        if (jmp_def_set != -1) {
+          dispatch(s, jmp_def_set);
+        }
+        i++;
+
+        kwds = kwds->cdr;
+      }
+      if (tail->cdr->car && !kwrest) {
+        genop_0(s, OP_KEYEND);
+      }
     }
   }
   codegen(s, tree->cdr->car, VAL);
   pop();
   if (s->pc > 0) {
-    c = s->iseq[s->pc-1];
-    if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) {
-      if (s->nregs == 0) {
-        genop(s, MKOP_A(OP_LOADNIL, 0));
-        genop(s, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
-      }
-      else {
-        genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
-      }
-    }
+    gen_return(s, OP_RETURN, cursp());
   }
   if (blk) {
     loop_pop(s, NOVAL);
@@ -736,24 +861,13 @@ scope_body(codegen_scope *s, node *tree, int val)
 {
   codegen_scope *scope = scope_new(s->mrb, s, tree->car);
   if (scope == NULL) {
-    raise_error(s, "unexpected scope");
+    codegen_error(s, "unexpected scope");
   }
 
   codegen(scope, tree->cdr, VAL);
+  gen_return(scope, OP_RETURN, scope->sp-1);
   if (!s->iseq) {
-    genop(scope, MKOP_A(OP_STOP, 0));
-  }
-  else if (!val) {
-    genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
-  }
-  else {
-    if (scope->nregs == 0) {
-      genop(scope, MKOP_A(OP_LOADNIL, 0));
-      genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
-    }
-    else {
-      genop_peep(scope, MKOP_AB(OP_RETURN, scope->sp-1, OP_R_NORMAL), NOVAL);
-    }
+    genop_0(scope, OP_STOP);
   }
   scope_finish(scope);
   if (!s->irep) {
@@ -763,9 +877,6 @@ scope_body(codegen_scope *s, node *tree, int val)
   return s->irep->rlen - 1;
 }
 
-#define nint(x) ((int)(intptr_t)(x))
-#define nchar(x) ((char)(intptr_t)(x))
-
 static mrb_bool
 nosplat(node *t)
 {
@@ -817,15 +928,15 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
         }
         else {
           pop_n(n);
-          genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+          genop_2(s, OP_ARRAY, cursp(), n);
           push();
           codegen(s, t->car, VAL);
           pop(); pop();
           if (is_splat) {
-            genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+            genop_1(s, OP_ARYCAT, cursp());
           }
           else {
-            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+            genop_1(s, OP_ARYPUSH, cursp());
           }
         }
         t = t->cdr;
@@ -834,10 +945,10 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
           codegen(s, t->car, VAL);
           pop(); pop();
           if (nint(t->car->car) == NODE_SPLAT) {
-            genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+            genop_1(s, OP_ARYCAT, cursp());
           }
           else {
-            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+            genop_1(s, OP_ARYPUSH, cursp());
           }
           t = t->cdr;
         }
@@ -868,16 +979,10 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
   codegen(s, tree->car, VAL); /* receiver */
   if (safe) {
     int recv = cursp()-1;
-    genop(s, MKOP_A(OP_LOADNIL, cursp()));
-    push();
-    genop(s, MKOP_AB(OP_MOVE, cursp(), recv));
-    push_n(2); pop_n(2); /* space for one arg and a block */
-    pop();
-    idx = new_msym(s, mrb_intern_lit(s->mrb, "=="));
-    genop(s, MKOP_ABC(OP_EQ, cursp(), idx, 1));
-    skip = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+    gen_move(s, cursp(), recv, 1);
+    skip = genjmp2(s, OP_JMPNIL, cursp(), 0, val);
   }
-  idx = new_msym(s, sym);
+  idx = new_sym(s, sym);
   tree = tree->cdr->cdr->car;
   if (tree) {
     n = gen_values(s, tree->car, VAL, sp?1:0);
@@ -886,14 +991,15 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
       push();
     }
   }
-  if (sp) {
+  if (sp) {                     /* last argument pushed (attr=) */
     if (sendv) {
+      gen_move(s, cursp(), sp, 0);
       pop();
-      genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp));
+      genop_1(s, OP_ARYPUSH, cursp());
       push();
     }
     else {
-      genop(s, MKOP_AB(OP_MOVE, cursp(), sp));
+      gen_move(s, cursp(), sp, 0);
       push();
       n++;
     }
@@ -902,9 +1008,7 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
     noop = 1;
     codegen(s, tree->cdr, VAL);
     pop();
-  }
-  else {
-    blk = cursp();
+    blk = 1;
   }
   push();pop();
   pop_n(n+1);
@@ -913,39 +1017,38 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
     const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen);
 
     if (!noop && symlen == 1 && symname[0] == '+' && n == 1)  {
-      genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val);
+      gen_addsub(s, OP_ADD, cursp(), idx);
     }
     else if (!noop && symlen == 1 && symname[0] == '-' && n == 1)  {
-      genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val);
+      gen_addsub(s, OP_SUB, cursp(), idx);
     }
     else if (!noop && symlen == 1 && symname[0] == '*' && n == 1)  {
-      genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n));
+      genop_2(s, OP_MUL, cursp(), idx);
     }
     else if (!noop && symlen == 1 && symname[0] == '/' && n == 1)  {
-      genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n));
+      genop_2(s, OP_DIV, cursp(), idx);
     }
     else if (!noop && symlen == 1 && symname[0] == '<' && n == 1)  {
-      genop(s, MKOP_ABC(OP_LT, cursp(), idx, n));
+      genop_2(s, OP_LT, cursp(), idx);
     }
     else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=' && n == 1)  {
-      genop(s, MKOP_ABC(OP_LE, cursp(), idx, n));
+      genop_2(s, OP_LE, cursp(), idx);
     }
     else if (!noop && symlen == 1 && symname[0] == '>' && n == 1)  {
-      genop(s, MKOP_ABC(OP_GT, cursp(), idx, n));
+      genop_2(s, OP_GT, cursp(), idx);
     }
     else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=' && n == 1)  {
-      genop(s, MKOP_ABC(OP_GE, cursp(), idx, n));
+      genop_2(s, OP_GE, cursp(), idx);
     }
     else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=' && n == 1)  {
-      genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n));
+      genop_2(s, OP_EQ, cursp(), idx);
     }
     else {
-      if (sendv) n = CALL_MAXARGS;
-      if (blk > 0) {                   /* no block */
-        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n));
+      if (sendv) {
+        genop_2(s, blk ? OP_SENDVB : OP_SENDV, cursp(), idx);
       }
       else {
-        genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n));
+        genop_3(s, blk ? OP_SENDB : OP_SEND, cursp(), idx, n);
       }
     }
   }
@@ -967,13 +1070,14 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
   switch (type) {
   case NODE_GVAR:
     idx = new_sym(s, nsym(tree));
-    genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val);
+    genop_2(s, OP_SETGV, sp, idx);
     break;
   case NODE_LVAR:
     idx = lv_idx(s, nsym(tree));
     if (idx > 0) {
       if (idx != sp) {
-        genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val);
+        gen_move(s, idx, sp, val);
+        if (val && on_eval(s)) genop_0(s, OP_NOP);
       }
       break;
     }
@@ -984,7 +1088,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
       while (up) {
         idx = lv_idx(up, nsym(tree));
         if (idx > 0) {
-          genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val);
+          genop_3(s, OP_SETUPVAR, sp, idx, lv);
           break;
         }
         lv++;
@@ -994,23 +1098,23 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
     break;
   case NODE_IVAR:
     idx = new_sym(s, nsym(tree));
-    genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val);
+    genop_2(s, OP_SETIV, sp, idx);
     break;
   case NODE_CVAR:
     idx = new_sym(s, nsym(tree));
-    genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val);
+    genop_2(s, OP_SETCV, sp, idx);
     break;
   case NODE_CONST:
     idx = new_sym(s, nsym(tree));
-    genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val);
+    genop_2(s, OP_SETCONST, sp, idx);
     break;
   case NODE_COLON2:
-    idx = new_sym(s, nsym(tree->cdr));
-    genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL);
+    gen_move(s, cursp(), sp, 0);
     push();
     codegen(s, tree->car, VAL);
     pop_n(2);
-    genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val);
+    idx = new_sym(s, nsym(tree->cdr));
+    genop_2(s, OP_SETMCNST, sp, idx);
     break;
 
   case NODE_CALL:
@@ -1020,7 +1124,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
              type == NODE_SCALL);
     pop();
     if (val) {
-      genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val);
+      gen_move(s, cursp(), sp, 0);
     }
     break;
 
@@ -1051,8 +1155,12 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
     t = tree->car;
     n = 0;
     while (t) {
-      genop(s, MKOP_ABC(OP_AREF, cursp(), rhs, n));
-      gen_assignment(s, t->car, cursp(), NOVAL);
+      int sp = cursp();
+
+      genop_3(s, OP_AREF, sp, rhs, n);
+      push();
+      gen_assignment(s, t->car, sp, NOVAL);
+      pop();
       n++;
       t = t->cdr;
     }
@@ -1066,15 +1174,10 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
         p = p->cdr;
       }
     }
-    if (val) {
-      genop(s, MKOP_AB(OP_MOVE, cursp(), rhs));
-    }
-    else {
-      pop();
-    }
-    push_n(post);
-    pop_n(post);
-    genop(s, MKOP_ABC(OP_APOST, cursp(), n, post));
+    gen_move(s, cursp(), rhs, val);
+    push_n(post+1);
+    pop_n(post+1);
+    genop_3(s, OP_APOST, cursp(), n, post);
     n = 1;
     if (t->car) {               /* rest */
       gen_assignment(s, t->car, cursp(), NOVAL);
@@ -1087,20 +1190,20 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
         n++;
       }
     }
-    if (!val) {
-      push();
+    if (val) {
+      gen_move(s, cursp(), rhs, 0);
     }
   }
 }
 
 static void
-gen_send_intern(codegen_scope *s)
+gen_intern(codegen_scope *s)
 {
-  push();pop(); /* space for a block */
   pop();
-  genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0));
+  genop_1(s, OP_INTERN, cursp());
   push();
 }
+
 static void
 gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
 {
@@ -1123,25 +1226,25 @@ gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
           j = 0;
           ++i;
           if (sym)
-            gen_send_intern(s);
+            gen_intern(s);
         }
         break;
       }
-      if (j >= 2) {
+      while (j >= 2) {
         pop(); pop();
-        genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+        genop_1(s, OP_STRCAT, cursp());
         push();
-        j = 1;
+        j--;
       }
       tree = tree->cdr;
     }
     if (j > 0) {
       ++i;
       if (sym)
-        gen_send_intern(s);
+        gen_intern(s);
     }
     pop_n(i);
-    genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i));
+    genop_2(s, OP_ARRAY, cursp(), i);
     push();
   }
   else {
@@ -1160,9 +1263,10 @@ 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, 1, idx));
+  genop_1(s, OP_ERR, idx);
 }
 
+#ifndef MRB_WITHOUT_FLOAT
 static double
 readint_float(codegen_scope *s, const char *p, int base)
 {
@@ -1188,6 +1292,7 @@ readint_float(codegen_scope *s, const char *p, int base)
   }
   return f;
 }
+#endif
 
 static mrb_int
 readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow)
@@ -1236,11 +1341,9 @@ static void
 gen_retval(codegen_scope *s, node *tree)
 {
   if (nint(tree->car) == NODE_SPLAT) {
-    genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), 0));
-    push();
     codegen(s, tree, VAL);
-    pop(); pop();
-    genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+    pop();
+    genop_1(s, OP_ARYDUP, cursp());
   }
   else {
     codegen(s, tree, VAL);
@@ -1256,7 +1359,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
   if (!tree) {
     if (val) {
-      genop(s, MKOP_A(OP_LOADNIL, cursp()));
+      genop_1(s, OP_LOADNIL, cursp());
       push();
     }
     return;
@@ -1280,7 +1383,7 @@ codegen(codegen_scope *s, node *tree, int val)
   switch (nt) {
   case NODE_BEGIN:
     if (val && !tree) {
-      genop(s, MKOP_A(OP_LOADNIL, cursp()));
+      genop_1(s, OP_LOADNIL, cursp());
       push();
     }
     while (tree) {
@@ -1291,18 +1394,18 @@ codegen(codegen_scope *s, node *tree, int val)
 
   case NODE_RESCUE:
     {
-      int onerr, noexc, exend, pos1, pos2, tmp;
+      int noexc, exend, pos1, pos2, tmp;
       struct loopinfo *lp;
 
       if (tree->car == NULL) goto exit;
-      onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
       lp = loop_push(s, LOOP_BEGIN);
-      lp->pc1 = onerr;
+      lp->pc0 = new_label(s);
+      lp->pc1 = genjmp(s, OP_ONERR, 0);
       codegen(s, tree->car, VAL);
       pop();
       lp->type = LOOP_RESCUE;
-      noexc = genop(s, MKOP_Bx(OP_JMP, 0));
-      dispatch(s, onerr);
+      noexc = genjmp(s, OP_JMP, 0);
+      dispatch(s, lp->pc1);
       tree = tree->cdr;
       exend = 0;
       pos1 = 0;
@@ -1310,7 +1413,7 @@ codegen(codegen_scope *s, node *tree, int val)
         node *n2 = tree->car;
         int exc = cursp();
 
-        genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
+        genop_1(s, OP_EXCEPT, exc);
         push();
         while (n2) {
           node *n3 = n2->car;
@@ -1321,29 +1424,29 @@ codegen(codegen_scope *s, node *tree, int val)
           do {
             if (n4 && n4->car && nint(n4->car->car) == NODE_SPLAT) {
               codegen(s, n4->car, VAL);
-              genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
+              gen_move(s, cursp(), exc, 0);
               push_n(2); pop_n(2); /* space for one arg and a block */
               pop();
-              genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
+              genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1);
             }
             else {
               if (n4) {
                 codegen(s, n4->car, VAL);
               }
               else {
-                genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError"))));
+                genop_2(s, OP_GETCONST, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "StandardError")));
                 push();
               }
               pop();
-              genop(s, MKOP_ABC(OP_RESCUE, exc, cursp(), 1));
+              genop_2(s, OP_RESCUE, exc, cursp());
             }
-            tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+            tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, val);
             pos2 = tmp;
             if (n4) {
               n4 = n4->cdr;
             }
           } while (n4);
-          pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
+          pos1 = genjmp(s, OP_JMP, 0);
           dispatch_linked(s, pos2);
 
           pop();
@@ -1354,20 +1457,20 @@ codegen(codegen_scope *s, node *tree, int val)
             codegen(s, n3->cdr->cdr->car, val);
             if (val) pop();
           }
-          tmp = genop(s, MKOP_sBx(OP_JMP, exend));
+          tmp = genjmp(s, OP_JMP, exend);
           exend = tmp;
           n2 = n2->cdr;
           push();
         }
         if (pos1) {
           dispatch(s, pos1);
-          genop(s, MKOP_A(OP_RAISE, exc));
+          genop_1(s, OP_RAISE, exc);
         }
       }
       pop();
       tree = tree->cdr;
       dispatch(s, noexc);
-      genop(s, MKOP_A(OP_POPERR, 1));
+      genop_1(s, OP_POPERR, 1);
       if (tree->car) {
         codegen(s, tree->car, val);
       }
@@ -1384,15 +1487,13 @@ codegen(codegen_scope *s, node *tree, int val)
         (nint(tree->cdr->cdr->car) == NODE_BEGIN &&
          tree->cdr->cdr->cdr)) {
       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, NOVAL);
-      s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx);
+      genop_1(s, OP_EPUSH, idx);
+      codegen(s, tree->car, val);
       s->ensure_level--;
-      genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL);
+      genop_1(s, OP_EPOP, 1);
     }
     else {                      /* empty ensure ignored */
       codegen(s, tree->car, val);
@@ -1403,7 +1504,7 @@ codegen(codegen_scope *s, node *tree, int val)
     if (val) {
       int idx = lambda_body(s, tree, 1);
 
-      genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA));
+      genop_2(s, OP_LAMBDA, cursp(), idx);
       push();
     }
     break;
@@ -1412,7 +1513,7 @@ codegen(codegen_scope *s, node *tree, int val)
     if (val) {
       int idx = lambda_body(s, tree, 1);
 
-      genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK));
+      genop_2(s, OP_BLOCK, cursp(), idx);
       push();
     }
     break;
@@ -1420,10 +1521,10 @@ codegen(codegen_scope *s, node *tree, int val)
   case NODE_IF:
     {
       int pos1, pos2;
-      node *e = tree->cdr->cdr->car;
+      node *elsepart = tree->cdr->cdr->car;
 
       if (!tree->car) {
-        codegen(s, e, val);
+        codegen(s, elsepart, val);
         goto exit;
       }
       switch (nint(tree->car->car)) {
@@ -1434,27 +1535,27 @@ codegen(codegen_scope *s, node *tree, int val)
         goto exit;
       case NODE_FALSE:
       case NODE_NIL:
-        codegen(s, e, val);
+        codegen(s, elsepart, val);
         goto exit;
       }
       codegen(s, tree->car, VAL);
       pop();
-      pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL);
+      pos1 = genjmp2(s, OP_JMPNOT, cursp(), 0, val);
 
       codegen(s, tree->cdr->car, val);
-      if (e) {
+      if (elsepart) {
         if (val) pop();
-        pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
+        pos2 = genjmp(s, OP_JMP, 0);
         dispatch(s, pos1);
-        codegen(s, e, val);
+        codegen(s, elsepart, val);
         dispatch(s, pos2);
       }
       else {
         if (val) {
           pop();
-          pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
+          pos2 = genjmp(s, OP_JMP, 0);
           dispatch(s, pos1);
-          genop(s, MKOP_A(OP_LOADNIL, cursp()));
+          genop_1(s, OP_LOADNIL, cursp());
           dispatch(s, pos2);
           push();
         }
@@ -1471,7 +1572,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
       codegen(s, tree->car, VAL);
       pop();
-      pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0));
+      pos = genjmp2(s, OP_JMPNOT, cursp(), 0, val);
       codegen(s, tree->cdr, val);
       dispatch(s, pos);
     }
@@ -1483,7 +1584,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
       codegen(s, tree->car, VAL);
       pop();
-      pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+      pos = genjmp2(s, OP_JMPIF, cursp(), 0, val);
       codegen(s, tree->cdr, val);
       dispatch(s, pos);
     }
@@ -1493,13 +1594,14 @@ codegen(codegen_scope *s, node *tree, int val)
     {
       struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
 
-      lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
+      lp->pc0 = new_label(s);
+      lp->pc1 = genjmp(s, OP_JMP, 0);
       lp->pc2 = new_label(s);
       codegen(s, tree->cdr, NOVAL);
       dispatch(s, lp->pc1);
       codegen(s, tree->car, VAL);
       pop();
-      genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc));
+      genjmp2(s, OP_JMPIF, cursp(), lp->pc2, NOVAL);
 
       loop_pop(s, val);
     }
@@ -1509,13 +1611,14 @@ codegen(codegen_scope *s, node *tree, int val)
     {
       struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
 
-      lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
+      lp->pc0 = new_label(s);
+      lp->pc1 = genjmp(s, OP_JMP, 0);
       lp->pc2 = new_label(s);
       codegen(s, tree->cdr, NOVAL);
       dispatch(s, lp->pc1);
       codegen(s, tree->car, VAL);
       pop();
-      genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc));
+      genjmp2(s, OP_JMPNOT, cursp(), lp->pc2, NOVAL);
 
       loop_pop(s, val);
     }
@@ -1544,41 +1647,40 @@ codegen(codegen_scope *s, node *tree, int val)
         while (n) {
           codegen(s, n->car, VAL);
           if (head) {
-            genop(s, MKOP_AB(OP_MOVE, cursp(), head));
-            push_n(2); pop_n(2); /* space for one arg and a block */
-            pop();
+            gen_move(s, cursp(), head, 0);
+            push(); push(); pop(); pop(); pop();
             if (nint(n->car->car) == NODE_SPLAT) {
-              genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
+              genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1);
             }
             else {
-              genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1));
+              genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "===")), 1);
             }
           }
           else {
             pop();
           }
-          tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+          tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, NOVAL);
           pos2 = tmp;
           n = n->cdr;
         }
         if (tree->car->car) {
-          pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
+          pos1 = genjmp(s, OP_JMP, 0);
           dispatch_linked(s, pos2);
         }
         codegen(s, tree->car->cdr, val);
         if (val) pop();
-        tmp = genop(s, MKOP_sBx(OP_JMP, pos3));
+        tmp = genjmp(s, OP_JMP, pos3);
         pos3 = tmp;
         if (pos1) dispatch(s, pos1);
         tree = tree->cdr;
       }
       if (val) {
         int pos = cursp();
-        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+        genop_1(s, OP_LOADNIL, cursp());
         if (pos3) dispatch_linked(s, pos3);
         if (head) pop();
         if (cursp() != pos) {
-          genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
+          gen_move(s, cursp(), pos, 0);
         }
         push();
       }
@@ -1610,7 +1712,7 @@ codegen(codegen_scope *s, node *tree, int val)
     codegen(s, tree->cdr, val);
     if (val) {
       pop(); pop();
-      genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE));
+      genop_1(s, OP_RANGE_INC, cursp());
       push();
     }
     break;
@@ -1620,7 +1722,7 @@ codegen(codegen_scope *s, node *tree, int val)
     codegen(s, tree->cdr, val);
     if (val) {
       pop(); pop();
-      genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE));
+      genop_1(s, OP_RANGE_EXC, cursp());
       push();
     }
     break;
@@ -1631,7 +1733,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
       codegen(s, tree->car, VAL);
       pop();
-      genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+      genop_2(s, OP_GETMCNST, cursp(), sym);
       if (val) push();
     }
     break;
@@ -1640,8 +1742,8 @@ codegen(codegen_scope *s, node *tree, int val)
     {
       int sym = new_sym(s, nsym(tree));
 
-      genop(s, MKOP_A(OP_OCLASS, cursp()));
-      genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+      genop_1(s, OP_OCLASS, cursp());
+      genop_2(s, OP_GETMCNST, cursp(), sym);
       if (val) push();
     }
     break;
@@ -1654,7 +1756,7 @@ codegen(codegen_scope *s, node *tree, int val)
       if (n >= 0) {
         if (val) {
           pop_n(n);
-          genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+          genop_2(s, OP_ARRAY, cursp(), n);
           push();
         }
       }
@@ -1665,21 +1767,47 @@ codegen(codegen_scope *s, node *tree, int val)
     break;
 
   case NODE_HASH:
+  case NODE_KW_HASH:
     {
       int len = 0;
       mrb_bool update = FALSE;
 
       while (tree) {
-        codegen(s, tree->car->car, val);
-        codegen(s, tree->car->cdr, val);
-        len++;
+        if (nint(tree->car->car->car) == NODE_KW_REST_ARGS) {
+          if (len > 0) {
+            pop_n(len*2);
+            if (!update) {
+              genop_2(s, OP_HASH, cursp(), len);
+            }
+            else {
+              pop();
+              genop_2(s, OP_HASHADD, cursp(), len);
+            }
+            push();
+          }
+          codegen(s, tree->car->cdr, VAL);
+          if (len > 0 || update) {
+            pop(); pop();
+            genop_1(s, OP_HASHCAT, cursp());
+            push();
+          }
+          update = TRUE;
+          len = 0;
+        }
+        else {
+          codegen(s, tree->car->car, val);
+          codegen(s, tree->car->cdr, val);
+          len++;
+        }
         tree = tree->cdr;
-        if (val && len == 126) {
+        if (val && len == 255) {
           pop_n(len*2);
-          genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
-          if (update) {
+          if (!update) {
+            genop_2(s, OP_HASH, cursp(), len);
+          }
+          else {
             pop();
-            genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
+            genop_2(s, OP_HASHADD, cursp(), len);
           }
           push();
           update = TRUE;
@@ -1688,10 +1816,14 @@ codegen(codegen_scope *s, node *tree, int val)
       }
       if (val) {
         pop_n(len*2);
-        genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
-        if (update) {
+        if (!update) {
+          genop_2(s, OP_HASH, cursp(), len);
+        }
+        else {
           pop();
-          genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
+          if (len > 0) {
+            genop_2(s, OP_HASHADD, cursp(), len);
+          }
         }
         push();
       }
@@ -1732,7 +1864,7 @@ codegen(codegen_scope *s, node *tree, int val)
               n++;
             }
             else {
-              genop(s, MKOP_A(OP_LOADNIL, rhs+n));
+              genop_1(s, OP_LOADNIL, rhs+n);
               gen_assignment(s, t->car, rhs+n, NOVAL);
             }
             t = t->cdr;
@@ -1756,7 +1888,7 @@ codegen(codegen_scope *s, node *tree, int val)
             else {
               rn = len - post - n;
             }
-            genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn));
+            genop_3(s, OP_ARRAY2, cursp(), rhs+n, rn);
             gen_assignment(s, t->car, cursp(), NOVAL);
             n += rn;
           }
@@ -1771,7 +1903,7 @@ codegen(codegen_scope *s, node *tree, int val)
         }
         pop_n(len);
         if (val) {
-          genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len));
+          genop_2(s, OP_ARRAY, rhs, len);
           push();
         }
       }
@@ -1799,17 +1931,17 @@ codegen(codegen_scope *s, node *tree, int val)
         int onerr, noexc, exc;
         struct loopinfo *lp;
 
-        onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
+        onerr = genjmp(s, OP_ONERR, 0);
         lp = loop_push(s, LOOP_BEGIN);
         lp->pc1 = onerr;
         exc = cursp();
         codegen(s, tree->car, VAL);
         lp->type = LOOP_RESCUE;
-        genop(s, MKOP_A(OP_POPERR, 1));
-        noexc = genop(s, MKOP_Bx(OP_JMP, 0));
+        genop_1(s, OP_POPERR, 1);
+        noexc = genjmp(s, OP_JMP, 0);
         dispatch(s, onerr);
-        genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
-        genop(s, MKOP_A(OP_LOADF, exc));
+        genop_1(s, OP_EXCEPT, exc);
+        genop_1(s, OP_LOADF, exc);
         dispatch(s, noexc);
         loop_pop(s, NOVAL);
       }
@@ -1823,7 +1955,7 @@ codegen(codegen_scope *s, node *tree, int val)
           push();
         }
         codegen(s, n->car, VAL);   /* receiver */
-        idx = new_msym(s, nsym(n->cdr->car));
+        idx = new_sym(s, nsym(n->cdr->car));
         base = cursp()-1;
         if (n->cdr->cdr->car) {
           nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 1);
@@ -1837,12 +1969,12 @@ codegen(codegen_scope *s, node *tree, int val)
           }
         }
         /* copy receiver and arguments */
-        genop(s, MKOP_AB(OP_MOVE, cursp(), base));
+        gen_move(s, cursp(), base, 1);
         for (i=0; i<nargs; i++) {
-          genop(s, MKOP_AB(OP_MOVE, cursp()+i+1, base+i+1));
+          gen_move(s, cursp()+i+1, base+i+1, 1);
         }
-        push_n(nargs+1);pop_n(nargs+1);
-        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+        push_n(nargs+2);pop_n(nargs+2); /* space for receiver, arguments and a block */
+        genop_3(s, OP_SEND, cursp(), idx, callargs);
         push();
       }
       else {
@@ -1856,30 +1988,30 @@ codegen(codegen_scope *s, node *tree, int val)
         pop();
         if (val) {
           if (vsp >= 0) {
-            genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+            gen_move(s, vsp, cursp(), 1);
           }
-          pos = genop(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0));
+          pos = genjmp2(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0, val);
         }
         else {
-          pos = genop_peep(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0), NOVAL);
+          pos = genjmp2(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0, val);
         }
         codegen(s, tree->cdr->cdr->car, VAL);
         pop();
         if (val && vsp >= 0) {
-          genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+          gen_move(s, vsp, cursp(), 1);
         }
         if (nint(tree->car->car) == NODE_CALL) {
           if (callargs == CALL_MAXARGS) {
             pop();
-            genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+            genop_1(s, OP_ARYPUSH, cursp());
           }
           else {
             pop_n(callargs);
             callargs++;
           }
           pop();
-          idx = new_msym(s, attrsym(s, nsym(tree->car->cdr->cdr->car)));
-          genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+          idx = new_sym(s, attrsym(s, nsym(tree->car->cdr->cdr->car)));
+          genop_3(s, OP_SEND, cursp(), idx, callargs);
         }
         else {
           gen_assignment(s, tree->car, cursp(), val);
@@ -1891,52 +2023,52 @@ codegen(codegen_scope *s, node *tree, int val)
       push(); pop();
       pop(); pop();
 
-      idx = new_msym(s, sym);
+      idx = new_sym(s, sym);
       if (len == 1 && name[0] == '+')  {
-        genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, 1), val);
+        gen_addsub(s, OP_ADD, cursp(), idx);
       }
       else if (len == 1 && name[0] == '-')  {
-        genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val);
+        gen_addsub(s, OP_SUB, cursp(), idx);
       }
       else if (len == 1 && name[0] == '*')  {
-        genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1));
+        genop_2(s, OP_MUL, cursp(), idx);
       }
       else if (len == 1 && name[0] == '/')  {
-        genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1));
+        genop_2(s, OP_DIV, cursp(), idx);
       }
       else if (len == 1 && name[0] == '<')  {
-        genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1));
+        genop_2(s, OP_LT, cursp(), idx);
       }
       else if (len == 2 && name[0] == '<' && name[1] == '=')  {
-        genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1));
+        genop_2(s, OP_LE, cursp(), idx);
       }
       else if (len == 1 && name[0] == '>')  {
-        genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1));
+        genop_2(s, OP_GT, cursp(), idx);
       }
       else if (len == 2 && name[0] == '>' && name[1] == '=')  {
-        genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1));
+        genop_2(s, OP_GE, cursp(), idx);
       }
       else {
-        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1));
+        genop_3(s, OP_SEND, cursp(), idx, 1);
       }
       if (callargs < 0) {
         gen_assignment(s, tree->car, cursp(), val);
       }
       else {
         if (val && vsp >= 0) {
-          genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+          gen_move(s, vsp, cursp(), 0);
         }
         if (callargs == CALL_MAXARGS) {
           pop();
-          genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+          genop_1(s, OP_ARYPUSH, cursp());
         }
         else {
           pop_n(callargs);
           callargs++;
         }
         pop();
-        idx = new_msym(s, attrsym(s,nsym(tree->car->cdr->cdr->car)));
-        genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+        idx = new_sym(s, attrsym(s,nsym(tree->car->cdr->cdr->car)));
+        genop_3(s, OP_SEND, cursp(), idx, callargs);
       }
     }
     break;
@@ -1953,7 +2085,7 @@ codegen(codegen_scope *s, node *tree, int val)
         s2 = s2->prev;
         if (!s2) break;
       }
-      genop(s, MKOP_ABx(OP_ARGARY, cursp(), (lv & 0xf)));
+      genop_2S(s, OP_ARGARY, cursp(), (lv & 0xf));
       push(); push();         /* ARGARY pushes two values */
       pop(); pop();
       if (tree) {
@@ -1971,12 +2103,12 @@ codegen(codegen_scope *s, node *tree, int val)
         pop();
       }
       else {
-        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+        genop_1(s, OP_LOADNIL, cursp());
         push(); pop();
       }
       pop_n(n+1);
       if (sendv) n = CALL_MAXARGS;
-      genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n));
+      genop_2(s, OP_SUPER, cursp(), n);
       if (val) push();
     }
     break;
@@ -1993,14 +2125,14 @@ codegen(codegen_scope *s, node *tree, int val)
         if (!s2) break;
       }
       if (s2) ainfo = s2->ainfo;
-      genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf)));
+      genop_2S(s, OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf));
       push(); push(); pop();    /* ARGARY pushes two values */
       if (tree && tree->cdr) {
         codegen(s, tree->cdr, VAL);
         pop();
       }
       pop(); pop();
-      genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS));
+      genop_2(s, OP_SUPER, cursp(), CALL_MAXARGS);
       if (val) push();
     }
     break;
@@ -2010,13 +2142,13 @@ codegen(codegen_scope *s, node *tree, int val)
       gen_retval(s, tree);
     }
     else {
-      genop(s, MKOP_A(OP_LOADNIL, cursp()));
+      genop_1(s, OP_LOADNIL, cursp());
     }
     if (s->loop) {
-      genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN));
+      gen_return(s, OP_RETURN_BLK, cursp());
     }
     else {
-      genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+      gen_return(s, OP_RETURN, cursp());
     }
     if (val) push();
     break;
@@ -2043,9 +2175,9 @@ codegen(codegen_scope *s, node *tree, int val)
       }
       push();pop(); /* space for a block */
       pop_n(n+1);
-      genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)));
+      genop_2S(s, OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf));
       if (sendv) n = CALL_MAXARGS;
-      genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n));
+      genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "call")), n);
       if (val) push();
     }
     break;
@@ -2061,10 +2193,10 @@ codegen(codegen_scope *s, node *tree, int val)
     }
     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);
+        genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
       }
       codegen(s, tree, NOVAL);
-      genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc));
+      genjmp(s, OP_JMP, s->loop->pc0);
     }
     else {
       if (tree) {
@@ -2072,9 +2204,9 @@ codegen(codegen_scope *s, node *tree, int val)
         pop();
       }
       else {
-        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+        genop_1(s, OP_LOADNIL, cursp());
       }
-      genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+      gen_return(s, OP_RETURN, cursp());
     }
     if (val) push();
     break;
@@ -2085,9 +2217,9 @@ codegen(codegen_scope *s, node *tree, int val)
     }
     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_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
       }
-      genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
+      genjmp(s, OP_JMP, s->loop->pc2);
     }
     if (val) push();
     break;
@@ -2114,12 +2246,12 @@ codegen(codegen_scope *s, node *tree, int val)
         }
         else {
           if (n > 0) {
-            genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
+            genop_1(s, OP_POPERR, n);
           }
           if (s->ensure_level > lp->ensure_level) {
-            genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL);
+            genop_1(s, OP_EPOP, s->ensure_level - lp->ensure_level);
           }
-          genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
+          genjmp(s, OP_JMP, lp->pc0);
         }
       }
       if (val) push();
@@ -2131,7 +2263,8 @@ codegen(codegen_scope *s, node *tree, int val)
       int idx = lv_idx(s, nsym(tree));
 
       if (idx > 0) {
-        genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL);
+        gen_move(s, cursp(), idx, val);
+        if (val && on_eval(s)) genop_0(s, OP_NOP);
       }
       else {
         int lv = 0;
@@ -2140,7 +2273,7 @@ codegen(codegen_scope *s, node *tree, int val)
         while (up) {
           idx = lv_idx(up, nsym(tree));
           if (idx > 0) {
-            genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv));
+            genop_3(s, OP_GETUPVAR, cursp(), idx, lv);
             break;
           }
           lv++;
@@ -2152,29 +2285,29 @@ codegen(codegen_scope *s, node *tree, int val)
     break;
 
   case NODE_GVAR:
-    if (val) {
+    {
       int sym = new_sym(s, nsym(tree));
 
-      genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
-      push();
+      genop_2(s, OP_GETGV, cursp(), sym);
+      if (val) push();
     }
     break;
 
   case NODE_IVAR:
-    if (val) {
+    {
       int sym = new_sym(s, nsym(tree));
 
-      genop(s, MKOP_ABx(OP_GETIV, cursp(), sym));
-      push();
+      genop_2(s, OP_GETIV, cursp(), sym);
+      if (val) push();
     }
     break;
 
   case NODE_CVAR:
-    if (val) {
+    {
       int sym = new_sym(s, nsym(tree));
 
-      genop(s, MKOP_ABx(OP_GETCV, cursp(), sym));
-      push();
+      genop_2(s, OP_GETCV, cursp(), sym);
+      if (val) push();
     }
     break;
 
@@ -2182,15 +2315,13 @@ codegen(codegen_scope *s, node *tree, int val)
     {
       int sym = new_sym(s, nsym(tree));
 
-      genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym));
-      if (val) {
-        push();
-      }
+      genop_2(s, OP_GETCONST, cursp(), sym);
+      if (val) push();
     }
     break;
 
   case NODE_DEFINED:
-    codegen(s, tree, VAL);
+    codegen(s, tree, val);
     break;
 
   case NODE_BACK_REF:
@@ -2202,7 +2333,7 @@ codegen(codegen_scope *s, node *tree, int val)
       buf[1] = nchar(tree);
       buf[2] = 0;
       sym = new_sym(s, mrb_intern_cstr(s->mrb, buf));
-      genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+      genop_2(s, OP_GETGV, cursp(), sym);
       push();
     }
     break;
@@ -2215,7 +2346,7 @@ codegen(codegen_scope *s, node *tree, int val)
 
       str = mrb_format(mrb, "$%S", mrb_fixnum_value(nint(tree)));
       sym = new_sym(s, mrb_intern_str(mrb, str));
-      genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+      genop_2(s, OP_GETGV, cursp(), sym);
       push();
     }
     break;
@@ -2225,7 +2356,7 @@ codegen(codegen_scope *s, node *tree, int val)
     break;
 
   case NODE_BLOCK_ARG:
-    codegen(s, tree, VAL);
+    codegen(s, tree, val);
     break;
 
   case NODE_INT:
@@ -2233,95 +2364,101 @@ codegen(codegen_scope *s, node *tree, int val)
       char *p = (char*)tree->car;
       int base = nint(tree->cdr->car);
       mrb_int i;
-      mrb_code co;
       mrb_bool overflow;
 
       i = readint_mrb_int(s, p, base, FALSE, &overflow);
+#ifndef MRB_WITHOUT_FLOAT
       if (overflow) {
         double f = readint_float(s, p, base);
         int off = new_lit(s, mrb_float_value(s->mrb, f));
 
-        genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+        genop_2(s, OP_LOADL, cursp(), off);
       }
-      else {
-        if (i < MAXARG_sBx && i > -MAXARG_sBx) {
-          co = MKOP_AsBx(OP_LOADI, cursp(), i);
-        }
+      else
+#endif
+      {
+        if (i == -1) genop_1(s, OP_LOADI__1, cursp());
+        else if (i < 0) genop_2(s, OP_LOADINEG, cursp(), (uint16_t)-i);
+        else if (i < 8) genop_1(s, OP_LOADI_0 + (uint8_t)i, cursp());
+        else if (i <= 0xffff) genop_2(s, OP_LOADI, cursp(), (uint16_t)i);
         else {
           int off = new_lit(s, mrb_fixnum_value(i));
-          co = MKOP_ABx(OP_LOADL, cursp(), off);
+          genop_2(s, OP_LOADL, cursp(), off);
         }
-        genop(s, co);
       }
       push();
     }
     break;
 
+#ifndef MRB_WITHOUT_FLOAT
   case NODE_FLOAT:
     if (val) {
       char *p = (char*)tree;
       mrb_float f = mrb_float_read(p, NULL);
       int off = new_lit(s, mrb_float_value(s->mrb, f));
 
-      genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+      genop_2(s, OP_LOADL, cursp(), off);
       push();
     }
     break;
+#endif
 
   case NODE_NEGATE:
     {
       nt = nint(tree->car);
-      tree = tree->cdr;
       switch (nt) {
+#ifndef MRB_WITHOUT_FLOAT
       case NODE_FLOAT:
         if (val) {
-          char *p = (char*)tree;
+          char *p = (char*)tree->cdr;
           mrb_float f = mrb_float_read(p, NULL);
           int off = new_lit(s, mrb_float_value(s->mrb, -f));
 
-          genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+          genop_2(s, OP_LOADL, cursp(), off);
           push();
         }
         break;
+#endif
 
       case NODE_INT:
         if (val) {
-          char *p = (char*)tree->car;
-          int base = nint(tree->cdr->car);
+          char *p = (char*)tree->cdr->car;
+          int base = nint(tree->cdr->cdr->car);
           mrb_int i;
-          mrb_code co;
           mrb_bool overflow;
 
           i = readint_mrb_int(s, p, base, TRUE, &overflow);
+#ifndef MRB_WITHOUT_FLOAT
           if (overflow) {
             double f = readint_float(s, p, base);
             int off = new_lit(s, mrb_float_value(s->mrb, -f));
 
-            genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+            genop_2(s, OP_LOADL, cursp(), off);
           }
           else {
-            if (i < MAXARG_sBx && i > -MAXARG_sBx) {
-              co = MKOP_AsBx(OP_LOADI, cursp(), i);
+#endif
+            if (i == -1) genop_1(s, OP_LOADI__1, cursp());
+            else if (i >= -0xffff) {
+              genop_2(s, OP_LOADINEG, cursp(), (uint16_t)-i);
             }
             else {
               int off = new_lit(s, mrb_fixnum_value(i));
-              co = MKOP_ABx(OP_LOADL, cursp(), off);
+              genop_2(s, OP_LOADL, cursp(), off);
             }
-            genop(s, co);
+#ifndef MRB_WITHOUT_FLOAT
           }
+#endif
           push();
         }
         break;
 
       default:
         if (val) {
-          int sym = new_msym(s, mrb_intern_lit(s->mrb, "-"));
-
-          genop(s, MKOP_ABx(OP_LOADI, cursp(), 0));
-          push();
+          int sym = new_sym(s, mrb_intern_lit(s->mrb, "-@"));
           codegen(s, tree, VAL);
-          pop(); pop();
-          genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2));
+          pop();
+          genop_3(s, OP_SEND, cursp(), sym, 0);
+          push();
         }
         else {
           codegen(s, tree, NOVAL);
@@ -2339,7 +2476,7 @@ codegen(codegen_scope *s, node *tree, int val)
       int off = new_lit(s, mrb_str_new(s->mrb, p, len));
 
       mrb_gc_arena_restore(s->mrb, ai);
-      genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+      genop_2(s, OP_STRING, cursp(), off);
       push();
     }
     break;
@@ -2352,7 +2489,7 @@ codegen(codegen_scope *s, node *tree, int val)
       node *n = tree;
 
       if (!n) {
-        genop(s, MKOP_A(OP_LOADNIL, cursp()));
+        genop_1(s, OP_LOADNIL, cursp());
         push();
         break;
       }
@@