Merge branch 'zig1' into zig0-0.15.2

# Conflicts:
#	build.zig
#	stage0/sema.c
#	stage0/stages_test.zig
This commit is contained in:
2026-02-23 18:21:30 +00:00
96 changed files with 964 additions and 1814 deletions

View File

@@ -9,6 +9,7 @@ const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;
const assert = std.debug.assert;
const DevEnv = @import("src/dev.zig").Env;
const ValueInterpretMode = enum { direct, by_name };
const corpus = @import("stage0/corpus.zig");
const zig0_headers = &[_][]const u8{
"air.h",
@@ -1699,25 +1700,9 @@ fn addZig0TestStep(
test_timeout: ?[]const u8,
exe_options: *std.Build.Step.Options,
) void {
// Step 1: Compile Zig test code to .o (cached independently of C objects).
// NOTE: test_mod does NOT import zig_internals — stage0 tests are fast.
const test_mod = b.createModule(.{
.root_source_file = b.path("stage0_test_root.zig"),
.optimize = optimize,
.target = target,
.valgrind = true,
});
test_mod.addIncludePath(b.path("stage0"));
test_mod.linkSystemLibrary("c", .{});
const test_obj = b.addTest(.{
.root_module = test_mod,
.emit_object = true,
.use_llvm = false,
.use_lld = false,
});
// Step 1b: Compile Zig dumper module (depends on zig_internals, cached separately).
// Step 0: Build AIR generator and generate air_bundle.zig.
// The generator uses zig_internals to compile source files and
// produces pre-computed AIR data + tag names in a single Zig file.
const zig_internals_mod = b.createModule(.{
.root_source_file = b.path("src/test_exports.zig"),
});
@@ -1738,25 +1723,72 @@ fn addZig0TestStep(
// processing empty Air. ReleaseFast elides the safety check.
const dumper_mod = b.createModule(.{
.root_source_file = b.path("src/verbose_air.zig"),
.target = target,
.target = b.graph.host,
.optimize = .ReleaseFast,
.valgrind = true,
});
dumper_mod.addImport("zig_internals", zig_internals_mod);
dumper_mod.linkSystemLibrary("c", .{});
const dumper_obj = b.addObject(.{
.name = "verbose_dumper",
.root_module = dumper_mod,
const gen_mod = b.createModule(.{
.root_source_file = b.path("src/verbose_air_gen.zig"),
.target = b.graph.host,
.optimize = .ReleaseFast,
});
gen_mod.addImport("verbose_air", dumper_mod);
gen_mod.addImport("zig_internals", zig_internals_mod);
gen_mod.linkSystemLibrary("c", .{});
const gen_exe = b.addExecutable(.{
.name = "air_gen",
.root_module = gen_mod,
});
// Step 2: Link test_obj + dumper_obj + C objects into final executable.
// Run generator: air_gen <output_dir> [<name> <resolved_path>]...
const gen_run = b.addRunArtifact(gen_exe);
const air_dir = gen_run.addOutputDirectoryArg("air");
// Add corpus files as name/path pairs.
for (corpus.files[0..corpus.num_passing]) |path| {
gen_run.addArg(path);
gen_run.addFileArg(b.path(path));
}
// Add sema unit test files.
for (corpus.sema_unit_tests) |path| {
gen_run.addArg(path);
gen_run.addFileArg(b.path(path));
}
// Step 1: Compile Zig test code to .o (cached independently of C objects).
// NOTE: test_mod does NOT import zig_internals — stage0 tests are fast.
const test_mod = b.createModule(.{
.root_source_file = b.path("stage0_test_root.zig"),
.optimize = optimize,
.target = target,
.valgrind = true,
});
test_mod.addIncludePath(b.path("stage0"));
test_mod.linkSystemLibrary("c", .{});
// Make pre-computed AIR data available to tests.
test_mod.addAnonymousImport("air_tag_names", .{
.root_source_file = air_dir.path(b, "tag_names.zig"),
});
test_mod.addAnonymousImport("air_data", .{
.root_source_file = air_dir.path(b, "air_data.zig"),
});
const test_obj = b.addTest(.{
.root_module = test_mod,
.emit_object = true,
.use_llvm = false,
.use_lld = false,
});
// Step 2: Link test_obj + C objects into final executable.
// No more dumper_obj — AIR is pre-computed at build time.
const link_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
link_mod.addObject(test_obj);
link_mod.addObject(dumper_obj);
addZig0CSources(b, link_mod, cc, optimize);
link_mod.linkSystemLibrary("c", .{});

View File

@@ -11,7 +11,7 @@ const Package = zig_internals.Package;
const Air = zig_internals.Air;
/// Matches C `Air` struct layout (air.h).
const CAir = extern struct {
pub const CAir = extern struct {
inst_len: u32,
inst_cap: u32,
inst_tags: ?[*]u8,
@@ -22,13 +22,13 @@ const CAir = extern struct {
};
/// Matches C `SemaFuncAir` struct layout (sema.h).
const CSemaFuncAir = extern struct {
pub const CSemaFuncAir = extern struct {
name: ?[*:0]u8,
func_ip: u32, // InternPoolIndex
air: CAir,
};
const CompileAirResult = extern struct {
pub const CompileAirResult = extern struct {
items: ?[*]CSemaFuncAir,
len: u32,
callback_count: u32,
@@ -132,7 +132,7 @@ export fn zig_compile_air(
};
}
export fn zig_compile_air_free(result: *CompileAirResult) void {
pub export fn zig_compile_air_free(result: *CompileAirResult) void {
const gpa = std.heap.c_allocator;
if (result.items) |items| {
for (items[0..result.len]) |*f| {
@@ -153,7 +153,7 @@ fn setErr(buf: *[err_buf_size]u8, comptime fmt: []const u8, args: anytype) void
buf[written.len] = 0;
}
fn zigCompileAirImpl(
pub fn zigCompileAirImpl(
src_path: []const u8,
module_root_opt: ?[]const u8,
err_buf: *[err_buf_size]u8,

232
src/verbose_air_gen.zig Normal file
View File

@@ -0,0 +1,232 @@
// verbose_air_gen.zig — Build-time generator for pre-computed AIR data.
// Compiles each source file via the Zig compiler pipeline and writes
// binary .air files + a tag_names.zig into an output directory.
//
// Usage: air_gen <output_dir> [<name> <resolved_path>]...
// output_dir: directory to write .air files and tag_names.zig
// name: repo-relative path (used as file name, e.g. "lib/foo.zig" -> "lib/foo.zig.air")
// resolved_path: filesystem path to compile
const std = @import("std");
pub const std_options: std.Options = .{ .log_level = .warn };
const verbose_air = @import("verbose_air");
const zig_internals = @import("zig_internals");
const Air = zig_internals.Air;
pub fn main() !void {
const gpa = std.heap.c_allocator;
const args = try std.process.argsAlloc(gpa);
if (args.len < 2) {
std.debug.print("usage: air_gen <output_dir> [<name> <resolved_path>]...\n", .{});
return error.InvalidArguments;
}
const output_dir_path = args[1];
const pairs = args[2..];
if (pairs.len % 2 != 0) {
std.debug.print("error: arguments must be pairs of <name> <resolved_path>\n", .{});
return error.InvalidArguments;
}
const num_entries = pairs.len / 2;
var output_dir = try std.fs.cwd().openDir(output_dir_path, .{});
defer output_dir.close();
// Derive repo_root by stripping the name suffix from the first resolved path.
// e.g. name="lib/std/crypto.zig", resolved="/home/user/zig1/lib/std/crypto.zig"
// => repo_root="/home/user/zig1"
if (num_entries == 0) return;
const first_name = pairs[0];
const first_resolved = pairs[1];
const repo_root = blk: {
if (first_resolved.len >= first_name.len and
std.mem.eql(u8, first_resolved[first_resolved.len - first_name.len ..], first_name))
{
break :blk first_resolved[0 .. first_resolved.len - first_name.len];
}
break :blk std.fs.path.dirname(first_resolved) orelse ".";
};
// Create a symlink workaround so that files in lib/std/ are not seen as
// belonging to both 'root' and 'std' modules. We symlink repo_root into
// a temp directory and pass all paths through the symlink.
// The temp directory must be OUTSIDE the output_dir to avoid confusing the
// Zig build system's cache (it would follow the symlink back into the repo).
const tmp_dir_path = "/tmp/zig-air-gen";
std.fs.makeDirAbsolute(tmp_dir_path) catch |err| switch (err) {
error.PathAlreadyExists => {},
else => {
std.debug.print("error creating tmp directory: {s}\n", .{@errorName(err)});
return err;
},
};
var tmp_dir = std.fs.openDirAbsolute(tmp_dir_path, .{}) catch |err| {
std.debug.print("error opening tmp directory: {s}\n", .{@errorName(err)});
return err;
};
defer tmp_dir.close();
defer std.fs.deleteTreeAbsolute(tmp_dir_path) catch {};
tmp_dir.symLink(repo_root, "root", .{ .is_directory = true }) catch |err| switch (err) {
error.PathAlreadyExists => {},
else => {
std.debug.print("error creating symlink: {s}\n", .{@errorName(err)});
return err;
},
};
for (0..num_entries) |i| {
const name = pairs[i * 2];
const resolved = pairs[i * 2 + 1];
_ = resolved;
// Construct paths through the symlink to avoid the "file exists in
// modules 'root' and 'std'" error.
const src_path = try std.fmt.allocPrint(gpa, "{s}/root/{s}", .{ tmp_dir_path, name });
defer gpa.free(src_path);
const module_root = try std.fmt.allocPrint(gpa, "{s}/root", .{tmp_dir_path});
defer gpa.free(module_root);
const data = processSource(gpa, src_path, module_root) catch |err| {
std.debug.print("FAIL: {s} ({s}): {s}\n", .{ name, src_path, @errorName(err) });
return err;
};
defer gpa.free(data);
// Write {name}.air, creating subdirectories as needed.
const air_name = try std.fmt.allocPrint(gpa, "{s}.air", .{name});
defer gpa.free(air_name);
if (std.fs.path.dirname(air_name)) |dir| {
output_dir.makePath(dir) catch |err| {
std.debug.print("error creating directory '{s}': {s}\n", .{ dir, @errorName(err) });
return err;
};
}
var file = output_dir.createFile(air_name, .{}) catch |err| {
std.debug.print("error creating file '{s}': {s}\n", .{ air_name, @errorName(err) });
return err;
};
defer file.close();
file.writeAll(data) catch |err| {
std.debug.print("error writing file '{s}': {s}\n", .{ air_name, @errorName(err) });
return err;
};
}
// Write tag_names.zig
{
var out: std.ArrayListUnmanaged(u8) = .empty;
defer out.deinit(gpa);
const writer = out.writer(gpa);
try writer.writeAll("// Generated by verbose_air_gen. Do not edit.\n\n");
const tag_count = @typeInfo(Air.Inst.Tag).@"enum".fields.len;
try writer.print("pub const names: [{d}][:0]const u8 = .{{\n", .{tag_count});
inline for (@typeInfo(Air.Inst.Tag).@"enum".fields) |field| {
try writer.print(" \"{s}\",\n", .{field.name});
}
try writer.writeAll("};\n");
var file = try output_dir.createFile("tag_names.zig", .{});
defer file.close();
try file.writeAll(out.items);
}
// Write air_data.zig — helper that bridges @embedFile for test code.
// Because @embedFile resolves relative to the source file (not --embed-dir),
// this file must live alongside the .air files so it can find them.
{
var file = try output_dir.createFile("air_data.zig", .{});
defer file.close();
try file.writeAll(
\\// Generated by verbose_air_gen. Do not edit.
\\pub fn getData(comptime path: []const u8) []const u8 {
\\ return @embedFile(path ++ ".air");
\\}
\\
);
}
}
/// Compile a source file and return the binary AIR data.
/// Binary format:
/// func_count: u32 (little-endian)
/// Per function:
/// name_len: u32
/// name: [name_len]u8
/// inst_len: u32
/// inst_tags: [inst_len]u8
/// inst_datas: [inst_len * 8]u8
/// extra_len: u32
/// extra: [extra_len * 4]u8
fn processSource(gpa: std.mem.Allocator, src_path: []const u8, module_root: []const u8) ![]const u8 {
var err_buf: [256]u8 = .{0} ** 256;
const result = verbose_air.zigCompileAirImpl(
src_path,
module_root,
&err_buf,
) catch |err| {
std.debug.print("zigCompileAirImpl error for {s}: {s} ({s})\n", .{
src_path,
std.mem.sliceTo(&err_buf, 0),
@errorName(err),
});
return err;
};
if (err_buf[0] != 0) {
std.debug.print("zigCompileAirImpl error for {s}: {s}\n", .{
src_path,
std.mem.sliceTo(&err_buf, 0),
});
return error.CompileError;
}
const funcs = if (result.items) |items| items[0..result.len] else &[_]verbose_air.CSemaFuncAir{};
defer {
var r = result;
verbose_air.zig_compile_air_free(&r);
}
// Serialize to binary format.
var buf: std.ArrayListUnmanaged(u8) = .empty;
// func_count
try buf.writer(gpa).writeInt(u32, @intCast(funcs.len), .little);
for (funcs) |*f| {
const w = buf.writer(gpa);
// name
const name = if (f.name) |n| std.mem.span(n) else "";
try w.writeInt(u32, @intCast(name.len), .little);
try w.writeAll(name);
// inst_tags + inst_datas
const inst_len = f.air.inst_len;
try w.writeInt(u32, inst_len, .little);
if (inst_len > 0) {
if (f.air.inst_tags) |tags| {
try w.writeAll(tags[0..inst_len]);
}
if (f.air.inst_datas) |datas| {
try w.writeAll(datas[0 .. inst_len * 8]);
}
}
// extra
const extra_len = f.air.extra_len;
try w.writeInt(u32, extra_len, .little);
if (extra_len > 0) {
if (f.air.extra) |extra| {
const bytes = std.mem.sliceAsBytes(extra[0..extra_len]);
try w.writeAll(bytes);
}
}
}
return try buf.toOwnedSlice(gpa);
}

296
stage0/corpus.zig Normal file
View File

@@ -0,0 +1,296 @@
/// Corpus files for stages testing, sorted by size.
/// Paths are repo-relative (no "../" prefix).
/// `num_passing` controls how many files are tested and pre-generated.
/// Both build.zig and stages_test.zig import this file.
/// To enable more tests: just increment `num_passing`.
pub const num_passing: usize = 66;
pub const files = [_][]const u8{
"lib/std/crypto/codecs.zig", // 165
"lib/std/os/uefi/tables/table_header.zig", // 214
"lib/std/zig/llvm.zig", // 247
"lib/compiler_rt/neghf2.zig", // 265
"lib/compiler_rt/negxf2.zig", // 265
"lib/compiler_rt/absvdi2.zig", // 311
"lib/compiler_rt/absvsi2.zig", // 311
"lib/compiler_rt/absvti2.zig", // 314
"lib/compiler_rt/addhf3.zig", // 319
"lib/compiler_rt/addxf3.zig", // 323
"lib/compiler_rt/mulhf3.zig", // 323
"lib/compiler_rt/mulxf3.zig", // 323
"lib/compiler_rt/truncxfdf2.zig", // 333
"lib/compiler_rt/truncxfsf2.zig", // 333
"lib/std/crypto/pcurves/p256/field.zig", // 338
"lib/compiler_rt/fixhfdi.zig", // 341
"lib/compiler_rt/fixhfsi.zig", // 341
"lib/compiler_rt/fixxfdi.zig", // 341
"lib/compiler_rt/fixxfsi.zig", // 341
"lib/compiler_rt/unordhf2.zig", // 341
"lib/compiler_rt/unordxf2.zig", // 341
"lib/std/crypto/pcurves/secp256k1/field.zig", // 343
"lib/compiler_rt/divhf3.zig", // 344
"lib/compiler_rt/floatdihf.zig", // 347
"lib/compiler_rt/floatdixf.zig", // 347
"lib/compiler_rt/floatsihf.zig", // 347
"lib/compiler_rt/floatsixf.zig", // 347
"lib/compiler_rt/fixunshfdi.zig", // 350
"lib/compiler_rt/fixunshfsi.zig", // 350
"lib/compiler_rt/fixunsxfdi.zig", // 350
"lib/compiler_rt/fixunsxfsi.zig", // 350
"lib/compiler_rt/floatundihf.zig", // 353
"lib/compiler_rt/floatundixf.zig", // 353
"lib/compiler_rt/floatunsixf.zig", // 353
"lib/compiler_rt/truncxfhf2.zig", // 356
"lib/compiler_rt/floatunsihf.zig", // 357
"lib/compiler_rt/trunctfhf2.zig", // 359
"lib/compiler_rt/extendsfxf2.zig", // 360
"lib/compiler/aro/backend.zig", // 362
"lib/compiler_rt/extenddfxf2.zig", // 364
"lib/std/compress.zig", // 372
"lib/compiler_rt/extendhfdf2.zig", // 373
"lib/compiler_rt/extendhfxf2.zig", // 373
"lib/compiler_rt/extendhftf2.zig", // 376
"lib/std/crypto/pcurves/p384/field.zig", // 376
"lib/compiler_rt/subxf3.zig", // 399
"lib/compiler_rt/subhf3.zig", // 406
"lib/compiler_rt/negtf2.zig", // 409
"lib/std/os/linux/bpf/btf_ext.zig", // 419
"lib/compiler_rt/muldc3.zig", // 425
"lib/compiler_rt/mulhc3.zig", // 425
"lib/compiler_rt/mulsc3.zig", // 425
"lib/compiler_rt/mulxc3.zig", // 425
"lib/compiler_rt/divdc3.zig", // 434
"lib/compiler_rt/divhc3.zig", // 434
"lib/compiler_rt/divsc3.zig", // 434
"lib/compiler_rt/divxc3.zig", // 434
"lib/std/math/complex/abs.zig", // 452
"lib/c/common.zig", // 457
"lib/std/math/complex/arg.zig", // 458
"lib/std/math/complex/conj.zig", // 484
"lib/std/math/scalbn.zig", // 503
"lib/compiler_rt/negdf2.zig", // 530
"lib/compiler_rt/negsf2.zig", // 530
"lib/std/Random/SplitMix64.zig", // 530
"lib/std/os/uefi/protocol/shell_parameters.zig", // 544
// --- files below this line are not yet passing ---
"lib/compiler_rt/gexf2.zig", // 531
"lib/c/strings.zig", // 549
"lib/compiler_rt/fixdfei.zig", // 564
"lib/compiler_rt/fixhfei.zig", // 564
"lib/compiler_rt/fixsfei.zig", // 564
"lib/compiler_rt/fixxfei.zig", // 564
"lib/compiler_rt/fixtfei.zig", // 565
"lib/compiler_rt/floateidf.zig", // 569
"lib/compiler_rt/floateihf.zig", // 569
"lib/compiler_rt/floateisf.zig", // 569
"lib/compiler_rt/floateixf.zig", // 569
"lib/c/inttypes.zig", // 570
"lib/compiler_rt/floateitf.zig", // 571
"lib/compiler_rt/fixunsdfei.zig", // 575
"lib/compiler_rt/fixunshfei.zig", // 575
"lib/compiler_rt/fixunssfei.zig", // 575
"lib/compiler_rt/fixunsxfei.zig", // 575
"lib/compiler_rt/fixunstfei.zig", // 576
"lib/compiler_rt/floatuneidf.zig", // 577
"lib/compiler_rt/floatuneihf.zig", // 577
"lib/compiler_rt/floatuneisf.zig", // 577
"lib/compiler_rt/floatuneixf.zig", // 577
"lib/std/math/complex/cos.zig", // 577
"lib/compiler_rt/floatuneitf.zig", // 579
"lib/compiler_rt/multc3.zig", // 581
"lib/compiler_rt/divtc3.zig", // 590
"lib/compiler_rt/adddf3.zig", // 594
"lib/compiler_rt/addsf3.zig", // 594
"lib/compiler_rt/muldf3.zig", // 598
"lib/compiler_rt/mulsf3.zig", // 598
"lib/compiler_rt/truncdfsf2.zig", // 600
"lib/std/math/complex/acos.zig", // 608
"lib/std/math/complex/pow.zig", // 608
"lib/compiler_rt/fixdfsi.zig", // 616
"lib/compiler_rt/fixsfsi.zig", // 616
"lib/compiler_rt/truncdfhf2.zig", // 616
"lib/compiler_rt/floatsidf.zig", // 619
"lib/compiler_rt/floatsisf.zig", // 619
"lib/std/math/complex/log.zig", // 620
"lib/std/math/complex/sin.zig", // 620
"lib/std/math/complex/tan.zig", // 626
"lib/compiler_rt/fixunsdfsi.zig", // 628
"lib/compiler_rt/fixunssfsi.zig", // 628
"lib/compiler_rt/floatunsidf.zig", // 628
"lib/compiler_rt/floatunsisf.zig", // 628
"lib/std/math/complex/proj.zig", // 628
"lib/compiler_rt/unorddf2.zig", // 634
"lib/compiler_rt/unordsf2.zig", // 634
"lib/std/math/complex/asinh.zig", // 641
"lib/std/dwarf/EH.zig", // 643
"lib/compiler_rt/extendsfdf2.zig", // 644
"lib/std/math/complex/atanh.zig", // 645
"lib/compiler_rt/unordtf2.zig", // 656
"lib/std/Target/generic.zig", // 665
"lib/compiler_rt/absv.zig", // 671
"lib/std/math/complex/acosh.zig", // 678
"lib/compiler_rt/fixdfdi.zig", // 701
"lib/compiler_rt/fixsfdi.zig", // 701
"lib/compiler_rt/floatdidf.zig", // 704
"lib/compiler_rt/floatdisf.zig", // 704
"lib/compiler_rt/floattidf.zig", // 712
"lib/compiler_rt/floattihf.zig", // 712
"lib/compiler_rt/floattisf.zig", // 712
"lib/compiler_rt/floattixf.zig", // 712
"lib/compiler_rt/fixdfti.zig", // 713
"lib/compiler_rt/fixhfti.zig", // 713
"lib/compiler_rt/fixsfti.zig", // 713
"lib/compiler_rt/fixunsdfdi.zig", // 713
"lib/compiler_rt/fixunssfdi.zig", // 713
"lib/compiler_rt/fixxfti.zig", // 713
"lib/compiler_rt/floatundidf.zig", // 713
"lib/compiler_rt/floatundisf.zig", // 713
"lib/compiler_rt/floatuntidf.zig", // 724
"lib/compiler_rt/floatuntihf.zig", // 724
"lib/compiler_rt/floatuntisf.zig", // 724
"lib/compiler_rt/floatuntixf.zig", // 724
"lib/compiler_rt/addtf3.zig", // 725
"lib/compiler_rt/fixunsdfti.zig", // 731
"lib/compiler_rt/fixunshfti.zig", // 731
"lib/compiler_rt/fixunssfti.zig", // 731
"lib/compiler_rt/fixunsxfti.zig", // 731
"lib/compiler_rt/trunctfdf2.zig", // 731
"lib/compiler_rt/trunctfsf2.zig", // 731
"lib/compiler_rt/subdf3.zig", // 735
"lib/compiler_rt/subsf3.zig", // 735
"lib/compiler_rt/fixtfdi.zig", // 736
"lib/compiler_rt/fixtfsi.zig", // 736
"lib/compiler_rt/multf3.zig", // 737
"lib/std/math/big.zig", // 746
"lib/compiler_rt/floatditf.zig", // 748
"lib/compiler_rt/floatsitf.zig", // 748
"lib/std/math/complex/asin.zig", // 750
"lib/compiler_rt/fixunstfdi.zig", // 754
"lib/compiler_rt/fixunstfsi.zig", // 754
"lib/init/src/root.zig", // 755
"lib/compiler_rt/floatunditf.zig", // 761
"lib/compiler_rt/floatunsitf.zig", // 761
"lib/compiler_rt/udivti3.zig", // 770
"lib/compiler_rt/extenddftf2.zig", // 781
"lib/compiler_rt/extendsftf2.zig", // 781
"lib/std/Build/Step/Fail.zig", // 831
"lib/std/crypto/test.zig", // 835
"lib/compiler_rt/bswapsi2_test.zig", // 840
"lib/compiler_rt/umodti3.zig", // 846
"lib/std/os/windows/crypt32.zig", // 850
"lib/compiler_rt/subvsi3.zig", // 860
"lib/compiler_rt/fixtfti.zig", // 867
"lib/compiler_rt/floattitf.zig", // 872
"lib/compiler_rt/addvsi3.zig", // 874
"lib/compiler_rt/bcmp.zig", // 874
"lib/compiler_rt/memset.zig", // 876
"lib/compiler_rt/truncsfhf2.zig", // 881
"lib/compiler_rt/subtf3.zig", // 884
"lib/compiler_rt/divti3_test.zig", // 886
"lib/compiler_rt/udivmodti4.zig", // 886
"lib/compiler_rt/floatuntitf.zig", // 888
"lib/compiler_rt/fixunstfti.zig", // 891
"lib/std/compress/lzma2.zig", // 894
"lib/compiler_rt/mulvsi3.zig", // 902
"lib/compiler_rt/bitreversesi2_test.zig", // 910
"lib/compiler_rt/extendhfsf2.zig", // 920
"lib/compiler_rt/memcmp.zig", // 931
"lib/compiler_rt/subvdi3.zig", // 932
"lib/init/src/main.zig", // 936
"lib/compiler_rt/gehf2.zig", // 960
"lib/compiler_rt/divsf3_test.zig", // 982
"lib/compiler_rt/paritysi2_test.zig", // 989
"lib/std/math/expo2.zig", // 995
};
pub const sema_unit_tests = [_][]const u8{
"stage0/sema_tests/empty.zig",
"stage0/sema_tests/const_decl.zig",
"stage0/sema_tests/empty_void_function.zig",
"stage0/sema_tests/return_integer.zig",
"stage0/sema_tests/identity_function.zig",
"stage0/sema_tests/add_two_args.zig",
"stage0/sema_tests/add_comptime_int.zig",
"stage0/sema_tests/sub_two_args.zig",
"stage0/sema_tests/xor_two_args.zig",
"stage0/sema_tests/xor_comptime_int.zig",
"stage0/sema_tests/bitcast_u32_to_f32.zig",
"stage0/sema_tests/bitcast_f32_to_u32.zig",
"stage0/sema_tests/as_node.zig",
"stage0/sema_tests/local_const_binding.zig",
"stage0/sema_tests/multiple_operations.zig",
"stage0/sema_tests/neghf2_inline_equivalent.zig",
"stage0/sema_tests/mul_two_args.zig",
"stage0/sema_tests/compare_lt.zig",
"stage0/sema_tests/compare_eq.zig",
"stage0/sema_tests/bit_shift_right.zig",
"stage0/sema_tests/mul_comptime_int.zig",
"stage0/sema_tests/chain_of_casts.zig",
"stage0/sema_tests/mixed_arithmetic_and_bitwise.zig",
"stage0/sema_tests/shift_and_mask.zig",
"stage0/sema_tests/f32_arithmetic.zig",
"stage0/sema_tests/multi_param_function.zig",
"stage0/sema_tests/nested_bitcast_xor.zig",
"stage0/sema_tests/pointer_param_identity.zig",
"stage0/sema_tests/store_to_pointer.zig",
"stage0/sema_tests/sub_comptime.zig",
"stage0/sema_tests/store_runtime_value.zig",
"stage0/sema_tests/load_from_pointer.zig",
"stage0/sema_tests/negate.zig",
"stage0/sema_tests/bit_not.zig",
"stage0/sema_tests/bit_shift_left.zig",
"stage0/sema_tests/intcast.zig",
"stage0/sema_tests/truncate.zig",
"stage0/sema_tests/two_local_bindings.zig",
"stage0/sema_tests/wrapping_add.zig",
"stage0/sema_tests/wrapping_sub.zig",
"stage0/sema_tests/wrapping_mul.zig",
"stage0/sema_tests/bool_not.zig",
"stage0/sema_tests/if_simple.zig",
"stage0/sema_tests/wrapping_negate.zig",
"stage0/sema_tests/clz.zig",
"stage0/sema_tests/ctz.zig",
"stage0/sema_tests/popcount.zig",
"stage0/sema_tests/byteswap.zig",
"stage0/sema_tests/float_cast_widen.zig",
"stage0/sema_tests/float_cast_narrow.zig",
"stage0/sema_tests/int_from_float.zig",
"stage0/sema_tests/float_from_int.zig",
"stage0/sema_tests/bitmask_shift_and.zig",
"stage0/sema_tests/double_negate.zig",
"stage0/sema_tests/return_ptr_type.zig",
"stage0/sema_tests/float_cast_f16_to_f32.zig",
"stage0/sema_tests/wrapping_add_comptime.zig",
"stage0/sema_tests/byteswap_and_xor.zig",
"stage0/sema_tests/same_file_inline_function_call.zig",
"stage0/sema_tests/same_file_inline_call_with_bitcast_and_xor.zig",
"stage0/sema_tests/same_file_inline_call_with_two_args.zig",
"stage0/sema_tests/intfrombool.zig",
"stage0/sema_tests/add_sat.zig",
"stage0/sema_tests/sub_sat.zig",
"stage0/sema_tests/mul_sat.zig",
"stage0/sema_tests/shl_sat.zig",
"stage0/sema_tests/bit_or.zig",
"stage0/sema_tests/bit_and.zig",
"stage0/sema_tests/f16_add.zig",
"stage0/sema_tests/f64_mul.zig",
"stage0/sema_tests/intcast_computed_dest_type.zig",
"stage0/sema_tests/multiple_return_paths.zig",
"stage0/sema_tests/if_with_early_return.zig",
"stage0/sema_tests/var_bitcast_and_if.zig",
"stage0/sema_tests/var_assignment_in_if.zig",
"stage0/sema_tests/nested_if.zig",
"stage0/sema_tests/wrapping_sub_in_expr.zig",
"stage0/sema_tests/if_else_block_result.zig",
"stage0/sema_tests/call_inside_runtime_conditional.zig",
"stage0/sema_tests/inline_fn_with_call_inside_conditional.zig",
"stage0/sema_tests/plus_eq_with_call_inside_conditional.zig",
"stage0/sema_tests/inline_fn_with_plus_eq_call_inside_conditional.zig",
"stage0/sema_tests/inline_fn_with_generic_call_inside_conditional.zig",
"stage0/sema_tests/inline_fn_with_two_generic_calls_in_conditionals.zig",
"stage0/sema_tests/inline_fn_with_plus_eq_call_inside_two_conditionals.zig",
"stage0/sema_tests/abs_float.zig",
"stage0/sema_tests/max_float.zig",
"stage0/sema_tests/min_float.zig",
"stage0/sema_tests/f64_div.zig",
};

View File

@@ -5484,8 +5484,8 @@ static AirInstRef semaResolveSwitchComptime(
const uint32_t* body = &sema->code.extra[body_start];
// Reserve BLOCK instruction (matches Zig line 11927-11931).
// Data is undefined in upstream; use 0 so the dead-block
// skip logic in the test comparison can handle it.
// Data is undefined in upstream; zero it so the dead-block
// skip rule in the test comparator fires correctly.
AirInstData block_data;
memset(&block_data, 0, sizeof(block_data));
uint32_t block_inst

View File

@@ -221,69 +221,128 @@ test "sema: function decl smoke test" {
}
// ---------------------------------------------------------------------------
// Air raw comparison: C vs Zig
// Air raw comparison: C vs pre-computed Zig AIR
// ---------------------------------------------------------------------------
const ZigCompileAirResult = extern struct {
items: ?[*]c.SemaFuncAir,
len: u32,
callback_count: u32,
};
extern fn zig_compile_air([*:0]const u8, ?[*:0]const u8, [*]u8) ZigCompileAirResult;
extern fn zig_compile_air_free(*ZigCompileAirResult) void;
const air_tag_names = @import("air_tag_names");
pub fn airCompareFromSource(source: [:0]const u8, c_func_air_list: c.SemaFuncAirList) !void {
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const f = tmp.dir.createFile("t.zig", .{}) catch return error.TmpFileCreate;
f.writeAll(source) catch {
f.close();
return error.TmpFileWrite;
};
f.close();
var path_buf: [std.fs.max_path_bytes:0]u8 = undefined;
const tmp_path = tmp.dir.realpathZ("t.zig", &path_buf) catch return error.TmpFileCreate;
path_buf[tmp_path.len] = 0;
return airCompare(@ptrCast(tmp_path.ptr), null, c_func_air_list);
/// A parsed function from the pre-computed AIR binary data.
pub const PrecomputedFunc = struct {
name: []const u8,
air: c.Air,
};
/// Parse pre-computed AIR from binary data (generated by air_gen).
/// Binary format:
/// func_count: u32 (little-endian)
/// Per function:
/// name_len: u32
/// name: [name_len]u8
/// inst_len: u32
/// inst_tags: [inst_len]u8
/// inst_datas: [inst_len * 8]u8
/// extra_len: u32
/// extra: [extra_len * 4]u8
pub fn parsePrecomputedAir(gpa: std.mem.Allocator, data: []const u8) ![]PrecomputedFunc {
var pos: usize = 0;
const func_count = readU32(data, &pos) orelse return error.InvalidAirData;
const funcs = try gpa.alloc(PrecomputedFunc, func_count);
errdefer gpa.free(funcs);
for (funcs) |*f| {
// name
const name_len = readU32(data, &pos) orelse return error.InvalidAirData;
if (pos + name_len > data.len) return error.InvalidAirData;
f.name = data[pos..][0..name_len];
pos += name_len;
// inst_tags + inst_datas (allocate copies with proper alignment)
const inst_len = readU32(data, &pos) orelse return error.InvalidAirData;
const tags: [*c]u8 = if (inst_len > 0) blk: {
if (pos + inst_len > data.len) return error.InvalidAirData;
const alloc = try gpa.alloc(u8, inst_len);
@memcpy(alloc, data[pos..][0..inst_len]);
break :blk alloc.ptr;
} else null;
pos += inst_len;
const datas: [*c]c.AirInstData = if (inst_len > 0) blk: {
const datas_byte_len = inst_len * 8;
if (pos + datas_byte_len > data.len) return error.InvalidAirData;
const alloc = try gpa.alloc(c.AirInstData, inst_len);
const alloc_bytes: [*]u8 = @ptrCast(alloc.ptr);
@memcpy(alloc_bytes[0..datas_byte_len], data[pos..][0..datas_byte_len]);
break :blk alloc.ptr;
} else null;
pos += inst_len * 8;
// extra (allocate copy with u32 alignment)
const extra_len = readU32(data, &pos) orelse return error.InvalidAirData;
const extra: [*c]u32 = if (extra_len > 0) blk: {
const extra_byte_len = extra_len * 4;
if (pos + extra_byte_len > data.len) return error.InvalidAirData;
const alloc = try gpa.alloc(u32, extra_len);
const alloc_bytes: [*]u8 = @ptrCast(alloc.ptr);
@memcpy(alloc_bytes[0..extra_byte_len], data[pos..][0..extra_byte_len]);
break :blk alloc.ptr;
} else null;
pos += extra_len * 4;
f.air = .{
.inst_len = inst_len,
.inst_cap = inst_len,
.inst_tags = tags,
.inst_datas = datas,
.extra_len = extra_len,
.extra_cap = extra_len,
.extra = extra,
};
}
return funcs;
}
pub fn airCompare(
src_path: [*:0]const u8,
module_root: ?[*:0]const u8,
c_func_air_list: c.SemaFuncAirList,
) !void {
var err_buf: [c.ZIG_COMPILE_ERR_BUF_SIZE]u8 = .{0} ** c.ZIG_COMPILE_ERR_BUF_SIZE;
var zig_result = zig_compile_air(src_path, module_root, &err_buf);
defer zig_compile_air_free(&zig_result);
fn readU32(data: []const u8, pos: *usize) ?u32 {
if (pos.* + 4 > data.len) return null;
const val = std.mem.readInt(u32, data[pos.*..][0..4], .little);
pos.* += 4;
return val;
}
if (err_buf[0] != 0) {
std.debug.print("zig_compile_air error: {s}\n", .{std.mem.sliceTo(&err_buf, 0)});
return error.ZigCompileError;
pub fn freePrecomputedAir(gpa: std.mem.Allocator, funcs: []PrecomputedFunc) void {
for (funcs) |f| {
if (f.air.inst_tags) |t| gpa.free(t[0..f.air.inst_len]);
if (f.air.inst_datas) |d| gpa.free(d[0..f.air.inst_len]);
if (f.air.extra) |e| gpa.free(e[0..f.air.extra_len]);
}
gpa.free(funcs);
}
// Canary: if C sema found functions, the Zig callback must have fired.
if (c_func_air_list.len > 0 and zig_result.callback_count == 0) {
std.debug.print("Canary: C sema produced {d} functions but Zig callback never fired\n", .{c_func_air_list.len});
return error.AirCallbackNotFired;
}
const zig_funcs = if (zig_result.items) |items| items[0..zig_result.len] else &[_]c.SemaFuncAir{};
/// Compare C sema output against pre-computed AIR data.
pub fn airComparePrecomputed(precomputed: []const PrecomputedFunc, c_func_air_list: c.SemaFuncAirList) !void {
const c_funcs_ptr: ?[*]const c.SemaFuncAir = @ptrCast(c_func_air_list.items);
const c_funcs = if (c_funcs_ptr) |items| items[0..c_func_air_list.len] else &[_]c.SemaFuncAir{};
// Compare functions that exist in both C and Zig output.
// The C sema may produce fewer functions (e.g. missing import
// resolution), so we iterate C functions and look them up in Zig.
for (c_funcs) |*cf| {
const c_name = if (cf.name) |n| std.mem.span(n) else "";
const zf = airFindByName(zig_funcs, c_name) orelse {
std.debug.print("C function '{s}' not found in Zig output\n", .{c_name});
const pf = precomputedFindByName(precomputed, c_name) orelse {
std.debug.print("C function '{s}' not found in pre-computed AIR\n", .{c_name});
return error.AirMismatch;
};
try airCompareOne(c_name, &zf.air, &cf.air);
try airCompareOne(c_name, &pf.air, &cf.air);
}
}
fn precomputedFindByName(funcs: []const PrecomputedFunc, name: []const u8) ?*const PrecomputedFunc {
const bare_name = stripModulePrefix(name);
for (funcs) |*f| {
if (std.mem.eql(u8, bare_name, stripModulePrefix(f.name))) return f;
}
return null;
}
fn cNameSpan(name: [*c]u8) []const u8 {
const opt: ?[*:0]const u8 = @ptrCast(name);
return if (opt) |n| std.mem.span(n) else "";
@@ -298,23 +357,13 @@ fn stripModulePrefix(fqn: []const u8) []const u8 {
fqn;
}
fn airFindByName(funcs: []const c.SemaFuncAir, name: []const u8) ?*const c.SemaFuncAir {
const bare_name = stripModulePrefix(name);
for (funcs) |*f| {
const c_name = cNameSpan(f.name);
if (std.mem.eql(u8, bare_name, stripModulePrefix(c_name))) return f;
}
return null;
}
fn cToOpt(comptime T: type, ptr: [*c]T) ?[*]const T {
return if (ptr == null) null else @ptrCast(ptr);
}
fn airTagNameSlice(tag_val: u8) []const u8 {
return std.mem.span(air_tag_name(tag_val));
return air_tag_names.names[tag_val];
}
extern fn air_tag_name(tag: u8) [*:0]const u8;
fn refKindStr(ref: u32) []const u8 {
if (ref == 0xFFFFFFFF) return "none";
@@ -791,586 +840,22 @@ fn airCompareOne(name: []const u8, zig_air: *const c.Air, c_air: *const c.Air) !
}
}
fn semaAirRawCheck(source: [:0]const u8) !void {
// C pipeline: parse -> astgen -> sema
var result = try semaCheck(source);
defer result.deinit();
// Zig pipeline: compile source and compare Air arrays
try airCompareFromSource(source, result.c_func_air_list);
}
test "sema: Air raw C vs Zig comparison (empty)" {
try semaAirRawCheck("");
}
test "sema: Air raw C vs Zig comparison (const)" {
try semaAirRawCheck("const x = 0;");
}
test "sema air: empty void function" {
try semaAirRawCheck("export fn f() void {}");
}
test "sema air: return integer" {
try semaAirRawCheck("export fn f() u32 { return 42; }");
}
test "sema air: identity function" {
try semaAirRawCheck("export fn f(x: u32) u32 { return x; }");
}
test "sema air: add two args" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x + y; }");
}
test "sema air: add comptime int" {
try semaAirRawCheck("export fn f(x: u32) u32 { return x + 1; }");
}
test "sema air: sub two args" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x - y; }");
}
test "sema air: xor two args" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x ^ y; }");
}
test "sema air: xor comptime int" {
try semaAirRawCheck("export fn f(x: u16) u16 { return x ^ 0x8000; }");
}
test "sema air: bitcast u32 to f32" {
try semaAirRawCheck("export fn f(x: u32) f32 { return @bitCast(x); }");
}
test "sema air: bitcast f32 to u32" {
try semaAirRawCheck("export fn f(x: f32) u32 { return @bitCast(x); }");
}
test "sema air: as node" {
try semaAirRawCheck("export fn f(x: u32) u32 { return @as(u32, x); }");
}
test "sema air: local const binding" {
try semaAirRawCheck("export fn f(x: u32) u32 { const y = x + 1; return y; }");
}
test "sema air: multiple operations" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return (x + y) ^ 0xFF; }");
}
test "sema air: neghf2 inline equivalent" {
try semaAirRawCheck(
\\export fn f(a: f16) f16 {
\\ return @bitCast(@as(u16, @bitCast(a)) ^ @as(u16, 0x8000));
\\}
);
}
test "sema air: mul two args" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x * y; }");
}
// TODO: bool and/or require block merges and conditional analysis (bool_br_and).
// test "sema air: bool and" {
// try semaAirRawCheck("export fn f(x: bool, y: bool) bool { return x and y; }");
// }
test "sema air: compare lt" {
try semaAirRawCheck("export fn f(x: u32, y: u32) bool { return x < y; }");
}
test "sema air: compare eq" {
try semaAirRawCheck("export fn f(x: u32, y: u32) bool { return x == y; }");
}
test "sema air: bit shift right" {
try semaAirRawCheck("export fn f(x: u32) u32 { return x >> 1; }");
}
test "sema air: mul comptime int" {
try semaAirRawCheck("export fn f(x: u32) u32 { return x * 3; }");
}
test "sema air: chain of casts" {
try semaAirRawCheck(
\\export fn f(x: u8) u32 {
\\ const wide: u16 = @intCast(x);
\\ return @intCast(wide);
\\}
);
}
test "sema air: mixed arithmetic and bitwise" {
try semaAirRawCheck(
\\export fn f(a: u32, b: u32) u32 {
\\ return (a + b) & 0xFF;
\\}
);
}
test "sema air: shift and mask" {
try semaAirRawCheck(
\\export fn f(x: u32) u32 {
\\ return (x >> 8) & 0xFF;
\\}
);
}
test "sema air: f32 arithmetic" {
try semaAirRawCheck("export fn f(x: f32, y: f32) f32 { return x + y; }");
}
test "sema air: multi-param function" {
try semaAirRawCheck(
\\export fn f(a: u32, b: u32, c: u32) u32 {
\\ return (a + b) * c;
\\}
);
}
test "sema air: nested bitcast xor" {
try semaAirRawCheck(
\\export fn f(a: f32) f32 {
\\ return @bitCast(@as(u32, @bitCast(a)) ^ @as(u32, 0x80000000));
\\}
);
}
test "sema air: pointer param identity" {
try semaAirRawCheck("export fn f(x: *u32) *u32 { return x; }");
}
test "sema air: store to pointer" {
try semaAirRawCheck(
\\export fn f(x: *u32) void {
\\ x.* = 42;
\\}
);
}
test "sema air: sub comptime" {
try semaAirRawCheck("export fn f(x: u32) u32 { return x - 1; }");
}
test "sema air: store runtime value" {
try semaAirRawCheck(
\\export fn f(p: *u32, x: u32) void {
\\ p.* = x;
\\}
);
}
test "sema air: load from pointer" {
try semaAirRawCheck(
\\export fn f(p: *u32) u32 {
\\ return p.*;
\\}
);
}
test "sema air: negate" {
try semaAirRawCheck("export fn f(x: i32) i32 { return -x; }");
}
test "sema air: bit not" {
try semaAirRawCheck("export fn f(x: u32) u32 { return ~x; }");
}
test "sema air: bit shift left" {
try semaAirRawCheck("export fn f(x: u32) u32 { return x << 1; }");
}
test "sema air: intcast" {
try semaAirRawCheck("export fn f(x: u16) u32 { return @intCast(x); }");
}
test "sema air: truncate" {
try semaAirRawCheck("export fn f(x: u32) u16 { return @truncate(x); }");
}
test "sema air: two local bindings" {
try semaAirRawCheck(
\\export fn f(x: u32, y: u32) u32 {
\\ const a = x + 1;
\\ const b = y + 2;
\\ return a ^ b;
\\}
);
}
test "sema air: wrapping add" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x +% y; }");
}
test "sema air: wrapping sub" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x -% y; }");
}
test "sema air: wrapping mul" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x *% y; }");
}
// test "sema air: div" {
// // Requires zirDiv with safety checks (div_trunc + remainder check).
// try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x / y; }");
// }
test "sema air: bool not" {
try semaAirRawCheck("export fn f(x: bool) bool { return !x; }");
}
test "sema air: if simple" {
// Requires condbr, block merging, conditional branching.
try semaAirRawCheck(
\\export fn f(x: u32, y: u32) u32 {
\\ if (x > y) return x;
\\ return y;
\\}
);
}
test "sema air: wrapping negate" {
try semaAirRawCheck("export fn f(x: i32) i32 { return -%x; }");
}
test "sema air: clz" {
try semaAirRawCheck("export fn f(x: u32) u32 { return @clz(x); }");
}
test "sema air: ctz" {
try semaAirRawCheck("export fn f(x: u32) u32 { return @ctz(x); }");
}
test "sema air: popcount" {
try semaAirRawCheck("export fn f(x: u32) u32 { return @popCount(x); }");
}
test "sema air: byteswap" {
try semaAirRawCheck("export fn f(x: u32) u32 { return @byteSwap(x); }");
}
test "sema air: float cast widen" {
try semaAirRawCheck("export fn f(x: f32) f64 { return @floatCast(x); }");
}
test "sema air: float cast narrow" {
try semaAirRawCheck("export fn f(x: f64) f32 { return @floatCast(x); }");
}
test "sema air: int from float" {
try semaAirRawCheck("export fn f(x: f32) u32 { return @intFromFloat(x); }");
}
test "sema air: float from int" {
try semaAirRawCheck("export fn f(x: u32) f32 { return @floatFromInt(x); }");
}
test "sema air: bitmask shift and" {
try semaAirRawCheck("export fn f(x: u32) u32 { return (x >> 16) & 0xFF; }");
}
test "sema air: double negate" {
try semaAirRawCheck("export fn f(x: i32) i32 { return -(-x); }");
}
test "sema air: return ptr type" {
try semaAirRawCheck("export fn f(p: *u32) *u32 { return p; }");
}
test "sema air: float cast f16 to f32" {
try semaAirRawCheck("export fn f(x: f16) f32 { return @floatCast(x); }");
}
test "sema air: wrapping add comptime" {
try semaAirRawCheck("export fn f(x: u32) u32 { return x +% 1; }");
}
test "sema air: byteswap and xor" {
try semaAirRawCheck(
\\export fn f(x: u32) u32 {
\\ return @byteSwap(x) ^ 0xFF;
\\}
);
}
test "sema air: same-file inline function call" {
try semaAirRawCheck(
\\inline fn negate(x: u16) u16 {
\\ return ~x;
\\}
\\export fn f(a: u16) u16 {
\\ return negate(a);
\\}
);
}
test "sema air: same-file inline call with bitcast and xor" {
try semaAirRawCheck(
\\inline fn flip_sign(x: u16) u16 {
\\ return x ^ 0x8000;
\\}
\\export fn f(a: u16) u16 {
\\ return flip_sign(a);
\\}
);
}
test "sema air: same-file inline call with two args" {
try semaAirRawCheck(
\\inline fn my_add(x: u32, y: u32) u32 {
\\ return x + y;
\\}
\\export fn f(a: u32, b: u32) u32 {
\\ return my_add(a, b);
\\}
);
}
test "sema air: intFromBool" {
try semaAirRawCheck("export fn f(x: bool) u32 { return @intFromBool(x); }");
}
test "sema air: add_sat" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x +| y; }");
}
test "sema air: sub_sat" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x -| y; }");
}
test "sema air: mul_sat" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x *| y; }");
}
test "sema air: shl_sat" {
try semaAirRawCheck("export fn f(x: u32) u32 { return x <<| 1; }");
}
test "sema air: bit_or" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x | y; }");
}
test "sema air: bit_and" {
try semaAirRawCheck("export fn f(x: u32, y: u32) u32 { return x & y; }");
}
test "sema air: f16 add" {
try semaAirRawCheck("export fn f(x: f16, y: f16) f16 { return x + y; }");
}
test "sema air: f64 mul" {
try semaAirRawCheck("export fn f(x: f64, y: f64) f64 { return x * y; }");
}
test "sema air: intcast computed dest type" {
try semaAirRawCheck(
\\export fn f(x: u16) u32 {
\\ return @intCast(x);
\\}
);
}
test "sema air: multiple return paths" {
try semaAirRawCheck(
\\export fn f(x: u32) u32 {
\\ return x + 1;
\\}
\\export fn g(x: u32) u32 {
\\ return x * 2;
\\}
);
}
test "sema air: if with early return" {
try semaAirRawCheck(
\\export fn f(x: u32, y: u32) u32 {
\\ if (x > y) return x;
\\ return y;
\\}
);
}
test "sema air: var bitcast and if" {
try semaAirRawCheck(
\\export fn f(a: f16) u16 {
\\ var x: u16 = @bitCast(a);
\\ if (x > 100) {
\\ x = x - 1;
\\ }
\\ return x;
\\}
);
}
test "sema air: var assignment in if" {
try semaAirRawCheck(
\\export fn f(a: u32, b: u32) u32 {
\\ var x = a;
\\ if (b > a) {
\\ x = b;
\\ }
\\ return x;
\\}
);
}
test "sema air: nested if" {
try semaAirRawCheck(
\\export fn f(a: u32, b: u32) u32 {
\\ if (a > 0) {
\\ if (b > 0) return a + b;
\\ return a;
\\ }
\\ return b;
\\}
);
}
test "sema air: wrapping sub in expr" {
try semaAirRawCheck(
\\export fn f(a: u32) u32 {
\\ return a -% 1;
\\}
);
}
test "sema air: if-else block result" {
try semaAirRawCheck(
\\export fn f(a: u32, b: u32) u32 {
\\ const x = if (a > b) a else b;
\\ return x;
\\}
);
}
test "sema air: call inside runtime conditional" {
try semaAirRawCheck(
\\fn bar(p: *u32) void {
\\ p.* += 1;
\\}
\\export fn f(a: u32) u32 {
\\ var x: u32 = a;
\\ if (a < 10) bar(&x);
\\ return x;
\\}
);
}
test "sema air: inline fn with call inside conditional" {
try semaAirRawCheck(
\\fn bar(p: *u32) void {
\\ p.* += 1;
\\}
\\inline fn baz(a: u32, x: *u32) void {
\\ if (a < 10) bar(x);
\\}
\\export fn f(a: u32) u32 {
\\ var x: u32 = a;
\\ baz(a, &x);
\\ return x;
\\}
);
}
test "sema air: += with call inside conditional" {
try semaAirRawCheck(
\\fn bar(p: *u32) u32 {
\\ return p.* + 1;
\\}
\\export fn f(a: u32) u32 {
\\ var x: u32 = a;
\\ if (a < 10) x += bar(&x);
\\ return x;
\\}
);
}
test "sema air: inline fn with += call inside conditional" {
try semaAirRawCheck(
\\fn bar(p: *u32) u32 {
\\ return p.* + 1;
\\}
\\inline fn baz(a: u32, x: *u32) void {
\\ if (a < 10) x.* += bar(x);
\\}
\\export fn f(a: u32) u32 {
\\ var x: u32 = a;
\\ baz(a, &x);
\\ return x;
\\}
);
}
test "sema air: inline fn with generic call inside conditional" {
try semaAirRawCheck(
\\fn normalize(comptime T: type, p: *T) i32 {
\\ p.* +%= 1;
\\ return 1;
\\}
\\inline fn mulf(comptime T: type, a: T) T {
\\ var x: T = a;
\\ var scale: i32 = 0;
\\ if (x < 10) scale += normalize(T, &x);
\\ return x +% @as(T, @intCast(@as(u32, @bitCast(scale))));
\\}
\\export fn f(a: u32) u32 {
\\ return mulf(u32, a);
\\}
);
}
test "sema air: inline fn with two generic calls in conditionals" {
try semaAirRawCheck(
\\fn normalize(comptime T: type, p: *T) i32 {
\\ p.* +%= 1;
\\ return 1;
\\}
\\inline fn mulf(comptime T: type, a: T, b: T) T {
\\ var x: T = a;
\\ var y: T = b;
\\ var scale: i32 = 0;
\\ if (x < 10) scale += normalize(T, &x);
\\ if (y < 10) scale += normalize(T, &y);
\\ return x +% y +% @as(T, @intCast(@as(u32, @bitCast(scale))));
\\}
\\export fn f(a: u32, b: u32) u32 {
\\ return mulf(u32, a, b);
\\}
);
}
test "sema air: inline fn with += call inside two conditionals" {
try semaAirRawCheck(
\\fn bar(p: *u32) u32 {
\\ return p.* + 1;
\\}
\\inline fn baz(a: u32, x: *u32, y: *u32) void {
\\ if (a < 10) x.* += bar(x);
\\ if (a < 20) y.* += bar(y);
\\}
\\export fn f(a: u32) u32 {
\\ var x: u32 = a;
\\ var y: u32 = a;
\\ baz(a, &x, &y);
\\ return x +% y;
\\}
);
}
test "sema air: @abs float" {
try semaAirRawCheck("export fn f(x: f64) f64 { return @abs(x); }");
}
test "sema air: @max float" {
try semaAirRawCheck("export fn f(x: f64, y: f64) f64 { return @max(x, y); }");
}
test "sema air: @min float" {
try semaAirRawCheck("export fn f(x: f64, y: f64) f64 { return @min(x, y); }");
}
test "sema air: f64 div" {
try semaAirRawCheck("export fn f(x: f64, y: f64) f64 { return x / y; }");
const corpus = @import("corpus.zig");
test "sema air: unit tests" {
const gpa = std.testing.allocator;
@setEvalBranchQuota(corpus.sema_unit_tests.len * 2);
inline for (corpus.sema_unit_tests) |path| {
const source: [:0]const u8 = @embedFile("../" ++ path);
var result = try semaCheck(source);
defer result.deinit();
const air_data = @import("air_data").getData(path);
const precomputed = try parsePrecomputedAir(gpa, air_data);
defer freePrecomputedAir(gpa, precomputed);
airComparePrecomputed(precomputed, result.c_func_air_list) catch {
std.debug.print("FAIL: {s}\n", .{path});
return error.TestFailed;
};
}
}

View File

@@ -0,0 +1 @@
export fn f(x: f64) f64 { return @abs(x); }

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return x + 1; }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return x +| y; }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return x + y; }

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return @as(u32, x); }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return x & y; }

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return ~x; }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return x | y; }

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return x << 1; }

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return x >> 1; }

