From 67b821e925d32bd550d01e01ba201734f36674a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Fri, 20 Feb 2026 08:42:53 +0000 Subject: [PATCH] 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 --- src/verbose_air.zig | 48 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/verbose_air.zig b/src/verbose_air.zig index 736beaa724..22cd44265c 100644 --- 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;