codegen.c: mrb_prev_pc() to take previous instruction position.

It rescans `s->iseq` so that peephole optimizer can take multiple
previous instructions for constant folding, etc.
This commit is contained in:
Yukihiro "Matz" Matsumoto
2021-07-31 21:06:33 +09:00
parent 4306893971
commit 43999492a7
2 changed files with 132 additions and 79 deletions
+1
View File
@@ -123,6 +123,7 @@ struct mrb_insn_data {
uint16_t a;
uint16_t b;
uint8_t c;
const mrb_code *addr;
};
struct mrb_insn_data mrb_decode_insn(const mrb_code *pc);
+131 -79
View File
@@ -57,7 +57,6 @@ typedef struct scope {
uint16_t sp;
uint32_t pc;
uint32_t lastpc;
uint32_t lastlabel;
int ainfo:15;
mrb_bool mscope:1;
@@ -229,14 +228,12 @@ gen_S(codegen_scope *s, uint16_t i)
static void
genop_0(codegen_scope *s, mrb_code i)
{
s->lastpc = s->pc;
gen_B(s, i);
}
static void
genop_1(codegen_scope *s, mrb_code i, uint16_t a)
{
s->lastpc = s->pc;
if (a > 0xff) {
gen_B(s, OP_EXT1);
gen_B(s, i);
@@ -251,7 +248,6 @@ genop_1(codegen_scope *s, mrb_code i, uint16_t a)
static void
genop_2(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b)
{
s->lastpc = s->pc;
if (a > 0xff && b > 0xff) {
gen_B(s, OP_EXT3);
gen_B(s, i);
@@ -306,7 +302,6 @@ genop_W(codegen_scope *s, mrb_code i, uint32_t a)
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);
@@ -328,6 +323,7 @@ struct mrb_insn_data
mrb_decode_insn(const mrb_code *pc)
{
struct mrb_insn_data data = { 0 };
data.addr = pc;
mrb_code insn = READ_B();
uint16_t a = 0;
uint16_t b = 0;
@@ -374,22 +370,119 @@ mrb_decode_insn(const mrb_code *pc)
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;
while (i<pc) {
uint8_t insn = i[0];
prev_pc = i;
switch (insn) {
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[insn];
break;
}
}
return prev_pc;
}
#define pc_addr(s) &((s)->iseq[(s)->pc])
#define addr_pc(s, addr) (uint32_t)((addr) - s->iseq)
static void
rewind_pc(codegen_scope *s)
{
const mrb_code *pc = mrb_prev_pc(s, pc_addr(s));
s->pc = addr_pc(s, pc);
}
static struct mrb_insn_data
mrb_last_insn(codegen_scope *s)
{
if (s->pc == s->lastpc) {
const mrb_code *pc = mrb_prev_pc(s, pc_addr(s));
if (pc == NULL) {
struct mrb_insn_data data;
data.insn = OP_NOP;
return data;
}
return mrb_decode_insn(&s->iseq[s->lastpc]);
return mrb_decode_insn(pc);
}
static mrb_bool
no_peephole(codegen_scope *s)
{
return no_optimize(s) || s->lastlabel == s->pc || s->pc == 0 || s->pc == s->lastpc;
return no_optimize(s) || s->lastlabel == s->pc || s->pc == 0;
}
#define JMPLINK_START UINT32_MAX
@@ -415,7 +508,6 @@ genjmp(codegen_scope *s, mrb_code i, uint32_t pc)
{
uint32_t pos;
s->lastpc = s->pc;
gen_B(s, i);
pos = s->pc;
gen_jmpdst(s, pc);
@@ -433,12 +525,11 @@ genjmp2(codegen_scope *s, mrb_code i, uint16_t a, uint32_t pc, int val)
struct mrb_insn_data data = mrb_last_insn(s);
if (data.insn == OP_MOVE && data.a == a) {
s->pc = s->lastpc;
rewind_pc(s);
a = data.b;
}
}
s->lastpc = s->pc;
if (a > 0xff) {
gen_B(s, OP_EXT1);
gen_B(s, i);
@@ -477,7 +568,7 @@ gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep)
case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3:
case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7:
if (nopeep || data.a != src || data.a < s->nlocals) goto normal;
s->pc = s->lastpc;
rewind_pc(s);
genop_1(s, data.insn, dst);
break;
case OP_LOADI: case OP_LOADINEG: case OP_LOADI16:
@@ -486,19 +577,19 @@ gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep)
case OP_GETCONST: case OP_STRING:
case OP_LAMBDA: case OP_BLOCK: case OP_METHOD: case OP_BLKPUSH:
if (nopeep || data.a != src || data.a < s->nlocals) goto normal;
s->pc = s->lastpc;
rewind_pc(s);
genop_2(s, data.insn, dst, data.b);
break;
case OP_GETUPVAR:
if (nopeep || data.a != src || data.a < s->nlocals) goto normal;
s->pc = s->lastpc;
rewind_pc(s);
genop_3(s, data.insn, dst, data.b, data.c);
break;
case OP_LOADI32:
if (nopeep || data.a != src || data.a < s->nlocals) goto normal;
else {
uint32_t i = (uint32_t)data.b<<16|data.c;
s->pc = s->lastpc;
rewind_pc(s);
genop_2SS(s, data.insn, dst, i);
}
break;
@@ -533,7 +624,7 @@ gen_setupvar(codegen_scope *s, uint16_t dst, mrb_sym id)
struct mrb_insn_data data = mrb_last_insn(s);
if (!no_peephole(s) && data.insn == OP_MOVE && data.a == dst) {
dst = data.b;
s->pc = s->lastpc;
rewind_pc(s);
}
genop_3(s, OP_SETUPVAR, dst, idx, lv);
}
@@ -548,7 +639,7 @@ gen_return(codegen_scope *s, uint8_t op, uint16_t src)
struct mrb_insn_data data = mrb_last_insn(s);
if (data.insn == OP_MOVE && src == data.a) {
s->pc = s->lastpc;
rewind_pc(s);
genop_1(s, op, data.b);
}
else if (data.insn != OP_RETURN) {
@@ -606,6 +697,8 @@ get_int_operand(codegen_scope *s, struct mrb_insn_data *data, mrb_int *n)
}
}
static void gen_int(codegen_scope *s, uint16_t dst, mrb_int i);
static void
gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst)
{
@@ -622,15 +715,27 @@ gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst)
/* not integer immediate */
goto normal;
}
/* OP_ADDI/OP_SUBI takes upto 16bits */
if (n > INT16_MAX) goto normal;
s->pc = s->lastpc;
if (op == OP_ADD) {
genop_2(s, OP_ADDI, dst, (uint16_t)n);
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 16bits */
if (n > INT16_MAX) goto normal;
rewind_pc(s);
if (op == OP_ADD) {
genop_2(s, OP_ADDI, dst, (uint16_t)n);
}
else {
genop_2(s, OP_SUBI, dst, (uint16_t)n);
}
return;
}
else {
genop_2(s, OP_SUBI, dst, (uint16_t)n);
if (op == OP_SUB) {
if (n == MRB_INT_MIN) goto normal;
n = -n;
}
if (mrb_int_add_overflow(n0, n, &n)) goto normal;
s->pc = addr_pc(s, data0.addr);
gen_int(s, dst, n);
}
}
@@ -860,7 +965,7 @@ gen_setxv(codegen_scope *s, uint8_t op, uint16_t dst, mrb_sym sym)
struct mrb_insn_data data = mrb_last_insn(s);
if (!no_peephole(s) && data.insn == OP_MOVE && data.a == dst) {
dst = data.b;
s->pc = s->lastpc;
rewind_pc(s);
}
genop_2(s, op, dst, idx);
}
@@ -892,7 +997,7 @@ gen_uniop(codegen_scope *s, mrb_sym sym, uint16_t dst)
mrb_int n;
if (get_int_operand(s, &data, &n)) {
s->pc = s->lastpc;
rewind_pc(s);
if (sym == MRB_OPSYM_2(s->mrb, minus)) {
n = -n;
}
@@ -3481,56 +3586,3 @@ mrb_irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
mrb_irep_remove_lv(mrb, (mrb_irep*)irep->reps[i]);
}
}
#undef OPCODE
#define Z 1
#define S 3
#define W 4
#define OPCODE(_,x) x,
/* instruction sizes */
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 */
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 */
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
uint8_t mrb_insn_size3[] = {
#include "mruby/ops.h"
};