View File

@@ -0,0 +1 @@
export fn f(x: f32) u32 { return @bitCast(x); }

View File

@@ -0,0 +1 @@
export fn f(x: u32) f32 { return @bitCast(x); }

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return (x >> 16) & 0xFF; }

View File

@@ -0,0 +1 @@
export fn f(x: bool) bool { return !x; }

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return @byteSwap(x); }

View File

@@ -0,0 +1,3 @@
export fn f(x: u32) u32 {
return @byteSwap(x) ^ 0xFF;
}

View File

@@ -0,0 +1,8 @@
fn bar(p: *u32) void {
p.* += 1;
}
export fn f(a: u32) u32 {
var x: u32 = a;
if (a < 10) bar(&x);
return x;
}

View File

@@ -0,0 +1,4 @@
export fn f(x: u8) u32 {
const wide: u16 = @intCast(x);
return @intCast(wide);
}

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return @clz(x); }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) bool { return x == y; }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) bool { return x < y; }

View File

@@ -0,0 +1 @@
const x = 0;

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return @ctz(x); }

View File

@@ -0,0 +1 @@
export fn f(x: i32) i32 { return -(-x); }

View File

View File

@@ -0,0 +1 @@
export fn f() void {}

View File

@@ -0,0 +1 @@
export fn f(x: f16, y: f16) f16 { return x + y; }

