4

I want my code to terminate when there is a floating point error. In linux-gcc the "feenableexcept()" function does the job, but that isn't available on in OSX. When using gcc on OS X the approach taken in (Enabling floating point interrupts on Mac OS X Intel) works just fine when using gcc, but does not work when using clang.

Example code:

#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <xmmintrin.h>

void handler(int sig) {
  void *array[10];
  size_t size;
  size = backtrace(array, 10);
  fprintf(stderr, "Error: signal %d:\n", sig);
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  exit(1);
}

int main(int argc, char **argv)
{
  _MM_SET_EXCEPTION_MASK( ( _MM_EXCEPT_INVALID |
                _MM_EXCEPT_DENORM |
                _MM_EXCEPT_DIV_ZERO |
                _MM_EXCEPT_OVERFLOW |
                _MM_EXCEPT_UNDERFLOW |
                _MM_EXCEPT_INEXACT ) );

  signal(SIGSEGV, handler);
  signal(SIGFPE, handler);

  std::cout<<"Perform 1.0/0.0"<<std::endl;
  double a = 1.0/0.0;
  std::cout<<"1.0/0.0 didn't kill program, result is "<<a<<std::endl<<std::endl;

  int* foo = (int*) - 1 ;// make a bad pointer
  std::cout<<"Attempting to print a bad pointer"<<std::endl;
  printf("%d\n", *foo);
  std::cout<<"Bad pointer didn't kill program."<<std::ends;
}

When compiled using gcc5, the result is:

Perform 1.0/0.0
Error: signal 8:
0   a.out                               0x000000010f97cb7f _Z7handleri + 28
1   libsystem_platform.dylib            0x00007fff895c652a _sigtramp + 26
2   ???                                 0x00007fff6eab6568 0x0 + 140735050114408
3   libdyld.dylib                       0x00007fff936a15ad start + 1

Wonderful. Works great. However, when compiled using clang (Apple LLVM version 7.3.0 (clang-703.0.29)) the result is this:

Perform 1.0/0.0
1.0/0.0 didn't kill program, result is inf

Attempting to print a bad pointer
Error: signal 11:
0   a.out                               0x000000010d501d1f _Z7handleri + 31
1   libsystem_platform.dylib            0x00007fff895c652a _sigtramp + 26
2   ???                                 0x00007fff62b7e568 0x0 + 140734849607016
3   libdyld.dylib                       0x00007fff936a15ad start + 1

Not great. Did not raise the FPE, and the code just kept chugging along. I have looked around, and can't find how to get clang to raise the FPE. Does anyone have experience with this? Thanks!

Community
  • 1
  • 1
doc07b5
  • 600
  • 1
  • 7
  • 18
  • Possible duplicate of [Enabling floating point interrupts on Mac OS X Intel](http://stackoverflow.com/questions/247053/enabling-floating-point-interrupts-on-mac-os-x-intel) – nwellnhof Jun 14 '16 at 18:14
  • @nwellnhof the approach in the linked question does not work with clang. – doc07b5 Jul 02 '16 at 23:37

2 Answers2

2

The accepted approach seems to be a new header to a project's built-in includes. For Linux, this file will be ignored and simply fallback to the stock fenv.h.

This code is credit 2009, David N. Williams, borrowed verbatim from the ardupilot project, which was the first result I could find. I can confirm this works on MacOS 10.13 with Apple LLVM version 9.0.0 (clang-900.0.39.2).

Note: Note, although many suggestions I've seen recommend uniquely naming this header for clarity's sake, in many ways it reminds me of a polyfill (term from JS/webdev), so mimicing that ideology the project the code is borrowed from as well as my own project have kept it names fenv.h. Rename as needed.

fenv.h

#pragma once

#include_next <fenv.h>

#if defined(__APPLE__) && defined(__MACH__)

// Public domain polyfill for feenableexcept on OS X
// http://www-personal.umich.edu/~williams/archive/computation/fe-handling-example.c

inline int feenableexcept(unsigned int excepts)
{
    static fenv_t fenv;
    unsigned int new_excepts = excepts & FE_ALL_EXCEPT;
    // previous masks
    unsigned int old_excepts;

    if (fegetenv(&fenv)) {
        return -1;
    }
    old_excepts = fenv.__control & FE_ALL_EXCEPT;

    // unmask
    fenv.__control &= ~new_excepts;
    fenv.__mxcsr   &= ~(new_excepts << 7);

    return fesetenv(&fenv) ? -1 : old_excepts;
}

inline int fedisableexcept(unsigned int excepts)
{
    static fenv_t fenv;
    unsigned int new_excepts = excepts & FE_ALL_EXCEPT;
    // all previous masks
    unsigned int old_excepts;

    if (fegetenv(&fenv)) {
        return -1;
    }
    old_excepts = fenv.__control & FE_ALL_EXCEPT;

    // mask
    fenv.__control |= new_excepts;
    fenv.__mxcsr   |= new_excepts << 7;

    return fesetenv(&fenv) ? -1 : old_excepts;
}

#endif
tresf
  • 7,103
  • 6
  • 40
  • 101
  • Thank you very much for your knowledge. I have tested the above code with MacOSX 10.14 Mojave and g++ in Apple LLVM, but that did not work for me. In contrast, that worked when I tested the above code with g++ v8.3 installed with Homebew. Do you have any idea for making the code working for Apple LLVM? – tatsy Jan 02 '19 at 07:35
  • @tatsy I have upgraded to Mojave but have not attempted to run the above patch. If I encounter issues I will provide feedback. I encourage others to do so as well. – tresf Jan 04 '19 at 18:52
0

You set the masked exceptions. So you have to clear the bit, if you want to get the exception:

_MM_SET_EXCEPTION_MASK( _MM_GET_EXCEPTION_MASK() 
       & ~( _MM_EXCEPT_INVALID |
            _MM_EXCEPT_DENORM |
            _MM_EXCEPT_DIV_ZERO |
            _MM_EXCEPT_OVERFLOW |
            _MM_EXCEPT_UNDERFLOW |
            _MM_EXCEPT_INEXACT ) );
Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50
  • 1
    Pasted this answer into the code, and it didn't work for either g++ or clang++. Did it work for you? Is there a intro level reference that you know of that explains how these bitfields work? – doc07b5 Jun 15 '16 at 21:15
  • I typed it in my browser, so I did not test it. Didn't you get exceptions? You can find a reference by googling `_MM_SET_EXCEPTION_MASK`. What do you mean with "these bitfields work"? In general or in particular to exception masks? – Amin Negm-Awad Jun 15 '16 at 23:17
  • I didn't get the floating point exception. I have googled _MM_SET_EXCEPTION_MASK, and the approaches that I have found work with gcc, but not with clang. What I mean by "these bitfields work"? is how is the bitfield interpreted? Is there a map of the bitfield? Is it platform dependent, or compiler dependent? I would think that this is something that should be easy to find, but I can't find anything. Perhaps I am just supposed to know this? – doc07b5 Jun 22 '16 at 04:36
  • As you know, all values are stored in a combination of bits. Usually these bits are interpreted as an integer or a floating point number. With bit fields every bit is interpreted isolated. Setting or clearing a bit sets resp. clears a specific option. To combine options the bit-or operator `|` is used, to read a specific bit the bit-and operator `&` is used. `~` reverts all bits. – Amin Negm-Awad Jun 22 '16 at 05:14