Emit "monomorphized" zig code

The pipeline of Zig code looks something like this:

  1. Source code
  2. AST (Abstract Syntax Tree)
  3. ZIR (Zig Intermediate Representation)
  4. AIR (Analyzed Intermediate Representation)
  5. Backend-specific logic (LLVM backend, x86-64 self-hosted backend, etc.)

Comptime evaluation happens in the transition from ZIR to AIR, using a process called Sema. So, it sounds like AIR is what you’re looking for.

It is possible to look at a textual representation of ZIR and AIR using the following two commands, but these commands only work on a debug build of the compiler, so you will need to build one from source to run them:

  • ZIR: zig ast-check -t file.zig
  • AIR: zig build-obj --verbose-air file.zig

For comparison, here’s a simple main function:

pub fn main() !void {
    std.debug.print("Hello, world!", .{});
}

And here are the ZIR and AIR for the function, as of Zig 0.12.0-dev.1836+dd189a354:

ZIR
  [60] pub main line(2) hash(b34a5dd1dd5f828d0d25f54718b526c5): %4 = block_inline({
    %23 = func_inferred(ret_ty=@void_type, inferror, body={
      %5 = block({
        %6 = dbg_block_begin()
        %7 = dbg_stmt(2, 5)
        %8 = decl_ref("std") token_offset:4:5 to :4:8
        %9 = dbg_stmt(2, 8)
        %10 = field_ptr(%8, "debug") node_offset:4:5 to :4:14
        %11 = dbg_stmt(2, 14)
        %12 = dbg_stmt(2, 20)
        %13 = field_call(nodiscard .auto, %10, "print", [
          {
            %14 = str("Hello, world!")
            %15 = break_inline(%13, %14)
          },
          {
            %16 = struct_init_empty_result(%13) node_offset:4:38 to :4:41
            %17 = break_inline(%13, %16)
          },
        ]) node_offset:4:5 to :4:42
        %18 = dbg_block_end()
        %19 = restore_err_ret_index(%5.none)
        %20 = break(%5, @void_value)
      }) node_offset:3:21 to :3:21
      %21 = restore_err_ret_index(.none.none)
      %22 = ret_implicit(@void_value) token_offset:5:1 to :5:1
    }) (lbrace=1:21,rbrace=3:1) node_offset:3:1 to :3:7
    %24 = break_inline(%4, %23)
  }) node_offset:3:1 to :3:20
AIR
  %0!= save_err_return_trace_index()
  %2!= dbg_block_begin()
  %3!= dbg_stmt(2:20)
  %4!= call(<fn (@TypeOf(.{})) void, (function 'print__anon_2997')>, [@Air.Inst.Ref.empty_struct])
  %5!= dbg_block_end()
  %7!= ret(<@typeInfo(@typeInfo(@TypeOf(test.main)).Fn.return_type.?).ErrorUnion.error_set!void, {}>)

In particular, in this example, you can see how the generic call to std.debug.print has been monomorphized down to a call to print__anon_2997.

11 Likes