verbose_air: add human-readable AIR printer and --verbose-air CLI flag

Add verbose_air.c/h implementing a human-readable AIR printer for
debugging the C sema, ported from src/Air/print.zig. Types print as
human-readable names (u32, *const u8, fn (...) noreturn) instead of
raw IP indices. Add --verbose-air flag to zig0 CLI and a `zig build
zig0` target for building the standalone executable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 15:20:06 +00:00
parent 29a4943fc2
commit 93b49123cc
8 changed files with 1196 additions and 10 deletions

View File

@@ -10,8 +10,34 @@ const assert = std.debug.assert;
const DevEnv = @import("src/dev.zig").Env;
const ValueInterpretMode = enum { direct, by_name };
const zig0_headers = &[_][]const u8{ "common.h", "ast.h", "parser.h", "zir.h", "astgen.h", "intern_pool.h", "air.h", "type.h", "value.h", "sema.h", "dump.h" };
const zig0_c_lib_files = &[_][]const u8{ "tokenizer.c", "ast.c", "zig0.c", "parser.c", "zir.c", "astgen.c", "intern_pool.c", "air.c", "type.c", "value.c", "sema.c" };
const zig0_headers = &[_][]const u8{
"air.h",
"ast.h",
"astgen.h",
"common.h",
"dump.h",
"intern_pool.h",
"parser.h",
"sema.h",
"type.h",
"value.h",
"verbose_air.h",
"zir.h",
};
const zig0_c_lib_files = &[_][]const u8{
"air.c",
"ast.c",
"astgen.c",
"intern_pool.c",
"parser.c",
"sema.c",
"tokenizer.c",
"type.c",
"value.c",
"verbose_air.c",
"zig0.c",
"zir.c",
};
const zig0_all_c_files = zig0_c_lib_files ++ &[_][]const u8{"main.c"};
const zig0_cflags = &[_][]const u8{
"-std=c11",
@@ -712,6 +738,25 @@ pub fn build(b: *std.Build) !void {
const test_zig0_step = b.step("test-zig0", "Run zig0 C implementation tests");
addZig0TestStep(b, test_zig0_step, zig0_target, optimize, zig0_cc, zig0_no_exec, zig0_valgrind, zig0_test_timeout, zig0_exe_options);
// zig0 standalone executable
const zig0_step = b.step("zig0", "Build zig0 standalone executable");
const zig0_mod = b.createModule(.{
.target = zig0_target,
.optimize = optimize,
});
zig0_mod.addCSourceFiles(.{
.root = b.path("stage0"),
.files = zig0_all_c_files,
.flags = zig0_cflags,
});
zig0_mod.linkSystemLibrary("c", .{});
const zig0_exe = b.addExecutable(.{
.name = "zig0",
.root_module = zig0_mod,
});
const zig0_install = b.addInstallArtifact(zig0_exe, .{});
zig0_step.dependOn(&zig0_install.step);
const fmt_zig0 = b.step("fmt-zig0", "Format zig0 C code");
const clang_format = b.addSystemCommand(&.{ "clang-format", "-i" });
for (zig0_all_c_files ++ zig0_headers) |f| clang_format.addFileArg(b.path(b.fmt("stage0/{s}", .{f})));

View File

@@ -24,6 +24,7 @@ const CAir = extern struct {
/// Matches C `SemaFuncAir` struct layout (sema.h).
const CSemaFuncAir = extern struct {
name: ?[*:0]u8,
func_ip: u32, // InternPoolIndex
air: CAir,
};
@@ -98,6 +99,7 @@ const AirCollector = struct {
try self.funcs.append(gpa, .{
.name = name_copy.ptr,
.func_ip = std.math.maxInt(u32), // IP_INDEX_NONE
.air = .{
.inst_len = inst_len,
.inst_cap = inst_len,

View File

@@ -3,22 +3,41 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int zig0Run(char* program, char** msg);
int zig0RunFile(char* fname, char** msg);
int zig0RunFile(const char* fname, bool verbose_air, char** msg);
static void usage(const char* argv0) {
fprintf(stderr, "Usage: %s program.zig\n", argv0);
fprintf(stderr, "Usage: %s [--verbose-air] program.zig\n", argv0);
}
int main(int argc, char** argv) {
if (argc != 2) {
bool verbose_air = false;
const char* fname = NULL;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--verbose-air") == 0) {
verbose_air = true;
} else if (argv[i][0] == '-') {
fprintf(stderr, "Unknown option: %s\n", argv[i]);
usage(argv[0]);
return 1;
} else if (fname == NULL) {
fname = argv[i];
} else {
fprintf(stderr, "Unexpected argument: %s\n", argv[i]);
usage(argv[0]);
return 1;
}
}
if (fname == NULL) {
usage(argv[0]);
return 1;
}
char* msg;
switch (zig0RunFile(argv[1], &msg)) {
switch (zig0RunFile(fname, verbose_air, &msg)) {
case 0:
return 0;
break;

View File

@@ -3241,6 +3241,7 @@ static void zirFunc(Sema* sema, SemaBlock* block, uint32_t inst) {
}
SemaFuncAir* entry = &list->items[list->len++];
entry->name = func_name;
entry->func_ip = IP_INDEX_NONE;
entry->air.inst_tags = sema->air_inst_tags;
entry->air.inst_datas = sema->air_inst_datas;
entry->air.inst_len = sema->air_inst_len;

View File

@@ -110,6 +110,7 @@ typedef struct {
typedef struct {
char* name; // function FQN (owned, null-terminated)
InternPoolIndex func_ip; // IP index of function value (for name resolution)
Air air; // per-function Air (owns its arrays)
} SemaFuncAir;

1102
stage0/verbose_air.c Normal file

File diff suppressed because it is too large Load Diff

13
stage0/verbose_air.h Normal file
View File

@@ -0,0 +1,13 @@
// verbose_air.h — Human-readable AIR printer for debugging.
// Ported from src/Air/print.zig.
#ifndef _ZIG0_VERBOSE_AIR_H__
#define _ZIG0_VERBOSE_AIR_H__
#include "intern_pool.h"
#include "sema.h"
#include <stdio.h>
void verboseAirPrint(
FILE* out, const SemaFuncAirList* list, const InternPool* ip);
#endif

View File

@@ -2,6 +2,7 @@
#include "astgen.h"
#include "intern_pool.h"
#include "sema.h"
#include "verbose_air.h"
#include "zir.h"
#include <stdbool.h>
@@ -13,7 +14,7 @@
// - code = 0: program successfully terminated.
// - code = 1: panicked, panic message in msg. Caller should free msg.
// - code = 2: interpreter error, error in msg. Caller should free msg.
static int zig0Run(const char* program, char** msg) {
static int zig0Run(const char* program, bool verbose_air, char** msg) {
uint32_t len = (uint32_t)strlen(program);
Ast ast = astParse(program, len);
if (ast.has_error) {
@@ -43,6 +44,8 @@ static int zig0Run(const char* program, char** msg) {
InternPool ip = ipInit();
Sema sema = semaInit(&ip, zir);
SemaFuncAirList func_airs = semaAnalyze(&sema);
if (verbose_air)
verboseAirPrint(stderr, &func_airs, &ip);
semaDeinit(&sema);
semaFuncAirListDeinit(&func_airs);
ipDeinit(&ip);
@@ -52,7 +55,7 @@ static int zig0Run(const char* program, char** msg) {
// API: run and:
// code = 3: abnormal error, expect something in stderr.
int zig0RunFile(const char* fname, char** msg) {
int zig0RunFile(const char* fname, bool verbose_air, char** msg) {
FILE* f = fopen(fname, "r");
if (f == NULL) {
perror("fopen");
@@ -89,7 +92,7 @@ int zig0RunFile(const char* fname, char** msg) {
fclose(f);
program[fsize] = 0;
int code = zig0Run(program, msg);
int code = zig0Run(program, verbose_air, msg);
free(program);
return code;
}