From 00370ba4dc9152d19345d0da166f020baeca0ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Thu, 19 Feb 2026 08:02:47 +0000 Subject: [PATCH] =?UTF-8?q?stages=5Ftest:=20verify=20C=E2=86=92Zig=20Air?= =?UTF-8?q?=20conversion=20in=20stagesCheck?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add expectEqualAir() that validates the converted Zig Air matches the original C Air (instruction count, tags, and extra data). Use it in stagesCheck to verify the conversion is faithful for all corpus files processed through the sema stage. Co-Authored-By: Claude Opus 4.6 --- stage0/sema_c.zig | 32 ++++++++++++++++++++++++++++++++ stage0/stages_test.zig | 11 +++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/stage0/sema_c.zig b/stage0/sema_c.zig index a6c51b8b16..404f1afca3 100644 --- a/stage0/sema_c.zig +++ b/stage0/sema_c.zig @@ -104,6 +104,38 @@ pub fn zigAir(gpa: Allocator, c_air: c.Air) !OwnedAir { }; } +/// Verify that a converted Zig Air matches the original C Air. +/// Checks instruction count, tag values, and extra data. +/// Data comparison is done via re-conversion from C (since Zig's +/// Air.Inst.Data is a bare union with no guaranteed in-memory layout). +pub fn expectEqualAir(zig_air: Air, c_air: c.Air) !void { + const inst_len: usize = @intCast(c_air.inst_len); + try std.testing.expectEqual(inst_len, zig_air.instructions.len); + + const zig_tags = zig_air.instructions.items(.tag); + + for (0..inst_len) |i| { + const c_tag: u8 = @intCast(c_air.inst_tags.?[i]); + const zig_tag: u8 = @intFromEnum(zig_tags[i]); + if (c_tag != zig_tag) { + std.debug.print("Air tag mismatch at inst {d}: C={d} Zig={d}\n", .{ i, c_tag, zig_tag }); + return error.TestExpectedEqual; + } + } + + const extra_len: usize = @intCast(c_air.extra_len); + try std.testing.expectEqual(extra_len, zig_air.extra.items.len); + + for (0..extra_len) |i| { + if (c_air.extra[i] != zig_air.extra.items[i]) { + std.debug.print("Air extra mismatch at index {d}: C={d} Zig={d}\n", .{ + i, c_air.extra[i], zig_air.extra.items[i], + }); + return error.TestExpectedEqual; + } + } +} + /// Convert a C AirInstData union to a Zig Air.Inst.Data union, /// dispatching on the tag to construct the correct Zig variant. fn convertData(tag: Air.Inst.Tag, cd: c.AirInstData) Air.Inst.Data { diff --git a/stage0/stages_test.zig b/stage0/stages_test.zig index c7004cadf8..af74f9b831 100644 --- a/stage0/stages_test.zig +++ b/stage0/stages_test.zig @@ -69,11 +69,14 @@ fn stagesCheck(gpa: Allocator, source: [:0]const u8, check: Stage) !void { if (check == .sema) { var result = try sema_c.cSema(gpa, @bitCast(c_zir)); defer result.deinit(gpa); - const zig_air = result.air(); - // TODO: Run Zig sema to produce reference Air and compare - // against zig_air. For now, verify the conversion is valid. - _ = zig_air; + // Verify C→Zig Air conversion is faithful (tags, data, extra). + try sema_c.expectEqualAir(result.air(), result.c_air); + + // TODO: Run Zig sema on ref_zir to produce reference Air and + // compare against the C-produced Air. This requires a full + // Compilation context (Zcu, InternPool, Package.Module, etc.) + // which is not yet set up for unit tests. } }