Strict aliasing violation and analysis

Solution 1:

I am not sure what you have in mind when you say "register-cache/memory coherency".

The issue with the aliasing violation is simply (as with all undefined behavior) that the optimizer may choose to rely on it never happening (because it is UB) to infer constraints on the program execution and to use that to produce optimized machine code that needs to be functional only under these constraints.

For example in your case here without the write through char* the optimizer could for example see that the writes through F* could not possibly modify z because that would be an aliasing violation. Therefore it could e.g. reorder the writes through F* after the output.

Alternatively the compiler may see that z first has values written to it that can be calculated at compile-time, which it may remember for optimization. Then it can ignore the writes through F*, which are impossible due to UB, and finally seeing the output it can simply choose to output constants corresponding to the values it "knows" are in z.

Your method of protection might thwart some of these optimizations, but there will always be others that a compiler may choose to employ.

For example a compiler may recognize that the write through F* is UB and conclude from that the only possible value size can ever have is 0, because the loop body may never be executed in a non-UB program.

It can then use that knowledge to optimize the whole program to:

int main()
{
  int input;
  std::cin >> input;
  std::cerr << "\n";
  std::cerr << "Initial z[] = ";
  std::cerr << "\n\n";
  std::cerr << "After writing floats z[] = ";
  
  return 0;
}

With your method you can only hope that a compiler doesn't at some point get complex enough optimization capability to make this determination (and I am not sure that there isn't currently a compiler able and willing to do so.)

If you want to make use of constructs violating the aliasing rules, then you need to make sure that your compiler doesn't rely on it for optimization at all. Compilers usually have a flag to tell them not to do so, e.g. -fno-strict-aliasing for GCC, although in your program it is not only the aliasing violation that is a problem, but it is also a problem that there isn't actually any F object or array of F objects on which you would be allowed to do pointer arithmetic or access members. I am not sure that GCC's -fno-strict-aliasing flag is generally sufficient to guarantee that it wont rely on these two kinds of UB for optimization.


As a side note: while(true); is also undefined behavior in C++ (but not C). You cannot have an infinite loop without IO, atomic or volatile operations.

For example, as long as the loop is there Clang 13 with -O3 on compiler explorer outputs:

Initial z[] = 0, 1, 2, 

After writing floats z[] = 4690138725358302659, 4668251232723858883, 4632222435709990994, 4210784, 1, 12884901889, 2, 4200237, 140113662963656, 4200160, 0, 4200160, 0, 0, 0, 140113661087923, 140113662942080, 140736215914984, 4295040000, 4198848, 4200160, -8494071215197471174, 4198608, 140736215914976, 0, 0, 8494236860351561274, 8413707913860136506, 0, 0, 0, 1, 140736215914984, 140736215915000, 140113666830736, 0, 0, 4198608, 140736215914976, 0, 0, 4198654, 140736215914968, 28, 1, 140736215916259, 0, 140736215916270, 140736215916410, 140736215916436, 140736215916463, 140736215916489, 140736215916515, 0, 33, 140736217014272, 16, 529267711, 6, 4096, 17, 100, 3, 4194368, 4, 56, 5, 11, 7, 140113666637824, 8, 0, 9, 4198608, 11, 0, 12, 0, 13, 0, 14, 0, 23, 0, 25, 140736215915385, 26, 2, 31, 140736215916525, 15, 140736215915401, 0, 0, 0, -5663448171308038656, 2109109041276691562, 14696481348417631, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8391735729100685312, 4921308987509732720, 6436278639021083743, 8011689642975907935, 7597692896546878576, 7813877729437312364, 7020094974597624431, 3328210922030065518, 8011686456465305392, 7597692896546878576, 7813877729437312364, 7020094974597624431, 3328210922030065518, 4193470700803862320, 7885630528017166127, 8675390226550253936, 7147056913697434736, 3329058620635505004, 3918810539134823984, 5719376094260428852, 7150963379136975952, 8605359904538979439, 4707178968379521377, 5642809484591980366, 4427723895174544723, 5548561706083904609, 5283936564644036947, 8028914707716066895, 8320788952091016562, 5786948835902442496, 8026326388909754708, 7023201308806115180, 4415020012612383609, 8011672841536692527, 32420700043113589, 0, 

and I don't know whether compiler explorer is truncating the output: https://godbolt.org/z/bcq5vKonG