]> Nishi Git Mirror - gwion.git/commitdiff
:art: Peephole optimization
authorJérémie Astor <fennecdjay@gmail.com>
Wed, 30 Dec 2020 01:48:32 +0000 (02:48 +0100)
committerJérémie Astor <fennecdjay@gmail.com>
Wed, 30 Dec 2020 01:48:32 +0000 (02:48 +0100)
13 files changed:
include/instr.h
include/opcode.h
include/vararg.h
opcode.txt
src/emit/emit.c
src/lib/array.c
src/lib/lib_func.c
src/lib/object_op.c
src/lib/ptr.c
src/lib/union.c
src/lib/vararg.c
src/vm/vm.c
src/vm/vm_code.c

index c5286af0b83606140a9a898dc7676e0bf3b7eee1..b084ce4af092956915c6ec1262c13ba7bf3f2612 100644 (file)
@@ -28,7 +28,7 @@ struct Instr_ {
   m_uint m_val2;
   void (*execute)(const VM_Shred shred, const Instr instr);
 };
-#define BYTECODE_SZ (sizeof(struct Instr_) - SZ_INT)
+#define BYTECODE_SZ ((2*sizeof(unsigned)) + sizeof(struct Instr_) - SZ_INT*2)
 
 INSTR(EOC);
 INSTR(DTOR_EOC);
@@ -49,7 +49,6 @@ INSTR(ArrayStruct);
 
 /* vararg */
 INSTR(VarargIni);
-INSTR(VarargCheck);
 
 INSTR(DotTmpl);
 INSTR(GTmpl);
