In safety-checked builds, Zir.Inst.Data is a tagged union where
@sizeOf(Data) > 8 due to the safety tag. saveZirCache strips these
tags by reinterpreting each Data as a HackDataLayout and copying the
first 8 bytes into a safety_buffer.
Union variants that use fewer than 8 bytes of payload leave the
remaining bytes uninitialised. The bulk copy propagates these
uninitialised V-bits into safety_buffer, causing valgrind to report:
Syscall param pwritev(vector[...]) points to uninitialised byte(s)
when the buffer is written to the cache file. This is harmless:
loadZirCache reconstructs the safety tag from the tag array, and each
variant only reads its own fields — the padding is never interpreted.
@memset before the copy does not help: the assignment
`safety_buffer[i] = as_struct.data` copies all 8 bytes from the
source union, and valgrind propagates the per-byte defined/undefined
status (V-bits) from source to destination, re-tainting the padding.
Use makeMemDefined after the copy loop to inform valgrind that the
padding contents are intentional. This compiles to a no-op when not
running under valgrind.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>