diff --git a/src/test_exports.zig b/src/test_exports.zig index 4165ad4a23..a3f22d4069 100644 --- a/src/test_exports.zig +++ b/src/test_exports.zig @@ -1,3 +1,3 @@ pub const InternPool = @import("InternPool.zig"); +pub const Air = @import("Air.zig"); // Later: pub const Sema = @import("Sema.zig"); -// Later: pub const Air = @import("Air.zig"); diff --git a/stage0/sema_c.zig b/stage0/sema_c.zig new file mode 100644 index 0000000000..bbe8e8da18 --- /dev/null +++ b/stage0/sema_c.zig @@ -0,0 +1,135 @@ +// sema_c.zig — Convert C Sema output (Air + InternPool) to Zig Air format. +// Ported mechanically from C structures defined in air.h / sema.h. + +const std = @import("std"); +const Allocator = std.mem.Allocator; +const Air = @import("zig_internals").Air; + +const sema_test = @import("sema_test.zig"); +pub const c = sema_test.c; + +/// Owning wrapper for a Zig Air struct built from C Air data. +/// Owns the backing MultiArrayList and extra array; call deinit() to free. +pub const OwnedAir = struct { + instructions: std.MultiArrayList(Air.Inst), + extra: std.ArrayListUnmanaged(u32), + + pub fn air(self: *const OwnedAir) Air { + return .{ + .instructions = self.instructions.slice(), + .extra = self.extra, + }; + } + + pub fn deinit(self: *OwnedAir, gpa: Allocator) void { + self.instructions.deinit(gpa); + self.extra.deinit(gpa); + } +}; + +/// Convert a C Air struct (from sema.h) to a Zig Air struct. +/// The caller owns the returned OwnedAir and must call deinit(). +pub fn zigAir(gpa: Allocator, c_air: c.Air) !OwnedAir { + const inst_len: usize = @intCast(c_air.inst_len); + + var instructions = std.MultiArrayList(Air.Inst){}; + try instructions.ensureTotalCapacity(gpa, inst_len); + + for (0..inst_len) |i| { + const tag_u8: u8 = @intCast(c_air.inst_tags.?[i]); + const zig_tag: Air.Inst.Tag = @enumFromInt(tag_u8); + instructions.appendAssumeCapacity(.{ + .tag = zig_tag, + .data = convertData(zig_tag, c_air.inst_datas.?[i]), + }); + } + + const extra_len: usize = @intCast(c_air.extra_len); + var extra = std.ArrayListUnmanaged(u32){}; + try extra.ensureTotalCapacity(gpa, extra_len); + if (extra_len > 0) { + extra.appendSliceAssumeCapacity(c_air.extra[0..extra_len]); + } + + return .{ + .instructions = instructions, + .extra = extra, + }; +} + +/// 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 { + // Read the raw two u32 words from the C union. Both the C union + // (AirInstData) and [2]u32 are 8 bytes with no padding. + const raw: [2]u32 = @bitCast(cd); + + return switch (tag) { + // --- no_op: void data --- + .trap, + .breakpoint, + .ret_addr, + .frame_addr, + .unreach, + .dbg_empty_stmt, + => .{ .no_op = {} }, + + // --- dbg_stmt: line + column --- + .dbg_stmt => .{ .dbg_stmt = .{ + .line = raw[0], + .column = raw[1], + } }, + + // --- un_op: single operand ref --- + .ret, + .ret_safe, + .ret_load, + => .{ .un_op = @enumFromInt(raw[0]) }, + + // --- bin_op: lhs + rhs refs --- + .add, + .add_safe, + .add_optimized, + .add_wrap, + .add_sat, + .sub, + .sub_safe, + .sub_optimized, + .sub_wrap, + .sub_sat, + .mul, + .mul_safe, + .mul_optimized, + .mul_wrap, + .mul_sat, + .bit_and, + .bit_or, + .xor, + .bool_and, + .bool_or, + => .{ .bin_op = .{ + .lhs = @enumFromInt(raw[0]), + .rhs = @enumFromInt(raw[1]), + } }, + + // --- ty_pl: type ref + payload index --- + .block, + .@"try", + .try_cold, + => .{ .ty_pl = .{ + .ty = @enumFromInt(raw[0]), + .payload = raw[1], + } }, + + // --- br: block_inst + operand --- + .br => .{ .br = .{ + .block_inst = @enumFromInt(raw[0]), + .operand = @enumFromInt(raw[1]), + } }, + + else => std.debug.panic( + "unhandled Air tag in C->Zig conversion: {}", + .{tag}, + ), + }; +} diff --git a/stage0/stages_test.zig b/stage0/stages_test.zig index d6f001b37c..2ad7a6f851 100644 --- a/stage0/stages_test.zig +++ b/stage0/stages_test.zig @@ -6,10 +6,12 @@ const AstGen = std.zig.AstGen; const parser_test = @import("parser_test.zig"); const astgen_test = @import("astgen_test.zig"); const sema_test = @import("sema_test.zig"); +const sema_c = @import("sema_c.zig"); const c = parser_test.c; const sc = sema_test.c; const zig_internals = @import("zig_internals"); const ZigIP = zig_internals.InternPool; +const ZigAir = zig_internals.Air; const Stage = enum { parser, sema }; @@ -69,17 +71,22 @@ fn stagesCheck(gpa: Allocator, source: [:0]const u8, check: Stage) !void { defer sc.ipDeinit(&ip); var sema = sc.semaInit(&ip, @bitCast(c_zir)); defer sc.semaDeinit(&sema); - var air = sc.semaAnalyze(&sema); - defer sc.airDeinit(&air); + var c_air = sc.semaAnalyze(&sema); + defer sc.airDeinit(&c_air); if (sema.has_compile_errors) { std.debug.print("Sema returned compile errors\n", .{}); return error.TestUnexpectedResult; } - // Verify Air arrays are properly transferred. - try std.testing.expect(air.inst_tags != null); - try std.testing.expect(air.inst_datas != null); + // Convert C Air to Zig Air for comparison. + var owned_air = try sema_c.zigAir(gpa, c_air); + defer owned_air.deinit(gpa); + const zig_air = owned_air.air(); + + // TODO: Run Zig sema to produce reference Air and compare + // against zig_air. For now, verify the conversion is valid. + _ = zig_air; } } @@ -1318,7 +1325,7 @@ fn expectKeysEqual(c_key: sc.InternPoolKey, zig_key: ZigIP.Key, index: u32) !voi } } -test "sema: const x = 42 intern pool comparison" { +test "sema: const x = 42 air + intern pool comparison" { const gpa = std.testing.allocator; const source: [:0]const u8 = "const x = 42;"; @@ -1332,8 +1339,17 @@ test "sema: const x = 42 intern pool comparison" { defer sc.ipDeinit(&c_ip); var sema = sc.semaInit(&c_ip, @bitCast(c_zir)); defer sc.semaDeinit(&sema); - var air = sc.semaAnalyze(&sema); - defer sc.airDeinit(&air); + var c_air = sc.semaAnalyze(&sema); + defer sc.airDeinit(&c_air); + + // Convert C Air to Zig Air. + var owned_air = try sema_c.zigAir(gpa, c_air); + defer owned_air.deinit(gpa); + const zig_air = owned_air.air(); + + // For `const x = 42;`, sema produces no AIR instructions + // (everything is resolved at comptime). + try std.testing.expectEqual(@as(usize, 0), zig_air.instructions.len); // C IP should have grown beyond 124 pre-interned entries try std.testing.expect(c_ip.items_len > 124);