From 93b49123cc2ed07b1b89d7366d3de0632dcc3fc7 Mon Sep 17 00:00:00 2001 From: Motiejus Date: Sat, 21 Feb 2026 15:20:06 +0000 Subject: [PATCH] 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 --- build.zig | 49 +- src/verbose_air.zig | 2 + stage0/main.c | 29 +- stage0/sema.c | 1 + stage0/sema.h | 1 + stage0/verbose_air.c | 1102 ++++++++++++++++++++++++++++++++++++++++++ stage0/verbose_air.h | 13 + stage0/zig0.c | 9 +- 8 files changed, 1196 insertions(+), 10 deletions(-) create mode 100644 stage0/verbose_air.c create mode 100644 stage0/verbose_air.h diff --git a/build.zig b/build.zig index e19d3a2a90..ed0fb0ffee 100644 --- a/build.zig +++ b/build.zig @@ -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}))); diff --git a/src/verbose_air.zig b/src/verbose_air.zig index 30bc0ab36d..1210837cb8 100644 --- a/src/verbose_air.zig +++ b/src/verbose_air.zig @@ -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, diff --git a/stage0/main.c b/stage0/main.c index 16909955eb..c7f05b3f53 100644 --- a/stage0/main.c +++ b/stage0/main.c @@ -3,22 +3,41 @@ #include #include #include +#include -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; diff --git a/stage0/sema.c b/stage0/sema.c index bb31ab9f94..003b426944 100644 --- a/stage0/sema.c +++ b/stage0/sema.c @@ -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; diff --git a/stage0/sema.h b/stage0/sema.h index f3e277110e..d9d30d210b 100644 --- a/stage0/sema.h +++ b/stage0/sema.h @@ -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; diff --git a/stage0/verbose_air.c b/stage0/verbose_air.c new file mode 100644 index 0000000000..52287f05e9 --- /dev/null +++ b/stage0/verbose_air.c @@ -0,0 +1,1102 @@ +// verbose_air.c — Human-readable AIR printer for debugging. +// Ported from src/Air/print.zig. + +#include "verbose_air.h" +#include "air.h" +#include "intern_pool.h" +#include "sema.h" + +#include +#include +#include +#include + +// --- Tag name table (generated from X-macro) --- + +#define AIR_TAG_STR(e) #e, +static const char* const air_tag_strs[] = { + AIR_INST_FOREACH_TAG(AIR_TAG_STR)}; + +// Print tag name: strip "AIR_INST_" prefix (9 chars) and lowercase. +static void writeTagName(FILE* out, uint8_t tag) { + const char* raw = air_tag_strs[tag] + 9; // skip "AIR_INST_" + for (const char* p = raw; *p; p++) { + fputc(tolower((unsigned char)*p), out); + } +} + +// --- Static IP name table (124 entries, matching InternPool.Index enum) --- + +static const char* const ip_static_names[IP_INDEX_PREINTERN_COUNT] = { + // Types (0-17): integer types + "u0_type", // 0 + "i0_type", // 1 + "u1_type", // 2 + "u8_type", // 3 + "i8_type", // 4 + "u16_type", // 5 + "i16_type", // 6 + "u29_type", // 7 + "u32_type", // 8 + "i32_type", // 9 + "u64_type", // 10 + "i64_type", // 11 + "u80_type", // 12 + "u128_type", // 13 + "i128_type", // 14 + "u256_type", // 15 + "usize_type", // 16 + "isize_type", // 17 + // C types (18-27) + "c_char_type", // 18 + "c_short_type", // 19 + "c_ushort_type", // 20 + "c_int_type", // 21 + "c_uint_type", // 22 + "c_long_type", // 23 + "c_ulong_type", // 24 + "c_longlong_type", // 25 + "c_ulonglong_type", // 26 + "c_longdouble_type", // 27 + // Float types (28-32) + "f16_type", // 28 + "f32_type", // 29 + "f64_type", // 30 + "f80_type", // 31 + "f128_type", // 32 + // Special types (33-44) + "anyopaque_type", // 33 + "bool_type", // 34 + "void_type", // 35 + "type_type", // 36 + "anyerror_type", // 37 + "comptime_int_type", // 38 + "comptime_float_type", // 39 + "noreturn_type", // 40 + "anyframe_type", // 41 + "null_type", // 42 + "undefined_type", // 43 + "enum_literal_type", // 44 + // Pointer types (45-51) + "ptr_usize_type", // 45 + "ptr_const_comptime_int_type", // 46 + "manyptr_u8_type", // 47 + "manyptr_const_u8_type", // 48 + "manyptr_const_u8_sentinel_0_type", // 49 + "slice_const_u8_type", // 50 + "slice_const_u8_sentinel_0_type", // 51 + // Vector types (52-98) + "vector_8_i8_type", // 52 + "vector_16_i8_type", // 53 + "vector_32_i8_type", // 54 + "vector_64_i8_type", // 55 + "vector_1_u8_type", // 56 + "vector_2_u8_type", // 57 + "vector_4_u8_type", // 58 + "vector_8_u8_type", // 59 + "vector_16_u8_type", // 60 + "vector_32_u8_type", // 61 + "vector_64_u8_type", // 62 + "vector_2_i16_type", // 63 + "vector_4_i16_type", // 64 + "vector_8_i16_type", // 65 + "vector_16_i16_type", // 66 + "vector_32_i16_type", // 67 + "vector_4_u16_type", // 68 + "vector_8_u16_type", // 69 + "vector_16_u16_type", // 70 + "vector_32_u16_type", // 71 + "vector_2_i32_type", // 72 + "vector_4_i32_type", // 73 + "vector_8_i32_type", // 74 + "vector_16_i32_type", // 75 + "vector_4_u32_type", // 76 + "vector_8_u32_type", // 77 + "vector_16_u32_type", // 78 + "vector_2_i64_type", // 79 + "vector_4_i64_type", // 80 + "vector_8_i64_type", // 81 + "vector_2_u64_type", // 82 + "vector_4_u64_type", // 83 + "vector_8_u64_type", // 84 + "vector_1_u128_type", // 85 + "vector_2_u128_type", // 86 + "vector_1_u256_type", // 87 + "vector_4_f16_type", // 88 + "vector_8_f16_type", // 89 + "vector_16_f16_type", // 90 + "vector_32_f16_type", // 91 + "vector_2_f32_type", // 92 + "vector_4_f32_type", // 93 + "vector_8_f32_type", // 94 + "vector_16_f32_type", // 95 + "vector_2_f64_type", // 96 + "vector_4_f64_type", // 97 + "vector_8_f64_type", // 98 + // More types (99-103) + "optional_noreturn_type", // 99 + "anyerror_void_error_union_type", // 100 + "adhoc_inferred_error_set_type", // 101 + "generic_poison_type", // 102 + "empty_tuple_type", // 103 + // Values (104-123) + "undef", // 104 + "undef_bool", // 105 + "undef_usize", // 106 + "undef_u1", // 107 + "zero", // 108 + "zero_usize", // 109 + "zero_u1", // 110 + "zero_u8", // 111 + "one", // 112 + "one_usize", // 113 + "one_u1", // 114 + "one_u8", // 115 + "four_u8", // 116 + "negative_one", // 117 + "void_value", // 118 + "unreachable_value", // 119 + "null_value", // 120 + "bool_true", // 121 + "bool_false", // 122 + "empty_tuple", // 123 +}; + +// --- SimpleType name table --- + +static const char* const simple_type_names[] = { + "f16", // 0 + "f32", // 1 + "f64", // 2 + "f80", // 3 + "f128", // 4 + "usize", // 5 + "isize", // 6 + "c_char", // 7 + "c_short", // 8 + "c_ushort", // 9 + "c_int", // 10 + "c_uint", // 11 + "c_long", // 12 + "c_ulong", // 13 + "c_longlong", // 14 + "c_ulonglong", // 15 + "c_longdouble", // 16 + "anyopaque", // 17 + "bool", // 18 + "void", // 19 + "type", // 20 + "anyerror", // 21 + "comptime_int", // 22 + "comptime_float", // 23 + "noreturn", // 24 + "null", // 25 + "undefined", // 26 + "enum_literal", // 27 + "adhoc_inferred_error_set", // 28 + "generic_poison", // 29 +}; + +#define SIMPLE_TYPE_COUNT 30 + +// --- Forward declarations --- + +static void writeValue( + FILE* out, const InternPool* ip, InternPoolIndex val_ip); +static void writeInstRef( + FILE* out, const Air* air, const InternPool* ip, AirInstRef ref); +static void writeBody(FILE* out, const Air* air, const InternPool* ip, + const uint32_t* body, uint32_t body_len, uint32_t indent); + +// --- Type printing --- +// Dispatch on InternPoolKey.tag to print a human-readable type. + +static void writeType(FILE* out, const InternPool* ip, InternPoolIndex ty_ip) { + if (ty_ip == IP_INDEX_NONE) { + fprintf(out, "(unknown)"); + return; + } + InternPoolKey key = ipIndexToKey(ip, ty_ip); + switch (key.tag) { + case IP_KEY_INT_TYPE: + fprintf(out, "%c%" PRIu16, + key.data.int_type.signedness ? 'i' : 'u', + key.data.int_type.bits); + break; + case IP_KEY_SIMPLE_TYPE: + if ((uint32_t)key.data.simple_type < SIMPLE_TYPE_COUNT) + fprintf(out, "%s", simple_type_names[key.data.simple_type]); + else + fprintf(out, "(simple_type@%u)", (unsigned)key.data.simple_type); + break; + case IP_KEY_PTR_TYPE: { + uint32_t size = key.data.ptr_type.flags & PTR_FLAGS_SIZE_MASK; + bool is_const = (key.data.ptr_type.flags & PTR_FLAGS_IS_CONST) != 0; + InternPoolIndex sentinel = key.data.ptr_type.sentinel; + switch (size) { + case PTR_FLAGS_SIZE_ONE: + fprintf(out, "*"); + break; + case PTR_FLAGS_SIZE_MANY: + if (sentinel != IP_INDEX_NONE) { + fprintf(out, "[*:"); + writeValue(out, ip, sentinel); + fprintf(out, "]"); + } else { + fprintf(out, "[*]"); + } + break; + case PTR_FLAGS_SIZE_SLICE: + if (sentinel != IP_INDEX_NONE) { + fprintf(out, "[:"); + writeValue(out, ip, sentinel); + fprintf(out, "]"); + } else { + fprintf(out, "[]"); + } + break; + case PTR_FLAGS_SIZE_C: + fprintf(out, "[*c]"); + break; + } + if (is_const) + fprintf(out, "const "); + writeType(out, ip, key.data.ptr_type.child); + break; + } + case IP_KEY_FUNC_TYPE: + fprintf(out, "fn ("); + for (uint32_t i = 0; i < key.data.func_type.param_count; i++) { + if (i > 0) + fprintf(out, ", "); + fprintf(out, "..."); + } + fprintf(out, ") "); + writeType(out, ip, key.data.func_type.return_type); + break; + case IP_KEY_OPT_TYPE: + fprintf(out, "?"); + writeType(out, ip, key.data.opt_type); + break; + case IP_KEY_ANYFRAME_TYPE: + if (key.data.anyframe_type != IP_INDEX_NONE) { + fprintf(out, "anyframe->"); + writeType(out, ip, key.data.anyframe_type); + } else { + fprintf(out, "anyframe"); + } + break; + case IP_KEY_ARRAY_TYPE: + fprintf(out, "[%" PRIu64 "]", key.data.array_type.len); + writeType(out, ip, key.data.array_type.child); + break; + case IP_KEY_VECTOR_TYPE: + fprintf(out, "@Vector(%" PRIu32 ", ", key.data.vector_type.len); + writeType(out, ip, key.data.vector_type.child); + fprintf(out, ")"); + break; + case IP_KEY_ERROR_UNION_TYPE: + writeType(out, ip, key.data.error_union_type.error_set); + fprintf(out, "!"); + writeType(out, ip, key.data.error_union_type.payload); + break; + case IP_KEY_TUPLE_TYPE: + fprintf(out, "struct{}"); + break; + default: + fprintf(out, "(type@%u)", ty_ip); + break; + } +} + +// --- Value printing --- +// Dispatch on InternPoolKey.tag to print a human-readable value. + +static void writeValue( + FILE* out, const InternPool* ip, InternPoolIndex val_ip) { + InternPoolKey key = ipIndexToKey(ip, val_ip); + switch (key.tag) { + case IP_KEY_INT: + if (key.data.int_val.is_negative) + fprintf(out, "-"); + fprintf(out, "%" PRIu64, key.data.int_val.value); + break; + case IP_KEY_SIMPLE_VALUE: + switch (key.data.simple_value) { + case SIMPLE_VALUE_VOID: + fprintf(out, "{}"); + break; + case SIMPLE_VALUE_NULL: + fprintf(out, "null"); + break; + case SIMPLE_VALUE_UNDEFINED: + fprintf(out, "undefined"); + break; + case SIMPLE_VALUE_TRUE: + fprintf(out, "true"); + break; + case SIMPLE_VALUE_FALSE: + fprintf(out, "false"); + break; + case SIMPLE_VALUE_UNREACHABLE: + fprintf(out, "unreachable"); + break; + case SIMPLE_VALUE_EMPTY_TUPLE: + fprintf(out, ".{}"); + break; + } + break; + case IP_KEY_UNDEF: + fprintf(out, "undefined"); + break; + case IP_KEY_FUNC: + fprintf(out, "(function)"); + break; + case IP_KEY_FLOAT: + fprintf(out, "%g", key.data.float_val); + break; + default: + fprintf(out, "(val@%u)", val_ip); + break; + } +} + +// --- InstRef printing --- +// Ported from print.zig writeInstRef / writeOperand. + +static void writeInstRef( + FILE* out, const Air* air, const InternPool* ip, AirInstRef ref) { + (void)air; + if (ref == AIR_REF_NONE) { + fprintf(out, "none"); + } else if (AIR_REF_IS_INST(ref)) { + fprintf(out, "%%%" PRIu32, AIR_REF_TO_INST(ref)); + } else { + // IP ref + uint32_t idx = AIR_REF_TO_IP(ref); + if (idx < IP_INDEX_PREINTERN_COUNT) { + fprintf(out, "@.%s", ip_static_names[idx]); + } else { + // Print + InternPoolIndex ty = ipTypeOf(ip, idx); + fprintf(out, "<"); + writeType(out, ip, ty); + fprintf(out, ", "); + writeValue(out, ip, idx); + fprintf(out, ">"); + } + } +} + +// --- Instruction index printing (for br/repeat targets) --- + +static void writeInstIndex(FILE* out, uint32_t inst) { + fprintf(out, "%%%" PRIu32, inst); +} + +// --- Indent helper --- + +static void writeIndent(FILE* out, uint32_t indent) { + for (uint32_t i = 0; i < indent; i++) + fputc(' ', out); +} + +// --- Instruction operand writers --- +// Each follows the upstream print.zig pattern. + +static void writeBinOp( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + AirInstRef lhs = air->inst_datas[inst].bin_op.lhs; + AirInstRef rhs = air->inst_datas[inst].bin_op.rhs; + writeInstRef(out, air, ip, lhs); + fprintf(out, ", "); + writeInstRef(out, air, ip, rhs); +} + +static void writeUnOp( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + AirInstRef operand = air->inst_datas[inst].un_op.operand; + writeInstRef(out, air, ip, operand); +} + +static void writeTy( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + (void)air; + AirInstRef ty_ref = air->inst_datas[inst].ty.ty_ref; + writeType(out, ip, AIR_REF_TO_IP(ty_ref)); +} + +static void writeArg( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + AirInstRef ty_ref = air->inst_datas[inst].arg.ty_ref; + uint32_t zir_param_index = air->inst_datas[inst].arg.zir_param_index; + writeType(out, ip, AIR_REF_TO_IP(ty_ref)); + fprintf(out, ", %" PRIu32, zir_param_index); +} + +static void writeTyOp( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + AirInstRef ty_ref = air->inst_datas[inst].ty_op.ty_ref; + AirInstRef operand = air->inst_datas[inst].ty_op.operand; + writeType(out, ip, AIR_REF_TO_IP(ty_ref)); + fprintf(out, ", "); + writeInstRef(out, air, ip, operand); +} + +static void writeBlock(FILE* out, const Air* air, const InternPool* ip, + uint8_t tag, uint32_t inst, uint32_t indent) { + AirInstRef ty_ref = air->inst_datas[inst].ty_pl.ty_ref; + uint32_t payload = air->inst_datas[inst].ty_pl.payload; + writeType(out, ip, AIR_REF_TO_IP(ty_ref)); + + uint32_t body_offset; + if (tag == AIR_INST_DBG_INLINE_BLOCK) { + // AirDbgInlineBlock: {func, body_len, ...body} + InternPoolIndex func_ip_idx = air->extra[payload]; + fprintf(out, ", "); + writeInstRef(out, air, ip, AIR_REF_FROM_IP(func_ip_idx)); + uint32_t body_len = air->extra[payload + 1]; + body_offset = payload + 2; + fprintf(out, ", {\n"); + writeBody(out, air, ip, &air->extra[body_offset], body_len, + indent + 2); + writeIndent(out, indent); + fprintf(out, "}"); + } else { + // AirBlock: {body_len, ...body} + uint32_t body_len = air->extra[payload]; + body_offset = payload + 1; + fprintf(out, ", {\n"); + writeBody(out, air, ip, &air->extra[body_offset], body_len, + indent + 2); + writeIndent(out, indent); + fprintf(out, "}"); + } +} + +static void writeLoop(FILE* out, const Air* air, const InternPool* ip, + uint32_t inst, uint32_t indent) { + AirInstRef ty_ref = air->inst_datas[inst].ty_pl.ty_ref; + uint32_t payload = air->inst_datas[inst].ty_pl.payload; + writeType(out, ip, AIR_REF_TO_IP(ty_ref)); + // AirBlock: {body_len, ...body} + uint32_t body_len = air->extra[payload]; + uint32_t body_offset = payload + 1; + fprintf(out, ", {\n"); + writeBody(out, air, ip, &air->extra[body_offset], body_len, indent + 2); + writeIndent(out, indent); + fprintf(out, "}"); +} + +static void writeTyPlBin( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + AirInstRef ty_ref = air->inst_datas[inst].ty_pl.ty_ref; + uint32_t payload = air->inst_datas[inst].ty_pl.payload; + // AirBin: {lhs, rhs} + AirInstRef lhs = air->extra[payload]; + AirInstRef rhs = air->extra[payload + 1]; + writeType(out, ip, AIR_REF_TO_IP(ty_ref)); + fprintf(out, ", "); + writeInstRef(out, air, ip, lhs); + fprintf(out, ", "); + writeInstRef(out, air, ip, rhs); +} + +static void writeCall( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + AirInstRef operand = air->inst_datas[inst].pl_op.operand; + uint32_t payload = air->inst_datas[inst].pl_op.payload; + // AirCall: {args_len, ...args} + uint32_t args_len = air->extra[payload]; + writeInstRef(out, air, ip, operand); + fprintf(out, ", ["); + for (uint32_t i = 0; i < args_len; i++) { + if (i > 0) + fprintf(out, ", "); + AirInstRef arg = air->extra[payload + 1 + i]; + writeInstRef(out, air, ip, arg); + } + fprintf(out, "]"); +} + +static void writeBr( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + uint32_t block_inst = air->inst_datas[inst].br.block_inst; + AirInstRef operand = air->inst_datas[inst].br.operand; + writeInstIndex(out, block_inst); + fprintf(out, ", "); + writeInstRef(out, air, ip, operand); +} + +static void writeRepeat(FILE* out, const Air* air, uint32_t inst) { + uint32_t loop_inst = air->inst_datas[inst].repeat_data.loop_inst; + writeInstIndex(out, loop_inst); +} + +static void writeCondBr(FILE* out, const Air* air, const InternPool* ip, + uint32_t inst, uint32_t indent) { + AirInstRef operand = air->inst_datas[inst].pl_op.operand; + uint32_t payload = air->inst_datas[inst].pl_op.payload; + // AirCondBr: {then_body_len, else_body_len, ...then_body, ...else_body} + uint32_t then_body_len = air->extra[payload]; + uint32_t else_body_len = air->extra[payload + 1]; + const uint32_t* then_body = &air->extra[payload + 2]; + const uint32_t* else_body = &air->extra[payload + 2 + then_body_len]; + + writeInstRef(out, air, ip, operand); + fprintf(out, ", {\n"); + writeBody(out, air, ip, then_body, then_body_len, indent + 2); + writeIndent(out, indent); + fprintf(out, "}, {\n"); + writeBody(out, air, ip, else_body, else_body_len, indent + 2); + writeIndent(out, indent); + fprintf(out, "}"); +} + +static void writeTry(FILE* out, const Air* air, const InternPool* ip, + uint32_t inst, uint32_t indent) { + AirInstRef operand = air->inst_datas[inst].pl_op.operand; + uint32_t payload = air->inst_datas[inst].pl_op.payload; + // AirTry: {body_len, ...body} + uint32_t body_len = air->extra[payload]; + const uint32_t* body = &air->extra[payload + 1]; + + writeInstRef(out, air, ip, operand); + fprintf(out, ", {\n"); + writeBody(out, air, ip, body, body_len, indent + 2); + writeIndent(out, indent); + fprintf(out, "}"); +} + +static void writeTryPtr(FILE* out, const Air* air, const InternPool* ip, + uint32_t inst, uint32_t indent) { + AirInstRef ty_ref = air->inst_datas[inst].ty_pl.ty_ref; + uint32_t payload = air->inst_datas[inst].ty_pl.payload; + // AirTryPtr: {body_len, ...body} (plus ptr from extra) + // Extra layout: ptr(u32), body_len, ...body + AirInstRef ptr = air->extra[payload]; + uint32_t body_len = air->extra[payload + 1]; + const uint32_t* body = &air->extra[payload + 2]; + + writeInstRef(out, air, ip, ptr); + fprintf(out, ", "); + writeType(out, ip, AIR_REF_TO_IP(ty_ref)); + fprintf(out, ", {\n"); + writeBody(out, air, ip, body, body_len, indent + 2); + writeIndent(out, indent); + fprintf(out, "}"); +} + +static void writeDbgStmt(FILE* out, const Air* air, uint32_t inst) { + uint32_t line = air->inst_datas[inst].dbg_stmt.line; + uint32_t column = air->inst_datas[inst].dbg_stmt.column; + // Print 1-indexed (matching upstream: line + 1, column + 1). + fprintf(out, "%" PRIu32 ":%" PRIu32, line + 1, column + 1); +} + +static void writeDbgVar( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + AirInstRef operand = air->inst_datas[inst].pl_op.operand; + uint32_t payload = air->inst_datas[inst].pl_op.payload; + writeInstRef(out, air, ip, operand); + // payload is index into air->extra where the NUL-terminated name is stored. + const char* name = (const char*)&air->extra[payload]; + fprintf(out, ", \"%s\"", name); +} + +static void writeStructField( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + AirInstRef ty_ref = air->inst_datas[inst].ty_pl.ty_ref; + uint32_t payload = air->inst_datas[inst].ty_pl.payload; + // AirStructField: {field_index} — but struct_field_ptr uses ty_pl + // with extra containing operand + field_index. + // Actually: upstream StructField has struct_operand + field_index. + // In C implementation, ty_pl.payload points to extra with {field_index}. + // The struct operand is in extra[payload] and field_index in + // extra[payload+1] (matching AirStructField layout). + (void)ty_ref; + AirInstRef struct_operand = air->extra[payload]; + uint32_t field_index = air->extra[payload + 1]; + writeInstRef(out, air, ip, struct_operand); + fprintf(out, ", %" PRIu32, field_index); +} + +static void writeAggregateInit( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + AirInstRef ty_ref = air->inst_datas[inst].ty_pl.ty_ref; + uint32_t payload = air->inst_datas[inst].ty_pl.payload; + writeType(out, ip, AIR_REF_TO_IP(ty_ref)); + // Elements are stored directly starting at payload. + // We don't know the count without the type, so print a TODO. + fprintf(out, ", [TODO:aggregate_init@%" PRIu32 "]", payload); +} + +static void writeUnionInit( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + AirInstRef ty_ref = air->inst_datas[inst].ty_pl.ty_ref; + uint32_t payload = air->inst_datas[inst].ty_pl.payload; + (void)ty_ref; + uint32_t field_index = air->extra[payload]; + AirInstRef init_ref = air->extra[payload + 1]; + fprintf(out, "%" PRIu32 ", ", field_index); + writeInstRef(out, air, ip, init_ref); +} + +static void writeFieldParentPtr( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + AirInstRef ty_ref = air->inst_datas[inst].ty_pl.ty_ref; + uint32_t payload = air->inst_datas[inst].ty_pl.payload; + (void)ty_ref; + // AirFieldParentPtr: {field_owner, field_index} + AirInstRef field_ptr = air->extra[payload]; + uint32_t field_index = air->extra[payload + 1]; + writeInstRef(out, air, ip, field_ptr); + fprintf(out, ", %" PRIu32, field_index); +} + +static void writeWasmMemorySize(FILE* out, const Air* air, uint32_t inst) { + uint32_t payload = air->inst_datas[inst].pl_op.payload; + fprintf(out, "%" PRIu32, payload); +} + +static void writeWasmMemoryGrow( + FILE* out, const Air* air, const InternPool* ip, uint32_t inst) { + AirInstRef operand = air->inst_datas[inst].pl_op.operand; + uint32_t payload = air->inst_datas[inst].pl_op.payload; + fprintf(out, "%" PRIu32 ", ", payload); + writeInstRef(out, air, ip, operand); +} + +static void writeWorkDimension(FILE* out, const Air* air, uint32_t inst) { + uint32_t payload = air->inst_datas[inst].pl_op.payload; + fprintf(out, "%" PRIu32, payload); +} + +// --- Main instruction dispatch --- +// Ported from print.zig writeInst (lines 104-341). + +static void writeInst(FILE* out, const Air* air, const InternPool* ip, + uint32_t inst, uint32_t indent) { + uint8_t tag = air->inst_tags[inst]; + + writeIndent(out, indent); + fprintf(out, "%%%" PRIu32 " = ", inst); + writeTagName(out, tag); + fprintf(out, "("); + + switch (tag) { + // --- bin_op group --- + case AIR_INST_ADD: + case AIR_INST_ADD_SAFE: + case AIR_INST_ADD_OPTIMIZED: + case AIR_INST_ADD_WRAP: + case AIR_INST_ADD_SAT: + case AIR_INST_SUB: + case AIR_INST_SUB_SAFE: + case AIR_INST_SUB_OPTIMIZED: + case AIR_INST_SUB_WRAP: + case AIR_INST_SUB_SAT: + case AIR_INST_MUL: + case AIR_INST_MUL_SAFE: + case AIR_INST_MUL_OPTIMIZED: + case AIR_INST_MUL_WRAP: + case AIR_INST_MUL_SAT: + case AIR_INST_DIV_FLOAT: + case AIR_INST_DIV_FLOAT_OPTIMIZED: + case AIR_INST_DIV_TRUNC: + case AIR_INST_DIV_TRUNC_OPTIMIZED: + case AIR_INST_DIV_FLOOR: + case AIR_INST_DIV_FLOOR_OPTIMIZED: + case AIR_INST_DIV_EXACT: + case AIR_INST_DIV_EXACT_OPTIMIZED: + case AIR_INST_REM: + case AIR_INST_REM_OPTIMIZED: + case AIR_INST_MOD: + case AIR_INST_MOD_OPTIMIZED: + case AIR_INST_BIT_AND: + case AIR_INST_BIT_OR: + case AIR_INST_XOR: + case AIR_INST_CMP_LT: + case AIR_INST_CMP_LT_OPTIMIZED: + case AIR_INST_CMP_LTE: + case AIR_INST_CMP_LTE_OPTIMIZED: + case AIR_INST_CMP_EQ: + case AIR_INST_CMP_EQ_OPTIMIZED: + case AIR_INST_CMP_GTE: + case AIR_INST_CMP_GTE_OPTIMIZED: + case AIR_INST_CMP_GT: + case AIR_INST_CMP_GT_OPTIMIZED: + case AIR_INST_CMP_NEQ: + case AIR_INST_CMP_NEQ_OPTIMIZED: + case AIR_INST_BOOL_AND: + case AIR_INST_BOOL_OR: + case AIR_INST_STORE: + case AIR_INST_STORE_SAFE: + case AIR_INST_ARRAY_ELEM_VAL: + case AIR_INST_SLICE_ELEM_VAL: + case AIR_INST_PTR_ELEM_VAL: + case AIR_INST_SHL: + case AIR_INST_SHL_EXACT: + case AIR_INST_SHL_SAT: + case AIR_INST_SHR: + case AIR_INST_SHR_EXACT: + case AIR_INST_SET_UNION_TAG: + case AIR_INST_MIN: + case AIR_INST_MAX: + case AIR_INST_MEMCPY: + case AIR_INST_MEMMOVE: + case AIR_INST_MEMSET: + case AIR_INST_MEMSET_SAFE: + writeBinOp(out, air, ip, inst); + break; + + // --- un_op group --- + case AIR_INST_IS_NULL: + case AIR_INST_IS_NON_NULL: + case AIR_INST_IS_NULL_PTR: + case AIR_INST_IS_NON_NULL_PTR: + case AIR_INST_IS_ERR: + case AIR_INST_IS_NON_ERR: + case AIR_INST_IS_ERR_PTR: + case AIR_INST_IS_NON_ERR_PTR: + case AIR_INST_RET: + case AIR_INST_RET_SAFE: + case AIR_INST_RET_LOAD: + case AIR_INST_IS_NAMED_ENUM_VALUE: + case AIR_INST_TAG_NAME: + case AIR_INST_ERROR_NAME: + case AIR_INST_SQRT: + case AIR_INST_SIN: + case AIR_INST_COS: + case AIR_INST_TAN: + case AIR_INST_EXP: + case AIR_INST_EXP2: + case AIR_INST_LOG: + case AIR_INST_LOG2: + case AIR_INST_LOG10: + case AIR_INST_FLOOR: + case AIR_INST_CEIL: + case AIR_INST_ROUND: + case AIR_INST_TRUNC_FLOAT: + case AIR_INST_NEG: + case AIR_INST_NEG_OPTIMIZED: + case AIR_INST_CMP_LT_ERRORS_LEN: + case AIR_INST_SET_ERR_RETURN_TRACE: + case AIR_INST_C_VA_END: + writeUnOp(out, air, ip, inst); + break; + + // --- no_op group --- + case AIR_INST_TRAP: + case AIR_INST_BREAKPOINT: + case AIR_INST_DBG_EMPTY_STMT: + case AIR_INST_UNREACH: + case AIR_INST_RET_ADDR: + case AIR_INST_FRAME_ADDR: + case AIR_INST_SAVE_ERR_RETURN_TRACE_INDEX: + // no operands + break; + + // --- ty group --- + case AIR_INST_ALLOC: + case AIR_INST_RET_PTR: + case AIR_INST_ERR_RETURN_TRACE: + case AIR_INST_C_VA_START: + writeTy(out, air, ip, inst); + break; + + // --- arg --- + case AIR_INST_ARG: + writeArg(out, air, ip, inst); + break; + + // --- ty_op group --- + case AIR_INST_NOT: + case AIR_INST_BITCAST: + case AIR_INST_LOAD: + case AIR_INST_FPTRUNC: + case AIR_INST_FPEXT: + case AIR_INST_INTCAST: + case AIR_INST_INTCAST_SAFE: + case AIR_INST_TRUNC: + case AIR_INST_OPTIONAL_PAYLOAD: + case AIR_INST_OPTIONAL_PAYLOAD_PTR: + case AIR_INST_OPTIONAL_PAYLOAD_PTR_SET: + case AIR_INST_ERRUNION_PAYLOAD_PTR_SET: + case AIR_INST_WRAP_OPTIONAL: + case AIR_INST_UNWRAP_ERRUNION_PAYLOAD: + case AIR_INST_UNWRAP_ERRUNION_ERR: + case AIR_INST_UNWRAP_ERRUNION_PAYLOAD_PTR: + case AIR_INST_UNWRAP_ERRUNION_ERR_PTR: + case AIR_INST_WRAP_ERRUNION_PAYLOAD: + case AIR_INST_WRAP_ERRUNION_ERR: + case AIR_INST_SLICE_PTR: + case AIR_INST_SLICE_LEN: + case AIR_INST_PTR_SLICE_LEN_PTR: + case AIR_INST_PTR_SLICE_PTR_PTR: + case AIR_INST_STRUCT_FIELD_PTR_INDEX_0: + case AIR_INST_STRUCT_FIELD_PTR_INDEX_1: + case AIR_INST_STRUCT_FIELD_PTR_INDEX_2: + case AIR_INST_STRUCT_FIELD_PTR_INDEX_3: + case AIR_INST_ARRAY_TO_SLICE: + case AIR_INST_FLOAT_FROM_INT: + case AIR_INST_SPLAT: + case AIR_INST_INT_FROM_FLOAT: + case AIR_INST_INT_FROM_FLOAT_OPTIMIZED: + case AIR_INST_INT_FROM_FLOAT_SAFE: + case AIR_INST_INT_FROM_FLOAT_OPTIMIZED_SAFE: + case AIR_INST_GET_UNION_TAG: + case AIR_INST_CLZ: + case AIR_INST_CTZ: + case AIR_INST_POPCOUNT: + case AIR_INST_BYTE_SWAP: + case AIR_INST_BIT_REVERSE: + case AIR_INST_ABS: + case AIR_INST_ERROR_SET_HAS_VALUE: + case AIR_INST_ADDRSPACE_CAST: + case AIR_INST_C_VA_ARG: + case AIR_INST_C_VA_COPY: + writeTyOp(out, air, ip, inst); + break; + + // --- block / dbg_inline_block --- + case AIR_INST_BLOCK: + case AIR_INST_DBG_INLINE_BLOCK: + writeBlock(out, air, ip, tag, inst, indent); + break; + + // --- loop --- + case AIR_INST_LOOP: + writeLoop(out, air, ip, inst, indent); + break; + + // --- ty_pl_bin group --- + case AIR_INST_SLICE: + case AIR_INST_SLICE_ELEM_PTR: + case AIR_INST_PTR_ELEM_PTR: + case AIR_INST_PTR_ADD: + case AIR_INST_PTR_SUB: + case AIR_INST_ADD_WITH_OVERFLOW: + case AIR_INST_SUB_WITH_OVERFLOW: + case AIR_INST_MUL_WITH_OVERFLOW: + case AIR_INST_SHL_WITH_OVERFLOW: + writeTyPlBin(out, air, ip, inst); + break; + + // --- call group --- + case AIR_INST_CALL: + case AIR_INST_CALL_ALWAYS_TAIL: + case AIR_INST_CALL_NEVER_TAIL: + case AIR_INST_CALL_NEVER_INLINE: + writeCall(out, air, ip, inst); + break; + + // --- dbg_var group --- + case AIR_INST_DBG_VAR_PTR: + case AIR_INST_DBG_VAR_VAL: + case AIR_INST_DBG_ARG_INLINE: + writeDbgVar(out, air, ip, inst); + break; + + // --- struct_field_ptr / struct_field_val --- + case AIR_INST_STRUCT_FIELD_PTR: + case AIR_INST_STRUCT_FIELD_VAL: + writeStructField(out, air, ip, inst); + break; + + // --- inferred_alloc --- + case AIR_INST_INFERRED_ALLOC: + case AIR_INST_INFERRED_ALLOC_COMPTIME: + fprintf(out, "TODO"); + break; + + // --- assembly --- + case AIR_INST_ASSEMBLY: + fprintf(out, "TODO"); + break; + + // --- dbg_stmt --- + case AIR_INST_DBG_STMT: + writeDbgStmt(out, air, inst); + break; + + // --- aggregate_init --- + case AIR_INST_AGGREGATE_INIT: + writeAggregateInit(out, air, ip, inst); + break; + + // --- union_init --- + case AIR_INST_UNION_INIT: + writeUnionInit(out, air, ip, inst); + break; + + // --- br / switch_dispatch --- + case AIR_INST_BR: + case AIR_INST_SWITCH_DISPATCH: + writeBr(out, air, ip, inst); + break; + + // --- repeat --- + case AIR_INST_REPEAT: + writeRepeat(out, air, inst); + break; + + // --- cond_br --- + case AIR_INST_COND_BR: + writeCondBr(out, air, ip, inst, indent); + break; + + // --- try / try_cold --- + case AIR_INST_TRY: + case AIR_INST_TRY_COLD: + writeTry(out, air, ip, inst, indent); + break; + + // --- try_ptr / try_ptr_cold --- + case AIR_INST_TRY_PTR: + case AIR_INST_TRY_PTR_COLD: + writeTryPtr(out, air, ip, inst, indent); + break; + + // --- switch_br / loop_switch_br --- + case AIR_INST_SWITCH_BR: + case AIR_INST_LOOP_SWITCH_BR: + fprintf(out, "TODO"); + break; + + // --- cmpxchg --- + case AIR_INST_CMPXCHG_WEAK: + case AIR_INST_CMPXCHG_STRONG: + fprintf(out, "TODO"); + break; + + // --- atomic_load --- + case AIR_INST_ATOMIC_LOAD: + fprintf(out, "TODO"); + break; + + // --- prefetch --- + case AIR_INST_PREFETCH: + fprintf(out, "TODO"); + break; + + // --- atomic_store --- + case AIR_INST_ATOMIC_STORE_UNORDERED: + case AIR_INST_ATOMIC_STORE_MONOTONIC: + case AIR_INST_ATOMIC_STORE_RELEASE: + case AIR_INST_ATOMIC_STORE_SEQ_CST: + writeBinOp(out, air, ip, inst); + break; + + // --- atomic_rmw --- + case AIR_INST_ATOMIC_RMW: + fprintf(out, "TODO"); + break; + + // --- field_parent_ptr --- + case AIR_INST_FIELD_PARENT_PTR: + writeFieldParentPtr(out, air, ip, inst); + break; + + // --- wasm --- + case AIR_INST_WASM_MEMORY_SIZE: + writeWasmMemorySize(out, air, inst); + break; + case AIR_INST_WASM_MEMORY_GROW: + writeWasmMemoryGrow(out, air, ip, inst); + break; + + // --- mul_add --- + case AIR_INST_MUL_ADD: + fprintf(out, "TODO"); + break; + + // --- select --- + case AIR_INST_SELECT: + fprintf(out, "TODO"); + break; + + // --- shuffle --- + case AIR_INST_SHUFFLE_ONE: + case AIR_INST_SHUFFLE_TWO: + fprintf(out, "TODO"); + break; + + // --- reduce --- + case AIR_INST_REDUCE: + case AIR_INST_REDUCE_OPTIMIZED: + fprintf(out, "TODO"); + break; + + // --- cmp_vector --- + case AIR_INST_CMP_VECTOR: + case AIR_INST_CMP_VECTOR_OPTIMIZED: + fprintf(out, "TODO"); + break; + + // --- vector_store_elem --- + case AIR_INST_VECTOR_STORE_ELEM: + fprintf(out, "TODO"); + break; + + // --- runtime_nav_ptr --- + case AIR_INST_RUNTIME_NAV_PTR: + fprintf(out, "TODO"); + break; + + // --- work dimensions --- + case AIR_INST_WORK_ITEM_ID: + case AIR_INST_WORK_GROUP_SIZE: + case AIR_INST_WORK_GROUP_ID: + writeWorkDimension(out, air, inst); + break; + } + + fprintf(out, ")\n"); +} + +// --- Body writer --- +// Iterates over instruction indices and prints each. + +static void writeBody(FILE* out, const Air* air, const InternPool* ip, + const uint32_t* body, uint32_t body_len, uint32_t indent) { + for (uint32_t i = 0; i < body_len; i++) { + writeInst(out, air, ip, body[i], indent); + } +} + +// --- Main entry point --- + +void verboseAirPrint( + FILE* out, const SemaFuncAirList* list, const InternPool* ip) { + for (uint32_t f = 0; f < list->len; f++) { + const SemaFuncAir* entry = &list->items[f]; + const Air* air = &entry->air; + const char* name = entry->name ? entry->name : "(unnamed)"; + + // Compute byte sizes matching upstream format. + uint32_t inst_bytes + = air->inst_len * (uint32_t)(sizeof(uint8_t) + sizeof(AirInstData)); + uint32_t extra_bytes = air->extra_len * (uint32_t)sizeof(uint32_t); + uint32_t total_bytes = (uint32_t)sizeof(Air) + inst_bytes + extra_bytes; + + fprintf(out, "# Begin Function AIR: %s:\n", name); + fprintf(out, "# Total AIR bytes: %uB\n", total_bytes); + fprintf(out, "# AIR Instructions: %u (%uB)\n", + air->inst_len, inst_bytes); + fprintf(out, "# AIR Extra Data: %u (%uB)\n", + air->extra_len, extra_bytes); + + // Extract main body from extra[0]. + if (air->extra_len > 0) { + uint32_t main_block_offset = air->extra[0]; + if (main_block_offset < air->extra_len) { + uint32_t body_len = air->extra[main_block_offset]; + const uint32_t* body = &air->extra[main_block_offset + 1]; + writeBody(out, air, ip, body, body_len, 2); + } + } + + fprintf(out, "# End Function AIR: %s\n\n", name); + } +} diff --git a/stage0/verbose_air.h b/stage0/verbose_air.h new file mode 100644 index 0000000000..44003fad2b --- /dev/null +++ b/stage0/verbose_air.h @@ -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 + +void verboseAirPrint( + FILE* out, const SemaFuncAirList* list, const InternPool* ip); + +#endif diff --git a/stage0/zig0.c b/stage0/zig0.c index 98c528af81..6739dfeaa9 100644 --- a/stage0/zig0.c +++ b/stage0/zig0.c @@ -2,6 +2,7 @@ #include "astgen.h" #include "intern_pool.h" #include "sema.h" +#include "verbose_air.h" #include "zir.h" #include @@ -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; }