View File

@@ -0,0 +1 @@
export fn f(x: f32, y: f32) f32 { return x + y; }

View File

@@ -0,0 +1 @@
export fn f(x: f64, y: f64) f64 { return x / y; }

View File

@@ -0,0 +1 @@
export fn f(x: f64, y: f64) f64 { return x * y; }

View File

@@ -0,0 +1 @@
export fn f(x: f16) f32 { return @floatCast(x); }

View File

@@ -0,0 +1 @@
export fn f(x: f64) f32 { return @floatCast(x); }

View File

@@ -0,0 +1 @@
export fn f(x: f32) f64 { return @floatCast(x); }

View File

@@ -0,0 +1 @@
export fn f(x: u32) f32 { return @floatFromInt(x); }

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return x; }

View File

@@ -0,0 +1,4 @@
export fn f(a: u32, b: u32) u32 {
const x = if (a > b) a else b;
return x;
}

View File

@@ -0,0 +1,4 @@
export fn f(x: u32, y: u32) u32 {
if (x > y) return x;
return y;
}

View File

@@ -0,0 +1,4 @@
export fn f(x: u32, y: u32) u32 {
if (x > y) return x;
return y;
}

View File

@@ -0,0 +1,11 @@
fn bar(p: *u32) void {
p.* += 1;
}
inline fn baz(a: u32, x: *u32) void {
if (a < 10) bar(x);
}
export fn f(a: u32) u32 {
var x: u32 = a;
baz(a, &x);
return x;
}

