TEMP_GLOBAL,
/* Temp is in a fixed register. */
TEMP_FIXED,
+ /* Temp is a fixed constant. */
+ TEMP_CONST,
} TCGTempKind;
typedef struct TCGTemp {
QSIMPLEQ_HEAD(, TCGOp) plugin_ops;
#endif
+ GHashTable *const_table[TCG_TYPE_COUNT];
TCGTempSet free_temps[TCG_TYPE_COUNT * 2];
TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */
static inline bool temp_readonly(TCGTemp *ts)
{
- return ts->kind == TEMP_FIXED;
+ return ts->kind >= TEMP_FIXED;
}
extern TCGContext tcg_init_ctx;
void tcg_optimize(TCGContext *s);
+/* Allocate a new temporary and initialize it with a constant. */
TCGv_i32 tcg_const_i32(int32_t val);
TCGv_i64 tcg_const_i64(int64_t val);
TCGv_i32 tcg_const_local_i32(int32_t val);
TCGv_vec tcg_const_zeros_vec_matching(TCGv_vec);
TCGv_vec tcg_const_ones_vec_matching(TCGv_vec);
+/*
+ * Locate or create a read-only temporary that is a constant.
+ * This kind of temporary need not and should not be freed.
+ */
+TCGTemp *tcg_constant_internal(TCGType type, int64_t val);
+
+static inline TCGv_i32 tcg_constant_i32(int32_t val)
+{
+ return temp_tcgv_i32(tcg_constant_internal(TCG_TYPE_I32, val));
+}
+
+static inline TCGv_i64 tcg_constant_i64(int64_t val)
+{
+ return temp_tcgv_i64(tcg_constant_internal(TCG_TYPE_I64, val));
+}
+
+TCGv_vec tcg_constant_vec(TCGType type, unsigned vece, int64_t val);
+
#if UINTPTR_MAX == UINT32_MAX
# define tcg_const_ptr(x) ((TCGv_ptr)tcg_const_i32((intptr_t)(x)))
# define tcg_const_local_ptr(x) ((TCGv_ptr)tcg_const_local_i32((intptr_t)(x)))
/* No temps have been previously allocated for size or locality. */
memset(s->free_temps, 0, sizeof(s->free_temps));
+ /* No constant temps have been previously allocated. */
+ for (int i = 0; i < TCG_TYPE_COUNT; ++i) {
+ if (s->const_table[i]) {
+ g_hash_table_remove_all(s->const_table[i]);
+ }
+ }
+
s->nb_ops = 0;
s->nb_labels = 0;
s->current_frame_offset = s->frame_start;
bigendian = 1;
#endif
- if (base_ts->kind != TEMP_FIXED) {
+ switch (base_ts->kind) {
+ case TEMP_FIXED:
+ break;
+ case TEMP_GLOBAL:
/* We do not support double-indirect registers. */
tcg_debug_assert(!base_ts->indirect_reg);
base_ts->indirect_base = 1;
s->nb_indirects += (TCG_TARGET_REG_BITS == 32 && type == TCG_TYPE_I64
? 2 : 1);
indirect_reg = 1;
+ break;
+ default:
+ g_assert_not_reached();
}
if (TCG_TARGET_REG_BITS == 32 && type == TCG_TYPE_I64) {
TCGContext *s = tcg_ctx;
int k, idx;
+ /* In order to simplify users of tcg_constant_*, silently ignore free. */
+ if (ts->kind == TEMP_CONST) {
+ return;
+ }
+
#if defined(CONFIG_DEBUG_TCG)
s->temps_in_use--;
if (s->temps_in_use < 0) {
set_bit(idx, s->free_temps[k].l);
}
+TCGTemp *tcg_constant_internal(TCGType type, int64_t val)
+{
+ TCGContext *s = tcg_ctx;
+ GHashTable *h = s->const_table[type];
+ TCGTemp *ts;
+
+ if (h == NULL) {
+ h = g_hash_table_new(g_int64_hash, g_int64_equal);
+ s->const_table[type] = h;
+ }
+
+ ts = g_hash_table_lookup(h, &val);
+ if (ts == NULL) {
+ ts = tcg_temp_alloc(s);
+
+ if (TCG_TARGET_REG_BITS == 32 && type == TCG_TYPE_I64) {
+ TCGTemp *ts2 = tcg_temp_alloc(s);
+
+ ts->base_type = TCG_TYPE_I64;
+ ts->type = TCG_TYPE_I32;
+ ts->kind = TEMP_CONST;
+ ts->temp_allocated = 1;
+ /*
+ * Retain the full value of the 64-bit constant in the low
+ * part, so that the hash table works. Actual uses will
+ * truncate the value to the low part.
+ */
+ ts->val = val;
+
+ tcg_debug_assert(ts2 == ts + 1);
+ ts2->base_type = TCG_TYPE_I64;
+ ts2->type = TCG_TYPE_I32;
+ ts2->kind = TEMP_CONST;
+ ts2->temp_allocated = 1;
+ ts2->val = val >> 32;
+ } else {
+ ts->base_type = type;
+ ts->type = type;
+ ts->kind = TEMP_CONST;
+ ts->temp_allocated = 1;
+ ts->val = val;
+ }
+ g_hash_table_insert(h, &ts->val, ts);
+ }
+
+ return ts;
+}
+
+TCGv_vec tcg_constant_vec(TCGType type, unsigned vece, int64_t val)
+{
+ val = dup_const(vece, val);
+ return temp_tcgv_vec(tcg_constant_internal(type, val));
+}
+
TCGv_i32 tcg_const_i32(int32_t val)
{
TCGv_i32 t0;
TCGTempVal val = TEMP_VAL_MEM;
switch (ts->kind) {
+ case TEMP_CONST:
+ val = TEMP_VAL_CONST;
+ break;
case TEMP_FIXED:
val = TEMP_VAL_REG;
break;
case TEMP_NORMAL:
snprintf(buf, buf_size, "tmp%d", idx - s->nb_globals);
break;
+ case TEMP_CONST:
+ switch (ts->type) {
+ case TCG_TYPE_I32:
+ snprintf(buf, buf_size, "$0x%x", (int32_t)ts->val);
+ break;
+#if TCG_TARGET_REG_BITS > 32
+ case TCG_TYPE_I64:
+ snprintf(buf, buf_size, "$0x%" PRIx64, ts->val);
+ break;
+#endif
+ case TCG_TYPE_V64:
+ case TCG_TYPE_V128:
+ case TCG_TYPE_V256:
+ snprintf(buf, buf_size, "v%d$0x%" PRIx64,
+ 64 << (ts->type - TCG_TYPE_V64), ts->val);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ break;
}
return buf;
}
state = TS_DEAD | TS_MEM;
break;
case TEMP_NORMAL:
+ case TEMP_CONST:
state = TS_DEAD;
break;
default:
la_global_sync(s, ng);
for (int i = ng; i < nt; ++i) {
- if (s->temps[i].kind == TEMP_LOCAL) {
- int state = s->temps[i].state;
- s->temps[i].state = state | TS_MEM;
+ TCGTemp *ts = &s->temps[i];
+ int state;
+
+ switch (ts->kind) {
+ case TEMP_LOCAL:
+ state = ts->state;
+ ts->state = state | TS_MEM;
if (state != TS_DEAD) {
continue;
}
- } else {
+ break;
+ case TEMP_NORMAL:
s->temps[i].state = TS_DEAD;
+ break;
+ case TEMP_CONST:
+ continue;
+ default:
+ g_assert_not_reached();
}
la_reset_pref(&s->temps[i]);
}
mark it free; otherwise mark it dead. */
static void temp_free_or_dead(TCGContext *s, TCGTemp *ts, int free_or_dead)
{
- if (temp_readonly(ts)) {
+ TCGTempVal new_type;
+
+ switch (ts->kind) {
+ case TEMP_FIXED:
return;
+ case TEMP_GLOBAL:
+ case TEMP_LOCAL:
+ new_type = TEMP_VAL_MEM;
+ break;
+ case TEMP_NORMAL:
+ new_type = free_or_dead < 0 ? TEMP_VAL_MEM : TEMP_VAL_DEAD;
+ break;
+ case TEMP_CONST:
+ new_type = TEMP_VAL_CONST;
+ break;
+ default:
+ g_assert_not_reached();
}
if (ts->val_type == TEMP_VAL_REG) {
s->reg_to_temp[ts->reg] = NULL;
}
- ts->val_type = (free_or_dead < 0
- || ts->kind != TEMP_NORMAL
- ? TEMP_VAL_MEM : TEMP_VAL_DEAD);
+ ts->val_type = new_type;
}
/* Mark a temporary as dead. */
static void temp_sync(TCGContext *s, TCGTemp *ts, TCGRegSet allocated_regs,
TCGRegSet preferred_regs, int free_or_dead)
{
- if (temp_readonly(ts)) {
- return;
- }
- if (!ts->mem_coherent) {
+ if (!temp_readonly(ts) && !ts->mem_coherent) {
if (!ts->mem_allocated) {
temp_allocate_frame(s, ts);
}
for (i = s->nb_globals; i < s->nb_temps; i++) {
TCGTemp *ts = &s->temps[i];
- if (ts->kind == TEMP_LOCAL) {
+
+ switch (ts->kind) {
+ case TEMP_LOCAL:
temp_save(s, ts, allocated_regs);
- } else {
+ break;
+ case TEMP_NORMAL:
/* The liveness analysis already ensures that temps are dead.
Keep an tcg_debug_assert for safety. */
tcg_debug_assert(ts->val_type == TEMP_VAL_DEAD);
+ break;
+ case TEMP_CONST:
+ /* Similarly, we should have freed any allocated register. */
+ tcg_debug_assert(ts->val_type == TEMP_VAL_CONST);
+ break;
+ default:
+ g_assert_not_reached();
}
}
* The liveness analysis already ensures that temps are dead.
* Keep tcg_debug_asserts for safety.
*/
- if (ts->kind == TEMP_LOCAL) {
+ switch (ts->kind) {
+ case TEMP_LOCAL:
tcg_debug_assert(ts->val_type != TEMP_VAL_REG || ts->mem_coherent);
- } else {
+ break;
+ case TEMP_NORMAL:
tcg_debug_assert(ts->val_type == TEMP_VAL_DEAD);
+ break;
+ case TEMP_CONST:
+ break;
+ default:
+ g_assert_not_reached();
}
}
}
i_preferred_regs = o_preferred_regs = 0;
if (arg_ct->ialias) {
o_preferred_regs = op->output_pref[arg_ct->alias_index];
- if (ts->kind == TEMP_FIXED) {
- /* if fixed register, we must allocate a new register
- if the alias is not the same register */
- if (arg != op->args[arg_ct->alias_index]) {
- goto allocate_in_reg;
- }
- } else {
- /* if the input is aliased to an output and if it is
- not dead after the instruction, we must allocate
- a new register and move it */
- if (!IS_DEAD_ARG(i)) {
- goto allocate_in_reg;
- }
- /* check if the current register has already been allocated
- for another input aliased to an output */
- if (ts->val_type == TEMP_VAL_REG) {
- int k2, i2;
- reg = ts->reg;
- for (k2 = 0 ; k2 < k ; k2++) {
- i2 = def->args_ct[nb_oargs + k2].sort_index;
- if (def->args_ct[i2].ialias && reg == new_args[i2]) {
- goto allocate_in_reg;
- }
+ /*
+ * If the input is readonly, then it cannot also be an
+ * output and aliased to itself. If the input is not
+ * dead after the instruction, we must allocate a new
+ * register and move it.
+ */
+ if (temp_readonly(ts) || !IS_DEAD_ARG(i)) {
+ goto allocate_in_reg;
+ }
+
+ /*
+ * Check if the current register has already been allocated
+ * for another input aliased to an output.
+ */
+ if (ts->val_type == TEMP_VAL_REG) {
+ reg = ts->reg;
+ for (int k2 = 0; k2 < k; k2++) {
+ int i2 = def->args_ct[nb_oargs + k2].sort_index;
+ if (def->args_ct[i2].ialias && reg == new_args[i2]) {
+ goto allocate_in_reg;
}
}
- i_preferred_regs = o_preferred_regs;
}
+ i_preferred_regs = o_preferred_regs;
}
temp_load(s, ts, arg_ct->regs, i_allocated_regs, i_preferred_regs);
reg = ts->reg;
- if (tcg_regset_test_reg(arg_ct->regs, reg)) {
- /* nothing to do : the constraint is satisfied */
- } else {
- allocate_in_reg:
- /* allocate a new register matching the constraint
- and move the temporary register into it */
+ if (!tcg_regset_test_reg(arg_ct->regs, reg)) {
+ allocate_in_reg:
+ /*
+ * Allocate a new register matching the constraint
+ * and move the temporary register into it.
+ */
temp_load(s, ts, tcg_target_available_regs[ts->type],
i_allocated_regs, 0);
reg = tcg_reg_alloc(s, arg_ct->regs, i_allocated_regs,