summaryrefslogtreecommitdiff
path: root/tcg/tcg.c
diff options
context:
space:
mode:
authorRichard Henderson <rth@twiddle.net>2016-06-19 22:59:13 -0700
committerRichard Henderson <rth@twiddle.net>2016-07-05 20:50:13 -0700
commit59d7c14eeff8d2ad7f61aed86ce5a176113bc153 (patch)
treea35fd8794ff364683cd31b650affc4f3d9289ce1 /tcg/tcg.c
parent120c1084ed53ba2967ba2a6e751cdfb00a17c246 (diff)
downloadqemu-59d7c14eeff8d2ad7f61aed86ce5a176113bc153.tar.gz
tcg: Optimize spills of constants
While we can store constants via constrants on INDEX_op_st_i32 et al, we weren't able to spill constants to backing store. Add a new backend interface, tcg_out_sti, which may store the constant (and is allowed to fail). Rearrange the temp_* helpers so that we only attempt to directly store a constant when the temp is becoming dead/free. Signed-off-by: Richard Henderson <rth@twiddle.net>
Diffstat (limited to 'tcg/tcg.c')
-rw-r--r--tcg/tcg.c165
1 files changed, 91 insertions, 74 deletions
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 154ffe801c..ef8a8556e4 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -108,6 +108,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args,
const int *const_args);
static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, TCGReg arg1,
intptr_t arg2);
+static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val,
+ TCGReg base, intptr_t ofs);
static void tcg_out_call(TCGContext *s, tcg_insn_unit *target);
static int tcg_target_const_match(tcg_target_long val, TCGType type,
const TCGArgConstraint *arg_ct);
@@ -1680,35 +1682,89 @@ static void temp_allocate_frame(TCGContext *s, int temp)
static void temp_load(TCGContext *, TCGTemp *, TCGRegSet, TCGRegSet);
-/* sync register 'reg' by saving it to the corresponding temporary */
-static void tcg_reg_sync(TCGContext *s, TCGReg reg, TCGRegSet allocated_regs)
+/* Mark a temporary as free or dead. If 'free_or_dead' is negative,
+ mark it free; otherwise mark it dead. */
+static void temp_free_or_dead(TCGContext *s, TCGTemp *ts, int free_or_dead)
{
- TCGTemp *ts = s->reg_to_temp[reg];
+ if (ts->fixed_reg) {
+ return;
+ }
+ if (ts->val_type == TEMP_VAL_REG) {
+ s->reg_to_temp[ts->reg] = NULL;
+ }
+ ts->val_type = (free_or_dead < 0
+ || ts->temp_local
+ || temp_idx(s, ts) < s->nb_globals
+ ? TEMP_VAL_MEM : TEMP_VAL_DEAD);
+}
- tcg_debug_assert(ts->val_type == TEMP_VAL_REG);
- if (!ts->mem_coherent && !ts->fixed_reg) {
+/* Mark a temporary as dead. */
+static inline void temp_dead(TCGContext *s, TCGTemp *ts)
+{
+ temp_free_or_dead(s, ts, 1);
+}
+
+/* Sync a temporary to memory. 'allocated_regs' is used in case a temporary
+ registers needs to be allocated to store a constant. If 'free_or_dead'
+ is non-zero, subsequently release the temporary; if it is positive, the
+ temp is dead; if it is negative, the temp is free. */
+static void temp_sync(TCGContext *s, TCGTemp *ts,
+ TCGRegSet allocated_regs, int free_or_dead)
+{
+ if (ts->fixed_reg) {
+ return;
+ }
+ if (!ts->mem_coherent) {
if (!ts->mem_allocated) {
temp_allocate_frame(s, temp_idx(s, ts));
- } else if (ts->indirect_reg) {
- tcg_regset_set_reg(allocated_regs, ts->reg);
+ }
+ if (ts->indirect_reg) {
+ if (ts->val_type == TEMP_VAL_REG) {
+ tcg_regset_set_reg(allocated_regs, ts->reg);
+ }
temp_load(s, ts->mem_base,
tcg_target_available_regs[TCG_TYPE_PTR],
allocated_regs);
}
- tcg_out_st(s, ts->type, reg, ts->mem_base->reg, ts->mem_offset);
+ switch (ts->val_type) {
+ case TEMP_VAL_CONST:
+ /* If we're going to free the temp immediately, then we won't
+ require it later in a register, so attempt to store the
+ constant to memory directly. */
+ if (free_or_dead
+ && tcg_out_sti(s, ts->type, ts->val,
+ ts->mem_base->reg, ts->mem_offset)) {
+ break;
+ }
+ temp_load(s, ts, tcg_target_available_regs[ts->type],
+ allocated_regs);
+ /* fallthrough */
+
+ case TEMP_VAL_REG:
+ tcg_out_st(s, ts->type, ts->reg,
+ ts->mem_base->reg, ts->mem_offset);
+ break;
+
+ case TEMP_VAL_MEM:
+ break;
+
+ case TEMP_VAL_DEAD:
+ default:
+ tcg_abort();
+ }
+ ts->mem_coherent = 1;
+ }
+ if (free_or_dead) {
+ temp_free_or_dead(s, ts, free_or_dead);
}
- ts->mem_coherent = 1;
}
/* free register 'reg' by spilling the corresponding temporary if necessary */
static void tcg_reg_free(TCGContext *s, TCGReg reg, TCGRegSet allocated_regs)
{
TCGTemp *ts = s->reg_to_temp[reg];
-
if (ts != NULL) {
- tcg_reg_sync(s, reg, allocated_regs);
- ts->val_type = TEMP_VAL_MEM;
- s->reg_to_temp[reg] = NULL;
+ temp_sync(s, ts, allocated_regs, -1);
}
}
@@ -1778,45 +1834,9 @@ static void temp_load(TCGContext *s, TCGTemp *ts, TCGRegSet desired_regs,
s->reg_to_temp[reg] = ts;
}
-/* mark a temporary as dead. */
-static inline void temp_dead(TCGContext *s, TCGTemp *ts)
-{
- if (ts->fixed_reg) {
- return;
- }
- if (ts->val_type == TEMP_VAL_REG) {
- s->reg_to_temp[ts->reg] = NULL;
- }
- ts->val_type = (temp_idx(s, ts) < s->nb_globals || ts->temp_local
- ? TEMP_VAL_MEM : TEMP_VAL_DEAD);
-}
-
-/* sync a temporary to memory. 'allocated_regs' is used in case a
- temporary registers needs to be allocated to store a constant. */
-static void temp_sync(TCGContext *s, TCGTemp *ts, TCGRegSet allocated_regs)
-{
- if (ts->fixed_reg) {
- return;
- }
- switch (ts->val_type) {
- case TEMP_VAL_CONST:
- temp_load(s, ts, tcg_target_available_regs[ts->type], allocated_regs);
- /* fallthrough */
- case TEMP_VAL_REG:
- tcg_reg_sync(s, ts->reg, allocated_regs);
- break;
- case TEMP_VAL_DEAD:
- case TEMP_VAL_MEM:
- break;
- default:
- tcg_abort();
- }
-}
-
-/* save a temporary to memory. 'allocated_regs' is used in case a
- temporary registers needs to be allocated to store a constant. */
-static inline void temp_save(TCGContext *s, TCGTemp *ts,
- TCGRegSet allocated_regs)
+/* Save a temporary to memory. 'allocated_regs' is used in case a
+ temporary registers needs to be allocated to store a constant. */
+static void temp_save(TCGContext *s, TCGTemp *ts, TCGRegSet allocated_regs)
{
#ifdef USE_LIVENESS_ANALYSIS
/* ??? Liveness does not yet incorporate indirect bases. */
@@ -1827,8 +1847,7 @@ static inline void temp_save(TCGContext *s, TCGTemp *ts,
return;
}
#endif
- temp_sync(s, ts, allocated_regs);
- temp_dead(s, ts);
+ temp_sync(s, ts, allocated_regs, 1);
}
/* save globals to their canonical location and assume they can be
@@ -1861,7 +1880,7 @@ static void sync_globals(TCGContext *s, TCGRegSet allocated_regs)
continue;
}
#endif
- temp_sync(s, ts, allocated_regs);
+ temp_sync(s, ts, allocated_regs, 0);
}
}
@@ -1905,21 +1924,21 @@ static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args,
val = args[1];
if (ots->fixed_reg) {
- /* for fixed registers, we do not do any constant
- propagation */
+ /* For fixed registers, we do not do any constant propagation. */
tcg_out_movi(s, ots->type, ots->reg, val);
- } else {
- /* The movi is not explicitly generated here */
- if (ots->val_type == TEMP_VAL_REG) {
- s->reg_to_temp[ots->reg] = NULL;
- }
- ots->val_type = TEMP_VAL_CONST;
- ots->val = val;
+ return;
}
- if (NEED_SYNC_ARG(0)) {
- temp_sync(s, ots, s->reserved_regs);
+
+ /* The movi is not explicitly generated here. */
+ if (ots->val_type == TEMP_VAL_REG) {
+ s->reg_to_temp[ots->reg] = NULL;
}
- if (IS_DEAD_ARG(0)) {
+ ots->val_type = TEMP_VAL_CONST;
+ ots->val = val;
+ ots->mem_coherent = 0;
+ if (NEED_SYNC_ARG(0)) {
+ temp_sync(s, ots, s->reserved_regs, IS_DEAD_ARG(0));
+ } else if (IS_DEAD_ARG(0)) {
temp_dead(s, ots);
}
}
@@ -2004,7 +2023,7 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def,
ots->mem_coherent = 0;
s->reg_to_temp[ots->reg] = ots;
if (NEED_SYNC_ARG(0)) {
- tcg_reg_sync(s, ots->reg, allocated_regs);
+ temp_sync(s, ots, allocated_regs, 0);
}
}
}
@@ -2163,9 +2182,8 @@ static void tcg_reg_alloc_op(TCGContext *s,
tcg_out_mov(s, ts->type, ts->reg, reg);
}
if (NEED_SYNC_ARG(i)) {
- tcg_reg_sync(s, reg, allocated_regs);
- }
- if (IS_DEAD_ARG(i)) {
+ temp_sync(s, ts, allocated_regs, IS_DEAD_ARG(i));
+ } else if (IS_DEAD_ARG(i)) {
temp_dead(s, ts);
}
}
@@ -2298,9 +2316,8 @@ static void tcg_reg_alloc_call(TCGContext *s, int nb_oargs, int nb_iargs,
ts->mem_coherent = 0;
s->reg_to_temp[reg] = ts;
if (NEED_SYNC_ARG(i)) {
- tcg_reg_sync(s, reg, allocated_regs);
- }
- if (IS_DEAD_ARG(i)) {
+ temp_sync(s, ts, allocated_regs, IS_DEAD_ARG(i));
+ } else if (IS_DEAD_ARG(i)) {
temp_dead(s, ts);
}
}