something that worries me is padding in hashmap key types. a while back i experienced a nasty surprise when i discovered clang to optimize copy operations around unused bytes in structs, causing padding to contain uninitialized memory and thus messing up hashes.

#devlog #C

@lritter is it possible to put all hashmap related function in the same translation unit and compile the object file with different optimization level than the rest of the project before linking?
That sounds like a pain to manage but I'm curious if that would work

@greenmoonmoon that wouldn't be the level at which i would counteract it, as it's quite fragile to maintain.

better would be to explicitly memclear a buffer then init the fields in it - before passing the key to any hashmap function. at least that's my plan when i encounter this again.

@lritter @greenmoonmoon I bet the compiler will still find a way to mess up your padding bytes. The only safe way are packed structs (not standardized though, but all C/C++ compilers support it).
@floooh @lritter @greenmoonmoon Packed structs are almost never "safe" in any way.
As soon as you pass a reference or pointer to a packed and non-aligned member in that struct, you are in Undefined Behaviour land, because non-aligned objects must not exist, and the compiler will break that basically immediately. The non-alignedness is not encoded in the type system, neither for #pragma pack, nor for __attribute__((packed)).
A simple call to std::min is enough to break things.

@manx @lritter @greenmoonmoon it should be safe when each "regular" struct member is still properly aligned (e.g. when using packed structs with "manually defined" padding, like below).

(it gets interesting though when the GPU alignment rules differ from the C alignment rules)

@floooh @lritter @greenmoonmoon At that point, #pragma pack is kind of pointless, because it does nothing. You have already filled all the holes that packing would eliminate.
If you want to model alignment that does not match the language alignment rules precisely (i.e. GPU, or file formats), I would strongly suggest doing what boost::endian does: Use distinct types that are naturally byte aligned (i.e. arrays of bytes), and (in C) accessor functions or (in C++) implicit conversions.

@manx @lritter @greenmoonmoon

> At that point, #pragma pack is kind of pointless, because it does nothing.

...the whole point of the thread is to make the content of padding bytes explicitly defined, and this is one way to do it (the other solution that came up is to use a compiler-specific builtin to zero the padding bytes, not yet supported by clang though)

@manx @lritter @greenmoonmoon tbf though, in my screenshot the explicit packing/alignment/padding has a different purpose: to make those structs compatible with GPU-side structs (e.g. those *must* be 16-byte aligned even if the C struct would only require 4-byte alignment), and there are some subtle differences in alignment rules for arrays of float3 vectors (those need 16-byte stride instead of 12). In any case, the resulting member alignments should be greater than the required by C.
@floooh @lritter @greenmoonmoon Yes, but that does not require #pragma pack at all. Just the explicit padding members are enough.
@floooh @lritter @greenmoonmoon In C++, you can actually check whether there are any implicit padding bits in a struct with std::has_unique_object_representations (i.e. <https://godbolt.org/z/ea45r6773>).
std::is_standard_layout is also probably also useful in related situations.
Compiler Explorer - C++ (x86-64 gcc 15.2)

struct foo { std::uint8_t x; std::uint32_t y; }; static_assert(!std::has_unique_object_representations<foo>::value); struct bar { std::uint8_t x; std::uint8_t padding1[3]; std::uint32_t y; }; static_assert(std::has_unique_object_representations<bar>::value);