@samitouri / QOSamiQemu / commits / bab972a2b0

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>

Richard Henderson committed Aug 11, 2026 at 12:15 UTC bab972a2b0e34b889fbe8deb1e41cbea47e797f2
1 file changed +24 -14
target/arm/tcg/translate-sve.c
+24 -14
@@ -3697,7 +3697,7 @@ TRANS_FEAT(WHILE_gt_cnt4, aa64_sme2_or_sve2p1, do_WHILE,
3697
3698 static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a)
3699 {
3700 - TCGv_i64 op0, op1, diff, t1, tmax;
3700 + TCGv_i64 op0, op1, diff, t1;
3701 TCGv_i32 t2;
3702 TCGv_ptr ptr;
3703 unsigned vsz = vec_full_reg_size(s);
@@ -3713,7 +3713,6 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a)
3713 op0 = read_cpu_reg(s, a->rn, 1);
3714 op1 = read_cpu_reg(s, a->rm, 1);
3715
3716 - tmax = tcg_constant_i64(vsz >> a->esz);
3716 diff = tcg_temp_new_i64();
3717
3718 if (a->rw) {
@@ -3723,25 +3722,36 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a)
3722 tcg_gen_sub_i64(diff, op0, op1);
3723 tcg_gen_sub_i64(t1, op1, op0);
3724 tcg_gen_movcond_i64(TCG_COND_GEU, diff, op0, op1, diff, t1);
3726 - /* Divide, rounding down, by ESIZE. */
3727 - tcg_gen_shri_i64(diff, diff, a->esz);
3728 - /* If op1 == op0, diff == 0, and the condition is always true. */
3729 - tcg_gen_movcond_i64(TCG_COND_EQ, diff, op0, op1, tmax, diff);
3725 } else {
3726 /* WHILEWR */
3732 - tcg_gen_sub_i64(diff, op1, op0);
3733 - /* Divide, rounding down, by ESIZE. */
3734 - tcg_gen_shri_i64(diff, diff, a->esz);
3735 - /* If op0 >= op1, diff <= 0, the condition is always true. */
3736 - tcg_gen_movcond_i64(TCG_COND_GEU, diff, op0, op1, tmax, diff);
3727 + /* Saturating subtraction maps diff <= 0 to diff == 0. */
3728 + tcg_gen_ussub_i64(diff, op1, op0);
3729 }
3730
3739 - /* Bound to the maximum. */
3740 - tcg_gen_umin_i64(diff, diff, tmax);
3731 + /* Divide, rounding down, by ESIZE. */
3732 + tcg_gen_shri_i64(diff, diff, a->esz);
3733
3742 - /* Since we're bounded, pass as a 32-bit type. */
3734 + /*
3735 + * If diff == 0, the condition is always true. Also, bound to max.
3736 + * Simplify
3737 + * diff = diff ? diff : max;
3738 + * diff = umin(diff, max);
3739 + * via
3740 + * diff -= 1;
3741 + * diff = umin(diff, max - 1);
3742 + * diff += 1;
3743 + * via 0 - 1 == UINT64_MAX.
3744 + */
3745 + tcg_gen_addi_i64(diff, diff, -1);
3746 + tcg_gen_umin_i64(diff, diff, tcg_constant_i64((vsz >> a->esz) - 1));
3747 +
3748 + /*
3749 + * Since we're bounded, pass as a 32-bit type.
3750 + * Sink the diff += 1 from above into the 32-bit type.
3751 + */
3752 t2 = tcg_temp_new_i32();
3753 tcg_gen_extrl_i64_i32(t2, diff);
3754 + tcg_gen_addi_i32(t2, t2, 1);
3755
3756 desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz / 8);
3757 desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz);