https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128301
Bug ID: 128301
Summary: -fharden-compares / -fharden-conditional-branches
silently skips all ordered floating-point comparisons
Product: gcc
Version: 16.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: middle-end
Assignee: unassigned at gcc dot gnu.org
Reporter: 220245569 at seu dot edu.cn
CC: 220255623 at seu dot edu.cn, jianhao.xu at seu dot edu.cn
Target Milestone: ---
Target: *-*-*
Summary
-------
Under `-O2 -fharden-compares -fharden-conditional-branches` (with default
`-ftrapping-math`), GCC silently skips hardening 100% of ordered floating-point
comparisons (`<`, `<=`, `>`, `>=`).
In `gcc/gimple-harden-conditionals.cc:353-363` and `469-477`, the pass computes
the inverted comparison via `invert_tree_comparison (op, HONOR_NANS (op1))`:
enum tree_code inv = invert_tree_comparison (op, HONOR_NANS (op1));
if (inv == ERROR_MARK)
{
/* ??? Can we do better? */
continue;
}
Under default `-ftrapping-math` (`flag_trapping_math == 1`),
`invert_tree_comparison`
(`gcc/fold-const.cc:2913`) returns `ERROR_MARK` for `LT_EXPR`, `LE_EXPR`,
`GT_EXPR`, and `GE_EXPR` whenever `HONOR_NANS` is true because inverting an
ordered signaling comparison (`comisd`) into an unordered comparison
(`ucomisd`)
in isolation would alter IEEE `FE_INVALID` exception behavior on QNaN.
Consequently, the pass hits `continue` on every ordered float/double comparison
(and the 55-line `throwing_compare_p` EH-edge splitting code at lines 526-580
is unreachable for `<, <=, >, >=` under default flags).
However, in `gimple-harden-conditionals`, the original ordered comparison is
still executed first and already raises `FE_INVALID` on NaN; emitting the
non-signaling unordered inverse (`invert_tree_comparison (op, false)`) for the
redundant check preserves exact IEEE exception semantics.
Reproducer
----------
$ cat trigger.c
int check_fp_limit(double val, double max_allowed) {
return val <= max_allowed;
}
void allow_op(void);
void branch_fp_limit(double val, double max_allowed) {
if (val <= max_allowed)
allow_op();
}
Compile flag: gcc -O2 -fharden-compares -fharden-conditional-branches -S
trigger.c
$ cat trigger.s
check_fp_limit:
xorl %eax, %eax
comisd %xmm0, %xmm1 # single comparison; zero redundant
check or ud2!
setnb %al
ret
branch_fp_limit:
comisd %xmm0, %xmm1 # single branch; zero redundant check!
jnb .L8
ret
.L8:
jmp allow_op
Suggested Fix
-------------
In `gcc/gimple-harden-conditionals.cc:353-363` and `469-477`, when
`invert_tree_comparison (op, HONOR_NANS (op1))` returns `ERROR_MARK`, fall back
to the non-signaling unordered inverse `invert_tree_comparison (op, false)`
executed after the original ordered comparison.