View File

@@ -0,0 +1,13 @@
fn normalize(comptime T: type, p: *T) i32 {
p.* +%= 1;
return 1;
}
inline fn mulf(comptime T: type, a: T) T {
var x: T = a;
var scale: i32 = 0;
if (x < 10) scale += normalize(T, &x);
return x +% @as(T, @intCast(@as(u32, @bitCast(scale))));
}
export fn f(a: u32) u32 {
return mulf(u32, a);
}

View File

@@ -0,0 +1,11 @@
fn bar(p: *u32) u32 {
return p.* + 1;
}
inline fn baz(a: u32, x: *u32) void {
if (a < 10) x.* += bar(x);
}
export fn f(a: u32) u32 {
var x: u32 = a;
baz(a, &x);
return x;
}

View File

@@ -0,0 +1,13 @@
fn bar(p: *u32) u32 {
return p.* + 1;
}
inline fn baz(a: u32, x: *u32, y: *u32) void {
if (a < 10) x.* += bar(x);
if (a < 20) y.* += bar(y);
}
export fn f(a: u32) u32 {
var x: u32 = a;
var y: u32 = a;
baz(a, &x, &y);
return x +% y;
}

View File

@@ -0,0 +1,15 @@
fn normalize(comptime T: type, p: *T) i32 {
p.* +%= 1;
return 1;
}
inline fn mulf(comptime T: type, a: T, b: T) T {
var x: T = a;
var y: T = b;
var scale: i32 = 0;
if (x < 10) scale += normalize(T, &x);
if (y < 10) scale += normalize(T, &y);
return x +% y +% @as(T, @intCast(@as(u32, @bitCast(scale))));
}
export fn f(a: u32, b: u32) u32 {
return mulf(u32, a, b);
}

