tcg: Restart TB generation after relocation overflow

If the TB generates too much code, such that backend relocations
overflow, try again with a smaller TB. In support of this, move
relocation processing from a random place within tcg_out_op, in
the handling of branch opcodes, to a new function at the end of
tcg_gen_code.

This is not a complete solution, as there are additional relocs
generated for out-of-line ldst handling and constant pools.

Backports commit 7ecd02a06f8f4c0bbf872ecc15e37035b7e1df5f from qemu
This commit is contained in:
Richard Henderson 2019-04-30 09:58:44 -04:00 committed by Lioncash
parent 434b3ab9ec
commit 45315fd8ef
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7
2 changed files with 40 additions and 39 deletions

View file

@ -211,37 +211,18 @@ static QEMU_UNUSED_FUNC inline void tcg_patch64(tcg_insn_unit *p,
static void tcg_out_reloc(TCGContext *s, tcg_insn_unit *code_ptr, int type,
TCGLabel *l, intptr_t addend)
{
TCGRelocation *r;
TCGRelocation *r = tcg_malloc(s, sizeof(TCGRelocation));
if (l->has_value) {
/* FIXME: This may break relocations on RISC targets that
modify instruction fields in place. The caller may not have
written the initial value. */
bool ok = patch_reloc(code_ptr, type, l->u.value, addend);
tcg_debug_assert(ok);
} else {
/* add a new relocation entry */
r = tcg_malloc(s, sizeof(TCGRelocation));
r->type = type;
r->ptr = code_ptr;
r->addend = addend;
r->next = l->u.first_reloc;
l->u.first_reloc = r;
}
r->type = type;
r->ptr = code_ptr;
r->addend = addend;
QSIMPLEQ_INSERT_TAIL(&l->relocs, r, next);
}
static void tcg_out_label(TCGContext *s, TCGLabel *l, tcg_insn_unit *ptr)
{
intptr_t value = (intptr_t)ptr;
TCGRelocation *r;
tcg_debug_assert(!l->has_value);
for (r = l->u.first_reloc; r != NULL; r = r->next) {
bool ok = patch_reloc(r->ptr, r->type, value, r->addend);
tcg_debug_assert(ok);
}
l->has_value = 1;
l->u.value_ptr = ptr;
}
@ -249,15 +230,33 @@ static void tcg_out_label(TCGContext *s, TCGLabel *l, tcg_insn_unit *ptr)
TCGLabel *gen_new_label(TCGContext *s)
{
TCGLabel *l = tcg_malloc(s, sizeof(TCGLabel));
TCGLabel ltmp = {0};
ltmp.id = s->nb_labels++;
*l = ltmp;
#ifdef CONFIG_DEBUG_TCG
memset(l, 0, sizeof(TCGLabel));
l->id = s->nb_labels++;
QSIMPLEQ_INIT(&l->relocs);
QSIMPLEQ_INSERT_TAIL(&s->labels, l, next);
#endif
return l;
}
static bool tcg_resolve_relocs(TCGContext *s)
{
TCGLabel *l;
QSIMPLEQ_FOREACH(l, &s->labels, next) {
TCGRelocation *r;
uintptr_t value = l->u.value;
QSIMPLEQ_FOREACH(r, &l->relocs, next) {
if (!patch_reloc(r->ptr, r->type, value, r->addend)) {
return false;
}
}
}
return true;
}
#include "tcg-target.inc.c"
/* pool based memory allocation */
@ -517,9 +516,7 @@ void tcg_func_start(TCGContext *s)
QTAILQ_INIT(&s->ops);
QTAILQ_INIT(&s->free_ops);
#ifdef CONFIG_DEBUG_TCG
QSIMPLEQ_INIT(&s->labels);
#endif
}
static inline TCGTemp *tcg_temp_alloc(TCGContext *s)
@ -3386,6 +3383,10 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb)
}
#endif
if (!tcg_resolve_relocs(s)) {
return -2;
}
/* flush instruction cache */
flush_icache_range((uintptr_t)s->code_buf, (uintptr_t)s->code_ptr);

View file

@ -244,12 +244,13 @@ typedef uint64_t tcg_insn_unit;
# endif
#endif
typedef struct TCGRelocation {
struct TCGRelocation *next;
int type;
typedef struct TCGRelocation TCGRelocation;
struct TCGRelocation {
QSIMPLEQ_ENTRY(TCGRelocation) next;
tcg_insn_unit *ptr;
intptr_t addend;
} TCGRelocation;
int type;
};
typedef struct TCGLabel TCGLabel;
struct TCGLabel {
@ -260,11 +261,10 @@ struct TCGLabel {
union {
uintptr_t value;
tcg_insn_unit *value_ptr;
TCGRelocation *first_reloc;
} u;
#ifdef CONFIG_DEBUG_TCG
QSIMPLEQ_HEAD(, TCGRelocation) relocs;
QSIMPLEQ_ENTRY(TCGLabel) next;
#endif
};
typedef struct TCGPool {
@ -784,7 +784,6 @@ struct TCGContext {
#endif
#ifdef CONFIG_DEBUG_TCG
QSIMPLEQ_HEAD(, TCGLabel) labels;
int temps_in_use;
int goto_tb_issue_mask;
#endif
@ -821,6 +820,7 @@ struct TCGContext {
TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */
QTAILQ_HEAD(TCGOpHead, TCGOp) ops, free_ops;
QSIMPLEQ_HEAD(, TCGLabel) labels;
/* Tells which temporary holds a given register.
It does not take into account fixed registers */