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/int128.h"
|
||||
#include "qemu/interval-tree.h"
|
||||
#include "tcg/tcg-op-common.h"
|
||||
#include "tcg-internal.h"
|
||||
|
||||
@ -37,10 +38,18 @@
|
||||
glue(glue(case INDEX_op_, x), _i64): \
|
||||
glue(glue(case INDEX_op_, x), _vec)
|
||||
|
||||
typedef struct MemCopyInfo {
|
||||
IntervalTreeNode itree;
|
||||
QSIMPLEQ_ENTRY (MemCopyInfo) next;
|
||||
TCGTemp *ts;
|
||||
TCGType type;
|
||||
} MemCopyInfo;
|
||||
|
||||
typedef struct TempOptInfo {
|
||||
bool is_const;
|
||||
TCGTemp *prev_copy;
|
||||
TCGTemp *next_copy;
|
||||
QSIMPLEQ_HEAD(, MemCopyInfo) mem_copy;
|
||||
uint64_t val;
|
||||
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. */
|
||||
@ -51,6 +60,9 @@ typedef struct OptContext {
|
||||
TCGOp *prev_mb;
|
||||
TCGTempSet temps_used;
|
||||
|
||||
IntervalTreeRoot mem_copy;
|
||||
QSIMPLEQ_HEAD(, MemCopyInfo) mem_free;
|
||||
|
||||
/* In flight values from optimization. */
|
||||
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 */
|
||||
@ -127,27 +139,6 @@ static TCGTemp *cmp_better_copy(TCGTemp *a, TCGTemp *b)
|
||||
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. */
|
||||
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->prev_copy = ts;
|
||||
QSIMPLEQ_INIT(&ti->mem_copy);
|
||||
if (ts->kind == TEMP_CONST) {
|
||||
ti->is_const = true;
|
||||
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)
|
||||
{
|
||||
TCGTemp *i, *ret;
|
||||
@ -195,6 +226,80 @@ static TCGTemp *find_better_copy(TCGTemp *ts)
|
||||
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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
di->is_const = si->is_const;
|
||||
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;
|
||||
}
|
||||
@ -688,6 +810,7 @@ static void finish_folding(OptContext *ctx, TCGOp *op)
|
||||
ctx->prev_mb = NULL;
|
||||
if (!(def->flags & TCG_OPF_COND_BRANCH)) {
|
||||
memset(&ctx->temps_used, 0, sizeof(ctx->temps_used));
|
||||
remove_mem_copy_all(ctx);
|
||||
}
|
||||
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. */
|
||||
for (i = 0; i < nb_oargs; i++) {
|
||||
reset_temp(ctx, op->args[i]);
|
||||
@ -2070,6 +2198,83 @@ static bool fold_tcg_ld(OptContext *ctx, TCGOp *op)
|
||||
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)
|
||||
{
|
||||
if (fold_const2_commutative(ctx, op) ||
|
||||
@ -2093,6 +2298,8 @@ void tcg_optimize(TCGContext *s)
|
||||
TCGOp *op, *op_next;
|
||||
OptContext ctx = { .tcg = s };
|
||||
|
||||
QSIMPLEQ_INIT(&ctx.mem_free);
|
||||
|
||||
/* 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 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:
|
||||
done = fold_tcg_ld(&ctx, op);
|
||||
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:
|
||||
done = fold_mb(&ctx, op);
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user