View File

@@ -0,0 +1 @@
export fn f(x: f32) u32 { return @intFromFloat(x); }

View File

@@ -0,0 +1 @@
export fn f(x: u16) u32 { return @intCast(x); }

View File

@@ -0,0 +1,3 @@
export fn f(x: u16) u32 {
return @intCast(x);
}

View File

@@ -0,0 +1 @@
export fn f(x: bool) u32 { return @intFromBool(x); }

View File

@@ -0,0 +1,3 @@
export fn f(p: *u32) u32 {
return p.*;
}

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { const y = x + 1; return y; }

View File

@@ -0,0 +1 @@
export fn f(x: f64, y: f64) f64 { return @max(x, y); }

View File

@@ -0,0 +1 @@
export fn f(x: f64, y: f64) f64 { return @min(x, y); }

View File

@@ -0,0 +1,3 @@
export fn f(a: u32, b: u32) u32 {
return (a + b) & 0xFF;
}

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return x * 3; }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return x *| y; }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return x * y; }

View File

@@ -0,0 +1,3 @@
export fn f(a: u32, b: u32, c: u32) u32 {
return (a + b) * c;
}

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return (x + y) ^ 0xFF; }

View File

@@ -0,0 +1,6 @@
export fn f(x: u32) u32 {
return x + 1;
}
export fn g(x: u32) u32 {
return x * 2;
}

View File

@@ -0,0 +1 @@
export fn f(x: i32) i32 { return -x; }

View File

@@ -0,0 +1,3 @@
export fn f(a: f16) f16 {
return @bitCast(@as(u16, @bitCast(a)) ^ @as(u16, 0x8000));
}

View File

@@ -0,0 +1,3 @@
export fn f(a: f32) f32 {
return @bitCast(@as(u32, @bitCast(a)) ^ @as(u32, 0x80000000));
}

View File

@@ -0,0 +1,7 @@
export fn f(a: u32, b: u32) u32 {
if (a > 0) {
if (b > 0) return a + b;
return a;
}
return b;
}

View File

@@ -0,0 +1,8 @@
fn bar(p: *u32) u32 {
return p.* + 1;
}
export fn f(a: u32) u32 {
var x: u32 = a;
if (a < 10) x += bar(&x);
return x;
}

View File

@@ -0,0 +1 @@
export fn f(x: *u32) *u32 { return x; }

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return @popCount(x); }

View File

@@ -0,0 +1 @@
export fn f() u32 { return 42; }

View File

@@ -0,0 +1 @@
export fn f(p: *u32) *u32 { return p; }

View File

@@ -0,0 +1,6 @@
inline fn flip_sign(x: u16) u16 {
return x ^ 0x8000;
}
export fn f(a: u16) u16 {
return flip_sign(a);
}

View File

@@ -0,0 +1,6 @@
inline fn my_add(x: u32, y: u32) u32 {
return x + y;
}
export fn f(a: u32, b: u32) u32 {
return my_add(a, b);
}

View File

@@ -0,0 +1,6 @@
inline fn negate(x: u16) u16 {
return ~x;
}
export fn f(a: u16) u16 {
return negate(a);
}

View File

@@ -0,0 +1,3 @@
export fn f(x: u32) u32 {
return (x >> 8) & 0xFF;
}

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return x <<| 1; }

View File

@@ -0,0 +1,3 @@
export fn f(p: *u32, x: u32) void {
p.* = x;
}

View File

@@ -0,0 +1,3 @@
export fn f(x: *u32) void {
x.* = 42;
}

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return x - 1; }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return x -| y; }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return x - y; }

View File

@@ -0,0 +1 @@
export fn f(x: u32) u16 { return @truncate(x); }

View File

@@ -0,0 +1,5 @@
export fn f(x: u32, y: u32) u32 {
const a = x + 1;
const b = y + 2;
return a ^ b;
}

View File

@@ -0,0 +1,7 @@
export fn f(a: u32, b: u32) u32 {
var x = a;
if (b > a) {
x = b;
}
return x;
}

View File

@@ -0,0 +1,7 @@
export fn f(a: f16) u16 {
var x: u16 = @bitCast(a);
if (x > 100) {
x = x - 1;
}
return x;
}

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return x +% y; }

View File

@@ -0,0 +1 @@
export fn f(x: u32) u32 { return x +% 1; }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return x *% y; }

View File

@@ -0,0 +1 @@
export fn f(x: i32) i32 { return -%x; }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return x -% y; }

View File

@@ -0,0 +1,3 @@
export fn f(a: u32) u32 {
return a -% 1;
}

View File

@@ -0,0 +1 @@
export fn f(x: u16) u16 { return x ^ 0x8000; }

View File

@@ -0,0 +1 @@
export fn f(x: u32, y: u32) u32 { return x ^ y; }

File diff suppressed because it is too large Load Diff