summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2026-08-11 12:15:39 -0700
committerMichael Tokarev <mjt@tls.msk.ru>2026-08-21 19:27:13 +0300
commitdab7a9f3a1042503f7caa8abeccb3e8efe462bd0 (patch)
tree4f83d90212b4d575bae72ba244dff70df423d47d
parentdd9a7a263c7e5037aded8b63a38127a04bc9fda0 (diff)
downloadqemu-dab7a9f3a1042503f7caa8abeccb3e8efe462bd0.tar.gz
qemu-dab7a9f3a1042503f7caa8abeccb3e8efe462bd0.zip
target/arm: Fix SVE2 WHILEWR/WHILERW zero diff boundary case
The trans_WHILE_ptr function incorrectly handles the case where the address difference divided by ESIZE results in zero. This happens when the address difference is less than ESIZE but greater than zero. Fix by dropping direct comparisons of op0 vs op1, and instead testing the scaled diff vs 0. Merge with the bounding to the maximum vector length via wrapping arithmetic. Cc: qemu-stable@nongnu.org Fixes: 14f6dad168e ("target/arm: Implement SVE2 WHILERW, WHILEWR") Reported-by: YanjunYang <yang.yanjun1@sanechips.com.cn> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20260811191540.79882-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> (cherry picked from commit bab972a2b0e34b889fbe8deb1e41cbea47e797f2) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
-rw-r--r--target/arm/tcg/translate-sve.c38
1 files changed, 24 insertions, 14 deletions
diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c
index a05967f4fb..9534453493 100644
--- a/target/arm/tcg/translate-sve.c
+++ b/target/arm/tcg/translate-sve.c
@@ -3523,7 +3523,7 @@ TRANS_FEAT(WHILE_gt_cnt4, aa64_sme2_or_sve2p1, do_WHILE,
static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a)
{
- TCGv_i64 op0, op1, diff, t1, tmax;
+ TCGv_i64 op0, op1, diff, t1;
TCGv_i32 t2;
TCGv_ptr ptr;
unsigned vsz = vec_full_reg_size(s);
@@ -3539,7 +3539,6 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a)
op0 = read_cpu_reg(s, a->rn, 1);
op1 = read_cpu_reg(s, a->rm, 1);
- tmax = tcg_constant_i64(vsz >> a->esz);
diff = tcg_temp_new_i64();
if (a->rw) {
@@ -3549,25 +3548,36 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a)
tcg_gen_sub_i64(diff, op0, op1);
tcg_gen_sub_i64(t1, op1, op0);
tcg_gen_movcond_i64(TCG_COND_GEU, diff, op0, op1, diff, t1);
- /* Divide, rounding down, by ESIZE. */
- tcg_gen_shri_i64(diff, diff, a->esz);
- /* If op1 == op0, diff == 0, and the condition is always true. */
- tcg_gen_movcond_i64(TCG_COND_EQ, diff, op0, op1, tmax, diff);
} else {
/* WHILEWR */
- tcg_gen_sub_i64(diff, op1, op0);
- /* Divide, rounding down, by ESIZE. */
- tcg_gen_shri_i64(diff, diff, a->esz);
- /* If op0 >= op1, diff <= 0, the condition is always true. */
- tcg_gen_movcond_i64(TCG_COND_GEU, diff, op0, op1, tmax, diff);
+ /* Saturating subtraction maps diff <= 0 to diff == 0. */
+ tcg_gen_ussub_i64(diff, op1, op0);
}
- /* Bound to the maximum. */
- tcg_gen_umin_i64(diff, diff, tmax);
+ /* Divide, rounding down, by ESIZE. */
+ tcg_gen_shri_i64(diff, diff, a->esz);
- /* Since we're bounded, pass as a 32-bit type. */
+ /*
+ * If diff == 0, the condition is always true. Also, bound to max.
+ * Simplify
+ * diff = diff ? diff : max;
+ * diff = umin(diff, max);
+ * via
+ * diff -= 1;
+ * diff = umin(diff, max - 1);
+ * diff += 1;
+ * via 0 - 1 == UINT64_MAX.
+ */
+ tcg_gen_addi_i64(diff, diff, -1);
+ tcg_gen_umin_i64(diff, diff, tcg_constant_i64((vsz >> a->esz) - 1));
+
+ /*
+ * Since we're bounded, pass as a 32-bit type.
+ * Sink the diff += 1 from above into the 32-bit type.
+ */
t2 = tcg_temp_new_i32();
tcg_gen_extrl_i64_i32(t2, diff);
+ tcg_gen_addi_i32(t2, t2, 1);
desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz / 8);
desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz);