mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
75466f9aab
As a side effect, memory allocation failure may cause segmentation fault. Maybe we have to add error detection later.
4235 lines
95 KiB
C
4235 lines
95 KiB
C
/*
|
|
** codegen.c - mruby code generator
|
|
**
|
|
** See Copyright Notice in mruby.h
|
|
*/
|
|
|
|
#include <mruby.h>
|
|
#include <mruby/compile.h>
|
|
#include <mruby/proc.h>
|
|
#include <mruby/dump.h>
|
|
#include <mruby/numeric.h>
|
|
#include <mruby/string.h>
|
|
#include <mruby/debug.h>
|
|
#include <mruby/presym.h>
|
|
#include "node.h"
|
|
#include <mruby/opcode.h>
|
|
#include <mruby/re.h>
|
|
#include <mruby/throw.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <mruby/internal.h>
|
|
|
|
#define mrbc_malloc(s) mrb_basic_alloc_func(NULL,(s))
|
|
#define mrbc_realloc(p,s) mrb_basic_alloc_func((p),(s))
|
|
#define mrbc_free(p) mrb_basic_alloc_func((p),0)
|
|
|
|
#ifndef MRB_CODEGEN_LEVEL_MAX
|
|
#define MRB_CODEGEN_LEVEL_MAX 256
|
|
#endif
|
|
|
|
#define MAXARG_S (1<<16)
|
|
|
|
typedef mrb_ast_node node;
|
|
typedef struct mrb_parser_state parser_state;
|
|
|
|
enum looptype {
|
|
LOOP_NORMAL,
|
|
LOOP_BLOCK,
|
|
LOOP_FOR,
|
|
LOOP_BEGIN,
|
|
LOOP_RESCUE,
|
|
};
|
|
|
|
struct loopinfo {
|
|
enum looptype type;
|
|
uint32_t pc0; /* `next` destination */
|
|
uint32_t pc1; /* `redo` destination */
|
|
uint32_t pc2; /* `break` destination */
|
|
int reg; /* destination register */
|
|
struct loopinfo *prev;
|
|
};
|
|
|
|
typedef struct scope {
|
|
mrb_state *mrb;
|
|
mempool *mpool;
|
|
|
|
struct scope *prev;
|
|
|
|
node *lv;
|
|
|
|
uint16_t sp;
|
|
uint32_t pc;
|
|
uint32_t lastpc;
|
|
uint32_t lastlabel;
|
|
uint16_t ainfo:15;
|
|
mrb_bool mscope:1;
|
|
|
|
struct loopinfo *loop;
|
|
mrb_sym filename_sym;
|
|
uint16_t lineno;
|
|
|
|
mrb_code *iseq;
|
|
uint16_t *lines;
|
|
uint32_t icapa;
|
|
|
|
mrb_irep *irep;
|
|
mrb_irep_pool *pool;
|
|
mrb_sym *syms;
|
|
mrb_irep **reps;
|
|
struct mrb_irep_catch_handler *catch_table;
|
|
uint32_t pcapa, scapa, rcapa;
|
|
|
|
uint16_t nlocals;
|
|
uint16_t nregs;
|
|
int ai;
|
|
|
|
int debug_start_pos;
|
|
uint16_t filename_index;
|
|
parser_state* parser;
|
|
|
|
int rlev; /* recursion levels */
|
|
} codegen_scope;
|
|
|
|
static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv);
|
|
static void scope_finish(codegen_scope *s);
|
|
static struct loopinfo *loop_push(codegen_scope *s, enum looptype t);
|
|
static void loop_break(codegen_scope *s, node *tree);
|
|
static void loop_pop(codegen_scope *s, int val);
|
|
|
|
/*
|
|
* The search for catch handlers starts at the end of the table in mrb_vm_run().
|
|
* Therefore, the next handler to be added must meet one of the following conditions.
|
|
* - Larger start position
|
|
* - Same start position but smaller end position
|
|
*/
|
|
static int catch_handler_new(codegen_scope *s);
|
|
static void catch_handler_set(codegen_scope *s, int ent, enum mrb_catch_type type, uint32_t begin, uint32_t end, uint32_t target);
|
|
|
|
static void gen_assignment(codegen_scope *s, node *tree, node *rhs, int sp, int val);
|
|
static void gen_massignment(codegen_scope *s, node *tree, int sp, int val);
|
|
|
|
static void codegen(codegen_scope *s, node *tree, int val);
|
|
static void raise_error(codegen_scope *s, const char *msg);
|
|
|
|
static void
|
|
codegen_error(codegen_scope *s, const char *message)
|
|
{
|
|
if (!s) return;
|
|
#ifndef MRB_NO_STDIO
|
|
if (s->filename_sym && s->lineno) {
|
|
const char *filename = mrb_sym_name_len(s->mrb, s->filename_sym, NULL);
|
|
fprintf(stderr, "%s:%d: %s\n", filename, s->lineno, message);
|
|
}
|
|
else {
|
|
fprintf(stderr, "%s\n", message);
|
|
}
|
|
#endif
|
|
while (s->prev) {
|
|
codegen_scope *tmp = s->prev;
|
|
if (s->irep) {
|
|
mrb_free(s->mrb, s->iseq);
|
|
for (int i=0; i<s->irep->plen; i++) {
|
|
mrb_irep_pool *p = &s->pool[i];
|
|
if ((p->tt & 0x3) == IREP_TT_STR || p->tt == IREP_TT_BIGINT) {
|
|
mrb_free(s->mrb, (void*)p->u.str);
|
|
}
|
|
}
|
|
mrb_free(s->mrb, s->pool);
|
|
mrb_free(s->mrb, s->syms);
|
|
mrb_free(s->mrb, s->catch_table);
|
|
if (s->reps) {
|
|
/* copied from mrb_irep_free() in state.c */
|
|
for (int i=0; i<s->irep->rlen; i++) {
|
|
if (s->reps[i])
|
|
mrb_irep_decref(s->mrb, (mrb_irep*)s->reps[i]);
|
|
}
|
|
mrb_free(s->mrb, s->reps);
|
|
}
|
|
mrb_free(s->mrb, s->lines);
|
|
}
|
|
mempool_close(s->mpool);
|
|
s = tmp;
|
|
}
|
|
MRB_THROW(s->mrb->jmp);
|
|
}
|
|
|
|
static void*
|
|
codegen_palloc(codegen_scope *s, size_t len)
|
|
{
|
|
void *p = mempool_alloc(s->mpool, len);
|
|
|
|
if (!p) codegen_error(s, "pool memory allocation");
|
|
return p;
|
|
}
|
|
|
|
static void
|
|
check_no_ext_ops(codegen_scope *s, uint16_t a, uint16_t b)
|
|
{
|
|
if (s->parser->no_ext_ops && (a | b) > 0xff) {
|
|
codegen_error(s, "need OP_EXTs instruction (currently OP_EXTs are prohibited)");
|
|
}
|
|
}
|
|
|
|
static int
|
|
new_label(codegen_scope *s)
|
|
{
|
|
return s->lastlabel = s->pc;
|
|
}
|
|
|
|
static void
|
|
emit_B(codegen_scope *s, uint32_t pc, uint8_t i)
|
|
{
|
|
if (pc >= s->icapa) {
|
|
if (pc == UINT32_MAX) {
|
|
codegen_error(s, "too big code block");
|
|
}
|
|
if (pc >= UINT32_MAX / 2) {
|
|
pc = UINT32_MAX;
|
|
}
|
|
else {
|
|
s->icapa *= 2;
|
|
}
|
|
s->iseq = (mrb_code*)mrbc_realloc(s->iseq, sizeof(mrb_code)*s->icapa);
|
|
if (s->lines) {
|
|
s->lines = (uint16_t*)mrbc_realloc(s->lines, sizeof(uint16_t)*s->icapa);
|
|
}
|
|
}
|
|
if (s->lines) {
|
|
if (s->lineno > 0 || pc == 0)
|
|
s->lines[pc] = s->lineno;
|
|
else
|
|
s->lines[pc] = s->lines[pc-1];
|
|
}
|
|
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;
|
|
check_no_ext_ops(s, a, 0);
|
|
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;
|
|
check_no_ext_ops(s, a, b);
|
|
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);
|
|
}
|
|
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, uint16_t c)
|
|
{
|
|
genop_2(s, i, a, b);
|
|
gen_B(s, (uint8_t)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_2SS(codegen_scope *s, mrb_code i, uint16_t a, uint32_t b)
|
|
{
|
|
genop_1(s, i, a);
|
|
gen_S(s, b>>16);
|
|
gen_S(s, b&0xffff);
|
|
}
|
|
|
|
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
|
|
no_optimize(codegen_scope *s)
|
|
{
|
|
if (s && s->parser && s->parser->no_optimize)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
struct mrb_insn_data
|
|
mrb_decode_insn(const mrb_code *pc)
|
|
{
|
|
struct mrb_insn_data data = { 0 };
|
|
if (pc == 0) return data;
|
|
data.addr = pc;
|
|
mrb_code insn = READ_B();
|
|
uint16_t a = 0;
|
|
uint16_t b = 0;
|
|
uint16_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;
|
|
}
|
|
|
|
#undef OPCODE
|
|
#define Z 1
|
|
#define S 3
|
|
#define W 4
|
|
#define OPCODE(_,x) x,
|
|
/* instruction sizes */
|
|
static uint8_t mrb_insn_size[] = {
|
|
#define B 2
|
|
#define BB 3
|
|
#define BBB 4
|
|
#define BS 4
|
|
#define BSS 6
|
|
#include <mruby/ops.h>
|
|
#undef B
|
|
#undef BB
|
|
#undef BBB
|
|
#undef BS
|
|
#undef BSS
|
|
};
|
|
/* EXT1 instruction sizes */
|
|
static uint8_t mrb_insn_size1[] = {
|
|
#define B 3
|
|
#define BB 4
|
|
#define BBB 5
|
|
#define BS 5
|
|
#define BSS 7
|
|
#include <mruby/ops.h>
|
|
#undef B
|
|
#undef BS
|
|
#undef BSS
|
|
};
|
|
/* EXT2 instruction sizes */
|
|
static uint8_t mrb_insn_size2[] = {
|
|
#define B 2
|
|
#define BS 4
|
|
#define BSS 6
|
|
#include <mruby/ops.h>
|
|
#undef B
|
|
#undef BB
|
|
#undef BBB
|
|
#undef BS
|
|
#undef BSS
|
|
};
|
|
/* EXT3 instruction sizes */
|
|
#define B 3
|
|
#define BB 5
|
|
#define BBB 6
|
|
#define BS 5
|
|
#define BSS 7
|
|
static uint8_t mrb_insn_size3[] = {
|
|
#include <mruby/ops.h>
|
|
};
|
|
#undef B
|
|
#undef BB
|
|
#undef BBB
|
|
#undef BS
|
|
#undef BSS
|
|
#undef OPCODE
|
|
|
|
static const mrb_code*
|
|
mrb_prev_pc(codegen_scope *s, const mrb_code *pc)
|
|
{
|
|
const mrb_code *prev_pc = NULL;
|
|
const mrb_code *i = s->iseq;
|
|
|
|
mrb_assert(pc < s->iseq + s->icapa);
|
|
while (i<pc) {
|
|
prev_pc = i;
|
|
switch (i[0]) {
|
|
case OP_EXT1:
|
|
i += mrb_insn_size1[i[1]] + 1;
|
|
break;
|
|
case OP_EXT2:
|
|
i += mrb_insn_size2[i[1]] + 1;
|
|
break;
|
|
case OP_EXT3:
|
|
i += mrb_insn_size3[i[1]] + 1;
|
|
break;
|
|
default:
|
|
i += mrb_insn_size[i[0]];
|
|
break;
|
|
}
|
|
}
|
|
return prev_pc;
|
|
}
|
|
|
|
#define pc_addr(s) &((s)->iseq[(s)->pc])
|
|
#define addr_pc(s, addr) (uint32_t)((addr) - s->iseq)
|
|
#define rewind_pc(s) s->pc = s->lastpc
|
|
|
|
static struct mrb_insn_data
|
|
mrb_last_insn(codegen_scope *s)
|
|
{
|
|
if (s->pc == 0) {
|
|
struct mrb_insn_data data = { OP_NOP, 0 };
|
|
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;
|
|
}
|
|
|
|
#define JMPLINK_START UINT32_MAX
|
|
|
|
static void
|
|
gen_jmpdst(codegen_scope *s, uint32_t pc)
|
|
{
|
|
|
|
if (pc == JMPLINK_START) {
|
|
pc = 0;
|
|
}
|
|
uint32_t pos2 = s->pc+2;
|
|
int32_t off = pc - pos2;
|
|
|
|
if (off > INT16_MAX || INT16_MIN > off) {
|
|
codegen_error(s, "too big jump offset");
|
|
}
|
|
gen_S(s, (uint16_t)off);
|
|
}
|
|
|
|
static uint32_t
|
|
genjmp(codegen_scope *s, mrb_code i, uint32_t pc)
|
|
{
|
|
uint32_t pos;
|
|
|
|
genop_0(s, i);
|
|
pos = s->pc;
|
|
gen_jmpdst(s, pc);
|
|
return pos;
|
|
}
|
|
|
|
#define genjmp_0(s,i) genjmp(s,i,JMPLINK_START)
|
|
|
|
static uint32_t
|
|
genjmp2(codegen_scope *s, mrb_code i, uint16_t a, uint32_t pc, int val)
|
|
{
|
|
uint32_t pos;
|
|
|
|
if (!no_peephole(s) && !val) {
|
|
struct mrb_insn_data data = mrb_last_insn(s);
|
|
|
|
switch (data.insn) {
|
|
case OP_MOVE:
|
|
if (data.a == a && data.a > s->nlocals) {
|
|
rewind_pc(s);
|
|
a = data.b;
|
|
}
|
|
break;
|
|
case OP_LOADNIL:
|
|
case OP_LOADF:
|
|
if (data.a == a || data.a > s->nlocals) {
|
|
s->pc = addr_pc(s, data.addr);
|
|
if (i == OP_JMPNOT || (i == OP_JMPNIL && data.insn == OP_LOADNIL)) {
|
|
return genjmp(s, OP_JMP, pc);
|
|
}
|
|
else { /* OP_JMPIF */
|
|
return JMPLINK_START;
|
|
}
|
|
}
|
|
break;
|
|
case OP_LOADT: case OP_LOADI8: case OP_LOADINEG: 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 (data.a == a || data.a > s->nlocals) {
|
|
s->pc = addr_pc(s, data.addr);
|
|
if (i == OP_JMPIF) {
|
|
return genjmp(s, OP_JMP, pc);
|
|
}
|
|
else { /* OP_JMPNOT and OP_JMPNIL */
|
|
return JMPLINK_START;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (a > 0xff) {
|
|
check_no_ext_ops(s, a, 0);
|
|
gen_B(s, OP_EXT1);
|
|
genop_0(s, i);
|
|
gen_S(s, a);
|
|
}
|
|
else {
|
|
genop_0(s, i);
|
|
gen_B(s, (uint8_t)a);
|
|
}
|
|
pos = s->pc;
|
|
gen_jmpdst(s, pc);
|
|
return pos;
|
|
}
|
|
|
|
#define genjmp2_0(s,i,a,val) genjmp2(s,i,a,JMPLINK_START,val)
|
|
|
|
static mrb_bool get_int_operand(codegen_scope *s, struct mrb_insn_data *data, mrb_int *ns);
|
|
static void gen_int(codegen_scope *s, uint16_t dst, mrb_int i);
|
|
|
|
static void
|
|
gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep)
|
|
{
|
|
if (nopeep || no_peephole(s)) goto normal;
|
|
else if (dst == src) 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.a == src) {
|
|
if (data.b == dst) /* skip swapping MOVE */
|
|
return;
|
|
if (data.a < s->nlocals) goto normal;
|
|
rewind_pc(s);
|
|
s->lastpc = addr_pc(s, mrb_prev_pc(s, data.addr));
|
|
gen_move(s, dst, data.b, FALSE);
|
|
return;
|
|
}
|
|
if (dst == data.a) { /* skip overwritten move */
|
|
rewind_pc(s);
|
|
s->lastpc = addr_pc(s, mrb_prev_pc(s, data.addr));
|
|
gen_move(s, dst, src, FALSE);
|
|
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 (data.a != src || data.a < s->nlocals) goto normal;
|
|
rewind_pc(s);
|
|
genop_1(s, data.insn, dst);
|
|
return;
|
|
case OP_HASH:
|
|
if (data.b != 0) goto normal;
|
|
/* fall through */
|
|
case OP_LOADI8: 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 (data.a != src || data.a < s->nlocals) goto normal;
|
|
rewind_pc(s);
|
|
genop_2(s, data.insn, dst, data.b);
|
|
return;
|
|
case OP_LOADI16:
|
|
if (data.a != src || data.a < s->nlocals) goto normal;
|
|
rewind_pc(s);
|
|
genop_2S(s, data.insn, dst, data.b);
|
|
return;
|
|
case OP_LOADI32:
|
|
if (data.a != src || data.a < s->nlocals) goto normal;
|
|
else {
|
|
uint32_t i = (uint32_t)data.b<<16|data.c;
|
|
rewind_pc(s);
|
|
genop_2SS(s, data.insn, dst, i);
|
|
}
|
|
return;
|
|
case OP_ARRAY:
|
|
if (data.a != src || data.a < s->nlocals || data.a < dst) goto normal;
|
|
rewind_pc(s);
|
|
if (data.b == 0 || dst == data.a)
|
|
genop_2(s, OP_ARRAY, dst, 0);
|
|
else
|
|
genop_3(s, OP_ARRAY2, dst, data.a, data.b);
|
|
return;
|
|
case OP_ARRAY2:
|
|
if (data.a != src || data.a < s->nlocals || data.a < dst) goto normal;
|
|
rewind_pc(s);
|
|
genop_3(s, OP_ARRAY2, dst, data.b, data.c);
|
|
return;
|
|
case OP_AREF:
|
|
case OP_GETUPVAR:
|
|
if (data.a != src || data.a < s->nlocals) goto normal;
|
|
rewind_pc(s);
|
|
genop_3(s, data.insn, dst, data.b, data.c);
|
|
return;
|
|
case OP_ADDI: case OP_SUBI:
|
|
if (addr_pc(s, data.addr) == s->lastlabel || data.a != src || data.a < s->nlocals) goto normal;
|
|
else {
|
|
struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr));
|
|
if (data0.insn != OP_MOVE || data0.a != data.a || data0.b != dst) goto normal;
|
|
s->pc = addr_pc(s, data0.addr);
|
|
if (addr_pc(s, data0.addr) != s->lastlabel) {
|
|
/* constant folding */
|
|
data0 = mrb_decode_insn(mrb_prev_pc(s, data0.addr));
|
|
mrb_int n;
|
|
if (data0.a == dst && get_int_operand(s, &data0, &n)) {
|
|
if ((data.insn == OP_ADDI && !mrb_int_add_overflow(n, data.b, &n)) ||
|
|
(data.insn == OP_SUBI && !mrb_int_sub_overflow(n, data.b, &n))) {
|
|
s->pc = addr_pc(s, data0.addr);
|
|
gen_int(s, dst, n);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
genop_2(s, data.insn, dst, data.b);
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
normal:
|
|
genop_2(s, OP_MOVE, dst, src);
|
|
return;
|
|
}
|
|
|
|
static int search_upvar(codegen_scope *s, mrb_sym id, int *idx);
|
|
|
|
static void
|
|
gen_getupvar(codegen_scope *s, uint16_t dst, mrb_sym id)
|
|
{
|
|
int idx;
|
|
int lv = search_upvar(s, id, &idx);
|
|
|
|
if (!no_peephole(s)) {
|
|
struct mrb_insn_data data = mrb_last_insn(s);
|
|
if (data.insn == OP_SETUPVAR && data.a == dst && data.b == idx && data.c == lv) {
|
|
/* skip GETUPVAR right after SETUPVAR */
|
|
return;
|
|
}
|
|
}
|
|
genop_3(s, OP_GETUPVAR, dst, idx, lv);
|
|
}
|
|
|
|
static void
|
|
gen_setupvar(codegen_scope *s, uint16_t dst, mrb_sym id)
|
|
{
|
|
int idx;
|
|
int lv = search_upvar(s, id, &idx);
|
|
|
|
if (!no_peephole(s)) {
|
|
struct mrb_insn_data data = mrb_last_insn(s);
|
|
if (data.insn == OP_MOVE && data.a == dst) {
|
|
dst = data.b;
|
|
rewind_pc(s);
|
|
}
|
|
}
|
|
genop_3(s, OP_SETUPVAR, dst, idx, lv);
|
|
}
|
|
|
|
static void
|
|
gen_return(codegen_scope *s, uint8_t op, uint16_t src)
|
|
{
|
|
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) {
|
|
rewind_pc(s);
|
|
genop_1(s, op, data.b);
|
|
}
|
|
else if (data.insn != OP_RETURN) {
|
|
genop_1(s, op, src);
|
|
}
|
|
}
|
|
}
|
|
|
|
static mrb_bool
|
|
get_int_operand(codegen_scope *s, struct mrb_insn_data *data, mrb_int *n)
|
|
{
|
|
switch (data->insn) {
|
|
case OP_LOADI__1:
|
|
*n = -1;
|
|
return TRUE;
|
|
|
|
case OP_LOADINEG:
|
|
*n = -data->b;
|
|
return TRUE;
|
|
|
|
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:
|
|
*n = data->insn - OP_LOADI_0;
|
|
return TRUE;
|
|
|
|
case OP_LOADI8:
|
|
case OP_LOADI16:
|
|
*n = (int16_t)data->b;
|
|
return TRUE;
|
|
|
|
case OP_LOADI32:
|
|
*n = (int32_t)((uint32_t)data->b<<16)+data->c;
|
|
return TRUE;
|
|
|
|
case OP_LOADL:
|
|
{
|
|
mrb_irep_pool *p = &s->pool[data->b];
|
|
|
|
if (p->tt == IREP_TT_INT32) {
|
|
*n = (mrb_int)p->u.i32;
|
|
}
|
|
#ifdef MRB_INT64
|
|
else if (p->tt == IREP_TT_INT64) {
|
|
*n = (mrb_int)p->u.i64;
|
|
}
|
|
#endif
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static int new_lit_str2(codegen_scope *s, const char *str1, mrb_int len1, const char *str2, mrb_int len2);
|
|
static int find_pool_str(codegen_scope *s, const char *str1, mrb_int len1, const char *str2, mrb_int len2);
|
|
|
|
static void
|
|
realloc_pool_str(codegen_scope *s, mrb_irep_pool *p, mrb_int len)
|
|
{
|
|
char *str;
|
|
if ((p->tt & 3) == IREP_TT_SSTR) {
|
|
str = (char*)mrbc_malloc(len+1);
|
|
}
|
|
else {
|
|
str = (char*)p->u.str;
|
|
str = (char*)mrbc_realloc(str, len+1);
|
|
}
|
|
p->tt = (uint32_t)(len<<2 | IREP_TT_STR);
|
|
str[len] = '\0';
|
|
p->u.str = (const char*)str;
|
|
}
|
|
|
|
static void
|
|
free_pool_str(codegen_scope *s, mrb_irep_pool *p)
|
|
{
|
|
if ((p->tt & 3) != IREP_TT_SSTR) {
|
|
mrbc_free((char*)p->u.str);
|
|
}
|
|
p->u.str = NULL;
|
|
s->irep->plen--;
|
|
}
|
|
|
|
static void
|
|
merge_op_string(codegen_scope *s, uint16_t dst, uint16_t b1, uint16_t b2, const mrb_code *pc)
|
|
{
|
|
int used = 0;
|
|
const mrb_code *i = s->iseq;
|
|
|
|
/* scan OP_STRING that refers b1 or b2 */
|
|
mrb_assert(pc < s->iseq + s->icapa);
|
|
while (i<pc) {
|
|
struct mrb_insn_data data = mrb_decode_insn(i);
|
|
if (data.insn == OP_STRING) {
|
|
if (data.b == b1) used |= 1;
|
|
if (data.b == b2) used |= 2;
|
|
}
|
|
switch (i[0]) {
|
|
case OP_EXT1:
|
|
i += mrb_insn_size1[i[1]] + 1;
|
|
break;
|
|
case OP_EXT2:
|
|
i += mrb_insn_size2[i[1]] + 1;
|
|
break;
|
|
case OP_EXT3:
|
|
i += mrb_insn_size3[i[1]] + 1;
|
|
break;
|
|
default:
|
|
i += mrb_insn_size[i[0]];
|
|
break;
|
|
}
|
|
}
|
|
|
|
mrb_irep_pool *p1 = &s->pool[b1];
|
|
mrb_irep_pool *p2 = &s->pool[b2];
|
|
mrb_int len1 = p1->tt>>2;
|
|
mrb_int len2 = p2->tt>>2;
|
|
int off = find_pool_str(s, p1->u.str, len1, p2->u.str, len2);
|
|
|
|
if (off < 0) {
|
|
switch (used) {
|
|
case 0: /* both pools are free */
|
|
case 2: /* b2 is referenced */
|
|
/* overwrite p1; free b2 if possible */
|
|
off = b1;
|
|
realloc_pool_str(s, p1, len1+len2);
|
|
memcpy((void*)(p1->u.str+len1), (void*)p2->u.str, len2);
|
|
if (b1 != b2 && used == 0 && b2+1 == s->irep->plen) {
|
|
free_pool_str(s, p2);
|
|
}
|
|
break;
|
|
case 1: /* b1 is referenced */
|
|
/* overwrite p2 */
|
|
off = b2;
|
|
realloc_pool_str(s, p2, len1+len2);
|
|
memmove((void*)(p2->u.str+len1), (void*)p2->u.str, len2);
|
|
memcpy((void*)p2->u.str, p1->u.str, len1);
|
|
break;
|
|
case 3: /* both b1&b2 are referenced */
|
|
/* create new pool */
|
|
off = new_lit_str2(s, p1->u.str, len1, p2->u.str, len2);
|
|
break;
|
|
}
|
|
}
|
|
s->pc = addr_pc(s, pc);
|
|
genop_2(s, OP_STRING, dst, off);
|
|
}
|
|
|
|
static void
|
|
gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst)
|
|
{
|
|
if (no_peephole(s)) {
|
|
normal:
|
|
genop_1(s, op, dst);
|
|
return;
|
|
}
|
|
else {
|
|
struct mrb_insn_data data = mrb_last_insn(s);
|
|
mrb_int n;
|
|
|
|
if (!get_int_operand(s, &data, &n)) {
|
|
/* not integer immediate */
|
|
if (op == OP_ADD && data.insn == OP_STRING) {
|
|
struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr));
|
|
if (data0.insn == OP_STRING) {
|
|
merge_op_string(s, dst, data0.b, data.b, data0.addr);
|
|
return;
|
|
}
|
|
}
|
|
goto normal;
|
|
}
|
|
struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr));
|
|
mrb_int n0;
|
|
if (addr_pc(s, data.addr) == s->lastlabel || !get_int_operand(s, &data0, &n0)) {
|
|
/* OP_ADDI/OP_SUBI takes upto 8bits */
|
|
if (n > UINT8_MAX || n < -UINT8_MAX) goto normal;
|
|
rewind_pc(s);
|
|
if (n == 0) return;
|
|
if (n > 0) {
|
|
if (op == OP_ADD) genop_2(s, OP_ADDI, dst, (uint16_t)n);
|
|
else genop_2(s, OP_SUBI, dst, (uint16_t)n);
|
|
}
|
|
else { /* n < 0 */
|
|
n = -n;
|
|
if (op == OP_ADD) genop_2(s, OP_SUBI, dst, (uint16_t)n);
|
|
else genop_2(s, OP_ADDI, dst, (uint16_t)n);
|
|
}
|
|
return;
|
|
}
|
|
if (op == OP_ADD) {
|
|
if (mrb_int_add_overflow(n0, n, &n)) goto normal;
|
|
}
|
|
else { /* OP_SUB */
|
|
if (mrb_int_sub_overflow(n0, n, &n)) goto normal;
|
|
}
|
|
s->pc = addr_pc(s, data0.addr);
|
|
gen_int(s, dst, n);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gen_muldiv(codegen_scope *s, uint8_t op, uint16_t dst)
|
|
{
|
|
if (no_peephole(s)) {
|
|
normal:
|
|
genop_1(s, op, dst);
|
|
return;
|
|
}
|
|
else {
|
|
struct mrb_insn_data data = mrb_last_insn(s);
|
|
mrb_int n, n0;
|
|
if (addr_pc(s, data.addr) == s->lastlabel || !get_int_operand(s, &data, &n)) {
|
|
/* not integer immediate */
|
|
goto normal;
|
|
}
|
|
struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr));
|
|
if (!get_int_operand(s, &data0, &n0)) {
|
|
goto normal;
|
|
}
|
|
if (op == OP_MUL) {
|
|
if (mrb_int_mul_overflow(n0, n, &n)) goto normal;
|
|
}
|
|
else { /* OP_DIV */
|
|
if (n == 0) goto normal;
|
|
if (n0 == MRB_INT_MIN && n == -1) goto normal;
|
|
n = mrb_div_int(n0, n);
|
|
}
|
|
s->pc = addr_pc(s, data0.addr);
|
|
gen_int(s, dst, n);
|
|
}
|
|
}
|
|
|
|
mrb_bool mrb_num_shift(mrb_state *mrb, mrb_int val, mrb_int width, mrb_int *num);
|
|
|
|
static mrb_bool
|
|
gen_binop(codegen_scope *s, mrb_sym op, uint16_t dst)
|
|
{
|
|
if (no_peephole(s)) return FALSE;
|
|
else if (op == MRB_OPSYM_2(s->mrb, aref)) {
|
|
genop_1(s, OP_GETIDX, dst);
|
|
return TRUE;
|
|
}
|
|
else {
|
|
struct mrb_insn_data data = mrb_last_insn(s);
|
|
mrb_int n, n0;
|
|
if (addr_pc(s, data.addr) == s->lastlabel || !get_int_operand(s, &data, &n)) {
|
|
/* not integer immediate */
|
|
return FALSE;
|
|
}
|
|
struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr));
|
|
if (!get_int_operand(s, &data0, &n0)) {
|
|
return FALSE;
|
|
}
|
|
if (op == MRB_OPSYM_2(s->mrb, lshift)) {
|
|
if (!mrb_num_shift(s->mrb, n0, n, &n)) return FALSE;
|
|
}
|
|
else if (op == MRB_OPSYM_2(s->mrb, rshift)) {
|
|
if (n == MRB_INT_MIN) return FALSE;
|
|
if (!mrb_num_shift(s->mrb, n0, -n, &n)) return FALSE;
|
|
}
|
|
else if (op == MRB_OPSYM_2(s->mrb, mod) && n != 0) {
|
|
if (n0 == MRB_INT_MIN && n == -1) {
|
|
n = 0;
|
|
}
|
|
else {
|
|
mrb_int n1 = n0 % n;
|
|
if ((n0 < 0) != (n < 0) && n1 != 0) {
|
|
n1 += n;
|
|
}
|
|
n = n1;
|
|
}
|
|
}
|
|
else if (op == MRB_OPSYM_2(s->mrb, and)) {
|
|
n = n0 & n;
|
|
}
|
|
else if (op == MRB_OPSYM_2(s->mrb, or)) {
|
|
n = n0 | n;
|
|
}
|
|
else if (op == MRB_OPSYM_2(s->mrb, xor)) {
|
|
n = n0 ^ n;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
s->pc = addr_pc(s, data0.addr);
|
|
gen_int(s, dst, n);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
dispatch(codegen_scope *s, uint32_t pos0)
|
|
{
|
|
int32_t pos1;
|
|
int32_t offset;
|
|
int16_t newpos;
|
|
|
|
if (pos0 == JMPLINK_START) return 0;
|
|
|
|
pos1 = pos0 + 2;
|
|
offset = s->pc - pos1;
|
|
if (offset > INT16_MAX) {
|
|
codegen_error(s, "too big jmp offset");
|
|
}
|
|
s->lastlabel = s->pc;
|
|
newpos = (int16_t)PEEK_S(s->iseq+pos0);
|
|
emit_S(s, pos0, (uint16_t)offset);
|
|
if (newpos == 0) return 0;
|
|
return pos1+newpos;
|
|
}
|
|
|
|
static void
|
|
dispatch_linked(codegen_scope *s, uint32_t pos)
|
|
{
|
|
if (pos==JMPLINK_START) return;
|
|
for (;;) {
|
|
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_n_(codegen_scope *s, int n)
|
|
{
|
|
if (s->sp+n >= 0xffff) {
|
|
codegen_error(s, "too complex expression");
|
|
}
|
|
s->sp+=n;
|
|
nregs_update;
|
|
}
|
|
|
|
static void
|
|
pop_n_(codegen_scope *s, int n)
|
|
{
|
|
if ((int)s->sp-n < 0) {
|
|
codegen_error(s, "stack pointer underflow");
|
|
}
|
|
s->sp-=n;
|
|
}
|
|
|
|
#define push() push_n_(s,1)
|
|
#define push_n(n) push_n_(s,n)
|
|
#define pop() pop_n_(s,1)
|
|
#define pop_n(n) pop_n_(s,n)
|
|
#define cursp() (s->sp)
|
|
|
|
static mrb_irep_pool*
|
|
lit_pool_extend(codegen_scope *s)
|
|
{
|
|
if (s->irep->plen == s->pcapa) {
|
|
s->pcapa *= 2;
|
|
s->pool = (mrb_irep_pool*)mrbc_realloc(s->pool, sizeof(mrb_irep_pool)*s->pcapa);
|
|
}
|
|
|
|
return &s->pool[s->irep->plen++];
|
|
}
|
|
|
|
static int
|
|
new_litbint(codegen_scope *s, const char *p, int base)
|
|
{
|
|
int i;
|
|
size_t plen;
|
|
mrb_irep_pool *pv;
|
|
|
|
plen = strlen(p);
|
|
if (plen > 255) {
|
|
codegen_error(s, "integer too big");
|
|
}
|
|
for (i=0; i<s->irep->plen; i++) {
|
|
size_t len;
|
|
pv = &s->pool[i];
|
|
if (pv->tt != IREP_TT_BIGINT) continue;
|
|
len = pv->u.str[0];
|
|
if (len == plen && pv->u.str[1] == base && memcmp(pv->u.str+2, p, len) == 0)
|
|
return i;
|
|
}
|
|
|
|
pv = lit_pool_extend(s);
|
|
|
|
char *buf;
|
|
pv->tt = IREP_TT_BIGINT;
|
|
buf = (char*)mrbc_malloc(plen+3);
|
|
buf[0] = (char)plen;
|
|
buf[1] = base;
|
|
memcpy(buf+2, p, plen);
|
|
buf[plen+2] = '\0';
|
|
pv->u.str = buf;
|
|
|
|
return i;
|
|
}
|
|
|
|
static int
|
|
find_pool_str(codegen_scope *s, const char *str1, mrb_int len1, const char *str2, mrb_int len2)
|
|
{
|
|
mrb_irep_pool *pool;
|
|
mrb_int len = len1 + len2;
|
|
int i;
|
|
|
|
for (i=0; i<s->irep->plen; i++) {
|
|
pool = &s->pool[i];
|
|
if (pool->tt & IREP_TT_NFLAG) continue;
|
|
mrb_int plen = pool->tt>>2;
|
|
if (len != plen) continue;
|
|
if (memcmp(pool->u.str, str1, len1) == 0 &&
|
|
(len2 == 0 || memcmp(pool->u.str + len1, str2, len2) == 0))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
new_lit_str2(codegen_scope *s, const char *str1, mrb_int len1, const char *str2, mrb_int len2)
|
|
{
|
|
int i = find_pool_str(s, str1, len1, str2, len2);
|
|
|
|
if (i >= 0) return i;
|
|
i = s->irep->plen;
|
|
|
|
mrb_irep_pool *pool = lit_pool_extend(s);
|
|
mrb_int len = len1 + len2;
|
|
|
|
if (mrb_ro_data_p(str1) && !str2) {
|
|
pool->tt = (uint32_t)(len<<2) | IREP_TT_SSTR;
|
|
pool->u.str = str1;
|
|
}
|
|
else {
|
|
char *p;
|
|
pool->tt = (uint32_t)(len<<2) | IREP_TT_STR;
|
|
p = (char*)mrbc_malloc(len+1);
|
|
memcpy(p, str1, len1);
|
|
if (str2) memcpy(p+len1, str2, len2);
|
|
p[len] = '\0';
|
|
pool->u.str = p;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
static int
|
|
new_lit_str(codegen_scope *s, const char *str, mrb_int len)
|
|
{
|
|
return new_lit_str2(s, str, len, NULL, 0);
|
|
}
|
|
|
|
static int
|
|
new_lit_cstr(codegen_scope *s, const char *str)
|
|
{
|
|
return new_lit_str(s, str, (mrb_int)strlen(str));
|
|
}
|
|
|
|
static int
|
|
new_lit_int(codegen_scope *s, mrb_int num)
|
|
{
|
|
int i;
|
|
mrb_irep_pool *pool;
|
|
|
|
for (i=0; i<s->irep->plen; i++) {
|
|
pool = &s->pool[i];
|
|
if (pool->tt == IREP_TT_INT32) {
|
|
if (num == pool->u.i32) return i;
|
|
}
|
|
#ifdef MRB_64BIT
|
|
else if (pool->tt == IREP_TT_INT64) {
|
|
if (num == pool->u.i64) return i;
|
|
}
|
|
continue;
|
|
#endif
|
|
}
|
|
|
|
pool = lit_pool_extend(s);
|
|
|
|
#ifdef MRB_INT64
|
|
pool->tt = IREP_TT_INT64;
|
|
pool->u.i64 = num;
|
|
#else
|
|
pool->tt = IREP_TT_INT32;
|
|
pool->u.i32 = num;
|
|
#endif
|
|
|
|
return i;
|
|
}
|
|
|
|
#ifndef MRB_NO_FLOAT
|
|
static int
|
|
new_lit_float(codegen_scope *s, mrb_float num)
|
|
{
|
|
int i;
|
|
mrb_irep_pool *pool;
|
|
|
|
for (i=0; i<s->irep->plen; i++) {
|
|
mrb_float f;
|
|
pool = &s->pool[i];
|
|
if (pool->tt != IREP_TT_FLOAT) continue;
|
|
f = pool->u.f;
|
|
if (f == num && !signbit(f) == !signbit(num)) return i;
|
|
}
|
|
|
|
pool = lit_pool_extend(s);
|
|
|
|
pool->tt = IREP_TT_FLOAT;
|
|
pool->u.f = num;
|
|
|
|
return i;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
new_sym(codegen_scope *s, mrb_sym sym)
|
|
{
|
|
int i, len;
|
|
|
|
mrb_assert(s->irep);
|
|
|
|
len = s->irep->slen;
|
|
for (i=0; i<len; i++) {
|
|
if (s->syms[i] == sym) return i;
|
|
}
|
|
if (s->irep->slen >= s->scapa) {
|
|
s->scapa *= 2;
|
|
if (s->scapa > 0xffff) {
|
|
codegen_error(s, "too many symbols");
|
|
}
|
|
s->syms = (mrb_sym*)mrbc_realloc(s->syms, sizeof(mrb_sym)*s->scapa);
|
|
}
|
|
s->syms[s->irep->slen] = sym;
|
|
return s->irep->slen++;
|
|
}
|
|
|
|
static void
|
|
gen_setxv(codegen_scope *s, uint8_t op, uint16_t dst, mrb_sym sym, int val)
|
|
{
|
|
int idx = new_sym(s, sym);
|
|
if (!val && !no_peephole(s)) {
|
|
struct mrb_insn_data data = mrb_last_insn(s);
|
|
if (data.insn == OP_MOVE && data.a == dst) {
|
|
dst = data.b;
|
|
rewind_pc(s);
|
|
}
|
|
}
|
|
genop_2(s, op, dst, idx);
|
|
}
|
|
|
|
static void
|
|
gen_int(codegen_scope *s, uint16_t dst, mrb_int i)
|
|
{
|
|
if (i < 0) {
|
|
if (i == -1) genop_1(s, OP_LOADI__1, dst);
|
|
else if (i >= -0xff) genop_2(s, OP_LOADINEG, dst, (uint16_t)-i);
|
|
else if (i >= INT16_MIN) genop_2S(s, OP_LOADI16, dst, (uint16_t)i);
|
|
else if (i >= INT32_MIN) genop_2SS(s, OP_LOADI32, dst, (uint32_t)i);
|
|
else goto int_lit;
|
|
}
|
|
else if (i < 8) genop_1(s, OP_LOADI_0 + (uint8_t)i, dst);
|
|
else if (i <= 0xff) genop_2(s, OP_LOADI8, dst, (uint16_t)i);
|
|
else if (i <= INT16_MAX) genop_2S(s, OP_LOADI16, dst, (uint16_t)i);
|
|
else if (i <= INT32_MAX) genop_2SS(s, OP_LOADI32, dst, (uint32_t)i);
|
|
else {
|
|
int_lit:
|
|
genop_2(s, OP_LOADL, dst, new_lit_int(s, i));
|
|
}
|
|
}
|
|
|
|
static mrb_bool
|
|
gen_uniop(codegen_scope *s, mrb_sym sym, uint16_t dst)
|
|
{
|
|
if (no_peephole(s)) return FALSE;
|
|
struct mrb_insn_data data = mrb_last_insn(s);
|
|
mrb_int n;
|
|
|
|
if (!get_int_operand(s, &data, &n)) return FALSE;
|
|
if (sym == MRB_OPSYM_2(s->mrb, plus)) {
|
|
/* unary plus does nothing */
|
|
}
|
|
else if (sym == MRB_OPSYM_2(s->mrb, minus)) {
|
|
if (n == MRB_INT_MIN) return FALSE;
|
|
n = -n;
|
|
}
|
|
else if (sym == MRB_OPSYM_2(s->mrb, neg)) {
|
|
n = ~n;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
s->pc = addr_pc(s, data.addr);
|
|
gen_int(s, dst, n);
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
node_len(node *tree)
|
|
{
|
|
int n = 0;
|
|
|
|
while (tree) {
|
|
n++;
|
|
tree = tree->cdr;
|
|
}
|
|
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)
|
|
{
|
|
node *lv = s->lv;
|
|
int n = 1;
|
|
|
|
while (lv) {
|
|
if (lv_name(lv) == id) return n;
|
|
n++;
|
|
lv = lv->cdr;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
search_upvar(codegen_scope *s, mrb_sym id, int *idx)
|
|
{
|
|
const struct RProc *u;
|
|
int lv = 0;
|
|
codegen_scope *up = s->prev;
|
|
|
|
while (up) {
|
|
*idx = lv_idx(up, id);
|
|
if (*idx > 0) {
|
|
return lv;
|
|
}
|
|
lv++;
|
|
up = up->prev;
|
|
}
|
|
|
|
if (lv < 1) lv = 1;
|
|
u = s->parser->upper;
|
|
while (u && !MRB_PROC_CFUNC_P(u)) {
|
|
const struct mrb_irep *ir = u->body.irep;
|
|
uint_fast16_t n = ir->nlocals;
|
|
int i;
|
|
|
|
const mrb_sym *v = ir->lv;
|
|
if (v) {
|
|
for (i=1; n > 1; n--, v++, i++) {
|
|
if (*v == id) {
|
|
*idx = i;
|
|
return lv - 1;
|
|
}
|
|
}
|
|
}
|
|
if (MRB_PROC_SCOPE_P(u)) break;
|
|
u = u->upper;
|
|
lv++;
|
|
}
|
|
|
|
if (id == MRB_OPSYM_2(s->mrb, and)) {
|
|
codegen_error(s, "No anonymous block parameter");
|
|
}
|
|
else if (id == MRB_OPSYM_2(s->mrb, mul)) {
|
|
codegen_error(s, "No anonymous rest parameter");
|
|
}
|
|
else if (id == MRB_OPSYM_2(s->mrb, pow)) {
|
|
codegen_error(s, "No anonymous keyword rest parameter");
|
|
}
|
|
else {
|
|
codegen_error(s, "Can't find local variables");
|
|
}
|
|
return -1; /* not reached */
|
|
}
|
|
|
|
static void
|
|
for_body(codegen_scope *s, node *tree)
|
|
{
|
|
codegen_scope *prev = s;
|
|
int idx;
|
|
struct loopinfo *lp;
|
|
node *n2;
|
|
|
|
/* generate receiver */
|
|
codegen(s, tree->cdr->car, VAL);
|
|
/* generate loop-block */
|
|
s = scope_new(s->mrb, s, NULL);
|
|
|
|
push(); /* push for a block parameter */
|
|
|
|
/* generate loop variable */
|
|
n2 = tree->car;
|
|
genop_W(s, OP_ENTER, 0x40000);
|
|
if (n2->car && !n2->car->cdr && !n2->cdr) {
|
|
gen_assignment(s, n2->car->car, NULL, 1, NOVAL);
|
|
}
|
|
else {
|
|
gen_massignment(s, n2, 1, VAL);
|
|
}
|
|
/* construct loop */
|
|
lp = loop_push(s, LOOP_FOR);
|
|
lp->pc1 = new_label(s);
|
|
genop_0(s, OP_NOP); /* for redo */
|
|
|
|
/* loop body */
|
|
codegen(s, tree->cdr->cdr->car, VAL);
|
|
pop();
|
|
gen_return(s, OP_RETURN, cursp());
|
|
loop_pop(s, NOVAL);
|
|
scope_finish(s);
|
|
s = prev;
|
|
genop_2(s, OP_BLOCK, cursp(), s->irep->rlen-1);
|
|
push();pop(); /* space for a block */
|
|
pop();
|
|
idx = new_sym(s, MRB_SYM_2(s->mrb, each));
|
|
genop_3(s, OP_SENDB, cursp(), idx, 0);
|
|
}
|
|
|
|
static int
|
|
lambda_body(codegen_scope *s, node *tree, int blk)
|
|
{
|
|
codegen_scope *parent = s;
|
|
s = scope_new(s->mrb, s, tree->car);
|
|
|
|
s->mscope = !blk;
|
|
|
|
if (blk) {
|
|
struct loopinfo *lp = loop_push(s, LOOP_BLOCK);
|
|
lp->pc0 = new_label(s);
|
|
}
|
|
tree = tree->cdr;
|
|
if (tree->car == NULL) {
|
|
genop_W(s, OP_ENTER, 0);
|
|
s->ainfo = 0;
|
|
}
|
|
else {
|
|
mrb_aspec a;
|
|
int ma, oa, ra, pa, ka, kd, ba, i;
|
|
uint32_t pos;
|
|
node *opt;
|
|
node *margs, *pargs;
|
|
node *tail;
|
|
|
|
/* mandatory arguments */
|
|
ma = node_len(tree->car->car);
|
|
margs = tree->car->car;
|
|
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 arguments after rest argument */
|
|
pa = node_len(tree->car->cdr->cdr->cdr->car);
|
|
pargs = tree->car->cdr->cdr->cdr->car;
|
|
/* 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");
|
|
}
|
|
/* (23bits = 5:5:1:5:5:1:1) */
|
|
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);
|
|
genop_W(s, OP_ENTER, a);
|
|
/* (12bits = 5:1:5:1) */
|
|
s->ainfo = (((ma+oa) & 0x3f) << 7)
|
|
| ((ra & 0x1) << 6)
|
|
| ((pa & 0x1f) << 1)
|
|
| (ka || kd);
|
|
/* generate jump table for optional arguments initializer */
|
|
pos = new_label(s);
|
|
for (i=0; i<oa; i++) {
|
|
new_label(s);
|
|
genjmp_0(s, OP_JMP);
|
|
}
|
|
if (oa > 0) {
|
|
genjmp_0(s, OP_JMP);
|
|
}
|
|
opt = tree->car->cdr->car;
|
|
i = 0;
|
|
while (opt) {
|
|
int idx;
|
|
mrb_sym id = nsym(opt->car->car);
|
|
|
|
dispatch(s, pos+i*3+1);
|
|
codegen(s, opt->car->cdr, VAL);
|
|
pop();
|
|
idx = lv_idx(s, id);
|
|
if (idx > 0) {
|
|
gen_move(s, idx, cursp(), 0);
|
|
}
|
|
else {
|
|
gen_getupvar(s, cursp(), id);
|
|
}
|
|
i++;
|
|
opt = opt->cdr;
|
|
}
|
|
if (oa > 0) {
|
|
dispatch(s, pos+i*3+1);
|
|
}
|
|
|
|
/* keyword arguments */
|
|
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) {
|
|
int idx;
|
|
genop_2(s, OP_KEY_P, lv_idx(s, kwd_sym), new_sym(s, kwd_sym));
|
|
jmpif_key_p = genjmp2_0(s, OP_JMPIF, lv_idx(s, kwd_sym), NOVAL);
|
|
codegen(s, def_arg, VAL);
|
|
pop();
|
|
idx = lv_idx(s, kwd_sym);
|
|
if (idx > 0) {
|
|
gen_move(s, idx, cursp(), 0);
|
|
}
|
|
else {
|
|
gen_getupvar(s, cursp(), kwd_sym);
|
|
}
|
|
jmp_def_set = genjmp_0(s, OP_JMP);
|
|
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);
|
|
}
|
|
if (ba) {
|
|
mrb_sym bparam = nsym(tail->cdr->cdr->cdr->car);
|
|
pos = ma+oa+ra+pa+(ka||kd);
|
|
if (bparam) {
|
|
int idx = lv_idx(s, bparam);
|
|
genop_2(s, OP_MOVE, idx, pos+1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* argument destructuring */
|
|
if (margs) {
|
|
node *n = margs;
|
|
|
|
pos = 1;
|
|
while (n) {
|
|
if (nint(n->car->car) == NODE_MASGN) {
|
|
gen_massignment(s, n->car->cdr->car, pos, NOVAL);
|
|
}
|
|
pos++;
|
|
n = n->cdr;
|
|
}
|
|
}
|
|
if (pargs) {
|
|
node *n = pargs;
|
|
|
|
pos = ma+oa+ra+1;
|
|
while (n) {
|
|
if (nint(n->car->car) == NODE_MASGN) {
|
|
gen_massignment(s, n->car->cdr->car, pos, NOVAL);
|
|
}
|
|
pos++;
|
|
n = n->cdr;
|
|
}
|
|
}
|
|
}
|
|
|
|
codegen(s, tree->cdr->car, VAL);
|
|
pop();
|
|
if (s->pc > 0) {
|
|
gen_return(s, OP_RETURN, cursp());
|
|
}
|
|
if (blk) {
|
|
loop_pop(s, NOVAL);
|
|
}
|
|
scope_finish(s);
|
|
return parent->irep->rlen - 1;
|
|
}
|
|
|
|
static int
|
|
scope_body(codegen_scope *s, node *tree, int val)
|
|
{
|
|
codegen_scope *scope = scope_new(s->mrb, s, tree->car);
|
|
|
|
codegen(scope, tree->cdr, VAL);
|
|
gen_return(scope, OP_RETURN, scope->sp-1);
|
|
if (!s->iseq) {
|
|
genop_0(scope, OP_STOP);
|
|
}
|
|
scope_finish(scope);
|
|
if (!s->irep) {
|
|
/* should not happen */
|
|
return 0;
|
|
}
|
|
return s->irep->rlen - 1;
|
|
}
|
|
|
|
static mrb_bool
|
|
nosplat(node *t)
|
|
{
|
|
while (t) {
|
|
if (nint(t->car->car) == NODE_SPLAT) return FALSE;
|
|
t = t->cdr;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static mrb_sym
|
|
attrsym(codegen_scope *s, mrb_sym a)
|
|
{
|
|
const char *name;
|
|
mrb_int len;
|
|
char *name2;
|
|
|
|
name = mrb_sym_name_len(s->mrb, a, &len);
|
|
name2 = (char*)codegen_palloc(s,
|
|
(size_t)len
|
|
+ 1 /* '=' */
|
|
+ 1 /* '\0' */
|
|
);
|
|
mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
|
|
memcpy(name2, name, (size_t)len);
|
|
name2[len] = '=';
|
|
name2[len+1] = '\0';
|
|
|
|
return mrb_intern(s->mrb, name2, len+1);
|
|
}
|
|
|
|
#define CALL_MAXARGS 15
|
|
#define GEN_LIT_ARY_MAX 64
|
|
#define GEN_VAL_STACK_MAX 99
|
|
|
|
static int
|
|
gen_values(codegen_scope *s, node *t, int val, int limit)
|
|
{
|
|
int n = 0;
|
|
int first = 1;
|
|
int slimit = GEN_VAL_STACK_MAX;
|
|
|
|
if (limit == 0) limit = GEN_LIT_ARY_MAX;
|
|
if (cursp() >= slimit) slimit = INT16_MAX;
|
|
|
|
if (!val) {
|
|
while (t) {
|
|
codegen(s, t->car, NOVAL);
|
|
n++;
|
|
t = t->cdr;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
while (t) {
|
|
int is_splat = nint(t->car->car) == NODE_SPLAT;
|
|
|
|
if (is_splat || cursp() >= slimit) { /* flush stack */
|
|
pop_n(n);
|
|
if (first) {
|
|
if (n == 0) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
}
|
|
else {
|
|
genop_2(s, OP_ARRAY, cursp(), n);
|
|
}
|
|
push();
|
|
first = 0;
|
|
limit = GEN_LIT_ARY_MAX;
|
|
}
|
|
else if (n > 0) {
|
|
pop();
|
|
genop_2(s, OP_ARYPUSH, cursp(), n);
|
|
push();
|
|
}
|
|
n = 0;
|
|
}
|
|
codegen(s, t->car, val);
|
|
if (is_splat) {
|
|
pop(); pop();
|
|
genop_1(s, OP_ARYCAT, cursp());
|
|
push();
|
|
}
|
|
else {
|
|
n++;
|
|
}
|
|
t = t->cdr;
|
|
}
|
|
if (!first) {
|
|
pop();
|
|
if (n > 0) {
|
|
pop_n(n);
|
|
genop_2(s, OP_ARYPUSH, cursp(), n);
|
|
}
|
|
return -1; /* variable length */
|
|
}
|
|
else if (n > limit) {
|
|
pop_n(n);
|
|
genop_2(s, OP_ARRAY, cursp(), n);
|
|
return -1;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static int
|
|
gen_hash(codegen_scope *s, node *tree, int val, int limit)
|
|
{
|
|
int slimit = GEN_VAL_STACK_MAX;
|
|
if (cursp() >= GEN_LIT_ARY_MAX) slimit = INT16_MAX;
|
|
int len = 0;
|
|
mrb_bool update = FALSE;
|
|
mrb_bool first = TRUE;
|
|
|
|
while (tree) {
|
|
if (nint(tree->car->car->car) == NODE_KW_REST_ARGS) {
|
|
if (val && first) {
|
|
genop_2(s, OP_HASH, cursp(), 0);
|
|
push();
|
|
update = TRUE;
|
|
}
|
|
else if (val && 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 (val && (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 && cursp() >= slimit) {
|
|
pop_n(len*2);
|
|
if (!update) {
|
|
genop_2(s, OP_HASH, cursp(), len);
|
|
}
|
|
else {
|
|
pop();
|
|
genop_2(s, OP_HASHADD, cursp(), len);
|
|
}
|
|
push();
|
|
update = TRUE;
|
|
len = 0;
|
|
}
|
|
first = FALSE;
|
|
}
|
|
if (val && len > limit) {
|
|
pop_n(len*2);
|
|
genop_2(s, OP_HASH, cursp(), len);
|
|
push();
|
|
return -1;
|
|
}
|
|
if (update) {
|
|
if (val && len > 0) {
|
|
pop_n(len*2+1);
|
|
genop_2(s, OP_HASHADD, cursp(), len);
|
|
push();
|
|
}
|
|
return -1; /* variable length */
|
|
}
|
|
return len;
|
|
}
|
|
|
|
static void
|
|
gen_call(codegen_scope *s, node *tree, int val, int safe)
|
|
{
|
|
mrb_sym sym = nsym(tree->cdr->car);
|
|
int skip = 0, n = 0, nk = 0, noop = no_optimize(s), noself = 0, blk = 0, sp_save = cursp();
|
|
enum mrb_insn opt_op = OP_NOP;
|
|
|
|
if (!noop) {
|
|
if (sym == MRB_OPSYM_2(s->mrb, add)) opt_op = OP_ADD;
|
|
else if (sym == MRB_OPSYM_2(s->mrb, sub)) opt_op = OP_SUB;
|
|
else if (sym == MRB_OPSYM_2(s->mrb, mul)) opt_op = OP_MUL;
|
|
else if (sym == MRB_OPSYM_2(s->mrb, div)) opt_op = OP_DIV;
|
|
else if (sym == MRB_OPSYM_2(s->mrb, lt)) opt_op = OP_LT;
|
|
else if (sym == MRB_OPSYM_2(s->mrb, le)) opt_op = OP_LE;
|
|
else if (sym == MRB_OPSYM_2(s->mrb, gt)) opt_op = OP_GT;
|
|
else if (sym == MRB_OPSYM_2(s->mrb, ge)) opt_op = OP_GE;
|
|
else if (sym == MRB_OPSYM_2(s->mrb, eq)) opt_op = OP_EQ;
|
|
else if (sym == MRB_OPSYM_2(s->mrb, aref)) opt_op = OP_GETIDX;
|
|
else if (sym == MRB_OPSYM_2(s->mrb, aset)) opt_op = OP_SETIDX;
|
|
}
|
|
if (!tree->car || (opt_op == OP_NOP && nint(tree->car->car) == NODE_SELF)) {
|
|
noself = 1;
|
|
push();
|
|
}
|
|
else {
|
|
codegen(s, tree->car, VAL); /* receiver */
|
|
}
|
|
if (safe) {
|
|
int recv = cursp()-1;
|
|
gen_move(s, cursp(), recv, 1);
|
|
skip = genjmp2_0(s, OP_JMPNIL, cursp(), val);
|
|
}
|
|
tree = tree->cdr->cdr->car;
|
|
if (tree) {
|
|
if (tree->car) { /* positional arguments */
|
|
n = gen_values(s, tree->car, VAL, 14);
|
|
if (n < 0) { /* variable length */
|
|
noop = 1; /* not operator */
|
|
n = 15;
|
|
push();
|
|
}
|
|
}
|
|
if (tree->cdr->car) { /* keyword arguments */
|
|
noop = 1;
|
|
nk = gen_hash(s, tree->cdr->car->cdr, VAL, 14);
|
|
if (nk < 0) nk = 15;
|
|
}
|
|
}
|
|
if (tree && tree->cdr && tree->cdr->cdr) {
|
|
codegen(s, tree->cdr->cdr, VAL);
|
|
pop();
|
|
noop = 1;
|
|
blk = 1;
|
|
}
|
|
push();
|
|
s->sp = sp_save;
|
|
if (opt_op == OP_ADD && n == 1) {
|
|
gen_addsub(s, OP_ADD, cursp());
|
|
}
|
|
else if (opt_op == OP_SUB && n == 1) {
|
|
gen_addsub(s, OP_SUB, cursp());
|
|
}
|
|
else if (opt_op == OP_MUL && n == 1) {
|
|
gen_muldiv(s, OP_MUL, cursp());
|
|
}
|
|
else if (opt_op == OP_DIV && n == 1) {
|
|
gen_muldiv(s, OP_DIV, cursp());
|
|
}
|
|
else if (opt_op == OP_LT && n == 1) {
|
|
genop_1(s, OP_LT, cursp());
|
|
}
|
|
else if (opt_op == OP_LE && n == 1) {
|
|
genop_1(s, OP_LE, cursp());
|
|
}
|
|
else if (opt_op == OP_GT && n == 1) {
|
|
genop_1(s, OP_GT, cursp());
|
|
}
|
|
else if (opt_op == OP_GE && n == 1) {
|
|
genop_1(s, OP_GE, cursp());
|
|
}
|
|
else if (opt_op == OP_EQ && n == 1) {
|
|
genop_1(s, OP_EQ, cursp());
|
|
}
|
|
else if (opt_op == OP_SETIDX && n == 2) {
|
|
genop_1(s, OP_SETIDX, cursp());
|
|
}
|
|
else if (!noop && n == 0 && gen_uniop(s, sym, cursp())) {
|
|
/* constant folding succeeded */
|
|
}
|
|
else if (!noop && n == 1 && gen_binop(s, sym, cursp())) {
|
|
/* constant folding succeeded */
|
|
}
|
|
else if (noself) {
|
|
genop_3(s, blk ? OP_SSENDB : OP_SSEND, cursp(), new_sym(s, sym), n|(nk<<4));
|
|
}
|
|
else {
|
|
genop_3(s, blk ? OP_SENDB : OP_SEND, cursp(), new_sym(s, sym), n|(nk<<4));
|
|
}
|
|
if (safe) {
|
|
dispatch(s, skip);
|
|
}
|
|
if (val) {
|
|
push();
|
|
}
|
|
}
|
|
|
|
static void
|
|
gen_assignment(codegen_scope *s, node *tree, node *rhs, int sp, int val)
|
|
{
|
|
int idx;
|
|
int type = nint(tree->car);
|
|
|
|
switch (type) {
|
|
case NODE_GVAR:
|
|
case NODE_ARG:
|
|
case NODE_LVAR:
|
|
case NODE_IVAR:
|
|
case NODE_CVAR:
|
|
case NODE_CONST:
|
|
case NODE_NIL:
|
|
case NODE_MASGN:
|
|
if (rhs) {
|
|
codegen(s, rhs, VAL);
|
|
pop();
|
|
sp = cursp();
|
|
}
|
|
break;
|
|
|
|
case NODE_COLON2:
|
|
case NODE_COLON3:
|
|
case NODE_CALL:
|
|
case NODE_SCALL:
|
|
/* keep evaluation order */
|
|
break;
|
|
|
|
case NODE_NVAR:
|
|
/* never happens; should have already checked in the parser */
|
|
codegen_error(s, "Can't assign to numbered parameter");
|
|
break;
|
|
|
|
default:
|
|
codegen_error(s, "unknown lhs");
|
|
break;
|
|
}
|
|
|
|
tree = tree->cdr;
|
|
switch (type) {
|
|
case NODE_GVAR:
|
|
gen_setxv(s, OP_SETGV, sp, nsym(tree), val);
|
|
break;
|
|
case NODE_ARG:
|
|
case NODE_LVAR:
|
|
idx = lv_idx(s, nsym(tree));
|
|
if (idx > 0) {
|
|
if (idx != sp) {
|
|
gen_move(s, idx, sp, val);
|
|
}
|
|
break;
|
|
}
|
|
else { /* upvar */
|
|
gen_setupvar(s, sp, nsym(tree));
|
|
}
|
|
break;
|
|
case NODE_IVAR:
|
|
gen_setxv(s, OP_SETIV, sp, nsym(tree), val);
|
|
break;
|
|
case NODE_CVAR:
|
|
gen_setxv(s, OP_SETCV, sp, nsym(tree), val);
|
|
break;
|
|
case NODE_CONST:
|
|
gen_setxv(s, OP_SETCONST, sp, nsym(tree), val);
|
|
break;
|
|
case NODE_COLON2:
|
|
case NODE_COLON3:
|
|
if (sp) {
|
|
gen_move(s, cursp(), sp, 0);
|
|
}
|
|
sp = cursp();
|
|
push();
|
|
if (type == NODE_COLON2) {
|
|
codegen(s, tree->car, VAL);
|
|
idx = new_sym(s, nsym(tree->cdr));
|
|
}
|
|
else { /* NODE_COLON3 */
|
|
genop_1(s, OP_OCLASS, cursp());
|
|
push();
|
|
idx = new_sym(s, nsym(tree));
|
|
}
|
|
if (rhs) {
|
|
codegen(s, rhs, VAL); pop();
|
|
gen_move(s, sp, cursp(), 0);
|
|
}
|
|
pop_n(2);
|
|
genop_2(s, OP_SETMCNST, sp, idx);
|
|
break;
|
|
|
|
case NODE_CALL:
|
|
case NODE_SCALL:
|
|
{
|
|
int noself = 0, safe = (type == NODE_SCALL), skip = 0, top, call, n = 0;
|
|
mrb_sym mid = nsym(tree->cdr->car);
|
|
|
|
top = cursp();
|
|
if (val || sp == cursp()) {
|
|
push(); /* room for retval */
|
|
}
|
|
call = cursp();
|
|
if (!tree->car) {
|
|
noself = 1;
|
|
push();
|
|
}
|
|
else {
|
|
codegen(s, tree->car, VAL); /* receiver */
|
|
}
|
|
if (safe) {
|
|
int recv = cursp()-1;
|
|
gen_move(s, cursp(), recv, 1);
|
|
skip = genjmp2_0(s, OP_JMPNIL, cursp(), val);
|
|
}
|
|
tree = tree->cdr->cdr->car;
|
|
if (tree) {
|
|
if (tree->car) { /* positional arguments */
|
|
n = gen_values(s, tree->car, VAL, (tree->cdr->car)?13:14);
|
|
if (n < 0) { /* variable length */
|
|
n = 15;
|
|
push();
|
|
}
|
|
}
|
|
if (tree->cdr->car) { /* keyword arguments */
|
|
if (n == 13 || n == 14) {
|
|
pop_n(n);
|
|
genop_2(s, OP_ARRAY, cursp(), n);
|
|
push();
|
|
n = 15;
|
|
}
|
|
gen_hash(s, tree->cdr->car->cdr, VAL, 0);
|
|
if (n < 14) {
|
|
n++;
|
|
}
|
|
else {
|
|
pop_n(2);
|
|
genop_2(s, OP_ARYPUSH, cursp(), 1);
|
|
}
|
|
push();
|
|
}
|
|
}
|
|
if (rhs) {
|
|
codegen(s, rhs, VAL);
|
|
pop();
|
|
}
|
|
else {
|
|
gen_move(s, cursp(), sp, 0);
|
|
}
|
|
if (val) {
|
|
gen_move(s, top, cursp(), 1);
|
|
}
|
|
if (n < 15) {
|
|
n++;
|
|
if (n == 15) {
|
|
pop_n(14);
|
|
genop_2(s, OP_ARRAY, cursp(), 15);
|
|
}
|
|
}
|
|
else {
|
|
pop();
|
|
genop_2(s, OP_ARYPUSH, cursp(), 1);
|
|
}
|
|
push(); pop();
|
|
s->sp = call;
|
|
if (mid == MRB_OPSYM_2(s->mrb, aref) && n == 2) {
|
|
push_n(4); pop_n(4); /* self + idx + value + (invisible block for OP_SEND) */
|
|
genop_1(s, OP_SETIDX, cursp());
|
|
}
|
|
else {
|
|
int st = 2 /* self + block */ +
|
|
(((n >> 0) & 0x0f) < 15 ? ((n >> 0) & 0x0f) : 1) +
|
|
(((n >> 4) & 0x0f) < 15 ? ((n >> 4) & 0x0f) * 2 : 1);
|
|
push_n(st); pop_n(st);
|
|
genop_3(s, noself ? OP_SSEND : OP_SEND, cursp(), new_sym(s, attrsym(s, mid)), n);
|
|
}
|
|
if (safe) {
|
|
dispatch(s, skip);
|
|
}
|
|
s->sp = top;
|
|
}
|
|
break;
|
|
|
|
case NODE_MASGN:
|
|
gen_massignment(s, tree->car, sp, val);
|
|
break;
|
|
|
|
/* splat without assignment */
|
|
case NODE_NIL:
|
|
break;
|
|
|
|
default:
|
|
codegen_error(s, "unknown lhs");
|
|
break;
|
|
}
|
|
if (val) push();
|
|
}
|
|
|
|
static void
|
|
gen_massignment(codegen_scope *s, node *tree, int rhs, int val)
|
|
{
|
|
int n = 0, post = 0;
|
|
node *t, *p;
|
|
|
|
if (tree->car) { /* pre */
|
|
t = tree->car;
|
|
n = 0;
|
|
while (t) {
|
|
int sp = cursp();
|
|
|
|
genop_3(s, OP_AREF, sp, rhs, n);
|
|
push();
|
|
gen_assignment(s, t->car, NULL, sp, NOVAL);
|
|
pop();
|
|
n++;
|
|
t = t->cdr;
|
|
}
|
|
}
|
|
t = tree->cdr;
|
|
if (t) {
|
|
if (t->cdr) { /* post count */
|
|
p = t->cdr->car;
|
|
while (p) {
|
|
post++;
|
|
p = p->cdr;
|
|
}
|
|
}
|
|
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 && t->car != (node*)-1) { /* rest */
|
|
gen_assignment(s, t->car, NULL, cursp(), NOVAL);
|
|
}
|
|
if (t->cdr && t->cdr->car) {
|
|
t = t->cdr->car;
|
|
while (t) {
|
|
gen_assignment(s, t->car, NULL, cursp()+n, NOVAL);
|
|
t = t->cdr;
|
|
n++;
|
|
}
|
|
}
|
|
if (val) {
|
|
gen_move(s, cursp(), rhs, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gen_intern(codegen_scope *s)
|
|
{
|
|
pop();
|
|
if (!no_peephole(s)) {
|
|
struct mrb_insn_data data = mrb_last_insn(s);
|
|
|
|
if (data.insn == OP_STRING && data.a == cursp()) {
|
|
rewind_pc(s);
|
|
genop_2(s, OP_SYMBOL, data.a, data.b);
|
|
push();
|
|
return;
|
|
}
|
|
}
|
|
genop_1(s, OP_INTERN, cursp());
|
|
push();
|
|
}
|
|
|
|
static void
|
|
gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
|
|
{
|
|
if (val) {
|
|
int i = 0, j = 0, gen = 0;
|
|
|
|
while (tree) {
|
|
switch (nint(tree->car->car)) {
|
|
case NODE_STR:
|
|
if ((tree->cdr == NULL) && (nint(tree->car->cdr->cdr) == 0))
|
|
break;
|
|
/* fall through */
|
|
case NODE_BEGIN:
|
|
codegen(s, tree->car, VAL);
|
|
j++;
|
|
break;
|
|
|
|
case NODE_LITERAL_DELIM:
|
|
if (j > 0) {
|
|
j = 0;
|
|
i++;
|
|
if (sym)
|
|
gen_intern(s);
|
|
}
|
|
break;
|
|
}
|
|
while (j >= 2) {
|
|
pop(); pop();
|
|
genop_1(s, OP_STRCAT, cursp());
|
|
push();
|
|
j--;
|
|
}
|
|
if (i > GEN_LIT_ARY_MAX) {
|
|
pop_n(i);
|
|
if (gen) {
|
|
pop();
|
|
genop_2(s, OP_ARYPUSH, cursp(), i);
|
|
}
|
|
else {
|
|
genop_2(s, OP_ARRAY, cursp(), i);
|
|
gen = 1;
|
|
}
|
|
push();
|
|
i = 0;
|
|
}
|
|
tree = tree->cdr;
|
|
}
|
|
if (j > 0) {
|
|
i++;
|
|
if (sym)
|
|
gen_intern(s);
|
|
}
|
|
pop_n(i);
|
|
if (gen) {
|
|
pop();
|
|
genop_2(s, OP_ARYPUSH, cursp(), i);
|
|
}
|
|
else {
|
|
genop_2(s, OP_ARRAY, cursp(), i);
|
|
}
|
|
push();
|
|
}
|
|
else {
|
|
while (tree) {
|
|
switch (nint(tree->car->car)) {
|
|
case NODE_BEGIN: case NODE_BLOCK:
|
|
codegen(s, tree->car, NOVAL);
|
|
}
|
|
tree = tree->cdr;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
raise_error(codegen_scope *s, const char *msg)
|
|
{
|
|
int idx = new_lit_cstr(s, msg);
|
|
|
|
genop_1(s, OP_ERR, idx);
|
|
}
|
|
|
|
static mrb_int
|
|
readint(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow)
|
|
{
|
|
const char *e = p + strlen(p);
|
|
mrb_int result = 0;
|
|
|
|
mrb_assert(base >= 2 && base <= 16);
|
|
if (*p == '+') p++;
|
|
while (p < e) {
|
|
int n;
|
|
char c = *p;
|
|
switch (c) {
|
|
case '0': case '1': case '2': case '3':
|
|
case '4': case '5': case '6': case '7':
|
|
n = c - '0'; break;
|
|
case '8': case '9':
|
|
n = c - '0'; break;
|
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
|
n = c - 'a' + 10; break;
|
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
|
n = c - 'A' + 10; break;
|
|
default:
|
|
codegen_error(s, "malformed readint input");
|
|
*overflow = TRUE;
|
|
/* not reached */
|
|
return result;
|
|
}
|
|
if (mrb_int_mul_overflow(result, base, &result)) {
|
|
overflow:
|
|
*overflow = TRUE;
|
|
return 0;
|
|
}
|
|
mrb_uint tmp = ((mrb_uint)result)+n;
|
|
if (neg && tmp == (mrb_uint)MRB_INT_MAX+1) {
|
|
*overflow = FALSE;
|
|
return MRB_INT_MIN;
|
|
}
|
|
if (tmp > MRB_INT_MAX) goto overflow;
|
|
result = (mrb_int)tmp;
|
|
p++;
|
|
}
|
|
*overflow = FALSE;
|
|
if (neg) return -result;
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
gen_retval(codegen_scope *s, node *tree)
|
|
{
|
|
if (nint(tree->car) == NODE_SPLAT) {
|
|
codegen(s, tree, VAL);
|
|
pop();
|
|
genop_1(s, OP_ARYSPLAT, cursp());
|
|
}
|
|
else {
|
|
codegen(s, tree, VAL);
|
|
pop();
|
|
}
|
|
}
|
|
|
|
static mrb_bool
|
|
true_always(node *tree)
|
|
{
|
|
switch (nint(tree->car)) {
|
|
case NODE_TRUE:
|
|
case NODE_INT:
|
|
case NODE_STR:
|
|
case NODE_SYM:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static mrb_bool
|
|
false_always(node *tree)
|
|
{
|
|
switch (nint(tree->car)) {
|
|
case NODE_FALSE:
|
|
case NODE_NIL:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gen_blkmove(codegen_scope *s, uint16_t ainfo, int lv)
|
|
{
|
|
int m1 = (ainfo>>7)&0x3f;
|
|
int r = (ainfo>>6)&0x1;
|
|
int m2 = (ainfo>>1)&0x1f;
|
|
int kd = (ainfo)&0x1;
|
|
int off = m1+r+m2+kd+1;
|
|
if (lv == 0) {
|
|
gen_move(s, cursp(), off, 0);
|
|
}
|
|
else {
|
|
genop_3(s, OP_GETUPVAR, cursp(), off, lv);
|
|
}
|
|
push();
|
|
}
|
|
|
|
static void
|
|
codegen(codegen_scope *s, node *tree, int val)
|
|
{
|
|
int nt;
|
|
int rlev = s->rlev;
|
|
|
|
if (!tree) {
|
|
if (val) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
return;
|
|
}
|
|
|
|
s->rlev++;
|
|
if (s->rlev > MRB_CODEGEN_LEVEL_MAX) {
|
|
codegen_error(s, "too complex expression");
|
|
}
|
|
if (s->irep && s->filename_index != tree->filename_index) {
|
|
mrb_sym fname = mrb_parser_get_filename(s->parser, s->filename_index);
|
|
const char *filename = mrb_sym_name_len(s->mrb, fname, NULL);
|
|
|
|
mrb_debug_info_append_file(s->mrb, s->irep->debug_info,
|
|
filename, s->lines, s->debug_start_pos, s->pc);
|
|
s->debug_start_pos = s->pc;
|
|
s->filename_index = tree->filename_index;
|
|
s->filename_sym = mrb_parser_get_filename(s->parser, tree->filename_index);
|
|
}
|
|
|
|
nt = nint(tree->car);
|
|
s->lineno = tree->lineno;
|
|
tree = tree->cdr;
|
|
switch (nt) {
|
|
case NODE_BEGIN:
|
|
if (val && !tree) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
while (tree) {
|
|
codegen(s, tree->car, tree->cdr ? NOVAL : val);
|
|
tree = tree->cdr;
|
|
}
|
|
break;
|
|
|
|
case NODE_RESCUE:
|
|
{
|
|
int noexc;
|
|
uint32_t exend, pos1, pos2, tmp;
|
|
struct loopinfo *lp;
|
|
int catch_entry, begin, end;
|
|
|
|
if (tree->car == NULL) goto exit;
|
|
lp = loop_push(s, LOOP_BEGIN);
|
|
lp->pc0 = new_label(s);
|
|
catch_entry = catch_handler_new(s);
|
|
begin = s->pc;
|
|
codegen(s, tree->car, VAL);
|
|
pop();
|
|
lp->type = LOOP_RESCUE;
|
|
end = s->pc;
|
|
noexc = genjmp_0(s, OP_JMP);
|
|
catch_handler_set(s, catch_entry, MRB_CATCH_RESCUE, begin, end, s->pc);
|
|
tree = tree->cdr;
|
|
exend = JMPLINK_START;
|
|
pos1 = JMPLINK_START;
|
|
if (tree->car) {
|
|
node *n2 = tree->car;
|
|
int exc = cursp();
|
|
|
|
genop_1(s, OP_EXCEPT, exc);
|
|
push();
|
|
while (n2) {
|
|
node *n3 = n2->car;
|
|
node *n4 = n3->car;
|
|
|
|
dispatch(s, pos1);
|
|
pos2 = JMPLINK_START;
|
|
do {
|
|
if (n4 && n4->car && nint(n4->car->car) == NODE_SPLAT) {
|
|
codegen(s, n4->car, VAL);
|
|
gen_move(s, cursp(), exc, 0);
|
|
push_n(2); pop_n(2); /* space for one arg and a block */
|
|
pop();
|
|
genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_SYM_2(s->mrb, __case_eqq)), 1);
|
|
}
|
|
else {
|
|
if (n4) {
|
|
codegen(s, n4->car, VAL);
|
|
}
|
|
else {
|
|
genop_2(s, OP_GETCONST, cursp(), new_sym(s, MRB_SYM_2(s->mrb, StandardError)));
|
|
push();
|
|
}
|
|
pop();
|
|
genop_2(s, OP_RESCUE, exc, cursp());
|
|
}
|
|
tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, val);
|
|
pos2 = tmp;
|
|
if (n4) {
|
|
n4 = n4->cdr;
|
|
}
|
|
} while (n4);
|
|
pos1 = genjmp_0(s, OP_JMP);
|
|
dispatch_linked(s, pos2);
|
|
|
|
pop();
|
|
if (n3->cdr->car) {
|
|
gen_assignment(s, n3->cdr->car, NULL, exc, NOVAL);
|
|
}
|
|
if (n3->cdr->cdr->car) {
|
|
codegen(s, n3->cdr->cdr->car, val);
|
|
if (val) pop();
|
|
}
|
|
tmp = genjmp(s, OP_JMP, exend);
|
|
exend = tmp;
|
|
n2 = n2->cdr;
|
|
push();
|
|
}
|
|
if (pos1 != JMPLINK_START) {
|
|
dispatch(s, pos1);
|
|
genop_1(s, OP_RAISEIF, exc);
|
|
}
|
|
}
|
|
pop();
|
|
tree = tree->cdr;
|
|
dispatch(s, noexc);
|
|
if (tree->car) {
|
|
codegen(s, tree->car, val);
|
|
}
|
|
else if (val) {
|
|
push();
|
|
}
|
|
dispatch_linked(s, exend);
|
|
loop_pop(s, NOVAL);
|
|
}
|
|
break;
|
|
|
|
case NODE_ENSURE:
|
|
if (!tree->cdr || !tree->cdr->cdr ||
|
|
(nint(tree->cdr->cdr->car) == NODE_BEGIN &&
|
|
tree->cdr->cdr->cdr)) {
|
|
int catch_entry, begin, end, target;
|
|
int idx;
|
|
|
|
catch_entry = catch_handler_new(s);
|
|
begin = s->pc;
|
|
codegen(s, tree->car, val);
|
|
end = target = s->pc;
|
|
push();
|
|
idx = cursp();
|
|
genop_1(s, OP_EXCEPT, idx);
|
|
push();
|
|
codegen(s, tree->cdr->cdr, NOVAL);
|
|
pop();
|
|
genop_1(s, OP_RAISEIF, idx);
|
|
pop();
|
|
catch_handler_set(s, catch_entry, MRB_CATCH_ENSURE, begin, end, target);
|
|
}
|
|
else { /* empty ensure ignored */
|
|
codegen(s, tree->car, val);
|
|
}
|
|
break;
|
|
|
|
case NODE_LAMBDA:
|
|
if (val) {
|
|
int idx = lambda_body(s, tree, 1);
|
|
|
|
genop_2(s, OP_LAMBDA, cursp(), idx);
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_BLOCK:
|
|
if (val) {
|
|
int idx = lambda_body(s, tree, 1);
|
|
|
|
genop_2(s, OP_BLOCK, cursp(), idx);
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_IF:
|
|
{
|
|
uint32_t pos1, pos2;
|
|
mrb_bool nil_p = FALSE;
|
|
node *elsepart = tree->cdr->cdr->car;
|
|
|
|
if (!tree->car) {
|
|
codegen(s, elsepart, val);
|
|
goto exit;
|
|
}
|
|
if (true_always(tree->car)) {
|
|
codegen(s, tree->cdr->car, val);
|
|
goto exit;
|
|
}
|
|
if (false_always(tree->car)) {
|
|
codegen(s, elsepart, val);
|
|
goto exit;
|
|
}
|
|
if (nint(tree->car->car) == NODE_CALL) {
|
|
node *n = tree->car->cdr;
|
|
mrb_sym mid = nsym(n->cdr->car);
|
|
mrb_sym sym_nil_p = MRB_SYM_Q_2(s->mrb, nil);
|
|
if (mid == sym_nil_p && n->cdr->cdr->car == NULL) {
|
|
nil_p = TRUE;
|
|
codegen(s, n->car, VAL);
|
|
}
|
|
}
|
|
if (!nil_p) {
|
|
codegen(s, tree->car, VAL);
|
|
}
|
|
pop();
|
|
if (val || tree->cdr->car) {
|
|
if (nil_p) {
|
|
pos2 = genjmp2_0(s, OP_JMPNIL, cursp(), val);
|
|
pos1 = genjmp_0(s, OP_JMP);
|
|
dispatch(s, pos2);
|
|
}
|
|
else {
|
|
pos1 = genjmp2_0(s, OP_JMPNOT, cursp(), val);
|
|
}
|
|
codegen(s, tree->cdr->car, val);
|
|
if (val) pop();
|
|
if (elsepart || val) {
|
|
pos2 = genjmp_0(s, OP_JMP);
|
|
dispatch(s, pos1);
|
|
codegen(s, elsepart, val);
|
|
dispatch(s, pos2);
|
|
}
|
|
else {
|
|
dispatch(s, pos1);
|
|
}
|
|
}
|
|
else { /* empty then-part */
|
|
if (elsepart) {
|
|
if (nil_p) {
|
|
pos1 = genjmp2_0(s, OP_JMPNIL, cursp(), val);
|
|
}
|
|
else {
|
|
pos1 = genjmp2_0(s, OP_JMPIF, cursp(), val);
|
|
}
|
|
codegen(s, elsepart, val);
|
|
dispatch(s, pos1);
|
|
}
|
|
else if (val && !nil_p) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_AND:
|
|
{
|
|
uint32_t pos;
|
|
|
|
if (true_always(tree->car)) {
|
|
codegen(s, tree->cdr, val);
|
|
goto exit;
|
|
}
|
|
if (false_always(tree->car)) {
|
|
codegen(s, tree->car, val);
|
|
goto exit;
|
|
}
|
|
codegen(s, tree->car, VAL);
|
|
pop();
|
|
pos = genjmp2_0(s, OP_JMPNOT, cursp(), val);
|
|
codegen(s, tree->cdr, val);
|
|
dispatch(s, pos);
|
|
}
|
|
break;
|
|
|
|
case NODE_OR:
|
|
{
|
|
uint32_t pos;
|
|
|
|
if (true_always(tree->car)) {
|
|
codegen(s, tree->car, val);
|
|
goto exit;
|
|
}
|
|
if (false_always(tree->car)) {
|
|
codegen(s, tree->cdr, val);
|
|
goto exit;
|
|
}
|
|
codegen(s, tree->car, VAL);
|
|
pop();
|
|
pos = genjmp2_0(s, OP_JMPIF, cursp(), val);
|
|
codegen(s, tree->cdr, val);
|
|
dispatch(s, pos);
|
|
}
|
|
break;
|
|
|
|
case NODE_WHILE:
|
|
case NODE_UNTIL:
|
|
{
|
|
if (true_always(tree->car)) {
|
|
if (nt == NODE_UNTIL) {
|
|
if (val) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
goto exit;
|
|
}
|
|
}
|
|
else if (false_always(tree->car)) {
|
|
if (nt == NODE_WHILE) {
|
|
if (val) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
uint32_t pos = JMPLINK_START;
|
|
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
|
|
|
|
if (!val) lp->reg = -1;
|
|
lp->pc0 = new_label(s);
|
|
codegen(s, tree->car, VAL);
|
|
pop();
|
|
if (nt == NODE_WHILE) {
|
|
pos = genjmp2_0(s, OP_JMPNOT, cursp(), NOVAL);
|
|
}
|
|
else {
|
|
pos = genjmp2_0(s, OP_JMPIF, cursp(), NOVAL);
|
|
}
|
|
lp->pc1 = new_label(s);
|
|
genop_0(s, OP_NOP); /* for redo */
|
|
codegen(s, tree->cdr, NOVAL);
|
|
genjmp(s, OP_JMP, lp->pc0);
|
|
dispatch(s, pos);
|
|
loop_pop(s, val);
|
|
}
|
|
break;
|
|
|
|
case NODE_FOR:
|
|
for_body(s, tree);
|
|
if (val) push();
|
|
break;
|
|
|
|
case NODE_CASE:
|
|
{
|
|
int head = 0;
|
|
uint32_t pos1, pos2, pos3, tmp;
|
|
node *n;
|
|
|
|
pos3 = JMPLINK_START;
|
|
if (tree->car) {
|
|
head = cursp();
|
|
codegen(s, tree->car, VAL);
|
|
}
|
|
tree = tree->cdr;
|
|
while (tree) {
|
|
n = tree->car->car;
|
|
pos1 = pos2 = JMPLINK_START;
|
|
while (n) {
|
|
codegen(s, n->car, VAL);
|
|
if (head) {
|
|
gen_move(s, cursp(), head, 0);
|
|
push(); push(); pop(); pop(); pop();
|
|
if (nint(n->car->car) == NODE_SPLAT) {
|
|
genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_SYM_2(s->mrb, __case_eqq)), 1);
|
|
}
|
|
else {
|
|
genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_OPSYM_2(s->mrb, eqq)), 1);
|
|
}
|
|
}
|
|
else {
|
|
pop();
|
|
}
|
|
tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, !head);
|
|
pos2 = tmp;
|
|
n = n->cdr;
|
|
}
|
|
if (tree->car->car) {
|
|
pos1 = genjmp_0(s, OP_JMP);
|
|
dispatch_linked(s, pos2);
|
|
}
|
|
codegen(s, tree->car->cdr, val);
|
|
if (val) pop();
|
|
tmp = genjmp(s, OP_JMP, pos3);
|
|
pos3 = tmp;
|
|
dispatch(s, pos1);
|
|
tree = tree->cdr;
|
|
}
|
|
if (val) {
|
|
uint32_t pos = cursp();
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
if (pos3 != JMPLINK_START) dispatch_linked(s, pos3);
|
|
if (head) pop();
|
|
if (cursp() != pos) {
|
|
gen_move(s, cursp(), pos, 0);
|
|
}
|
|
push();
|
|
}
|
|
else {
|
|
if (pos3 != JMPLINK_START) {
|
|
dispatch_linked(s, pos3);
|
|
}
|
|
if (head) {
|
|
pop();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_SCOPE:
|
|
scope_body(s, tree, NOVAL);
|
|
break;
|
|
|
|
case NODE_CALL:
|
|
case NODE_FCALL:
|
|
gen_call(s, tree, val, 0);
|
|
break;
|
|
case NODE_SCALL:
|
|
gen_call(s, tree, val, 1);
|
|
break;
|
|
|
|
case NODE_DOT2:
|
|
codegen(s, tree->car, val);
|
|
codegen(s, tree->cdr, val);
|
|
if (val) {
|
|
pop(); pop();
|
|
genop_1(s, OP_RANGE_INC, cursp());
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_DOT3:
|
|
codegen(s, tree->car, val);
|
|
codegen(s, tree->cdr, val);
|
|
if (val) {
|
|
pop(); pop();
|
|
genop_1(s, OP_RANGE_EXC, cursp());
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_COLON2:
|
|
{
|
|
int sym = new_sym(s, nsym(tree->cdr));
|
|
|
|
codegen(s, tree->car, VAL);
|
|
pop();
|
|
genop_2(s, OP_GETMCNST, cursp(), sym);
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_COLON3:
|
|
{
|
|
int sym = new_sym(s, nsym(tree));
|
|
|
|
genop_1(s, OP_OCLASS, cursp());
|
|
genop_2(s, OP_GETMCNST, cursp(), sym);
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_ARRAY:
|
|
{
|
|
int n;
|
|
|
|
n = gen_values(s, tree, val, 0);
|
|
if (val) {
|
|
if (n >= 0) {
|
|
pop_n(n);
|
|
genop_2(s, OP_ARRAY, cursp(), n);
|
|
}
|
|
push();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_HASH:
|
|
case NODE_KW_HASH:
|
|
{
|
|
int nk = gen_hash(s, tree, val, GEN_LIT_ARY_MAX);
|
|
if (val && nk >= 0) {
|
|
pop_n(nk*2);
|
|
genop_2(s, OP_HASH, cursp(), nk);
|
|
push();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_SPLAT:
|
|
codegen(s, tree, val);
|
|
break;
|
|
|
|
case NODE_ASGN:
|
|
gen_assignment(s, tree->car, tree->cdr, 0, val);
|
|
break;
|
|
|
|
case NODE_MASGN:
|
|
{
|
|
int len = 0, n = 0, post = 0;
|
|
node *t = tree->cdr, *p;
|
|
int rhs = cursp();
|
|
|
|
if (!val && nint(t->car) == NODE_ARRAY && t->cdr && nosplat(t->cdr)) {
|
|
/* fixed rhs */
|
|
t = t->cdr;
|
|
while (t) {
|
|
codegen(s, t->car, VAL);
|
|
len++;
|
|
t = t->cdr;
|
|
}
|
|
tree = tree->car;
|
|
if (tree->car) { /* pre */
|
|
t = tree->car;
|
|
n = 0;
|
|
while (t) {
|
|
if (n < len) {
|
|
gen_assignment(s, t->car, NULL, rhs+n, NOVAL);
|
|
n++;
|
|
}
|
|
else {
|
|
genop_1(s, OP_LOADNIL, rhs+n);
|
|
gen_assignment(s, t->car, NULL, rhs+n, NOVAL);
|
|
}
|
|
t = t->cdr;
|
|
}
|
|
}
|
|
t = tree->cdr;
|
|
if (t) {
|
|
if (t->cdr) { /* post count */
|
|
p = t->cdr->car;
|
|
while (p) {
|
|
post++;
|
|
p = p->cdr;
|
|
}
|
|
}
|
|
if (t->car) { /* rest (len - pre - post) */
|
|
int rn;
|
|
|
|
if (len < post + n) {
|
|
rn = 0;
|
|
}
|
|
else {
|
|
rn = len - post - n;
|
|
}
|
|
if (cursp() == rhs+n) {
|
|
genop_2(s, OP_ARRAY, cursp(), rn);
|
|
}
|
|
else {
|
|
genop_3(s, OP_ARRAY2, cursp(), rhs+n, rn);
|
|
}
|
|
gen_assignment(s, t->car, NULL, cursp(), NOVAL);
|
|
n += rn;
|
|
}
|
|
if (t->cdr && t->cdr->car) {
|
|
t = t->cdr->car;
|
|
while (t) {
|
|
if (n<len) {
|
|
gen_assignment(s, t->car, NULL, rhs+n, NOVAL);
|
|
}
|
|
else {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
gen_assignment(s, t->car, NULL, cursp(), NOVAL);
|
|
}
|
|
t = t->cdr;
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
pop_n(len);
|
|
}
|
|
else {
|
|
/* variable rhs */
|
|
codegen(s, t, VAL);
|
|
gen_massignment(s, tree->car, rhs, val);
|
|
if (!val) {
|
|
pop();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_OP_ASGN:
|
|
{
|
|
mrb_sym sym = nsym(tree->cdr->car);
|
|
mrb_int len;
|
|
const char *name = mrb_sym_name_len(s->mrb, sym, &len);
|
|
int idx, callargs = -1, vsp = -1;
|
|
|
|
if ((len == 2 && name[0] == '|' && name[1] == '|') &&
|
|
(nint(tree->car->car) == NODE_CONST ||
|
|
nint(tree->car->car) == NODE_CVAR)) {
|
|
int catch_entry, begin, end;
|
|
int noexc, exc;
|
|
struct loopinfo *lp;
|
|
|
|
lp = loop_push(s, LOOP_BEGIN);
|
|
lp->pc0 = new_label(s);
|
|
catch_entry = catch_handler_new(s);
|
|
begin = s->pc;
|
|
exc = cursp();
|
|
codegen(s, tree->car, VAL);
|
|
end = s->pc;
|
|
noexc = genjmp_0(s, OP_JMP);
|
|
lp->type = LOOP_RESCUE;
|
|
catch_handler_set(s, catch_entry, MRB_CATCH_RESCUE, begin, end, s->pc);
|
|
genop_1(s, OP_EXCEPT, exc);
|
|
genop_1(s, OP_LOADF, exc);
|
|
dispatch(s, noexc);
|
|
loop_pop(s, NOVAL);
|
|
}
|
|
else if (nint(tree->car->car) == NODE_CALL) {
|
|
node *n = tree->car->cdr;
|
|
int base, i, nargs = 0;
|
|
callargs = 0;
|
|
|
|
if (val) {
|
|
vsp = cursp();
|
|
push();
|
|
}
|
|
codegen(s, n->car, VAL); /* receiver */
|
|
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, 13);
|
|
if (nargs >= 0) {
|
|
callargs = nargs;
|
|
}
|
|
else { /* varargs */
|
|
push();
|
|
nargs = 1;
|
|
callargs = CALL_MAXARGS;
|
|
}
|
|
}
|
|
/* copy receiver and arguments */
|
|
gen_move(s, cursp(), base, 1);
|
|
for (i=0; i<nargs; i++) {
|
|
gen_move(s, cursp()+i+1, base+i+1, 1);
|
|
}
|
|
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 {
|
|
codegen(s, tree->car, VAL);
|
|
}
|
|
if (len == 2 &&
|
|
((name[0] == '|' && name[1] == '|') ||
|
|
(name[0] == '&' && name[1] == '&'))) {
|
|
uint32_t pos;
|
|
|
|
pop();
|
|
if (val) {
|
|
if (vsp >= 0) {
|
|
gen_move(s, vsp, cursp(), 1);
|
|
}
|
|
pos = genjmp2_0(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), val);
|
|
}
|
|
else {
|
|
pos = genjmp2_0(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), val);
|
|
}
|
|
codegen(s, tree->cdr->cdr->car, VAL);
|
|
pop();
|
|
if (val && vsp >= 0) {
|
|
gen_move(s, vsp, cursp(), 1);
|
|
}
|
|
if (nint(tree->car->car) == NODE_CALL) {
|
|
if (callargs == CALL_MAXARGS) {
|
|
pop();
|
|
genop_2(s, OP_ARYPUSH, cursp(), 1);
|
|
}
|
|
else {
|
|
pop_n(callargs);
|
|
callargs++;
|
|
}
|
|
pop();
|
|
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, NULL, cursp(), val);
|
|
}
|
|
dispatch(s, pos);
|
|
goto exit;
|
|
}
|
|
codegen(s, tree->cdr->cdr->car, VAL);
|
|
push(); pop();
|
|
pop(); pop();
|
|
|
|
if (len == 1 && name[0] == '+') {
|
|
gen_addsub(s, OP_ADD, cursp());
|
|
}
|
|
else if (len == 1 && name[0] == '-') {
|
|
gen_addsub(s, OP_SUB, cursp());
|
|
}
|
|
else if (len == 1 && name[0] == '*') {
|
|
genop_1(s, OP_MUL, cursp());
|
|
}
|
|
else if (len == 1 && name[0] == '/') {
|
|
genop_1(s, OP_DIV, cursp());
|
|
}
|
|
else if (len == 1 && name[0] == '<') {
|
|
genop_1(s, OP_LT, cursp());
|
|
}
|
|
else if (len == 2 && name[0] == '<' && name[1] == '=') {
|
|
genop_1(s, OP_LE, cursp());
|
|
}
|
|
else if (len == 1 && name[0] == '>') {
|
|
genop_1(s, OP_GT, cursp());
|
|
}
|
|
else if (len == 2 && name[0] == '>' && name[1] == '=') {
|
|
genop_1(s, OP_GE, cursp());
|
|
}
|
|
else {
|
|
idx = new_sym(s, sym);
|
|
genop_3(s, OP_SEND, cursp(), idx, 1);
|
|
}
|
|
if (callargs < 0) {
|
|
gen_assignment(s, tree->car, NULL, cursp(), val);
|
|
}
|
|
else {
|
|
if (val && vsp >= 0) {
|
|
gen_move(s, vsp, cursp(), 0);
|
|
}
|
|
if (callargs == CALL_MAXARGS) {
|
|
pop();
|
|
genop_2(s, OP_ARYPUSH, cursp(), 1);
|
|
}
|
|
else {
|
|
pop_n(callargs);
|
|
callargs++;
|
|
}
|
|
pop();
|
|
idx = new_sym(s, attrsym(s,nsym(tree->car->cdr->cdr->car)));
|
|
genop_3(s, OP_SEND, cursp(), idx, callargs);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_SUPER:
|
|
{
|
|
codegen_scope *s2 = s;
|
|
int lv = 0;
|
|
int n = 0, nk = 0, st = 0;
|
|
|
|
push();
|
|
while (!s2->mscope) {
|
|
lv++;
|
|
s2 = s2->prev;
|
|
if (!s2) break;
|
|
}
|
|
if (tree) {
|
|
node *args = tree->car;
|
|
if (args) {
|
|
st = n = gen_values(s, args, VAL, 14);
|
|
if (n < 0) {
|
|
st = 1; n = 15;
|
|
push();
|
|
}
|
|
}
|
|
/* keyword arguments */
|
|
if (tree->cdr->car) {
|
|
nk = gen_hash(s, tree->cdr->car->cdr, VAL, 14);
|
|
if (nk < 0) {st++; nk = 15;}
|
|
else st += nk*2;
|
|
n |= nk<<4;
|
|
}
|
|
/* block arguments */
|
|
if (tree->cdr->cdr) {
|
|
codegen(s, tree->cdr->cdr, VAL);
|
|
}
|
|
else if (s2) gen_blkmove(s, s2->ainfo, lv);
|
|
else {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
}
|
|
else {
|
|
if (s2) gen_blkmove(s, s2->ainfo, lv);
|
|
else {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
}
|
|
st++;
|
|
pop_n(st+1);
|
|
genop_2(s, OP_SUPER, cursp(), n);
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_ZSUPER:
|
|
{
|
|
codegen_scope *s2 = s;
|
|
int lv = 0;
|
|
uint16_t ainfo = 0;
|
|
int n = CALL_MAXARGS;
|
|
int sp = cursp();
|
|
|
|
push(); /* room for receiver */
|
|
while (!s2->mscope) {
|
|
lv++;
|
|
s2 = s2->prev;
|
|
if (!s2) break;
|
|
}
|
|
if (s2 && s2->ainfo > 0) {
|
|
ainfo = s2->ainfo;
|
|
}
|
|
if (lv > 0xf) codegen_error(s, "too deep nesting");
|
|
if (ainfo > 0) {
|
|
genop_2S(s, OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf));
|
|
push(); push(); push(); /* ARGARY pushes 3 values at most */
|
|
pop(); pop(); pop();
|
|
/* keyword arguments */
|
|
if (ainfo & 0x1) {
|
|
n |= CALL_MAXARGS<<4;
|
|
push();
|
|
}
|
|
/* block argument */
|
|
if (tree && tree->cdr && tree->cdr->cdr) {
|
|
push();
|
|
codegen(s, tree->cdr->cdr, VAL);
|
|
}
|
|
}
|
|
else {
|
|
/* block argument */
|
|
if (tree && tree->cdr && tree->cdr->cdr) {
|
|
codegen(s, tree->cdr->cdr, VAL);
|
|
}
|
|
else if (s2) {
|
|
gen_blkmove(s, 0, lv);
|
|
}
|
|
else {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
}
|
|
n = 0;
|
|
}
|
|
s->sp = sp;
|
|
genop_2(s, OP_SUPER, cursp(), n);
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_RETURN:
|
|
if (tree) {
|
|
gen_retval(s, tree);
|
|
}
|
|
else {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
}
|
|
if (s->loop) {
|
|
gen_return(s, OP_RETURN_BLK, cursp());
|
|
}
|
|
else {
|
|
gen_return(s, OP_RETURN, cursp());
|
|
}
|
|
if (val) push();
|
|
break;
|
|
|
|
case NODE_YIELD:
|
|
{
|
|
codegen_scope *s2 = s;
|
|
int lv = 0, ainfo = -1;
|
|
int n = 0, nk = 0, sendv = 0;
|
|
|
|
while (!s2->mscope) {
|
|
lv++;
|
|
s2 = s2->prev;
|
|
if (!s2) break;
|
|
}
|
|
if (s2) {
|
|
ainfo = (int)s2->ainfo;
|
|
}
|
|
if (ainfo < 0) codegen_error(s, "invalid yield (SyntaxError)");
|
|
if (lv > 0xf) codegen_error(s, "too deep nesting");
|
|
push();
|
|
if (tree) {
|
|
if (tree->car) {
|
|
n = gen_values(s, tree->car, VAL, 14);
|
|
if (n < 0) {
|
|
n = sendv = 1;
|
|
push();
|
|
}
|
|
}
|
|
|
|
if (tree->cdr->car) {
|
|
nk = gen_hash(s, tree->cdr->car->cdr, VAL, 14);
|
|
if (nk < 0) {
|
|
nk = 15;
|
|
}
|
|
}
|
|
}
|
|
push();pop(); /* space for a block */
|
|
pop_n(n + (nk == 15 ? 1 : nk * 2) + 1);
|
|
genop_2S(s, OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf));
|
|
if (sendv) n = CALL_MAXARGS;
|
|
genop_3(s, OP_SEND, cursp(), new_sym(s, MRB_SYM_2(s->mrb, call)), n|(nk<<4));
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_BREAK:
|
|
loop_break(s, tree);
|
|
if (val) push();
|
|
break;
|
|
|
|
case NODE_NEXT:
|
|
if (!s->loop) {
|
|
raise_error(s, "unexpected next");
|
|
}
|
|
else if (s->loop->type == LOOP_NORMAL) {
|
|
codegen(s, tree, NOVAL);
|
|
genjmp(s, OP_JMPUW, s->loop->pc0);
|
|
}
|
|
else {
|
|
if (tree) {
|
|
codegen(s, tree, VAL);
|
|
pop();
|
|
}
|
|
else {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
}
|
|
gen_return(s, OP_RETURN, cursp());
|
|
}
|
|
if (val) push();
|
|
break;
|
|
|
|
case NODE_REDO:
|
|
for (const struct loopinfo *lp = s->loop; ; lp = lp->prev) {
|
|
if (!lp) {
|
|
raise_error(s, "unexpected redo");
|
|
break;
|
|
}
|
|
if (lp->type != LOOP_BEGIN && lp->type != LOOP_RESCUE) {
|
|
genjmp(s, OP_JMPUW, lp->pc1);
|
|
break;
|
|
}
|
|
}
|
|
if (val) push();
|
|
break;
|
|
|
|
case NODE_RETRY:
|
|
{
|
|
const struct loopinfo *lp = s->loop;
|
|
|
|
while (lp && lp->type != LOOP_RESCUE) {
|
|
lp = lp->prev;
|
|
}
|
|
if (!lp) {
|
|
raise_error(s, "unexpected retry");
|
|
break;
|
|
}
|
|
else {
|
|
genjmp(s, OP_JMPUW, lp->pc0);
|
|
}
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_LVAR:
|
|
if (val) {
|
|
int idx = lv_idx(s, nsym(tree));
|
|
|
|
if (idx > 0) {
|
|
gen_move(s, cursp(), idx, val);
|
|
}
|
|
else {
|
|
gen_getupvar(s, cursp(), nsym(tree));
|
|
}
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_NVAR:
|
|
if (val) {
|
|
int idx = nint(tree);
|
|
|
|
gen_move(s, cursp(), idx, val);
|
|
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_GVAR:
|
|
{
|
|
int sym = new_sym(s, nsym(tree));
|
|
|
|
genop_2(s, OP_GETGV, cursp(), sym);
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_IVAR:
|
|
{
|
|
int sym = new_sym(s, nsym(tree));
|
|
|
|
genop_2(s, OP_GETIV, cursp(), sym);
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_CVAR:
|
|
{
|
|
int sym = new_sym(s, nsym(tree));
|
|
|
|
genop_2(s, OP_GETCV, cursp(), sym);
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_CONST:
|
|
{
|
|
int sym = new_sym(s, nsym(tree));
|
|
|
|
genop_2(s, OP_GETCONST, cursp(), sym);
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_BACK_REF:
|
|
if (val) {
|
|
char buf[] = {'$', nchar(tree)};
|
|
int sym = new_sym(s, mrb_intern(s->mrb, buf, sizeof(buf)));
|
|
|
|
genop_2(s, OP_GETGV, cursp(), sym);
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_NTH_REF:
|
|
if (val) {
|
|
mrb_state *mrb = s->mrb;
|
|
mrb_value str;
|
|
int sym;
|
|
|
|
str = mrb_format(mrb, "$%d", nint(tree));
|
|
sym = new_sym(s, mrb_intern_str(mrb, str));
|
|
genop_2(s, OP_GETGV, cursp(), sym);
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_ARG:
|
|
/* should not happen */
|
|
break;
|
|
|
|
case NODE_BLOCK_ARG:
|
|
if (!tree) {
|
|
int idx = lv_idx(s, MRB_OPSYM_2(s->mrb, and));
|
|
|
|
if (idx == 0) {
|
|
gen_getupvar(s, cursp(), MRB_OPSYM_2(s->mrb, and));
|
|
}
|
|
else {
|
|
gen_move(s, cursp(), idx, val);
|
|
}
|
|
if (val) push();
|
|
}
|
|
else {
|
|
codegen(s, tree, val);
|
|
}
|
|
break;
|
|
|
|
case NODE_INT:
|
|
if (val) {
|
|
char *p = (char*)tree->car;
|
|
int base = nint(tree->cdr->car);
|
|
mrb_int i;
|
|
mrb_bool overflow;
|
|
|
|
i = readint(s, p, base, FALSE, &overflow);
|
|
if (overflow) {
|
|
int off = new_litbint(s, p, base);
|
|
genop_2(s, OP_LOADL, cursp(), off);
|
|
}
|
|
else {
|
|
gen_int(s, cursp(), i);
|
|
}
|
|
push();
|
|
}
|
|
break;
|
|
|
|
#ifndef MRB_NO_FLOAT
|
|
case NODE_FLOAT:
|
|
if (val) {
|
|
char *p = (char*)tree;
|
|
double f;
|
|
mrb_read_float(p, NULL, &f);
|
|
int off = new_lit_float(s, (mrb_float)f);
|
|
|
|
genop_2(s, OP_LOADL, cursp(), off);
|
|
push();
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case NODE_NEGATE:
|
|
{
|
|
nt = nint(tree->car);
|
|
switch (nt) {
|
|
#ifndef MRB_NO_FLOAT
|
|
case NODE_FLOAT:
|
|
if (val) {
|
|
char *p = (char*)tree->cdr;
|
|
double f;
|
|
mrb_read_float(p, NULL, &f);
|
|
int off = new_lit_float(s, (mrb_float)-f);
|
|
|
|
genop_2(s, OP_LOADL, cursp(), off);
|
|
push();
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case NODE_INT:
|
|
if (val) {
|
|
char *p = (char*)tree->cdr->car;
|
|
int base = nint(tree->cdr->cdr->car);
|
|
mrb_int i;
|
|
mrb_bool overflow;
|
|
|
|
i = readint(s, p, base, TRUE, &overflow);
|
|
if (overflow) {
|
|
base = -base;
|
|
int off = new_litbint(s, p, base);
|
|
genop_2(s, OP_LOADL, cursp(), off);
|
|
}
|
|
else {
|
|
gen_int(s, cursp(), i);
|
|
}
|
|
push();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
codegen(s, tree, VAL);
|
|
pop();
|
|
push_n(2);pop_n(2); /* space for receiver&block */
|
|
mrb_sym minus = MRB_OPSYM_2(s->mrb, minus);
|
|
if (!gen_uniop(s, minus, cursp())) {
|
|
genop_3(s, OP_SEND, cursp(), new_sym(s, minus), 0);
|
|
}
|
|
if (val) push();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_STR:
|
|
if (val) {
|
|
char *p = (char*)tree->car;
|
|
mrb_int len = nint(tree->cdr);
|
|
int off = new_lit_str(s, p, len);
|
|
|
|
genop_2(s, OP_STRING, cursp(), off);
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_HEREDOC:
|
|
tree = ((struct mrb_parser_heredoc_info*)tree)->doc;
|
|
/* fall through */
|
|
case NODE_DSTR:
|
|
if (val) {
|
|
node *n = tree;
|
|
|
|
if (!n) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
break;
|
|
}
|
|
codegen(s, n->car, VAL);
|
|
n = n->cdr;
|
|
while (n) {
|
|
codegen(s, n->car, VAL);
|
|
pop(); pop();
|
|
genop_1(s, OP_STRCAT, cursp());
|
|
push();
|
|
n = n->cdr;
|
|
}
|
|
}
|
|
else {
|
|
node *n = tree;
|
|
|
|
while (n) {
|
|
if (nint(n->car->car) != NODE_STR) {
|
|
codegen(s, n->car, NOVAL);
|
|
}
|
|
n = n->cdr;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_WORDS:
|
|
gen_literal_array(s, tree, FALSE, val);
|
|
break;
|
|
|
|
case NODE_SYMBOLS:
|
|
gen_literal_array(s, tree, TRUE, val);
|
|
break;
|
|
|
|
case NODE_DXSTR:
|
|
{
|
|
node *n;
|
|
int sym = new_sym(s, MRB_SYM_2(s->mrb, Kernel));
|
|
|
|
push();
|
|
codegen(s, tree->car, VAL);
|
|
n = tree->cdr;
|
|
while (n) {
|
|
if (nint(n->car->car) == NODE_XSTR) {
|
|
n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR;
|
|
mrb_assert(!n->cdr); /* must be the end */
|
|
}
|
|
codegen(s, n->car, VAL);
|
|
pop(); pop();
|
|
genop_1(s, OP_STRCAT, cursp());
|
|
push();
|
|
n = n->cdr;
|
|
}
|
|
push(); /* for block */
|
|
pop_n(3);
|
|
sym = new_sym(s, MRB_OPSYM_2(s->mrb, tick)); /* ` */
|
|
genop_3(s, OP_SSEND, cursp(), sym, 1);
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_XSTR:
|
|
{
|
|
char *p = (char*)tree->car;
|
|
mrb_int len = nint(tree->cdr);
|
|
int off = new_lit_str(s, p, len);
|
|
int sym;
|
|
|
|
push();
|
|
genop_2(s, OP_STRING, cursp(), off);
|
|
push(); push();
|
|
pop_n(3);
|
|
sym = new_sym(s, MRB_OPSYM_2(s->mrb, tick)); /* ` */
|
|
genop_3(s, OP_SSEND, cursp(), sym, 1);
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_REGX:
|
|
if (val) {
|
|
char *p1 = (char*)tree->car;
|
|
char *p2 = (char*)tree->cdr->car;
|
|
char *p3 = (char*)tree->cdr->cdr;
|
|
int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS));
|
|
int off = new_lit_cstr(s, p1);
|
|
int argc = 1;
|
|
|
|
genop_1(s, OP_OCLASS, cursp());
|
|
genop_2(s, OP_GETMCNST, cursp(), sym);
|
|
push();
|
|
genop_2(s, OP_STRING, cursp(), off);
|
|
push();
|
|
if (p2 || p3) {
|
|
if (p2) { /* opt */
|
|
off = new_lit_cstr(s, p2);
|
|
genop_2(s, OP_STRING, cursp(), off);
|
|
}
|
|
else {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
}
|
|
push();
|
|
argc++;
|
|
if (p3) { /* enc */
|
|
off = new_lit_str(s, p3, 1);
|
|
genop_2(s, OP_STRING, cursp(), off);
|
|
push();
|
|
argc++;
|
|
}
|
|
}
|
|
push(); /* space for a block */
|
|
pop_n(argc+2);
|
|
sym = new_sym(s, MRB_SYM_2(s->mrb, compile));
|
|
genop_3(s, OP_SEND, cursp(), sym, argc);
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_DREGX:
|
|
if (val) {
|
|
node *n = tree->car;
|
|
int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS));
|
|
int argc = 1;
|
|
int off;
|
|
char *p;
|
|
|
|
genop_1(s, OP_OCLASS, cursp());
|
|
genop_2(s, OP_GETMCNST, cursp(), sym);
|
|
push();
|
|
codegen(s, n->car, VAL);
|
|
n = n->cdr;
|
|
while (n) {
|
|
codegen(s, n->car, VAL);
|
|
pop(); pop();
|
|
genop_1(s, OP_STRCAT, cursp());
|
|
push();
|
|
n = n->cdr;
|
|
}
|
|
n = tree->cdr->cdr;
|
|
if (n->car) { /* tail */
|
|
p = (char*)n->car;
|
|
off = new_lit_cstr(s, p);
|
|
codegen(s, tree->car, VAL);
|
|
genop_2(s, OP_STRING, cursp(), off);
|
|
pop();
|
|
genop_1(s, OP_STRCAT, cursp());
|
|
push();
|
|
}
|
|
if (n->cdr->car) { /* opt */
|
|
char *p2 = (char*)n->cdr->car;
|
|
off = new_lit_cstr(s, p2);
|
|
genop_2(s, OP_STRING, cursp(), off);
|
|
push();
|
|
argc++;
|
|
}
|
|
if (n->cdr->cdr) { /* enc */
|
|
char *p2 = (char*)n->cdr->cdr;
|
|
off = new_lit_cstr(s, p2);
|
|
genop_2(s, OP_STRING, cursp(), off);
|
|
push();
|
|
argc++;
|
|
}
|
|
push(); /* space for a block */
|
|
pop_n(argc+2);
|
|
sym = new_sym(s, MRB_SYM_2(s->mrb, compile));
|
|
genop_3(s, OP_SEND, cursp(), sym, argc);
|
|
push();
|
|
}
|
|
else {
|
|
node *n = tree->car;
|
|
|
|
while (n) {
|
|
if (nint(n->car->car) != NODE_STR) {
|
|
codegen(s, n->car, NOVAL);
|
|
}
|
|
n = n->cdr;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_SYM:
|
|
if (val) {
|
|
int sym = new_sym(s, nsym(tree));
|
|
|
|
genop_2(s, OP_LOADSYM, cursp(), sym);
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_DSYM:
|
|
codegen(s, tree, val);
|
|
if (val) {
|
|
gen_intern(s);
|
|
}
|
|
break;
|
|
|
|
case NODE_SELF:
|
|
if (val) {
|
|
genop_1(s, OP_LOADSELF, cursp());
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_NIL:
|
|
if (val) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_TRUE:
|
|
if (val) {
|
|
genop_1(s, OP_LOADT, cursp());
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_FALSE:
|
|
if (val) {
|
|
genop_1(s, OP_LOADF, cursp());
|
|
push();
|
|
}
|
|
break;
|
|
|
|
case NODE_ALIAS:
|
|
{
|
|
int a = new_sym(s, nsym(tree->car));
|
|
int b = new_sym(s, nsym(tree->cdr));
|
|
|
|
genop_2(s, OP_ALIAS, a, b);
|
|
if (val) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_UNDEF:
|
|
{
|
|
node *t = tree;
|
|
|
|
while (t) {
|
|
int symbol = new_sym(s, nsym(t->car));
|
|
genop_1(s, OP_UNDEF, symbol);
|
|
t = t->cdr;
|
|
}
|
|
if (val) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_CLASS:
|
|
{
|
|
int idx;
|
|
node *body;
|
|
|
|
if (tree->car->car == (node*)0) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
else if (tree->car->car == (node*)1) {
|
|
genop_1(s, OP_OCLASS, cursp());
|
|
push();
|
|
}
|
|
else {
|
|
codegen(s, tree->car->car, VAL);
|
|
}
|
|
if (tree->cdr->car) {
|
|
codegen(s, tree->cdr->car, VAL);
|
|
}
|
|
else {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
pop(); pop();
|
|
idx = new_sym(s, nsym(tree->car->cdr));
|
|
genop_2(s, OP_CLASS, cursp(), idx);
|
|
body = tree->cdr->cdr->car;
|
|
if (nint(body->cdr->car) == NODE_BEGIN && body->cdr->cdr == NULL) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
}
|
|
else {
|
|
idx = scope_body(s, body, val);
|
|
genop_2(s, OP_EXEC, cursp(), idx);
|
|
}
|
|
if (val) {
|
|
push();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_MODULE:
|
|
{
|
|
int idx;
|
|
|
|
if (tree->car->car == (node*)0) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
push();
|
|
}
|
|
else if (tree->car->car == (node*)1) {
|
|
genop_1(s, OP_OCLASS, cursp());
|
|
push();
|
|
}
|
|
else {
|
|
codegen(s, tree->car->car, VAL);
|
|
}
|
|
pop();
|
|
idx = new_sym(s, nsym(tree->car->cdr));
|
|
genop_2(s, OP_MODULE, cursp(), idx);
|
|
if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN &&
|
|
tree->cdr->car->cdr->cdr == NULL) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
}
|
|
else {
|
|
idx = scope_body(s, tree->cdr->car, val);
|
|
genop_2(s, OP_EXEC, cursp(), idx);
|
|
}
|
|
if (val) {
|
|
push();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_SCLASS:
|
|
{
|
|
int idx;
|
|
|
|
codegen(s, tree->car, VAL);
|
|
pop();
|
|
genop_1(s, OP_SCLASS, cursp());
|
|
if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN &&
|
|
tree->cdr->car->cdr->cdr == NULL) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
}
|
|
else {
|
|
idx = scope_body(s, tree->cdr->car, val);
|
|
genop_2(s, OP_EXEC, cursp(), idx);
|
|
}
|
|
if (val) {
|
|
push();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NODE_DEF:
|
|
{
|
|
int sym = new_sym(s, nsym(tree->car));
|
|
int idx = lambda_body(s, tree->cdr, 0);
|
|
|
|
genop_1(s, OP_TCLASS, cursp());
|
|
push();
|
|
genop_2(s, OP_METHOD, cursp(), idx);
|
|
push(); pop();
|
|
pop();
|
|
genop_2(s, OP_DEF, cursp(), sym);
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_SDEF:
|
|
{
|
|
node *recv = tree->car;
|
|
int sym = new_sym(s, nsym(tree->cdr->car));
|
|
int idx = lambda_body(s, tree->cdr->cdr, 0);
|
|
|
|
codegen(s, recv, VAL);
|
|
pop();
|
|
genop_1(s, OP_SCLASS, cursp());
|
|
push();
|
|
genop_2(s, OP_METHOD, cursp(), idx);
|
|
push(); pop();
|
|
pop();
|
|
genop_2(s, OP_DEF, cursp(), sym);
|
|
if (val) push();
|
|
}
|
|
break;
|
|
|
|
case NODE_POSTEXE:
|
|
codegen(s, tree, NOVAL);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
exit:
|
|
s->rlev = rlev;
|
|
}
|
|
|
|
static void
|
|
scope_add_irep(codegen_scope *s)
|
|
{
|
|
mrb_irep *irep;
|
|
codegen_scope *prev = s->prev;
|
|
|
|
if (prev->irep == NULL) {
|
|
irep = mrb_add_irep(s->mrb);
|
|
prev->irep = s->irep = irep;
|
|
return;
|
|
}
|
|
else {
|
|
if (prev->irep->rlen == UINT16_MAX) {
|
|
codegen_error(s, "too many nested blocks/methods");
|
|
}
|
|
s->irep = irep = mrb_add_irep(s->mrb);
|
|
if (prev->irep->rlen == prev->rcapa) {
|
|
prev->rcapa *= 2;
|
|
prev->reps = (mrb_irep**)mrbc_realloc(prev->reps, sizeof(mrb_irep*)*prev->rcapa);
|
|
}
|
|
prev->reps[prev->irep->rlen] = irep;
|
|
prev->irep->rlen++;
|
|
}
|
|
}
|
|
|
|
static codegen_scope*
|
|
scope_new(mrb_state *mrb, codegen_scope *prev, node *nlv)
|
|
{
|
|
static const codegen_scope codegen_scope_zero = { 0 };
|
|
mempool *pool = mempool_open();
|
|
codegen_scope *s = (codegen_scope*)mempool_alloc(pool, sizeof(codegen_scope));
|
|
|
|
if (!s) {
|
|
if (prev)
|
|
codegen_error(prev, "unexpected scope");
|
|
return NULL;
|
|
}
|
|
*s = codegen_scope_zero;
|
|
s->mrb = mrb;
|
|
s->mpool = pool;
|
|
if (!prev) return s;
|
|
s->prev = prev;
|
|
s->ainfo = 0;
|
|
s->mscope = 0;
|
|
|
|
scope_add_irep(s);
|
|
|
|
s->rcapa = 8;
|
|
s->reps = (mrb_irep**)mrbc_malloc(sizeof(mrb_irep*)*s->rcapa);
|
|
|
|
s->icapa = 1024;
|
|
s->iseq = (mrb_code*)mrbc_malloc(sizeof(mrb_code)*s->icapa);
|
|
|
|
s->pcapa = 32;
|
|
s->pool = (mrb_irep_pool*)mrbc_malloc(sizeof(mrb_irep_pool)*s->pcapa);
|
|
|
|
s->scapa = 256;
|
|
s->syms = (mrb_sym*)mrbc_malloc(sizeof(mrb_sym)*s->scapa);
|
|
|
|
s->lv = nlv;
|
|
s->sp += node_len(nlv)+1; /* add self */
|
|
s->nlocals = s->nregs = s->sp;
|
|
if (nlv) {
|
|
mrb_sym *lv;
|
|
node *n = nlv;
|
|
size_t i = 0;
|
|
|
|
s->irep->lv = lv = (mrb_sym*)mrbc_malloc(sizeof(mrb_sym)*(s->nlocals-1));
|
|
for (i=0, n=nlv; n; i++,n=n->cdr) {
|
|
lv[i] = lv_name(n);
|
|
}
|
|
mrb_assert(i + 1 == s->nlocals);
|
|
}
|
|
s->ai = mrb_gc_arena_save(mrb);
|
|
|
|
s->filename_sym = prev->filename_sym;
|
|
if (s->filename_sym) {
|
|
s->lines = (uint16_t*)mrbc_malloc(sizeof(short)*s->icapa);
|
|
}
|
|
s->lineno = prev->lineno;
|
|
|
|
/* debug setting */
|
|
s->debug_start_pos = 0;
|
|
if (s->filename_sym) {
|
|
mrb_debug_info_alloc(mrb, s->irep);
|
|
}
|
|
else {
|
|
s->irep->debug_info = NULL;
|
|
}
|
|
s->parser = prev->parser;
|
|
s->filename_index = prev->filename_index;
|
|
|
|
s->rlev = prev->rlev+1;
|
|
|
|
return s;
|
|
}
|
|
|
|
static void
|
|
scope_finish(codegen_scope *s)
|
|
{
|
|
mrb_state *mrb = s->mrb;
|
|
mrb_irep *irep = s->irep;
|
|
|
|
if (s->nlocals > 0xff) {
|
|
codegen_error(s, "too many local variables");
|
|
}
|
|
irep->flags = 0;
|
|
if (s->iseq) {
|
|
size_t catchsize = sizeof(struct mrb_irep_catch_handler) * irep->clen;
|
|
irep->iseq = (const mrb_code*)mrbc_realloc(s->iseq, sizeof(mrb_code)*s->pc + catchsize);
|
|
irep->ilen = s->pc;
|
|
if (irep->clen > 0) {
|
|
memcpy((void*)(irep->iseq + irep->ilen), s->catch_table, catchsize);
|
|
}
|
|
}
|
|
else {
|
|
irep->clen = 0;
|
|
}
|
|
mrbc_free(s->catch_table);
|
|
s->catch_table = NULL;
|
|
irep->pool = (const mrb_irep_pool*)mrbc_realloc(s->pool, sizeof(mrb_irep_pool)*irep->plen);
|
|
irep->syms = (const mrb_sym*)mrbc_realloc(s->syms, sizeof(mrb_sym)*irep->slen);
|
|
irep->reps = (const mrb_irep**)mrbc_realloc(s->reps, sizeof(mrb_irep*)*irep->rlen);
|
|
if (s->filename_sym) {
|
|
mrb_sym fname = mrb_parser_get_filename(s->parser, s->filename_index);
|
|
const char *filename = mrb_sym_name_len(s->mrb, fname, NULL);
|
|
|
|
mrb_debug_info_append_file(s->mrb, s->irep->debug_info,
|
|
filename, s->lines, s->debug_start_pos, s->pc);
|
|
}
|
|
mrb_free(s->mrb, s->lines);
|
|
|
|
irep->nlocals = s->nlocals;
|
|
irep->nregs = s->nregs;
|
|
|
|
mrb_gc_arena_restore(mrb, s->ai);
|
|
mempool_close(s->mpool);
|
|
}
|
|
|
|
static struct loopinfo*
|
|
loop_push(codegen_scope *s, enum looptype t)
|
|
{
|
|
struct loopinfo *p = (struct loopinfo*)codegen_palloc(s, sizeof(struct loopinfo));
|
|
|
|
p->type = t;
|
|
p->pc0 = p->pc1 = p->pc2 = JMPLINK_START;
|
|
p->prev = s->loop;
|
|
p->reg = cursp();
|
|
s->loop = p;
|
|
|
|
return p;
|
|
}
|
|
|
|
static void
|
|
loop_break(codegen_scope *s, node *tree)
|
|
{
|
|
if (!s->loop) {
|
|
codegen(s, tree, NOVAL);
|
|
raise_error(s, "unexpected break");
|
|
}
|
|
else {
|
|
struct loopinfo *loop;
|
|
|
|
|
|
loop = s->loop;
|
|
if (tree) {
|
|
if (loop->reg < 0) {
|
|
codegen(s, tree, NOVAL);
|
|
}
|
|
else {
|
|
gen_retval(s, tree);
|
|
}
|
|
}
|
|
while (loop) {
|
|
if (loop->type == LOOP_BEGIN) {
|
|
loop = loop->prev;
|
|
}
|
|
else if (loop->type == LOOP_RESCUE) {
|
|
loop = loop->prev;
|
|
}
|
|
else{
|
|
break;
|
|
}
|
|
}
|
|
if (!loop) {
|
|
raise_error(s, "unexpected break");
|
|
return;
|
|
}
|
|
|
|
if (loop->type == LOOP_NORMAL) {
|
|
int tmp;
|
|
|
|
if (loop->reg >= 0) {
|
|
if (tree) {
|
|
gen_move(s, loop->reg, cursp(), 0);
|
|
}
|
|
else {
|
|
genop_1(s, OP_LOADNIL, loop->reg);
|
|
}
|
|
}
|
|
tmp = genjmp(s, OP_JMPUW, loop->pc2);
|
|
loop->pc2 = tmp;
|
|
}
|
|
else {
|
|
if (!tree) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
}
|
|
gen_return(s, OP_BREAK, cursp());
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
loop_pop(codegen_scope *s, int val)
|
|
{
|
|
if (val) {
|
|
genop_1(s, OP_LOADNIL, cursp());
|
|
}
|
|
dispatch_linked(s, s->loop->pc2);
|
|
s->loop = s->loop->prev;
|
|
if (val) push();
|
|
}
|
|
|
|
static int
|
|
catch_handler_new(codegen_scope *s)
|
|
{
|
|
size_t newsize = sizeof(struct mrb_irep_catch_handler) * (s->irep->clen + 1);
|
|
s->catch_table = (struct mrb_irep_catch_handler*)mrbc_realloc((void*)s->catch_table, newsize);
|
|
return s->irep->clen++;
|
|
}
|
|
|
|
static void
|
|
catch_handler_set(codegen_scope *s, int ent, enum mrb_catch_type type, uint32_t begin, uint32_t end, uint32_t target)
|
|
{
|
|
struct mrb_irep_catch_handler *e;
|
|
|
|
mrb_assert(ent >= 0 && ent < s->irep->clen);
|
|
|
|
e = &s->catch_table[ent];
|
|
uint8_to_bin(type, &e->type);
|
|
mrb_irep_catch_handler_pack(begin, e->begin);
|
|
mrb_irep_catch_handler_pack(end, e->end);
|
|
mrb_irep_catch_handler_pack(target, e->target);
|
|
}
|
|
|
|
static struct RProc*
|
|
generate_code(mrb_state *mrb, parser_state *p, int val)
|
|
{
|
|
codegen_scope *scope = scope_new(mrb, 0, 0);
|
|
struct mrb_jmpbuf *prev_jmp = mrb->jmp;
|
|
struct mrb_jmpbuf jmpbuf;
|
|
struct RProc *proc;
|
|
|
|
mrb->jmp = &jmpbuf;
|
|
|
|
scope->mrb = mrb;
|
|
scope->parser = p;
|
|
scope->filename_sym = p->filename_sym;
|
|
scope->filename_index = p->current_filename_index;
|
|
|
|
MRB_TRY(mrb->jmp) {
|
|
/* prepare irep */
|
|
codegen(scope, p->tree, val);
|
|
proc = mrb_proc_new(mrb, scope->irep);
|
|
mrb_irep_decref(mrb, scope->irep);
|
|
mempool_close(scope->mpool);
|
|
proc->c = NULL;
|
|
if (mrb->c->cibase && mrb->c->cibase->proc == proc->upper) {
|
|
proc->upper = NULL;
|
|
}
|
|
mrb->jmp = prev_jmp;
|
|
return proc;
|
|
}
|
|
MRB_CATCH(mrb->jmp) {
|
|
mrb_irep_decref(mrb, scope->irep);
|
|
mempool_close(scope->mpool);
|
|
mrb->jmp = prev_jmp;
|
|
return NULL;
|
|
}
|
|
MRB_END_EXC(mrb->jmp);
|
|
}
|
|
|
|
MRB_API struct RProc*
|
|
mrb_generate_code(mrb_state *mrb, parser_state *p)
|
|
{
|
|
return generate_code(mrb, p, VAL);
|
|
}
|