https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61876

            Bug ID: 61876
           Summary: Converting __builtin_round + cast into
                    __builtin_lround is not always equivalent in regards
                    to math errno
           Product: gcc
           Version: 4.10.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ktkachov at gcc dot gnu.org

Consider code:

long int
foo (double a)
{
   return __builtin_round (a);
}

With an aarch64-none-elf toolchain this compiles to:
foo:
        fcvtas  x0, d0
        ret
whereas with an aarch64-linux toolchain this compiles to:
foo:
        b       lround

The linux (glibc) toolchain does not inline the lround implementation.

The suspicious starting point is this code in convert.c:
        CASE_FLT_FN (BUILT_IN_ROUND):
          /* Only convert in ISO C99 mode.  */
          if (!targetm.libc_has_function (function_c99_misc))
            break;
          if (outprec < TYPE_PRECISION (integer_type_node)
              || (outprec == TYPE_PRECISION (integer_type_node)
                  && !TYPE_UNSIGNED (type)))
            fn = mathfn_built_in (s_intype, BUILT_IN_IROUND);
          else if (outprec == TYPE_PRECISION (long_integer_type_node)
                   && !TYPE_UNSIGNED (type))
            fn = mathfn_built_in (s_intype, BUILT_IN_LROUND);
          else if (outprec == TYPE_PRECISION (long_long_integer_type_node)
                   && !TYPE_UNSIGNED (type))
            fn = mathfn_built_in (s_intype, BUILT_IN_LLROUND);
          break;
Basically it does the conversion of (cast to long int + round) == lround
But later on in builtins.c the lround does not get expanded into the sfix optab
unless -fno-math-errno is specified:

  /* There's no easy way to detect the case we need to set EDOM.  */
  if (!flag_errno_math)
    {
      rtx result = gen_reg_rtx (mode);

      /* Wrap the computation of the argument in a SAVE_EXPR, as we may
         need to expand the argument again.  This way, we will not perform
         side-effects more the once.  */
      CALL_EXPR_ARG (exp, 0) = arg = builtin_save_expr (arg);

      op0 = expand_expr (arg, NULL, VOIDmode, EXPAND_NORMAL);

      start_sequence ();

      if (expand_sfix_optab (result, op0, builtin_optab))
        {
          /* Output the entire sequence.  */
          insns = get_insns ();
          end_sequence ();
          emit_insn (insns);
          return result;
        }

I think if the cast+round -> lround transformation is done it should be assumed
in that case that lround will not set errno.

Another approach would be to not do the transformation unless -fno-math-errno

Reply via email to