Hacker News new | ask | show | jobs
by matheusmoreira 28 days ago
If some architecture traps on unaligned access, then the compiler can and should simply generate the correct code so that it loads the integer piece by piece instead. Load multiple integers and shift and mask away the irrelevant bits, done. This is exactly what modern architectures already do in hardware. Works, it's just a little slower.

This is exactly what the compilers do if you use a packed structure to access unaligned data. Works everywhere, as expected. Compilers have always known what to do, they just weren't doing it. C standard says no.

The fact is the standard is garbage and the first thing every C programmer should learn is that they can and should ignore it. There is never any reason to wonder what the standard is supposed to do. The only thing that matters is what compilers actually do.

4 comments

But if it's a pointer, the compiler doesn't know the alignment at compile time. Should the compiler insert an alignment check of every pointer access?
Compilers could add support for an unaligned attribute that we can apply to pointers. I'd prefer that to wrapping everything in a packed structure which is quite unsightly.

Would have been better if correct behavior was the default while pointer alignment requirements were opt in, just like vector stuff. Nothing we can do about it now.

I would hope the compiler is smart enough to figure out which accesses are aligned and unaligned on its own.

> If some architecture traps on unaligned access, then the compiler can and should simply generate the correct code so that it loads the integer piece by piece instead.

Wouldn't the compiler have to assume that every pointer access might be unaligned and do the slow "piece by piece" access every time? It can hardly guess the runtime value of a pointer during compilation.

It should be able to make a lot of inferences. For example, taking the address of some value allocated by the compiler itself results in an aligned pointer unless the programmer overrides it. Compiler should be able to trace it from there. Pointers from malloc are also aligned.

If compiler is not doing it for some reason, __builtin_assume_aligned can be used to explicitly mark a pointer as aligned.

The pointer might be something you forced. The compiler needs to do the right thing but if you set the pointer to an unaligned address because you have information on the hardware you can get this undefined situation with nothing the compiler can do about it.
Any reason the hardware pointer can't be accessed via the packed structure?

https://news.ycombinator.com/item?id=48205371

The same reason you probably aren’t adding manual alignment fixes to your code?
No reason at all, then. Because I am manually dealing with alignment in my code.

Wrote a lisp, its bytes type supports reading and writing integers at arbitrary locations within the buffer. Test suite exercises aligned and unaligned memory access for every C integer type. Also wrote my own mem* functions, dealing with alignment in those was certainly a fun exercise. It wasn't necessary, I just wanted the performance benefits.

however you certainly can do that. The point of unaligned is the hardware can't load it from a single memory location in one address. It needs two accesses. And in that time, the value of one of the two addresses that the hardware has to load can change.

I would hope you're not so stupid as to design hardware that relies on this, but the fact is it certainly is possible for someone to do that. And if you do that, there is nothing that the compiler or the standard can do. It can't be done correctly

Yeah, the unaligned accesses aren't going to be atomic unless the hardware supports it.

> And in that time, the value of one of the two addresses that the hardware has to load can change.

You mean volatile addresses that could spontaneously change in the middle of the reads? Like memory mapped I/O addresses?

I would expect these to have stricter access requirements than arbitrary general purpose memory locations.

> I would hope you're not so stupid as to design hardware that relies on this

You and me both.

> And if you do that, there is nothing that the compiler or the standard can do. It can't be done correctly

Anything that does that is broken and terrible anyway. It really shouldn't contaminate language design. It's the sort of thing that compilers should be adding attributes for, rather than constraining the language to the point nothing works correctly and making us use attributes on everything to restore some sane baseline behavior.

> Anything that does that is broken and terrible anyway

which is why it is undefined behaviour. the optimizer writers have told me consistently that if they can assume you're not doing this thing that's stupid anyway, they can make my code faster. And since I'm not doing that stupid thing anyway, I want my code to be faster.

Unaligned memory access isn't really stupid though. Not in the general case. Not to the point where it should give the compiler free reign to crash things or introduce security holes. It should just introduce a performance regression instead, which is a tractable problem. Just measure it and fix it by making things aligned.

Compilers can add some custom attributes that encode whatever semantics the badly designed hardware requires. This lets it freely break incorrect code in the small sections that are actually handling those special variables, while allowing the rest of the language to make sense.

> If some architecture traps on unaligned access, then the compiler can and should simply generate the correct code so that it loads the integer piece by piece instead.

LMAO what?!

The compiler should pessimize each and every memory access everywhere with an alignment check on the pointer and a branch, or forego the efficient memory access method of the platform entirely and just do bytewise loads only?!

Unaligned access. Not every access. Compiler should be able to analyze code, determine alignment invariants and optimize everything it can. If not, __builtin_assume_aligned could help whenever it needs to be made explicit. Alignment should have been part of the type itself to begin with but there's no fixing that now.
So yes, pessimize each and every access. No, that's not acceptable. And no, just because the compiler can get rid of some of the alignment checks where static analysis can prove that the pointer is aligned doesn't cut it.

Yes, making alignment part of the type system would be the correct fix. And yes, that's absolutely still possible since unaligned access is still UB. You're not breaking existing code. You could easily add new pointer types with (static) alignment information.