News for hackers. Stuff that matters.
Re:C and C++ Prioritize Performance over Correctness by mxmlnkn on Friday August 18, 2023 @01:57PM (#37179556)
The undefined behavior I struggle with keeps me from better performance though. I have something like [(uint32_t value) >> (32 - nbits)] & (lowest nbits set). For the case of nbits=0, I would expect it to always return 0, even if the right shift of a 32-bit value by 32 bits is undefined behavior, then bit-wise and with 0 should make it always result in 0. But I cannot leave it that way because the compiler thinks that undefined behavior may not happen and might optimize out everything.
Re:C and C++ Prioritize Performance over Correctness by titzer on Friday August 18, 2023 @02:39PM (#37180166)
Exactly. The irony in all of this is that C is not a portable assembler. If you want the exact semantics of a hardware instruction, you cannot get it, because the compiler reasons with C's abstract machine that assumes your program doesn't have undefined behavior, like signed wraparound, when in some situations you in fact do want signed wraparound, since that's what literally every modern CPU does.
Re:C and C++ Prioritize Performance over Correctness by zzo38computer on Friday August 18, 2023 @02:08PM (#37179744)
I think that the undefinde behaviour should be partially specified. In the case you describe, it should require that it must do one of the following:

1. Return any 32-bit answer for the right shift. (The final result will be zero due to the bitwise AND, though, regardless of the intermediate answer.) The intermediate answer must be "frozen" so that if it is assigned to a variable and then used multiple times without writing to that variable again then you will get the same answer each time.

2. Result in a run-time error when that code is reached.

3. Result in a compile-time error (only valid if the compiler can determine for sure that the program would run with a shift amount out of range, e.g. if the shift amount is a constant).

4. Have a behaviour which depends on the underlying instruction set (whatever the right shift instruction does in that instruction set when given a shift amount which is out of range), if it is defined. (A compiler switch may be provided to switch between this and other behaviours.) In this case, if optimization is enabled then there may be some strange cases with some instruction sets where the optimizer makes an assumption which is not valid, but bad assumptions such as this should be reduced if possible and reasonable to do so.

In all cases, a compiler warning may be given (if enabled and detected by the compiler), in addition to the effects above.

Re:C and C++ Prioritize Performance over Correctness by mxmlnkn on Friday August 18, 2023 @02:28PM (#37180021)
I wanted to reply that your point 3 should already be possible with C++ constexpr functions because it doesn't allow undefined behavior. But I it seems I was wrong about that or maybe I'm doing it wrong:

    [[nodiscard]] constexpr uint64_t
    getBits( uint8_t nBits )
    {
        return BITBUFFER >> ( 64 - nBits ) & ( ( 1ULL << nBits ) - 1U );
    }
 
    int main()
    {
        std::cerr << getBits( 0 ) << "\n";
        std::cerr << getBits( 1 ) << "\n";
        return 0;
    }
The first output will print a random number, 140728069214376 in my case, while the second line will always print 1. However, when I put the ( ( 1ULL << nBits ) - 1U ) part into a separate function and print the values for that, then getBits( 0 ) suddenly always returns 0 as if the compiler understands suddenly that it will and with 0.

    template<uint8_t nBits>
    [[nodiscard]] constexpr uint64_t
    getBits2()
    {
        return BITBUFFER >> ( 64 - nBits ) & ( ( 1ULL << nBits ) - 1U );
    }
In this case, the compiler will only print a warning when trying to call it with getBits2<0>. And here I kinda thought that constexpr would lead to errors on undefined behavior, partly because it always complains about uninitialized std::array local variables being an error. That seems inconsistent to me. Well, I guess that's what -Werror is for ...

Compiled with -std=c++17 and clang 16.0.0 on godbolt: https://godbolt.org/z/qxxWW93Tx

Re:C and C++ Prioritize Performance over Correctness by gpderetta on Friday August 18, 2023 @03:50PM (#37181155)
Unfortunately constexpr doesn't imply constant evaluation. Your function can still potentially be executed at runtime.

If you use the result in an expression that requires a constant (an array bound, a non-type template parameter, a static_assert, or, in c++20, to initialize a constinit variable), then that will force constant evaluation and you'll see the error.

Having said that, compilers have bugs (or simply not fully implemented features), so it is certainly possible that both GCC and clang will fail to correctly catch constant time evaluation UB in some circumstances.

Re:C and C++ Prioritize Performance over Correctness by mxmlnkn on Friday August 18, 2023 @04:02PM (#37181317)
Ah thanks, I was not aware that these compile-time checks are only done when it is evaluated in a compile-time evaluating context.

To add to your list, using C++20 consteval instead of constexpr also triggers the error.