https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127570
Bug ID: 127570
Summary: [17 Regression] Folding sign-changing conversions in
IVOPTS regresses code on RISC-V
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: law at gcc dot gnu.org
Target Milestone: ---
This change:
commit eb311f861255ae1fcaafa3454d4d1d6f1da07497
Author: liuhongt <[email protected]>
Date: Tue Aug 4 01:55:18 2026 -0700
tree-optimization/126486 - fold sign-changing conversions in IVOPTS
r15-6657 canonicalized signed arithmetic to unsigned and exposed a
17.5% code-size regression at -O2 in SPEC CPU2017 507.cactuBSSN_r.
The resulting conversion chains hide common outer-loop IV terms from
affine decomposition.
[ ... ]
Seems to be causing a pretty clear code quality regression on RISC-V. Let's
take the test vsetvl-23.c compiled with -mrvv-vector-bits=scalable
-march=rv32gcv -mabi=ilp32 -fno-schedule-insns -fno-schedule-insns2 -O2
/* { dg-do compile } */
/* { dg-options "-mrvv-vector-bits=scalable -march=rv32gcv -mabi=ilp32
-fno-schedule-insns -fno-schedule-insns2" } */
#include "riscv_vector.h"
void f(int8_t *base, int8_t *out, size_t vl, size_t m, size_t k) {
size_t avl;
switch (m)
{
case 50:
avl = __riscv_vsetvl_e16mf4(vl << 4);
break;
case 1:
avl = __riscv_vsetvl_e32mf2(k);
break;
case 2:
avl = __riscv_vsetvl_e64m1(vl);
break;
case 3:
avl = __riscv_vsetvl_e32mf2(k >> 8);
break;
default:
avl = __riscv_vsetvl_e32mf2(k + vl);
break;
}
for (size_t i = 0; i < m; i++) {
vint8mf8_t v0 = __riscv_vle8_v_i8mf8(base + i, avl);
v0 = __riscv_vadd_vv_i8mf8 (v0, v0, avl);
v0 = __riscv_vadd_vv_i8mf8_tu (v0, v0, v0, avl);
__riscv_vse8_v_i8mf8(out + i, v0, avl);
}
}
/* { dg-final { scan-assembler-times {slli\s+[a-x0-9]+,\s*[a-x0-9]+,\s*4} 1 {
target { no-opts "-O0" no-opts "-g" no-opts "-funroll-loops" } } } } */
/* { dg-final { scan-assembler-times {srli\s+[a-x0-9]+,\s*[a-x0-9]+,\s*8} 1 {
target { no-opts "-O0" no-opts "-g" no-opts "-funroll-loops" } } } } */
/* { dg-final { scan-assembler-times {vsetvli} 5 { target { no-opts "-O0"
no-opts "-Os" no-opts "-Oz" no-opts "-g" no-opts "-funroll-loops" } } } } */
It looks like the problem is the change obfuscates the loop exit test enough
that the threader doesn't realize that the loop exits without branching back on
at least some paths.
Before your change we have this loop in thread2:
;; basic block 12, loop depth 0, count 105119324 (estimated locally, freq
0.8900), maybe hot
;; prev block 11, next block 13, flags: (NEW, REACHABLE, VISITED)
;; pred: 11 [always] count:10630044 (estimated locally, freq 0.0900)
(FALLTHRU,EXECUTABLE)
;; 7 [always] count:23622320 (estimated locally, freq 0.2000)
(FALLTHRU,EXECUTABLE)
;; 10 [always] count:23622320 (estimated locally, freq 0.2000)
(FALLTHRU,EXECUTABLE)
;; 9 [always] count:23622320 (estimated locally, freq 0.2000)
(FALLTHRU,EXECUTABLE)
;; 8 [always] count:23622320 (estimated locally, freq 0.2000)
(FALLTHRU,EXECUTABLE)
# avl_36 = PHI <avl_16(11), avl_7(7), avl_30(10), avl_32(9), avl_34(8)>
;; succ: 13 [always] count:105119324 (estimated locally, freq 0.8900)
(FALLTHRU,EXECUTABLE)
;; basic block 13, loop depth 1, count 955630224 (estimated locally, freq
8.0909), maybe hot
;; prev block 12, next block 14, flags: (NEW, REACHABLE, VISITED)
;; pred: 13 [89.0% (guessed)] count:850510900 (estimated locally,
freq 7.2009) (TRUE_VALUE,EXECUTABLE)
;; 12 [always] count:105119324 (estimated locally, freq 0.8900)
(FALLTHRU,EXECUTABLE)
# i_27 = PHI <i_24(13), 0(12)>
_41 = (unsigned int) base_18(D);
_40 = i_27 + _41;
_4 = (int8_t *) _40;
v0_19 = __riscv_vle8_v_i8mf8 (_4, avl_36);
v0_20 = __riscv_vadd_vv_i8mf8 (v0_19, v0_19, avl_36);
v0_21 = __riscv_vadd_vv_i8mf8_tu (v0_20, v0_20, v0_20, avl_36);
_39 = (unsigned int) out_22(D);
_38 = i_27 + _39;
_5 = (int8_t *) _38;
__riscv_vse8_v_i8mf8 (_5, v0_21, avl_36);
i_24 = i_27 + 1;
if (m_9(D) > i_24)
goto <bb 13>; [89.00%]
else
goto <bb 14>; [11.00%]
Note the simple iteration variable "i" which has a known initial value of 0,
iterates by one each iteration of the loop and tests against m_9 for the exit
test. DOM is able to track the value of m_9 on paths entering the loop and is
able to thread the path bb8->bb12->bb13->bb14.
After your change the loop looks like:
;; basic block 12, loop depth 0, count 105119324 (estimated locally, freq
0.8900), maybe hot
;; prev block 11, next block 13, flags: (NEW, REACHABLE, VISITED)
;; pred: 11 [always] count:10630044 (estimated locally, freq 0.0900)
(FALLTHRU,EXECUTABLE)
;; 7 [always] count:23622320 (estimated locally, freq 0.2000)
(FALLTHRU,EXECUTABLE)
;; 10 [always] count:23622320 (estimated locally, freq 0.2000)
(FALLTHRU,EXECUTABLE)
;; 9 [always] count:23622320 (estimated locally, freq 0.2000)
(FALLTHRU,EXECUTABLE)
;; 8 [always] count:23622320 (estimated locally, freq 0.2000)
(FALLTHRU,EXECUTABLE)
# avl_36 = PHI <avl_16(11), avl_7(7), avl_30(10), avl_32(9), avl_34(8)>
ivtmp.6_39 = (unsigned int) base_18(D);
ivtmp.7_35 = (unsigned int) out_22(D);
_31 = m_9(D) + ivtmp.6_39;
;; succ: 13 [always] count:105119324 (estimated locally, freq 0.8900)
(FALLTHRU,EXECUTABLE)
;; basic block 13, loop depth 1, count 955630224 (estimated locally, freq
8.0909), maybe hot
;; prev block 12, next block 14, flags: (NEW, REACHABLE, VISITED)
;; pred: 13 [89.0% (guessed)] count:850510900 (estimated locally,
freq 7.2009) (TRUE_VALUE,EXECUTABLE)
;; 12 [always] count:105119324 (estimated locally, freq 0.8900)
(FALLTHRU,EXECUTABLE)
# ivtmp.6_41 = PHI <ivtmp.6_40(13), ivtmp.6_39(12)>
# ivtmp.7_38 = PHI <ivtmp.7_37(13), ivtmp.7_35(12)>
_4 = (int8_t *) ivtmp.6_41;
v0_19 = __riscv_vle8_v_i8mf8 (_4, avl_36);
v0_20 = __riscv_vadd_vv_i8mf8 (v0_19, v0_19, avl_36);
v0_21 = __riscv_vadd_vv_i8mf8_tu (v0_20, v0_20, v0_20, avl_36);
_5 = (int8_t *) ivtmp.7_38;
__riscv_vse8_v_i8mf8 (_5, v0_21, avl_36);
ivtmp.6_40 = ivtmp.6_41 + 1;
ivtmp.7_37 = ivtmp.7_38 + 1;
if (ivtmp.6_40 != _31)
goto <bb 13>; [89.00%]
else
goto <bb 14>; [11.00%]
Note the loop exit test is meaningfully obfuscated, which inhibits jump
threading and we get worse code.
I don't know if this is actually important in any way. It was just a
regression noted by my daily tester (about 3 dozen tests started failing, I've
only analyzed this one).