target/arm: Implement VSCCLRM insn

Implement the v8.1M VSCCLRM insn, which zeros floating point
registers if there is an active floating point context.
This requires support in write_neon_element32() for the MO_32
element size, so add it.

Because we want to use arm_gen_condlabel(), we need to move
the definition of that function up in translate.c so it is
before the #include of translate-vfp.c.inc.

Backports 83ff3d6add965c9752324de11eac5687121ea826
This commit is contained in:
Peter Maydell 2021-03-03 17:55:35 -05:00 committed by Lioncash
parent 952ebdc207
commit 43d8441881
4 changed files with 113 additions and 13 deletions

View file

@ -3405,6 +3405,15 @@ static inline bool isar_feature_aa32_mprofile(const ARMISARegisters *id)
return FIELD_EX32(id->id_pfr1, ID_PFR1, MPROGMOD) != 0;
}
static inline bool isar_feature_aa32_m_sec_state(const ARMISARegisters *id)
{
/*
* Return true if M-profile state handling insns
* (VSCCLRM, CLRM, FPCTX access insns) are implemented
*/
return FIELD_EX32(id->id_pfr1, ID_PFR1, SECURITY) >= 3;
}
static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id)
{
/* Sadly this is encoded differently for A-profile and M-profile */

View file

@ -29,13 +29,17 @@
# If the coprocessor is not present or disabled then we will generate
# the NOCP exception; otherwise we let the insn through to the main decode.
%vd_dp 22:1 12:4
%vd_sp 12:4 22:1
&nocp cp
{
# Special cases which do not take an early NOCP: VLLDM and VLSTM
VLLDM_VLSTM 1110 1100 001 l:1 rn:4 0000 1010 0000 0000
# TODO: VSCCLRM (new in v8.1M) is similar:
#VSCCLRM 1110 1100 1-01 1111 ---- 1011 ---- ---0
# VSCCLRM (new in v8.1M) is similar:
VSCCLRM 1110 1100 1.01 1111 .... 1011 imm:7 0 vd=%vd_dp size=3
VSCCLRM 1110 1100 1.01 1111 .... 1010 imm:8 vd=%vd_sp size=2
NOCP 111- 1110 ---- ---- ---- cp:4 ---- ---- &nocp
NOCP 111- 110- ---- ---- ---- cp:4 ---- ---- &nocp

View file

@ -3468,6 +3468,91 @@ static bool trans_VLLDM_VLSTM(DisasContext *s, arg_VLLDM_VLSTM *a)
return true;
}
static bool trans_VSCCLRM(DisasContext *s, arg_VSCCLRM *a)
{
TCGContext *tcg_ctx = s->uc->tcg_ctx;
int btmreg, topreg;
TCGv_i64 zero;
TCGv_i32 aspen, sfpa;
if (!dc_isar_feature(aa32_m_sec_state, s)) {
/* Before v8.1M, fall through in decode to NOCP check */
return false;
}
/* Explicitly UNDEF because this takes precedence over NOCP */
if (!arm_dc_feature(s, ARM_FEATURE_M_MAIN) || !s->v8m_secure) {
unallocated_encoding(s);
return true;
}
if (!dc_isar_feature(aa32_vfp_simd, s)) {
/* NOP if we have neither FP nor MVE */
return true;
}
/*
* If FPCCR.ASPEN != 0 && CONTROL_S.SFPA == 0 then there is no
* active floating point context so we must NOP (without doing
* any lazy state preservation or the NOCP check).
*/
aspen = load_cpu_field(s, v7m.fpccr[M_REG_S]);
sfpa = load_cpu_field(s, v7m.control[M_REG_S]);
tcg_gen_andi_i32(tcg_ctx, aspen, aspen, R_V7M_FPCCR_ASPEN_MASK);
tcg_gen_xori_i32(tcg_ctx, aspen, aspen, R_V7M_FPCCR_ASPEN_MASK);
tcg_gen_andi_i32(tcg_ctx, sfpa, sfpa, R_V7M_CONTROL_SFPA_MASK);
tcg_gen_or_i32(tcg_ctx, sfpa, sfpa, aspen);
arm_gen_condlabel(s);
tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, sfpa, 0, s->condlabel);
if (s->fp_excp_el != 0) {
gen_exception_insn(s, s->pc_curr, EXCP_NOCP,
syn_uncategorized(), s->fp_excp_el);
return true;
}
topreg = a->vd + a->imm - 1;
btmreg = a->vd;
/* Convert to Sreg numbers if the insn specified in Dregs */
if (a->size == 3) {
topreg = topreg * 2 + 1;
btmreg *= 2;
}
if (topreg > 63 || (topreg > 31 && !(topreg & 1))) {
/* UNPREDICTABLE: we choose to undef */
unallocated_encoding(s);
return true;
}
/* Silently ignore requests to clear D16-D31 if they don't exist */
if (topreg > 31 && !dc_isar_feature(aa32_simd_r32, s)) {
topreg = 31;
}
if (!vfp_access_check(s)) {
return true;
}
/* Zero the Sregs from btmreg to topreg inclusive. */
zero = tcg_const_i64(tcg_ctx, 0);
if (btmreg & 1) {
write_neon_element64(s, zero, btmreg >> 1, 1, MO_32);
btmreg++;
}
for (; btmreg + 1 <= topreg; btmreg += 2) {
write_neon_element64(s, zero, btmreg >> 1, 0, MO_64);
}
if (btmreg == topreg) {
write_neon_element64(s, zero, btmreg >> 1, 0, MO_32);
btmreg++;
}
assert(btmreg == topreg + 1);
/* TODO: when MVE is implemented, zero VPR here */
return true;
}
static bool trans_NOCP(DisasContext *s, arg_NOCP *a)
{
/*

View file

@ -93,6 +93,16 @@ void arm_translate_init(struct uc_struct *uc)
a64_translate_init(uc);
}
/* Generate a label used for skipping this instruction */
static void arm_gen_condlabel(DisasContext *s)
{
if (!s->condjmp) {
TCGContext *tcg_ctx = s->uc->tcg_ctx;
s->condlabel = gen_new_label(tcg_ctx);
s->condjmp = 1;
}
}
/* Flags for the disas_set_da_iss info argument:
* lower bits hold the Rt register number, higher bits are flags.
*/
@ -1273,6 +1283,9 @@ static void write_neon_element64(DisasContext *s, TCGv_i64 src, int reg, int ele
long off = neon_element_offset(reg, ele, memop);
switch (memop) {
case MO_32:
tcg_gen_st32_i64(tcg_ctx, src, tcg_ctx->cpu_env, off);
break;
case MO_64:
tcg_gen_st_i64(tcg_ctx, src, tcg_ctx->cpu_env, off);
break;
@ -5236,17 +5249,6 @@ static void gen_srs(DisasContext *s,
s->base.is_jmp = DISAS_UPDATE_EXIT;
}
/* Generate a label used for skipping this instruction */
static void arm_gen_condlabel(DisasContext *s)
{
if (!s->condjmp) {
TCGContext *tcg_ctx = s->uc->tcg_ctx;
s->condlabel = gen_new_label(tcg_ctx);
s->condjmp = 1;
}
}
/* Skip this instruction if the ARM condition is false */
static void arm_skip_unless(DisasContext *s, uint32_t cond)
{