stage0: refactor globals into structs matching Zig architecture

Eliminates all 39 mutable static globals across sema.c (37) and
intern_pool.c (2). State is now held in properly-typed structs passed
as parameters, mirroring the Zig reference implementation layout.

New files matching Zig src/ layout:
- compilation.h: CompilationConfig + Compilation (matches Compilation.zig)
- zcu.h/zcu.c: ZcuFile, ZcuNamespace, Zcu, zcuInit/zcuDeinit (matches Zcu.zig)
- zcu_per_thread.h: forward declarations for PerThread-style functions

Key changes:
- InternPool gains navs[] (dynamically allocated) + nav_count/nav_cap;
  Nav functions now take InternPool* (was implicit via globals)
- Sema gains Zcu* zcu; semaInit now takes Zcu* instead of InternPool*
- All module-level state (files, namespaces, memoized state, config)
  moved from static globals into Zcu struct
- zig0.c creates Compilation + Zcu before semaInit
- Test files updated to use zcuInit/zcuDeinit API

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-02 16:44:27 +00:00
parent ba547674f8
commit 9cd2e0ff77
12 changed files with 1170 additions and 925 deletions

View File

@@ -16,6 +16,7 @@ const zig0_headers = &[_][]const u8{
"ast.h",
"astgen.h",
"common.h",
"compilation.h",
"intern_pool.h",
"parser.h",
"sema.h",
@@ -24,6 +25,8 @@ const zig0_headers = &[_][]const u8{
"verbose_air.h",
"verbose_intern_pool.h",
"wyhash.h",
"zcu.h",
"zcu_per_thread.h",
"zir.h",
};
const zig0_c_lib_files = &[_][]const u8{
@@ -40,6 +43,7 @@ const zig0_c_lib_files = &[_][]const u8{
"verbose_intern_pool.c",
"wyhash.c",
"zig0.c",
"zcu.c",
"zir.c",
};
const zig0_all_c_files = zig0_c_lib_files ++ &[_][]const u8{"main.c"};

20
stage0/compilation.h Normal file
View File

@@ -0,0 +1,20 @@
// compilation.h — ported from src/Compilation.zig (Config subset we use)
#ifndef _ZIG0_COMPILATION_H__
#define _ZIG0_COMPILATION_H__
#include <stdbool.h>
// Matches Compilation.Config (the fields relevant to stage0).
typedef struct {
const char* module_root; // was: s_global_module_root
const char* target_cpu_arch; // was: s_target_cpu_arch_name ("wasm32")
const char* target_cpu_model; // was: s_target_cpu_model_name ("lime1")
const char* object_format; // was: s_config_object_format ("wasm")
const char* link_mode; // was: s_config_link_mode ("static")
bool is_test; // was: s_config_is_test
} CompilationConfig;
// Matches Compilation struct (minimal subset).
typedef struct Compilation {
CompilationConfig config;
} Compilation;
#endif

View File

@@ -443,6 +443,8 @@ InternPool ipInit(void) {
ip.string_bytes_cap = IP_STRING_INITIAL_CAP;
ip.extra = ARR_INIT(uint32_t, IP_EXTRA_INITIAL_CAP);
ip.extra_cap = IP_EXTRA_INITIAL_CAP;
ip.navs = ARR_INIT(Nav, 64);
ip.nav_cap = 64;
// Index 0: u0_type
ipAppendItem(&ip, ipMakeIntType(0, 0));
@@ -685,10 +687,12 @@ void ipDeinit(InternPool* ip) {
free(ip->hash_table);
free(ip->string_bytes);
free(ip->extra);
free(ip->navs);
ip->items = NULL;
ip->hash_table = NULL;
ip->string_bytes = NULL;
ip->extra = NULL;
ip->navs = NULL;
ip->items_len = 0;
ip->items_cap = 0;
ip->hash_cap = 0;
@@ -696,6 +700,8 @@ void ipDeinit(InternPool* ip) {
ip->string_bytes_cap = 0;
ip->extra_len = 0;
ip->extra_cap = 0;
ip->nav_count = 0;
ip->nav_cap = 0;
}
InternPoolIndex ipIntern(InternPool* ip, InternPoolKey key) {
@@ -1008,14 +1014,19 @@ InternPoolIndex ipTypeOf(const InternPool* ip, InternPoolIndex index) {
// --- Nav management ---
// Ported from InternPool.createDeclNav / InternPool.getNav.
static Nav s_navs[IP_MAX_NAVS];
static uint32_t s_num_navs;
uint32_t ipCreateDeclNav(uint32_t name, uint32_t fqn, uint32_t zir_index,
uint32_t namespace_idx, bool is_pub, bool is_const) {
assert(s_num_navs < IP_MAX_NAVS);
uint32_t idx = s_num_navs++;
Nav* nav = &s_navs[idx];
uint32_t ipCreateDeclNav(InternPool* ip, uint32_t name, uint32_t fqn,
uint32_t zir_index, uint32_t namespace_idx, bool is_pub, bool is_const) {
assert(ip->nav_count < IP_MAX_NAVS);
if (ip->nav_count >= ip->nav_cap) {
uint32_t new_cap = ip->nav_cap * 2;
Nav* new_navs = realloc(ip->navs, new_cap * sizeof(Nav));
if (!new_navs)
exit(1);
ip->navs = new_navs;
ip->nav_cap = new_cap;
}
uint32_t idx = ip->nav_count++;
Nav* nav = &ip->navs[idx];
memset(nav, 0, sizeof(*nav));
nav->name = name;
nav->fqn = fqn;
@@ -1028,14 +1039,14 @@ uint32_t ipCreateDeclNav(uint32_t name, uint32_t fqn, uint32_t zir_index,
return idx;
}
Nav* ipGetNav(uint32_t nav_index) {
assert(nav_index < s_num_navs);
return &s_navs[nav_index];
Nav* ipGetNav(InternPool* ip, uint32_t nav_index) {
assert(nav_index < ip->nav_count);
return &ip->navs[nav_index];
}
void ipResetNavs(void) { s_num_navs = 0; }
void ipResetNavs(InternPool* ip) { ip->nav_count = 0; }
uint32_t ipNavCount(void) { return s_num_navs; }
uint32_t ipNavCount(const InternPool* ip) { return ip->nav_count; }
// --- String interning ---
// Add a null-terminated string to the IP string table. Returns the

View File

@@ -423,6 +423,24 @@ typedef struct {
} data;
} InternPoolKey;
// --- Nav (declaration entry) ---
// Ported from InternPool.Nav. Navs are stored in a separate list from
// IP items — they do NOT consume IP indices. However, ptr_nav entries
// (pointers TO Navs) DO consume IP indices.
typedef struct {
uint32_t name; // NullTerminatedString index into IP string_bytes
uint32_t fqn; // fully qualified name string index
uint32_t zir_index; // ZIR instruction index of declaration
uint32_t namespace_idx; // owning namespace index
InternPoolIndex resolved_type; // IP_INDEX_NONE if unresolved
InternPoolIndex resolved_val; // IP_INDEX_NONE if unresolved
bool is_pub;
bool is_const;
} Nav;
#define IP_MAX_NAVS 16384
// --- InternPool struct ---
typedef struct {
@@ -449,26 +467,14 @@ typedef struct {
uint32_t skip_dedup_end;
uint32_t cc_keep_start;
uint32_t cc_keep_end;
// Nav storage (ported from InternPool.zig Nav storage).
// Navs do NOT consume IP indices; ptr_nav entries (pointers to navs)
// do consume IP indices. Dynamically allocated (like items/extra).
Nav* navs;
uint32_t nav_count;
uint32_t nav_cap;
} InternPool;
// --- Nav (declaration entry) ---
// Ported from InternPool.Nav. Navs are stored in a separate list from
// IP items — they do NOT consume IP indices. However, ptr_nav entries
// (pointers TO Navs) DO consume IP indices.
typedef struct {
uint32_t name; // NullTerminatedString index into IP string_bytes
uint32_t fqn; // fully qualified name string index
uint32_t zir_index; // ZIR instruction index of declaration
uint32_t namespace_idx; // owning namespace index
InternPoolIndex resolved_type; // IP_INDEX_NONE if unresolved
InternPoolIndex resolved_val; // IP_INDEX_NONE if unresolved
bool is_pub;
bool is_const;
} Nav;
#define IP_MAX_NAVS 16384
// --- Function declarations ---
InternPool ipInit(void);
@@ -479,11 +485,11 @@ InternPoolKey ipIndexToKey(const InternPool* ip, InternPoolIndex index);
InternPoolIndex ipTypeOf(const InternPool* ip, InternPoolIndex index);
// Nav management (ported from InternPool.createDeclNav)
uint32_t ipCreateDeclNav(uint32_t name, uint32_t fqn, uint32_t zir_index,
uint32_t namespace_idx, bool is_pub, bool is_const);
Nav* ipGetNav(uint32_t nav_index);
void ipResetNavs(void);
uint32_t ipNavCount(void);
uint32_t ipCreateDeclNav(InternPool* ip, uint32_t name, uint32_t fqn,
uint32_t zir_index, uint32_t namespace_idx, bool is_pub, bool is_const);
Nav* ipGetNav(InternPool* ip, uint32_t nav_index);
void ipResetNavs(InternPool* ip);
uint32_t ipNavCount(const InternPool* ip);
// String interning: add a null-terminated string to the IP string table.
// Returns the NullTerminatedString index (offset into string_bytes).

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,11 @@
#include <stdbool.h>
#include <stdint.h>
// Forward declaration — full definition in zcu.h, included by sema.c.
struct Zcu;
#define MAX_EXPORTED_DECL_NAMES 16
// --- InstMap ---
// Maps ZIR instruction index -> AIR instruction ref.
// Uses a flat array indexed by ZIR instruction index minus a start offset.
@@ -109,24 +114,6 @@ typedef struct {
uint32_t runtime_index;
} ComptimeAlloc;
// --- SemaNamespace ---
// Groups declarations by owning type (file root struct, etc.).
// Ported from Zcu.Namespace.
#define SEMA_NS_MAX_NAVS 1024
#define SEMA_NS_MAX_COMPTIME 64
typedef struct {
InternPoolIndex owner_type; // type_struct IP index
uint32_t file_idx; // index into loaded modules
uint32_t pub_navs[SEMA_NS_MAX_NAVS];
uint32_t pub_nav_count;
uint32_t priv_navs[SEMA_NS_MAX_NAVS];
uint32_t priv_nav_count;
uint32_t comptime_decls[SEMA_NS_MAX_COMPTIME]; // ZIR inst indices
uint32_t comptime_decl_count;
} SemaNamespace;
// --- SemaFuncAir ---
// Per-function Air result, produced during sema analysis.
@@ -164,7 +151,8 @@ typedef struct {
// generation.
typedef struct Sema {
InternPool* ip;
InternPool* ip; // points to zcu->ip for convenient access
struct Zcu* zcu; // owning Zcu (set by semaInit)
Zir code;
uint8_t* air_inst_tags; // AirInstTag stored as uint8_t (matches Zig u8)
AirInstData* air_inst_datas;
@@ -194,6 +182,7 @@ typedef struct Sema {
// Populated by analyzeBodyInner when processing comptime blocks
// that contain @export. Used by zirFunc to decide whether to analyze
// non-declaration-level-exported functions.
uint32_t exported_decl_names[MAX_EXPORTED_DECL_NAMES];
uint32_t num_exported_decl_names;
// Declaration table: maps string_bytes index (name) to the ZIR
// declaration instruction index. Built by zirStructDecl.
@@ -204,10 +193,6 @@ typedef struct Sema {
// Source directory for resolving relative imports.
// Set by the caller before semaAnalyze. NULL = no import resolution.
const char* source_dir;
// Module root directory for resolving non-relative imports
// (e.g. @import("std") → <module_root>/lib/std/std.zig).
// Set by the caller before semaAnalyze. NULL = no std resolution.
const char* module_root;
// Root FQN prefix for function name construction.
// Function names are "{root_fqn}.funcName" (e.g. "empty_void_function.f").
// Must match the Zig compiler's module naming (filename stem).
@@ -287,7 +272,7 @@ typedef struct Sema {
// --- Function declarations ---
void semaInit(Sema* sema, InternPool* ip, Zir code);
void semaInit(Sema* sema, struct Zcu* zcu, Zir code);
void semaDeinit(Sema* sema);
SemaFuncAirList semaAnalyze(Sema* sema);
void semaFuncAirListDeinit(SemaFuncAirList* list);

View File

@@ -5,6 +5,7 @@ const std = @import("std");
pub const c = @cImport({
@cInclude("astgen.h");
@cInclude("sema.h");
@cInclude("zcu.h");
});
// Helper to convert C #define integer constants (c_int) to u32 for comparison
@@ -167,14 +168,14 @@ test "intern_pool: pointer types" {
// ---------------------------------------------------------------------------
const SemaCheckResult = struct {
c_ip: c.InternPool,
c_zcu: *c.Zcu,
c_sema: c.Sema,
c_func_air_list: c.SemaFuncAirList,
fn deinit(self: *SemaCheckResult) void {
c.semaFuncAirListDeinit(&self.c_func_air_list);
c.semaDeinit(&self.c_sema);
c.ipDeinit(&self.c_ip);
c.zcuDeinit(self.c_zcu);
}
};
@@ -184,8 +185,16 @@ fn semaCheck(source: [:0]const u8) !SemaCheckResult {
var c_zir = c.astGen(&c_ast);
defer c.zirDeinit(&c_zir);
var result: SemaCheckResult = undefined;
result.c_ip = c.ipInit();
c.semaInit(&result.c_sema, &result.c_ip, c_zir);
var comp: c.Compilation = .{ .config = .{
.module_root = null,
.target_cpu_arch = "wasm32",
.target_cpu_model = "lime1",
.object_format = "wasm",
.link_mode = "static",
.is_test = false,
} };
result.c_zcu = c.zcuInit(&comp);
c.semaInit(&result.c_sema, result.c_zcu, c_zir);
result.c_func_air_list = c.semaAnalyze(&result.c_sema);
return result;
}

View File

@@ -68,29 +68,34 @@ fn stagesCheck(gpa: Allocator, comptime path: []const u8, source: [:0]const u8)
var module_root_buf: [std.fs.max_path_bytes:0]u8 = undefined;
const module_root_path = std.fmt.bufPrintZ(&module_root_buf, "{s}", .{abs_repo_root}) catch unreachable;
var c_ip = sc.ipInit();
defer sc.ipDeinit(&c_ip);
var comp: sc.Compilation = .{ .config = .{
.module_root = module_root_path.ptr,
.target_cpu_arch = "wasm32",
.target_cpu_model = "lime1",
.object_format = "wasm",
.link_mode = "static",
.is_test = false,
} };
const c_zcu = sc.zcuInit(&comp);
defer sc.zcuDeinit(c_zcu);
var c_sema: sc.Sema = undefined;
sc.semaInit(&c_sema, &c_ip, @bitCast(c_zir));
sc.semaInit(&c_sema, c_zcu, @bitCast(c_zir));
defer sc.semaDeinit(&c_sema);
// Pre-generated AIR uses ReleaseSmall (strip=true), so match it.
c_sema.strip = true;
// Set source_dir, module_root, root_fqn based on path prefix.
// Set source_dir, root_fqn based on path prefix.
if (comptime std.mem.startsWith(u8, path, "stage0/")) {
// Sema unit tests: set module_root to enable std/start/builtin
// loading, matching the Zig compiler which always creates std
// even with std_mod=null (Compilation.zig line 2196).
c_sema.source_dir = source_dir_path.ptr;
c_sema.module_root = module_root_path.ptr;
c_sema.root_fqn = comptime sema_test.pathStem(path);
} else if (comptime std.mem.startsWith(u8, path, "lib/std/")) {
c_sema.source_dir = source_dir_path.ptr;
c_sema.module_root = module_root_path.ptr;
c_sema.root_fqn = "std";
c_sema.module_prefix = sema_test.pathToModulePrefix(path);
} else {
c_sema.source_dir = source_dir_path.ptr;
c_sema.module_root = module_root_path.ptr;
c_sema.root_fqn = comptime sema_test.pathStem(path);
}
var c_func_air_list = sc.semaAnalyze(&c_sema);

30
stage0/zcu.c Normal file
View File

@@ -0,0 +1,30 @@
#include "zcu.h"
#include <stdlib.h>
Zcu* zcuInit(Compilation* comp) {
Zcu* zcu = (Zcu*)calloc(1, sizeof(Zcu));
if (!zcu)
exit(1);
zcu->ip = ipInit();
zcu->comp = comp;
// Initialize sentinel values.
zcu->std_file_idx = UINT32_MAX;
zcu->builtin_file_idx = UINT32_MAX;
zcu->cg_builtin_ns_idx = UINT32_MAX;
zcu->cg_builtin_nav = UINT32_MAX;
return zcu;
}
void zcuDeinit(Zcu* zcu) {
// Free loaded file ASTs and ZIRs.
for (uint32_t i = 0; i < zcu->num_files; i++) {
ZcuFile* f = &zcu->files[i];
if (f->has_zir) {
zirDeinit(&f->zir);
astDeinit(&f->ast);
}
}
ipDeinit(&zcu->ip);
// zcuAlloc uses calloc; free the struct itself.
free(zcu);
}

109
stage0/zcu.h Normal file
View File

@@ -0,0 +1,109 @@
// zcu.h — ported from src/Zcu.zig + src/Zcu/PerThread.zig.
// Holds all module-level state for a compilation unit.
#ifndef _ZIG0_ZCU_H__
#define _ZIG0_ZCU_H__
#include "ast.h"
#include "compilation.h"
#include "intern_pool.h"
#include "zir.h"
#include <stdbool.h>
#include <stdint.h>
// --- ZcuFile (matches Zcu.File) ---
// Per-source-file state.
#define ZCU_MAX_FILES 512
typedef struct {
char path[1024]; // canonical file path
char source_dir[1024]; // directory containing this file
Zir zir; // parsed ZIR (inst_len>0 if loaded)
Ast ast; // AST (kept alive to own source memory)
bool has_zir; // true if zir/ast are populated
bool analyzed; // true if createFileRootStructC has been called
} ZcuFile;
// --- ZcuNamespace (matches Zcu.Namespace) ---
#define ZCU_NS_MAX_NAVS 1024
#define ZCU_NS_MAX_COMPTIME 64
#define ZCU_MAX_NAMESPACES 512
typedef struct {
InternPoolIndex owner_type; // type_struct IP index
uint32_t file_idx; // index into files[]
uint32_t pub_navs[ZCU_NS_MAX_NAVS];
uint32_t pub_nav_count;
uint32_t priv_navs[ZCU_NS_MAX_NAVS];
uint32_t priv_nav_count;
uint32_t comptime_decls[ZCU_NS_MAX_COMPTIME]; // ZIR inst indices
uint32_t comptime_decl_count;
} ZcuNamespace;
// --- Zcu struct (matches Zcu.zig) ---
// Heap-allocated (use zcuInit/zcuDeinit); too large for stack.
#define NUM_BUILTIN_DECL_MAIN 36
typedef struct Zcu {
InternPool ip; // owns IP; matches Zcu.intern_pool
// --- File management (matches Zcu.import_table / Zcu.File) ---
ZcuFile files[ZCU_MAX_FILES]; // matches Zcu.files
uint32_t num_files;
uint32_t root_file_idx;
uint32_t std_file_idx;
uint32_t builtin_file_idx;
// --- Builtin namespace/nav ---
uint32_t cg_builtin_ns_idx; // compiler-generated builtin namespace
uint32_t cg_builtin_nav;
// --- Struct type hashing (C-specific) ---
uint32_t next_struct_hash; // unique hash counter for struct types
// --- Analysis state ---
bool in_main_analysis;
uint32_t enum_force_intern_threshold;
// --- Namespaces (matches Zcu.Namespace storage) ---
ZcuNamespace namespaces[ZCU_MAX_NAMESPACES];
uint32_t num_namespaces;
InternPoolIndex file_root_types[ZCU_MAX_FILES];
uint32_t file_namespaces[ZCU_MAX_FILES];
// --- Type/layout resolution tracking ---
bool struct_layout_resolved[4096];
bool struct_fully_resolved[4096];
bool union_fully_resolved[4096];
InternPoolIndex union_tag_enums[256];
InternPoolIndex union_tag_types[256];
uint32_t num_union_tag_enums;
// --- Memoized builtin state (matches Zcu.builtin_decl_values) ---
bool memoized_main_resolved;
bool full_memoized_resolved;
InternPoolIndex builtin_decl_values[NUM_BUILTIN_DECL_MAIN];
// --- Preamble IP range tracking (C-specific shard simulation) ---
uint32_t preamble_memoized_start;
uint32_t preamble_memoized_end;
uint32_t preamble_cc_start;
uint32_t preamble_cc_end;
uint32_t preamble_builtin_navs[5];
uint32_t preamble_builtin_nav_count;
// --- Compilation config ---
Compilation* comp; // back-pointer; matches Zcu.comp in Zig
} Zcu;
// --- Function declarations ---
// Allocate and initialize a Zcu on the heap (too large for stack).
Zcu* zcuInit(Compilation* comp);
// Free all resources including the struct itself (matches zcuInit).
void zcuDeinit(Zcu* zcu);
#endif

67
stage0/zcu_per_thread.h Normal file
View File

@@ -0,0 +1,67 @@
// zcu_per_thread.h — ported from src/Zcu/PerThread.zig.
// Functions for per-thread analysis orchestration.
#ifndef _ZIG0_ZCU_PER_THREAD_H__
#define _ZIG0_ZCU_PER_THREAD_H__
#include "zcu.h"
#include "zir.h"
#include <stdbool.h>
#include <stdint.h>
// Forward declaration: avoid including sema.h here.
struct Sema;
// These match PerThread.zig functions (taking Sema* for Zcu access):
// Load, parse, and analyze an imported file. Returns the file index,
// or UINT32_MAX on failure. (matches PerThread.ensureFileAnalyzed)
uint32_t ensureFileAnalyzedC(struct Sema* sema, const char* full_path);
// Create the root struct type for a file. (matches
// PerThread.createFileRootStruct)
InternPoolIndex createFileRootStructC(
struct Sema* sema, uint32_t file_idx, const Zir* zir);
// Scan a namespace, building its Nav list. (matches PerThread.scanNamespace)
void scanNamespaceC(struct Sema* sema, uint32_t ns_idx,
const uint32_t* decl_insts, uint32_t decls_len, const Zir* zir);
// Analyze a nav declaration's value. (matches PerThread.analyzeNavVal)
InternPoolIndex analyzeNavValC(struct Sema* sema, uint32_t nav_idx);
// Resolve memoized state for .main builtins. (matches
// PerThread.analyzeMemoizedState)
void analyzeMemoizedStateC(struct Sema* sema);
// Ensure a Nav's value is up to date.
InternPoolIndex ensureNavValUpToDate(struct Sema* sema, uint32_t nav_idx);
// Resolve all 36 .main-stage builtins. (matches
// PerThread.ensureFullMemoizedState)
void ensureFullMemoizedStateC(struct Sema* sema);
// Handle @import. (matches PerThread.importFile / doImport)
uint32_t doImport(
struct Sema* sema, const char* source_dir, const char* import_string);
// Resolve struct layout (field offsets).
bool resolveStructLayoutC(struct Sema* sema, uint32_t nav_idx);
// Resolve struct fully (field types).
void resolveStructFullyC(struct Sema* sema, uint32_t nav_idx);
// Resolve union fully (field types + tag enum).
void resolveUnionFullyC(struct Sema* sema, uint32_t nav_idx);
// Find namespace index for a type IP index.
uint32_t findNamespaceForType(struct Sema* sema, InternPoolIndex type_ip);
// Find a nav in a namespace by name. Returns UINT32_MAX if not found.
uint32_t findNavInNamespace(
struct Sema* sema, uint32_t ns_idx, const char* name);
// Analyze a comptime block at file scope.
void analyzeComptimeUnit(
struct Sema* sema, uint32_t file_idx, uint32_t comptime_decl);
#endif

View File

@@ -1,9 +1,11 @@
#include "ast.h"
#include "astgen.h"
#include "compilation.h"
#include "intern_pool.h"
#include "sema.h"
#include "verbose_air.h"
#include "verbose_intern_pool.h"
#include "zcu.h"
#include "zir.h"
#include <stdbool.h>
@@ -44,19 +46,25 @@ static int zig0Run(const char* program, const char* source_dir,
fprintf(stderr, "zir: %u instructions, %u extra, %u string bytes\n",
zir.inst_len, zir.extra_len, zir.string_bytes_len);
InternPool ip = ipInit();
Compilation comp;
comp.config.module_root = module_root;
comp.config.target_cpu_arch = "wasm32";
comp.config.target_cpu_model = "lime1";
comp.config.object_format = "wasm";
comp.config.link_mode = "static";
comp.config.is_test = false;
Zcu* zcu = zcuInit(&comp);
Sema sema;
semaInit(&sema, &ip, zir);
semaInit(&sema, zcu, zir);
sema.source_dir = source_dir;
sema.module_root = module_root;
SemaFuncAirList func_airs = semaAnalyze(&sema);
if (verbose_intern_pool)
verboseIpPrint(stderr, &ip);
verboseIpPrint(stderr, &zcu->ip);
if (verbose_air)
verboseAirPrint(stderr, &func_airs, &ip);
verboseAirPrint(stderr, &func_airs, &zcu->ip);
semaDeinit(&sema);
semaFuncAirListDeinit(&func_airs);
ipDeinit(&ip);
zcuDeinit(zcu);
zirDeinit(&zir);
return 0;
}