add emitter stats to recc

This commit is contained in:
Anthony Pesch 2016-12-16 23:35:26 -08:00
parent 37b9f52b6c
commit f67f5c5b12
26 changed files with 302 additions and 214 deletions

View File

@ -214,9 +214,10 @@ set(REDREAM_SOURCES
src/jit/ir/passes/dead_code_elimination_pass.c
src/jit/ir/passes/expression_simplification_pass.c
src/jit/ir/passes/load_store_elimination_pass.c
src/jit/ir/passes/pass_stat.c
src/jit/ir/passes/register_allocation_pass.c
src/jit/emit_stats.c
src/jit/jit.c
src/jit/pass_stats.c
src/sys/exception_handler.c
src/sys/filesystem.c
src/sys/memory.c

View File

@ -228,11 +228,16 @@ static void sh4_debug_menu(struct device *dev, struct nk_context *ctx) {
jit_invalidate_blocks(sh4->jit);
}
int dumping = jit_is_dumping(sh4->jit);
if (!dumping && nk_button_label(ctx, "start dumping blocks")) {
jit_toggle_dumping(sh4->jit);
} else if (dumping && nk_button_label(ctx, "stop dumping blocks")) {
jit_toggle_dumping(sh4->jit);
struct jit *jit = sh4->jit;
if (!jit->dump_blocks) {
if (nk_button_label(ctx, "start dumping blocks")) {
jit->dump_blocks = 1;
jit_invalidate_blocks(jit);
}
} else {
if (nk_button_label(ctx, "stop dumping blocks")) {
jit->dump_blocks = 0;
}
}
nk_menu_end(ctx);

View File

@ -22,8 +22,7 @@ struct jit_backend {
void (*reset)(struct jit_backend *base);
void *(*assemble_code)(struct jit_backend *base, struct ir *ir, int *size);
void (*disassemble_code)(struct jit_backend *base, const uint8_t *code,
int size, int dump, int *num_instrs);
void (*dump_code)(struct jit_backend *base, const uint8_t *code, int size);
int (*handle_exception)(struct jit_backend *base, struct exception *ex);
};

View File

@ -9,6 +9,7 @@ extern "C" {
#include "jit/backend/jit_backend.h"
#include "jit/backend/x64/x64_backend.h"
#include "jit/backend/x64/x64_disassembler.h"
#include "jit/emit_stats.h"
#include "jit/ir/ir.h"
#include "jit/jit.h"
#include "sys/exception_handler.h"
@ -154,11 +155,14 @@ struct x64_backend {
int stack_size;
Xbyak::CodeGenerator *codegen;
csh capstone_handle;
Xbyak::Label xmm_const[NUM_XMM_CONST];
void (*load_thunk[16])();
void (*store_thunk)();
int num_temps;
csh capstone_handle;
struct ir_instr *last_op;
void *last_op_begin;
};
static const Xbyak::Reg x64_backend_register(struct x64_backend *backend,
@ -279,6 +283,12 @@ static const Xbyak::Address x64_backend_xmm_constant(
return e.ptr[e.rip + backend->xmm_const[c]];
}
static void x64_backend_label_name(char *name, size_t size,
struct ir_value *v) {
/* all ir labels are local labels */
snprintf(name, size, ".%s", v->str);
}
static int x64_backend_can_encode_imm(const struct ir_value *v) {
if (!ir_is_constant(v)) {
return 0;
@ -287,11 +297,34 @@ static int x64_backend_can_encode_imm(const struct ir_value *v) {
return v->type <= VALUE_I32;
}
void *x64_backend_emit(struct x64_backend *backend, struct ir *ir, int *size) {
PROF_ENTER("cpu", "x64_backend_emit");
static void x64_backend_emit_stats(struct x64_backend *backend,
struct ir_instr *op) {
if (!backend->base.jit->emit_stats) {
return;
}
void *curr = backend->codegen->getCurr<void *>();
if (backend->last_op) {
cs_insn *insns;
size_t size = (int)((uint8_t *)curr - (uint8_t *)backend->last_op_begin);
size_t count =
cs_disasm(backend->capstone_handle, (uint8_t *)backend->last_op_begin,
size, 0, 0, &insns);
cs_free(insns, count);
const char *desc = backend->last_op->arg[0]->str;
emit_stats_add(desc, count);
}
backend->last_op = op;
backend->last_op_begin = curr;
}
static void *x64_backend_emit(struct x64_backend *backend, struct ir *ir,
int *size) {
auto &e = *backend->codegen;
void *fn = (void *)backend->codegen->getCurr();
void *code = (void *)backend->codegen->getCurr();
CHECK_LT(ir->locals_size, backend->stack_size);
@ -301,7 +334,12 @@ void *x64_backend_emit(struct x64_backend *backend, struct ir *ir, int *size) {
x64_emit_cb emit = x64_backend_emitters[instr->op];
CHECK_NOTNULL(emit);
// reset temp count used by GetRegister
/* track stats for each guest op */
if (instr->op == OP_DEBUG_INFO) {
x64_backend_emit_stats(backend, instr);
}
/* reset temp count used by x64_backend_get_register */
backend->num_temps = 0;
emit(backend, *backend->codegen, instr);
@ -309,11 +347,12 @@ void *x64_backend_emit(struct x64_backend *backend, struct ir *ir, int *size) {
e.outLocalLabel();
*size = (int)((uint8_t *)backend->codegen->getCurr() - (uint8_t *)fn);
*size = (int)((uint8_t *)backend->codegen->getCurr() - (uint8_t *)code);
PROF_LEAVE();
/* flush stats for last op */
x64_backend_emit_stats(backend, NULL);
return fn;
return code;
}
static void x64_backend_emit_thunks(struct x64_backend *backend) {
@ -364,55 +403,6 @@ static void x64_backend_emit_constants(struct x64_backend *backend) {
e.dq(INT64_C(0x8000000000000000));
}
static void x64_backend_reset(struct jit_backend *base) {
struct x64_backend *backend = container_of(base, struct x64_backend, base);
backend->codegen->reset();
x64_backend_emit_thunks(backend);
x64_backend_emit_constants(backend);
}
static void *x64_backend_assemble_code(struct jit_backend *base, struct ir *ir,
int *size) {
struct x64_backend *backend = container_of(base, struct x64_backend, base);
// try to generate the x64 code. if the code buffer overflows let the backend
// know so it can reset the cache and try again
void *fn = NULL;
try {
fn = x64_backend_emit(backend, ir, size);
} catch (const Xbyak::Error &e) {
if (e != Xbyak::ERR_CODE_IS_TOO_BIG) {
LOG_FATAL("X64 codegen failure, %s", e.what());
}
}
return fn;
}
static void x64_backend_disassemble_code(struct jit_backend *base,
const uint8_t *code, int size,
int dump, int *num_instrs) {
struct x64_backend *backend = container_of(base, struct x64_backend, base);
cs_insn *insns;
size_t count = cs_disasm(backend->capstone_handle, code, size, 0, 0, &insns);
CHECK(count);
*num_instrs = count;
if (dump) {
for (size_t i = 0; i < count; i++) {
cs_insn &insn = insns[i];
LOG_INFO("0x%" PRIx64 ":\t%s\t\t%s", insn.address, insn.mnemonic,
insn.op_str);
}
}
cs_free(insns, count);
}
static int x64_backend_handle_exception(struct jit_backend *base,
struct exception *ex) {
struct x64_backend *backend = container_of(base, struct x64_backend, base);
@ -499,10 +489,53 @@ static int x64_backend_handle_exception(struct jit_backend *base,
return 1;
}
static void x64_backend_label_name(char *name, size_t size,
struct ir_value *v) {
/* all ir labels are local labels */
snprintf(name, size, ".%s", v->str);
static void x64_backend_dump_code(struct jit_backend *base, const uint8_t *code,
int size) {
struct x64_backend *backend = container_of(base, struct x64_backend, base);
cs_insn *insns;
size_t count = cs_disasm(backend->capstone_handle, code, size, 0, 0, &insns);
CHECK(count);
for (size_t i = 0; i < count; i++) {
cs_insn &insn = insns[i];
LOG_INFO("0x%" PRIx64 ":\t%s\t\t%s", insn.address, insn.mnemonic,
insn.op_str);
}
cs_free(insns, count);
}
static void *x64_backend_assemble_code(struct jit_backend *base, struct ir *ir,
int *size) {
PROF_ENTER("cpu", "x64_backend_assemble_code");
struct x64_backend *backend = container_of(base, struct x64_backend, base);
// try to generate the x64 code. if the code buffer overflows let the backend
// know so it can reset the cache and try again
void *code = NULL;
try {
code = x64_backend_emit(backend, ir, size);
} catch (const Xbyak::Error &e) {
if (e != Xbyak::ERR_CODE_IS_TOO_BIG) {
LOG_FATAL("X64 codegen failure, %s", e.what());
}
}
PROF_LEAVE();
return code;
}
static void x64_backend_reset(struct jit_backend *base) {
struct x64_backend *backend = container_of(base, struct x64_backend, base);
backend->codegen->reset();
x64_backend_emit_thunks(backend);
x64_backend_emit_constants(backend);
}
EMITTER(LOAD) {
@ -1545,7 +1578,7 @@ EMITTER(LABEL) {
}
EMITTER(BRANCH) {
if (instr->arg[0]->type == VALUE_LABEL) {
if (instr->arg[0]->type == VALUE_STRING) {
char name[128];
x64_backend_label_name(name, sizeof(name), instr->arg[0]);
e.jmp(name);
@ -1560,7 +1593,7 @@ EMITTER(BRANCH_FALSE) {
e.test(cond, cond);
if (instr->arg[0]->type == VALUE_LABEL) {
if (instr->arg[0]->type == VALUE_STRING) {
char name[128];
x64_backend_label_name(name, sizeof(name), instr->arg[0]);
e.jz(name);
@ -1575,7 +1608,7 @@ EMITTER(BRANCH_TRUE) {
e.test(cond, cond);
if (instr->arg[0]->type == VALUE_LABEL) {
if (instr->arg[0]->type == VALUE_STRING) {
char name[128];
x64_backend_label_name(name, sizeof(name), instr->arg[0]);
e.jnz(name);
@ -1634,7 +1667,7 @@ struct jit_backend *x64_backend_create(struct jit *jit, void *code,
backend->base.num_registers = array_size(x64_registers);
backend->base.reset = &x64_backend_reset;
backend->base.assemble_code = &x64_backend_assemble_code;
backend->base.disassemble_code = &x64_backend_disassemble_code;
backend->base.dump_code = &x64_backend_dump_code;
backend->base.handle_exception = &x64_backend_handle_exception;
backend->code = code;

69
src/jit/emit_stats.c Normal file
View File

@ -0,0 +1,69 @@
#include "jit/emit_stats.h"
#include "core/assert.h"
#include "core/math.h"
#include "core/string.h"
#define EMIT_STATS_MAX 512
struct emit_stat {
char name[32];
int n;
int count;
};
static struct emit_stat stats[EMIT_STATS_MAX];
static int num_stats;
static struct emit_stat *emit_stats_demand(const char *name) {
/* this is terribly slow, should really be using a hashtable,
but this is currently only used when debugging with recc */
for (int i = 0; i < num_stats; i++) {
struct emit_stat *stat = &stats[i];
if (!strcmp(stat->name, name)) {
return stat;
}
}
CHECK_LT(num_stats, EMIT_STATS_MAX);
struct emit_stat *stat = &stats[num_stats++];
strncpy(stat->name, name, sizeof(stat->name));
return stat;
}
void emit_stats_add(const char *name, int count) {
struct emit_stat *stat = emit_stats_demand(name);
stat->count += count;
stat->n++;
}
void emit_stats_dump() {
LOG_INFO("===-----------------------------------------------------===");
LOG_INFO("Emit stats");
LOG_INFO("===-----------------------------------------------------===");
int w = 5; /* TOTAL */
int total_n = 0;
int total_count = 0;
float total_avg = 0.0f;
for (int i = 0; i < num_stats; i++) {
struct emit_stat *stat = &stats[i];
int l = (int)strlen(stat->name);
w = MAX(l, w);
total_n += stat->n;
total_count += stat->count;
}
if (total_n) {
total_avg = total_count / (float)total_n;
}
for (int i = 0; i < num_stats; i++) {
struct emit_stat *stat = &stats[i];
float avg = stat->n ? stat->count / (float)stat->n : 0.0f;
LOG_INFO("%*s, %9d, %9.2f", w, stat->name, stat->n, avg);
}
LOG_INFO("%*s, %9d, %9.2f", w, "TOTAL", total_n, total_avg);
LOG_INFO("");
}

7
src/jit/emit_stats.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef EMIT_STATS_H
#define EMIT_STATS_H
void emit_stats_add(const char *name, int count);
void emit_stats_dump();
#endif

View File

@ -11,8 +11,8 @@ static void armv3_frontend_translate_code(struct jit_frontend *base,
frontend->translate(frontend->data, addr, ir, flags);
}
static void armv3_frontend_disassemble_code(struct jit_frontend *base,
uint32_t addr, int size) {
static void armv3_frontend_dump_code(struct jit_frontend *base, uint32_t addr,
int size) {
struct armv3_frontend *frontend = (struct armv3_frontend *)base;
struct jit_guest *guest = frontend->jit->guest;
@ -33,7 +33,7 @@ struct jit_frontend *armv3_frontend_create(struct jit *jit) {
frontend->jit = jit;
frontend->translate_code = &armv3_frontend_translate_code;
frontend->disassemble_code = &armv3_frontend_disassemble_code;
frontend->dump_code = &armv3_frontend_dump_code;
return (struct jit_frontend *)frontend;
}

View File

@ -11,7 +11,7 @@ struct jit_frontend {
struct jit *jit;
void (*translate_code)(struct jit_frontend *base, uint32_t addr,
struct ir *ir, int fastmem, int *size);
void (*disassemble_code)(struct jit_frontend *base, uint32_t addr, int size);
void (*dump_code)(struct jit_frontend *base, uint32_t addr, int size);
};
#endif

View File

@ -2,26 +2,14 @@
#include "core/assert.h"
#include "core/string.h"
struct sh4_opdef {
enum sh4_op op;
const char *desc;
const char *sig;
int cycles;
int flags;
uint16_t opcode_mask;
uint16_t imm_mask, imm_shift;
uint16_t disp_mask, disp_shift;
uint16_t rm_mask, rm_shift;
uint16_t rn_mask, rn_shift;
};
static struct sh4_opdef *sh4_opdef_lookup[UINT16_MAX];
static struct sh4_opdef s_opdefs[NUM_SH4_OPS] = {
struct sh4_opdef sh4_opdefs[NUM_SH4_OPS] = {
#define SH4_INSTR(name, desc, sig, cycles, flags) \
{SH4_OP_##name, desc, #sig, cycles, flags, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{SH4_OP_##name, #name, desc, #sig, cycles, flags, 0, 0, 0, 0, 0, 0, 0, 0, 0},
#include "jit/frontend/sh4/sh4_instr.inc"
#undef SH4_INSTR
};
static struct sh4_opdef *s_opdef_lookup[UINT16_MAX];
static void sh4_arg_mask(const char *instr_code, char c, uint16_t *mask,
uint16_t *shift) {
@ -58,7 +46,7 @@ static void sh4_init_opdefs() {
* from signatures
*/
for (int i = 1 /* skip SH4_OP_INVALID */; i < NUM_SH4_OPS; i++) {
struct sh4_opdef *def = &s_opdefs[i];
struct sh4_opdef *def = &sh4_opdefs[i];
sh4_arg_mask(def->sig, 'i', &def->imm_mask, &def->imm_shift);
sh4_arg_mask(def->sig, 'd', &def->disp_mask, &def->disp_shift);
@ -75,12 +63,12 @@ static void sh4_init_opdefs() {
uint16_t value = w + x + y + z;
for (int i = 1 /* skip SH4_OP_INVALID */; i < NUM_SH4_OPS; i++) {
struct sh4_opdef *def = &s_opdefs[i];
struct sh4_opdef *def = &sh4_opdefs[i];
uint16_t arg_mask =
def->imm_mask | def->disp_mask | def->rm_mask | def->rn_mask;
if ((value & ~arg_mask) == def->opcode_mask) {
s_opdef_lookup[value] = def;
sh4_opdef_lookup[value] = def;
break;
}
}
@ -93,7 +81,7 @@ static void sh4_init_opdefs() {
int sh4_disasm(struct sh4_instr *i) {
sh4_init_opdefs();
struct sh4_opdef *def = s_opdef_lookup[i->opcode];
struct sh4_opdef *def = sh4_opdef_lookup[i->opcode];
if (!def) {
i->op = SH4_OP_INVALID;
@ -125,7 +113,7 @@ void sh4_format(const struct sh4_instr *i, char *buffer, size_t buffer_size) {
uint32_t pcmask;
/* copy initial formatted description */
snprintf(buffer, buffer_size, "%08x %s", i->addr, s_opdefs[i->op].desc);
snprintf(buffer, buffer_size, "%08x %s", i->addr, sh4_opdefs[i->op].desc);
/* used by mov operators with displacements */
if (strnstr(buffer, ".b", buffer_size)) {

View File

@ -22,6 +22,20 @@ enum sh4_op {
NUM_SH4_OPS,
};
struct sh4_opdef {
enum sh4_op op;
const char *name;
const char *desc;
const char *sig;
int cycles;
int flags;
uint16_t opcode_mask;
uint16_t imm_mask, imm_shift;
uint16_t disp_mask, disp_shift;
uint16_t rm_mask, rm_shift;
uint16_t rn_mask, rn_shift;
};
struct sh4_instr {
uint32_t addr;
uint16_t opcode;
@ -35,6 +49,8 @@ struct sh4_instr {
uint16_t imm;
};
extern struct sh4_opdef sh4_opdefs[NUM_SH4_OPS];
int sh4_disasm(struct sh4_instr *i);
void sh4_format(const struct sh4_instr *i, char *buffer, size_t buffer_size);

View File

@ -18,8 +18,8 @@ static void sh4_frontend_translate_code(struct jit_frontend *base,
PROF_LEAVE();
}
static void sh4_frontend_disassemble_code(struct jit_frontend *base,
uint32_t addr, int size) {
static void sh4_frontend_dump_code(struct jit_frontend *base, uint32_t addr,
int size) {
struct sh4_frontend *frontend = (struct sh4_frontend *)base;
struct jit_guest *guest = frontend->jit->guest;
@ -57,7 +57,7 @@ struct jit_frontend *sh4_frontend_create(struct jit *jit) {
frontend->jit = jit;
frontend->translate_code = &sh4_frontend_translate_code;
frontend->disassemble_code = &sh4_frontend_disassemble_code;
frontend->dump_code = &sh4_frontend_dump_code;
return (struct jit_frontend *)frontend;
}

View File

@ -200,7 +200,7 @@ static void ir_branch_guest(struct sh4_frontend *frontend, struct ir *ir,
static void ir_branch_false_guest(struct sh4_frontend *frontend, struct ir *ir,
struct ir_value *addr,
struct ir_value *cond) {
struct ir_value *skip = ir_alloc_label(ir, "skip_%p", addr);
struct ir_value *skip = ir_alloc_str(ir, "skip_%p", addr);
ir_branch_true(ir, skip, cond);
ir_store_context(ir, offsetof(struct sh4_ctx, pc), addr);
ir_label(ir, skip);
@ -208,7 +208,7 @@ static void ir_branch_false_guest(struct sh4_frontend *frontend, struct ir *ir,
static void ir_branch_true_guest(struct sh4_frontend *frontend, struct ir *ir,
struct ir_value *addr, struct ir_value *cond) {
struct ir_value *skip = ir_alloc_label(ir, "skip_%p", addr);
struct ir_value *skip = ir_alloc_str(ir, "skip_%p", addr);
ir_branch_false(ir, skip, cond);
ir_store_context(ir, offsetof(struct sh4_ctx, pc), addr);
ir_label(ir, skip);
@ -217,8 +217,11 @@ static void ir_branch_true_guest(struct sh4_frontend *frontend, struct ir *ir,
void sh4_emit_instr(struct sh4_frontend *frontend, struct ir *ir, int flags,
const struct sh4_instr *instr,
const struct sh4_instr *delay) {
/* emit debug info op for recc metrics */
ir_debug_info(ir, instr->addr, ir_alloc_i16(ir, instr->opcode));
/* emit extra debug info for recc */
if (frontend->jit->dump_blocks) {
const char *name = sh4_opdefs[instr->op].name;
ir_debug_info(ir, name, instr->addr, instr->opcode);
}
(emit_callbacks[instr->op])(frontend, ir, flags, instr, delay);
}
@ -1503,7 +1506,7 @@ EMITTER(PREF) {
struct ir_value *addr = load_gpr(i->Rn, VALUE_I32);
struct ir_value *cond = ir_lshr(ir, addr, ir_alloc_i32(ir, 26));
cond = ir_cmp_ne(ir, cond, ir_alloc_i32(ir, 0x38));
struct ir_value *skip = ir_alloc_label(ir, "skip_%p", cond);
struct ir_value *skip = ir_alloc_str(ir, "skip_%p", cond);
ir_branch_true(ir, skip, cond);
struct ir_value *data = ir_alloc_ptr(ir, frontend->data);

View File

@ -144,17 +144,13 @@ struct ir_value *ir_alloc_f64(struct ir *ir, double c) {
return v;
}
struct ir_value *ir_alloc_ptr(struct ir *ir, void *c) {
return ir_alloc_i64(ir, (uint64_t)c);
}
struct ir_value *ir_alloc_label(struct ir *ir, const char *format, ...) {
struct ir_value *ir_alloc_str(struct ir *ir, const char *format, ...) {
struct ir_value *v = ir_calloc(ir, sizeof(struct ir_value));
v->type = VALUE_LABEL;
v->type = VALUE_STRING;
v->reg = NO_REGISTER;
/* format the label name */
/* format the string */
va_list args;
va_start(args, format);
@ -170,6 +166,10 @@ struct ir_value *ir_alloc_label(struct ir *ir, const char *format, ...) {
return v;
}
struct ir_value *ir_alloc_ptr(struct ir *ir, void *c) {
return ir_alloc_i64(ir, (uint64_t)c);
}
struct ir_local *ir_alloc_local(struct ir *ir, enum ir_type type) {
/* align local to natural size */
int type_size = ir_type_size(type);
@ -765,14 +765,14 @@ struct ir_value *ir_lshd(struct ir *ir, struct ir_value *a,
}
void ir_label(struct ir *ir, struct ir_value *lbl) {
CHECK(lbl->type == VALUE_LABEL);
CHECK(lbl->type == VALUE_STRING);
struct ir_instr *instr = ir_append_instr(ir, OP_LABEL, VALUE_V);
ir_set_arg0(ir, instr, lbl);
}
void ir_branch(struct ir *ir, struct ir_value *dst) {
CHECK(dst->type == VALUE_LABEL || dst->type == VALUE_I64);
CHECK(dst->type == VALUE_STRING || dst->type == VALUE_I64);
struct ir_instr *instr = ir_append_instr(ir, OP_BRANCH, VALUE_V);
ir_set_arg0(ir, instr, dst);
@ -780,7 +780,7 @@ void ir_branch(struct ir *ir, struct ir_value *dst) {
void ir_branch_false(struct ir *ir, struct ir_value *dst,
struct ir_value *cond) {
CHECK(dst->type == VALUE_LABEL || dst->type == VALUE_I64);
CHECK(dst->type == VALUE_STRING || dst->type == VALUE_I64);
struct ir_instr *instr = ir_append_instr(ir, OP_BRANCH_FALSE, VALUE_V);
ir_set_arg0(ir, instr, dst);
@ -789,7 +789,7 @@ void ir_branch_false(struct ir *ir, struct ir_value *dst,
void ir_branch_true(struct ir *ir, struct ir_value *dst,
struct ir_value *cond) {
CHECK(dst->type == VALUE_LABEL || dst->type == VALUE_I64);
CHECK(dst->type == VALUE_STRING || dst->type == VALUE_I64);
struct ir_instr *instr = ir_append_instr(ir, OP_BRANCH_TRUE, VALUE_V);
ir_set_arg0(ir, instr, dst);
@ -830,8 +830,10 @@ void ir_call_fallback(struct ir *ir, void *fallback, uint32_t addr,
ir_set_arg2(ir, instr, ir_alloc_i32(ir, raw_instr));
}
void ir_debug_info(struct ir *ir, uint32_t addr, struct ir_value *data) {
void ir_debug_info(struct ir *ir, const char *desc, uint32_t addr,
uint32_t raw_instr) {
struct ir_instr *instr = ir_append_instr(ir, OP_DEBUG_INFO, VALUE_V);
ir_set_arg0(ir, instr, ir_alloc_i32(ir, addr));
ir_set_arg1(ir, instr, data);
ir_set_arg0(ir, instr, ir_alloc_str(ir, desc));
ir_set_arg1(ir, instr, ir_alloc_i32(ir, addr));
ir_set_arg2(ir, instr, ir_alloc_i32(ir, raw_instr));
}

View File

@ -21,7 +21,7 @@ enum ir_type {
VALUE_F32,
VALUE_F64,
VALUE_V128,
VALUE_LABEL,
VALUE_STRING,
VALUE_NUM,
};
@ -192,8 +192,8 @@ struct ir_value *ir_alloc_i32(struct ir *ir, int32_t c);
struct ir_value *ir_alloc_i64(struct ir *ir, int64_t c);
struct ir_value *ir_alloc_f32(struct ir *ir, float c);
struct ir_value *ir_alloc_f64(struct ir *ir, double c);
struct ir_value *ir_alloc_str(struct ir *ir, const char *format, ...);
struct ir_value *ir_alloc_ptr(struct ir *ir, void *c);
struct ir_value *ir_alloc_label(struct ir *ir, const char *format, ...);
struct ir_local *ir_alloc_local(struct ir *ir, enum ir_type type);
struct ir_local *ir_reuse_local(struct ir *ir, struct ir_value *offset,
enum ir_type type);
@ -341,6 +341,7 @@ void ir_call_fallback(struct ir *ir, void *fallback, uint32_t addr,
uint32_t raw_instr);
/* debug */
void ir_debug_info(struct ir *ir, uint32_t addr, struct ir_value *data);
void ir_debug_info(struct ir *ir, const char *desc, uint32_t addr,
uint32_t instr);
#endif

View File

@ -24,7 +24,7 @@ struct ir_parser {
};
static const char *typenames[] = {"", "i8", "i16", "i32", "i64",
"f32", "f64", "v128", "lbl"};
"f32", "f64", "v128", "str"};
static const int num_typenames = sizeof(typenames) / sizeof(typenames[0]);
static char ir_lex_get(struct ir_parser *p) {
@ -216,11 +216,9 @@ int ir_parse_value(struct ir_parser *p, struct ir *ir,
CHECK_NOTNULL(instr);
*value = instr->result;
} else if (ident[0] == '.') {
} else {
/* label reference */
const char *label = &ident[1];
*value = ir_alloc_label(ir, "%s", label);
*value = ir_alloc_str(ir, ident);
}
} else if (p->tok == TOK_INTEGER) {
switch (type) {

View File

@ -25,8 +25,8 @@ static void ir_write_type(enum ir_type type, FILE *output) {
case VALUE_V128:
fprintf(output, "v128");
break;
case VALUE_LABEL:
fprintf(output, "lbl");
case VALUE_STRING:
fprintf(output, "str");
break;
default:
LOG_FATAL("Unexpected value type");
@ -71,8 +71,8 @@ static void ir_write_value(const struct ir_value *value, FILE *output) {
double v = value->f64;
fprintf(output, "0x%" PRIx64, *(uint64_t *)&v);
} break;
case VALUE_LABEL: {
fprintf(output, ".%s", value->str);
case VALUE_STRING: {
fprintf(output, "%s", value->str);
} break;
default:
LOG_FATAL("Unexpected value type");

View File

@ -1,10 +1,10 @@
#include "jit/ir/passes/conversion_elimination_pass.h"
#include "jit/ir/ir.h"
#include "jit/ir/passes/pass_stat.h"
#include "jit/pass_stats.h"
DEFINE_STAT(sext_removed, "Sign extends eliminated");
DEFINE_STAT(zext_removed, "Zero extends eliminated");
DEFINE_STAT(trunc_removed, "Truncations eliminated");
DEFINE_STAT(sext_removed, "sign extends eliminated");
DEFINE_STAT(zext_removed, "zero extends eliminated");
DEFINE_STAT(trunc_removed, "truncations eliminated");
void cve_run(struct ir *ir) {
list_for_each_entry_safe(instr, &ir->instrs, struct ir_instr, it) {

View File

@ -1,8 +1,8 @@
#include "jit/ir/passes/dead_code_elimination_pass.h"
#include "jit/ir/ir.h"
#include "jit/ir/passes/pass_stat.h"
#include "jit/pass_stats.h"
DEFINE_STAT(dead_removed, "Dead instructions eliminated");
DEFINE_STAT(dead_removed, "dead instructions eliminated");
void dce_run(struct ir *ir) {
/* iterate in reverse in order to remove groups of dead instructions that

View File

@ -1,11 +1,11 @@
#include "jit/ir/passes/expression_simplification_pass.h"
#include "jit/ir/ir.h"
#include "jit/ir/passes/pass_stat.h"
#include "jit/pass_stats.h"
DEFINE_STAT(zero_properties_removed, "Zero properties removed");
DEFINE_STAT(zero_identities_removed, "Zero identities removed");
DEFINE_STAT(one_identities_removed, "One identities removed");
DEFINE_STAT(bitwise_identities_removed, "Bitwise identities removed");
DEFINE_STAT(bitwise_identities_removed, "bitwise identities removed");
DEFINE_STAT(zero_properties_removed, "zero properties removed");
DEFINE_STAT(zero_identities_removed, "zero identities removed");
DEFINE_STAT(one_identities_removed, "one identities removed");
void esimp_run(struct ir *ir) {
list_for_each_entry(instr, &ir->instrs, struct ir_instr, it) {

View File

@ -1,9 +1,9 @@
#include "jit/ir/passes/load_store_elimination_pass.h"
#include "jit/ir/ir.h"
#include "jit/ir/passes/pass_stat.h"
#include "jit/pass_stats.h"
DEFINE_STAT(loads_removed, "Context loads eliminated");
DEFINE_STAT(stores_removed, "Context stores eliminated");
DEFINE_STAT(loads_removed, "context loads eliminated");
DEFINE_STAT(stores_removed, "context stores eliminated");
#define MAX_OFFSET 16384

View File

@ -2,10 +2,10 @@
#include "core/mm_heap.h"
#include "jit/backend/jit_backend.h"
#include "jit/ir/ir.h"
#include "jit/ir/passes/pass_stat.h"
#include "jit/pass_stats.h"
DEFINE_STAT(gprs_spilled, "GPRs spilled");
DEFINE_STAT(fprs_spilled, "FPRs spilled");
DEFINE_STAT(gprs_spilled, "gprs spilled");
DEFINE_STAT(fprs_spilled, "fprs spilled");
#define MAX_REGISTERS 32
@ -389,7 +389,7 @@ void ra_run(struct ir *ir, const struct jit_register *registers,
/* only allocate registers for results, assume constants can always be
encoded as immediates or that the backend has registers reserved
for storing the constants */
if (!result || result->type == VALUE_LABEL) {
if (!result) {
continue;
}

View File

@ -319,7 +319,7 @@ void jit_compile_block(struct jit *jit, uint32_t guest_addr) {
&guest_size);
/* dump unoptimized block */
if (jit->dump_compiled_blocks) {
if (jit->dump_blocks) {
jit_dump_block(jit, guest_addr, &ir);
}
@ -353,21 +353,6 @@ void jit_compile_block(struct jit *jit, uint32_t guest_addr) {
PROF_LEAVE();
}
void jit_toggle_dumping(struct jit *jit) {
int enabled = !jit->dump_compiled_blocks;
if (enabled) {
/* invalidate current blocks so they recompile and dump on the next run */
jit_invalidate_blocks(jit);
}
jit->dump_compiled_blocks = enabled;
}
int jit_is_dumping(struct jit *jit) {
return jit->dump_compiled_blocks;
}
static int jit_handle_exception(void *data, struct exception *ex) {
struct jit *jit = data;

View File

@ -45,11 +45,14 @@ struct jit {
struct rb_tree blocks;
struct rb_tree reverse_blocks;
/* debug flag for dumping blocks as they are compiled */
int dump_compiled_blocks;
/* compiled block perf map */
FILE *perf_map;
/* dump ir to application directory as blocks compile */
int dump_blocks;
/* track emitter stats as blocks compile */
int emit_stats;
};
struct jit *jit_create(const char *tag);
@ -58,9 +61,6 @@ void jit_destroy(struct jit *jit);
int jit_init(struct jit *jit, struct jit_guest *guest,
struct jit_frontend *frontend, struct jit_backend *backend);
int jit_is_dumping(struct jit *jit);
void jit_toggle_dumping(struct jit *jit);
void jit_compile_block(struct jit *jit, uint32_t guest_addr);
void jit_add_edge(struct jit *jit, void *code, uint32_t dst);

View File

@ -1,19 +1,19 @@
#include "jit/ir/passes/pass_stat.h"
#include "jit/pass_stats.h"
#include "core/assert.h"
#include "core/math.h"
#include "core/string.h"
static struct list stats;
void pass_stat_register(struct pass_stat *stat) {
void pass_stats_register(struct pass_stat *stat) {
list_add(&stats, &stat->it);
}
void pass_stat_unregister(struct pass_stat *stat) {
void pass_stats_unregister(struct pass_stat *stat) {
list_remove(&stats, &stat->it);
}
void pass_stat_print_all() {
void pass_stats_dump() {
LOG_INFO("===-----------------------------------------------------===");
LOG_INFO("Pass stats");
LOG_INFO("===-----------------------------------------------------===");
@ -27,4 +27,6 @@ void pass_stat_print_all() {
list_for_each_entry(stat, &stats, struct pass_stat, it) {
LOG_INFO("%-*s %d", w, stat->desc, *stat->n);
}
LOG_INFO("");
}

View File

@ -8,10 +8,10 @@
static int STAT_##name; \
static struct pass_stat STAT_T_##name = {#name, desc, &STAT_##name, {0}}; \
CONSTRUCTOR(STAT_REGISTER_##name) { \
pass_stat_register(&STAT_T_##name); \
pass_stats_register(&STAT_T_##name); \
} \
DESTRUCTOR(STAT_UNREGISTER_##name) { \
pass_stat_unregister(&STAT_T_##name); \
pass_stats_unregister(&STAT_T_##name); \
}
struct pass_stat {
@ -21,8 +21,8 @@ struct pass_stat {
struct list_node it;
};
void pass_stat_register(struct pass_stat *stat);
void pass_stat_unregister(struct pass_stat *stat);
void pass_stat_print_all();
void pass_stats_register(struct pass_stat *stat);
void pass_stats_unregister(struct pass_stat *stat);
void pass_stats_dump();
#endif

View File

@ -1,27 +1,24 @@
#include "core/log.h"
#include "core/option.h"
#include "jit/backend/x64/x64_backend.h"
#include "jit/frontend/sh4/sh4_frontend.h"
#include "jit/emit_stats.h"
#include "jit/frontend/sh4/sh4_disasm.h"
#include "jit/ir/ir.h"
#include "jit/ir/passes/conversion_elimination_pass.h"
#include "jit/ir/passes/dead_code_elimination_pass.h"
#include "jit/ir/passes/expression_simplification_pass.h"
#include "jit/ir/passes/load_store_elimination_pass.h"
#include "jit/ir/passes/pass_stat.h"
#include "jit/ir/passes/register_allocation_pass.h"
#include "jit/jit.h"
#include "jit/pass_stats.h"
#include "sys/filesystem.h"
DEFINE_OPTION_INT(help, 0, "Show help");
DEFINE_OPTION_STRING(pass, "lse,cve,esimp,dce,ra",
"Comma-separated list of passes to run");
DEFINE_OPTION_INT(stats, 1, "Print pass stats");
DEFINE_OPTION_INT(print_after_all, 1, "Print IR after each pass");
DEFINE_STAT(guest_instrs, "Guest instructions");
DEFINE_STAT(host_instrs, "Host instructions");
DEFINE_STAT(ir_instrs, "IR instructions");
DEFINE_STAT(ir_instrs_removed, "IR instructions removed");
DEFINE_STAT(ir_instrs_total, "total ir instructions");
DEFINE_STAT(ir_instrs_removed, "removed ir instructions");
static uint8_t ir_buffer[1024 * 1024];
static uint8_t code[1024 * 1024];
@ -70,14 +67,6 @@ static void process_file(struct jit *jit, const char *filename,
/* sanitize absolute addresses in the ir */
sanitize_ir(&ir);
/* calculate number of guest instructions */
int guest_num_instrs = 0;
list_for_each_entry(instr, &ir.instrs, struct ir_instr, it) {
if (instr->op == OP_DEBUG_INFO) {
guest_num_instrs++;
}
}
/* run optimization passes */
char passes[MAX_OPTION_LENGTH];
strncpy(passes, OPTION_pass, sizeof(passes));
@ -101,7 +90,7 @@ static void process_file(struct jit *jit, const char *filename,
}
/* print ir after each pass if requested */
if (!disable_dumps && OPTION_print_after_all) {
if (!disable_dumps) {
LOG_INFO("===-----------------------------------------------------===");
LOG_INFO("IR after %s", name);
LOG_INFO("===-----------------------------------------------------===");
@ -114,14 +103,8 @@ static void process_file(struct jit *jit, const char *filename,
int num_instrs_after = get_num_instrs(&ir);
/* print out the final ir */
if (!disable_dumps && !OPTION_print_after_all) {
ir_write(&ir, stdout);
}
/* assemble backend code */
int host_size = 0;
int host_num_instrs = 0;
uint8_t *host_code = NULL;
jit->backend->reset(jit->backend);
@ -131,17 +114,12 @@ static void process_file(struct jit *jit, const char *filename,
LOG_INFO("===-----------------------------------------------------===");
LOG_INFO("X64 code");
LOG_INFO("===-----------------------------------------------------===");
jit->backend->disassemble_code(jit->backend, host_code, host_size, 1,
&host_num_instrs);
} else {
jit->backend->disassemble_code(jit->backend, host_code, host_size, 0,
&host_num_instrs);
jit->backend->dump_code(jit->backend, host_code, host_size);
LOG_INFO("");
}
/* update stats */
STAT_guest_instrs += guest_num_instrs;
STAT_host_instrs += host_num_instrs;
STAT_ir_instrs += num_instrs_before;
STAT_ir_instrs_total += num_instrs_before;
STAT_ir_instrs_removed += num_instrs_before - num_instrs_after;
}
@ -185,6 +163,7 @@ int main(int argc, char **argv) {
/* initailize jit, stubbing out guest interfaces that are used during
assembly to a valid address */
struct jit *jit = jit_create("recc");
jit->emit_stats = 1;
struct jit_guest guest = {0};
guest.r8 = (void *)code;
@ -205,9 +184,9 @@ int main(int argc, char **argv) {
process_dir(jit, path);
}
if (OPTION_stats) {
pass_stat_print_all();
}
LOG_INFO("");
emit_stats_dump();
pass_stats_dump();
jit_destroy(jit);
x64_backend_destroy(backend);