Search code examples
c++clanguage-design

Why was no intrinsic access to the CPU's status register in the design of both C and C++?


In the case of the overflow flag, it would seem that access to this flag would be a great boon to cross-architecture programming. It would provide a safe alternative to relying on undefined behaviour to check for signed integer overflow such as:

if(a < a + 100) //detect overflow

I do understand that there are safe alternatives such as:

if(a > (INT_MAX - 100)) //detected overflow

However, it would seem that access to the status register or the individual flags within it is missing from both the C and C++ languages. Why was this feature not included or what language design decisions were made that prohibited this feature from being included?


Solution

    • Because C++ is designed as a portable language, i.e. one that compiles on many CPUs (e.g. x86, ARM, LSI-11/2, with devices like Game Boys, Mobile Phones, Freezers, Airplanes, Human Manipulation Chips and Laser Swords).
      • the available flags across CPUs may largely differ
      • even within the same CPU, flags may differ (take x86 scalar vs. vector instructions)
      • some CPUs may not even have the flag you desire at all
    • The question has to be answered: Should the compiler always deliver/enable that flag when it can't determine whether it is used at all?, which does not conform the pay only for what you use unwritten but holy law of both C and C++
    • Because compilers would have to be forbidden to optimize and e.g. reorder code to keep those flags valid

    Example for the latter:

    int x = 7;
    x += z;
    int y = 2;
    y += z;
    

    The optimizer may transform this to that pseudo assembly code:

    alloc_stack_frame 2*sizeof(int)
    load_int 7, $0
    load_int 2, $1
    add z, $0
    add z, $1
    

    which in turn would be more similar to

    int x = 7;
    int y = 2;
    x += z;
    y += z; 
    

    Now if you query registers inbetween

    int x = 7;
    x += z;
    if (check_overflow($0)) {...}
    int y = 2;
    y += z;
    

    then after optimizing and dissasembling you might end with this:

    int x = 7;
    int y = 2;
    x += z;
    y += z;
    if (check_overflow($0)) {...}
    

    which is then incorrect.

    More examples could be constructed, like what happens with a constant-folding-compile-time-overflow.


    Sidenotes: I remember an old Borland C++ compiler having a small API to read the current CPU registers. However, the argumentation above about optimization still applies.

    On another sidenote: To check for overflow:

    // desired expression: int z = x + y
    would_overflow = x > MAX-y;
    

    more concrete

    auto would_overflow = x > std::numeric_limits<int>::max()-y;
    

    or better, less concrete:

    auto would_overflow = x > std::numeric_limits<decltype(x+y)>::max()-y;