added initial expression simplification pass

This commit is contained in:
Anthony Pesch 2016-12-10 13:08:55 -08:00
parent 9b9cde2b6d
commit 2b28a457da
11 changed files with 134 additions and 51 deletions

View File

@ -210,6 +210,7 @@ set(REDREAM_SOURCES
#src/jit/ir/passes/constant_propagation_pass.c
src/jit/ir/passes/conversion_elimination_pass.c
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

View File

@ -72,6 +72,30 @@ void ir_remove_instr(struct ir *ir, struct ir_instr *instr) {
list_remove(&ir->instrs, &instr->it);
}
struct ir_value *ir_alloc_int(struct ir *ir, int64_t c, enum ir_type type) {
struct ir_value *v = ir_calloc(ir, sizeof(struct ir_value));
v->type = type;
switch (type) {
case VALUE_I8:
v->i8 = (int8_t)c;
break;
case VALUE_I16:
v->i16 = (int16_t)c;
break;
case VALUE_I32:
v->i32 = (int32_t)c;
break;
case VALUE_I64:
v->i64 = c;
break;
default:
LOG_FATAL("Unexpected value type");
break;
}
v->reg = NO_REGISTER;
return v;
}
struct ir_value *ir_alloc_i8(struct ir *ir, int8_t c) {
struct ir_value *v = ir_calloc(ir, sizeof(struct ir_value));
v->type = VALUE_I8;

View File

@ -181,6 +181,7 @@ struct ir_instr *ir_append_instr(struct ir *ir, enum ir_op op,
enum ir_type result_type);
void ir_remove_instr(struct ir *ir, struct ir_instr *instr);
struct ir_value *ir_alloc_int(struct ir *ir, int64_t c, enum ir_type type);
struct ir_value *ir_alloc_i8(struct ir *ir, int8_t c);
struct ir_value *ir_alloc_i16(struct ir *ir, int16_t c);
struct ir_value *ir_alloc_i32(struct ir *ir, int32_t c);

View File

@ -2,21 +2,19 @@
#include "jit/ir/ir.h"
#include "jit/ir/passes/pass_stat.h"
DEFINE_STAT(num_sext_removed, "Number of sext eliminated");
DEFINE_STAT(num_zext_removed, "Number of zext eliminated");
DEFINE_STAT(num_trunc_removed, "Number of trunc eliminated");
const char *cve_name = "cve";
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) {
// eliminate unnecessary sext / zext operations
/* eliminate unnecessary sext / zext operations */
if (instr->op == OP_LOAD || instr->op == OP_LOAD_FAST ||
instr->op == OP_LOAD_SLOW || instr->op == OP_LOAD_CONTEXT) {
enum ir_type memory_type = VALUE_V;
bool same_type = true;
bool all_sext = true;
bool all_zext = true;
int same_type = 1;
int all_sext = 1;
int all_zext = 1;
list_for_each_entry(use, &instr->result->uses, struct ir_use, it) {
struct ir_instr *use_instr = use->instr;
@ -28,38 +26,38 @@ void cve_run(struct ir *ir) {
}
if (memory_type != use_result->type) {
same_type = false;
same_type = 0;
}
}
if (use_instr->op != OP_SEXT) {
all_sext = false;
all_sext = 0;
}
if (use_instr->op != OP_ZEXT) {
all_zext = false;
all_zext = 0;
}
}
if (same_type && all_sext) {
// TODO implement
/* TODO implement */
STAT_num_sext_removed++;
STAT_sext_removed++;
} else if (same_type && all_zext) {
// TODO implement
/* TODO implement */
STAT_num_zext_removed++;
STAT_zext_removed++;
}
} else if (instr->op == OP_STORE || instr->op == OP_STORE_FAST ||
instr->op == OP_STORE_SLOW || instr->op == OP_STORE_CONTEXT) {
struct ir_value *store_value = instr->arg[1];
if (store_value->def && store_value->def->op == OP_TRUNC) {
// TODO implement
/* TODO implement */
// note, don't actually remove the truncation as other values may
// reference it. let DCE clean it up
STAT_num_trunc_removed++;
/* note, don't actually remove the truncation as other values may
reference it. let DCE clean it up */
STAT_trunc_removed++;
}
}
}

View File

