target/arm: Split out add_cpreg_to_hashtable_aa{32, 64}

The nesting level for the inner loop of define_one_arm_cp_reg
was overly deep.  Split out that code into two functions, for
the AArch32 and AArch64 paths separately.  Simplify the innermost
loop to a switch statement over r->state.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Richard Henderson 2025-09-16 07:22:21 -07:00 committed by Peter Maydell
parent 398a33aeda
commit 3b230602e2

View file

@ -7557,6 +7557,66 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r,
g_hash_table_insert(cpu->cp_regs, (gpointer)(uintptr_t)key, r2);
}
static void add_cpreg_to_hashtable_aa32(ARMCPU *cpu, const ARMCPRegInfo *r,
int crm, int opc1, int opc2)
{
/*
* Under AArch32 CP registers can be common
* (same for secure and non-secure world) or banked.
*/
char *name;
assert(!(r->type & ARM_CP_ADD_TLBI_NXS)); /* aa64 only */
switch (r->secure) {
case ARM_CP_SECSTATE_S:
case ARM_CP_SECSTATE_NS:
add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32,
r->secure, crm, opc1, opc2, r->name);
break;
case ARM_CP_SECSTATE_BOTH:
name = g_strdup_printf("%s_S", r->name);
add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32,
ARM_CP_SECSTATE_S, crm, opc1, opc2, name);
g_free(name);
add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA32,
ARM_CP_SECSTATE_NS, crm, opc1, opc2, r->name);
break;
default:
g_assert_not_reached();
}
}
static void add_cpreg_to_hashtable_aa64(ARMCPU *cpu, const ARMCPRegInfo *r,
int crm, int opc1, int opc2)
{
if ((r->type & ARM_CP_ADD_TLBI_NXS) &&
cpu_isar_feature(aa64_xs, cpu)) {
/*
* This is a TLBI insn which has an NXS variant. The
* NXS variant is at the same encoding except that
* crn is +1, and has the same behaviour except for
* fine-grained trapping. Add the NXS insn here and
* then fall through to add the normal register.
* add_cpreg_to_hashtable() copies the cpreg struct
* and name that it is passed, so it's OK to use
* a local struct here.
*/
ARMCPRegInfo nxs_ri = *r;
g_autofree char *name = g_strdup_printf("%sNXS", r->name);
assert(nxs_ri.crn < 0xf);
nxs_ri.crn++;
if (nxs_ri.fgt) {
nxs_ri.fgt |= R_FGT_NXS_MASK;
}
add_cpreg_to_hashtable(cpu, &nxs_ri, ARM_CP_STATE_AA64,
ARM_CP_SECSTATE_NS, crm, opc1, opc2, name);
}
add_cpreg_to_hashtable(cpu, r, ARM_CP_STATE_AA64, ARM_CP_SECSTATE_NS,
crm, opc1, opc2, r->name);
}
void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r)
{
@ -7584,14 +7644,12 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r)
* bits; the ARM_CP_64BIT* flag applies only to the AArch32 view of
* the register, if any.
*/
int crm, opc1, opc2;
int crmmin = (r->crm == CP_ANY) ? 0 : r->crm;
int crmmax = (r->crm == CP_ANY) ? 15 : r->crm;
int opc1min = (r->opc1 == CP_ANY) ? 0 : r->opc1;
int opc1max = (r->opc1 == CP_ANY) ? 7 : r->opc1;
int opc2min = (r->opc2 == CP_ANY) ? 0 : r->opc2;
int opc2max = (r->opc2 == CP_ANY) ? 7 : r->opc2;
CPState state;
/* 64 bit registers have only CRm and Opc1 fields */
assert(!((r->type & ARM_CP_64BIT) && (r->opc2 || r->crn)));
@ -7688,75 +7746,22 @@ void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *r)
}
}
for (crm = crmmin; crm <= crmmax; crm++) {
for (opc1 = opc1min; opc1 <= opc1max; opc1++) {
for (opc2 = opc2min; opc2 <= opc2max; opc2++) {
for (state = ARM_CP_STATE_AA32;
state <= ARM_CP_STATE_AA64; state++) {
if (r->state != state && r->state != ARM_CP_STATE_BOTH) {
continue;
}
if ((r->type & ARM_CP_ADD_TLBI_NXS) &&
cpu_isar_feature(aa64_xs, cpu)) {
/*
* This is a TLBI insn which has an NXS variant. The
* NXS variant is at the same encoding except that
* crn is +1, and has the same behaviour except for
* fine-grained trapping. Add the NXS insn here and
* then fall through to add the normal register.
* add_cpreg_to_hashtable() copies the cpreg struct
* and name that it is passed, so it's OK to use
* a local struct here.
*/
ARMCPRegInfo nxs_ri = *r;
g_autofree char *name = g_strdup_printf("%sNXS", r->name);
assert(state == ARM_CP_STATE_AA64);
assert(nxs_ri.crn < 0xf);
nxs_ri.crn++;
if (nxs_ri.fgt) {
nxs_ri.fgt |= R_FGT_NXS_MASK;
}
add_cpreg_to_hashtable(cpu, &nxs_ri, state,
ARM_CP_SECSTATE_NS,
crm, opc1, opc2, name);
}
if (state == ARM_CP_STATE_AA32) {
/*
* Under AArch32 CP registers can be common
* (same for secure and non-secure world) or banked.
*/
char *name;
switch (r->secure) {
case ARM_CP_SECSTATE_S:
case ARM_CP_SECSTATE_NS:
add_cpreg_to_hashtable(cpu, r, state,
r->secure, crm, opc1, opc2,
r->name);
break;
case ARM_CP_SECSTATE_BOTH:
name = g_strdup_printf("%s_S", r->name);
add_cpreg_to_hashtable(cpu, r, state,
ARM_CP_SECSTATE_S,
crm, opc1, opc2, name);
g_free(name);
add_cpreg_to_hashtable(cpu, r, state,
ARM_CP_SECSTATE_NS,
crm, opc1, opc2, r->name);
break;
default:
g_assert_not_reached();
}
} else {
/*
* AArch64 registers get mapped to non-secure instance
* of AArch32
*/
add_cpreg_to_hashtable(cpu, r, state,
ARM_CP_SECSTATE_NS,
crm, opc1, opc2, r->name);
}
for (int crm = crmmin; crm <= crmmax; crm++) {
for (int opc1 = opc1min; opc1 <= opc1max; opc1++) {
for (int opc2 = opc2min; opc2 <= opc2max; opc2++) {
switch (r->state) {
case ARM_CP_STATE_AA32:
add_cpreg_to_hashtable_aa32(cpu, r, crm, opc1, opc2);
break;
case ARM_CP_STATE_AA64:
add_cpreg_to_hashtable_aa64(cpu, r, crm, opc1, opc2);
break;
case ARM_CP_STATE_BOTH:
add_cpreg_to_hashtable_aa32(cpu, r, crm, opc1, opc2);
add_cpreg_to_hashtable_aa64(cpu, r, crm, opc1, opc2);
break;
default:
g_assert_not_reached();
}
}
}