mirror of https://github.com/xemu-project/xemu.git
tcg/optimize: Optimize env memory operations
Propagate stores to loads, loads to loads. Reviewed-by: Song Gao <gaosong@loongson.cn> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
parent
9f75e52828
commit
ab84dc398b
264
tcg/optimize.c
264
tcg/optimize.c
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/int128.h"
|
#include "qemu/int128.h"
|
||||||
|
#include "qemu/interval-tree.h"
|
||||||
#include "tcg/tcg-op-common.h"
|
#include "tcg/tcg-op-common.h"
|
||||||
#include "tcg-internal.h"
|
#include "tcg-internal.h"
|
||||||
|
|
||||||
|
@ -37,10 +38,18 @@
|
||||||
glue(glue(case INDEX_op_, x), _i64): \
|
glue(glue(case INDEX_op_, x), _i64): \
|
||||||
glue(glue(case INDEX_op_, x), _vec)
|
glue(glue(case INDEX_op_, x), _vec)
|
||||||
|
|
||||||
|
typedef struct MemCopyInfo {
|
||||||
|
IntervalTreeNode itree;
|
||||||
|
QSIMPLEQ_ENTRY (MemCopyInfo) next;
|
||||||
|
TCGTemp *ts;
|
||||||
|
TCGType type;
|
||||||
|
} MemCopyInfo;
|
||||||
|
|
||||||
typedef struct TempOptInfo {
|
typedef struct TempOptInfo {
|
||||||
bool is_const;
|
bool is_const;
|
||||||
TCGTemp *prev_copy;
|
TCGTemp *prev_copy;
|
||||||
TCGTemp *next_copy;
|
TCGTemp *next_copy;
|
||||||
|
QSIMPLEQ_HEAD(, MemCopyInfo) mem_copy;
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
uint64_t z_mask; /* mask bit is 0 if and only if value bit is 0 */
|
uint64_t z_mask; /* mask bit is 0 if and only if value bit is 0 */
|
||||||
uint64_t s_mask; /* a left-aligned mask of clrsb(value) bits. */
|
uint64_t s_mask; /* a left-aligned mask of clrsb(value) bits. */
|
||||||
|
@ -51,6 +60,9 @@ typedef struct OptContext {
|
||||||
TCGOp *prev_mb;
|
TCGOp *prev_mb;
|
||||||
TCGTempSet temps_used;
|
TCGTempSet temps_used;
|
||||||
|
|
||||||
|
IntervalTreeRoot mem_copy;
|
||||||
|
QSIMPLEQ_HEAD(, MemCopyInfo) mem_free;
|
||||||
|
|
||||||
/* In flight values from optimization. */
|
/* In flight values from optimization. */
|
||||||
uint64_t a_mask; /* mask bit is 0 iff value identical to first input */
|
uint64_t a_mask; /* mask bit is 0 iff value identical to first input */
|
||||||
uint64_t z_mask; /* mask bit is 0 iff value bit is 0 */
|
uint64_t z_mask; /* mask bit is 0 iff value bit is 0 */
|
||||||
|
@ -127,27 +139,6 @@ static TCGTemp *cmp_better_copy(TCGTemp *a, TCGTemp *b)
|
||||||
return a->kind < b->kind ? b : a;
|
return a->kind < b->kind ? b : a;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset TEMP's state, possibly removing the temp for the list of copies. */
|
|
||||||
static void reset_ts(OptContext *ctx, TCGTemp *ts)
|
|
||||||
{
|
|
||||||
TempOptInfo *ti = ts_info(ts);
|
|
||||||
TempOptInfo *pi = ts_info(ti->prev_copy);
|
|
||||||
TempOptInfo *ni = ts_info(ti->next_copy);
|
|
||||||
|
|
||||||
ni->prev_copy = ti->prev_copy;
|
|
||||||
pi->next_copy = ti->next_copy;
|
|
||||||
ti->next_copy = ts;
|
|
||||||
ti->prev_copy = ts;
|
|
||||||
ti->is_const = false;
|
|
||||||
ti->z_mask = -1;
|
|
||||||
ti->s_mask = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void reset_temp(OptContext *ctx, TCGArg arg)
|
|
||||||
{
|
|
||||||
reset_ts(ctx, arg_temp(arg));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize and activate a temporary. */
|
/* Initialize and activate a temporary. */
|
||||||
static void init_ts_info(OptContext *ctx, TCGTemp *ts)
|
static void init_ts_info(OptContext *ctx, TCGTemp *ts)
|
||||||
{
|
{
|
||||||
|
@ -167,6 +158,7 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts)
|
||||||
|
|
||||||
ti->next_copy = ts;
|
ti->next_copy = ts;
|
||||||
ti->prev_copy = ts;
|
ti->prev_copy = ts;
|
||||||
|
QSIMPLEQ_INIT(&ti->mem_copy);
|
||||||
if (ts->kind == TEMP_CONST) {
|
if (ts->kind == TEMP_CONST) {
|
||||||
ti->is_const = true;
|
ti->is_const = true;
|
||||||
ti->val = ts->val;
|
ti->val = ts->val;
|
||||||
|
@ -179,6 +171,45 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MemCopyInfo *mem_copy_first(OptContext *ctx, intptr_t s, intptr_t l)
|
||||||
|
{
|
||||||
|
IntervalTreeNode *r = interval_tree_iter_first(&ctx->mem_copy, s, l);
|
||||||
|
return r ? container_of(r, MemCopyInfo, itree) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MemCopyInfo *mem_copy_next(MemCopyInfo *mem, intptr_t s, intptr_t l)
|
||||||
|
{
|
||||||
|
IntervalTreeNode *r = interval_tree_iter_next(&mem->itree, s, l);
|
||||||
|
return r ? container_of(r, MemCopyInfo, itree) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_mem_copy(OptContext *ctx, MemCopyInfo *mc)
|
||||||
|
{
|
||||||
|
TCGTemp *ts = mc->ts;
|
||||||
|
TempOptInfo *ti = ts_info(ts);
|
||||||
|
|
||||||
|
interval_tree_remove(&mc->itree, &ctx->mem_copy);
|
||||||
|
QSIMPLEQ_REMOVE(&ti->mem_copy, mc, MemCopyInfo, next);
|
||||||
|
QSIMPLEQ_INSERT_TAIL(&ctx->mem_free, mc, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_mem_copy_in(OptContext *ctx, intptr_t s, intptr_t l)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
MemCopyInfo *mc = mem_copy_first(ctx, s, l);
|
||||||
|
if (!mc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
remove_mem_copy(ctx, mc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_mem_copy_all(OptContext *ctx)
|
||||||
|
{
|
||||||
|
remove_mem_copy_in(ctx, 0, -1);
|
||||||
|
tcg_debug_assert(interval_tree_is_empty(&ctx->mem_copy));
|
||||||
|
}
|
||||||
|
|
||||||
static TCGTemp *find_better_copy(TCGTemp *ts)
|
static TCGTemp *find_better_copy(TCGTemp *ts)
|
||||||
{
|
{
|
||||||
TCGTemp *i, *ret;
|
TCGTemp *i, *ret;
|
||||||
|
@ -195,6 +226,80 @@ static TCGTemp *find_better_copy(TCGTemp *ts)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void move_mem_copies(TCGTemp *dst_ts, TCGTemp *src_ts)
|
||||||
|
{
|
||||||
|
TempOptInfo *si = ts_info(src_ts);
|
||||||
|
TempOptInfo *di = ts_info(dst_ts);
|
||||||
|
MemCopyInfo *mc;
|
||||||
|
|
||||||
|
QSIMPLEQ_FOREACH(mc, &si->mem_copy, next) {
|
||||||
|
tcg_debug_assert(mc->ts == src_ts);
|
||||||
|
mc->ts = dst_ts;
|
||||||
|
}
|
||||||
|
QSIMPLEQ_CONCAT(&di->mem_copy, &si->mem_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset TEMP's state, possibly removing the temp for the list of copies. */
|
||||||
|
static void reset_ts(OptContext *ctx, TCGTemp *ts)
|
||||||
|
{
|
||||||
|
TempOptInfo *ti = ts_info(ts);
|
||||||
|
TCGTemp *pts = ti->prev_copy;
|
||||||
|
TCGTemp *nts = ti->next_copy;
|
||||||
|
TempOptInfo *pi = ts_info(pts);
|
||||||
|
TempOptInfo *ni = ts_info(nts);
|
||||||
|
|
||||||
|
ni->prev_copy = ti->prev_copy;
|
||||||
|
pi->next_copy = ti->next_copy;
|
||||||
|
ti->next_copy = ts;
|
||||||
|
ti->prev_copy = ts;
|
||||||
|
ti->is_const = false;
|
||||||
|
ti->z_mask = -1;
|
||||||
|
ti->s_mask = 0;
|
||||||
|
|
||||||
|
if (!QSIMPLEQ_EMPTY(&ti->mem_copy)) {
|
||||||
|
if (ts == nts) {
|
||||||
|
/* Last temp copy being removed, the mem copies die. */
|
||||||
|
MemCopyInfo *mc;
|
||||||
|
QSIMPLEQ_FOREACH(mc, &ti->mem_copy, next) {
|
||||||
|
interval_tree_remove(&mc->itree, &ctx->mem_copy);
|
||||||
|
}
|
||||||
|
QSIMPLEQ_CONCAT(&ctx->mem_free, &ti->mem_copy);
|
||||||
|
} else {
|
||||||
|
move_mem_copies(find_better_copy(nts), ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset_temp(OptContext *ctx, TCGArg arg)
|
||||||
|
{
|
||||||
|
reset_ts(ctx, arg_temp(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void record_mem_copy(OptContext *ctx, TCGType type,
|
||||||
|
TCGTemp *ts, intptr_t start, intptr_t last)
|
||||||
|
{
|
||||||
|
MemCopyInfo *mc;
|
||||||
|
TempOptInfo *ti;
|
||||||
|
|
||||||
|
mc = QSIMPLEQ_FIRST(&ctx->mem_free);
|
||||||
|
if (mc) {
|
||||||
|
QSIMPLEQ_REMOVE_HEAD(&ctx->mem_free, next);
|
||||||
|
} else {
|
||||||
|
mc = tcg_malloc(sizeof(*mc));
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(mc, 0, sizeof(*mc));
|
||||||
|
mc->itree.start = start;
|
||||||
|
mc->itree.last = last;
|
||||||
|
mc->type = type;
|
||||||
|
interval_tree_insert(&mc->itree, &ctx->mem_copy);
|
||||||
|
|
||||||
|
ts = find_better_copy(ts);
|
||||||
|
ti = ts_info(ts);
|
||||||
|
mc->ts = ts;
|
||||||
|
QSIMPLEQ_INSERT_TAIL(&ti->mem_copy, mc, next);
|
||||||
|
}
|
||||||
|
|
||||||
static bool ts_are_copies(TCGTemp *ts1, TCGTemp *ts2)
|
static bool ts_are_copies(TCGTemp *ts1, TCGTemp *ts2)
|
||||||
{
|
{
|
||||||
TCGTemp *i;
|
TCGTemp *i;
|
||||||
|
@ -221,6 +326,18 @@ static bool args_are_copies(TCGArg arg1, TCGArg arg2)
|
||||||
return ts_are_copies(arg_temp(arg1), arg_temp(arg2));
|
return ts_are_copies(arg_temp(arg1), arg_temp(arg2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TCGTemp *find_mem_copy_for(OptContext *ctx, TCGType type, intptr_t s)
|
||||||
|
{
|
||||||
|
MemCopyInfo *mc;
|
||||||
|
|
||||||
|
for (mc = mem_copy_first(ctx, s, s); mc; mc = mem_copy_next(mc, s, s)) {
|
||||||
|
if (mc->itree.start == s && mc->type == type) {
|
||||||
|
return find_better_copy(mc->ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src)
|
static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src)
|
||||||
{
|
{
|
||||||
TCGTemp *dst_ts = arg_temp(dst);
|
TCGTemp *dst_ts = arg_temp(dst);
|
||||||
|
@ -270,6 +387,11 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src)
|
||||||
si->next_copy = dst_ts;
|
si->next_copy = dst_ts;
|
||||||
di->is_const = si->is_const;
|
di->is_const = si->is_const;
|
||||||
di->val = si->val;
|
di->val = si->val;
|
||||||
|
|
||||||
|
if (!QSIMPLEQ_EMPTY(&si->mem_copy)
|
||||||
|
&& cmp_better_copy(src_ts, dst_ts) == dst_ts) {
|
||||||
|
move_mem_copies(dst_ts, src_ts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -688,6 +810,7 @@ static void finish_folding(OptContext *ctx, TCGOp *op)
|
||||||
ctx->prev_mb = NULL;
|
ctx->prev_mb = NULL;
|
||||||
if (!(def->flags & TCG_OPF_COND_BRANCH)) {
|
if (!(def->flags & TCG_OPF_COND_BRANCH)) {
|
||||||
memset(&ctx->temps_used, 0, sizeof(ctx->temps_used));
|
memset(&ctx->temps_used, 0, sizeof(ctx->temps_used));
|
||||||
|
remove_mem_copy_all(ctx);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1213,6 +1336,11 @@ static bool fold_call(OptContext *ctx, TCGOp *op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If the function has side effects, reset mem data. */
|
||||||
|
if (!(flags & TCG_CALL_NO_SIDE_EFFECTS)) {
|
||||||
|
remove_mem_copy_all(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
/* Reset temp data for outputs. */
|
/* Reset temp data for outputs. */
|
||||||
for (i = 0; i < nb_oargs; i++) {
|
for (i = 0; i < nb_oargs; i++) {
|
||||||
reset_temp(ctx, op->args[i]);
|
reset_temp(ctx, op->args[i]);
|
||||||
|
@ -2070,6 +2198,83 @@ static bool fold_tcg_ld(OptContext *ctx, TCGOp *op)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool fold_tcg_ld_memcopy(OptContext *ctx, TCGOp *op)
|
||||||
|
{
|
||||||
|
TCGTemp *dst, *src;
|
||||||
|
intptr_t ofs;
|
||||||
|
TCGType type;
|
||||||
|
|
||||||
|
if (op->args[1] != tcgv_ptr_arg(tcg_env)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = ctx->type;
|
||||||
|
ofs = op->args[2];
|
||||||
|
dst = arg_temp(op->args[0]);
|
||||||
|
src = find_mem_copy_for(ctx, type, ofs);
|
||||||
|
if (src && src->base_type == type) {
|
||||||
|
return tcg_opt_gen_mov(ctx, op, temp_arg(dst), temp_arg(src));
|
||||||
|
}
|
||||||
|
|
||||||
|
reset_ts(ctx, dst);
|
||||||
|
record_mem_copy(ctx, type, dst, ofs, ofs + tcg_type_size(type) - 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool fold_tcg_st(OptContext *ctx, TCGOp *op)
|
||||||
|
{
|
||||||
|
intptr_t ofs = op->args[2];
|
||||||
|
intptr_t lm1;
|
||||||
|
|
||||||
|
if (op->args[1] != tcgv_ptr_arg(tcg_env)) {
|
||||||
|
remove_mem_copy_all(ctx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (op->opc) {
|
||||||
|
CASE_OP_32_64(st8):
|
||||||
|
lm1 = 0;
|
||||||
|
break;
|
||||||
|
CASE_OP_32_64(st16):
|
||||||
|
lm1 = 1;
|
||||||
|
break;
|
||||||
|
case INDEX_op_st32_i64:
|
||||||
|
case INDEX_op_st_i32:
|
||||||
|
lm1 = 3;
|
||||||
|
break;
|
||||||
|
case INDEX_op_st_i64:
|
||||||
|
lm1 = 7;
|
||||||
|
break;
|
||||||
|
case INDEX_op_st_vec:
|
||||||
|
lm1 = tcg_type_size(ctx->type) - 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
remove_mem_copy_in(ctx, ofs, ofs + lm1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool fold_tcg_st_memcopy(OptContext *ctx, TCGOp *op)
|
||||||
|
{
|
||||||
|
TCGTemp *src;
|
||||||
|
intptr_t ofs, last;
|
||||||
|
TCGType type;
|
||||||
|
|
||||||
|
if (op->args[1] != tcgv_ptr_arg(tcg_env)) {
|
||||||
|
fold_tcg_st(ctx, op);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
src = arg_temp(op->args[0]);
|
||||||
|
ofs = op->args[2];
|
||||||
|
type = ctx->type;
|
||||||
|
last = ofs + tcg_type_size(type) - 1;
|
||||||
|
remove_mem_copy_in(ctx, ofs, last);
|
||||||
|
record_mem_copy(ctx, type, src, ofs, last);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool fold_xor(OptContext *ctx, TCGOp *op)
|
static bool fold_xor(OptContext *ctx, TCGOp *op)
|
||||||
{
|
{
|
||||||
if (fold_const2_commutative(ctx, op) ||
|
if (fold_const2_commutative(ctx, op) ||
|
||||||
|
@ -2093,6 +2298,8 @@ void tcg_optimize(TCGContext *s)
|
||||||
TCGOp *op, *op_next;
|
TCGOp *op, *op_next;
|
||||||
OptContext ctx = { .tcg = s };
|
OptContext ctx = { .tcg = s };
|
||||||
|
|
||||||
|
QSIMPLEQ_INIT(&ctx.mem_free);
|
||||||
|
|
||||||
/* Array VALS has an element for each temp.
|
/* Array VALS has an element for each temp.
|
||||||
If this temp holds a constant then its value is kept in VALS' element.
|
If this temp holds a constant then its value is kept in VALS' element.
|
||||||
If this temp is a copy of other ones then the other copies are
|
If this temp is a copy of other ones then the other copies are
|
||||||
|
@ -2214,6 +2421,21 @@ void tcg_optimize(TCGContext *s)
|
||||||
case INDEX_op_ld32u_i64:
|
case INDEX_op_ld32u_i64:
|
||||||
done = fold_tcg_ld(&ctx, op);
|
done = fold_tcg_ld(&ctx, op);
|
||||||
break;
|
break;
|
||||||
|
case INDEX_op_ld_i32:
|
||||||
|
case INDEX_op_ld_i64:
|
||||||
|
case INDEX_op_ld_vec:
|
||||||
|
done = fold_tcg_ld_memcopy(&ctx, op);
|
||||||
|
break;
|
||||||
|
CASE_OP_32_64(st8):
|
||||||
|
CASE_OP_32_64(st16):
|
||||||
|
case INDEX_op_st32_i64:
|
||||||
|
done = fold_tcg_st(&ctx, op);
|
||||||
|
break;
|
||||||
|
case INDEX_op_st_i32:
|
||||||
|
case INDEX_op_st_i64:
|
||||||
|
case INDEX_op_st_vec:
|
||||||
|
done = fold_tcg_st_memcopy(&ctx, op);
|
||||||
|
break;
|
||||||
case INDEX_op_mb:
|
case INDEX_op_mb:
|
||||||
done = fold_mb(&ctx, op);
|
done = fold_mb(&ctx, op);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue