sema_c: add C Air → Zig Air conversion module
Add stage0/sema_c.zig that converts C Sema output (Air struct) to Zig's Air type via MultiArrayList, with per-tag data dispatch. Update stagesCheck to use the conversion, and extend the const x = 42 test to verify both Air structure and InternPool contents. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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");
|
||||
|
||||
135
stage0/sema_c.zig
Normal file
135
stage0/sema_c.zig
Normal file
@@ -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},
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user