Illegal instruction in std::remainder (libsystem_m.dylib) when floating point exceptions enabled on M1 mac

Call to std::remainder(double(411.0), int(365)); results in a crash due to a nan in libsystem_m.dylib. MCVE program is provided + lldb backtrace and system report.


$ clang++ -g -arch arm64 -std=c++20 main.cpp -o test
$ ./test
ori_fpcr=0, new_fpcr=1792
std::fmod(simTimeInDays, numDays) = 46
Illegal instruction: 4

main.cpp

#include <cassert>
#include <cfenv>
#include <cmath>
#include <iostream>

#if !defined(__arm64__) || !defined(__APPLE__)
#  error "Meant to be run on arm64 apple"
#endif

inline int feenableexcept(unsigned int excepts) {
  static fenv_t fenv;
  if (std::fegetenv(&fenv) != 0) {
    return -1;
  }
  const unsigned long long old_fpcr = fenv.__fpcr;
  const unsigned int old_excepts = (old_fpcr >> 8u) & unsigned(FE_ALL_EXCEPT);

  // Check the bits passed are valid, and bit shift them
  const unsigned int new_excepts = excepts & unsigned(FE_ALL_EXCEPT);
  const unsigned long long new_fpcr = new_excepts << 8u;

  // Set the new bits
  fenv.__fpcr = fenv.__fpcr | new_fpcr;

  return (std::fesetenv(&fenv) != 0) ? -1 : static_cast<int>(old_excepts);
}

int main([[maybe_unused]] int argc, [[maybe_unused]] const char** argv) {

  constexpr unsigned int flags = FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW;
  static_assert(flags == 7);
  constexpr uint32_t fpcr_flags_shifted = flags << 8;
  constexpr uint32_t fpcr_flags = (__fpcr_trap_divbyzero | __fpcr_trap_invalid | __fpcr_trap_overflow);
  static_assert(fpcr_flags_shifted == fpcr_flags);
  static_assert(fpcr_flags_shifted == 1792);

  uint32_t ori_fpcr = __builtin_arm_rsr("fpcr");
  feenableexcept(flags);
  uint32_t new_fpcr = __builtin_arm_rsr("fpcr");

  // std::cout << "(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW) = " << flags << '\n';
  // std::cout << "((FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW) << 8) = " << fpcr_flags_shifted << '\n';
  // std::cout << "(__fpcr_trap_divbyzero | __fpcr_trap_invalid | __fpcr_trap_overflow) = " << fpcr_flags << '\n';
  std::cout << "ori_fpcr=" << ori_fpcr << ", new_fpcr=" << new_fpcr << '\n';

  const double simTimeInDays = 411.0;
  const int numDays = 365;

  // This is fine
  std::cout << "std::fmod(simTimeInDays, numDays) = " << std::fmod(simTimeInDays, numDays) << '\n';

  // This isn't
  std::cout << "std::fmod(simTimeInDays, numDays) = " << std::remainder(simTimeInDays, numDays) << '\n';

  return 0;
}

backtrace: see attachment


$ system_profiler SPSoftwareDataType SPHardwareDataType
Software:

    System Software Overview:

      System Version: macOS 13.2 (22D49)
      Kernel Version: Darwin 22.3.0
      Boot Volume: Macintosh HD
      Boot Mode: Normal
      Secure Virtual Memory: Enabled
      System Integrity Protection: Enabled
      Time since boot: 7 hours, 58 minutes

Hardware:

    Hardware Overview:

      Model Name: MacBook Pro
      Model Identifier: MacBookPro18,2
      Model Number: Z14V000NBFN/A
      Chip: Apple M1 Max
      Total Number of Cores: 10 (8 performance and 2 efficiency)
      Memory: 64 GB
      System Firmware Version: 8419.80.7
      OS Loader Version: 8419.80.7
      Activation Lock Status: Enabled
$ otool -L test
test:
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1300.36.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0
$ clang++ --version
Apple clang version 14.0.0 (clang-1400.0.29.202)
Target: arm64-apple-darwin22.3.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
Answered by Apple Staff in 802402022

Please try macOS 15 beta where we expect this to be resolved.

Please try macOS 15 beta where we expect this to be resolved.

Illegal instruction in std::remainder (libsystem_m.dylib) when floating point exceptions enabled on M1 mac
 
 
Q