diff --git a/astgen.c b/astgen.c new file mode 100644 index 0000000000..15b092b72a --- /dev/null +++ b/astgen.c @@ -0,0 +1,69 @@ +#include "astgen.h" +#include "common.h" +#include +#include + +// Blake3("auto") truncated to 128 bits, as 4 x uint32_t (LE). +// This is std.zig.hashSrc("auto") used for the fields_hash of +// an empty auto-layout struct. +static const uint32_t HASH_AUTO[4] + = { 0x8e48032fu, 0x49f070dfu, 0x17991ae1u, 0xa6c4651au }; + +// StructDecl.Small packed struct layout (all zero for empty auto): +// bits 0-5: booleans (has_captures/fields/decls_len, has_backing_int, +// known_non_opv, known_comptime_only) +// bits 6-7: name_strategy (parent=0) +// bits 8-9: layout (auto=0) +// bits 10-12: booleans (any_default_inits/comptime_fields/aligned_fields) +// bits 13-15: padding + +Zir astGen(const Ast* ast) { + Zir zir; + memset(&zir, 0, sizeof(zir)); + + // Allocate instruction arrays (1 instruction: root struct_decl). + zir.inst_cap = 1; + zir.inst_tags = ARR_INIT(ZirInstTag, 1); + zir.inst_datas = ARR_INIT(ZirInstData, 1); + + // Allocate extra: 2 reserved + 6 StructDecl payload = 8. + zir.extra_cap = 8; + zir.extra = ARR_INIT(uint32_t, 8); + + // Allocate string_bytes: 1 byte (reserved index 0). + zir.string_bytes_cap = 1; + zir.string_bytes = ARR_INIT(uint8_t, 1); + zir.string_bytes[0] = 0; + zir.string_bytes_len = 1; + + // Reserved extra slots. + zir.extra[ZIR_EXTRA_COMPILE_ERRORS] = 0; + zir.extra[ZIR_EXTRA_IMPORTS] = 0; + zir.extra_len = ZIR_EXTRA_RESERVED_COUNT; + + // StructDecl payload at extra[2..7]: + // fields_hash[0..3], src_line, src_node + uint32_t payload_index = zir.extra_len; + zir.extra[zir.extra_len++] = HASH_AUTO[0]; + zir.extra[zir.extra_len++] = HASH_AUTO[1]; + zir.extra[zir.extra_len++] = HASH_AUTO[2]; + zir.extra[zir.extra_len++] = HASH_AUTO[3]; + zir.extra[zir.extra_len++] = 0; // src_line + zir.extra[zir.extra_len++] = 0; // src_node (root) + + // Instruction 0: extended/struct_decl. + ZirInstData data; + memset(&data, 0, sizeof(data)); + data.extended.opcode = (uint16_t)ZIR_EXT_STRUCT_DECL; + data.extended.small = 0; // all flags zero for empty auto struct + data.extended.operand = payload_index; + + zir.inst_tags[0] = ZIR_INST_EXTENDED; + zir.inst_datas[0] = data; + zir.inst_len = 1; + + zir.has_compile_errors = false; + + (void)ast; + return zir; +} diff --git a/astgen.h b/astgen.h new file mode 100644 index 0000000000..0f0e1eefce --- /dev/null +++ b/astgen.h @@ -0,0 +1,11 @@ +// astgen.h — AST to ZIR conversion, ported from lib/std/zig/AstGen.zig. +#ifndef _ZIG0_ASTGEN_H__ +#define _ZIG0_ASTGEN_H__ + +#include "ast.h" +#include "zir.h" + +// Convert AST to ZIR. +Zir astGen(const Ast* ast); + +#endif diff --git a/astgen_test.zig b/astgen_test.zig new file mode 100644 index 0000000000..bb4e58a872 --- /dev/null +++ b/astgen_test.zig @@ -0,0 +1,122 @@ +const std = @import("std"); +const Ast = std.zig.Ast; +const Zir = std.zig.Zir; +const AstGen = std.zig.AstGen; +const Allocator = std.mem.Allocator; + +const c = @cImport({ + @cInclude("astgen.h"); +}); + +test "astgen: empty source" { + const gpa = std.testing.allocator; + + const source: [:0]const u8 = ""; + + // Reference: parse and generate ZIR with Zig. + var tree = try Ast.parse(gpa, source, .zig); + defer tree.deinit(gpa); + var ref_zir = try AstGen.generate(gpa, tree); + defer ref_zir.deinit(gpa); + + // Test: parse and generate ZIR with C. + var c_ast = c.astParse(source.ptr, @intCast(source.len)); + defer c.astDeinit(&c_ast); + var c_zir = c.astGen(&c_ast); + defer c.zirDeinit(&c_zir); + + try expectEqualZir(ref_zir, c_zir); +} + +fn expectEqualZir(ref: Zir, got: c.Zir) !void { + // Compare instruction count. + const ref_len: u32 = @intCast(ref.instructions.len); + try std.testing.expectEqual(ref_len, got.inst_len); + + // Compare instructions (tag + data) field-by-field. + const ref_tags = ref.instructions.items(.tag); + const ref_datas = ref.instructions.items(.data); + for (0..ref_len) |i| { + const ref_tag: u8 = @intFromEnum(ref_tags[i]); + const got_tag: u8 = @intCast(got.inst_tags[i]); + if (ref_tag != got_tag) { + std.debug.print( + "inst_tags[{d}] mismatch: ref={d} got={d}\n", + .{ i, ref_tag, got_tag }, + ); + return error.TestExpectedEqual; + } + try expectEqualData(i, ref_tags[i], ref_datas[i], got.inst_datas[i]); + } + + // Compare extra data. + const ref_extra_len: u32 = @intCast(ref.extra.len); + try std.testing.expectEqual(ref_extra_len, got.extra_len); + for (0..ref_extra_len) |i| { + if (ref.extra[i] != got.extra[i]) { + std.debug.print( + "extra[{d}] mismatch: ref=0x{x:0>8} got=0x{x:0>8}\n", + .{ i, ref.extra[i], got.extra[i] }, + ); + return error.TestExpectedEqual; + } + } + + // Compare string bytes. + const ref_sb_len: u32 = @intCast(ref.string_bytes.len); + try std.testing.expectEqual(ref_sb_len, got.string_bytes_len); + for (0..ref_sb_len) |i| { + if (ref.string_bytes[i] != got.string_bytes[i]) { + std.debug.print( + "string_bytes[{d}] mismatch: ref=0x{x:0>2} got=0x{x:0>2}\n", + .{ i, ref.string_bytes[i], got.string_bytes[i] }, + ); + return error.TestExpectedEqual; + } + } +} + +/// Compare a single instruction's data, dispatching by tag. +/// Zig's Data union has no guaranteed in-memory layout, so we +/// compare each variant's fields individually. +fn expectEqualData( + idx: usize, + tag: Zir.Inst.Tag, + ref: Zir.Inst.Data, + got: c.ZirInstData, +) !void { + switch (tag) { + .extended => { + const r = ref.extended; + const g = got.extended; + if (@intFromEnum(r.opcode) != g.opcode or + r.small != g.small or + r.operand != g.operand) + { + std.debug.print( + "inst_datas[{d}] (extended) mismatch:\n" ++ + " ref: opcode={d} small=0x{x:0>4} operand={d}\n" ++ + " got: opcode={d} small=0x{x:0>4} operand={d}\n", + .{ + idx, + @intFromEnum(r.opcode), + r.small, + r.operand, + g.opcode, + g.small, + g.operand, + }, + ); + return error.TestExpectedEqual; + } + }, + // Add more tag handlers as AstGen implementation grows. + else => { + std.debug.print( + "inst_datas[{d}]: unhandled tag {d} in comparison\n", + .{ idx, @intFromEnum(tag) }, + ); + return error.TestUnexpectedResult; + }, + } +} diff --git a/build.zig b/build.zig index c8d011772e..be2dcf249e 100644 --- a/build.zig +++ b/build.zig @@ -5,6 +5,8 @@ const headers = &[_][]const u8{ "common.h", "ast.h", "parser.h", + "zir.h", + "astgen.h", }; const c_lib_files = &[_][]const u8{ @@ -12,6 +14,8 @@ const c_lib_files = &[_][]const u8{ "ast.c", "zig0.c", "parser.c", + "zir.c", + "astgen.c", }; const all_c_files = c_lib_files ++ &[_][]const u8{"main.c"}; diff --git a/test_all.zig b/test_all.zig index 44861320da..560d7d77d4 100644 --- a/test_all.zig +++ b/test_all.zig @@ -1,4 +1,5 @@ test "zig0 test suite" { _ = @import("tokenizer_test.zig"); _ = @import("parser_test.zig"); + _ = @import("astgen_test.zig"); } diff --git a/zir.c b/zir.c new file mode 100644 index 0000000000..8e6b406aaf --- /dev/null +++ b/zir.c @@ -0,0 +1,19 @@ +#include "zir.h" +#include + +void zirDeinit(Zir* zir) { + free(zir->inst_tags); + free(zir->inst_datas); + free(zir->extra); + free(zir->string_bytes); + zir->inst_tags = NULL; + zir->inst_datas = NULL; + zir->extra = NULL; + zir->string_bytes = NULL; + zir->inst_len = 0; + zir->inst_cap = 0; + zir->extra_len = 0; + zir->extra_cap = 0; + zir->string_bytes_len = 0; + zir->string_bytes_cap = 0; +} diff --git a/zir.h b/zir.h new file mode 100644 index 0000000000..7245f28597 --- /dev/null +++ b/zir.h @@ -0,0 +1,466 @@ +// zir.h — ZIR data structures, ported from lib/std/zig/Zir.zig. +#ifndef _ZIG0_ZIR_H__ +#define _ZIG0_ZIR_H__ + +#include "common.h" +#include +#include + +// --- ZIR instruction tags (uint8_t) --- +// Matches Zir.Inst.Tag enum order from Zir.zig. + +#define ZIR_INST_FOREACH_TAG(TAG) \ + TAG(ZIR_INST_ADD) \ + TAG(ZIR_INST_ADDWRAP) \ + TAG(ZIR_INST_ADD_SAT) \ + TAG(ZIR_INST_ADD_UNSAFE) \ + TAG(ZIR_INST_SUB) \ + TAG(ZIR_INST_SUBWRAP) \ + TAG(ZIR_INST_SUB_SAT) \ + TAG(ZIR_INST_MUL) \ + TAG(ZIR_INST_MULWRAP) \ + TAG(ZIR_INST_MUL_SAT) \ + TAG(ZIR_INST_DIV_EXACT) \ + TAG(ZIR_INST_DIV_FLOOR) \ + TAG(ZIR_INST_DIV_TRUNC) \ + TAG(ZIR_INST_MOD) \ + TAG(ZIR_INST_REM) \ + TAG(ZIR_INST_MOD_REM) \ + TAG(ZIR_INST_SHL) \ + TAG(ZIR_INST_SHL_EXACT) \ + TAG(ZIR_INST_SHL_SAT) \ + TAG(ZIR_INST_SHR) \ + TAG(ZIR_INST_SHR_EXACT) \ + TAG(ZIR_INST_PARAM) \ + TAG(ZIR_INST_PARAM_COMPTIME) \ + TAG(ZIR_INST_PARAM_ANYTYPE) \ + TAG(ZIR_INST_PARAM_ANYTYPE_COMPTIME) \ + TAG(ZIR_INST_ARRAY_CAT) \ + TAG(ZIR_INST_ARRAY_MUL) \ + TAG(ZIR_INST_ARRAY_TYPE) \ + TAG(ZIR_INST_ARRAY_TYPE_SENTINEL) \ + TAG(ZIR_INST_VECTOR_TYPE) \ + TAG(ZIR_INST_ELEM_TYPE) \ + TAG(ZIR_INST_INDEXABLE_PTR_ELEM_TYPE) \ + TAG(ZIR_INST_SPLAT_OP_RESULT_TY) \ + TAG(ZIR_INST_INDEXABLE_PTR_LEN) \ + TAG(ZIR_INST_ANYFRAME_TYPE) \ + TAG(ZIR_INST_AS_NODE) \ + TAG(ZIR_INST_AS_SHIFT_OPERAND) \ + TAG(ZIR_INST_BIT_AND) \ + TAG(ZIR_INST_BITCAST) \ + TAG(ZIR_INST_BIT_NOT) \ + TAG(ZIR_INST_BIT_OR) \ + TAG(ZIR_INST_BLOCK) \ + TAG(ZIR_INST_BLOCK_COMPTIME) \ + TAG(ZIR_INST_BLOCK_INLINE) \ + TAG(ZIR_INST_DECLARATION) \ + TAG(ZIR_INST_SUSPEND_BLOCK) \ + TAG(ZIR_INST_BOOL_NOT) \ + TAG(ZIR_INST_BOOL_BR_AND) \ + TAG(ZIR_INST_BOOL_BR_OR) \ + TAG(ZIR_INST_BREAK) \ + TAG(ZIR_INST_BREAK_INLINE) \ + TAG(ZIR_INST_SWITCH_CONTINUE) \ + TAG(ZIR_INST_CHECK_COMPTIME_CONTROL_FLOW) \ + TAG(ZIR_INST_CALL) \ + TAG(ZIR_INST_FIELD_CALL) \ + TAG(ZIR_INST_BUILTIN_CALL) \ + TAG(ZIR_INST_CMP_LT) \ + TAG(ZIR_INST_CMP_LTE) \ + TAG(ZIR_INST_CMP_EQ) \ + TAG(ZIR_INST_CMP_GTE) \ + TAG(ZIR_INST_CMP_GT) \ + TAG(ZIR_INST_CMP_NEQ) \ + TAG(ZIR_INST_CONDBR) \ + TAG(ZIR_INST_CONDBR_INLINE) \ + TAG(ZIR_INST_TRY) \ + TAG(ZIR_INST_TRY_PTR) \ + TAG(ZIR_INST_ERROR_SET_DECL) \ + TAG(ZIR_INST_DBG_STMT) \ + TAG(ZIR_INST_DBG_VAR_PTR) \ + TAG(ZIR_INST_DBG_VAR_VAL) \ + TAG(ZIR_INST_DECL_REF) \ + TAG(ZIR_INST_DECL_VAL) \ + TAG(ZIR_INST_LOAD) \ + TAG(ZIR_INST_DIV) \ + TAG(ZIR_INST_ELEM_PTR_NODE) \ + TAG(ZIR_INST_ELEM_PTR) \ + TAG(ZIR_INST_ELEM_VAL_NODE) \ + TAG(ZIR_INST_ELEM_VAL) \ + TAG(ZIR_INST_ELEM_VAL_IMM) \ + TAG(ZIR_INST_ENSURE_RESULT_USED) \ + TAG(ZIR_INST_ENSURE_RESULT_NON_ERROR) \ + TAG(ZIR_INST_ENSURE_ERR_UNION_PAYLOAD_VOID) \ + TAG(ZIR_INST_ERROR_UNION_TYPE) \ + TAG(ZIR_INST_ERROR_VALUE) \ + TAG(ZIR_INST_EXPORT) \ + TAG(ZIR_INST_FIELD_PTR) \ + TAG(ZIR_INST_FIELD_VAL) \ + TAG(ZIR_INST_FIELD_PTR_NAMED) \ + TAG(ZIR_INST_FIELD_VAL_NAMED) \ + TAG(ZIR_INST_FUNC) \ + TAG(ZIR_INST_FUNC_INFERRED) \ + TAG(ZIR_INST_FUNC_FANCY) \ + TAG(ZIR_INST_IMPORT) \ + TAG(ZIR_INST_INT) \ + TAG(ZIR_INST_INT_BIG) \ + TAG(ZIR_INST_FLOAT) \ + TAG(ZIR_INST_FLOAT128) \ + TAG(ZIR_INST_INT_TYPE) \ + TAG(ZIR_INST_IS_NON_NULL) \ + TAG(ZIR_INST_IS_NON_NULL_PTR) \ + TAG(ZIR_INST_IS_NON_ERR) \ + TAG(ZIR_INST_IS_NON_ERR_PTR) \ + TAG(ZIR_INST_RET_IS_NON_ERR) \ + TAG(ZIR_INST_LOOP) \ + TAG(ZIR_INST_REPEAT) \ + TAG(ZIR_INST_REPEAT_INLINE) \ + TAG(ZIR_INST_FOR_LEN) \ + TAG(ZIR_INST_MERGE_ERROR_SETS) \ + TAG(ZIR_INST_REF) \ + TAG(ZIR_INST_RET_NODE) \ + TAG(ZIR_INST_RET_LOAD) \ + TAG(ZIR_INST_RET_IMPLICIT) \ + TAG(ZIR_INST_RET_ERR_VALUE) \ + TAG(ZIR_INST_RET_ERR_VALUE_CODE) \ + TAG(ZIR_INST_RET_PTR) \ + TAG(ZIR_INST_RET_TYPE) \ + TAG(ZIR_INST_PTR_TYPE) \ + TAG(ZIR_INST_SLICE_START) \ + TAG(ZIR_INST_SLICE_END) \ + TAG(ZIR_INST_SLICE_SENTINEL) \ + TAG(ZIR_INST_SLICE_LENGTH) \ + TAG(ZIR_INST_SLICE_SENTINEL_TY) \ + TAG(ZIR_INST_STORE_NODE) \ + TAG(ZIR_INST_STORE_TO_INFERRED_PTR) \ + TAG(ZIR_INST_STR) \ + TAG(ZIR_INST_NEGATE) \ + TAG(ZIR_INST_NEGATE_WRAP) \ + TAG(ZIR_INST_TYPEOF) \ + TAG(ZIR_INST_TYPEOF_BUILTIN) \ + TAG(ZIR_INST_TYPEOF_LOG2_INT_TYPE) \ + TAG(ZIR_INST_UNREACHABLE) \ + TAG(ZIR_INST_XOR) \ + TAG(ZIR_INST_OPTIONAL_TYPE) \ + TAG(ZIR_INST_OPTIONAL_PAYLOAD_SAFE) \ + TAG(ZIR_INST_OPTIONAL_PAYLOAD_UNSAFE) \ + TAG(ZIR_INST_OPTIONAL_PAYLOAD_SAFE_PTR) \ + TAG(ZIR_INST_OPTIONAL_PAYLOAD_UNSAFE_PTR) \ + TAG(ZIR_INST_ERR_UNION_PAYLOAD_UNSAFE) \ + TAG(ZIR_INST_ERR_UNION_PAYLOAD_UNSAFE_PTR) \ + TAG(ZIR_INST_ERR_UNION_CODE) \ + TAG(ZIR_INST_ERR_UNION_CODE_PTR) \ + TAG(ZIR_INST_ENUM_LITERAL) \ + TAG(ZIR_INST_DECL_LITERAL) \ + TAG(ZIR_INST_DECL_LITERAL_NO_COERCE) \ + TAG(ZIR_INST_SWITCH_BLOCK) \ + TAG(ZIR_INST_SWITCH_BLOCK_REF) \ + TAG(ZIR_INST_SWITCH_BLOCK_ERR_UNION) \ + TAG(ZIR_INST_VALIDATE_DEREF) \ + TAG(ZIR_INST_VALIDATE_DESTRUCTURE) \ + TAG(ZIR_INST_FIELD_TYPE_REF) \ + TAG(ZIR_INST_OPT_EU_BASE_PTR_INIT) \ + TAG(ZIR_INST_COERCE_PTR_ELEM_TY) \ + TAG(ZIR_INST_VALIDATE_REF_TY) \ + TAG(ZIR_INST_VALIDATE_CONST) \ + TAG(ZIR_INST_STRUCT_INIT_EMPTY) \ + TAG(ZIR_INST_STRUCT_INIT_EMPTY_RESULT) \ + TAG(ZIR_INST_STRUCT_INIT_EMPTY_REF_RESULT) \ + TAG(ZIR_INST_STRUCT_INIT_ANON) \ + TAG(ZIR_INST_STRUCT_INIT) \ + TAG(ZIR_INST_STRUCT_INIT_REF) \ + TAG(ZIR_INST_VALIDATE_STRUCT_INIT_TY) \ + TAG(ZIR_INST_VALIDATE_STRUCT_INIT_RESULT_TY) \ + TAG(ZIR_INST_VALIDATE_PTR_STRUCT_INIT) \ + TAG(ZIR_INST_STRUCT_INIT_FIELD_TYPE) \ + TAG(ZIR_INST_STRUCT_INIT_FIELD_PTR) \ + TAG(ZIR_INST_ARRAY_INIT_ANON) \ + TAG(ZIR_INST_ARRAY_INIT) \ + TAG(ZIR_INST_ARRAY_INIT_REF) \ + TAG(ZIR_INST_VALIDATE_ARRAY_INIT_TY) \ + TAG(ZIR_INST_VALIDATE_ARRAY_INIT_RESULT_TY) \ + TAG(ZIR_INST_VALIDATE_ARRAY_INIT_REF_TY) \ + TAG(ZIR_INST_VALIDATE_PTR_ARRAY_INIT) \ + TAG(ZIR_INST_ARRAY_INIT_ELEM_TYPE) \ + TAG(ZIR_INST_ARRAY_INIT_ELEM_PTR) \ + TAG(ZIR_INST_UNION_INIT) \ + TAG(ZIR_INST_TYPE_INFO) \ + TAG(ZIR_INST_SIZE_OF) \ + TAG(ZIR_INST_BIT_SIZE_OF) \ + TAG(ZIR_INST_INT_FROM_PTR) \ + TAG(ZIR_INST_COMPILE_ERROR) \ + TAG(ZIR_INST_SET_EVAL_BRANCH_QUOTA) \ + TAG(ZIR_INST_INT_FROM_ENUM) \ + TAG(ZIR_INST_ALIGN_OF) \ + TAG(ZIR_INST_INT_FROM_BOOL) \ + TAG(ZIR_INST_EMBED_FILE) \ + TAG(ZIR_INST_ERROR_NAME) \ + TAG(ZIR_INST_PANIC) \ + TAG(ZIR_INST_TRAP) \ + TAG(ZIR_INST_SET_RUNTIME_SAFETY) \ + TAG(ZIR_INST_SQRT) \ + TAG(ZIR_INST_SIN) \ + TAG(ZIR_INST_COS) \ + TAG(ZIR_INST_TAN) \ + TAG(ZIR_INST_EXP) \ + TAG(ZIR_INST_EXP2) \ + TAG(ZIR_INST_LOG) \ + TAG(ZIR_INST_LOG2) \ + TAG(ZIR_INST_LOG10) \ + TAG(ZIR_INST_ABS) \ + TAG(ZIR_INST_FLOOR) \ + TAG(ZIR_INST_CEIL) \ + TAG(ZIR_INST_TRUNC) \ + TAG(ZIR_INST_ROUND) \ + TAG(ZIR_INST_TAG_NAME) \ + TAG(ZIR_INST_TYPE_NAME) \ + TAG(ZIR_INST_FRAME_TYPE) \ + TAG(ZIR_INST_INT_FROM_FLOAT) \ + TAG(ZIR_INST_FLOAT_FROM_INT) \ + TAG(ZIR_INST_PTR_FROM_INT) \ + TAG(ZIR_INST_ENUM_FROM_INT) \ + TAG(ZIR_INST_FLOAT_CAST) \ + TAG(ZIR_INST_INT_CAST) \ + TAG(ZIR_INST_PTR_CAST) \ + TAG(ZIR_INST_TRUNCATE) \ + TAG(ZIR_INST_HAS_DECL) \ + TAG(ZIR_INST_HAS_FIELD) \ + TAG(ZIR_INST_CLZ) \ + TAG(ZIR_INST_CTZ) \ + TAG(ZIR_INST_POP_COUNT) \ + TAG(ZIR_INST_BYTE_SWAP) \ + TAG(ZIR_INST_BIT_REVERSE) \ + TAG(ZIR_INST_BIT_OFFSET_OF) \ + TAG(ZIR_INST_OFFSET_OF) \ + TAG(ZIR_INST_SPLAT) \ + TAG(ZIR_INST_REDUCE) \ + TAG(ZIR_INST_SHUFFLE) \ + TAG(ZIR_INST_ATOMIC_LOAD) \ + TAG(ZIR_INST_ATOMIC_RMW) \ + TAG(ZIR_INST_ATOMIC_STORE) \ + TAG(ZIR_INST_MUL_ADD) \ + TAG(ZIR_INST_MEMCPY) \ + TAG(ZIR_INST_MEMMOVE) \ + TAG(ZIR_INST_MEMSET) \ + TAG(ZIR_INST_MIN) \ + TAG(ZIR_INST_MAX) \ + TAG(ZIR_INST_C_IMPORT) \ + TAG(ZIR_INST_ALLOC) \ + TAG(ZIR_INST_ALLOC_MUT) \ + TAG(ZIR_INST_ALLOC_COMPTIME_MUT) \ + TAG(ZIR_INST_ALLOC_INFERRED) \ + TAG(ZIR_INST_ALLOC_INFERRED_MUT) \ + TAG(ZIR_INST_ALLOC_INFERRED_COMPTIME) \ + TAG(ZIR_INST_ALLOC_INFERRED_COMPTIME_MUT) \ + TAG(ZIR_INST_RESOLVE_INFERRED_ALLOC) \ + TAG(ZIR_INST_MAKE_PTR_CONST) \ + TAG(ZIR_INST_RESUME) \ + TAG(ZIR_INST_DEFER) \ + TAG(ZIR_INST_DEFER_ERR_CODE) \ + TAG(ZIR_INST_SAVE_ERR_RET_INDEX) \ + TAG(ZIR_INST_RESTORE_ERR_RET_INDEX_UNCONDITIONAL) \ + TAG(ZIR_INST_RESTORE_ERR_RET_INDEX_FN_ENTRY) \ + TAG(ZIR_INST_EXTENDED) + +#define ZIR_GENERATE_ENUM(e) e, +typedef enum { ZIR_INST_FOREACH_TAG(ZIR_GENERATE_ENUM) } ZirInstTag; + +// --- ZIR extended opcodes (uint16_t) --- +// Matches Zir.Inst.Extended enum order from Zir.zig. + +#define ZIR_EXT_FOREACH_TAG(TAG) \ + TAG(ZIR_EXT_STRUCT_DECL) \ + TAG(ZIR_EXT_ENUM_DECL) \ + TAG(ZIR_EXT_UNION_DECL) \ + TAG(ZIR_EXT_OPAQUE_DECL) \ + TAG(ZIR_EXT_TUPLE_DECL) \ + TAG(ZIR_EXT_THIS) \ + TAG(ZIR_EXT_RET_ADDR) \ + TAG(ZIR_EXT_BUILTIN_SRC) \ + TAG(ZIR_EXT_ERROR_RETURN_TRACE) \ + TAG(ZIR_EXT_FRAME) \ + TAG(ZIR_EXT_FRAME_ADDRESS) \ + TAG(ZIR_EXT_ALLOC) \ + TAG(ZIR_EXT_BUILTIN_EXTERN) \ + TAG(ZIR_EXT_ASM) \ + TAG(ZIR_EXT_ASM_EXPR) \ + TAG(ZIR_EXT_COMPILE_LOG) \ + TAG(ZIR_EXT_TYPEOF_PEER) \ + TAG(ZIR_EXT_MIN_MULTI) \ + TAG(ZIR_EXT_MAX_MULTI) \ + TAG(ZIR_EXT_ADD_WITH_OVERFLOW) \ + TAG(ZIR_EXT_SUB_WITH_OVERFLOW) \ + TAG(ZIR_EXT_MUL_WITH_OVERFLOW) \ + TAG(ZIR_EXT_SHL_WITH_OVERFLOW) \ + TAG(ZIR_EXT_C_UNDEF) \ + TAG(ZIR_EXT_C_INCLUDE) \ + TAG(ZIR_EXT_C_DEFINE) \ + TAG(ZIR_EXT_WASM_MEMORY_SIZE) \ + TAG(ZIR_EXT_WASM_MEMORY_GROW) \ + TAG(ZIR_EXT_PREFETCH) \ + TAG(ZIR_EXT_SET_FLOAT_MODE) \ + TAG(ZIR_EXT_ERROR_CAST) \ + TAG(ZIR_EXT_BREAKPOINT) \ + TAG(ZIR_EXT_DISABLE_INSTRUMENTATION) \ + TAG(ZIR_EXT_DISABLE_INTRINSICS) \ + TAG(ZIR_EXT_SELECT) \ + TAG(ZIR_EXT_INT_FROM_ERROR) \ + TAG(ZIR_EXT_ERROR_FROM_INT) \ + TAG(ZIR_EXT_REIFY) \ + TAG(ZIR_EXT_CMPXCHG) \ + TAG(ZIR_EXT_C_VA_ARG) \ + TAG(ZIR_EXT_C_VA_COPY) \ + TAG(ZIR_EXT_C_VA_END) \ + TAG(ZIR_EXT_C_VA_START) \ + TAG(ZIR_EXT_PTR_CAST_FULL) \ + TAG(ZIR_EXT_PTR_CAST_NO_DEST) \ + TAG(ZIR_EXT_WORK_ITEM_ID) \ + TAG(ZIR_EXT_WORK_GROUP_SIZE) \ + TAG(ZIR_EXT_WORK_GROUP_ID) \ + TAG(ZIR_EXT_IN_COMPTIME) \ + TAG(ZIR_EXT_RESTORE_ERR_RET_INDEX) \ + TAG(ZIR_EXT_CLOSURE_GET) \ + TAG(ZIR_EXT_VALUE_PLACEHOLDER) \ + TAG(ZIR_EXT_FIELD_PARENT_PTR) \ + TAG(ZIR_EXT_BUILTIN_VALUE) \ + TAG(ZIR_EXT_BRANCH_HINT) \ + TAG(ZIR_EXT_INPLACE_ARITH_RESULT_TY) \ + TAG(ZIR_EXT_DBG_EMPTY_STMT) \ + TAG(ZIR_EXT_ASTGEN_ERROR) + +#define ZIR_EXT_GENERATE_ENUM(e) e, +typedef enum { ZIR_EXT_FOREACH_TAG(ZIR_EXT_GENERATE_ENUM) } ZirInstExtended; + +// --- ZIR instruction data (8-byte union) --- +// Matches Zir.Inst.Data union from Zir.zig. + +typedef uint32_t ZirInstIndex; +typedef uint32_t ZirInstRef; + +typedef union { + struct { + uint16_t opcode; + uint16_t small; + uint32_t operand; + } extended; + struct { + int32_t src_node; + ZirInstRef operand; + } un_node; + struct { + int32_t src_tok; + ZirInstRef operand; + } un_tok; + struct { + int32_t src_node; + uint32_t payload_index; + } pl_node; + struct { + int32_t src_tok; + uint32_t payload_index; + } pl_tok; + struct { + ZirInstRef lhs; + ZirInstRef rhs; + } bin; + struct { + uint32_t start; + uint32_t len; + } str; + struct { + uint32_t start; + int32_t src_tok; + } str_tok; + int32_t tok; + int32_t node; + uint64_t int_val; + double float_val; + struct { + uint8_t flags; + uint8_t size; + uint16_t _pad; + uint32_t payload_index; + } ptr_type; + struct { + int32_t src_node; + uint8_t signedness; + uint8_t _pad; + uint16_t bit_count; + } int_type; + struct { + int32_t src_node; + uint32_t _pad; + } unreachable_data; + struct { + ZirInstRef operand; + uint32_t payload_index; + } break_data; + struct { + uint32_t line; + uint32_t column; + } dbg_stmt; + struct { + int32_t src_node; + ZirInstIndex inst; + } inst_node; + struct { + uint32_t str; + ZirInstRef operand; + } str_op; + struct { + uint32_t index; + uint32_t len; + } defer_data; + struct { + ZirInstRef err_code; + uint32_t payload_index; + } defer_err_code; + struct { + ZirInstRef operand; + uint32_t _pad; + } save_err_ret_index; + struct { + ZirInstRef operand; + uint32_t idx; + } elem_val_imm; + struct { + uint32_t src_node; + uint32_t payload_index; + } declaration; +} ZirInstData; + +// --- ZIR built-in refs --- +// Matches Zir.Inst.Ref enum from Zir.zig. +// Values below REF_START_INDEX are InternPool indices. + +#define ZIR_REF_START_INDEX 124 +#define ZIR_REF_NONE UINT32_MAX +#define ZIR_MAIN_STRUCT_INST 0 + +// --- Extra indices reserved at the start of extra[] --- +// Matches Zir.ExtraIndex enum from Zir.zig. + +#define ZIR_EXTRA_COMPILE_ERRORS 0 +#define ZIR_EXTRA_IMPORTS 1 +#define ZIR_EXTRA_RESERVED_COUNT 2 + +// --- Zir output structure --- + +typedef struct { + uint32_t inst_len; + uint32_t inst_cap; + ZirInstTag* inst_tags; + ZirInstData* inst_datas; + uint32_t extra_len; + uint32_t extra_cap; + uint32_t* extra; + uint32_t string_bytes_len; + uint32_t string_bytes_cap; + uint8_t* string_bytes; + bool has_compile_errors; +} Zir; + +void zirDeinit(Zir* zir); + +#endif