diff options
| author | Richard Henderson <richard.henderson@linaro.org> | 2026-08-11 12:15:39 -0700 |
|---|---|---|
| committer | Michael Tokarev <mjt@tls.msk.ru> | 2026-08-21 19:27:13 +0300 |
| commit | dab7a9f3a1042503f7caa8abeccb3e8efe462bd0 (patch) | |
| tree | 4f83d90212b4d575bae72ba244dff70df423d47d | |
| parent | dd9a7a263c7e5037aded8b63a38127a04bc9fda0 (diff) | |
| download | qemu-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.c | 38 |
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); |
