commit fbfecf51da19f4f99df525a9cd1ed8dfc355f936 (tree)
parent 689b6b901dedd560122d12d951f92e711a68c679
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Fri, 20 Feb 2026 08:42:53 +0000
verbose_air: zero padding in Air.Inst.Data when collecting
Air.Inst.Data is a union; variants smaller than 8 bytes (un_op,
no_op, ty, repeat) leave padding bytes uninitialised. Zero the
destination buffer and copy only the meaningful bytes per instruction
so that byte-level comparisons in tests are deterministic and
valgrind-clean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
1 file changed, 38 insertions(+), 10 deletions(-)
diff --git a/src/verbose_air.zig b/src/verbose_air.zig
@@ -10,6 +10,34 @@ const Compilation = zig_internals.Compilation;
const Package = zig_internals.Package;
const Air = zig_internals.Air;
+/// Number of meaningful bytes in Air.Inst.Data for a given tag.
+/// Variants smaller than 8 bytes leave padding uninitialised; callers
+/// must only copy this many bytes and zero the rest.
+fn airInstDataSize(tag: Air.Inst.Tag) usize {
+ return switch (tag) {
+ // no_op: 0 meaningful bytes
+ .ret_addr, .frame_addr => 0,
+ // un_op: 4 meaningful bytes (single Ref / u32)
+ .sqrt, .sin, .cos, .tan, .exp, .exp2,
+ .log, .log2, .log10,
+ .floor, .ceil, .round, .trunc_float,
+ .neg, .neg_optimized,
+ .is_null, .is_non_null, .is_null_ptr, .is_non_null_ptr,
+ .is_err, .is_non_err, .is_err_ptr, .is_non_err_ptr,
+ .ret, .ret_safe, .ret_load,
+ .is_named_enum_value, .tag_name, .error_name,
+ .cmp_lt_errors_len,
+ .c_va_end,
+ => 4,
+ // ty: 4 meaningful bytes (single Type / u32)
+ .alloc, .ret_ptr, .c_va_start => 4,
+ // repeat: 4 meaningful bytes (single Index / u32)
+ .repeat => 4,
+ // All other variants use the full 8 bytes.
+ else => 8,
+ };
+}
+
/// Matches C `Air` struct layout (air.h).
const CAir = extern struct {
inst_len: u32,
@@ -64,20 +92,20 @@ const AirCollector = struct {
break :blk dst.ptr;
} else null;
- // Copy datas (8 bytes per instruction)
+ // Copy datas (8 bytes per instruction).
+ // Air.Inst.Data is a union; variants smaller than 8 bytes
+ // (un_op, no_op, ty, repeat) leave padding bytes uninitialised.
+ // Zero the buffer first, then copy only the meaningful bytes
+ // per instruction so that padding is deterministically zero.
const datas_byte_len = inst_len * 8;
const datas_copy: ?[*]u8 = if (inst_len > 0) blk: {
const dst = try gpa.alloc(u8, datas_byte_len);
+ @memset(dst, 0);
const zig_datas = air.instructions.items(.data);
- if (@sizeOf(Air.Inst.Data) == 8) {
- const src = @as([*]const u8, @ptrCast(zig_datas.ptr))[0..datas_byte_len];
- @memcpy(dst, src);
- } else {
- // Safety build: @sizeOf(Data) may be > 8, copy first 8 bytes per element
- for (zig_datas, 0..) |*d, i| {
- const src = @as(*const [8]u8, @ptrCast(d));
- @memcpy(dst[i * 8 ..][0..8], src);
- }
+ for (0..inst_len) |i| {
+ const src = @as(*const [8]u8, @ptrCast(&zig_datas[i]));
+ const n = airInstDataSize(zig_tags[i]);
+ @memcpy(dst[i * 8 ..][0..n], src[0..n]);
}
break :blk dst.ptr;
} else null;