Hi! As documented in the manual, FIX/UNSIGNED_FIX from floating point mode to integral mode has unspecified rounding and FIX from floating point mode to the same floating point mode is expressing rounding toward zero. So, some targets (arc, arm, csky, m68k, mmix, nds32, pdp11, sparc and visium) use (fix:SI (fix:SF (match_operand:SF 1 "..._operand"))) etc. to express the rounding toward zero during conversion to integer. For some reason other targets don't use that.
Anyway, the 2 FIXes (or inner FIX with outer UNSIGNED_FIX) cause problems since the r15-2890 which removed some strict checks in ifcvt.cc on what SET_SRC can be actually conditionalized (I must say I'm still worried about the change, don't know why one can't get e.g. inline asm or something with UNSPEC or some complex backend specific RTLs that force_operand can't handle), force_operand just ICEs on it, it can only handle (through expand_fix) conversions from floating point to integral. The following patch fixes this by detecting this case and just pretend the inner FIX isn't there, i.e. call expand_fix with the inner FIX's operand instead, which works and on targets like arm it will just create the nested FIXes again. Bootstrapped/regtested on x86_64-linux and i686-linux and Christophe tested this on arm, ok for trunk? 2025-02-27 Jakub Jelinek <ja...@redhat.com> PR rtl-optimization/117712 * expr.cc (force_operand): Handle {,UNSIGNED_}FIX with FIX operand using expand_fix on the inner FIX operand. * gcc.dg/pr117712.c: New test. --- gcc/expr.cc.jj 2025-01-31 15:12:57.892489693 +0100 +++ gcc/expr.cc 2025-02-27 12:07:06.079581662 +0100 @@ -8747,7 +8747,19 @@ force_operand (rtx value, rtx target) { if (!target) target = gen_reg_rtx (GET_MODE (value)); - op1 = force_operand (XEXP (value, 0), NULL_RTX); + /* FIX or UNSIGNED_FIX with integral mode has unspecified rounding, + while FIX with floating point mode rounds toward zero. So, some + targets use expressions like (fix:SI (fix:DF (reg:DF ...))) + to express rounding toward zero during the conversion to int. + expand_fix isn't able to handle that, it can only handle + FIX/UNSIGNED_FIX from floating point mode to integral one. */ + if ((code == FIX || code == UNSIGNED_FIX) + && GET_CODE (XEXP (value, 0)) == FIX + && (GET_MODE (XEXP (value, 0)) + == GET_MODE (XEXP (XEXP (value, 0), 0)))) + op1 = force_operand (XEXP (XEXP (value, 0), 0), NULL_RTX); + else + op1 = force_operand (XEXP (value, 0), NULL_RTX); switch (code) { case ZERO_EXTEND: --- gcc/testsuite/gcc.dg/pr117712.c.jj 2025-02-27 11:59:55.906554128 +0100 +++ gcc/testsuite/gcc.dg/pr117712.c 2025-02-27 12:04:27.397775236 +0100 @@ -0,0 +1,13 @@ +/* PR rtl-optimization/117712 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -ffast-math" } */ + +int b; + +int +foo (int x) +{ + if (b) + x = 0.96 * x; + return x; +} Jakub