diff --git a/src/codegen.c b/src/codegen.c index c8f964f56d..d8c83b92c6 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -639,6 +639,11 @@ lambda_body(codegen_scope *s, node *tree, int blk) | ((ra & 1) << 5) | (pa & 0x1f); genop(s, MKOP_Ax(OP_ENTER, a)); + if (ra) { + size_t ppos = s->irep->plen; + s->irep->pool[ppos] = mrb_fixnum_value(0); + s->irep->plen++; + } pos = new_label(s); for (i=0; irlen; i++) { + if (mrb_check_export_reps(mrb, irep->reps[i], drno, level + 1)) { + return 1; + } + } + + for (i = 0, pc = irep->iseq; i < irep->ilen; i++, pc++) { + ins = *pc; + //disasm_once(mrb, irep, ins); + switch(GET_OPCODE(ins)) { + case OP_GETUPVAR: + case OP_SETUPVAR: + if (GETARG_B(ins) == drno && GETARG_C(ins) == level) { + return 1; + } + break; + + default: + break; + } + } + + return 0; +} + +int +mrb_patch_irep_var2fix(mrb_state *mrb, mrb_irep *irep, mrb_int drno) +{ + int i; + mrb_code *oiseq = irep->iseq; + mrb_code *pc; + mrb_code ins; + mrb_int tdrno = drno; + + for (i = 0; i < irep->rlen; i++) { + if (mrb_check_export_reps(mrb, irep->reps[i], drno, 0)) { + return 0; + } + } + + irep->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*irep->ilen); + for (i = 0, pc = oiseq; i < irep->ilen; i++, pc++) { + ins = *pc; + irep->iseq[i] = ins; + + if (GET_OPCODE(ins) == OP_ARRAY && + GET_OPCODE(*(pc + 1)) == OP_MOVE && + GETARG_B(*(pc + 1)) == drno && + GET_OPCODE(*(pc + 2)) == OP_ARYCAT && + GET_OPCODE(*(pc + 3)) == OP_SEND && + GETARG_C(*(pc + 3)) == 127) { + + if (GETARG_C(ins) == 0) { + irep->iseq[i++] = MKOP_A(OP_NOP, 0); + pc++; + ins = *pc; + irep->iseq[i++] = MKOP_AB(OP_MOVE, GETARG_A(ins) - 1, GETARG_B(ins)); + pc++; + irep->iseq[i++] = MKOP_A(OP_NOP, 0); + pc++; + ins = *pc; + irep->iseq[i] = MKOP_ABC(OP_SEND, GETARG_A(ins), GETARG_B(ins), 1); + } + else { + mrb_free(mrb, irep->iseq); + irep->iseq = oiseq; + return 0; + } + } + + if (GET_OPCODE(ins) == OP_MOVE) { + if (GETARG_B(ins) == drno) { + tdrno = GETARG_A(ins); + } + else if (GETARG_A(ins) == drno) { + mrb_free(mrb, irep->iseq); + irep->iseq = oiseq; + return 0; + } + } + + if (GET_OPCODE(ins) == OP_RETURN) { + if ( GETARG_A(ins) == tdrno + || GETARG_A(ins) == drno) { + mrb_free(mrb, irep->iseq); + irep->iseq = oiseq; + return 0; + } + } + + if (GET_OPCODE(ins) == OP_SEND && + (GETARG_A(ins) == tdrno || + (GETARG_C(ins) == 1 && GETARG_A(ins) + 1 == tdrno))) { + if (GETARG_C(ins) == 127) { + irep->iseq[i] = MKOP_ABC(OP_SEND, GETARG_A(ins), GETARG_B(ins), 1); + } + else if (strcmp(mrb_sym2name(mrb, irep->syms[GETARG_B(ins)]), "__svalue") == 0) { + irep->iseq[i] = MKOP_A(OP_NOP, 0); + } + else if (strcmp(mrb_sym2name(mrb, irep->syms[GETARG_B(ins)]), "size") == 0) { + irep->iseq[i] = MKOP_AB(OP_LOADI, tdrno, 1); + } + else if (strcmp(mrb_sym2name(mrb, irep->syms[GETARG_B(ins)]), "[]") == 0) { + irep->iseq[i] = MKOP_A(OP_NOP, 0); + } + else { + mrb_free(mrb, irep->iseq); + irep->iseq = oiseq; + return 0; + } + } + else { + switch (GET_OPCODE(ins)) { + case OP_SEND: + case OP_ARRAY: + case OP_HASH: + case OP_ADD: + case OP_SUB: + if (GETARG_A(ins) <= tdrno && + tdrno <= GETARG_A(ins) + GETARG_C(ins)) { + mrb_free(mrb, irep->iseq); + irep->iseq = oiseq; + return 0; + } + break; + } + } + } + + return 1; +} + MRB_API mrb_value mrb_context_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) { @@ -1418,7 +1556,43 @@ mrb_context_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int } if (r) { rnum = argc-m1-o-m2; - regs[m1+o+1] = mrb_ary_new_from_values(mrb, rnum, argv+m1+o); + if (rnum == 1) { + int ipos = 0; + mrb_irep *nirep = (mrb_irep *)mrb_fixnum(irep->pool[ipos]); + struct RProc *p; + + if (nirep == NULL) { + mrb_irep *cirep = mrb_add_irep(mrb); + *cirep = *irep; + if (mrb_patch_irep_var2fix(mrb, cirep, m1 + o + 1)) { + p = mrb_proc_new(mrb, cirep); + p->flags = proc->flags; + p->body.irep->refcnt++; + p->target_class = proc->target_class; + p->env = proc->env; + irep->pool[ipos] = mrb_fixnum_value((mrb_int)cirep); + mrb->c->ci->proc = proc = p; + irep = cirep; + pc = cirep->iseq; + } + else { + regs[m1+o+1] = mrb_ary_new_from_values(mrb, rnum, argv+m1+o); + } + } + else { + p = mrb_proc_new(mrb, nirep); + p->flags = proc->flags; + p->body.irep->refcnt++; + p->target_class = proc->target_class; + p->env = proc->env; + mrb->c->ci->proc = proc = p; + irep = nirep; + pc = nirep->iseq; + } + } + else { + regs[m1+o+1] = mrb_ary_new_from_values(mrb, rnum, argv+m1+o); + } } if (m2) { if (argc-m2 > m1) {