From 26e4783c707a96ebb42c6ec4537578e635974573 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Fri, 28 Feb 2025 12:42:27 +0100 Subject: [PATCH] ifcvt: Fix ICE with (fix:SI (fix:DF (reg:DF))) [PR117712] 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. 2025-02-28 Jakub Jelinek 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 | 14 +++++++++++++- gcc/testsuite/gcc.dg/pr117712.c | 13 +++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.dg/pr117712.c diff --git a/gcc/expr.cc b/gcc/expr.cc index f684e26cef7..9f4382d7986 100644 --- a/gcc/expr.cc +++ b/gcc/expr.cc @@ -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: diff --git a/gcc/testsuite/gcc.dg/pr117712.c b/gcc/testsuite/gcc.dg/pr117712.c new file mode 100644 index 00000000000..7377a610c06 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr117712.c @@ -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; +} -- 2.47.2