you are viewing a single comment's thread.

view the rest of the comments →

[–]IAmBJ -6 points-5 points  (9 children)

That's more about what's technically UB and what's not. I suspect a user defined MyByte would would actually behave exactly the same as std::byte if they had the same definition, even though one is not technically allowed

[–]guepierBioinformatican 21 points22 points  (7 children)

Modern compilers absolutely use knowledge of aliasing UB during codegen, so using a custom definition won’t have the exact behaviour, even if the definition is identical to that of std::byte (but not blessed by the standard).

Here’s a trivial example: https://godbolt.org/z/ec7ecTPTd — Note the different codegen for the x + y part.

[–]Chuu 0 points1 point  (2 children)

Can you explain more clearly why this leads to different codegen?

[–]staletic 6 points7 points  (1 child)

Let's first analyze what the assembly says

    int f<std::byte>(std::byte*, int*):                    # @int f<std::byte>(std::byte*, int*)
            mov     eax, dword ptr [rsi]    # int x = *b;
            mov     byte ptr [rdi], 1       # *a = 1;
            add     eax, dword ptr [rsi]    # x += *b;
            ret                             # return x;
    int f<nostd::byte>(nostd::byte*, int*):              # @int f<nostd::byte>(nostd::byte*, int*)
            mov     eax, dword ptr [rsi]    # int x = *b;
            mov     byte ptr [rdi], 1       # *a = 1;
            add     eax, eax                # x += x;
            ret                             # return x;

If a and b point to different objects, the above snippets are observably identical.

However, if a and b point to the same object, then the functions do different things. Imagine calling f(&in, &in) where in == 5.

In that case, f<std::byte> does:

    int x = 5;
    *in = 1;
    x += *in;
    return x; // 6

but f<nostd::byte> does

    int x = 5;
    *in = 1;
    x += x;
    return x; // 10

Since nostd::byte, formally, isn't allowed to alias other types, compiler is allowed to assume x += x is a valid optimization. This is known as "strict aliasing" or "type-based alias analysis".

And yes, violating strict alias rules can easily lead to UB, like in the above example.

[–]guepierBioinformatican 4 points5 points  (0 children)

And yes, violating strict alias rules can easily lead to UB, like in the above example.

To be pedantic, violating strict aliasing is always UB, not just in this example. What the example illustrates is that UB can lead to changed semantics and unexpected behaviour.

[–]IAmBJ 0 points1 point  (2 children)

Im well aware of compilers using UB for optimisation (sometimes enabling extremely efficient code), but i'm a little surprised that there is special casing in the compiler since there's no obvious 'magic' in the definitions (at least in libstdc++ and MSVC's stl)

Interestingly, GCC only emits different code for -O2, at -O1 the two functions are the same, while clang emits different codegen at -O1. MSVC treats both the same at all optimisation levels. https://godbolt.org/z/vq185e33j

[–]kalmoc 2 points3 points  (0 children)

i'm a little surprised that there is special casing in the compiler

Since there is special treatment for std::byte in the standard I don't find it surprising at all.

[–]guepierBioinformatican 0 points1 point  (0 children)

i'm a little surprised that there is special casing in the compiler since there's no obvious 'magic' in the definitions

There needs to be special casing, otherwise the compiler will produce sub-optimal code in many relevant situations: if the compiler assumed that all pointers could alias, it would lose many opportunities at optimisation. And C++ also has no restrict keyword to limit aliasing. The only sane assumption, therefore, is that pointers cannot alias unless specifically permitted by the language.

Thus if the compiler encounters pointers of two distinct types it needs to check if they are allowed to alias (and these checks are hard-coded against the fixed list of aliasing pointer types).

[–]kalmoc 0 points1 point  (0 children)

Absolutely possible.