index a94876d0509fd513a9bdcd1d821499077dea5f3a..60813e0daa8c14e2e54da4ff30c3df418c055d79 100644 (file)
@@ -125,8 +125,7 @@ enum {
   eCastF2I,
   eTime_Advance,
   eSetCode,
-  eRegPop,
-  eRegPush,
+  eRegMove,
   eReg2Mem,
   eReg2Mem4,
   eOverflow,
@@ -313,8 +312,7 @@ enum {
 #define  CastF2I             (f_instr)eCastF2I
 #define  Time_Advance        (f_instr)eTime_Advance
 #define  SetCode             (f_instr)eSetCode
-#define  RegPop              (f_instr)eRegPop
-#define  RegPush             (f_instr)eRegPush
+#define  RegMove             (f_instr)eRegMove
 #define  Reg2Mem             (f_instr)eReg2Mem
 #define  Reg2Mem4            (f_instr)eReg2Mem4
 #define  Overflow            (f_instr)eOverflow
index 76feea9f13f7407779291a34b34e9ffa78b5d07f..14dbdf14d028c07d07b53927b5a67fb0c8c23495 100644 (file)
@@ -3,7 +3,7 @@
 struct Vararg_ {
   struct Vector_ t; // types
   m_bit *d; // d(ata)
-  m_uint o, i, s, l;   // o(ffset), i(ndex), s(ize), l(en)
+  m_uint /*o, i, s,*/ l;   // o(ffset), i(ndex), s(ize), l(en)
   m_uint pc;
 };
 ANN void emit_vararg_end(const Emitter emit, const m_uint pc);
index 840be081f0f68bbcd0910018d70cd44c53ac7134..f8f71c43ec97e49098c5d9a8fa68c0f920bb18ef 100644 (file)
@@ -122,8 +122,7 @@ CastI2F
 CastF2I
 Time_Advance
 SetCode
-RegPop
-RegPush
+RegMove
 Reg2Mem
 Reg2Mem4
 Overflow
index 2e26303f8dcaca042c3e0afea9025ed24a9a0090..d4d1a2c6d72268b8ab6f9a68ad1a49bca434d81b 100644 (file)
@@ -82,14 +82,20 @@ static const f_instr allocmember[]  = { RegPushImm, RegPushImm2, RegPushImm3, Al
 static const f_instr allocword[]  = { AllocWord, AllocWord2, AllocWord3, RegPushMem4 };
 static const f_instr structmember[]  = { StructMember, StructMemberFloat, StructMemberOther, StructMemberAddr };
 
+#define regmove(name, op) \
+ANN static inline Instr reg##name(const Emitter emit, const m_uint sz) { \
+  const Instr instr = emit_add_instr(emit, RegMove); \
+  instr->m_val = op sz; \
+  return instr; \
+}
+regmove(pop,-)
+regmove(push,)
 #define regxxx(name, instr) \
 ANN static inline Instr reg##name(const Emitter emit, const m_uint sz) { \
   const Instr instr = emit_add_instr(emit, Reg##instr); \
   instr->m_val = sz; \
   return instr; \
 }
-regxxx(pop, Pop)
-regxxx(push, Push)
 regxxx(pushi, PushImm)
 regxxx(seti, SetImm)
 
@@ -125,7 +131,7 @@ ANN static void emit_struct_ctor(const Emitter emit, const Type type, const m_ui
   regpush(emit, SZ_INT *2);
   const Instr prelude = emit_add_instr(emit, SetCode);
   prelude->m_val2 = 2;
-  prelude->m_val = SZ_INT*3;
+  prelude->m_val = -SZ_INT*3;
   const Instr next = emit_add_instr(emit, Overflow);
   next->m_val2 = code_offset;
   emit->code->frame->curr_offset -= SZ_INT;
@@ -322,7 +328,7 @@ ANN void emit_ext_ctor(const Emitter emit, const Type t) {
   regpush(emit, SZ_INT*2);
   const Instr prelude = emit_add_instr(emit, SetCode);
   prelude->m_val2 = 2;
-  prelude->m_val = SZ_INT;
+  prelude->m_val = -SZ_INT;
   emit_add_instr(emit, Reg2Mem);
   const Instr next = emit_add_instr(emit, Overflow);
   next->m_val2 = offset;
@@ -769,7 +775,7 @@ ANN static m_bool emit_exp_decl_non_static(const Emitter emit, const Exp_Decl *d
   if(!vflag(v, vflag_member)) {
     v->from->offset = emit_local(emit, type);
     exec = (f_instr*)(allocword);
-    if(GET_FLAG(decl->td, late)) { // ref or emit_var ?
+    if(GET_FLAG(v, late)) { // ref or emit_var ?
       const Instr clean = emit_add_instr(emit, MemSetImm);
       clean->m_val = v->from->offset;
     }
@@ -1148,7 +1154,7 @@ ANN static m_bool me_arg(MemoizeEmitter *me) {
 
 ANN static Instr emit_call(const Emitter emit, const Func f) {
   const Instr prelude = get_prelude(emit, f);
-  prelude->m_val = f->def->stack_depth;
+  prelude->m_val = -f->def->stack_depth;
   const m_uint member = vflag(f->value_ref, vflag_member) ? SZ_INT : 0;
   if(member) {
     const Instr instr = emit_add_instr(emit, Reg2Mem);
@@ -1621,9 +1627,14 @@ ANN static m_bool variadic_state(const Emitter emit, const Stmt_VarLoop stmt, co
 ANN static m_bool emit_stmt_varloop(const Emitter emit, const Stmt_VarLoop stmt) {
   CHECK_BB(variadic_state(emit, stmt, 1))
   CHECK_BB(emit_exp(emit, stmt->exp))
-  const Instr check = emit_add_instr(emit, VarargCheck);
+  const Instr s = emit_add_instr(emit, DotMember);
+  s->m_val = SZ_INT * 5;
+  const Instr nonnull = emit_add_instr(emit, Goto);
+  regpop(emit, SZ_INT);
+  const Instr check = emit_add_instr(emit, Goto);
   const Instr member = emit_add_instr(emit, DotMember4);
   member->m_val = SZ_INT*2;
+  nonnull->m_val = emit_code_size(emit);
   const Instr instr = emit_add_instr(emit, BranchEqInt);
   const m_uint pc = emit_code_size(emit);
   emit_stmt(emit, stmt->body, 1);
index 21da4fc61f9ff18bcd77971983a3ed6b5c88d106..e356907f06daf1a9d9512ea7096b72f84ad2411c 100644 (file)
@@ -191,8 +191,8 @@ static OP_CHECK(opck_array_sr) {
 }
 
 ANN static inline m_bool emit_array_shift(const Emitter emit, const f_instr exec) {
-  const Instr pop = emit_add_instr(emit, RegPop);
-  pop->m_val = SZ_INT;
+  const Instr pop = emit_add_instr(emit, RegMove);
+  pop->m_val = -SZ_INT;
   (void)emit_add_instr(emit, exec);
   return GW_OK;
 }
@@ -243,8 +243,8 @@ static OP_EMIT(opem_array_sr) {
   const Exp_Binary* bin = (Exp_Binary*)data;
   if(bin->rhs->type->array_depth == bin->lhs->type->array_depth)
     return emit_array_shift(emit, ArrayConcatRight);
-  const Instr pop = emit_add_instr(emit, RegPop);
-  pop->m_val = SZ_INT;
+  const Instr pop = emit_add_instr(emit, RegMove);
+  pop->m_val = -SZ_INT;
   emit_gc(emit, 0);
   (void)emit_add_instr(emit, ArrayAppendFront);
   return GW_OK;
@@ -254,8 +254,8 @@ static OP_EMIT(opem_array_sl) {
   const Exp_Binary* bin = (Exp_Binary*)data;
   if(bin->lhs->type->array_depth == bin->rhs->type->array_depth)
     return emit_array_shift(emit, ArrayConcatLeft);
-  const Instr pop = emit_add_instr(emit, RegPop);
-  pop->m_val = bin->rhs->type->size;
+  const Instr pop = emit_add_instr(emit, RegMove);
+  pop->m_val = -bin->rhs->type->size;
   emit_gc(emit, -SZ_INT);
   emit_add_instr(emit, ArrayAppend);
   return GW_OK;
@@ -332,8 +332,8 @@ static OP_CHECK(opck_array) {
 }
 
 ANN static void array_loop(const Emitter emit, const m_uint depth) {
-  const Instr pre_pop = emit_add_instr(emit, RegPop);
-  pre_pop->m_val = depth * SZ_INT;
+  const Instr pre_pop = emit_add_instr(emit, RegMove);
+  pre_pop->m_val = -depth * SZ_INT;
   for(m_uint i = 0; i < depth - 1; ++i) {
     const Instr access = emit_add_instr(emit, ArrayAccess);
     access->m_val = i * SZ_INT;
@@ -342,8 +342,8 @@ ANN static void array_loop(const Emitter emit, const m_uint depth) {
     get->m_val = i * SZ_INT;
     get->m_val2 = -SZ_INT;
   }
-  const Instr post_pop = emit_add_instr(emit, RegPop);
-  post_pop->m_val = SZ_INT;
+  const Instr post_pop = emit_add_instr(emit, RegMove);
+  post_pop->m_val = -SZ_INT;
   const Instr access = emit_add_instr(emit, ArrayAccess);
   access->m_val = depth * SZ_INT;
 }
@@ -352,7 +352,8 @@ ANN static void array_finish(const Emitter emit, const m_uint depth,
                const m_uint size, const m_bool is_var) {
   const Instr get = emit_add_instr(emit, is_var ? ArrayAddr : ArrayGet);
   get->m_val = depth * SZ_INT;
-  const Instr push = emit_add_instr(emit, ArrayValid);
+  emit_add_instr(emit, ArrayValid);
+  const Instr push = emit_add_instr(emit, RegMove);
   push->m_val = is_var ? SZ_INT : size;
 }
 
index 6e8937645d0588b366d88246d9b6e604c9a7b6cc..55ba5e94fa3f933859444c72e189f446657fdc8b 100644 (file)
@@ -34,8 +34,8 @@ static OP_EMIT(opem_func_assign) {
     fptr_instr(emit, bin->lhs->type->info->func, 2);
   (void)emit_add_instr(emit, int_r_assign);
   if(!is_fptr(emit->gwion, bin->lhs->type) && vflag(bin->rhs->type->info->func->value_ref, vflag_member)) {
-    const Instr pop = emit_add_instr(emit, RegPop);
-    pop->m_val = SZ_INT;
+    const Instr pop = emit_add_instr(emit, RegMove);
+    pop->m_val = -SZ_INT;
     const Instr cpy = emit_add_instr(emit, Reg2Reg);
     cpy->m_val = -SZ_INT;
   }
@@ -235,8 +235,8 @@ static OP_CHECK(opck_fptr_cast) {
 }
 
 static void member_fptr(const Emitter emit) {
-  const Instr instr = emit_add_instr(emit, RegPop);
-  instr->m_val = SZ_INT;
+  const Instr instr = emit_add_instr(emit, RegMove);
+  instr->m_val = -SZ_INT;
   const Instr dup = emit_add_instr(emit, Reg2Reg);
   dup->m_val = -SZ_INT;
 }
index 3f71c3887c307e151dd49758aa2787b54ab2fc75..3ad5a6b7d646f995219f70fd146d5f5acfa5514b 100644 (file)
@@ -136,7 +136,7 @@ ANN static inline void emit_struct_data(const Emitter emit, const Value v, const
   const Instr instr = emit_kind(emit, v->type->size, emit_addr, structmember);
   instr->m_val = v->from->offset;
   if(!emit_addr) {
-    const Instr instr = emit_add_instr(emit, RegPush);
+    const Instr instr = emit_add_instr(emit, RegMove);
     instr->m_val = v->type->size -SZ_INT;
   }
 }
@@ -294,9 +294,18 @@ ANN void struct_release(const VM_Shred shred, const Type base, const m_bit *ptr)
 }
 
 static OP_EMIT(opem_not_object) {
-  const Instr instr = (Instr)vector_back(&emit->code->instr);
-  if(instr->opcode == eGWOP_EXCEPT)
-    vector_pop(&emit->code->instr);
+  const Vector v = &emit->code->instr;
+  const Instr last = (Instr)vector_pop(v);
+  mp_free(emit->gwion->mp, Instr, last);
+  const Instr back = (Instr)vector_back(v);
+  if(back->opcode == eGWOP_EXCEPT) {
+    vector_pop(v);
+    mp_free(emit->gwion->mp, Instr, back);
+    emit_add_instr(emit, IntNot);
+    return GW_OK;
+  }
+  const Instr instr = emit_add_instr(emit, RegSetImm);
+  instr->m_val2 = -SZ_INT;
   return GW_OK;
 }
 
index 98e146b9002b551a57cca5134efc1e633eebeaeb..cbb1793e1a33a177ae131a6ece88bcff60960929 100644 (file)
@@ -56,8 +56,8 @@ static INSTR(instr_ptr_assign_obj) {
 }
 
 static OP_EMIT(opem_ptr_assign) {
-  const Instr pop = emit_add_instr(emit, RegPop);
-  pop->m_val = SZ_INT;
+  const Instr pop = emit_add_instr(emit, RegMove);
+  pop->m_val = -SZ_INT;
   const Exp_Binary* bin = (Exp_Binary*)data;
   if(isa(bin->lhs->type, emit->gwion->type[et_object]) > 0) {
     const Instr instr = emit_add_instr(emit, RegAddRefAddr);
index 5f68019df8458284022839521fd46f8f22c94e19..bf368cdcba28735c80c5e2a512909ee1b7e33bf3 100644 (file)
@@ -25,8 +25,8 @@ static OP_CHECK(opck_none) {
 }
 
 static OP_EMIT(opem_none) {
-  const Instr instr = emit_add_instr(emit, RegPop);
-  instr->m_val = SZ_INT;
+  const Instr instr = emit_add_instr(emit, RegMove);
+  instr->m_val = -SZ_INT;
   return GW_OK;
 }
 
index 35d3806128b2f0ebfcdca961bcd7c06a62cdc692..a3c87d57f79f795a9470e7ca5c27c9088cb318a4 100644 (file)
@@ -46,18 +46,18 @@ static MFUN(mfun_vararg_cpy) {
   arg->d = (m_bit*)xmalloc(round2szint(*(m_uint*)(o->data + SZ_INT*2)));
   m_uint offset = 0;
   for(m_uint i = 0; i < vector_size(&arg->t); ++i) {
-    const Type t = (Type)vector_at(&arg->t, arg->i);
+    const Type t = (Type)vector_at(&arg->t, *(m_uint*)(o->data + SZ_INT*4));
     *(m_uint*)(arg->d + offset) = *(m_uint*)(src->d + offset);
     if(isa(t, shred->info->vm->gwion->type[et_object]) > 0)
       ++(*(M_Object*)(arg->d + offset))->ref;
     offset += t->size;
   }
-  arg->s = vector_size(&arg->t);
-  arg->i = src->i;
-  arg->o = src->o;
   const M_Object obj = new_object(shred->info->mp, shred, o->type_ref);
   *(struct Vararg_**)obj->data = arg;
   *(m_uint*)(obj->data + SZ_INT*2) = *(m_uint*)(o->data + SZ_INT*2);
+  *(m_uint*)(obj->data + SZ_INT*3) = *(m_uint*)(o->data + SZ_INT*3);
+  *(m_uint*)(obj->data + SZ_INT*4) = *(m_uint*)(o->data + SZ_INT*4);
+  *(m_uint*)(obj->data + SZ_INT*4) = vector_size(&arg->t); // can we copy?
   *(M_Object*)RETURN = obj;
 }
 
@@ -81,28 +81,22 @@ INSTR(VarargIni) {
       }
       offset += t->size;
     }
-    arg->s = vector_size(kinds);
+    *(m_uint*)(o->data + SZ_INT * 5) = vector_size(kinds);
   }
   *(M_Object*)REG(-SZ_INT) = o;
 }
 
-INSTR(VarargCheck) {
-  const M_Object o = *(M_Object*)(shred->reg-SZ_INT);
-  struct Vararg_ *arg = *(struct Vararg_**)o->data;
-  if(arg->s)
-    return;
-  shred->reg -= SZ_INT;
-  shred->pc = instr->m_val;
-}
-
 static INSTR(VarargEnd) {
   const M_Object o = *(M_Object*)REG(0);
   struct Vararg_* arg = *(struct Vararg_**)o->data;
-  arg->o += arg->t.ptr ? ((Type)vector_at(&arg->t, arg->i))->size : 0;
-  if(++arg->i < arg->s)
-    shred->pc = instr->m_val;
-  else
-    arg->i = arg->o = 0;
+  *(m_uint*)(o->data + SZ_INT*3) += arg->t.ptr ? ((Type)vector_at(&arg->t, *(m_uint*)(o->data + SZ_INT*4)))->size : 0;
+  if(++*(m_uint*)(o->data + SZ_INT*4) == *(m_uint*)(o->data + SZ_INT * 5)) {
+//  if(++*(m_uint*)(o->data + SZ_INT*4) < *(m_uint*)(o->data + SZ_INT * 5))
+//    shred->pc = instr->m_val;
+//  else
+    *(m_uint*)(o->data + SZ_INT*4) = *(m_uint*)(o->data + SZ_INT*3) = 0;
+    ++shred->pc;
+  }
 }
 
 static OP_CHECK(opck_vararg_cast) {
@@ -116,12 +110,12 @@ static INSTR(VarargCast) {
          Except(shred, "Using Vararg outside varloop");
   struct Vararg_* arg = *(struct Vararg_**)o->data;
   const Type t = (Type)instr->m_val,
-             u = (Type)vector_at(&arg->t, arg->i);
+             u = (Type)vector_at(&arg->t, *(m_uint*)(o->data + SZ_INT*4));
   if(isa(u, t) > 0 ||
       (u == shred->info->vm->gwion->type[et_error] &&
        isa(t, shred->info->vm->gwion->type[et_object]) > 0)) {
     for(m_uint i = 0; i < t->size; i += SZ_INT)
-      *(m_uint*)REG(i - SZ_INT) = *(m_uint*)(arg->d + arg->o + i);
+      *(m_uint*)REG(i - SZ_INT) = *(m_uint*)(arg->d + *(m_uint*)(o->data + SZ_INT*3) + i);
   } else
          Except(shred, "InvalidVariadicAccess");
 }
@@ -130,7 +124,7 @@ static OP_EMIT(opem_vararg_cast) {
   const Exp_Cast* cast = (Exp_Cast*)data;
   const Instr instr = emit_add_instr(emit, VarargCast);
   instr->m_val = (m_uint)exp_self(cast)->type;
-  const Instr push = emit_add_instr(emit, RegPush);
+  const Instr push = emit_add_instr(emit, RegMove);
   push->m_val = exp_self(cast)->type->size - SZ_INT;
   return GW_OK;
 }
@@ -157,9 +151,10 @@ static GACK(gack_vararg) {
 }
 
 ANN void emit_vararg_end(const Emitter emit, const m_uint pc) {
-  const Instr pop = emit_add_instr(emit, RegPop);
-  pop->m_val = SZ_INT;
-  const Instr instr = emit_add_instr(emit, VarargEnd);
+  const Instr pop = emit_add_instr(emit, RegMove);
+  pop->m_val = -SZ_INT;
+  (void)emit_add_instr(emit, VarargEnd);
+  const Instr instr = emit_add_instr(emit, Goto);
   instr->m_val = pc;
 }
 
@@ -173,6 +168,12 @@ GWION_IMPORT(vararg) {
   GWI_BB(gwi_item_end(gwi, ae_flag_none, NULL))
   GWI_BB(gwi_item_ini(gwi, "int", "@len"))
   GWI_BB(gwi_item_end(gwi, ae_flag_none, NULL))
+  GWI_BB(gwi_item_ini(gwi, "int", "@o"))
+  GWI_BB(gwi_item_end(gwi, ae_flag_none, NULL))
+  GWI_BB(gwi_item_ini(gwi, "int", "@i"))
+  GWI_BB(gwi_item_end(gwi, ae_flag_none, NULL))
+  GWI_BB(gwi_item_ini(gwi, "int", "@s"))
+  GWI_BB(gwi_item_end(gwi, ae_flag_none, NULL))
   GWI_BB(gwi_func_ini(gwi, "Vararg", "cpy"))
   GWI_BB(gwi_func_end(gwi, mfun_vararg_cpy, ae_flag_none))
   GWI_BB(gwi_class_end(gwi))
index 3dd682e7d299ee0d1cfcf60db2e894ff68661b75..e12d49f8e0144a86b93880c9c1b9e9c678e43c17 100644 (file)
@@ -166,7 +166,7 @@ ANN static VM_Shred init_fork_shred(const VM_Shred shred, const VM_Code code, co
 
 #define ADISPATCH() { ADVANCE(); SDISPATCH(); }
 
-#define PC ((*(unsigned*)(byte + 1)) + 1)
+#define PC (*(unsigned*)(byte + 1))
 
 #define OP(t, sz, op, ...) \
   reg -= sz;\
@@ -318,7 +318,7 @@ ANN void vm_run(const VM* vm) { // lgtm [cpp/use-of-goto]
     &&itof, &&ftoi,
     &&timeadv,
     &&setcode,
-    &&regpop, &&regpush, &&regtomem, &&regtomemother, &&overflow, &&funcusrend, &&funcmemberend,
+    &&regmove, &&regtomem, &&regtomemother, &&overflow, &&funcusrend, &&funcmemberend,
     &&sporkini, &&forkini, &&sporkfunc, &&sporkmemberfptr, &&sporkexp, &&sporkend,
     &&brancheqint, &&branchneint, &&brancheqfloat, &&branchnefloat,
     &&arrayappend, &&autoloop, &&autoloopptr, &&autoloopcount, &&arraytop, &&arrayaccess, &&arrayget, &&arrayaddr, &&arrayvalid,
@@ -631,11 +631,8 @@ PRAGMA_PUSH()
     next = eFuncMemberEnd;
   }
 PRAGMA_POP()
-regpop:
-  reg -= VAL;
-  DISPATCH();
-regpush:
-  reg += VAL;
+regmove:
+  reg += (m_int)VAL;
   DISPATCH();
 regtomem:
   *(m_uint*)(mem+VAL) = *(m_uint*)(reg+(m_int)VAL2);
@@ -757,7 +754,7 @@ arrayvalid:
 // are we sure it is the array ?
 // rather increase ref
   vector_pop(&shred->gc);
-  goto regpush;
+  DISPATCH()
 newobj:
   *(M_Object*)reg = new_object(vm->gwion->mp, NULL, (Type)VAL2);
   reg += SZ_INT;
index 663f6c4ea5ecab1f6d740436634308d8b32f19d8..c5180e4fb4a7f87d79d461732e4f8b46ac19ca21 100644 (file)
@@ -30,7 +30,6 @@ ANN void free_vmcode(VM_Code a, Gwion gwion) {
   if(a->memoize)
     memoize_end(gwion->mp, a->memoize);
   if(!a->builtin) {
-//    _mp_free(gwion->mp, vector_size(a->instr) * SZ_INT, a->bytecode);
     _mp_free(gwion->mp, vector_size(a->instr) * BYTECODE_SZ, a->bytecode);
     _free_code_instr(a->instr, gwion);
   }
@@ -40,34 +39,95 @@ ANN void free_vmcode(VM_Code a, Gwion gwion) {
   mp_free(gwion->mp , VM_Code, a);
 }
 
+static inline uint isgoto(const unsigned opcode) {
+  return opcode == eGoto ||
+      opcode == eBranchEqInt || opcode == eBranchNeqInt ||
+      opcode == eBranchEqFloat || opcode == eBranchNeqFloat;
+}
+
+static inline void setpc(const m_bit *data, const m_uint i) {
+  *(unsigned*)(data+1) = i + 1;
+}
+
 ANN static m_bit* tobytecode(MemPool p, const VM_Code code) {
   const Vector v = code->instr;
   const m_uint sz = vector_size(v);
   m_bit *ptr = _mp_malloc(p, sz * BYTECODE_SZ);
+  struct Vector_ nop;
+  vector_init(&nop);
   for(m_uint i= 0; i < sz; ++i) {
+    m_bit *const data = ptr + i*BYTECODE_SZ;
     const Instr instr = (Instr)vector_at(v, i);
-    if(instr->opcode < eOP_MAX)
-      memcpy(ptr + i*BYTECODE_SZ, instr, BYTECODE_SZ);
-    else {
-      *(m_bit*)(ptr + (i*BYTECODE_SZ)) = instr->opcode;
-//      *(m_bit*)(ptr + (i*BYTECODE_SZ)) = eOP_MAX;
-      *(Instr*)(ptr + (i*BYTECODE_SZ) + SZ_INT) = instr;
-      *(f_instr*)(ptr + (i*BYTECODE_SZ) + SZ_INT*2) = instr->execute;
+    if(instr->opcode < eOP_MAX) {
+      if(instr->opcode == eRegMove) {
+        m_int move = (m_int)instr->m_val;
+        m_uint j = 0;
+        Instr next;
+        while((next = (Instr)vector_at(v, i + j +1)) && next->opcode == eRegMove) {
+          ++j; vector_add(&nop, i+j);
+          move += (m_int)next->m_val;
+          next->opcode = eNoOp;
+        }
+        if((instr->m_val = move)) {
+          memcpy(data, instr, BYTECODE_SZ);
+          setpc(data, i);
+        } else {
+          vector_add(&nop, i);
+          instr->opcode = eNoOp;
+        }
+        i += j;
+        continue;
+      }
+      if(instr->opcode != eNoOp)
+        memcpy(data, instr, BYTECODE_SZ);
+      else
+        vector_add(&nop, i);
+    } else {
+      *(m_bit*)(data) = instr->opcode;
+      *(Instr*)(data + SZ_INT) = instr;
+      *(f_instr*)(data + SZ_INT*2) = instr->execute;
     }
-    *(unsigned*)(ptr + (i*BYTECODE_SZ) + 1) = i;
+    setpc(data, i);
+  }
+  if(!vector_size(&nop)) {
+    vector_release(&nop);
+    return ptr;
   }
-  return ptr;
+  m_bit *const final = _mp_malloc(p, sz * BYTECODE_SZ); // could use smaller size
+  for(m_uint i= 0, j = 0; i < sz; ++i) {
+    const Instr instr = (Instr)vector_at(v, i);
+    unsigned opcode = instr->opcode;
+    if(opcode != eNoOp) {
+      m_bit *const base = ptr   + i*BYTECODE_SZ,
+            *const data = final + j*BYTECODE_SZ;
+//        if(!isgoto(opcode))
+      memcpy(data, base, BYTECODE_SZ);
+      if(isgoto(opcode)) {
+        m_bit pc = 0;
+        for(m_uint k = 0; k < vector_size(&nop); ++k) {
+          if(instr->m_val <= vector_at(&nop, k))
+            break;
+          ++pc;
+        }
+        *(m_uint*)(data + SZ_INT) = instr->m_val > pc ? instr->m_val - pc : 0;
+      }
+      setpc(data, j);
+      ++j;
+    }
+  }
+  vector_release(&nop);
+  mp_free2(p, sz*BYTECODE_SZ, ptr);
+  return final;
 }
 
-
 VM_Code new_vmcode(MemPool p, const Vector instr, const m_uint stack_depth,
-    const int builtin, const m_str name) {
+      const int builtin, const m_str name) {
   VM_Code code           = mp_calloc(p, VM_Code);
+  code->name             = mstrdup(p, name);
   if(instr) {
     code->instr            = vector_copy(p, instr);
     code->bytecode = tobytecode(p, code);
   }
-  code->name             = mstrdup(p, name);
   code->stack_depth      = stack_depth;
   code->builtin = builtin;
   code->ref = 1;