Is it conceivable that strictly according to the provisions of the C11 (or above) standard that this program could be optimized to print hello?

Viewed 132

Given this program, strict aliasing rules and type based alias analysis optimization:

#include<stdio.h>
#include<stddef.h>
#include<stdalign.h>
#include<assert.h>

struct thing {
  int x;
  int y;
};

static_assert(sizeof("hello") < sizeof(struct thing),"buffer can't hold text");

int main() {

    alignas(struct thing) char buffer[sizeof(struct thing)] = "hello";
    void *ptr = buffer;
    struct thing* s = ptr;
    
    s->x = 20;
    s->y = 30;

    printf("%s",buffer);

    return 0;
}

could a conforming compiler decide that the buffer never changes, so it can be placed in read-only segment (or similar) and thus this will print hello? (works as expected in practise)

Perhaps I shall add a motivating example as to why one would want to do this. Say one have created an arena allocator that works fine when passed an initial slab of memory by malloc. No problem, any time we write to memory gotten from this allocator we are changing effective types on first write to the (void*) we get. However if our arena allocator is initialized with a char* gotten from stack memory (say aligned suitably for any type we will write to it), it is a big no no as we would be aliasing the char buffer.

This leads me to feel this is a hole in the language. Why can we not stack allocate suitably aligned untyped (but sized) memory?

2 Answers

Alignment isn't the only issue here. The effective type of buffer is a character array but you make lvalue access as int. That's a pretty clear strict aliasing violation and undefined behavior. Anything can happen.

"Anything" includes the s->x = 20; write happening, or getting optimized away, or the program crashing because the string was allocated in read-only section, or the program not crashing but the string remaining intact because it was allocated in true NVM like flash memory.

The Standard invites implementations whose customers would find constructs like yours useful to specify that they will process such code "in a documented manner characteristic of the environment" even in cases where the Standard itself imposes no requirements. Because the authors had no reason to expect that implementations whose customers would find such support useful would balk at providing it, they saw no need to mandate it. Instead, such support is left as a Quality of Implementation issue. Implementations designed and configured to be suitable for low-level programming will provide it, but those that are not designed and configured that way might not.

As for what might happen if an implementation doesn't provide such support, some compilers that are designed and configured to be suitable only for programs that will exclusively process trusted inputs (e.g. clang and gcc with full optimizations enabled) will deliberately regard as impossible any input which the Standard would not forbid them from processing in nonsensical fashion, and omit code which would only be relevant if such input is received. Consequently, such implementations may process actions the Standard characterizes as Undefined Behavior in nonsensical ways which are not constrained by normal laws of time or causality.

Related