@ -2,11 +2,11 @@
#include "jit/ir/ir.h"
#include "jit/ir/passes/pass_stat.h"
DEFINE_STAT(num_dead_removed, "Number of 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
// only use eachother
/* iterate in reverse in order to remove groups of dead instructions that
only use eachother */
list_for_each_entry_safe_reverse(instr, &ir->instrs, struct ir_instr, it) {
struct ir_value *result = instr->result;
@ -17,7 +17,7 @@ void dce_run(struct ir *ir) {
if (list_empty(&result->uses)) {
ir_remove_instr(ir, instr);
STAT_num_dead_removed++;
STAT_dead_removed++;
}
}
}

View File

@ -0,0 +1,44 @@
#include "jit/ir/passes/expression_simplification_pass.h"
#include "jit/ir/ir.h"
#include "jit/ir/passes/pass_stat.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");
void esimp_run(struct ir *ir) {
list_for_each_entry(instr, &ir->instrs, struct ir_instr, it) {
/* TODO simplify bitwise identities */
/* x & x = x */
/* x | x = x */
/* x ^ x = 0 */
/* binary ops involving constants normally have the constant
argument as the second argument */
if (instr->arg[1] && ir_is_constant(instr->arg[1]) &&
ir_is_int(instr->arg[1]->type)) {
uint64_t rhs = ir_zext_constant(instr->arg[1]);
/* simplify binary ops where an argument of 0 always results in 0 */
if ((instr->op == OP_AND || instr->op == OP_SMUL ||
instr->op == OP_UMUL) &&
rhs == 0) {
struct ir_value *zero = ir_alloc_int(ir, 0, instr->result->type);
ir_replace_uses(instr->result, zero);
STAT_zero_properties_removed++;
}
/* TODO simplify binary ops where 0 is the identity element */
/* x + 0 = x */
/* x - 0 = x */
/* x | 0 = x */
/* x ^ 0 = x */
/* x << 0 = x */
/* x >> 0 = x */
/* TODO simplify binary ops where 1 is the identity element */
/* x * 1 = x */
/* x / 1 = x */
}
}
}

View File

@ -0,0 +1,8 @@
#ifndef EXPRESSION_SIMPLIFICATION_PASS_H
#define EXPRESSION_SIMPLIFICATION_PASS_H
struct ir;
void esimp_run(struct ir *ir);
#endif

View File

@ -2,10 +2,8 @@
#include "jit/ir/ir.h"
#include "jit/ir/passes/pass_stat.h"
DEFINE_STAT(num_loads_removed, "Number of loads eliminated");
DEFINE_STAT(num_stores_removed, "Number of stores eliminated");
const char *lse_name = "lse";
DEFINE_STAT(loads_removed, "Context loads eliminated");
DEFINE_STAT(stores_removed, "Context stores eliminated");
#define MAX_OFFSET 16384
@ -19,7 +17,7 @@ struct lse {
};
static void lse_clear_available(struct lse *lse) {
// TODO use auto-incremented token instead of memset'ing this each time
/* TODO use auto-incremented token instead of memset'ing this each time */
memset(lse->available, 0, sizeof(lse->available));
}
@ -28,9 +26,9 @@ static struct ir_value *lse_get_available(struct lse *lse, int offset) {
struct available *entry = &lse->available[offset];
// entries are added for the entire range of an available value to help with
// invalidation. if this entry doesn't start at the requested offset, it's
// not actually valid for reuse
/* entries are added for the entire range of an available value to help with
invalidation. if this entry doesn't start at the requested offset, it's
not actually valid for reuse */
if (entry->offset != offset) {
return NULL;
}
@ -44,8 +42,8 @@ static void lse_erase_available(struct lse *lse, int offset, int size) {
int begin = offset;
int end = offset + size - 1;
// if the invalidation range intersects with an entry, merge that entry into
// the invalidation range
/* if the invalidation range intersects with an entry, merge that entry into
the invalidation range */
struct available *begin_entry = &lse->available[begin];
struct available *end_entry = &lse->available[end];
@ -73,8 +71,8 @@ static void lse_set_available(struct lse *lse, int offset, struct ir_value *v) {
lse_erase_available(lse, offset, size);
// add entries for the entire range to aid in invalidation. only the initial
// entry where offset == entry.offset is valid for reuse
/* add entries for the entire range to aid in invalidation. only the initial
entry where offset == entry.offset is valid for reuse */
for (; begin <= end; begin++) {
struct available *entry = &lse->available[begin];
entry->offset = offset;
@ -85,7 +83,7 @@ static void lse_set_available(struct lse *lse, int offset, struct ir_value *v) {
void lse_run(struct ir *ir) {
struct lse lse;
// eliminate redundant loads
/* eliminate redundant loads */
{
lse_clear_available(&lse);
@ -93,8 +91,8 @@ void lse_run(struct ir *ir) {
if (instr->op == OP_LABEL) {
lse_clear_available(&lse);
} else if (instr->op == OP_LOAD_CONTEXT) {
// if there is already a value available for this offset, reuse it and
// remove this redundant load
/* if there is already a value available for this offset, reuse it and
remove this redundant load */
int offset = instr->arg[0]->i32;
struct ir_value *available = lse_get_available(&lse, offset);
@ -102,7 +100,7 @@ void lse_run(struct ir *ir) {
ir_replace_uses(instr->result, available);
ir_remove_instr(ir, instr);
STAT_num_loads_removed++;
STAT_loads_removed++;
continue;
}
@ -111,15 +109,14 @@ void lse_run(struct ir *ir) {
} else if (instr->op == OP_STORE_CONTEXT) {
int offset = instr->arg[0]->i32;
// mark the value being stored as available
/* mark the value being stored as available */
lse_set_available(&lse, offset, instr->arg[1]);
}
}
}
// eliminate dead stores
/* eliminate dead stores */
{
// iterate in reverse so the current instruction is the one being removed
lse_clear_available(&lse);
list_for_each_entry_safe_reverse(instr, &ir->instrs, struct ir_instr, it) {
@ -131,8 +128,8 @@ void lse_run(struct ir *ir) {
lse_erase_available(&lse, offset, size);
} else if (instr->op == OP_STORE_CONTEXT) {
// if subsequent stores have been made for this offset that would
// overwrite it completely, mark instruction as dead
/* if subsequent stores have been made for this offset that would
overwrite it completely, mark instruction as dead */
int offset = instr->arg[0]->i32;
struct ir_value *available = lse_get_available(&lse, offset);
int available_size = available ? ir_type_size(available->type) : 0;
@ -141,7 +138,7 @@ void lse_run(struct ir *ir) {
if (available_size >= store_size) {
ir_remove_instr(ir, instr);
STAT_num_stores_removed++;
STAT_stores_removed++;
continue;
}

View File

@ -4,7 +4,8 @@
#include "jit/ir/ir.h"
#include "jit/ir/passes/pass_stat.h"
DEFINE_STAT(num_spills, "Number of registers spilled");
DEFINE_STAT(gprs_spilled, "GPRs spilled");
DEFINE_STAT(fprs_spilled, "FPRs spilled");
#define MAX_REGISTERS 32
@ -212,7 +213,11 @@ static int ra_alloc_blocked_register(struct ra *ra, struct ir *ir,
/* reset insert point */
ir->current_instr = insert_point;
STAT_num_spills++;
if (ir_is_int(instr->result->type)) {
STAT_gprs_spilled++;
} else {
STAT_fprs_spilled++;
}
return interval->reg;
}

View File

@ -7,6 +7,7 @@
#include "jit/frontend/jit_frontend.h"
#include "jit/ir/ir.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/register_allocation_pass.h"
#include "sys/exception_handler.h"
@ -325,6 +326,7 @@ void jit_compile_block(struct jit *jit, uint32_t guest_addr) {
/* run optimization passes */
lse_run(&ir);
esimp_run(&ir);
dce_run(&ir);
ra_run(&ir, jit->backend->registers, jit->backend->num_registers);

View File

@ -5,6 +5,7 @@
#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"
@ -12,14 +13,14 @@
#include "sys/filesystem.h"
DEFINE_OPTION_BOOL(help, false, "Show help");
DEFINE_OPTION_STRING(pass, "lse,dce,ra",
DEFINE_OPTION_STRING(pass, "lse,cve,esimp,dce,ra",
"Comma-separated list of passes to run");
DEFINE_OPTION_BOOL(stats, true, "Print pass stats");
DEFINE_OPTION_BOOL(print_after_all, true, "Print IR after each pass");
DEFINE_STAT(backend_size, "Backend code size");
DEFINE_STAT(num_instrs, "Total IR instructions");
DEFINE_STAT(num_instrs_removed, "Total IR instructions removed");
DEFINE_STAT(num_instrs, "Total instructions");
DEFINE_STAT(num_instrs_removed, "Total instructions removed");
static uint8_t ir_buffer[1024 * 1024];
static uint8_t code[1024 * 1024];
@ -64,6 +65,8 @@ static void process_file(struct jit *jit, const char *filename,
cve_run(&ir);
} else if (!strcmp(name, "dce")) {
dce_run(&ir);
} else if (!strcmp(name, "esimp")) {
esimp_run(&ir);
} else if (!strcmp(name, "ra")) {
ra_run(&ir, x64_registers, x64_num_registers);
} else {