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.

Reply via email to