target/arm: mte_check unemitted on STORE_ONLY load
This feature disables generation of the mte check helper on loads when STORE_ONLY tag checking mode is enabled. Signed-off-by: Gabriel Brookman <brookmangabriel@gmail.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260529-feat-mte4-v7-3-ccbd3c14eb3c@gmail.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Gabriel Brookman committed
May 29, 2026 at 12:52 UTC
12670c82c344b0ab1f989222456222e2dcfff287
4 files changed
+22
-2
target/arm/cpu.h
+2
@@ -2534,6 +2534,8 @@ FIELD(TBFLAG_A64, GCS_EN, 41, 1)
2534
FIELD(TBFLAG_A64, GCS_RVCEN, 42, 1)
2535
FIELD(TBFLAG_A64, GCSSTR_EL, 43, 2)
2536
FIELD(TBFLAG_A64, FPMR_EL, 45, 2)
2537
+FIELD(TBFLAG_A64, MTE_STORE_ONLY, 47, 1)
2538
+FIELD(TBFLAG_A64, MTE0_STORE_ONLY, 48, 1)
2539
2540
/*
2541
* Helpers for using the above. Note that only the A64 accessors use
target/arm/tcg/hflags.c
+12
@@ -461,6 +461,15 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
461
*/
462
DP_TBFLAG_A64(flags, MTE0_ACTIVE, 1);
463
}
464
+ /*
465
+ * Repeat for MTE_STORE_ONLY
466
+ */
467
+ if ((el == 0 ? SCTLR_TCSO0 : SCTLR_TCSO) & sctlr) {
468
+ DP_TBFLAG_A64(flags, MTE_STORE_ONLY, 1);
469
+ if (!EX_TBFLAG_A64(flags, UNPRIV)) {
470
+ DP_TBFLAG_A64(flags, MTE0_STORE_ONLY, 1);
471
+ }
472
+ }
473
}
474
}
475
/* And again for unprivileged accesses, if required. */
@@ -470,6 +479,9 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el,
479
&& (sctlr & SCTLR_TCF0)
480
&& allocation_tag_access_enabled(env, 0, sctlr)) {
481
DP_TBFLAG_A64(flags, MTE0_ACTIVE, 1);
482
+ if (SCTLR_TCSO0 & sctlr) {
483
+ DP_TBFLAG_A64(flags, MTE0_STORE_ONLY, 1);
484
+ }
485
}
486
/*
487
* For unpriv tag-setting accesses we also need ATA0. Again, in
target/arm/tcg/translate-a64.c
+6
-2
@@ -302,7 +302,8 @@ static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr,
302
MemOp memop, bool is_unpriv,
303
int core_idx)
304
{
305
- if (tag_checked && s->mte_active[is_unpriv]) {
305
+ if (tag_checked && s->mte_active[is_unpriv] &&
306
+ (is_write || !s->mte_store_only[is_unpriv])) {
307
TCGv_i64 ret;
308
int desc = 0;
309
@@ -334,7 +335,8 @@ TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write,
335
TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write,
336
bool tag_checked, int total_size, MemOp single_mop)
337
{
337
- if (tag_checked && s->mte_active[0]) {
338
+ if (tag_checked && s->mte_active[0] &&
339
+ (is_write || !s->mte_store_only[0])) {
340
TCGv_i64 ret;
341
int desc = 0;
342
@@ -10805,6 +10807,8 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
10807
dc->ata[1] = EX_TBFLAG_A64(tb_flags, ATA0);
10808
dc->mte_active[0] = EX_TBFLAG_A64(tb_flags, MTE_ACTIVE);
10809
dc->mte_active[1] = EX_TBFLAG_A64(tb_flags, MTE0_ACTIVE);
10810
+ dc->mte_store_only[0] = EX_TBFLAG_A64(tb_flags, MTE_STORE_ONLY);
10811
+ dc->mte_store_only[1] = EX_TBFLAG_A64(tb_flags, MTE0_STORE_ONLY);
10812
dc->pstate_sm = EX_TBFLAG_A64(tb_flags, PSTATE_SM);
10813
dc->pstate_za = EX_TBFLAG_A64(tb_flags, PSTATE_ZA);
10814
dc->sme_trap_nonstreaming = EX_TBFLAG_A64(tb_flags, SME_TRAP_NONSTREAMING);
target/arm/tcg/translate.h
+2
@@ -140,6 +140,8 @@ typedef struct DisasContext {
140
bool ata[2];
141
/* True if v8.5-MTE tag checks affect the PE; index with is_unpriv. */
142
bool mte_active[2];
143
+ /* True if v8.5-MTE tag checks disabled for reads; index with is_unpriv. */
144
+ bool mte_store_only[2];
145
/* True with v8.5-BTI and SCTLR_ELx.BT* set. */
146
bool bt;
147
/* True if any CP15 access is trapped by HSTR_EL2 */