Commit 2a990d6966

Andrew Kelley <andrew@ziglang.org>
2021-05-28 01:32:35
stage1: rework tokenizer to match stage2
* Extracts AstGen logic from ir.cpp into astgen.cpp. Reduces the largest file of stage1 from 33,551 lines to 25,510. * tokenizer: rework it completely to match the stage2 tokenizer logic. They can now be maintained together; when one is changed, the other can be changed in the same way. - Each token now takes up 13 bytes instead of 64 bytes. The tokenizer does not parse char literals, string literals, integer literals, etc into meaningful data. Instead, that happens during parsing or astgen. - no longer store line offsets. Error messages scan source files to find the line/column as needed (same as stage2). - main loop: instead of checking the loop, handle a null byte explicitly in the switch statements. This is a nice improvement that we may want to backport to stage2. - delete some dead tokens, artifacts of past syntax that no longer exists. * Parser: fix a TODO by parsing builtin functions as tokens rather than `@` as a separate token. This is how stage2 does it. * Remove some debugging infrastructure. These will need to be redone, if at all, as the code migrates to match stage2. - remove the ast_render code. - remove the IR debugging stuff - remove teh token printing code
1 parent 673ae5b
lib/std/zig/string_literal.zig
@@ -107,6 +107,7 @@ pub fn parseAppend(buf: *std.ArrayList(u8), bytes: []const u8) error{OutOfMemory
                             const hex_str = slice[index + 2 .. index_end];
                             if (std.fmt.parseUnsigned(u32, hex_str, 16)) |uint| {
                                 if (uint <= 0x10ffff) {
+                                    // TODO this incorrectly depends on endianness
                                     try buf.appendSlice(std.mem.toBytes(uint)[0..]);
                                     state = State.Start;
                                     index = index_end; // loop-header increments
src/stage1/all_types.hpp
@@ -670,7 +670,7 @@ enum NodeType {
     NodeTypeIntLiteral,
     NodeTypeStringLiteral,
     NodeTypeCharLiteral,
-    NodeTypeSymbol,
+    NodeTypeIdentifier,
     NodeTypePrefixOpExpr,
     NodeTypePointerType,
     NodeTypeFnCallExpr,
@@ -710,6 +710,7 @@ enum NodeType {
     NodeTypeAwaitExpr,
     NodeTypeSuspend,
     NodeTypeAnyFrameType,
+    // main_token points to the identifier.
     NodeTypeEnumLiteral,
     NodeTypeAnyTypeField,
 };
@@ -733,7 +734,8 @@ struct AstNodeFnProto {
     AstNode *section_expr;
     // populated if the "callconv(S)" is present
     AstNode *callconv_expr;
-    Buf doc_comments;
+
+    TokenIndex doc_comments;
 
     // This is set based only on the existence of a noinline or inline keyword.
     // This is then resolved to an is_noinline bool and (potentially .Inline)
@@ -755,8 +757,8 @@ struct AstNodeFnDef {
 struct AstNodeParamDecl {
     Buf *name;
     AstNode *type;
-    Token *anytype_token;
-    Buf doc_comments;
+    TokenIndex doc_comments;
+    TokenIndex anytype_token;
     bool is_noalias;
     bool is_comptime;
     bool is_var_args;
@@ -799,9 +801,9 @@ struct AstNodeVariableDeclaration {
     AstNode *align_expr;
     // populated if the "section(S)" is present
     AstNode *section_expr;
-    Token *threadlocal_tok;
-    Buf doc_comments;
+    TokenIndex doc_comments;
 
+    TokenIndex threadlocal_tok;
     VisibMod visib_mod;
     bool is_const;
     bool is_comptime;
@@ -935,13 +937,14 @@ struct AstNodePrefixOpExpr {
 };
 
 struct AstNodePointerType {
-    Token *star_token;
+    TokenIndex star_token;
+    TokenIndex allow_zero_token;
+    TokenIndex bit_offset_start;
+    TokenIndex host_int_bytes;
+
     AstNode *sentinel;
     AstNode *align_expr;
-    BigInt *bit_offset_start;
-    BigInt *host_int_bytes;
     AstNode *op_expr;
-    Token *allow_zero_token;
     bool is_const;
     bool is_volatile;
 };
@@ -956,7 +959,7 @@ struct AstNodeArrayType {
     AstNode *sentinel;
     AstNode *child_type;
     AstNode *align_expr;
-    Token *allow_zero_token;
+    TokenIndex allow_zero_token;
     bool is_const;
     bool is_volatile;
 };
@@ -974,11 +977,11 @@ struct AstNodeIfBoolExpr {
 
 struct AstNodeTryExpr {
     Buf *var_symbol;
-    bool var_is_ptr;
     AstNode *target_node;
     AstNode *then_node;
     AstNode *else_node;
     Buf *err_symbol;
+    bool var_is_ptr;
 };
 
 struct AstNodeTestExpr {
@@ -993,12 +996,12 @@ struct AstNodeWhileExpr {
     Buf *name;
     AstNode *condition;
     Buf *var_symbol;
-    bool var_is_ptr;
     AstNode *continue_expr;
     AstNode *body;
     AstNode *else_node;
     Buf *err_symbol;
     bool is_inline;
+    bool var_is_ptr;
 };
 
 struct AstNodeForExpr {
@@ -1070,7 +1073,7 @@ struct AsmToken {
 };
 
 struct AstNodeAsmExpr {
-    Token *volatile_token;
+    TokenIndex volatile_token;
     AstNode *asm_template;
     ZigList<AsmOutput*> output_list;
     ZigList<AsmInput*> input_list;
@@ -1094,7 +1097,7 @@ struct AstNodeContainerDecl {
     AstNode *init_arg_expr; // enum(T), struct(endianness), or union(T), or union(enum(T))
     ZigList<AstNode *> fields;
     ZigList<AstNode *> decls;
-    Buf doc_comments;
+    TokenIndex doc_comments;
 
     ContainerKind kind;
     ContainerLayout layout;
@@ -1103,7 +1106,7 @@ struct AstNodeContainerDecl {
 };
 
 struct AstNodeErrorSetField {
-    Buf doc_comments;
+    TokenIndex doc_comments;
     AstNode *field_name;
 };
 
@@ -1118,28 +1121,8 @@ struct AstNodeStructField {
     AstNode *value;
     // populated if the "align(A)" is present
     AstNode *align_expr;
-    Buf doc_comments;
-    Token *comptime_token;
-};
-
-struct AstNodeStringLiteral {
-    Buf *buf;
-};
-
-struct AstNodeCharLiteral {
-    uint32_t value;
-};
-
-struct AstNodeFloatLiteral {
-    BigFloat *bigfloat;
-
-    // overflow is true if when parsing the number, we discovered it would not
-    // fit without losing data in a double
-    bool overflow;
-};
-
-struct AstNodeIntLiteral {
-    BigInt *bigint;
+    TokenIndex doc_comments;
+    TokenIndex comptime_token;
 };
 
 struct AstNodeStructValueField {
@@ -1158,19 +1141,6 @@ struct AstNodeContainerInitExpr {
     ContainerInitKind kind;
 };
 
-struct AstNodeNullLiteral {
-};
-
-struct AstNodeUndefinedLiteral {
-};
-
-struct AstNodeThisLiteral {
-};
-
-struct AstNodeSymbolExpr {
-    Buf *symbol;
-};
-
 struct AstNodeBoolLiteral {
     bool value;
 };
@@ -1188,13 +1158,6 @@ struct AstNodeContinueExpr {
     Buf *name;
 };
 
-struct AstNodeUnreachableExpr {
-};
-
-
-struct AstNodeErrorType {
-};
-
 struct AstNodeAwaitExpr {
     AstNode *expr;
 };
@@ -1207,16 +1170,10 @@ struct AstNodeAnyFrameType {
     AstNode *payload_type; // can be NULL
 };
 
-struct AstNodeEnumLiteral {
-    Token *period;
-    Token *identifier;
-};
-
 struct AstNode {
     enum NodeType type;
+    TokenIndex main_token;
     bool already_traced_this_node;
-    size_t line;
-    size_t column;
     ZigType *owner;
     union {
         AstNodeFnDef fn_def;
@@ -1252,30 +1209,19 @@ struct AstNode {
         AstNodePtrDerefExpr ptr_deref_expr;
         AstNodeContainerDecl container_decl;
         AstNodeStructField struct_field;
-        AstNodeStringLiteral string_literal;
-        AstNodeCharLiteral char_literal;
-        AstNodeFloatLiteral float_literal;
-        AstNodeIntLiteral int_literal;
         AstNodeContainerInitExpr container_init_expr;
         AstNodeStructValueField struct_val_field;
-        AstNodeNullLiteral null_literal;
-        AstNodeUndefinedLiteral undefined_literal;
-        AstNodeThisLiteral this_literal;
-        AstNodeSymbolExpr symbol_expr;
         AstNodeBoolLiteral bool_literal;
         AstNodeBreakExpr break_expr;
         AstNodeContinueExpr continue_expr;
-        AstNodeUnreachableExpr unreachable_expr;
         AstNodeArrayType array_type;
         AstNodeInferredArrayType inferred_array_type;
-        AstNodeErrorType error_type;
         AstNodeErrorSetDecl err_set_decl;
         AstNodeErrorSetField err_set_field;
         AstNodeResumeExpr resume_expr;
         AstNodeAwaitExpr await_expr;
         AstNodeSuspend suspend;
         AstNodeAnyFrameType anyframe_type;
-        AstNodeEnumLiteral enum_literal;
     } data;
 
     // This is a function for use in the debugger to print
@@ -1409,9 +1355,11 @@ struct ZigPackage {
 struct RootStruct {
     ZigPackage *package;
     Buf *path; // relative to root_package->root_src_dir
-    ZigList<size_t> *line_offsets;
     Buf *source_code;
     ZigLLVMDIFile *di_file;
+    size_t token_count;
+    TokenId *token_ids;
+    TokenLoc *token_locs;
 };
 
 enum StructSpecial {
src/stage1/analyze.cpp
@@ -6,9 +6,9 @@
  */
 
 #include "analyze.hpp"
-#include "ast_render.hpp"
 #include "codegen.hpp"
 #include "error.hpp"
+#include "astgen.hpp"
 #include "ir.hpp"
 #include "ir_print.hpp"
 #include "os.hpp"
@@ -41,41 +41,44 @@ static bool is_top_level_struct(ZigType *import) {
     return import->id == ZigTypeIdStruct && import->data.structure.root_struct != nullptr;
 }
 
-static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ZigType *owner, Token *token, Buf *msg) {
+static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ZigType *owner,
+    TokenIndex token, Buf *msg)
+{
     assert(is_top_level_struct(owner));
     RootStruct *root_struct = owner->data.structure.root_struct;
-
-    ErrorMsg *err = err_msg_create_with_line(root_struct->path, token->start_line, token->start_column,
-            root_struct->source_code, root_struct->line_offsets, msg);
+    uint32_t byte_offset = root_struct->token_locs[token].offset;
+    ErrorMsg *err = err_msg_create_with_offset(root_struct->path, byte_offset,
+        buf_ptr(root_struct->source_code), msg);
 
     err_msg_add_note(parent_msg, err);
     return err;
 }
 
-ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, Token *token, Buf *msg) {
+ErrorMsg *add_token_error_offset(CodeGen *g, ZigType *owner, TokenIndex token, Buf *msg,
+        uint32_t bad_index)
+{
     assert(is_top_level_struct(owner));
     RootStruct *root_struct = owner->data.structure.root_struct;
-    ErrorMsg *err = err_msg_create_with_line(root_struct->path, token->start_line, token->start_column,
-            root_struct->source_code, root_struct->line_offsets, msg);
+    uint32_t byte_offset = root_struct->token_locs[token].offset + bad_index;
+    ErrorMsg *err = err_msg_create_with_offset(root_struct->path, byte_offset,
+        buf_ptr(root_struct->source_code), msg);
 
     g->errors.append(err);
     g->trace_err = err;
     return err;
 }
 
+ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, TokenIndex token, Buf *msg) {
+    return add_token_error_offset(g, owner, token, msg, 0);
+}
+
 ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
-    Token fake_token;
-    fake_token.start_line = node->line;
-    fake_token.start_column = node->column;
     node->already_traced_this_node = true;
-    return add_token_error(g, node->owner, &fake_token, msg);
+    return add_token_error(g, node->owner, node->main_token, msg);
 }
 
 ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, const AstNode *node, Buf *msg) {
-    Token fake_token;
-    fake_token.start_line = node->line;
-    fake_token.start_column = node->column;
-    return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg);
+    return add_error_note_token(g, parent_msg, node->owner, node->main_token, msg);
 }
 
 ZigType *new_type_table_entry(ZigTypeId id) {
@@ -913,13 +916,27 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
     return entry;
 }
 
-ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *full_name, Buf *bare_name) {
+static uint32_t node_line_onebased(AstNode *node) {
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    assert(node->main_token < root_struct->token_count);
+    return root_struct->token_locs[node->main_token].line + 1;
+}
+
+static uint32_t node_column_onebased(AstNode *node) {
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    assert(node->main_token < root_struct->token_count);
+    return root_struct->token_locs[node->main_token].column + 1;
+}
+
+ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *full_name,
+    Buf *bare_name)
+{
     ZigType *entry = new_type_table_entry(ZigTypeIdOpaque);
 
     buf_init_from_str(&entry->name, full_name);
 
     ZigType *import = scope ? get_scope_import(scope) : nullptr;
-    unsigned line = source_node ? (unsigned)(source_node->line + 1) : 0;
+    unsigned line = source_node ? node_line_onebased(source_node) : 0;
 
     // Note: duplicated in get_partial_container_type
     entry->llvm_type = LLVMInt8Type();
@@ -1142,7 +1159,7 @@ ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind
             break;
         case ContainerKindOpaque: {
             ZigType *import = scope ? get_scope_import(scope) : nullptr;
-            unsigned line = decl_node ? (unsigned)(decl_node->line + 1) : 0;
+            unsigned line = decl_node ? node_line_onebased(decl_node) : 0;
             // Note: duplicated in get_opaque_type
             entry->llvm_type = LLVMInt8Type();
             entry->llvm_di_type = ZigLLVMCreateDebugForwardDeclType(g->dbuilder,
@@ -1584,8 +1601,9 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV
 ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
     Error err;
     // Hot path for simple identifiers, to avoid unnecessary memory allocations.
-    if (node->type == NodeTypeSymbol) {
-        Buf *variable_name = node->data.symbol_expr.symbol;
+    if (node->type == NodeTypeIdentifier) {
+        RootStruct *root_struct = node->owner->data.structure.root_struct;
+        Buf *variable_name = token_identifier_buf(root_struct, node->main_token);
         if (buf_eql_str(variable_name, "_"))
             goto abort_hot_path;
         ZigType *primitive_type;
@@ -2034,7 +2052,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
                         buf_sprintf("var args only allowed in functions with C calling convention"));
                 return g->builtin_types.entry_invalid;
             }
-        } else if (param_node->data.param_decl.anytype_token != nullptr) {
+        } else if (param_node->data.param_decl.anytype_token != 0) {
             if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
                 add_node_error(g, param_node,
                         buf_sprintf("parameter of type 'anytype' not allowed in function with calling convention '%s'",
@@ -3021,7 +3039,7 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
             field_node = decl_node->data.container_decl.fields.at(i);
             type_struct_field->name = field_node->data.struct_field.name;
             type_struct_field->decl_node = field_node;
-            if (field_node->data.struct_field.comptime_token != nullptr) {
+            if (field_node->data.struct_field.comptime_token != 0) {
                 if (field_node->data.struct_field.value == nullptr) {
                     add_token_error(g, field_node->owner,
                         field_node->data.struct_field.comptime_token,
@@ -4042,7 +4060,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeUndefinedLiteral:
-        case NodeTypeSymbol:
+        case NodeTypeIdentifier:
         case NodeTypePrefixOpExpr:
         case NodeTypePointerType:
         case NodeTypeIfBoolExpr:
@@ -4238,7 +4256,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
     bool is_const = var_decl->is_const;
     bool is_extern = var_decl->is_extern;
     bool is_export = var_decl->is_export;
-    bool is_thread_local = var_decl->threadlocal_tok != nullptr;
+    bool is_thread_local = var_decl->threadlocal_tok != 0;
 
     ZigType *explicit_type = nullptr;
     if (var_decl->type) {
@@ -5225,8 +5243,6 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) {
     }
 
     if (g->verbose_ir) {
-        fprintf(stderr, "\n");
-        ast_render(stderr, fn_table_entry->body_node, 4);
         fprintf(stderr, "\nfn %s() { // (IR)\n", buf_ptr(&fn_table_entry->symbol_name));
         ir_print_src(g, stderr, fn_table_entry->ir_executable, 4);
         fprintf(stderr, "}\n");
@@ -5238,33 +5254,17 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) {
 ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Buf *source_code,
         SourceKind source_kind)
 {
-    if (g->verbose_tokenize) {
-        fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(resolved_path));
-        fprintf(stderr, "----------------\n");
-        fprintf(stderr, "%s\n", buf_ptr(source_code));
-
-        fprintf(stderr, "\nTokens:\n");
-        fprintf(stderr, "---------\n");
-    }
-
     Tokenization tokenization = {0};
-    tokenize(source_code, &tokenization);
+    tokenize(buf_ptr(source_code), &tokenization);
 
     if (tokenization.err) {
-        ErrorMsg *err = err_msg_create_with_line(resolved_path, tokenization.err_line, tokenization.err_column,
-                source_code, tokenization.line_offsets, tokenization.err);
+        ErrorMsg *err = err_msg_create_with_offset(resolved_path, tokenization.err_byte_offset,
+                buf_ptr(source_code), tokenization.err);
 
         print_err_msg(err, g->err_color);
         exit(1);
     }
 
-    if (g->verbose_tokenize) {
-        print_tokens(source_code, tokenization.tokens);
-
-        fprintf(stderr, "\nAST:\n");
-        fprintf(stderr, "------\n");
-    }
-
     Buf *src_dirname = buf_alloc();
     Buf *src_basename = buf_alloc();
     os_path_split(resolved_path, src_dirname, src_basename);
@@ -5297,9 +5297,20 @@ ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Bu
     RootStruct *root_struct = heap::c_allocator.create<RootStruct>();
     root_struct->package = package;
     root_struct->source_code = source_code;
-    root_struct->line_offsets = tokenization.line_offsets;
     root_struct->path = resolved_path;
     root_struct->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname));
+
+    assert(tokenization.ids.length == tokenization.locs.length);
+    size_t token_count = tokenization.ids.length;
+    root_struct->token_count = token_count;
+    root_struct->token_ids = g->pass1_arena->allocate<TokenId>(token_count);
+    memcpy(root_struct->token_ids, tokenization.ids.items, token_count * sizeof(TokenId));
+    root_struct->token_locs = g->pass1_arena->allocate<TokenLoc>(token_count);
+    memcpy(root_struct->token_locs, tokenization.locs.items, token_count * sizeof(TokenLoc));
+
+    tokenization.ids.deinit();
+    tokenization.locs.deinit();
+
     ZigType *import_entry = get_root_container_type(g, buf_ptr(namespace_name), bare_name, root_struct);
     if (source_kind == SourceKindRoot) {
         assert(g->root_import == nullptr);
@@ -5307,14 +5318,11 @@ ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Bu
     }
     g->import_table.put(resolved_path, import_entry);
 
-    AstNode *root_node = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color);
+    AstNode *root_node = ast_parse(source_code, import_entry, g->err_color);
     assert(root_node != nullptr);
     assert(root_node->type == NodeTypeContainerDecl);
     import_entry->data.structure.decl_node = root_node;
     import_entry->data.structure.decls_scope->base.source_node = root_node;
-    if (g->verbose_ast) {
-        ast_print(stderr, root_node, 0);
-    }
 
     for (size_t decl_i = 0; decl_i < root_node->data.container_decl.decls.length; decl_i += 1) {
         AstNode *top_level_decl = root_node->data.container_decl.decls.at(decl_i);
@@ -6254,9 +6262,12 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) {
     zig_unreachable();
 }
 
-void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str) {
+void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str, bool move_str) {
     auto entry = g->string_literals_table.maybe_get(str);
     if (entry != nullptr) {
+        if (move_str) {
+            buf_destroy(str);
+        }
         memcpy(const_val, entry->value, sizeof(ZigValue));
         return;
     }
@@ -6280,7 +6291,7 @@ void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str) {
 
 ZigValue *create_const_str_lit(CodeGen *g, Buf *str) {
     ZigValue *const_val = g->pass1_arena->create<ZigValue>();
-    init_const_str_lit(g, const_val, str);
+    init_const_str_lit(g, const_val, str, false);
     return const_val;
 }
 
@@ -8626,7 +8637,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
         ZigType *import = get_scope_import(scope);
         di_file = import->data.structure.root_struct->di_file;
         di_scope = ZigLLVMFileToScope(di_file);
-        line = decl_node->line + 1;
+        line = node_line_onebased(decl_node);
     } else {
         di_file = nullptr;
         di_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
@@ -8830,7 +8841,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
         unsigned line;
         if (decl_node != nullptr) {
             AstNode *field_node = field->decl_node;
-            line = field_node->line + 1;
+            line = node_line_onebased(field_node);
         } else {
             line = 0;
         }
@@ -8881,7 +8892,7 @@ static ZigLLVMDIType *make_empty_namespace_llvm_di_type(CodeGen *g, ZigType *imp
     return ZigLLVMCreateDebugStructType(g->dbuilder,
         ZigLLVMFileToScope(import->data.structure.root_struct->di_file),
         name,
-        import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
+        import->data.structure.root_struct->di_file, node_line_onebased(decl_node),
         debug_size_in_bits,
         debug_align_in_bits,
         ZigLLVM_DIFlags_Zero,
@@ -8926,7 +8937,7 @@ static void resolve_llvm_types_enum(CodeGen *g, ZigType *enum_type, ResolveStatu
     uint64_t tag_debug_align_in_bits = 8*tag_int_type->abi_align;
     ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
             ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&enum_type->name),
-            import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
+            import->data.structure.root_struct->di_file, node_line_onebased(decl_node),
             tag_debug_size_in_bits,
             tag_debug_align_in_bits,
             di_enumerators, field_count,
@@ -8966,12 +8977,12 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta
 
     if (union_type->data.unionation.resolve_status < ResolveStatusLLVMFwdDecl) {
         union_type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&union_type->name));
-        size_t line = decl_node ? decl_node->line : 0;
+        unsigned line = decl_node ? node_line_onebased(decl_node) : 0;
         unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
         union_type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
             dwarf_kind, buf_ptr(&union_type->name),
             ZigLLVMFileToScope(import->data.structure.root_struct->di_file),
-            import->data.structure.root_struct->di_file, (unsigned)(line + 1));
+            import->data.structure.root_struct->di_file, line);
 
         union_type->data.unionation.resolve_status = ResolveStatusLLVMFwdDecl;
         if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
@@ -8992,7 +9003,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta
         AstNode *field_node = union_field->decl_node;
         union_inner_di_types[union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
                 ZigLLVMTypeToScope(union_type->llvm_di_type), buf_ptr(union_field->enum_field->name),
-                import->data.structure.root_struct->di_file, (unsigned)(field_node->line + 1),
+                import->data.structure.root_struct->di_file, node_line_onebased(field_node),
                 store_size_in_bits,
                 abi_align_in_bits,
                 0,
@@ -9022,7 +9033,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta
         // create debug type for union
         ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
             ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&union_type->name),
-            import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
+            import->data.structure.root_struct->di_file, node_line_onebased(decl_node),
             union_type->data.unionation.union_abi_size * 8,
             most_aligned_union_member->align * 8,
             ZigLLVM_DIFlags_Zero, union_inner_di_types,
@@ -9057,7 +9068,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta
     // create debug type for union
     ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
             ZigLLVMTypeToScope(union_type->llvm_di_type), "AnonUnion",
-            import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
+            import->data.structure.root_struct->di_file, node_line_onebased(decl_node),
             most_aligned_union_member->type_entry->size_in_bits, 8*most_aligned_union_member->align,
             ZigLLVM_DIFlags_Zero, union_inner_di_types, gen_field_count, 0, "");
 
@@ -9068,7 +9079,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta
 
     ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
             ZigLLVMTypeToScope(union_type->llvm_di_type), "payload",
-            import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
+            import->data.structure.root_struct->di_file, node_line_onebased(decl_node),
             most_aligned_union_member->type_entry->size_in_bits,
             8*most_aligned_union_member->align,
             union_offset_in_bits,
@@ -9079,7 +9090,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta
 
     ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
             ZigLLVMTypeToScope(union_type->llvm_di_type), "tag",
-            import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
+            import->data.structure.root_struct->di_file, node_line_onebased(decl_node),
             tag_debug_size_in_bits,
             tag_debug_align_in_bits,
             tag_offset_in_bits,
@@ -9094,7 +9105,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta
     ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
             ZigLLVMFileToScope(import->data.structure.root_struct->di_file),
             buf_ptr(&union_type->name),
-            import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1),
+            import->data.structure.root_struct->di_file, node_line_onebased(decl_node),
             debug_size_in_bits,
             debug_align_in_bits,
             ZigLLVM_DIFlags_Zero, nullptr, di_root_members, 2, 0, nullptr, "");
@@ -9774,9 +9785,9 @@ void src_assert_impl(bool ok, AstNode *source_node, char const *file, unsigned i
     if (source_node == nullptr) {
         fprintf(stderr, "when analyzing (unknown source location) ");
     } else {
-        fprintf(stderr, "when analyzing %s:%u:%u ",
-            buf_ptr(source_node->owner->data.structure.root_struct->path),
-            (unsigned)source_node->line + 1, (unsigned)source_node->column + 1);
+        RootStruct *root_struct = source_node->owner->data.structure.root_struct;
+        fprintf(stderr, "when analyzing %s:%u:%u ", buf_ptr(root_struct->path),
+                node_line_onebased(source_node), node_column_onebased(source_node));
     }
     fprintf(stderr, "in compiler source at %s:%u: ", file, line);
     const char *msg = "assertion failed. This is a bug in the Zig compiler.";
@@ -9842,6 +9853,14 @@ Error analyze_import(CodeGen *g, ZigType *source_import, Buf *import_target_str,
     return ErrorNone;
 }
 
+void AstNode::src() {
+    RootStruct *root_struct = this->owner->data.structure.root_struct;
+    uint32_t line = root_struct->token_locs[this->main_token].line + 1;
+    uint32_t column = root_struct->token_locs[this->main_token].column + 1;
+    fprintf(stderr, "%s:%" PRIu32 ":%" PRIu32 "\n",
+            buf_ptr(root_struct->path),
+            line, column);
+}
 
 void IrExecutableSrc::src() {
     if (this->source_node != nullptr) {
src/stage1/analyze.hpp
@@ -12,7 +12,9 @@
 
 void semantic_analyze(CodeGen *g);
 ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
-ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, Token *token, Buf *msg);
+ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, TokenIndex token, Buf *msg);
+ErrorMsg *add_token_error_offset(CodeGen *g, ZigType *owner, TokenIndex token, Buf *msg,
+        uint32_t bad_index);
 ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, const AstNode *node, Buf *msg);
 ZigType *new_type_table_entry(ZigTypeId id);
 ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn);
@@ -140,7 +142,7 @@ Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstSrc
 Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent);
 ScopeExpr *create_expr_scope(CodeGen *g, AstNode *node, Scope *parent);
 
-void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str);
+void init_const_str_lit(CodeGen *g, ZigValue *const_val, Buf *str, bool move_str);
 ZigValue *create_const_str_lit(CodeGen *g, Buf *str);
 
 void init_const_bigint(ZigValue *const_val, ZigType *type, const BigInt *bigint);
src/stage1/ast_render.cpp
@@ -1,1241 +0,0 @@
-/*
- * Copyright (c) 2016 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#include "analyze.hpp"
-#include "ast_render.hpp"
-#include "os.hpp"
-
-#include <stdio.h>
-
-static const char *bin_op_str(BinOpType bin_op) {
-    switch (bin_op) {
-        case BinOpTypeInvalid:                return "(invalid)";
-        case BinOpTypeBoolOr:                 return "or";
-        case BinOpTypeBoolAnd:                return "and";
-        case BinOpTypeCmpEq:                  return "==";
-        case BinOpTypeCmpNotEq:               return "!=";
-        case BinOpTypeCmpLessThan:            return "<";
-        case BinOpTypeCmpGreaterThan:         return ">";
-        case BinOpTypeCmpLessOrEq:            return "<=";
-        case BinOpTypeCmpGreaterOrEq:         return ">=";
-        case BinOpTypeBinOr:                  return "|";
-        case BinOpTypeBinXor:                 return "^";
-        case BinOpTypeBinAnd:                 return "&";
-        case BinOpTypeBitShiftLeft:           return "<<";
-        case BinOpTypeBitShiftRight:          return ">>";
-        case BinOpTypeAdd:                    return "+";
-        case BinOpTypeAddWrap:                return "+%";
-        case BinOpTypeSub:                    return "-";
-        case BinOpTypeSubWrap:                return "-%";
-        case BinOpTypeMult:                   return "*";
-        case BinOpTypeMultWrap:               return "*%";
-        case BinOpTypeDiv:                    return "/";
-        case BinOpTypeMod:                    return "%";
-        case BinOpTypeAssign:                 return "=";
-        case BinOpTypeAssignTimes:            return "*=";
-        case BinOpTypeAssignTimesWrap:        return "*%=";
-        case BinOpTypeAssignDiv:              return "/=";
-        case BinOpTypeAssignMod:              return "%=";
-        case BinOpTypeAssignPlus:             return "+=";
-        case BinOpTypeAssignPlusWrap:         return "+%=";
-        case BinOpTypeAssignMinus:            return "-=";
-        case BinOpTypeAssignMinusWrap:        return "-%=";
-        case BinOpTypeAssignBitShiftLeft:     return "<<=";
-        case BinOpTypeAssignBitShiftRight:    return ">>=";
-        case BinOpTypeAssignBitAnd:           return "&=";
-        case BinOpTypeAssignBitXor:           return "^=";
-        case BinOpTypeAssignBitOr:            return "|=";
-        case BinOpTypeUnwrapOptional:         return "orelse";
-        case BinOpTypeArrayCat:               return "++";
-        case BinOpTypeArrayMult:              return "**";
-        case BinOpTypeErrorUnion:             return "!";
-        case BinOpTypeMergeErrorSets:         return "||";
-    }
-    zig_unreachable();
-}
-
-static const char *prefix_op_str(PrefixOp prefix_op) {
-    switch (prefix_op) {
-        case PrefixOpInvalid: return "(invalid)";
-        case PrefixOpNegation: return "-";
-        case PrefixOpNegationWrap: return "-%";
-        case PrefixOpBoolNot: return "!";
-        case PrefixOpBinNot: return "~";
-        case PrefixOpOptional: return "?";
-        case PrefixOpAddrOf: return "&";
-    }
-    zig_unreachable();
-}
-
-static const char *visib_mod_string(VisibMod mod) {
-    switch (mod) {
-        case VisibModPub: return "pub ";
-        case VisibModPrivate: return "";
-    }
-    zig_unreachable();
-}
-
-static const char *return_string(ReturnKind kind) {
-    switch (kind) {
-        case ReturnKindUnconditional: return "return";
-        case ReturnKindError: return "try";
-    }
-    zig_unreachable();
-}
-
-static const char *defer_string(ReturnKind kind) {
-    switch (kind) {
-        case ReturnKindUnconditional: return "defer";
-        case ReturnKindError: return "errdefer";
-    }
-    zig_unreachable();
-}
-
-static const char *layout_string(ContainerLayout layout) {
-    switch (layout) {
-        case ContainerLayoutAuto: return "";
-        case ContainerLayoutExtern: return "extern ";
-        case ContainerLayoutPacked: return "packed ";
-    }
-    zig_unreachable();
-}
-
-static const char *extern_string(bool is_extern) {
-    return is_extern ? "extern " : "";
-}
-
-static const char *export_string(bool is_export) {
-    return is_export ? "export " : "";
-}
-
-//static const char *calling_convention_string(CallingConvention cc) {
-//    switch (cc) {
-//        case CallingConventionUnspecified: return "";
-//        case CallingConventionC: return "extern ";
-//        case CallingConventionCold: return "coldcc ";
-//        case CallingConventionNaked: return "nakedcc ";
-//        case CallingConventionStdcall: return "stdcallcc ";
-//    }
-//    zig_unreachable();
-//}
-
-static const char *inline_string(FnInline fn_inline) {
-    switch (fn_inline) {
-        case FnInlineAlways: return "inline ";
-        case FnInlineNever:  return "noinline ";
-        case FnInlineAuto:   return "";
-    }
-    zig_unreachable();
-}
-
-static const char *const_or_var_string(bool is_const) {
-    return is_const ? "const" : "var";
-}
-
-static const char *thread_local_string(Token *tok) {
-    return (tok == nullptr) ? "" : "threadlocal ";
-}
-
-static const char *token_to_ptr_len_str(Token *tok) {
-    assert(tok != nullptr);
-    switch (tok->id) {
-        case TokenIdStar:
-        case TokenIdStarStar:
-            return "*";
-        case TokenIdLBracket:
-            return "[*]";
-        case TokenIdSymbol:
-            return "[*c]";
-        default:
-            zig_unreachable();
-    }
-}
-
-static const char *node_type_str(NodeType node_type) {
-    switch (node_type) {
-        case NodeTypeFnDef:
-            return "FnDef";
-        case NodeTypeFnProto:
-            return "FnProto";
-        case NodeTypeParamDecl:
-            return "ParamDecl";
-        case NodeTypeBlock:
-            return "Block";
-        case NodeTypeGroupedExpr:
-            return "Parens";
-        case NodeTypeBinOpExpr:
-            return "BinOpExpr";
-        case NodeTypeCatchExpr:
-            return "CatchExpr";
-        case NodeTypeFnCallExpr:
-            return "FnCallExpr";
-        case NodeTypeArrayAccessExpr:
-            return "ArrayAccessExpr";
-        case NodeTypeSliceExpr:
-            return "SliceExpr";
-        case NodeTypeReturnExpr:
-            return "ReturnExpr";
-        case NodeTypeDefer:
-            return "Defer";
-        case NodeTypeVariableDeclaration:
-            return "VariableDeclaration";
-        case NodeTypeTestDecl:
-            return "TestDecl";
-        case NodeTypeIntLiteral:
-            return "IntLiteral";
-        case NodeTypeFloatLiteral:
-            return "FloatLiteral";
-        case NodeTypeStringLiteral:
-            return "StringLiteral";
-        case NodeTypeCharLiteral:
-            return "CharLiteral";
-        case NodeTypeSymbol:
-            return "Symbol";
-        case NodeTypePrefixOpExpr:
-            return "PrefixOpExpr";
-        case NodeTypeUsingNamespace:
-            return "UsingNamespace";
-        case NodeTypeBoolLiteral:
-            return "BoolLiteral";
-        case NodeTypeNullLiteral:
-            return "NullLiteral";
-        case NodeTypeUndefinedLiteral:
-            return "UndefinedLiteral";
-        case NodeTypeIfBoolExpr:
-            return "IfBoolExpr";
-        case NodeTypeWhileExpr:
-            return "WhileExpr";
-        case NodeTypeForExpr:
-            return "ForExpr";
-        case NodeTypeSwitchExpr:
-            return "SwitchExpr";
-        case NodeTypeSwitchProng:
-            return "SwitchProng";
-        case NodeTypeSwitchRange:
-            return "SwitchRange";
-        case NodeTypeCompTime:
-            return "CompTime";
-        case NodeTypeNoSuspend:
-            return "NoSuspend";
-        case NodeTypeBreak:
-            return "Break";
-        case NodeTypeContinue:
-            return "Continue";
-        case NodeTypeUnreachable:
-            return "Unreachable";
-        case NodeTypeAsmExpr:
-            return "AsmExpr";
-        case NodeTypeFieldAccessExpr:
-            return "FieldAccessExpr";
-        case NodeTypePtrDeref:
-            return "PtrDerefExpr";
-        case NodeTypeUnwrapOptional:
-            return "UnwrapOptional";
-        case NodeTypeContainerDecl:
-            return "ContainerDecl";
-        case NodeTypeStructField:
-            return "StructField";
-        case NodeTypeStructValueField:
-            return "StructValueField";
-        case NodeTypeContainerInitExpr:
-            return "ContainerInitExpr";
-        case NodeTypeArrayType:
-            return "ArrayType";
-        case NodeTypeInferredArrayType:
-            return "InferredArrayType";
-        case NodeTypeErrorType:
-            return "ErrorType";
-        case NodeTypeIfErrorExpr:
-            return "IfErrorExpr";
-        case NodeTypeIfOptional:
-            return "IfOptional";
-        case NodeTypeErrorSetDecl:
-            return "ErrorSetDecl";
-        case NodeTypeResume:
-            return "Resume";
-        case NodeTypeAwaitExpr:
-            return "AwaitExpr";
-        case NodeTypeSuspend:
-            return "Suspend";
-        case NodeTypePointerType:
-            return "PointerType";
-        case NodeTypeAnyFrameType:
-            return "AnyFrameType";
-        case NodeTypeEnumLiteral:
-            return "EnumLiteral";
-        case NodeTypeErrorSetField:
-            return "ErrorSetField";
-        case NodeTypeAnyTypeField:
-            return "AnyTypeField";
-    }
-    zig_unreachable();
-}
-
-struct AstPrint {
-    int indent;
-    FILE *f;
-};
-
-static void ast_print_visit(AstNode **node_ptr, void *context) {
-    AstNode *node = *node_ptr;
-    AstPrint *ap = (AstPrint *)context;
-
-    for (int i = 0; i < ap->indent; i += 1) {
-        fprintf(ap->f, " ");
-    }
-
-    fprintf(ap->f, "%s\n", node_type_str(node->type));
-
-    AstPrint new_ap;
-    new_ap.indent = ap->indent + 2;
-    new_ap.f = ap->f;
-
-    ast_visit_node_children(node, ast_print_visit, &new_ap);
-}
-
-void ast_print(FILE *f, AstNode *node, int indent) {
-    AstPrint ap;
-    ap.indent = indent;
-    ap.f = f;
-    ast_visit_node_children(node, ast_print_visit, &ap);
-}
-
-
-struct AstRender {
-    int indent;
-    int indent_size;
-    FILE *f;
-};
-
-static void print_indent(AstRender *ar) {
-    for (int i = 0; i < ar->indent; i += 1) {
-        fprintf(ar->f, " ");
-    }
-}
-
-static bool is_alpha_under(uint8_t c) {
-    return (c >= 'a' && c <= 'z') ||
-        (c >= 'A' && c <= 'Z') || c == '_';
-}
-
-static bool is_digit(uint8_t c) {
-    return (c >= '0' && c <= '9');
-}
-
-static bool is_printable(uint8_t c) {
-    if (c == 0) {
-        return false;
-    }
-    static const uint8_t printables[] =
-        " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.~`!@#$%^&*()_-+=\\{}[];'\"?/<>,:";
-    for (size_t i = 0; i < array_length(printables); i += 1) {
-        if (c == printables[i]) return true;
-    }
-    return false;
-}
-
-static void string_literal_escape(Buf *source, Buf *dest) {
-    buf_resize(dest, 0);
-    for (size_t i = 0; i < buf_len(source); i += 1) {
-        uint8_t c = *((uint8_t*)buf_ptr(source) + i);
-        if (c == '\'') {
-            buf_append_str(dest, "\\'");
-        } else if (c == '"') {
-            buf_append_str(dest, "\\\"");
-        } else if (c == '\\') {
-            buf_append_str(dest, "\\\\");
-        } else if (c == '\n') {
-            buf_append_str(dest, "\\n");
-        } else if (c == '\r') {
-            buf_append_str(dest, "\\r");
-        } else if (c == '\t') {
-            buf_append_str(dest, "\\t");
-        } else if (is_printable(c)) {
-            buf_append_char(dest, c);
-        } else {
-            buf_appendf(dest, "\\x%02x", (int)c);
-        }
-    }
-}
-
-static bool is_valid_bare_symbol(Buf *symbol) {
-    if (buf_len(symbol) == 0) {
-        return false;
-    }
-    uint8_t first_char = *buf_ptr(symbol);
-    if (!is_alpha_under(first_char)) {
-        return false;
-    }
-    for (size_t i = 1; i < buf_len(symbol); i += 1) {
-        uint8_t c = *((uint8_t*)buf_ptr(symbol) + i);
-        if (!is_alpha_under(c) && !is_digit(c)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-static void print_symbol(AstRender *ar, Buf *symbol) {
-    if (is_zig_keyword(symbol)) {
-        fprintf(ar->f, "@\"%s\"", buf_ptr(symbol));
-        return;
-    }
-    if (is_valid_bare_symbol(symbol)) {
-        fprintf(ar->f, "%s", buf_ptr(symbol));
-        return;
-    }
-    Buf escaped = BUF_INIT;
-    string_literal_escape(symbol, &escaped);
-    fprintf(ar->f, "@\"%s\"", buf_ptr(&escaped));
-}
-
-static bool statement_terminates_without_semicolon(AstNode *node) {
-    switch (node->type) {
-        case NodeTypeIfBoolExpr:
-            if (node->data.if_bool_expr.else_node)
-                return statement_terminates_without_semicolon(node->data.if_bool_expr.else_node);
-            return node->data.if_bool_expr.then_block->type == NodeTypeBlock;
-        case NodeTypeIfErrorExpr:
-            if (node->data.if_err_expr.else_node)
-                return statement_terminates_without_semicolon(node->data.if_err_expr.else_node);
-            return node->data.if_err_expr.then_node->type == NodeTypeBlock;
-        case NodeTypeIfOptional:
-            if (node->data.test_expr.else_node)
-                return statement_terminates_without_semicolon(node->data.test_expr.else_node);
-            return node->data.test_expr.then_node->type == NodeTypeBlock;
-        case NodeTypeWhileExpr:
-            return node->data.while_expr.body->type == NodeTypeBlock;
-        case NodeTypeForExpr:
-            return node->data.for_expr.body->type == NodeTypeBlock;
-        case NodeTypeCompTime:
-            return node->data.comptime_expr.expr->type == NodeTypeBlock;
-        case NodeTypeDefer:
-            return node->data.defer.expr->type == NodeTypeBlock;
-        case NodeTypeSuspend:
-            return node->data.suspend.block != nullptr && node->data.suspend.block->type == NodeTypeBlock;
-        case NodeTypeSwitchExpr:
-        case NodeTypeBlock:
-            return true;
-        default:
-            return false;
-    }
-}
-
-static void render_node_extra(AstRender *ar, AstNode *node, bool grouped);
-
-static void render_node_grouped(AstRender *ar, AstNode *node) {
-    return render_node_extra(ar, node, true);
-}
-
-static void render_node_ungrouped(AstRender *ar, AstNode *node) {
-    return render_node_extra(ar, node, false);
-}
-
-static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
-    switch (node->type) {
-        case NodeTypeSwitchProng:
-        case NodeTypeSwitchRange:
-        case NodeTypeStructValueField:
-            zig_unreachable();
-        case NodeTypeFnProto:
-            {
-                const char *pub_str = visib_mod_string(node->data.fn_proto.visib_mod);
-                const char *extern_str = extern_string(node->data.fn_proto.is_extern);
-                const char *export_str = export_string(node->data.fn_proto.is_export);
-                const char *inline_str = inline_string(node->data.fn_proto.fn_inline);
-                fprintf(ar->f, "%s%s%s%sfn ", pub_str, inline_str, export_str, extern_str);
-                if (node->data.fn_proto.name != nullptr) {
-                    print_symbol(ar, node->data.fn_proto.name);
-                }
-                fprintf(ar->f, "(");
-                size_t arg_count = node->data.fn_proto.params.length;
-                for (size_t arg_i = 0; arg_i < arg_count; arg_i += 1) {
-                    AstNode *param_decl = node->data.fn_proto.params.at(arg_i);
-                    assert(param_decl->type == NodeTypeParamDecl);
-                    if (param_decl->data.param_decl.name != nullptr) {
-                        const char *noalias_str = param_decl->data.param_decl.is_noalias ? "noalias " : "";
-                        const char *inline_str = param_decl->data.param_decl.is_comptime ? "comptime " : "";
-                        fprintf(ar->f, "%s%s", noalias_str, inline_str);
-                        print_symbol(ar, param_decl->data.param_decl.name);
-                        fprintf(ar->f, ": ");
-                    }
-                    if (param_decl->data.param_decl.is_var_args) {
-                        fprintf(ar->f, "...");
-                    } else if (param_decl->data.param_decl.anytype_token != nullptr) {
-                        fprintf(ar->f, "anytype");
-                    } else {
-                        render_node_grouped(ar, param_decl->data.param_decl.type);
-                    }
-
-                    if (arg_i + 1 < arg_count) {
-                        fprintf(ar->f, ", ");
-                    }
-                }
-                if (node->data.fn_proto.is_var_args) {
-                    fprintf(ar->f, ", ...");
-                }
-                fprintf(ar->f, ")");
-                if (node->data.fn_proto.align_expr) {
-                    fprintf(ar->f, " align(");
-                    render_node_grouped(ar, node->data.fn_proto.align_expr);
-                    fprintf(ar->f, ")");
-                }
-                if (node->data.fn_proto.section_expr) {
-                    fprintf(ar->f, " section(");
-                    render_node_grouped(ar, node->data.fn_proto.section_expr);
-                    fprintf(ar->f, ")");
-                }
-                if (node->data.fn_proto.callconv_expr) {
-                    fprintf(ar->f, " callconv(");
-                    render_node_grouped(ar, node->data.fn_proto.callconv_expr);
-                    fprintf(ar->f, ")");
-                }
-
-                AstNode *return_type_node = node->data.fn_proto.return_type;
-                assert(return_type_node != nullptr);
-                fprintf(ar->f, " ");
-                if (node->data.fn_proto.auto_err_set) {
-                    fprintf(ar->f, "!");
-                }
-                render_node_grouped(ar, return_type_node);
-                break;
-            }
-        case NodeTypeFnDef:
-            {
-                render_node_grouped(ar, node->data.fn_def.fn_proto);
-                fprintf(ar->f, " ");
-                render_node_grouped(ar, node->data.fn_def.body);
-                break;
-            }
-        case NodeTypeBlock:
-            if (node->data.block.name != nullptr) {
-                fprintf(ar->f, "%s: ", buf_ptr(node->data.block.name));
-            }
-            if (node->data.block.statements.length == 0) {
-                fprintf(ar->f, "{}");
-                break;
-            }
-            fprintf(ar->f, "{\n");
-            ar->indent += ar->indent_size;
-            for (size_t i = 0; i < node->data.block.statements.length; i += 1) {
-                AstNode *statement = node->data.block.statements.at(i);
-                print_indent(ar);
-                render_node_grouped(ar, statement);
-
-                if (!statement_terminates_without_semicolon(statement))
-                    fprintf(ar->f, ";");
-
-                fprintf(ar->f, "\n");
-            }
-            ar->indent -= ar->indent_size;
-            print_indent(ar);
-            fprintf(ar->f, "}");
-            break;
-        case NodeTypeGroupedExpr:
-            fprintf(ar->f, "(");
-            render_node_ungrouped(ar, node->data.grouped_expr);
-            fprintf(ar->f, ")");
-            break;
-        case NodeTypeReturnExpr:
-            {
-                const char *return_str = return_string(node->data.return_expr.kind);
-                fprintf(ar->f, "%s", return_str);
-                if (node->data.return_expr.expr) {
-                    fprintf(ar->f, " ");
-                    render_node_grouped(ar, node->data.return_expr.expr);
-                }
-                break;
-            }
-        case NodeTypeBreak:
-            {
-                fprintf(ar->f, "break");
-                if (node->data.break_expr.name != nullptr) {
-                    fprintf(ar->f, " :%s", buf_ptr(node->data.break_expr.name));
-                }
-                if (node->data.break_expr.expr) {
-                    fprintf(ar->f, " ");
-                    render_node_grouped(ar, node->data.break_expr.expr);
-                }
-                break;
-            }
-        case NodeTypeDefer:
-            {
-                const char *defer_str = defer_string(node->data.defer.kind);
-                fprintf(ar->f, "%s ", defer_str);
-                render_node_grouped(ar, node->data.defer.expr);
-                break;
-            }
-        case NodeTypeVariableDeclaration:
-            {
-                const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);
-                const char *extern_str = extern_string(node->data.variable_declaration.is_extern);
-                const char *thread_local_str = thread_local_string(node->data.variable_declaration.threadlocal_tok);
-                const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const);
-                fprintf(ar->f, "%s%s%s%s ", pub_str, extern_str, thread_local_str, const_or_var);
-                print_symbol(ar, node->data.variable_declaration.symbol);
-
-                if (node->data.variable_declaration.type) {
-                    fprintf(ar->f, ": ");
-                    render_node_grouped(ar, node->data.variable_declaration.type);
-                }
-                if (node->data.variable_declaration.align_expr) {
-                    fprintf(ar->f, "align(");
-                    render_node_grouped(ar, node->data.variable_declaration.align_expr);
-                    fprintf(ar->f, ") ");
-                }
-                if (node->data.variable_declaration.section_expr) {
-                    fprintf(ar->f, "section(");
-                    render_node_grouped(ar, node->data.variable_declaration.section_expr);
-                    fprintf(ar->f, ") ");
-                }
-                if (node->data.variable_declaration.expr) {
-                    fprintf(ar->f, " = ");
-                    render_node_grouped(ar, node->data.variable_declaration.expr);
-                }
-                break;
-            }
-        case NodeTypeBinOpExpr:
-            if (!grouped) fprintf(ar->f, "(");
-            render_node_ungrouped(ar, node->data.bin_op_expr.op1);
-            fprintf(ar->f, " %s ", bin_op_str(node->data.bin_op_expr.bin_op));
-            render_node_ungrouped(ar, node->data.bin_op_expr.op2);
-            if (!grouped) fprintf(ar->f, ")");
-            break;
-        case NodeTypeFloatLiteral:
-            {
-                Buf rendered_buf = BUF_INIT;
-                buf_resize(&rendered_buf, 0);
-                bigfloat_append_buf(&rendered_buf, node->data.float_literal.bigfloat);
-                fprintf(ar->f, "%s", buf_ptr(&rendered_buf));
-            }
-            break;
-        case NodeTypeIntLiteral:
-            {
-                Buf rendered_buf = BUF_INIT;
-                buf_resize(&rendered_buf, 0);
-                bigint_append_buf(&rendered_buf, node->data.int_literal.bigint, 10);
-                fprintf(ar->f, "%s", buf_ptr(&rendered_buf));
-            }
-            break;
-        case NodeTypeStringLiteral:
-            {
-                Buf tmp_buf = BUF_INIT;
-                string_literal_escape(node->data.string_literal.buf, &tmp_buf);
-                fprintf(ar->f, "\"%s\"", buf_ptr(&tmp_buf));
-            }
-            break;
-        case NodeTypeCharLiteral:
-            {
-                uint8_t c = node->data.char_literal.value;
-                if (c == '\'') {
-                    fprintf(ar->f, "'\\''");
-                } else if (c == '\"') {
-                    fprintf(ar->f, "'\\\"'");
-                } else if (c == '\\') {
-                    fprintf(ar->f, "'\\\\'");
-                } else if (c == '\n') {
-                    fprintf(ar->f, "'\\n'");
-                } else if (c == '\r') {
-                    fprintf(ar->f, "'\\r'");
-                } else if (c == '\t') {
-                    fprintf(ar->f, "'\\t'");
-                } else if (is_printable(c)) {
-                    fprintf(ar->f, "'%c'", c);
-                } else {
-                    fprintf(ar->f, "'\\x%02x'", (int)c);
-                }
-                break;
-            }
-        case NodeTypeSymbol:
-            print_symbol(ar, node->data.symbol_expr.symbol);
-            break;
-        case NodeTypePrefixOpExpr:
-            {
-                if (!grouped) fprintf(ar->f, "(");
-                PrefixOp op = node->data.prefix_op_expr.prefix_op;
-                fprintf(ar->f, "%s", prefix_op_str(op));
-
-                AstNode *child_node = node->data.prefix_op_expr.primary_expr;
-                bool new_grouped = child_node->type == NodeTypePrefixOpExpr || child_node->type == NodeTypePointerType;
-                render_node_extra(ar, child_node, new_grouped);
-                if (!grouped) fprintf(ar->f, ")");
-                break;
-            }
-        case NodeTypePointerType:
-            {
-                if (!grouped) fprintf(ar->f, "(");
-                const char *ptr_len_str = token_to_ptr_len_str(node->data.pointer_type.star_token);
-                fprintf(ar->f, "%s", ptr_len_str);
-                if (node->data.pointer_type.align_expr != nullptr) {
-                    fprintf(ar->f, "align(");
-                    render_node_grouped(ar, node->data.pointer_type.align_expr);
-                    if (node->data.pointer_type.bit_offset_start != nullptr) {
-                        assert(node->data.pointer_type.host_int_bytes != nullptr);
-
-                        Buf offset_start_buf = BUF_INIT;
-                        buf_resize(&offset_start_buf, 0);
-                        bigint_append_buf(&offset_start_buf, node->data.pointer_type.bit_offset_start, 10);
-
-                        Buf offset_end_buf = BUF_INIT;
-                        buf_resize(&offset_end_buf, 0);
-                        bigint_append_buf(&offset_end_buf, node->data.pointer_type.host_int_bytes, 10);
-
-                        fprintf(ar->f, ":%s:%s ", buf_ptr(&offset_start_buf), buf_ptr(&offset_end_buf));
-                    }
-                    fprintf(ar->f, ") ");
-                }
-                if (node->data.pointer_type.is_const) {
-                    fprintf(ar->f, "const ");
-                }
-                if (node->data.pointer_type.is_volatile) {
-                    fprintf(ar->f, "volatile ");
-                }
-
-                render_node_ungrouped(ar, node->data.pointer_type.op_expr);
-                if (!grouped) fprintf(ar->f, ")");
-                break;
-            }
-        case NodeTypeFnCallExpr:
-            {
-                switch (node->data.fn_call_expr.modifier) {
-                    case CallModifierNone:
-                        break;
-                    case CallModifierNoSuspend:
-                        fprintf(ar->f, "nosuspend ");
-                        break;
-                    case CallModifierAsync:
-                        fprintf(ar->f, "async ");
-                        break;
-                    case CallModifierNeverTail:
-                        fprintf(ar->f, "notail ");
-                        break;
-                    case CallModifierNeverInline:
-                        fprintf(ar->f, "noinline ");
-                        break;
-                    case CallModifierAlwaysTail:
-                        fprintf(ar->f, "tail ");
-                        break;
-                    case CallModifierAlwaysInline:
-                        fprintf(ar->f, "inline ");
-                        break;
-                    case CallModifierCompileTime:
-                        fprintf(ar->f, "comptime ");
-                        break;
-                    case CallModifierBuiltin:
-                        fprintf(ar->f, "@");
-                        break;
-                }
-                AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
-                bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypePointerType);
-                render_node_extra(ar, fn_ref_node, grouped);
-                fprintf(ar->f, "(");
-                for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
-                    AstNode *param = node->data.fn_call_expr.params.at(i);
-                    if (i != 0) {
-                        fprintf(ar->f, ", ");
-                    }
-                    render_node_grouped(ar, param);
-                }
-                fprintf(ar->f, ")");
-                break;
-            }
-        case NodeTypeArrayAccessExpr:
-            render_node_ungrouped(ar, node->data.array_access_expr.array_ref_expr);
-            fprintf(ar->f, "[");
-            render_node_grouped(ar, node->data.array_access_expr.subscript);
-            fprintf(ar->f, "]");
-            break;
-        case NodeTypeFieldAccessExpr:
-            {
-                AstNode *lhs = node->data.field_access_expr.struct_expr;
-                Buf *rhs = node->data.field_access_expr.field_name;
-                if (lhs->type == NodeTypeErrorType) {
-                    fprintf(ar->f, "error");
-                } else {
-                    render_node_ungrouped(ar, lhs);
-                }
-                fprintf(ar->f, ".");
-                print_symbol(ar, rhs);
-                break;
-            }
-        case NodeTypePtrDeref:
-            {
-                AstNode *lhs = node->data.ptr_deref_expr.target;
-                render_node_ungrouped(ar, lhs);
-                fprintf(ar->f, ".*");
-                break;
-            }
-        case NodeTypeUnwrapOptional:
-            {
-                AstNode *lhs = node->data.unwrap_optional.expr;
-                render_node_ungrouped(ar, lhs);
-                fprintf(ar->f, ".?");
-                break;
-            }
-        case NodeTypeUndefinedLiteral:
-            fprintf(ar->f, "undefined");
-            break;
-        case NodeTypeContainerDecl:
-            {
-                if (!node->data.container_decl.is_root) {
-                    const char *layout_str = layout_string(node->data.container_decl.layout);
-                    const char *container_str = container_string(node->data.container_decl.kind);
-                    fprintf(ar->f, "%s%s", layout_str, container_str);
-                    if (node->data.container_decl.auto_enum) {
-                        fprintf(ar->f, "(enum");
-                    }
-                    if (node->data.container_decl.init_arg_expr != nullptr) {
-                        fprintf(ar->f, "(");
-                        render_node_grouped(ar, node->data.container_decl.init_arg_expr);
-                        fprintf(ar->f, ")");
-                    }
-                    if (node->data.container_decl.auto_enum) {
-                        fprintf(ar->f, ")");
-                    }
-
-                    fprintf(ar->f, " {\n");
-                    ar->indent += ar->indent_size;
-                }
-                for (size_t field_i = 0; field_i < node->data.container_decl.fields.length; field_i += 1) {
-                    AstNode *field_node = node->data.container_decl.fields.at(field_i);
-                    assert(field_node->type == NodeTypeStructField);
-                    print_indent(ar);
-                    print_symbol(ar, field_node->data.struct_field.name);
-                    if (field_node->data.struct_field.type != nullptr) {
-                        fprintf(ar->f, ": ");
-                        render_node_grouped(ar, field_node->data.struct_field.type);
-                    }
-                    if (field_node->data.struct_field.value != nullptr) {
-                        fprintf(ar->f, " = ");
-                        render_node_grouped(ar, field_node->data.struct_field.value);
-                    }
-                    fprintf(ar->f, ",\n");
-                }
-
-                for (size_t decl_i = 0; decl_i < node->data.container_decl.decls.length; decl_i += 1) {
-                    AstNode *decls_node = node->data.container_decl.decls.at(decl_i);
-                    render_node_grouped(ar, decls_node);
-
-                    if (decls_node->type == NodeTypeUsingNamespace ||
-                        decls_node->type == NodeTypeVariableDeclaration ||
-                        decls_node->type == NodeTypeFnProto)
-                    {
-                        fprintf(ar->f, ";");
-                    }
-                    fprintf(ar->f, "\n");
-                }
-
-                if (!node->data.container_decl.is_root) {
-                    ar->indent -= ar->indent_size;
-                    print_indent(ar);
-                    fprintf(ar->f, "}");
-                }
-                break;
-            }
-        case NodeTypeContainerInitExpr:
-            if (node->data.container_init_expr.type != nullptr) {
-                render_node_ungrouped(ar, node->data.container_init_expr.type);
-            }
-            if (node->data.container_init_expr.kind == ContainerInitKindStruct) {
-                fprintf(ar->f, "{\n");
-                ar->indent += ar->indent_size;
-            } else {
-                fprintf(ar->f, "{");
-            }
-            for (size_t i = 0; i < node->data.container_init_expr.entries.length; i += 1) {
-                AstNode *entry = node->data.container_init_expr.entries.at(i);
-                if (entry->type == NodeTypeStructValueField) {
-                    Buf *name = entry->data.struct_val_field.name;
-                    AstNode *expr = entry->data.struct_val_field.expr;
-                    print_indent(ar);
-                    fprintf(ar->f, ".%s = ", buf_ptr(name));
-                    render_node_grouped(ar, expr);
-                    fprintf(ar->f, ",\n");
-                } else {
-                    if (i != 0)
-                        fprintf(ar->f, ", ");
-                    render_node_grouped(ar, entry);
-                }
-            }
-            if (node->data.container_init_expr.kind == ContainerInitKindStruct) {
-                ar->indent -= ar->indent_size;
-            }
-            print_indent(ar);
-            fprintf(ar->f, "}");
-            break;
-        case NodeTypeArrayType:
-            {
-                fprintf(ar->f, "[");
-                if (node->data.array_type.size) {
-                    render_node_grouped(ar, node->data.array_type.size);
-                }
-                fprintf(ar->f, "]");
-                if (node->data.array_type.is_const) {
-                    fprintf(ar->f, "const ");
-                }
-                render_node_ungrouped(ar, node->data.array_type.child_type);
-                break;
-            }
-        case NodeTypeInferredArrayType:
-            {
-                fprintf(ar->f, "[_]");
-                render_node_ungrouped(ar, node->data.inferred_array_type.child_type);
-                break;
-            }
-        case NodeTypeAnyFrameType: {
-            fprintf(ar->f, "anyframe");
-            if (node->data.anyframe_type.payload_type != nullptr) {
-                fprintf(ar->f, "->");
-                render_node_grouped(ar, node->data.anyframe_type.payload_type);
-            }
-            break;
-        }
-        case NodeTypeErrorType:
-            fprintf(ar->f, "anyerror");
-            break;
-        case NodeTypeAsmExpr:
-            {
-                AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
-                const char *volatile_str = (asm_expr->volatile_token != nullptr) ? " volatile" : "";
-                fprintf(ar->f, "asm%s (", volatile_str);
-                render_node_ungrouped(ar, asm_expr->asm_template);
-                fprintf(ar->f, ")");
-                print_indent(ar);
-                fprintf(ar->f, ": ");
-                for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
-                    AsmOutput *asm_output = asm_expr->output_list.at(i);
-
-                    if (i != 0) {
-                        fprintf(ar->f, ",\n");
-                        print_indent(ar);
-                    }
-
-                    fprintf(ar->f, "[%s] \"%s\" (",
-                            buf_ptr(asm_output->asm_symbolic_name),
-                            buf_ptr(asm_output->constraint));
-                    if (asm_output->return_type) {
-                        fprintf(ar->f, "-> ");
-                        render_node_grouped(ar, asm_output->return_type);
-                    } else {
-                        fprintf(ar->f, "%s", buf_ptr(asm_output->variable_name));
-                    }
-                    fprintf(ar->f, ")");
-                }
-                fprintf(ar->f, "\n");
-                print_indent(ar);
-                fprintf(ar->f, ": ");
-                for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
-                    AsmInput *asm_input = asm_expr->input_list.at(i);
-
-                    if (i != 0) {
-                        fprintf(ar->f, ",\n");
-                        print_indent(ar);
-                    }
-
-                    fprintf(ar->f, "[%s] \"%s\" (",
-                            buf_ptr(asm_input->asm_symbolic_name),
-                            buf_ptr(asm_input->constraint));
-                    render_node_grouped(ar, asm_input->expr);
-                    fprintf(ar->f, ")");
-                }
-                fprintf(ar->f, "\n");
-                print_indent(ar);
-                fprintf(ar->f, ": ");
-                for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1) {
-                    Buf *reg_name = asm_expr->clobber_list.at(i);
-                    if (i != 0) fprintf(ar->f, ", ");
-                    fprintf(ar->f, "\"%s\"", buf_ptr(reg_name));
-                }
-                fprintf(ar->f, ")");
-                break;
-            }
-        case NodeTypeWhileExpr:
-            {
-                if (node->data.while_expr.name != nullptr) {
-                    fprintf(ar->f, "%s: ", buf_ptr(node->data.while_expr.name));
-                }
-                const char *inline_str = node->data.while_expr.is_inline ? "inline " : "";
-                fprintf(ar->f, "%swhile (", inline_str);
-                render_node_grouped(ar, node->data.while_expr.condition);
-                fprintf(ar->f, ") ");
-                if (node->data.while_expr.var_symbol) {
-                    fprintf(ar->f, "|%s| ", buf_ptr(node->data.while_expr.var_symbol));
-                }
-                if (node->data.while_expr.continue_expr) {
-                    fprintf(ar->f, ": (");
-                    render_node_grouped(ar, node->data.while_expr.continue_expr);
-                    fprintf(ar->f, ") ");
-                }
-                render_node_grouped(ar, node->data.while_expr.body);
-                if (node->data.while_expr.else_node) {
-                    fprintf(ar->f, " else ");
-                    if (node->data.while_expr.err_symbol) {
-                        fprintf(ar->f, "|%s| ", buf_ptr(node->data.while_expr.err_symbol));
-                    }
-                    render_node_grouped(ar, node->data.while_expr.else_node);
-                }
-                break;
-            }
-        case NodeTypeBoolLiteral:
-            {
-                const char *bool_str = node->data.bool_literal.value ? "true" : "false";
-                fprintf(ar->f, "%s", bool_str);
-                break;
-            }
-        case NodeTypeIfBoolExpr:
-            {
-                fprintf(ar->f, "if (");
-                render_node_grouped(ar, node->data.if_bool_expr.condition);
-                fprintf(ar->f, ") ");
-                render_node_grouped(ar, node->data.if_bool_expr.then_block);
-                if (node->data.if_bool_expr.else_node) {
-                    fprintf(ar->f, " else ");
-                    render_node_grouped(ar, node->data.if_bool_expr.else_node);
-                }
-                break;
-            }
-        case NodeTypeNullLiteral:
-            {
-                fprintf(ar->f, "null");
-                break;
-            }
-        case NodeTypeIfErrorExpr:
-            {
-                fprintf(ar->f, "if (");
-                render_node_grouped(ar, node->data.if_err_expr.target_node);
-                fprintf(ar->f, ") ");
-                if (node->data.if_err_expr.var_symbol) {
-                    const char *ptr_str = node->data.if_err_expr.var_is_ptr ? "*" : "";
-                    const char *var_name = buf_ptr(node->data.if_err_expr.var_symbol);
-                    fprintf(ar->f, "|%s%s| ", ptr_str, var_name);
-                }
-                render_node_grouped(ar, node->data.if_err_expr.then_node);
-                if (node->data.if_err_expr.else_node) {
-                    fprintf(ar->f, " else ");
-                    if (node->data.if_err_expr.err_symbol) {
-                        fprintf(ar->f, "|%s| ", buf_ptr(node->data.if_err_expr.err_symbol));
-                    }
-                    render_node_grouped(ar, node->data.if_err_expr.else_node);
-                }
-                break;
-            }
-        case NodeTypeIfOptional:
-            {
-                fprintf(ar->f, "if (");
-                render_node_grouped(ar, node->data.test_expr.target_node);
-                fprintf(ar->f, ") ");
-                if (node->data.test_expr.var_symbol) {
-                    const char *ptr_str = node->data.test_expr.var_is_ptr ? "*" : "";
-                    const char *var_name = buf_ptr(node->data.test_expr.var_symbol);
-                    fprintf(ar->f, "|%s%s| ", ptr_str, var_name);
-                }
-                render_node_grouped(ar, node->data.test_expr.then_node);
-                if (node->data.test_expr.else_node) {
-                    fprintf(ar->f, " else ");
-                    render_node_grouped(ar, node->data.test_expr.else_node);
-                }
-                break;
-            }
-        case NodeTypeSwitchExpr:
-            {
-                AstNodeSwitchExpr *switch_expr = &node->data.switch_expr;
-                fprintf(ar->f, "switch (");
-                render_node_grouped(ar, switch_expr->expr);
-                fprintf(ar->f, ") {\n");
-                ar->indent += ar->indent_size;
-
-                for (size_t prong_i = 0; prong_i < switch_expr->prongs.length; prong_i += 1) {
-                    AstNode *prong_node = switch_expr->prongs.at(prong_i);
-                    AstNodeSwitchProng *switch_prong = &prong_node->data.switch_prong;
-                    print_indent(ar);
-                    for (size_t item_i = 0; item_i < switch_prong->items.length; item_i += 1) {
-                        AstNode *item_node = switch_prong->items.at(item_i);
-                        if (item_i != 0)
-                            fprintf(ar->f, ", ");
-                        if (item_node->type == NodeTypeSwitchRange) {
-                            AstNode *start_node = item_node->data.switch_range.start;
-                            AstNode *end_node = item_node->data.switch_range.end;
-                            render_node_grouped(ar, start_node);
-                            fprintf(ar->f, "...");
-                            render_node_grouped(ar, end_node);
-                        } else {
-                            render_node_grouped(ar, item_node);
-                        }
-                    }
-                    const char *else_str = (switch_prong->items.length == 0) ? "else" : "";
-                    fprintf(ar->f, "%s => ", else_str);
-                    if (switch_prong->var_symbol) {
-                        const char *star_str = switch_prong->var_is_ptr ? "*" : "";
-                        Buf *var_name = switch_prong->var_symbol->data.symbol_expr.symbol;
-                        fprintf(ar->f, "|%s%s| ", star_str, buf_ptr(var_name));
-                    }
-                    render_node_grouped(ar, switch_prong->expr);
-                    fprintf(ar->f, ",\n");
-                }
-
-                ar->indent -= ar->indent_size;
-                print_indent(ar);
-                fprintf(ar->f, "}");
-                break;
-            }
-        case NodeTypeCompTime:
-            {
-                fprintf(ar->f, "comptime ");
-                render_node_grouped(ar, node->data.comptime_expr.expr);
-                break;
-            }
-        case NodeTypeNoSuspend:
-            {
-                fprintf(ar->f, "nosuspend ");
-                render_node_grouped(ar, node->data.nosuspend_expr.expr);
-                break;
-            }
-        case NodeTypeForExpr:
-            {
-                if (node->data.for_expr.name != nullptr) {
-                    fprintf(ar->f, "%s: ", buf_ptr(node->data.for_expr.name));
-                }
-                const char *inline_str = node->data.for_expr.is_inline ? "inline " : "";
-                fprintf(ar->f, "%sfor (", inline_str);
-                render_node_grouped(ar, node->data.for_expr.array_expr);
-                fprintf(ar->f, ") ");
-                if (node->data.for_expr.elem_node) {
-                    fprintf(ar->f, "|");
-                    if (node->data.for_expr.elem_is_ptr)
-                        fprintf(ar->f, "*");
-                    render_node_grouped(ar, node->data.for_expr.elem_node);
-                    if (node->data.for_expr.index_node) {
-                        fprintf(ar->f, ", ");
-                        render_node_grouped(ar, node->data.for_expr.index_node);
-                    }
-                    fprintf(ar->f, "| ");
-                }
-                render_node_grouped(ar, node->data.for_expr.body);
-                if (node->data.for_expr.else_node) {
-                    fprintf(ar->f, " else");
-                    render_node_grouped(ar, node->data.for_expr.else_node);
-                }
-                break;
-            }
-        case NodeTypeContinue:
-            {
-                fprintf(ar->f, "continue");
-                if (node->data.continue_expr.name != nullptr) {
-                    fprintf(ar->f, " :%s", buf_ptr(node->data.continue_expr.name));
-                }
-                break;
-            }
-        case NodeTypeUnreachable:
-            {
-                fprintf(ar->f, "unreachable");
-                break;
-            }
-        case NodeTypeSliceExpr:
-            {
-                render_node_ungrouped(ar, node->data.slice_expr.array_ref_expr);
-                fprintf(ar->f, "[");
-                render_node_grouped(ar, node->data.slice_expr.start);
-                fprintf(ar->f, "..");
-                if (node->data.slice_expr.end)
-                    render_node_grouped(ar, node->data.slice_expr.end);
-                fprintf(ar->f, "]");
-                break;
-            }
-        case NodeTypeCatchExpr:
-            {
-                render_node_ungrouped(ar, node->data.unwrap_err_expr.op1);
-                fprintf(ar->f, " catch ");
-                if (node->data.unwrap_err_expr.symbol) {
-                    Buf *var_name = node->data.unwrap_err_expr.symbol->data.symbol_expr.symbol;
-                    fprintf(ar->f, "|%s| ", buf_ptr(var_name));
-                }
-                render_node_ungrouped(ar, node->data.unwrap_err_expr.op2);
-                break;
-            }
-        case NodeTypeErrorSetDecl:
-            {
-                fprintf(ar->f, "error {\n");
-                ar->indent += ar->indent_size;
-
-                for (size_t i = 0; i < node->data.err_set_decl.decls.length; i += 1) {
-                    AstNode *field_node = node->data.err_set_decl.decls.at(i);
-                    switch (field_node->type) {
-                        case NodeTypeSymbol:
-                            print_indent(ar);
-                            print_symbol(ar, field_node->data.symbol_expr.symbol);
-                            fprintf(ar->f, ",\n");
-                            break;
-                        case NodeTypeErrorSetField:
-                            print_indent(ar);
-                            print_symbol(ar, field_node->data.err_set_field.field_name->data.symbol_expr.symbol);
-                            fprintf(ar->f, ",\n");
-                            break;
-                        default:
-                            zig_unreachable();
-                    }
-                }
-
-                ar->indent -= ar->indent_size;
-                print_indent(ar);
-                fprintf(ar->f, "}");
-                break;
-            }
-        case NodeTypeResume:
-            {
-                fprintf(ar->f, "resume ");
-                render_node_grouped(ar, node->data.resume_expr.expr);
-                break;
-            }
-        case NodeTypeAwaitExpr:
-            {
-                fprintf(ar->f, "await ");
-                render_node_grouped(ar, node->data.await_expr.expr);
-                break;
-            }
-        case NodeTypeSuspend:
-            {
-                if (node->data.suspend.block != nullptr) {
-                    fprintf(ar->f, "suspend ");
-                    render_node_grouped(ar, node->data.suspend.block);
-                } else {
-                    fprintf(ar->f, "suspend\n");
-                }
-                break;
-            }
-        case NodeTypeEnumLiteral:
-            {
-                fprintf(ar->f, ".%s", buf_ptr(&node->data.enum_literal.identifier->data.str_lit.str));
-                break;
-            }
-        case NodeTypeAnyTypeField: {
-            fprintf(ar->f, "anytype");
-            break;
-        }
-        case NodeTypeParamDecl:
-        case NodeTypeTestDecl:
-        case NodeTypeStructField:
-        case NodeTypeUsingNamespace:
-        case NodeTypeErrorSetField:
-            zig_panic("TODO more ast rendering");
-    }
-}
-
-
-void ast_render(FILE *f, AstNode *node, int indent_size) {
-    AstRender ar = {0};
-    ar.f = f;
-    ar.indent_size = indent_size;
-    ar.indent = 0;
-
-    render_node_grouped(&ar, node);
-}
-
-void AstNode::src() {
-    fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize "\n",
-            buf_ptr(this->owner->data.structure.root_struct->path),
-            this->line + 1, this->column + 1);
-}
src/stage1/ast_render.hpp
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2015 Andrew Kelley
- *
- * This file is part of zig, which is MIT licensed.
- * See http://opensource.org/licenses/MIT
- */
-
-#ifndef ZIG_AST_RENDER_HPP
-#define ZIG_AST_RENDER_HPP
-
-#include "all_types.hpp"
-#include "parser.hpp"
-
-#include <stdio.h>
-
-void ast_print(FILE *f, AstNode *node, int indent);
-
-void ast_render(FILE *f, AstNode *node, int indent_size);
-
-#endif
src/stage1/astgen.cpp
@@ -0,0 +1,8120 @@
+/*
+ * Copyright (c) 2021 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include "astgen.hpp"
+#include "analyze.hpp"
+#include "util.hpp"
+#include "os.hpp"
+#include "parser.hpp"
+
+static IrInstSrc *ir_gen_node(IrBuilderSrc *irb, AstNode *node, Scope *scope);
+static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval,
+        ResultLoc *result_loc);
+
+static IrInstSrc *ir_lval_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *value, LVal lval,
+        ResultLoc *result_loc);
+static IrInstSrc *ir_expr_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *inst,
+        ResultLoc *result_loc);
+static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *union_type, IrInstSrc *field_name, AstNode *expr_node,
+    LVal lval, ResultLoc *parent_result_loc);
+static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type,
+        ResultLoc *parent_result_loc);
+static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name,
+        bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime);
+static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        ZigVar *var, IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime);
+
+static void ir_assert_impl(bool ok, IrInst *source_instruction, char const *file, unsigned int line) {
+    if (ok) return;
+    src_assert_impl(ok, source_instruction->source_node, file, line);
+}
+
+static void ir_add_call_stack_errors(CodeGen *codegen, IrExecutableSrc *exec, ErrorMsg *err_msg, int limit) {
+    if (!exec || !exec->source_node || limit < 0) return;
+    add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here"));
+
+    ir_add_call_stack_errors_gen(codegen, exec->parent_exec, err_msg, limit - 1);
+}
+
+static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutableSrc *exec, AstNode *source_node, Buf *msg) {
+    ErrorMsg *err_msg = add_node_error(codegen, source_node, msg);
+    invalidate_exec(exec, err_msg);
+    if (exec->parent_exec) {
+        ir_add_call_stack_errors(codegen, exec, err_msg, 10);
+    }
+    return err_msg;
+}
+
+
+#define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
+
+
+static bool instr_is_unreachable(IrInstSrc *instruction) {
+    return instruction->is_noreturn;
+}
+
+void destroy_instruction_src(IrInstSrc *inst) {
+    switch (inst->id) {
+        case IrInstSrcIdInvalid:
+            zig_unreachable();
+        case IrInstSrcIdReturn:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReturn *>(inst));
+        case IrInstSrcIdConst:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcConst *>(inst));
+        case IrInstSrcIdBinOp:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBinOp *>(inst));
+        case IrInstSrcIdMergeErrSets:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMergeErrSets *>(inst));
+        case IrInstSrcIdDeclVar:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclVar *>(inst));
+        case IrInstSrcIdCall:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCall *>(inst));
+        case IrInstSrcIdCallExtra:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallExtra *>(inst));
+        case IrInstSrcIdAsyncCallExtra:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAsyncCallExtra *>(inst));
+        case IrInstSrcIdUnOp:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnOp *>(inst));
+        case IrInstSrcIdCondBr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCondBr *>(inst));
+        case IrInstSrcIdBr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBr *>(inst));
+        case IrInstSrcIdPhi:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPhi *>(inst));
+        case IrInstSrcIdContainerInitList:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcContainerInitList *>(inst));
+        case IrInstSrcIdContainerInitFields:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcContainerInitFields *>(inst));
+        case IrInstSrcIdUnreachable:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnreachable *>(inst));
+        case IrInstSrcIdElemPtr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcElemPtr *>(inst));
+        case IrInstSrcIdVarPtr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcVarPtr *>(inst));
+        case IrInstSrcIdLoadPtr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcLoadPtr *>(inst));
+        case IrInstSrcIdStorePtr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcStorePtr *>(inst));
+        case IrInstSrcIdTypeOf:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeOf *>(inst));
+        case IrInstSrcIdFieldPtr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFieldPtr *>(inst));
+        case IrInstSrcIdSetCold:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetCold *>(inst));
+        case IrInstSrcIdSetRuntimeSafety:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetRuntimeSafety *>(inst));
+        case IrInstSrcIdSetFloatMode:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetFloatMode *>(inst));
+        case IrInstSrcIdArrayType:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArrayType *>(inst));
+        case IrInstSrcIdSliceType:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSliceType *>(inst));
+        case IrInstSrcIdAnyFrameType:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAnyFrameType *>(inst));
+        case IrInstSrcIdAsm:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAsm *>(inst));
+        case IrInstSrcIdSizeOf:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSizeOf *>(inst));
+        case IrInstSrcIdTestNonNull:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTestNonNull *>(inst));
+        case IrInstSrcIdOptionalUnwrapPtr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcOptionalUnwrapPtr *>(inst));
+        case IrInstSrcIdPopCount:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPopCount *>(inst));
+        case IrInstSrcIdClz:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcClz *>(inst));
+        case IrInstSrcIdCtz:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCtz *>(inst));
+        case IrInstSrcIdBswap:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBswap *>(inst));
+        case IrInstSrcIdBitReverse:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBitReverse *>(inst));
+        case IrInstSrcIdSwitchBr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchBr *>(inst));
+        case IrInstSrcIdSwitchVar:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchVar *>(inst));
+        case IrInstSrcIdSwitchElseVar:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchElseVar *>(inst));
+        case IrInstSrcIdSwitchTarget:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchTarget *>(inst));
+        case IrInstSrcIdImport:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcImport *>(inst));
+        case IrInstSrcIdRef:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcRef *>(inst));
+        case IrInstSrcIdCompileErr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCompileErr *>(inst));
+        case IrInstSrcIdCompileLog:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCompileLog *>(inst));
+        case IrInstSrcIdErrName:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrName *>(inst));
+        case IrInstSrcIdCImport:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCImport *>(inst));
+        case IrInstSrcIdCInclude:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCInclude *>(inst));
+        case IrInstSrcIdCDefine:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCDefine *>(inst));
+        case IrInstSrcIdCUndef:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCUndef *>(inst));
+        case IrInstSrcIdEmbedFile:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcEmbedFile *>(inst));
+        case IrInstSrcIdCmpxchg:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCmpxchg *>(inst));
+        case IrInstSrcIdFence:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFence *>(inst));
+        case IrInstSrcIdReduce:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReduce *>(inst));
+        case IrInstSrcIdTruncate:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTruncate *>(inst));
+        case IrInstSrcIdIntCast:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntCast *>(inst));
+        case IrInstSrcIdFloatCast:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFloatCast *>(inst));
+        case IrInstSrcIdErrSetCast:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrSetCast *>(inst));
+        case IrInstSrcIdIntToFloat:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToFloat *>(inst));
+        case IrInstSrcIdFloatToInt:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFloatToInt *>(inst));
+        case IrInstSrcIdBoolToInt:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBoolToInt *>(inst));
+        case IrInstSrcIdVectorType:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcVectorType *>(inst));
+        case IrInstSrcIdShuffleVector:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcShuffleVector *>(inst));
+        case IrInstSrcIdSplat:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSplat *>(inst));
+        case IrInstSrcIdBoolNot:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBoolNot *>(inst));
+        case IrInstSrcIdMemset:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMemset *>(inst));
+        case IrInstSrcIdMemcpy:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMemcpy *>(inst));
+        case IrInstSrcIdSlice:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSlice *>(inst));
+        case IrInstSrcIdBreakpoint:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBreakpoint *>(inst));
+        case IrInstSrcIdReturnAddress:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReturnAddress *>(inst));
+        case IrInstSrcIdFrameAddress:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameAddress *>(inst));
+        case IrInstSrcIdFrameHandle:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameHandle *>(inst));
+        case IrInstSrcIdFrameType:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameType *>(inst));
+        case IrInstSrcIdFrameSize:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameSize *>(inst));
+        case IrInstSrcIdAlignOf:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAlignOf *>(inst));
+        case IrInstSrcIdOverflowOp:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcOverflowOp *>(inst));
+        case IrInstSrcIdTestErr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTestErr *>(inst));
+        case IrInstSrcIdUnwrapErrCode:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnwrapErrCode *>(inst));
+        case IrInstSrcIdUnwrapErrPayload:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnwrapErrPayload *>(inst));
+        case IrInstSrcIdFnProto:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFnProto *>(inst));
+        case IrInstSrcIdTestComptime:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTestComptime *>(inst));
+        case IrInstSrcIdPtrCast:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrCast *>(inst));
+        case IrInstSrcIdBitCast:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBitCast *>(inst));
+        case IrInstSrcIdPtrToInt:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrToInt *>(inst));
+        case IrInstSrcIdIntToPtr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToPtr *>(inst));
+        case IrInstSrcIdIntToEnum:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToEnum *>(inst));
+        case IrInstSrcIdIntToErr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToErr *>(inst));
+        case IrInstSrcIdErrToInt:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrToInt *>(inst));
+        case IrInstSrcIdCheckSwitchProngsUnderNo:
+        case IrInstSrcIdCheckSwitchProngsUnderYes:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckSwitchProngs *>(inst));
+        case IrInstSrcIdCheckStatementIsVoid:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckStatementIsVoid *>(inst));
+        case IrInstSrcIdTypeName:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeName *>(inst));
+        case IrInstSrcIdTagName:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagName *>(inst));
+        case IrInstSrcIdPtrType:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrType *>(inst));
+        case IrInstSrcIdPtrTypeSimple:
+        case IrInstSrcIdPtrTypeSimpleConst:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrTypeSimple *>(inst));
+        case IrInstSrcIdDeclRef:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclRef *>(inst));
+        case IrInstSrcIdPanic:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPanic *>(inst));
+        case IrInstSrcIdFieldParentPtr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFieldParentPtr *>(inst));
+        case IrInstSrcIdByteOffsetOf:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcByteOffsetOf *>(inst));
+        case IrInstSrcIdBitOffsetOf:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBitOffsetOf *>(inst));
+        case IrInstSrcIdTypeInfo:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeInfo *>(inst));
+        case IrInstSrcIdType:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcType *>(inst));
+        case IrInstSrcIdHasField:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcHasField *>(inst));
+        case IrInstSrcIdSetEvalBranchQuota:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetEvalBranchQuota *>(inst));
+        case IrInstSrcIdAlignCast:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAlignCast *>(inst));
+        case IrInstSrcIdImplicitCast:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcImplicitCast *>(inst));
+        case IrInstSrcIdResolveResult:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResolveResult *>(inst));
+        case IrInstSrcIdResetResult:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResetResult *>(inst));
+        case IrInstSrcIdSetAlignStack:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetAlignStack *>(inst));
+        case IrInstSrcIdArgTypeAllowVarFalse:
+        case IrInstSrcIdArgTypeAllowVarTrue:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArgType *>(inst));
+        case IrInstSrcIdExport:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst));
+        case IrInstSrcIdExtern:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExtern *>(inst));
+        case IrInstSrcIdErrorReturnTrace:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrorReturnTrace *>(inst));
+        case IrInstSrcIdErrorUnion:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrorUnion *>(inst));
+        case IrInstSrcIdAtomicRmw:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAtomicRmw *>(inst));
+        case IrInstSrcIdSaveErrRetAddr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSaveErrRetAddr *>(inst));
+        case IrInstSrcIdAddImplicitReturnType:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAddImplicitReturnType *>(inst));
+        case IrInstSrcIdFloatOp:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFloatOp *>(inst));
+        case IrInstSrcIdMulAdd:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMulAdd *>(inst));
+        case IrInstSrcIdAtomicLoad:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAtomicLoad *>(inst));
+        case IrInstSrcIdAtomicStore:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAtomicStore *>(inst));
+        case IrInstSrcIdEnumToInt:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcEnumToInt *>(inst));
+        case IrInstSrcIdCheckRuntimeScope:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckRuntimeScope *>(inst));
+        case IrInstSrcIdHasDecl:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcHasDecl *>(inst));
+        case IrInstSrcIdUndeclaredIdent:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUndeclaredIdent *>(inst));
+        case IrInstSrcIdAlloca:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAlloca *>(inst));
+        case IrInstSrcIdEndExpr:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcEndExpr *>(inst));
+        case IrInstSrcIdUnionInitNamedField:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnionInitNamedField *>(inst));
+        case IrInstSrcIdSuspendBegin:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSuspendBegin *>(inst));
+        case IrInstSrcIdSuspendFinish:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSuspendFinish *>(inst));
+        case IrInstSrcIdResume:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResume *>(inst));
+        case IrInstSrcIdAwait:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAwait *>(inst));
+        case IrInstSrcIdSpillBegin:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSpillBegin *>(inst));
+        case IrInstSrcIdSpillEnd:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSpillEnd *>(inst));
+        case IrInstSrcIdCallArgs:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallArgs *>(inst));
+        case IrInstSrcIdWasmMemorySize:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemorySize *>(inst));
+        case IrInstSrcIdWasmMemoryGrow:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemoryGrow *>(inst));
+        case IrInstSrcIdSrc:
+            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSrc *>(inst));
+    }
+    zig_unreachable();
+}
+
+
+bool ir_should_inline(IrExecutableSrc *exec, Scope *scope) {
+    if (exec->is_inline)
+        return true;
+
+    while (scope != nullptr) {
+        if (scope->id == ScopeIdCompTime)
+            return true;
+        if (scope->id == ScopeIdTypeOf)
+            return false;
+        if (scope->id == ScopeIdFnDef)
+            break;
+        scope = scope->parent;
+    }
+    return false;
+}
+
+static void ir_instruction_append(IrBasicBlockSrc *basic_block, IrInstSrc *instruction) {
+    assert(basic_block);
+    assert(instruction);
+    basic_block->instruction_list.append(instruction);
+}
+
+static size_t exec_next_debug_id(IrExecutableSrc *exec) {
+    size_t result = exec->next_debug_id;
+    exec->next_debug_id += 1;
+    return result;
+}
+
+static ZigFn *exec_fn_entry(IrExecutableSrc *exec) {
+    return exec->fn_entry;
+}
+
+static Buf *exec_c_import_buf(IrExecutableSrc *exec) {
+    return exec->c_import_buf;
+}
+
+static void ir_ref_bb(IrBasicBlockSrc *bb) {
+    bb->ref_count += 1;
+}
+
+static void ir_ref_instruction(IrInstSrc *instruction, IrBasicBlockSrc *cur_bb) {
+    assert(instruction->id != IrInstSrcIdInvalid);
+    instruction->base.ref_count += 1;
+    if (instruction->owner_bb != cur_bb && !instr_is_unreachable(instruction)
+        && instruction->id != IrInstSrcIdConst)
+    {
+        ir_ref_bb(instruction->owner_bb);
+    }
+}
+
+static IrBasicBlockSrc *ir_create_basic_block(IrBuilderSrc *irb, Scope *scope, const char *name_hint) {
+    IrBasicBlockSrc *result = heap::c_allocator.create<IrBasicBlockSrc>();
+    result->scope = scope;
+    result->name_hint = name_hint;
+    result->debug_id = exec_next_debug_id(irb->exec);
+    result->index = UINT32_MAX; // set later
+    return result;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcDeclVar *) {
+    return IrInstSrcIdDeclVar;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcBr *) {
+    return IrInstSrcIdBr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCondBr *) {
+    return IrInstSrcIdCondBr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchBr *) {
+    return IrInstSrcIdSwitchBr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchVar *) {
+    return IrInstSrcIdSwitchVar;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchElseVar *) {
+    return IrInstSrcIdSwitchElseVar;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchTarget *) {
+    return IrInstSrcIdSwitchTarget;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcPhi *) {
+    return IrInstSrcIdPhi;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnOp *) {
+    return IrInstSrcIdUnOp;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcBinOp *) {
+    return IrInstSrcIdBinOp;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcMergeErrSets *) {
+    return IrInstSrcIdMergeErrSets;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcLoadPtr *) {
+    return IrInstSrcIdLoadPtr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcStorePtr *) {
+    return IrInstSrcIdStorePtr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcFieldPtr *) {
+    return IrInstSrcIdFieldPtr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcElemPtr *) {
+    return IrInstSrcIdElemPtr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcVarPtr *) {
+    return IrInstSrcIdVarPtr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCall *) {
+    return IrInstSrcIdCall;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallArgs *) {
+    return IrInstSrcIdCallArgs;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallExtra *) {
+    return IrInstSrcIdCallExtra;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsyncCallExtra *) {
+    return IrInstSrcIdAsyncCallExtra;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcConst *) {
+    return IrInstSrcIdConst;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcReturn *) {
+    return IrInstSrcIdReturn;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcContainerInitList *) {
+    return IrInstSrcIdContainerInitList;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcContainerInitFields *) {
+    return IrInstSrcIdContainerInitFields;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnreachable *) {
+    return IrInstSrcIdUnreachable;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeOf *) {
+    return IrInstSrcIdTypeOf;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetCold *) {
+    return IrInstSrcIdSetCold;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetRuntimeSafety *) {
+    return IrInstSrcIdSetRuntimeSafety;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetFloatMode *) {
+    return IrInstSrcIdSetFloatMode;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcArrayType *) {
+    return IrInstSrcIdArrayType;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcAnyFrameType *) {
+    return IrInstSrcIdAnyFrameType;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSliceType *) {
+    return IrInstSrcIdSliceType;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsm *) {
+    return IrInstSrcIdAsm;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSizeOf *) {
+    return IrInstSrcIdSizeOf;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestNonNull *) {
+    return IrInstSrcIdTestNonNull;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcOptionalUnwrapPtr *) {
+    return IrInstSrcIdOptionalUnwrapPtr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcClz *) {
+    return IrInstSrcIdClz;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCtz *) {
+    return IrInstSrcIdCtz;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcPopCount *) {
+    return IrInstSrcIdPopCount;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcBswap *) {
+    return IrInstSrcIdBswap;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitReverse *) {
+    return IrInstSrcIdBitReverse;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcImport *) {
+    return IrInstSrcIdImport;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCImport *) {
+    return IrInstSrcIdCImport;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCInclude *) {
+    return IrInstSrcIdCInclude;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCDefine *) {
+    return IrInstSrcIdCDefine;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCUndef *) {
+    return IrInstSrcIdCUndef;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcRef *) {
+    return IrInstSrcIdRef;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCompileErr *) {
+    return IrInstSrcIdCompileErr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCompileLog *) {
+    return IrInstSrcIdCompileLog;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrName *) {
+    return IrInstSrcIdErrName;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcEmbedFile *) {
+    return IrInstSrcIdEmbedFile;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCmpxchg *) {
+    return IrInstSrcIdCmpxchg;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcFence *) {
+    return IrInstSrcIdFence;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcReduce *) {
+    return IrInstSrcIdReduce;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcTruncate *) {
+    return IrInstSrcIdTruncate;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntCast *) {
+    return IrInstSrcIdIntCast;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatCast *) {
+    return IrInstSrcIdFloatCast;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToFloat *) {
+    return IrInstSrcIdIntToFloat;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatToInt *) {
+    return IrInstSrcIdFloatToInt;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolToInt *) {
+    return IrInstSrcIdBoolToInt;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcVectorType *) {
+    return IrInstSrcIdVectorType;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcShuffleVector *) {
+    return IrInstSrcIdShuffleVector;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSplat *) {
+    return IrInstSrcIdSplat;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolNot *) {
+    return IrInstSrcIdBoolNot;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemset *) {
+    return IrInstSrcIdMemset;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemcpy *) {
+    return IrInstSrcIdMemcpy;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSlice *) {
+    return IrInstSrcIdSlice;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcBreakpoint *) {
+    return IrInstSrcIdBreakpoint;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcReturnAddress *) {
+    return IrInstSrcIdReturnAddress;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameAddress *) {
+    return IrInstSrcIdFrameAddress;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameHandle *) {
+    return IrInstSrcIdFrameHandle;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameType *) {
+    return IrInstSrcIdFrameType;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameSize *) {
+    return IrInstSrcIdFrameSize;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlignOf *) {
+    return IrInstSrcIdAlignOf;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcOverflowOp *) {
+    return IrInstSrcIdOverflowOp;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestErr *) {
+    return IrInstSrcIdTestErr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcMulAdd *) {
+    return IrInstSrcIdMulAdd;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatOp *) {
+    return IrInstSrcIdFloatOp;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnwrapErrCode *) {
+    return IrInstSrcIdUnwrapErrCode;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnwrapErrPayload *) {
+    return IrInstSrcIdUnwrapErrPayload;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcFnProto *) {
+    return IrInstSrcIdFnProto;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestComptime *) {
+    return IrInstSrcIdTestComptime;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrCast *) {
+    return IrInstSrcIdPtrCast;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitCast *) {
+    return IrInstSrcIdBitCast;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToPtr *) {
+    return IrInstSrcIdIntToPtr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrToInt *) {
+    return IrInstSrcIdPtrToInt;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToEnum *) {
+    return IrInstSrcIdIntToEnum;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcEnumToInt *) {
+    return IrInstSrcIdEnumToInt;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToErr *) {
+    return IrInstSrcIdIntToErr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) {
+    return IrInstSrcIdErrToInt;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) {
+    return IrInstSrcIdCheckStatementIsVoid;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeName *) {
+    return IrInstSrcIdTypeName;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcDeclRef *) {
+    return IrInstSrcIdDeclRef;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcPanic *) {
+    return IrInstSrcIdPanic;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcTagName *) {
+    return IrInstSrcIdTagName;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcFieldParentPtr *) {
+    return IrInstSrcIdFieldParentPtr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcByteOffsetOf *) {
+    return IrInstSrcIdByteOffsetOf;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitOffsetOf *) {
+    return IrInstSrcIdBitOffsetOf;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeInfo *) {
+    return IrInstSrcIdTypeInfo;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcType *) {
+    return IrInstSrcIdType;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcHasField *) {
+    return IrInstSrcIdHasField;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetEvalBranchQuota *) {
+    return IrInstSrcIdSetEvalBranchQuota;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrType *) {
+    return IrInstSrcIdPtrType;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlignCast *) {
+    return IrInstSrcIdAlignCast;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcImplicitCast *) {
+    return IrInstSrcIdImplicitCast;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcResolveResult *) {
+    return IrInstSrcIdResolveResult;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcResetResult *) {
+    return IrInstSrcIdResetResult;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) {
+    return IrInstSrcIdSetAlignStack;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) {
+    return IrInstSrcIdExport;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcExtern *) {
+    return IrInstSrcIdExtern;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorReturnTrace *) {
+    return IrInstSrcIdErrorReturnTrace;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorUnion *) {
+    return IrInstSrcIdErrorUnion;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicRmw *) {
+    return IrInstSrcIdAtomicRmw;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicLoad *) {
+    return IrInstSrcIdAtomicLoad;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicStore *) {
+    return IrInstSrcIdAtomicStore;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSaveErrRetAddr *) {
+    return IrInstSrcIdSaveErrRetAddr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcAddImplicitReturnType *) {
+    return IrInstSrcIdAddImplicitReturnType;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrSetCast *) {
+    return IrInstSrcIdErrSetCast;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckRuntimeScope *) {
+    return IrInstSrcIdCheckRuntimeScope;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcHasDecl *) {
+    return IrInstSrcIdHasDecl;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcUndeclaredIdent *) {
+    return IrInstSrcIdUndeclaredIdent;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlloca *) {
+    return IrInstSrcIdAlloca;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcEndExpr *) {
+    return IrInstSrcIdEndExpr;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnionInitNamedField *) {
+    return IrInstSrcIdUnionInitNamedField;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSuspendBegin *) {
+    return IrInstSrcIdSuspendBegin;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSuspendFinish *) {
+    return IrInstSrcIdSuspendFinish;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcAwait *) {
+    return IrInstSrcIdAwait;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcResume *) {
+    return IrInstSrcIdResume;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillBegin *) {
+    return IrInstSrcIdSpillBegin;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillEnd *) {
+    return IrInstSrcIdSpillEnd;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemorySize *) {
+    return IrInstSrcIdWasmMemorySize;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemoryGrow *) {
+    return IrInstSrcIdWasmMemoryGrow;
+}
+
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcSrc *) {
+    return IrInstSrcIdSrc;
+}
+
+template<typename T>
+static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    T *special_instruction = heap::c_allocator.create<T>();
+    special_instruction->base.id = ir_inst_id(special_instruction);
+    special_instruction->base.base.scope = scope;
+    special_instruction->base.base.source_node = source_node;
+    special_instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
+    special_instruction->base.owner_bb = irb->current_basic_block;
+    return special_instruction;
+}
+
+template<typename T>
+static T *ir_build_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    T *special_instruction = ir_create_instruction<T>(irb, scope, source_node);
+    ir_instruction_append(irb->current_basic_block, &special_instruction->base);
+    return special_instruction;
+}
+
+static IrInstSrc *ir_build_cond_br(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *condition,
+        IrBasicBlockSrc *then_block, IrBasicBlockSrc *else_block, IrInstSrc *is_comptime)
+{
+    IrInstSrcCondBr *inst = ir_build_instruction<IrInstSrcCondBr>(irb, scope, source_node);
+    inst->base.is_noreturn = true;
+    inst->condition = condition;
+    inst->then_block = then_block;
+    inst->else_block = else_block;
+    inst->is_comptime = is_comptime;
+
+    ir_ref_instruction(condition, irb->current_basic_block);
+    ir_ref_bb(then_block);
+    ir_ref_bb(else_block);
+    if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_return_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand) {
+    IrInstSrcReturn *inst = ir_build_instruction<IrInstSrcReturn>(irb, scope, source_node);
+    inst->base.is_noreturn = true;
+    inst->operand = operand;
+
+    if (operand != nullptr) ir_ref_instruction(operand, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_const_void(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
+    ir_instruction_append(irb->current_basic_block, &const_instruction->base);
+    const_instruction->value = irb->codegen->intern.for_void();
+    return &const_instruction->base;
+}
+
+static IrInstSrc *ir_build_const_undefined(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
+    ir_instruction_append(irb->current_basic_block, &const_instruction->base);
+    const_instruction->value = irb->codegen->intern.for_undefined();
+    const_instruction->value->special = ConstValSpecialUndef;
+    return &const_instruction->base;
+}
+
+static IrInstSrc *ir_build_const_uint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) {
+    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
+    const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int;
+    const_instruction->value->special = ConstValSpecialStatic;
+    bigint_init_unsigned(&const_instruction->value->data.x_bigint, value);
+    return &const_instruction->base;
+}
+
+static IrInstSrc *ir_build_const_bigint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        BigInt bigint)
+{
+    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
+    const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int;
+    const_instruction->value->special = ConstValSpecialStatic;
+    const_instruction->value->data.x_bigint = bigint;
+    return &const_instruction->base;
+}
+
+static IrInstSrc *ir_build_const_bigfloat(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        BigFloat bigfloat)
+{
+    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
+    const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_float;
+    const_instruction->value->special = ConstValSpecialStatic;
+    const_instruction->value->data.x_bigfloat = bigfloat;
+    return &const_instruction->base;
+}
+
+static IrInstSrc *ir_build_const_null(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
+    ir_instruction_append(irb->current_basic_block, &const_instruction->base);
+    const_instruction->value = irb->codegen->intern.for_null();
+    return &const_instruction->base;
+}
+
+static IrInstSrc *ir_build_const_usize(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) {
+    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
+    const_instruction->value->type = irb->codegen->builtin_types.entry_usize;
+    const_instruction->value->special = ConstValSpecialStatic;
+    bigint_init_unsigned(&const_instruction->value->data.x_bigint, value);
+    return &const_instruction->base;
+}
+
+static IrInstSrc *ir_create_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        ZigType *type_entry)
+{
+    IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
+    const_instruction->value->type = irb->codegen->builtin_types.entry_type;
+    const_instruction->value->special = ConstValSpecialStatic;
+    const_instruction->value->data.x_type = type_entry;
+    return &const_instruction->base;
+}
+
+static IrInstSrc *ir_build_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        ZigType *type_entry)
+{
+    IrInstSrc *instruction = ir_create_const_type(irb, scope, source_node, type_entry);
+    ir_instruction_append(irb->current_basic_block, instruction);
+    return instruction;
+}
+
+static IrInstSrc *ir_build_const_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigType *import) {
+    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
+    const_instruction->value->type = irb->codegen->builtin_types.entry_type;
+    const_instruction->value->special = ConstValSpecialStatic;
+    const_instruction->value->data.x_type = import;
+    return &const_instruction->base;
+}
+
+static IrInstSrc *ir_build_const_bool(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, bool value) {
+    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
+    const_instruction->value->type = irb->codegen->builtin_types.entry_bool;
+    const_instruction->value->special = ConstValSpecialStatic;
+    const_instruction->value->data.x_bool = value;
+    return &const_instruction->base;
+}
+
+static IrInstSrc *ir_build_const_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) {
+    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
+    const_instruction->value->type = irb->codegen->builtin_types.entry_enum_literal;
+    const_instruction->value->special = ConstValSpecialStatic;
+    const_instruction->value->data.x_enum_literal = name;
+    return &const_instruction->base;
+}
+
+// Consumes `str`.
+static IrInstSrc *ir_create_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) {
+    IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
+    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
+    init_const_str_lit(irb->codegen, const_instruction->value, str, true);
+
+    return &const_instruction->base;
+}
+
+// Consumes `str`.
+static IrInstSrc *ir_build_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) {
+    IrInstSrc *instruction = ir_create_const_str_lit(irb, scope, source_node, str);
+    ir_instruction_append(irb->current_basic_block, instruction);
+    return instruction;
+}
+
+static IrInstSrc *ir_build_bin_op(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrBinOp op_id,
+        IrInstSrc *op1, IrInstSrc *op2, bool safety_check_on)
+{
+    IrInstSrcBinOp *inst = ir_build_instruction<IrInstSrcBinOp>(irb, scope, source_node);
+    inst->op_id = op_id;
+    inst->op1 = op1;
+    inst->op2 = op2;
+    inst->safety_check_on = safety_check_on;
+
+    ir_ref_instruction(op1, irb->current_basic_block);
+    ir_ref_instruction(op2, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *op1, IrInstSrc *op2, Buf *type_name)
+{
+    IrInstSrcMergeErrSets *inst = ir_build_instruction<IrInstSrcMergeErrSets>(irb, scope, source_node);
+    inst->op1 = op1;
+    inst->op2 = op2;
+    inst->type_name = type_name;
+
+    ir_ref_instruction(op1, irb->current_basic_block);
+    ir_ref_instruction(op2, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_var_ptr_x(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var,
+        ScopeFnDef *crossed_fndef_scope)
+{
+    IrInstSrcVarPtr *instruction = ir_build_instruction<IrInstSrcVarPtr>(irb, scope, source_node);
+    instruction->var = var;
+    instruction->crossed_fndef_scope = crossed_fndef_scope;
+
+    var->ref_count += 1;
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_var_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var) {
+    return ir_build_var_ptr_x(irb, scope, source_node, var, nullptr);
+}
+
+static IrInstSrc *ir_build_elem_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *array_ptr, IrInstSrc *elem_index, bool safety_check_on, PtrLen ptr_len,
+        AstNode *init_array_type_source_node)
+{
+    IrInstSrcElemPtr *instruction = ir_build_instruction<IrInstSrcElemPtr>(irb, scope, source_node);
+    instruction->array_ptr = array_ptr;
+    instruction->elem_index = elem_index;
+    instruction->safety_check_on = safety_check_on;
+    instruction->ptr_len = ptr_len;
+    instruction->init_array_type_source_node = init_array_type_source_node;
+
+    ir_ref_instruction(array_ptr, irb->current_basic_block);
+    ir_ref_instruction(elem_index, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_field_ptr_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *container_ptr, IrInstSrc *field_name_expr, bool initializing)
+{
+    IrInstSrcFieldPtr *instruction = ir_build_instruction<IrInstSrcFieldPtr>(irb, scope, source_node);
+    instruction->container_ptr = container_ptr;
+    instruction->field_name_buffer = nullptr;
+    instruction->field_name_expr = field_name_expr;
+    instruction->initializing = initializing;
+
+    ir_ref_instruction(container_ptr, irb->current_basic_block);
+    ir_ref_instruction(field_name_expr, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_field_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *container_ptr, Buf *field_name, bool initializing)
+{
+    IrInstSrcFieldPtr *instruction = ir_build_instruction<IrInstSrcFieldPtr>(irb, scope, source_node);
+    instruction->container_ptr = container_ptr;
+    instruction->field_name_buffer = field_name;
+    instruction->field_name_expr = nullptr;
+    instruction->initializing = initializing;
+
+    ir_ref_instruction(container_ptr, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_has_field(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *container_type, IrInstSrc *field_name)
+{
+    IrInstSrcHasField *instruction = ir_build_instruction<IrInstSrcHasField>(irb, scope, source_node);
+    instruction->container_type = container_type;
+    instruction->field_name = field_name;
+
+    ir_ref_instruction(container_type, irb->current_basic_block);
+    ir_ref_instruction(field_name, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc *args, ResultLoc *result_loc)
+{
+    IrInstSrcCallExtra *call_instruction = ir_build_instruction<IrInstSrcCallExtra>(irb, scope, source_node);
+    call_instruction->options = options;
+    call_instruction->fn_ref = fn_ref;
+    call_instruction->args = args;
+    call_instruction->result_loc = result_loc;
+
+    ir_ref_instruction(options, irb->current_basic_block);
+    ir_ref_instruction(fn_ref, irb->current_basic_block);
+    ir_ref_instruction(args, irb->current_basic_block);
+
+    return &call_instruction->base;
+}
+
+static IrInstSrc *ir_build_async_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        CallModifier modifier, IrInstSrc *fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstSrc *args, ResultLoc *result_loc)
+{
+    IrInstSrcAsyncCallExtra *call_instruction = ir_build_instruction<IrInstSrcAsyncCallExtra>(irb, scope, source_node);
+    call_instruction->modifier = modifier;
+    call_instruction->fn_ref = fn_ref;
+    call_instruction->ret_ptr = ret_ptr;
+    call_instruction->new_stack = new_stack;
+    call_instruction->args = args;
+    call_instruction->result_loc = result_loc;
+
+    ir_ref_instruction(fn_ref, irb->current_basic_block);
+    if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block);
+    ir_ref_instruction(new_stack, irb->current_basic_block);
+    ir_ref_instruction(args, irb->current_basic_block);
+
+    return &call_instruction->base;
+}
+
+static IrInstSrc *ir_build_call_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc **args_ptr, size_t args_len,
+        ResultLoc *result_loc)
+{
+    IrInstSrcCallArgs *call_instruction = ir_build_instruction<IrInstSrcCallArgs>(irb, scope, source_node);
+    call_instruction->options = options;
+    call_instruction->fn_ref = fn_ref;
+    call_instruction->args_ptr = args_ptr;
+    call_instruction->args_len = args_len;
+    call_instruction->result_loc = result_loc;
+
+    ir_ref_instruction(options, irb->current_basic_block);
+    ir_ref_instruction(fn_ref, irb->current_basic_block);
+    for (size_t i = 0; i < args_len; i += 1)
+        ir_ref_instruction(args_ptr[i], irb->current_basic_block);
+
+    return &call_instruction->base;
+}
+
+static IrInstSrc *ir_build_call_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        ZigFn *fn_entry, IrInstSrc *fn_ref, size_t arg_count, IrInstSrc **args,
+        IrInstSrc *ret_ptr, CallModifier modifier, bool is_async_call_builtin,
+        IrInstSrc *new_stack, ResultLoc *result_loc)
+{
+    IrInstSrcCall *call_instruction = ir_build_instruction<IrInstSrcCall>(irb, scope, source_node);
+    call_instruction->fn_entry = fn_entry;
+    call_instruction->fn_ref = fn_ref;
+    call_instruction->args = args;
+    call_instruction->arg_count = arg_count;
+    call_instruction->modifier = modifier;
+    call_instruction->is_async_call_builtin = is_async_call_builtin;
+    call_instruction->new_stack = new_stack;
+    call_instruction->result_loc = result_loc;
+    call_instruction->ret_ptr = ret_ptr;
+
+    if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block);
+    for (size_t i = 0; i < arg_count; i += 1)
+        ir_ref_instruction(args[i], irb->current_basic_block);
+    if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block);
+    if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block);
+
+    return &call_instruction->base;
+}
+
+static IrInstSrc *ir_build_phi(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        size_t incoming_count, IrBasicBlockSrc **incoming_blocks, IrInstSrc **incoming_values,
+        ResultLocPeerParent *peer_parent)
+{
+    assert(incoming_count != 0);
+    assert(incoming_count != SIZE_MAX);
+
+    IrInstSrcPhi *phi_instruction = ir_build_instruction<IrInstSrcPhi>(irb, scope, source_node);
+    phi_instruction->incoming_count = incoming_count;
+    phi_instruction->incoming_blocks = incoming_blocks;
+    phi_instruction->incoming_values = incoming_values;
+    phi_instruction->peer_parent = peer_parent;
+
+    for (size_t i = 0; i < incoming_count; i += 1) {
+        ir_ref_bb(incoming_blocks[i]);
+        ir_ref_instruction(incoming_values[i], irb->current_basic_block);
+    }
+
+    return &phi_instruction->base;
+}
+
+static IrInstSrc *ir_build_br(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrBasicBlockSrc *dest_block, IrInstSrc *is_comptime)
+{
+    IrInstSrcBr *inst = ir_build_instruction<IrInstSrcBr>(irb, scope, source_node);
+    inst->base.is_noreturn = true;
+    inst->dest_block = dest_block;
+    inst->is_comptime = is_comptime;
+
+    ir_ref_bb(dest_block);
+    if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_ptr_type_simple(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *child_type, bool is_const)
+{
+    IrInstSrcPtrTypeSimple *inst = heap::c_allocator.create<IrInstSrcPtrTypeSimple>();
+    inst->base.id = is_const ? IrInstSrcIdPtrTypeSimpleConst : IrInstSrcIdPtrTypeSimple;
+    inst->base.base.scope = scope;
+    inst->base.base.source_node = source_node;
+    inst->base.base.debug_id = exec_next_debug_id(irb->exec);
+    inst->base.owner_bb = irb->current_basic_block;
+    ir_instruction_append(irb->current_basic_block, &inst->base);
+
+    inst->child_type = child_type;
+
+    ir_ref_instruction(child_type, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len,
+        IrInstSrc *sentinel, IrInstSrc *align_value,
+        uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero)
+{
+    if (!is_volatile && ptr_len == PtrLenSingle && sentinel == nullptr && align_value == nullptr &&
+            bit_offset_start == 0 && host_int_bytes == 0 && is_allow_zero == 0)
+    {
+        return ir_build_ptr_type_simple(irb, scope, source_node, child_type, is_const);
+    }
+
+    IrInstSrcPtrType *inst = ir_build_instruction<IrInstSrcPtrType>(irb, scope, source_node);
+    inst->sentinel = sentinel;
+    inst->align_value = align_value;
+    inst->child_type = child_type;
+    inst->is_const = is_const;
+    inst->is_volatile = is_volatile;
+    inst->ptr_len = ptr_len;
+    inst->bit_offset_start = bit_offset_start;
+    inst->host_int_bytes = host_int_bytes;
+    inst->is_allow_zero = is_allow_zero;
+
+    if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block);
+    if (align_value) ir_ref_instruction(align_value, irb->current_basic_block);
+    ir_ref_instruction(child_type, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_un_op_lval(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrUnOp op_id,
+        IrInstSrc *value, LVal lval, ResultLoc *result_loc)
+{
+    IrInstSrcUnOp *instruction = ir_build_instruction<IrInstSrcUnOp>(irb, scope, source_node);
+    instruction->op_id = op_id;
+    instruction->value = value;
+    instruction->lval = lval;
+    instruction->result_loc = result_loc;
+
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_un_op(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrUnOp op_id,
+        IrInstSrc *value)
+{
+    return ir_build_un_op_lval(irb, scope, source_node, op_id, value, LValNone, nullptr);
+}
+
+static IrInstSrc *ir_build_container_init_list(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        size_t item_count, IrInstSrc **elem_result_loc_list, IrInstSrc *result_loc,
+        AstNode *init_array_type_source_node)
+{
+    IrInstSrcContainerInitList *container_init_list_instruction =
+        ir_build_instruction<IrInstSrcContainerInitList>(irb, scope, source_node);
+    container_init_list_instruction->item_count = item_count;
+    container_init_list_instruction->elem_result_loc_list = elem_result_loc_list;
+    container_init_list_instruction->result_loc = result_loc;
+    container_init_list_instruction->init_array_type_source_node = init_array_type_source_node;
+
+    for (size_t i = 0; i < item_count; i += 1) {
+        ir_ref_instruction(elem_result_loc_list[i], irb->current_basic_block);
+    }
+    if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
+
+    return &container_init_list_instruction->base;
+}
+
+static IrInstSrc *ir_build_container_init_fields(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        size_t field_count, IrInstSrcContainerInitFieldsField *fields, IrInstSrc *result_loc)
+{
+    IrInstSrcContainerInitFields *container_init_fields_instruction =
+        ir_build_instruction<IrInstSrcContainerInitFields>(irb, scope, source_node);
+    container_init_fields_instruction->field_count = field_count;
+    container_init_fields_instruction->fields = fields;
+    container_init_fields_instruction->result_loc = result_loc;
+
+    for (size_t i = 0; i < field_count; i += 1) {
+        ir_ref_instruction(fields[i].result_loc, irb->current_basic_block);
+    }
+    if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
+
+    return &container_init_fields_instruction->base;
+}
+
+static IrInstSrc *ir_build_unreachable(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    IrInstSrcUnreachable *inst = ir_build_instruction<IrInstSrcUnreachable>(irb, scope, source_node);
+    inst->base.is_noreturn = true;
+    return &inst->base;
+}
+
+static IrInstSrcStorePtr *ir_build_store_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *ptr, IrInstSrc *value)
+{
+    IrInstSrcStorePtr *instruction = ir_build_instruction<IrInstSrcStorePtr>(irb, scope, source_node);
+    instruction->ptr = ptr;
+    instruction->value = value;
+
+    ir_ref_instruction(ptr, irb->current_basic_block);
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return instruction;
+}
+
+static IrInstSrc *ir_build_var_decl_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        ZigVar *var, IrInstSrc *align_value, IrInstSrc *ptr)
+{
+    IrInstSrcDeclVar *inst = ir_build_instruction<IrInstSrcDeclVar>(irb, scope, source_node);
+    inst->var = var;
+    inst->align_value = align_value;
+    inst->ptr = ptr;
+
+    if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
+    ir_ref_instruction(ptr, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_export(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *target, IrInstSrc *options)
+{
+    IrInstSrcExport *export_instruction = ir_build_instruction<IrInstSrcExport>(
+            irb, scope, source_node);
+    export_instruction->target = target;
+    export_instruction->options = options;
+
+    ir_ref_instruction(target, irb->current_basic_block);
+    ir_ref_instruction(options, irb->current_basic_block);
+
+    return &export_instruction->base;
+}
+
+static IrInstSrc *ir_build_extern(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *type, IrInstSrc *options)
+{
+    IrInstSrcExtern *extern_instruction = ir_build_instruction<IrInstSrcExtern>(
+            irb, scope, source_node);
+    extern_instruction->type = type;
+    extern_instruction->options = options;
+
+    ir_ref_instruction(type, irb->current_basic_block);
+    ir_ref_instruction(options, irb->current_basic_block);
+
+    return &extern_instruction->base;
+}
+
+static IrInstSrc *ir_build_load_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *ptr) {
+    IrInstSrcLoadPtr *instruction = ir_build_instruction<IrInstSrcLoadPtr>(irb, scope, source_node);
+    instruction->ptr = ptr;
+
+    ir_ref_instruction(ptr, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_typeof_n(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc **values, size_t value_count)
+{
+    assert(value_count >= 2);
+
+    IrInstSrcTypeOf *instruction = ir_build_instruction<IrInstSrcTypeOf>(irb, scope, source_node);
+    instruction->value.list = values;
+    instruction->value_count = value_count;
+
+    for (size_t i = 0; i < value_count; i++)
+        ir_ref_instruction(values[i], irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_typeof_1(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
+    IrInstSrcTypeOf *instruction = ir_build_instruction<IrInstSrcTypeOf>(irb, scope, source_node);
+    instruction->value.scalar = value;
+
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_set_cold(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *is_cold) {
+    IrInstSrcSetCold *instruction = ir_build_instruction<IrInstSrcSetCold>(irb, scope, source_node);
+    instruction->is_cold = is_cold;
+
+    ir_ref_instruction(is_cold, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_set_runtime_safety(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *safety_on)
+{
+    IrInstSrcSetRuntimeSafety *inst = ir_build_instruction<IrInstSrcSetRuntimeSafety>(irb, scope, source_node);
+    inst->safety_on = safety_on;
+
+    ir_ref_instruction(safety_on, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_set_float_mode(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *mode_value)
+{
+    IrInstSrcSetFloatMode *instruction = ir_build_instruction<IrInstSrcSetFloatMode>(irb, scope, source_node);
+    instruction->mode_value = mode_value;
+
+    ir_ref_instruction(mode_value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_array_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *size,
+        IrInstSrc *sentinel, IrInstSrc *child_type)
+{
+    IrInstSrcArrayType *instruction = ir_build_instruction<IrInstSrcArrayType>(irb, scope, source_node);
+    instruction->size = size;
+    instruction->sentinel = sentinel;
+    instruction->child_type = child_type;
+
+    ir_ref_instruction(size, irb->current_basic_block);
+    if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block);
+    ir_ref_instruction(child_type, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_anyframe_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *payload_type)
+{
+    IrInstSrcAnyFrameType *instruction = ir_build_instruction<IrInstSrcAnyFrameType>(irb, scope, source_node);
+    instruction->payload_type = payload_type;
+
+    if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_slice_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *child_type, bool is_const, bool is_volatile,
+        IrInstSrc *sentinel, IrInstSrc *align_value, bool is_allow_zero)
+{
+    IrInstSrcSliceType *instruction = ir_build_instruction<IrInstSrcSliceType>(irb, scope, source_node);
+    instruction->is_const = is_const;
+    instruction->is_volatile = is_volatile;
+    instruction->child_type = child_type;
+    instruction->sentinel = sentinel;
+    instruction->align_value = align_value;
+    instruction->is_allow_zero = is_allow_zero;
+
+    if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block);
+    if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
+    ir_ref_instruction(child_type, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_asm_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *asm_template, IrInstSrc **input_list, IrInstSrc **output_types,
+        ZigVar **output_vars, size_t return_count, bool has_side_effects, bool is_global)
+{
+    IrInstSrcAsm *instruction = ir_build_instruction<IrInstSrcAsm>(irb, scope, source_node);
+    instruction->asm_template = asm_template;
+    instruction->input_list = input_list;
+    instruction->output_types = output_types;
+    instruction->output_vars = output_vars;
+    instruction->return_count = return_count;
+    instruction->has_side_effects = has_side_effects;
+    instruction->is_global = is_global;
+
+    assert(source_node->type == NodeTypeAsmExpr);
+    for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) {
+        IrInstSrc *output_type = output_types[i];
+        if (output_type) ir_ref_instruction(output_type, irb->current_basic_block);
+    }
+
+    for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) {
+        IrInstSrc *input_value = input_list[i];
+        ir_ref_instruction(input_value, irb->current_basic_block);
+    }
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_size_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value,
+        bool bit_size)
+{
+    IrInstSrcSizeOf *instruction = ir_build_instruction<IrInstSrcSizeOf>(irb, scope, source_node);
+    instruction->type_value = type_value;
+    instruction->bit_size = bit_size;
+
+    ir_ref_instruction(type_value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_test_non_null_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *value)
+{
+    IrInstSrcTestNonNull *instruction = ir_build_instruction<IrInstSrcTestNonNull>(irb, scope, source_node);
+    instruction->value = value;
+
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_optional_unwrap_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *base_ptr, bool safety_check_on)
+{
+    IrInstSrcOptionalUnwrapPtr *instruction = ir_build_instruction<IrInstSrcOptionalUnwrapPtr>(irb, scope, source_node);
+    instruction->base_ptr = base_ptr;
+    instruction->safety_check_on = safety_check_on;
+
+    ir_ref_instruction(base_ptr, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_clz(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
+        IrInstSrc *op)
+{
+    IrInstSrcClz *instruction = ir_build_instruction<IrInstSrcClz>(irb, scope, source_node);
+    instruction->type = type;
+    instruction->op = op;
+
+    ir_ref_instruction(type, irb->current_basic_block);
+    ir_ref_instruction(op, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_ctz(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
+        IrInstSrc *op)
+{
+    IrInstSrcCtz *instruction = ir_build_instruction<IrInstSrcCtz>(irb, scope, source_node);
+    instruction->type = type;
+    instruction->op = op;
+
+    ir_ref_instruction(type, irb->current_basic_block);
+    ir_ref_instruction(op, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_pop_count(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
+        IrInstSrc *op)
+{
+    IrInstSrcPopCount *instruction = ir_build_instruction<IrInstSrcPopCount>(irb, scope, source_node);
+    instruction->type = type;
+    instruction->op = op;
+
+    ir_ref_instruction(type, irb->current_basic_block);
+    ir_ref_instruction(op, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_bswap(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
+        IrInstSrc *op)
+{
+    IrInstSrcBswap *instruction = ir_build_instruction<IrInstSrcBswap>(irb, scope, source_node);
+    instruction->type = type;
+    instruction->op = op;
+
+    ir_ref_instruction(type, irb->current_basic_block);
+    ir_ref_instruction(op, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_bit_reverse(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
+        IrInstSrc *op)
+{
+    IrInstSrcBitReverse *instruction = ir_build_instruction<IrInstSrcBitReverse>(irb, scope, source_node);
+    instruction->type = type;
+    instruction->op = op;
+
+    ir_ref_instruction(type, irb->current_basic_block);
+    ir_ref_instruction(op, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrcSwitchBr *ir_build_switch_br_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *target_value, IrBasicBlockSrc *else_block, size_t case_count, IrInstSrcSwitchBrCase *cases,
+        IrInstSrc *is_comptime, IrInstSrc *switch_prongs_void)
+{
+    IrInstSrcSwitchBr *instruction = ir_build_instruction<IrInstSrcSwitchBr>(irb, scope, source_node);
+    instruction->base.is_noreturn = true;
+    instruction->target_value = target_value;
+    instruction->else_block = else_block;
+    instruction->case_count = case_count;
+    instruction->cases = cases;
+    instruction->is_comptime = is_comptime;
+    instruction->switch_prongs_void = switch_prongs_void;
+
+    ir_ref_instruction(target_value, irb->current_basic_block);
+    ir_ref_instruction(is_comptime, irb->current_basic_block);
+    ir_ref_bb(else_block);
+    ir_ref_instruction(switch_prongs_void, irb->current_basic_block);
+
+    for (size_t i = 0; i < case_count; i += 1) {
+        ir_ref_instruction(cases[i].value, irb->current_basic_block);
+        ir_ref_bb(cases[i].block);
+    }
+
+    return instruction;
+}
+
+static IrInstSrc *ir_build_switch_target(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *target_value_ptr)
+{
+    IrInstSrcSwitchTarget *instruction = ir_build_instruction<IrInstSrcSwitchTarget>(irb, scope, source_node);
+    instruction->target_value_ptr = target_value_ptr;
+
+    ir_ref_instruction(target_value_ptr, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_switch_var(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *target_value_ptr, IrInstSrc **prongs_ptr, size_t prongs_len)
+{
+    IrInstSrcSwitchVar *instruction = ir_build_instruction<IrInstSrcSwitchVar>(irb, scope, source_node);
+    instruction->target_value_ptr = target_value_ptr;
+    instruction->prongs_ptr = prongs_ptr;
+    instruction->prongs_len = prongs_len;
+
+    ir_ref_instruction(target_value_ptr, irb->current_basic_block);
+    for (size_t i = 0; i < prongs_len; i += 1) {
+        ir_ref_instruction(prongs_ptr[i], irb->current_basic_block);
+    }
+
+    return &instruction->base;
+}
+
+// For this instruction the switch_br must be set later.
+static IrInstSrcSwitchElseVar *ir_build_switch_else_var(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *target_value_ptr)
+{
+    IrInstSrcSwitchElseVar *instruction = ir_build_instruction<IrInstSrcSwitchElseVar>(irb, scope, source_node);
+    instruction->target_value_ptr = target_value_ptr;
+
+    ir_ref_instruction(target_value_ptr, irb->current_basic_block);
+
+    return instruction;
+}
+
+static IrInstSrc *ir_build_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) {
+    IrInstSrcImport *instruction = ir_build_instruction<IrInstSrcImport>(irb, scope, source_node);
+    instruction->name = name;
+
+    ir_ref_instruction(name, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_ref_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
+    IrInstSrcRef *instruction = ir_build_instruction<IrInstSrcRef>(irb, scope, source_node);
+    instruction->value = value;
+
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_compile_err(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *msg) {
+    IrInstSrcCompileErr *instruction = ir_build_instruction<IrInstSrcCompileErr>(irb, scope, source_node);
+    instruction->msg = msg;
+
+    ir_ref_instruction(msg, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_compile_log(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        size_t msg_count, IrInstSrc **msg_list)
+{
+    IrInstSrcCompileLog *instruction = ir_build_instruction<IrInstSrcCompileLog>(irb, scope, source_node);
+    instruction->msg_count = msg_count;
+    instruction->msg_list = msg_list;
+
+    for (size_t i = 0; i < msg_count; i += 1) {
+        ir_ref_instruction(msg_list[i], irb->current_basic_block);
+    }
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_err_name(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
+    IrInstSrcErrName *instruction = ir_build_instruction<IrInstSrcErrName>(irb, scope, source_node);
+    instruction->value = value;
+
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_c_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    IrInstSrcCImport *instruction = ir_build_instruction<IrInstSrcCImport>(irb, scope, source_node);
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_c_include(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) {
+    IrInstSrcCInclude *instruction = ir_build_instruction<IrInstSrcCInclude>(irb, scope, source_node);
+    instruction->name = name;
+
+    ir_ref_instruction(name, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_c_define(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name, IrInstSrc *value) {
+    IrInstSrcCDefine *instruction = ir_build_instruction<IrInstSrcCDefine>(irb, scope, source_node);
+    instruction->name = name;
+    instruction->value = value;
+
+    ir_ref_instruction(name, irb->current_basic_block);
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_c_undef(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) {
+    IrInstSrcCUndef *instruction = ir_build_instruction<IrInstSrcCUndef>(irb, scope, source_node);
+    instruction->name = name;
+
+    ir_ref_instruction(name, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_embed_file(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) {
+    IrInstSrcEmbedFile *instruction = ir_build_instruction<IrInstSrcEmbedFile>(irb, scope, source_node);
+    instruction->name = name;
+
+    ir_ref_instruction(name, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_cmpxchg_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *type_value, IrInstSrc *ptr, IrInstSrc *cmp_value, IrInstSrc *new_value,
+    IrInstSrc *success_order_value, IrInstSrc *failure_order_value, bool is_weak, ResultLoc *result_loc)
+{
+    IrInstSrcCmpxchg *instruction = ir_build_instruction<IrInstSrcCmpxchg>(irb, scope, source_node);
+    instruction->type_value = type_value;
+    instruction->ptr = ptr;
+    instruction->cmp_value = cmp_value;
+    instruction->new_value = new_value;
+    instruction->success_order_value = success_order_value;
+    instruction->failure_order_value = failure_order_value;
+    instruction->is_weak = is_weak;
+    instruction->result_loc = result_loc;
+
+    ir_ref_instruction(type_value, irb->current_basic_block);
+    ir_ref_instruction(ptr, irb->current_basic_block);
+    ir_ref_instruction(cmp_value, irb->current_basic_block);
+    ir_ref_instruction(new_value, irb->current_basic_block);
+    ir_ref_instruction(success_order_value, irb->current_basic_block);
+    ir_ref_instruction(failure_order_value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_fence(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *order) {
+    IrInstSrcFence *instruction = ir_build_instruction<IrInstSrcFence>(irb, scope, source_node);
+    instruction->order = order;
+
+    ir_ref_instruction(order, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_reduce(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *op, IrInstSrc *value) {
+    IrInstSrcReduce *instruction = ir_build_instruction<IrInstSrcReduce>(irb, scope, source_node);
+    instruction->op = op;
+    instruction->value = value;
+
+    ir_ref_instruction(op, irb->current_basic_block);
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_truncate(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *dest_type, IrInstSrc *target)
+{
+    IrInstSrcTruncate *instruction = ir_build_instruction<IrInstSrcTruncate>(irb, scope, source_node);
+    instruction->dest_type = dest_type;
+    instruction->target = target;
+
+    ir_ref_instruction(dest_type, irb->current_basic_block);
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_int_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type,
+        IrInstSrc *target)
+{
+    IrInstSrcIntCast *instruction = ir_build_instruction<IrInstSrcIntCast>(irb, scope, source_node);
+    instruction->dest_type = dest_type;
+    instruction->target = target;
+
+    ir_ref_instruction(dest_type, irb->current_basic_block);
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_float_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type,
+        IrInstSrc *target)
+{
+    IrInstSrcFloatCast *instruction = ir_build_instruction<IrInstSrcFloatCast>(irb, scope, source_node);
+    instruction->dest_type = dest_type;
+    instruction->target = target;
+
+    ir_ref_instruction(dest_type, irb->current_basic_block);
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_err_set_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *dest_type, IrInstSrc *target)
+{
+    IrInstSrcErrSetCast *instruction = ir_build_instruction<IrInstSrcErrSetCast>(irb, scope, source_node);
+    instruction->dest_type = dest_type;
+    instruction->target = target;
+
+    ir_ref_instruction(dest_type, irb->current_basic_block);
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_int_to_float(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *dest_type, IrInstSrc *target)
+{
+    IrInstSrcIntToFloat *instruction = ir_build_instruction<IrInstSrcIntToFloat>(irb, scope, source_node);
+    instruction->dest_type = dest_type;
+    instruction->target = target;
+
+    ir_ref_instruction(dest_type, irb->current_basic_block);
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_float_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *dest_type, IrInstSrc *target)
+{
+    IrInstSrcFloatToInt *instruction = ir_build_instruction<IrInstSrcFloatToInt>(irb, scope, source_node);
+    instruction->dest_type = dest_type;
+    instruction->target = target;
+
+    ir_ref_instruction(dest_type, irb->current_basic_block);
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_bool_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) {
+    IrInstSrcBoolToInt *instruction = ir_build_instruction<IrInstSrcBoolToInt>(irb, scope, source_node);
+    instruction->target = target;
+
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_vector_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *len,
+        IrInstSrc *elem_type)
+{
+    IrInstSrcVectorType *instruction = ir_build_instruction<IrInstSrcVectorType>(irb, scope, source_node);
+    instruction->len = len;
+    instruction->elem_type = elem_type;
+
+    ir_ref_instruction(len, irb->current_basic_block);
+    ir_ref_instruction(elem_type, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_shuffle_vector(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *scalar_type, IrInstSrc *a, IrInstSrc *b, IrInstSrc *mask)
+{
+    IrInstSrcShuffleVector *instruction = ir_build_instruction<IrInstSrcShuffleVector>(irb, scope, source_node);
+    instruction->scalar_type = scalar_type;
+    instruction->a = a;
+    instruction->b = b;
+    instruction->mask = mask;
+
+    if (scalar_type != nullptr) ir_ref_instruction(scalar_type, irb->current_basic_block);
+    ir_ref_instruction(a, irb->current_basic_block);
+    ir_ref_instruction(b, irb->current_basic_block);
+    ir_ref_instruction(mask, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_splat_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *len, IrInstSrc *scalar)
+{
+    IrInstSrcSplat *instruction = ir_build_instruction<IrInstSrcSplat>(irb, scope, source_node);
+    instruction->len = len;
+    instruction->scalar = scalar;
+
+    ir_ref_instruction(len, irb->current_basic_block);
+    ir_ref_instruction(scalar, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_bool_not(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
+    IrInstSrcBoolNot *instruction = ir_build_instruction<IrInstSrcBoolNot>(irb, scope, source_node);
+    instruction->value = value;
+
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_memset_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *dest_ptr, IrInstSrc *byte, IrInstSrc *count)
+{
+    IrInstSrcMemset *instruction = ir_build_instruction<IrInstSrcMemset>(irb, scope, source_node);
+    instruction->dest_ptr = dest_ptr;
+    instruction->byte = byte;
+    instruction->count = count;
+
+    ir_ref_instruction(dest_ptr, irb->current_basic_block);
+    ir_ref_instruction(byte, irb->current_basic_block);
+    ir_ref_instruction(count, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_memcpy_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *dest_ptr, IrInstSrc *src_ptr, IrInstSrc *count)
+{
+    IrInstSrcMemcpy *instruction = ir_build_instruction<IrInstSrcMemcpy>(irb, scope, source_node);
+    instruction->dest_ptr = dest_ptr;
+    instruction->src_ptr = src_ptr;
+    instruction->count = count;
+
+    ir_ref_instruction(dest_ptr, irb->current_basic_block);
+    ir_ref_instruction(src_ptr, irb->current_basic_block);
+    ir_ref_instruction(count, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_slice_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *ptr, IrInstSrc *start, IrInstSrc *end, IrInstSrc *sentinel,
+    bool safety_check_on, ResultLoc *result_loc)
+{
+    IrInstSrcSlice *instruction = ir_build_instruction<IrInstSrcSlice>(irb, scope, source_node);
+    instruction->ptr = ptr;
+    instruction->start = start;
+    instruction->end = end;
+    instruction->sentinel = sentinel;
+    instruction->safety_check_on = safety_check_on;
+    instruction->result_loc = result_loc;
+
+    ir_ref_instruction(ptr, irb->current_basic_block);
+    ir_ref_instruction(start, irb->current_basic_block);
+    if (end) ir_ref_instruction(end, irb->current_basic_block);
+    if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_breakpoint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    IrInstSrcBreakpoint *instruction = ir_build_instruction<IrInstSrcBreakpoint>(irb, scope, source_node);
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_return_address_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    IrInstSrcReturnAddress *instruction = ir_build_instruction<IrInstSrcReturnAddress>(irb, scope, source_node);
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_frame_address_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    IrInstSrcFrameAddress *inst = ir_build_instruction<IrInstSrcFrameAddress>(irb, scope, source_node);
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_handle_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    IrInstSrcFrameHandle *inst = ir_build_instruction<IrInstSrcFrameHandle>(irb, scope, source_node);
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_frame_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn) {
+    IrInstSrcFrameType *inst = ir_build_instruction<IrInstSrcFrameType>(irb, scope, source_node);
+    inst->fn = fn;
+
+    ir_ref_instruction(fn, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_frame_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn) {
+    IrInstSrcFrameSize *inst = ir_build_instruction<IrInstSrcFrameSize>(irb, scope, source_node);
+    inst->fn = fn;
+
+    ir_ref_instruction(fn, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_overflow_op_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrOverflowOp op, IrInstSrc *type_value, IrInstSrc *op1, IrInstSrc *op2, IrInstSrc *result_ptr)
+{
+    IrInstSrcOverflowOp *instruction = ir_build_instruction<IrInstSrcOverflowOp>(irb, scope, source_node);
+    instruction->op = op;
+    instruction->type_value = type_value;
+    instruction->op1 = op1;
+    instruction->op2 = op2;
+    instruction->result_ptr = result_ptr;
+
+    ir_ref_instruction(type_value, irb->current_basic_block);
+    ir_ref_instruction(op1, irb->current_basic_block);
+    ir_ref_instruction(op2, irb->current_basic_block);
+    ir_ref_instruction(result_ptr, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_float_op_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand,
+        BuiltinFnId fn_id)
+{
+    IrInstSrcFloatOp *instruction = ir_build_instruction<IrInstSrcFloatOp>(irb, scope, source_node);
+    instruction->operand = operand;
+    instruction->fn_id = fn_id;
+
+    ir_ref_instruction(operand, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_mul_add_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *type_value, IrInstSrc *op1, IrInstSrc *op2, IrInstSrc *op3)
+{
+    IrInstSrcMulAdd *instruction = ir_build_instruction<IrInstSrcMulAdd>(irb, scope, source_node);
+    instruction->type_value = type_value;
+    instruction->op1 = op1;
+    instruction->op2 = op2;
+    instruction->op3 = op3;
+
+    ir_ref_instruction(type_value, irb->current_basic_block);
+    ir_ref_instruction(op1, irb->current_basic_block);
+    ir_ref_instruction(op2, irb->current_basic_block);
+    ir_ref_instruction(op3, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_align_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) {
+    IrInstSrcAlignOf *instruction = ir_build_instruction<IrInstSrcAlignOf>(irb, scope, source_node);
+    instruction->type_value = type_value;
+
+    ir_ref_instruction(type_value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_test_err_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *base_ptr, bool resolve_err_set, bool base_ptr_is_payload)
+{
+    IrInstSrcTestErr *instruction = ir_build_instruction<IrInstSrcTestErr>(irb, scope, source_node);
+    instruction->base_ptr = base_ptr;
+    instruction->resolve_err_set = resolve_err_set;
+    instruction->base_ptr_is_payload = base_ptr_is_payload;
+
+    ir_ref_instruction(base_ptr, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_unwrap_err_code_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *err_union_ptr)
+{
+    IrInstSrcUnwrapErrCode *inst = ir_build_instruction<IrInstSrcUnwrapErrCode>(irb, scope, source_node);
+    inst->err_union_ptr = err_union_ptr;
+
+    ir_ref_instruction(err_union_ptr, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_unwrap_err_payload_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *value, bool safety_check_on, bool initializing)
+{
+    IrInstSrcUnwrapErrPayload *inst = ir_build_instruction<IrInstSrcUnwrapErrPayload>(irb, scope, source_node);
+    inst->value = value;
+    inst->safety_check_on = safety_check_on;
+    inst->initializing = initializing;
+
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_fn_proto(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc **param_types, IrInstSrc *align_value, IrInstSrc *callconv_value,
+    IrInstSrc *return_type, bool is_var_args)
+{
+    IrInstSrcFnProto *instruction = ir_build_instruction<IrInstSrcFnProto>(irb, scope, source_node);
+    instruction->param_types = param_types;
+    instruction->align_value = align_value;
+    instruction->callconv_value = callconv_value;
+    instruction->return_type = return_type;
+    instruction->is_var_args = is_var_args;
+
+    assert(source_node->type == NodeTypeFnProto);
+    size_t param_count = source_node->data.fn_proto.params.length;
+    if (is_var_args) param_count -= 1;
+    for (size_t i = 0; i < param_count; i += 1) {
+        if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block);
+    }
+    if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
+    if (callconv_value != nullptr) ir_ref_instruction(callconv_value, irb->current_basic_block);
+    ir_ref_instruction(return_type, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_test_comptime(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
+    IrInstSrcTestComptime *instruction = ir_build_instruction<IrInstSrcTestComptime>(irb, scope, source_node);
+    instruction->value = value;
+
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_ptr_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *dest_type, IrInstSrc *ptr, bool safety_check_on)
+{
+    IrInstSrcPtrCast *instruction = ir_build_instruction<IrInstSrcPtrCast>(
+            irb, scope, source_node);
+    instruction->dest_type = dest_type;
+    instruction->ptr = ptr;
+    instruction->safety_check_on = safety_check_on;
+
+    ir_ref_instruction(dest_type, irb->current_basic_block);
+    ir_ref_instruction(ptr, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_implicit_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *operand, ResultLocCast *result_loc_cast)
+{
+    IrInstSrcImplicitCast *instruction = ir_build_instruction<IrInstSrcImplicitCast>(irb, scope, source_node);
+    instruction->operand = operand;
+    instruction->result_loc_cast = result_loc_cast;
+
+    ir_ref_instruction(operand, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_bit_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *operand, ResultLocBitCast *result_loc_bit_cast)
+{
+    IrInstSrcBitCast *instruction = ir_build_instruction<IrInstSrcBitCast>(irb, scope, source_node);
+    instruction->operand = operand;
+    instruction->result_loc_bit_cast = result_loc_bit_cast;
+
+    ir_ref_instruction(operand, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_int_to_ptr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *dest_type, IrInstSrc *target)
+{
+    IrInstSrcIntToPtr *instruction = ir_build_instruction<IrInstSrcIntToPtr>(irb, scope, source_node);
+    instruction->dest_type = dest_type;
+    instruction->target = target;
+
+    ir_ref_instruction(dest_type, irb->current_basic_block);
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_ptr_to_int_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *target)
+{
+    IrInstSrcPtrToInt *inst = ir_build_instruction<IrInstSrcPtrToInt>(irb, scope, source_node);
+    inst->target = target;
+
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_int_to_enum_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *dest_type, IrInstSrc *target)
+{
+    IrInstSrcIntToEnum *instruction = ir_build_instruction<IrInstSrcIntToEnum>(irb, scope, source_node);
+    instruction->dest_type = dest_type;
+    instruction->target = target;
+
+    if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block);
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_enum_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *target)
+{
+    IrInstSrcEnumToInt *instruction = ir_build_instruction<IrInstSrcEnumToInt>(
+            irb, scope, source_node);
+    instruction->target = target;
+
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_int_to_err_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *target)
+{
+    IrInstSrcIntToErr *instruction = ir_build_instruction<IrInstSrcIntToErr>(irb, scope, source_node);
+    instruction->target = target;
+
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_err_to_int_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *target)
+{
+    IrInstSrcErrToInt *instruction = ir_build_instruction<IrInstSrcErrToInt>(
+            irb, scope, source_node);
+    instruction->target = target;
+
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,
+        AstNode* else_prong, bool have_underscore_prong)
+{
+    IrInstSrcCheckSwitchProngs *instruction = heap::c_allocator.create<IrInstSrcCheckSwitchProngs>();
+    instruction->base.id = have_underscore_prong ?
+        IrInstSrcIdCheckSwitchProngsUnderYes : IrInstSrcIdCheckSwitchProngsUnderNo;
+    instruction->base.base.scope = scope;
+    instruction->base.base.source_node = source_node;
+    instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
+    instruction->base.owner_bb = irb->current_basic_block;
+    ir_instruction_append(irb->current_basic_block, &instruction->base);
+
+    instruction->target_value = target_value;
+    instruction->ranges = ranges;
+    instruction->range_count = range_count;
+    instruction->else_prong = else_prong;
+
+    ir_ref_instruction(target_value, irb->current_basic_block);
+    for (size_t i = 0; i < range_count; i += 1) {
+        ir_ref_instruction(ranges[i].start, irb->current_basic_block);
+        ir_ref_instruction(ranges[i].end, irb->current_basic_block);
+    }
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_check_statement_is_void(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc* statement_value)
+{
+    IrInstSrcCheckStatementIsVoid *instruction = ir_build_instruction<IrInstSrcCheckStatementIsVoid>(
+            irb, scope, source_node);
+    instruction->statement_value = statement_value;
+
+    ir_ref_instruction(statement_value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_type_name(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *type_value)
+{
+    IrInstSrcTypeName *instruction = ir_build_instruction<IrInstSrcTypeName>(irb, scope, source_node);
+    instruction->type_value = type_value;
+
+    ir_ref_instruction(type_value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_decl_ref(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Tld *tld, LVal lval) {
+    IrInstSrcDeclRef *instruction = ir_build_instruction<IrInstSrcDeclRef>(irb, scope, source_node);
+    instruction->tld = tld;
+    instruction->lval = lval;
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_panic_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *msg) {
+    IrInstSrcPanic *instruction = ir_build_instruction<IrInstSrcPanic>(irb, scope, source_node);
+    instruction->base.is_noreturn = true;
+    instruction->msg = msg;
+
+    ir_ref_instruction(msg, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_tag_name_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) {
+    IrInstSrcTagName *instruction = ir_build_instruction<IrInstSrcTagName>(irb, scope, source_node);
+    instruction->target = target;
+
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_field_parent_ptr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *type_value, IrInstSrc *field_name, IrInstSrc *field_ptr)
+{
+    IrInstSrcFieldParentPtr *inst = ir_build_instruction<IrInstSrcFieldParentPtr>(
+            irb, scope, source_node);
+    inst->type_value = type_value;
+    inst->field_name = field_name;
+    inst->field_ptr = field_ptr;
+
+    ir_ref_instruction(type_value, irb->current_basic_block);
+    ir_ref_instruction(field_name, irb->current_basic_block);
+    ir_ref_instruction(field_ptr, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_byte_offset_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *type_value, IrInstSrc *field_name)
+{
+    IrInstSrcByteOffsetOf *instruction = ir_build_instruction<IrInstSrcByteOffsetOf>(irb, scope, source_node);
+    instruction->type_value = type_value;
+    instruction->field_name = field_name;
+
+    ir_ref_instruction(type_value, irb->current_basic_block);
+    ir_ref_instruction(field_name, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_bit_offset_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *type_value, IrInstSrc *field_name)
+{
+    IrInstSrcBitOffsetOf *instruction = ir_build_instruction<IrInstSrcBitOffsetOf>(irb, scope, source_node);
+    instruction->type_value = type_value;
+    instruction->field_name = field_name;
+
+    ir_ref_instruction(type_value, irb->current_basic_block);
+    ir_ref_instruction(field_name, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_type_info(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) {
+    IrInstSrcTypeInfo *instruction = ir_build_instruction<IrInstSrcTypeInfo>(irb, scope, source_node);
+    instruction->type_value = type_value;
+
+    ir_ref_instruction(type_value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_info) {
+    IrInstSrcType *instruction = ir_build_instruction<IrInstSrcType>(irb, scope, source_node);
+    instruction->type_info = type_info;
+
+    ir_ref_instruction(type_info, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_set_eval_branch_quota(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *new_quota)
+{
+    IrInstSrcSetEvalBranchQuota *instruction = ir_build_instruction<IrInstSrcSetEvalBranchQuota>(irb, scope, source_node);
+    instruction->new_quota = new_quota;
+
+    ir_ref_instruction(new_quota, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_align_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *align_bytes, IrInstSrc *target)
+{
+    IrInstSrcAlignCast *instruction = ir_build_instruction<IrInstSrcAlignCast>(irb, scope, source_node);
+    instruction->align_bytes = align_bytes;
+    instruction->target = target;
+
+    ir_ref_instruction(align_bytes, irb->current_basic_block);
+    ir_ref_instruction(target, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_resolve_result(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        ResultLoc *result_loc, IrInstSrc *ty)
+{
+    IrInstSrcResolveResult *instruction = ir_build_instruction<IrInstSrcResolveResult>(irb, scope, source_node);
+    instruction->result_loc = result_loc;
+    instruction->ty = ty;
+
+    if (ty != nullptr) ir_ref_instruction(ty, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_reset_result(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        ResultLoc *result_loc)
+{
+    IrInstSrcResetResult *instruction = ir_build_instruction<IrInstSrcResetResult>(irb, scope, source_node);
+    instruction->result_loc = result_loc;
+    instruction->base.is_gen = true;
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_set_align_stack(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *align_bytes)
+{
+    IrInstSrcSetAlignStack *instruction = ir_build_instruction<IrInstSrcSetAlignStack>(irb, scope, source_node);
+    instruction->align_bytes = align_bytes;
+
+    ir_ref_instruction(align_bytes, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var)
+{
+    IrInstSrcArgType *instruction = heap::c_allocator.create<IrInstSrcArgType>();
+    instruction->base.id = allow_var ?
+        IrInstSrcIdArgTypeAllowVarTrue : IrInstSrcIdArgTypeAllowVarFalse;
+    instruction->base.base.scope = scope;
+    instruction->base.base.source_node = source_node;
+    instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
+    instruction->base.owner_bb = irb->current_basic_block;
+    ir_instruction_append(irb->current_basic_block, &instruction->base);
+
+    instruction->fn_type = fn_type;
+    instruction->arg_index = arg_index;
+
+    ir_ref_instruction(fn_type, irb->current_basic_block);
+    ir_ref_instruction(arg_index, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_error_return_trace_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstErrorReturnTraceOptional optional)
+{
+    IrInstSrcErrorReturnTrace *inst = ir_build_instruction<IrInstSrcErrorReturnTrace>(irb, scope, source_node);
+    inst->optional = optional;
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_error_union(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *err_set, IrInstSrc *payload)
+{
+    IrInstSrcErrorUnion *instruction = ir_build_instruction<IrInstSrcErrorUnion>(irb, scope, source_node);
+    instruction->err_set = err_set;
+    instruction->payload = payload;
+
+    ir_ref_instruction(err_set, irb->current_basic_block);
+    ir_ref_instruction(payload, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_atomic_rmw_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *op, IrInstSrc *operand,
+        IrInstSrc *ordering)
+{
+    IrInstSrcAtomicRmw *instruction = ir_build_instruction<IrInstSrcAtomicRmw>(irb, scope, source_node);
+    instruction->operand_type = operand_type;
+    instruction->ptr = ptr;
+    instruction->op = op;
+    instruction->operand = operand;
+    instruction->ordering = ordering;
+
+    ir_ref_instruction(operand_type, irb->current_basic_block);
+    ir_ref_instruction(ptr, irb->current_basic_block);
+    ir_ref_instruction(op, irb->current_basic_block);
+    ir_ref_instruction(operand, irb->current_basic_block);
+    ir_ref_instruction(ordering, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_atomic_load_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *ordering)
+{
+    IrInstSrcAtomicLoad *instruction = ir_build_instruction<IrInstSrcAtomicLoad>(irb, scope, source_node);
+    instruction->operand_type = operand_type;
+    instruction->ptr = ptr;
+    instruction->ordering = ordering;
+
+    ir_ref_instruction(operand_type, irb->current_basic_block);
+    ir_ref_instruction(ptr, irb->current_basic_block);
+    ir_ref_instruction(ordering, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_atomic_store_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *value, IrInstSrc *ordering)
+{
+    IrInstSrcAtomicStore *instruction = ir_build_instruction<IrInstSrcAtomicStore>(irb, scope, source_node);
+    instruction->operand_type = operand_type;
+    instruction->ptr = ptr;
+    instruction->value = value;
+    instruction->ordering = ordering;
+
+    ir_ref_instruction(operand_type, irb->current_basic_block);
+    ir_ref_instruction(ptr, irb->current_basic_block);
+    ir_ref_instruction(value, irb->current_basic_block);
+    ir_ref_instruction(ordering, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_save_err_ret_addr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    IrInstSrcSaveErrRetAddr *inst = ir_build_instruction<IrInstSrcSaveErrRetAddr>(irb, scope, source_node);
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_add_implicit_return_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *value, ResultLocReturn *result_loc_ret)
+{
+    IrInstSrcAddImplicitReturnType *inst = ir_build_instruction<IrInstSrcAddImplicitReturnType>(irb, scope, source_node);
+    inst->value = value;
+    inst->result_loc_ret = result_loc_ret;
+
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_has_decl(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *container, IrInstSrc *name)
+{
+    IrInstSrcHasDecl *instruction = ir_build_instruction<IrInstSrcHasDecl>(irb, scope, source_node);
+    instruction->container = container;
+    instruction->name = name;
+
+    ir_ref_instruction(container, irb->current_basic_block);
+    ir_ref_instruction(name, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_undeclared_identifier(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) {
+    IrInstSrcUndeclaredIdent *instruction = ir_build_instruction<IrInstSrcUndeclaredIdent>(irb, scope, source_node);
+    instruction->name = name;
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_check_runtime_scope(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *scope_is_comptime, IrInstSrc *is_comptime) {
+    IrInstSrcCheckRuntimeScope *instruction = ir_build_instruction<IrInstSrcCheckRuntimeScope>(irb, scope, source_node);
+    instruction->scope_is_comptime = scope_is_comptime;
+    instruction->is_comptime = is_comptime;
+
+    ir_ref_instruction(scope_is_comptime, irb->current_basic_block);
+    ir_ref_instruction(is_comptime, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_union_init_named_field(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *union_type, IrInstSrc *field_name, IrInstSrc *field_result_loc, IrInstSrc *result_loc)
+{
+    IrInstSrcUnionInitNamedField *instruction = ir_build_instruction<IrInstSrcUnionInitNamedField>(irb, scope, source_node);
+    instruction->union_type = union_type;
+    instruction->field_name = field_name;
+    instruction->field_result_loc = field_result_loc;
+    instruction->result_loc = result_loc;
+
+    ir_ref_instruction(union_type, irb->current_basic_block);
+    ir_ref_instruction(field_name, irb->current_basic_block);
+    ir_ref_instruction(field_result_loc, irb->current_basic_block);
+    if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_alloca_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *align, const char *name_hint, IrInstSrc *is_comptime)
+{
+    IrInstSrcAlloca *instruction = ir_build_instruction<IrInstSrcAlloca>(irb, scope, source_node);
+    instruction->base.is_gen = true;
+    instruction->align = align;
+    instruction->name_hint = name_hint;
+    instruction->is_comptime = is_comptime;
+
+    if (align != nullptr) ir_ref_instruction(align, irb->current_basic_block);
+    if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_end_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *value, ResultLoc *result_loc)
+{
+    IrInstSrcEndExpr *instruction = ir_build_instruction<IrInstSrcEndExpr>(irb, scope, source_node);
+    instruction->base.is_gen = true;
+    instruction->value = value;
+    instruction->result_loc = result_loc;
+
+    ir_ref_instruction(value, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrcSuspendBegin *ir_build_suspend_begin_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    return ir_build_instruction<IrInstSrcSuspendBegin>(irb, scope, source_node);
+}
+
+static IrInstSrc *ir_build_suspend_finish_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrcSuspendBegin *begin)
+{
+    IrInstSrcSuspendFinish *inst = ir_build_instruction<IrInstSrcSuspendFinish>(irb, scope, source_node);
+    inst->begin = begin;
+
+    ir_ref_instruction(&begin->base, irb->current_basic_block);
+
+    return &inst->base;
+}
+
+static IrInstSrc *ir_build_await_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *frame, ResultLoc *result_loc, bool is_nosuspend)
+{
+    IrInstSrcAwait *instruction = ir_build_instruction<IrInstSrcAwait>(irb, scope, source_node);
+    instruction->frame = frame;
+    instruction->result_loc = result_loc;
+    instruction->is_nosuspend = is_nosuspend;
+
+    ir_ref_instruction(frame, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_resume_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *frame) {
+    IrInstSrcResume *instruction = ir_build_instruction<IrInstSrcResume>(irb, scope, source_node);
+    instruction->frame = frame;
+
+    ir_ref_instruction(frame, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrcSpillBegin *ir_build_spill_begin_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrc *operand, SpillId spill_id)
+{
+    IrInstSrcSpillBegin *instruction = ir_build_instruction<IrInstSrcSpillBegin>(irb, scope, source_node);
+    instruction->operand = operand;
+    instruction->spill_id = spill_id;
+
+    ir_ref_instruction(operand, irb->current_basic_block);
+
+    return instruction;
+}
+
+static IrInstSrc *ir_build_spill_end_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        IrInstSrcSpillBegin *begin)
+{
+    IrInstSrcSpillEnd *instruction = ir_build_instruction<IrInstSrcSpillEnd>(irb, scope, source_node);
+    instruction->begin = begin;
+
+    ir_ref_instruction(&begin->base, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index) {
+    IrInstSrcWasmMemorySize *instruction = ir_build_instruction<IrInstSrcWasmMemorySize>(irb, scope, source_node);
+    instruction->index = index;
+
+    ir_ref_instruction(index, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_wasm_memory_grow_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index, IrInstSrc *delta) {
+    IrInstSrcWasmMemoryGrow *instruction = ir_build_instruction<IrInstSrcWasmMemoryGrow>(irb, scope, source_node);
+    instruction->index = index;
+    instruction->delta = delta;
+
+    ir_ref_instruction(index, irb->current_basic_block);
+    ir_ref_instruction(delta, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstSrc *ir_build_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
+    IrInstSrcSrc *instruction = ir_build_instruction<IrInstSrcSrc>(irb, scope, source_node);
+
+    return &instruction->base;
+}
+
+static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
+    results[ReturnKindUnconditional] = 0;
+    results[ReturnKindError] = 0;
+
+    Scope *scope = inner_scope;
+
+    while (scope != outer_scope) {
+        assert(scope);
+        switch (scope->id) {
+            case ScopeIdDefer: {
+                AstNode *defer_node = scope->source_node;
+                assert(defer_node->type == NodeTypeDefer);
+                ReturnKind defer_kind = defer_node->data.defer.kind;
+                results[defer_kind] += 1;
+                scope = scope->parent;
+                continue;
+            }
+            case ScopeIdDecls:
+            case ScopeIdFnDef:
+                return;
+            case ScopeIdBlock:
+            case ScopeIdVarDecl:
+            case ScopeIdLoop:
+            case ScopeIdSuspend:
+            case ScopeIdCompTime:
+            case ScopeIdNoSuspend:
+            case ScopeIdRuntime:
+            case ScopeIdTypeOf:
+            case ScopeIdExpr:
+                scope = scope->parent;
+                continue;
+            case ScopeIdDeferExpr:
+            case ScopeIdCImport:
+                zig_unreachable();
+        }
+    }
+}
+
+static IrInstSrc *ir_mark_gen(IrInstSrc *instruction) {
+    instruction->is_gen = true;
+    return instruction;
+}
+
+static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, bool *is_noreturn, IrInstSrc *err_value) {
+    Scope *scope = inner_scope;
+    if (is_noreturn != nullptr) *is_noreturn = false;
+    while (scope != outer_scope) {
+        if (!scope)
+            return true;
+
+        switch (scope->id) {
+            case ScopeIdDefer: {
+                AstNode *defer_node = scope->source_node;
+                assert(defer_node->type == NodeTypeDefer);
+                ReturnKind defer_kind = defer_node->data.defer.kind;
+                AstNode *defer_expr_node = defer_node->data.defer.expr;
+                AstNode *defer_var_node = defer_node->data.defer.err_payload;
+
+                if (defer_kind == ReturnKindError && err_value == nullptr) {
+                    // This is an `errdefer` but we're generating code for a
+                    // `return` that doesn't return an error, skip it
+                    scope = scope->parent;
+                    continue;
+                }
+
+                Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
+                if (defer_var_node != nullptr) {
+                    assert(defer_kind == ReturnKindError);
+                    assert(defer_var_node->type == NodeTypeIdentifier);
+                    Buf *var_name = node_identifier_buf(defer_var_node);
+
+                    if (defer_expr_node->type == NodeTypeUnreachable) {
+                        add_node_error(irb->codegen, defer_var_node,
+                            buf_sprintf("unused variable: '%s'", buf_ptr(var_name)));
+                        return false;
+                    }
+
+                    IrInstSrc *is_comptime;
+                    if (ir_should_inline(irb->exec, defer_expr_scope)) {
+                        is_comptime = ir_build_const_bool(irb, defer_expr_scope,
+                            defer_expr_node, true);
+                    } else {
+                        is_comptime = ir_build_test_comptime(irb, defer_expr_scope,
+                            defer_expr_node, err_value);
+                    }
+
+                    ZigVar *err_var = ir_create_var(irb, defer_var_node, defer_expr_scope,
+                        var_name, true, true, false, is_comptime);
+                    build_decl_var_and_init(irb, defer_expr_scope, defer_var_node, err_var, err_value,
+                        buf_ptr(var_name), is_comptime);
+
+                    defer_expr_scope = err_var->child_scope;
+                }
+
+                IrInstSrc *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
+                if (defer_expr_value == irb->codegen->invalid_inst_src)
+                    return irb->codegen->invalid_inst_src;
+
+                if (defer_expr_value->is_noreturn) {
+                    if (is_noreturn != nullptr) *is_noreturn = true;
+                } else {
+                    ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node,
+                                defer_expr_value));
+                }
+                scope = scope->parent;
+                continue;
+            }
+            case ScopeIdDecls:
+            case ScopeIdFnDef:
+                return true;
+            case ScopeIdBlock:
+            case ScopeIdVarDecl:
+            case ScopeIdLoop:
+            case ScopeIdSuspend:
+            case ScopeIdCompTime:
+            case ScopeIdNoSuspend:
+            case ScopeIdRuntime:
+            case ScopeIdTypeOf:
+            case ScopeIdExpr:
+                scope = scope->parent;
+                continue;
+            case ScopeIdDeferExpr:
+            case ScopeIdCImport:
+                zig_unreachable();
+        }
+    }
+    return true;
+}
+
+static void ir_set_cursor_at_end(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) {
+    assert(basic_block);
+    irb->current_basic_block = basic_block;
+}
+
+static void ir_set_cursor_at_end_and_append_block(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) {
+    basic_block->index = irb->exec->basic_block_list.length;
+    irb->exec->basic_block_list.append(basic_block);
+    ir_set_cursor_at_end(irb, basic_block);
+}
+
+static ScopeSuspend *get_scope_suspend(Scope *scope) {
+    while (scope) {
+        if (scope->id == ScopeIdSuspend)
+            return (ScopeSuspend *)scope;
+        if (scope->id == ScopeIdFnDef)
+            return nullptr;
+
+        scope = scope->parent;
+    }
+    return nullptr;
+}
+
+static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {
+    while (scope) {
+        if (scope->id == ScopeIdDeferExpr)
+            return (ScopeDeferExpr *)scope;
+        if (scope->id == ScopeIdFnDef)
+            return nullptr;
+
+        scope = scope->parent;
+    }
+    return nullptr;
+}
+
+static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
+    assert(node->type == NodeTypeReturnExpr);
+
+    ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope);
+    if (scope_defer_expr) {
+        if (!scope_defer_expr->reported_err) {
+            add_node_error(irb->codegen, node, buf_sprintf("cannot return from defer expression"));
+            scope_defer_expr->reported_err = true;
+        }
+        return irb->codegen->invalid_inst_src;
+    }
+
+    Scope *outer_scope = irb->exec->begin_scope;
+
+    AstNode *expr_node = node->data.return_expr.expr;
+    switch (node->data.return_expr.kind) {
+        case ReturnKindUnconditional:
+            {
+                ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
+                result_loc_ret->base.id = ResultLocIdReturn;
+                ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
+
+                IrInstSrc *return_value;
+                if (expr_node) {
+                    // Temporarily set this so that if we return a type it gets the name of the function
+                    ZigFn *prev_name_fn = irb->exec->name_fn;
+                    irb->exec->name_fn = exec_fn_entry(irb->exec);
+                    return_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, &result_loc_ret->base);
+                    irb->exec->name_fn = prev_name_fn;
+                    if (return_value == irb->codegen->invalid_inst_src)
+                        return irb->codegen->invalid_inst_src;
+                } else {
+                    return_value = ir_build_const_void(irb, scope, node);
+                    ir_build_end_expr(irb, scope, node, return_value, &result_loc_ret->base);
+                }
+
+                ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value, result_loc_ret));
+
+                size_t defer_counts[2];
+                ir_count_defers(irb, scope, outer_scope, defer_counts);
+                bool have_err_defers = defer_counts[ReturnKindError] > 0;
+                if (!have_err_defers && !irb->codegen->have_err_ret_tracing) {
+                    // only generate unconditional defers
+                    if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr))
+                        return irb->codegen->invalid_inst_src;
+                    IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr);
+                    result_loc_ret->base.source_instruction = result;
+                    return result;
+                }
+                bool should_inline = ir_should_inline(irb->exec, scope);
+
+                IrBasicBlockSrc *err_block = ir_create_basic_block(irb, scope, "ErrRetErr");
+                IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk");
+
+                IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node, return_value, false, true);
+
+                IrInstSrc *is_comptime;
+                if (should_inline) {
+                    is_comptime = ir_build_const_bool(irb, scope, node, should_inline);
+                } else {
+                    is_comptime = ir_build_test_comptime(irb, scope, node, is_err);
+                }
+
+                ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime));
+                IrBasicBlockSrc *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt");
+
+                ir_set_cursor_at_end_and_append_block(irb, err_block);
+                if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, return_value))
+                    return irb->codegen->invalid_inst_src;
+                if (irb->codegen->have_err_ret_tracing && !should_inline) {
+                    ir_build_save_err_ret_addr_src(irb, scope, node);
+                }
+                ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
+
+                ir_set_cursor_at_end_and_append_block(irb, ok_block);
+                if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr))
+                    return irb->codegen->invalid_inst_src;
+                ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
+
+                ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block);
+                IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr);
+                result_loc_ret->base.source_instruction = result;
+                return result;
+            }
+        case ReturnKindError:
+            {
+                assert(expr_node);
+                IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
+                if (err_union_ptr == irb->codegen->invalid_inst_src)
+                    return irb->codegen->invalid_inst_src;
+                IrInstSrc *is_err_val = ir_build_test_err_src(irb, scope, node, err_union_ptr, true, false);
+
+                IrBasicBlockSrc *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn");
+                IrBasicBlockSrc *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue");
+                IrInstSrc *is_comptime;
+                bool should_inline = ir_should_inline(irb->exec, scope);
+                if (should_inline) {
+                    is_comptime = ir_build_const_bool(irb, scope, node, true);
+                } else {
+                    is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val);
+                }
+                ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime));
+
+                ir_set_cursor_at_end_and_append_block(irb, return_block);
+                IrInstSrc *err_val_ptr = ir_build_unwrap_err_code_src(irb, scope, node, err_union_ptr);
+                IrInstSrc *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr);
+                ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr));
+                IrInstSrcSpillBegin *spill_begin = ir_build_spill_begin_src(irb, scope, node, err_val,
+                        SpillIdRetErrCode);
+                ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
+                result_loc_ret->base.id = ResultLocIdReturn;
+                ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
+                ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base);
+
+                bool is_noreturn = false;
+                if (!ir_gen_defers_for_block(irb, scope, outer_scope, &is_noreturn, err_val)) {
+                    return irb->codegen->invalid_inst_src;
+                }
+                if (!is_noreturn) {
+                    if (irb->codegen->have_err_ret_tracing && !should_inline) {
+                        ir_build_save_err_ret_addr_src(irb, scope, node);
+                    }
+                    err_val = ir_build_spill_end_src(irb, scope, node, spill_begin);
+                    IrInstSrc *ret_inst = ir_build_return_src(irb, scope, node, err_val);
+                    result_loc_ret->base.source_instruction = ret_inst;
+                }
+
+                ir_set_cursor_at_end_and_append_block(irb, continue_block);
+                IrInstSrc *unwrapped_ptr = ir_build_unwrap_err_payload_src(irb, scope, node, err_union_ptr, false, false);
+                if (lval == LValPtr)
+                    return unwrapped_ptr;
+                else
+                    return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, unwrapped_ptr), result_loc);
+            }
+    }
+    zig_unreachable();
+}
+
+ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope,
+        Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime,
+        bool skip_name_check)
+{
+    ZigVar *variable_entry = heap::c_allocator.create<ZigVar>();
+    variable_entry->parent_scope = parent_scope;
+    variable_entry->shadowable = is_shadowable;
+    variable_entry->is_comptime = is_comptime;
+    variable_entry->src_arg_index = SIZE_MAX;
+    variable_entry->const_value = codegen->pass1_arena->create<ZigValue>();
+
+    if (is_comptime != nullptr) {
+        is_comptime->base.ref_count += 1;
+    }
+
+    if (name) {
+        variable_entry->name = strdup(buf_ptr(name));
+
+        if (!skip_name_check) {
+            ZigVar *existing_var = find_variable(codegen, parent_scope, name, nullptr);
+            if (existing_var && !existing_var->shadowable) {
+                if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) {
+                    ErrorMsg *msg = add_node_error(codegen, node,
+                            buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
+                    add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
+                }
+                variable_entry->var_type = codegen->builtin_types.entry_invalid;
+            } else {
+                ZigType *type;
+                if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) {
+                    add_node_error(codegen, node,
+                            buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name)));
+                    variable_entry->var_type = codegen->builtin_types.entry_invalid;
+                } else {
+                    Tld *tld = find_decl(codegen, parent_scope, name);
+                    if (tld != nullptr) {
+                        bool want_err_msg = true;
+                        if (tld->id == TldIdVar) {
+                            ZigVar *var = reinterpret_cast<TldVar *>(tld)->var;
+                            if (var != nullptr && var->var_type != nullptr && type_is_invalid(var->var_type)) {
+                                want_err_msg = false;
+                            }
+                        }
+                        if (want_err_msg) {
+                            ErrorMsg *msg = add_node_error(codegen, node,
+                                    buf_sprintf("redefinition of '%s'", buf_ptr(name)));
+                            add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here"));
+                        }
+                        variable_entry->var_type = codegen->builtin_types.entry_invalid;
+                    }
+                }
+            }
+        }
+    } else {
+        assert(is_shadowable);
+        // TODO make this name not actually be in scope. user should be able to make a variable called "_anon"
+        // might already be solved, let's just make sure it has test coverage
+        // maybe we put a prefix on this so the debug info doesn't clobber user debug info for same named variables
+        variable_entry->name = "_anon";
+    }
+
+    variable_entry->src_is_const = src_is_const;
+    variable_entry->gen_is_const = gen_is_const;
+    variable_entry->decl_node = node;
+    variable_entry->child_scope = create_var_scope(codegen, node, parent_scope, variable_entry);
+
+    return variable_entry;
+}
+
+
+// Set name to nullptr to make the variable anonymous (not visible to programmer).
+// After you call this function var->child_scope has the variable in scope
+static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name,
+        bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime)
+{
+    bool is_underscored = name ? buf_eql_str(name, "_") : false;
+    ZigVar *var = create_local_var(irb->codegen, node, scope,
+            (is_underscored ? nullptr : name), src_is_const, gen_is_const,
+            (is_underscored ? true : is_shadowable), is_comptime, false);
+    assert(var->child_scope);
+    return var;
+}
+
+static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) {
+    ResultLocPeer *result = heap::c_allocator.create<ResultLocPeer>();
+    result->base.id = ResultLocIdPeer;
+    result->base.source_instruction = peer_parent->base.source_instruction;
+    result->parent = peer_parent;
+    result->base.allow_write_through_const = peer_parent->parent->allow_write_through_const;
+    return result;
+}
+
+static bool is_duplicate_label(CodeGen *g, Scope *scope, AstNode *node, Buf *name) {
+    if (name == nullptr) return false;
+
+    for (;;) {
+        if (scope == nullptr || scope->id == ScopeIdFnDef) {
+            break;
+        } else if (scope->id == ScopeIdBlock || scope->id == ScopeIdLoop) {
+            Buf *this_block_name = scope->id == ScopeIdBlock ? ((ScopeBlock *)scope)->name : ((ScopeLoop *)scope)->name;
+            if (this_block_name != nullptr && buf_eql_buf(name, this_block_name)) {
+                ErrorMsg *msg = add_node_error(g, node, buf_sprintf("redeclaration of label '%s'", buf_ptr(name)));
+                add_error_note(g, msg, scope->source_node, buf_sprintf("previous declaration is here"));
+                return true;
+            }
+        }
+        scope = scope->parent;
+    }
+    return false;
+}
+
+static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *block_node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(block_node->type == NodeTypeBlock);
+
+    ZigList<IrInstSrc *> incoming_values = {0};
+    ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
+
+    if (is_duplicate_label(irb->codegen, parent_scope, block_node, block_node->data.block.name))
+        return irb->codegen->invalid_inst_src;
+
+    ScopeBlock *scope_block = create_block_scope(irb->codegen, block_node, parent_scope);
+
+    Scope *outer_block_scope = &scope_block->base;
+    Scope *child_scope = outer_block_scope;
+
+    ZigFn *fn_entry = scope_fn_entry(parent_scope);
+    if (fn_entry && fn_entry->child_scope == parent_scope) {
+        fn_entry->def_scope = scope_block;
+    }
+
+    if (block_node->data.block.statements.length == 0) {
+        if (scope_block->name != nullptr) {
+            add_node_error(irb->codegen, block_node, buf_sprintf("unused block label"));
+        }
+        // {}
+        return ir_lval_wrap(irb, parent_scope, ir_build_const_void(irb, child_scope, block_node), lval, result_loc);
+    }
+
+    if (block_node->data.block.name != nullptr) {
+        scope_block->lval = lval;
+        scope_block->incoming_blocks = &incoming_blocks;
+        scope_block->incoming_values = &incoming_values;
+        scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd");
+        scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node,
+                ir_should_inline(irb->exec, parent_scope));
+
+        scope_block->peer_parent = heap::c_allocator.create<ResultLocPeerParent>();
+        scope_block->peer_parent->base.id = ResultLocIdPeerParent;
+        scope_block->peer_parent->base.source_instruction = scope_block->is_comptime;
+        scope_block->peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const;
+        scope_block->peer_parent->end_bb = scope_block->end_block;
+        scope_block->peer_parent->is_comptime = scope_block->is_comptime;
+        scope_block->peer_parent->parent = result_loc;
+        ir_build_reset_result(irb, parent_scope, block_node, &scope_block->peer_parent->base);
+    }
+
+    bool is_continuation_unreachable = false;
+    bool found_invalid_inst = false;
+    IrInstSrc *noreturn_return_value = nullptr;
+    for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) {
+        AstNode *statement_node = block_node->data.block.statements.at(i);
+
+        IrInstSrc *statement_value = ir_gen_node(irb, statement_node, child_scope);
+        if (statement_value == irb->codegen->invalid_inst_src) {
+            // keep generating all the elements of the block in case of error,
+            // we want to collect other compile errors
+            found_invalid_inst = true;
+            continue;
+        }
+
+        is_continuation_unreachable = instr_is_unreachable(statement_value);
+        if (is_continuation_unreachable) {
+            // keep the last noreturn statement value around in case we need to return it
+            noreturn_return_value = statement_value;
+        }
+        // This logic must be kept in sync with
+        // [STMT_EXPR_TEST_THING] <--- (search this token)
+        if (statement_node->type == NodeTypeDefer) {
+            // defer starts a new scope
+            child_scope = statement_node->data.defer.child_scope;
+            assert(child_scope);
+        } else if (statement_value->id == IrInstSrcIdDeclVar) {
+            // variable declarations start a new scope
+            IrInstSrcDeclVar *decl_var_instruction = (IrInstSrcDeclVar *)statement_value;
+            child_scope = decl_var_instruction->var->child_scope;
+        } else if (!is_continuation_unreachable) {
+            // this statement's value must be void
+            ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value));
+        }
+    }
+
+    if (scope_block->name != nullptr && scope_block->name_used == false) {
+        add_node_error(irb->codegen, block_node, buf_sprintf("unused block label"));
+    }
+
+    if (found_invalid_inst)
+        return irb->codegen->invalid_inst_src;
+
+    if (is_continuation_unreachable) {
+        assert(noreturn_return_value != nullptr);
+        if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) {
+            return noreturn_return_value;
+        }
+
+        if (scope_block->peer_parent != nullptr && scope_block->peer_parent->peers.length != 0) {
+            scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block;
+        }
+        ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
+        IrInstSrc *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length,
+                incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
+        return ir_expr_wrap(irb, parent_scope, phi, result_loc);
+    } else {
+        incoming_blocks.append(irb->current_basic_block);
+        IrInstSrc *else_expr_result = ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node));
+
+        if (scope_block->peer_parent != nullptr) {
+            ResultLocPeer *peer_result = create_peer_result(scope_block->peer_parent);
+            scope_block->peer_parent->peers.append(peer_result);
+            ir_build_end_expr(irb, parent_scope, block_node, else_expr_result, &peer_result->base);
+
+            if (scope_block->peer_parent->peers.length != 0) {
+                scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block;
+            }
+        }
+
+        incoming_values.append(else_expr_result);
+    }
+
+    bool is_return_from_fn = block_node == irb->main_block_node;
+    if (!is_return_from_fn) {
+        if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr))
+            return irb->codegen->invalid_inst_src;
+    }
+
+    IrInstSrc *result;
+    if (block_node->data.block.name != nullptr) {
+        ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime));
+        ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
+        IrInstSrc *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length,
+                incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
+        result = ir_expr_wrap(irb, parent_scope, phi, result_loc);
+    } else {
+        IrInstSrc *void_inst = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
+        result = ir_lval_wrap(irb, parent_scope, void_inst, lval, result_loc);
+    }
+    if (!is_return_from_fn)
+        return result;
+
+    // no need for save_err_ret_addr because this cannot return error
+    // only generate unconditional defers
+
+    ir_mark_gen(ir_build_add_implicit_return_type(irb, child_scope, block_node, result, nullptr));
+    ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
+    result_loc_ret->base.id = ResultLocIdReturn;
+    ir_build_reset_result(irb, parent_scope, block_node, &result_loc_ret->base);
+    ir_mark_gen(ir_build_end_expr(irb, parent_scope, block_node, result, &result_loc_ret->base));
+    if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr))
+        return irb->codegen->invalid_inst_src;
+    return ir_mark_gen(ir_build_return_src(irb, child_scope, result->base.source_node, result));
+}
+
+static IrInstSrc *ir_gen_bin_op_id(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
+    Scope *inner_scope = scope;
+    if (op_id == IrBinOpArrayCat || op_id == IrBinOpArrayMult) {
+        inner_scope = create_comptime_scope(irb->codegen, node, scope);
+    }
+
+    IrInstSrc *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, inner_scope);
+    IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, inner_scope);
+
+    if (op1 == irb->codegen->invalid_inst_src || op2 == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    return ir_build_bin_op(irb, scope, node, op_id, op1, op2, true);
+}
+
+static IrInstSrc *ir_gen_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    IrInstSrc *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
+    IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
+
+    if (op1 == irb->codegen->invalid_inst_src || op2 == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    // TODO only pass type_name when the || operator is the top level AST node in the var decl expr
+    Buf bare_name = BUF_INIT;
+    Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", scope, node, &bare_name);
+
+    return ir_build_merge_err_sets(irb, scope, node, op1, op2, type_name);
+}
+
+static IrInstSrc *ir_gen_assign(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr);
+    if (lvalue == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
+    result_loc_inst->base.id = ResultLocIdInstruction;
+    result_loc_inst->base.source_instruction = lvalue;
+    ir_ref_instruction(lvalue, irb->current_basic_block);
+    ir_build_reset_result(irb, scope, node, &result_loc_inst->base);
+
+    IrInstSrc *rvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op2, scope, LValNone,
+            &result_loc_inst->base);
+    if (rvalue == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    return ir_build_const_void(irb, scope, node);
+}
+
+static IrInstSrc *ir_gen_assign_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
+    IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr);
+    if (lvalue == irb->codegen->invalid_inst_src)
+        return lvalue;
+    IrInstSrc *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue);
+    IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
+    if (op2 == irb->codegen->invalid_inst_src)
+        return op2;
+    IrInstSrc *result = ir_build_bin_op(irb, scope, node, op_id, op1, op2, true);
+    ir_build_store_ptr(irb, scope, node, lvalue, result);
+    return ir_build_const_void(irb, scope, node);
+}
+
+static IrInstSrc *ir_gen_bool_or(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeBinOpExpr);
+
+    IrInstSrc *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
+    if (val1 == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+    IrBasicBlockSrc *post_val1_block = irb->current_basic_block;
+
+    IrInstSrc *is_comptime;
+    if (ir_should_inline(irb->exec, scope)) {
+        is_comptime = ir_build_const_bool(irb, scope, node, true);
+    } else {
+        is_comptime = ir_build_test_comptime(irb, scope, node, val1);
+    }
+
+    // block for when val1 == false
+    IrBasicBlockSrc *false_block = ir_create_basic_block(irb, scope, "BoolOrFalse");
+    // block for when val1 == true (don't even evaluate the second part)
+    IrBasicBlockSrc *true_block = ir_create_basic_block(irb, scope, "BoolOrTrue");
+
+    ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, false_block);
+    IrInstSrc *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
+    if (val2 == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+    IrBasicBlockSrc *post_val2_block = irb->current_basic_block;
+
+    ir_build_br(irb, scope, node, true_block, is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, true_block);
+
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
+    incoming_values[0] = val1;
+    incoming_values[1] = val2;
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
+    incoming_blocks[0] = post_val1_block;
+    incoming_blocks[1] = post_val2_block;
+
+    return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr);
+}
+
+static IrInstSrc *ir_gen_bool_and(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeBinOpExpr);
+
+    IrInstSrc *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
+    if (val1 == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+    IrBasicBlockSrc *post_val1_block = irb->current_basic_block;
+
+    IrInstSrc *is_comptime;
+    if (ir_should_inline(irb->exec, scope)) {
+        is_comptime = ir_build_const_bool(irb, scope, node, true);
+    } else {
+        is_comptime = ir_build_test_comptime(irb, scope, node, val1);
+    }
+
+    // block for when val1 == true
+    IrBasicBlockSrc *true_block = ir_create_basic_block(irb, scope, "BoolAndTrue");
+    // block for when val1 == false (don't even evaluate the second part)
+    IrBasicBlockSrc *false_block = ir_create_basic_block(irb, scope, "BoolAndFalse");
+
+    ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, true_block);
+    IrInstSrc *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
+    if (val2 == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+    IrBasicBlockSrc *post_val2_block = irb->current_basic_block;
+
+    ir_build_br(irb, scope, node, false_block, is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, false_block);
+
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
+    incoming_values[0] = val1;
+    incoming_values[1] = val2;
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
+    incoming_blocks[0] = post_val1_block;
+    incoming_blocks[1] = post_val2_block;
+
+    return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr);
+}
+
+static ResultLocPeerParent *ir_build_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst,
+        IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime)
+{
+    ResultLocPeerParent *peer_parent = heap::c_allocator.create<ResultLocPeerParent>();
+    peer_parent->base.id = ResultLocIdPeerParent;
+    peer_parent->base.source_instruction = cond_br_inst;
+    peer_parent->base.allow_write_through_const = parent->allow_write_through_const;
+    peer_parent->end_bb = end_block;
+    peer_parent->is_comptime = is_comptime;
+    peer_parent->parent = parent;
+
+    IrInstSrc *popped_inst = irb->current_basic_block->instruction_list.pop();
+    ir_assert(popped_inst == cond_br_inst, &cond_br_inst->base);
+
+    ir_build_reset_result(irb, cond_br_inst->base.scope, cond_br_inst->base.source_node, &peer_parent->base);
+    irb->current_basic_block->instruction_list.append(popped_inst);
+
+    return peer_parent;
+}
+
+static ResultLocPeerParent *ir_build_binary_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst,
+        IrBasicBlockSrc *else_block, IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime)
+{
+    ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, parent, is_comptime);
+
+    peer_parent->peers.append(create_peer_result(peer_parent));
+    peer_parent->peers.last()->next_bb = else_block;
+
+    peer_parent->peers.append(create_peer_result(peer_parent));
+    peer_parent->peers.last()->next_bb = end_block;
+
+    return peer_parent;
+}
+
+static IrInstSrc *ir_gen_orelse(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypeBinOpExpr);
+
+    AstNode *op1_node = node->data.bin_op_expr.op1;
+    AstNode *op2_node = node->data.bin_op_expr.op2;
+
+    IrInstSrc *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr, nullptr);
+    if (maybe_ptr == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr);
+    IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, parent_scope, node, maybe_val);
+
+    IrInstSrc *is_comptime;
+    if (ir_should_inline(irb->exec, parent_scope)) {
+        is_comptime = ir_build_const_bool(irb, parent_scope, node, true);
+    } else {
+        is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null);
+    }
+
+    IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull");
+    IrBasicBlockSrc *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull");
+    IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd");
+    IrInstSrc *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime);
+
+    ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block,
+            result_loc, is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, null_block);
+    IrInstSrc *null_result = ir_gen_node_extra(irb, op2_node, parent_scope, LValNone,
+            &peer_parent->peers.at(0)->base);
+    if (null_result == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+    IrBasicBlockSrc *after_null_block = irb->current_basic_block;
+    if (!instr_is_unreachable(null_result))
+        ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
+
+    ir_set_cursor_at_end_and_append_block(irb, ok_block);
+    IrInstSrc *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, parent_scope, node, maybe_ptr, false);
+    IrInstSrc *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
+    ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base);
+    IrBasicBlockSrc *after_ok_block = irb->current_basic_block;
+    ir_build_br(irb, parent_scope, node, end_block, is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, end_block);
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
+    incoming_values[0] = null_result;
+    incoming_values[1] = unwrapped_payload;
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
+    incoming_blocks[0] = after_null_block;
+    incoming_blocks[1] = after_ok_block;
+    IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
+    return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
+}
+
+static IrInstSrc *ir_gen_error_union(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
+    assert(node->type == NodeTypeBinOpExpr);
+
+    AstNode *op1_node = node->data.bin_op_expr.op1;
+    AstNode *op2_node = node->data.bin_op_expr.op2;
+
+    IrInstSrc *err_set = ir_gen_node(irb, op1_node, parent_scope);
+    if (err_set == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *payload = ir_gen_node(irb, op2_node, parent_scope);
+    if (payload == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    return ir_build_error_union(irb, parent_scope, node, err_set, payload);
+}
+
+static IrInstSrc *ir_gen_bin_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
+    assert(node->type == NodeTypeBinOpExpr);
+
+    BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
+    switch (bin_op_type) {
+        case BinOpTypeInvalid:
+            zig_unreachable();
+        case BinOpTypeAssign:
+            return ir_lval_wrap(irb, scope, ir_gen_assign(irb, scope, node), lval, result_loc);
+        case BinOpTypeAssignTimes:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMult), lval, result_loc);
+        case BinOpTypeAssignTimesWrap:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap), lval, result_loc);
+        case BinOpTypeAssignDiv:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc);
+        case BinOpTypeAssignMod:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc);
+        case BinOpTypeAssignPlus:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAdd), lval, result_loc);
+        case BinOpTypeAssignPlusWrap:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap), lval, result_loc);
+        case BinOpTypeAssignMinus:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSub), lval, result_loc);
+        case BinOpTypeAssignMinusWrap:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap), lval, result_loc);
+        case BinOpTypeAssignBitShiftLeft:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
+        case BinOpTypeAssignBitShiftRight:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
+        case BinOpTypeAssignBitAnd:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd), lval, result_loc);
+        case BinOpTypeAssignBitXor:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinXor), lval, result_loc);
+        case BinOpTypeAssignBitOr:
+            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinOr), lval, result_loc);
+        case BinOpTypeBoolOr:
+            return ir_lval_wrap(irb, scope, ir_gen_bool_or(irb, scope, node), lval, result_loc);
+        case BinOpTypeBoolAnd:
+            return ir_lval_wrap(irb, scope, ir_gen_bool_and(irb, scope, node), lval, result_loc);
+        case BinOpTypeCmpEq:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq), lval, result_loc);
+        case BinOpTypeCmpNotEq:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq), lval, result_loc);
+        case BinOpTypeCmpLessThan:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan), lval, result_loc);
+        case BinOpTypeCmpGreaterThan:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan), lval, result_loc);
+        case BinOpTypeCmpLessOrEq:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq), lval, result_loc);
+        case BinOpTypeCmpGreaterOrEq:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq), lval, result_loc);
+        case BinOpTypeBinOr:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr), lval, result_loc);
+        case BinOpTypeBinXor:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor), lval, result_loc);
+        case BinOpTypeBinAnd:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd), lval, result_loc);
+        case BinOpTypeBitShiftLeft:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
+        case BinOpTypeBitShiftRight:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
+        case BinOpTypeAdd:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd), lval, result_loc);
+        case BinOpTypeAddWrap:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap), lval, result_loc);
+        case BinOpTypeSub:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSub), lval, result_loc);
+        case BinOpTypeSubWrap:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap), lval, result_loc);
+        case BinOpTypeMult:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMult), lval, result_loc);
+        case BinOpTypeMultWrap:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap), lval, result_loc);
+        case BinOpTypeDiv:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc);
+        case BinOpTypeMod:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc);
+        case BinOpTypeArrayCat:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat), lval, result_loc);
+        case BinOpTypeArrayMult:
+            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult), lval, result_loc);
+        case BinOpTypeMergeErrorSets:
+            return ir_lval_wrap(irb, scope, ir_gen_merge_err_sets(irb, scope, node), lval, result_loc);
+        case BinOpTypeUnwrapOptional:
+            return ir_gen_orelse(irb, scope, node, lval, result_loc);
+        case BinOpTypeErrorUnion:
+            return ir_lval_wrap(irb, scope, ir_gen_error_union(irb, scope, node), lval, result_loc);
+    }
+    zig_unreachable();
+}
+
+static IrInstSrc *ir_gen_int_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeIntLiteral);
+
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    BigInt bigint;
+    token_number_literal_bigint(root_struct, &bigint, node->main_token);
+    return ir_build_const_bigint(irb, scope, node, bigint);
+}
+
+static IrInstSrc *ir_gen_float_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    Error err;
+    assert(node->type == NodeTypeFloatLiteral);
+
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    const char *source = buf_ptr(root_struct->source_code);
+    uint32_t byte_offset = root_struct->token_locs[node->main_token].offset;
+
+    BigFloat bigfloat;
+    if ((err = bigfloat_init_buf(&bigfloat, (const uint8_t *)source + byte_offset))) {
+        add_node_error(irb->codegen, node, buf_sprintf("float literal out of range of any type"));
+        return irb->codegen->invalid_inst_src;
+    }
+
+    return ir_build_const_bigfloat(irb, scope, node, bigfloat);
+}
+
+static IrInstSrc *ir_gen_char_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    Error err;
+    assert(node->type == NodeTypeCharLiteral);
+
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    const char *source = buf_ptr(root_struct->source_code);
+    uint32_t byte_offset = root_struct->token_locs[node->main_token].offset;
+
+    src_assert(source[byte_offset] == '\'', node);
+    byte_offset += 1;
+
+    uint32_t codepoint;
+    size_t bad_index;
+    if ((err = source_char_literal(source + byte_offset, &codepoint, &bad_index))) {
+        add_node_error(irb->codegen, node, buf_sprintf("invalid character"));
+        return irb->codegen->invalid_inst_src;
+    }
+    return ir_build_const_uint(irb, scope, node, codepoint);
+}
+
+static IrInstSrc *ir_gen_null_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeNullLiteral);
+
+    return ir_build_const_null(irb, scope, node);
+}
+
+static IrInstSrc *ir_gen_symbol(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
+    Error err;
+    assert(node->type == NodeTypeIdentifier);
+
+    Buf *variable_name = node_identifier_buf(node);
+
+    if (buf_eql_str(variable_name, "_")) {
+        if (lval == LValAssign) {
+            IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, node);
+            const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
+            const_instruction->value->type = get_pointer_to_type(irb->codegen,
+                    irb->codegen->builtin_types.entry_void, false);
+            const_instruction->value->special = ConstValSpecialStatic;
+            const_instruction->value->data.x_ptr.special = ConstPtrSpecialDiscard;
+            return &const_instruction->base;
+        } else {
+            add_node_error(irb->codegen, node, buf_sprintf("`_` may only be used to assign things to"));
+            return irb->codegen->invalid_inst_src;
+        }
+    }
+
+    ZigType *primitive_type;
+    if ((err = get_primitive_type(irb->codegen, variable_name, &primitive_type))) {
+        if (err == ErrorOverflow) {
+            add_node_error(irb->codegen, node,
+                buf_sprintf("primitive integer type '%s' exceeds maximum bit width of 65535",
+                    buf_ptr(variable_name)));
+            return irb->codegen->invalid_inst_src;
+        }
+        assert(err == ErrorPrimitiveTypeNotFound);
+    } else {
+        IrInstSrc *value = ir_build_const_type(irb, scope, node, primitive_type);
+        if (lval == LValPtr || lval == LValAssign) {
+            return ir_build_ref_src(irb, scope, node, value);
+        } else {
+            return ir_expr_wrap(irb, scope, value, result_loc);
+        }
+    }
+
+    ScopeFnDef *crossed_fndef_scope;
+    ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope);
+    if (var) {
+        IrInstSrc *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope);
+        if (lval == LValPtr || lval == LValAssign) {
+            return var_ptr;
+        } else {
+            return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, var_ptr), result_loc);
+        }
+    }
+
+    Tld *tld = find_decl(irb->codegen, scope, variable_name);
+    if (tld) {
+        IrInstSrc *decl_ref = ir_build_decl_ref(irb, scope, node, tld, lval);
+        if (lval == LValPtr || lval == LValAssign) {
+            return decl_ref;
+        } else {
+            return ir_expr_wrap(irb, scope, decl_ref, result_loc);
+        }
+    }
+
+    if (get_container_scope(node->owner)->any_imports_failed) {
+        // skip the error message since we had a failing import in this file
+        // if an import breaks we don't need redundant undeclared identifier errors
+        return irb->codegen->invalid_inst_src;
+    }
+
+    return ir_build_undeclared_identifier(irb, scope, node, variable_name);
+}
+
+static IrInstSrc *ir_gen_array_access(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypeArrayAccessExpr);
+
+    AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr;
+    IrInstSrc *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr, nullptr);
+    if (array_ref_instruction == irb->codegen->invalid_inst_src)
+        return array_ref_instruction;
+
+    // Create an usize-typed result location to hold the subscript value, this
+    // makes it possible for the compiler to infer the subscript expression type
+    // if needed
+    IrInstSrc *usize_type_inst = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
+    ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, usize_type_inst, no_result_loc());
+
+    AstNode *subscript_node = node->data.array_access_expr.subscript;
+    IrInstSrc *subscript_value = ir_gen_node_extra(irb, subscript_node, scope, LValNone, &result_loc_cast->base);
+    if (subscript_value == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *subscript_instruction = ir_build_implicit_cast(irb, scope, subscript_node, subscript_value, result_loc_cast);
+
+    IrInstSrc *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction,
+            subscript_instruction, true, PtrLenSingle, nullptr);
+    if (lval == LValPtr || lval == LValAssign)
+        return ptr_instruction;
+
+    IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
+    return ir_expr_wrap(irb, scope, load_ptr, result_loc);
+}
+
+static IrInstSrc *ir_gen_field_access(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeFieldAccessExpr);
+
+    AstNode *container_ref_node = node->data.field_access_expr.struct_expr;
+    Buf *field_name = node->data.field_access_expr.field_name;
+
+    IrInstSrc *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr, nullptr);
+    if (container_ref_instruction == irb->codegen->invalid_inst_src)
+        return container_ref_instruction;
+
+    return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name, false);
+}
+
+static IrInstSrc *ir_gen_overflow_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrOverflowOp op) {
+    assert(node->type == NodeTypeFnCallExpr);
+
+    AstNode *type_node = node->data.fn_call_expr.params.at(0);
+    AstNode *op1_node = node->data.fn_call_expr.params.at(1);
+    AstNode *op2_node = node->data.fn_call_expr.params.at(2);
+    AstNode *result_ptr_node = node->data.fn_call_expr.params.at(3);
+
+
+    IrInstSrc *type_value = ir_gen_node(irb, type_node, scope);
+    if (type_value == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *op1 = ir_gen_node(irb, op1_node, scope);
+    if (op1 == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *op2 = ir_gen_node(irb, op2_node, scope);
+    if (op2 == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *result_ptr = ir_gen_node(irb, result_ptr_node, scope);
+    if (result_ptr == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    return ir_build_overflow_op_src(irb, scope, node, op, type_value, op1, op2, result_ptr);
+}
+
+static IrInstSrc *ir_gen_mul_add(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeFnCallExpr);
+
+    AstNode *type_node = node->data.fn_call_expr.params.at(0);
+    AstNode *op1_node = node->data.fn_call_expr.params.at(1);
+    AstNode *op2_node = node->data.fn_call_expr.params.at(2);
+    AstNode *op3_node = node->data.fn_call_expr.params.at(3);
+
+    IrInstSrc *type_value = ir_gen_node(irb, type_node, scope);
+    if (type_value == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *op1 = ir_gen_node(irb, op1_node, scope);
+    if (op1 == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *op2 = ir_gen_node(irb, op2_node, scope);
+    if (op2 == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *op3 = ir_gen_node(irb, op3_node, scope);
+    if (op3 == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    return ir_build_mul_add_src(irb, scope, node, type_value, op1, op2, op3);
+}
+
+static IrInstSrc *ir_gen_this(IrBuilderSrc *irb, Scope *orig_scope, AstNode *node) {
+    for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) {
+        if (it_scope->id == ScopeIdDecls) {
+            ScopeDecls *decls_scope = (ScopeDecls *)it_scope;
+            ZigType *container_type = decls_scope->container_type;
+            if (container_type != nullptr) {
+                return ir_build_const_type(irb, orig_scope, node, container_type);
+            } else {
+                return ir_build_const_import(irb, orig_scope, node, decls_scope->import);
+            }
+        }
+    }
+    zig_unreachable();
+}
+
+static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *await_node, AstNode *call_node,
+        LVal lval, ResultLoc *result_loc)
+{
+    if (call_node->data.fn_call_expr.params.length != 4) {
+        add_node_error(irb->codegen, call_node,
+            buf_sprintf("expected 4 arguments, found %" ZIG_PRI_usize,
+                call_node->data.fn_call_expr.params.length));
+        return irb->codegen->invalid_inst_src;
+    }
+
+    AstNode *bytes_node = call_node->data.fn_call_expr.params.at(0);
+    IrInstSrc *bytes = ir_gen_node(irb, bytes_node, scope);
+    if (bytes == irb->codegen->invalid_inst_src)
+        return bytes;
+
+    AstNode *ret_ptr_node = call_node->data.fn_call_expr.params.at(1);
+    IrInstSrc *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope);
+    if (ret_ptr == irb->codegen->invalid_inst_src)
+        return ret_ptr;
+
+    AstNode *fn_ref_node = call_node->data.fn_call_expr.params.at(2);
+    IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
+    if (fn_ref == irb->codegen->invalid_inst_src)
+        return fn_ref;
+
+    CallModifier modifier = (await_node == nullptr) ? CallModifierAsync : CallModifierNone;
+    bool is_async_call_builtin = true;
+    AstNode *args_node = call_node->data.fn_call_expr.params.at(3);
+    if (args_node->type == NodeTypeContainerInitExpr) {
+        if (args_node->data.container_init_expr.kind == ContainerInitKindArray ||
+            args_node->data.container_init_expr.entries.length == 0)
+        {
+            size_t arg_count = args_node->data.container_init_expr.entries.length;
+            IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
+            for (size_t i = 0; i < arg_count; i += 1) {
+                AstNode *arg_node = args_node->data.container_init_expr.entries.at(i);
+                IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
+                if (arg == irb->codegen->invalid_inst_src)
+                    return arg;
+                args[i] = arg;
+            }
+
+            IrInstSrc *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args,
+                ret_ptr, modifier, is_async_call_builtin, bytes, result_loc);
+            return ir_lval_wrap(irb, scope, call, lval, result_loc);
+        } else {
+            exec_add_error_node(irb->codegen, irb->exec, args_node,
+                    buf_sprintf("TODO: @asyncCall with anon struct literal"));
+            return irb->codegen->invalid_inst_src;
+        }
+    }
+    IrInstSrc *args = ir_gen_node(irb, args_node, scope);
+    if (args == irb->codegen->invalid_inst_src)
+        return args;
+
+    IrInstSrc *call = ir_build_async_call_extra(irb, scope, call_node, modifier, fn_ref, ret_ptr, bytes, args, result_loc);
+    return ir_lval_wrap(irb, scope, call, lval, result_loc);
+}
+
+static IrInstSrc *ir_gen_fn_call_with_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        AstNode *fn_ref_node, CallModifier modifier, IrInstSrc *options,
+        AstNode **args_ptr, size_t args_len, LVal lval, ResultLoc *result_loc)
+{
+    IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
+    if (fn_ref == irb->codegen->invalid_inst_src)
+        return fn_ref;
+
+    IrInstSrc *fn_type = ir_build_typeof_1(irb, scope, source_node, fn_ref);
+
+    IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(args_len);
+    for (size_t i = 0; i < args_len; i += 1) {
+        AstNode *arg_node = args_ptr[i];
+
+        IrInstSrc *arg_index = ir_build_const_usize(irb, scope, arg_node, i);
+        IrInstSrc *arg_type = ir_build_arg_type(irb, scope, source_node, fn_type, arg_index, true);
+        ResultLoc *no_result = no_result_loc();
+        ir_build_reset_result(irb, scope, source_node, no_result);
+        ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, arg_type, no_result);
+
+        IrInstSrc *arg = ir_gen_node_extra(irb, arg_node, scope, LValNone, &result_loc_cast->base);
+        if (arg == irb->codegen->invalid_inst_src)
+            return arg;
+
+        args[i] = ir_build_implicit_cast(irb, scope, arg_node, arg, result_loc_cast);
+    }
+
+    IrInstSrc *fn_call;
+    if (options != nullptr) {
+        fn_call = ir_build_call_args(irb, scope, source_node, options, fn_ref, args, args_len, result_loc);
+    } else {
+        fn_call = ir_build_call_src(irb, scope, source_node, nullptr, fn_ref, args_len, args, nullptr,
+                modifier, false, nullptr, result_loc);
+    }
+    return ir_lval_wrap(irb, scope, fn_call, lval, result_loc);
+}
+
+static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypeFnCallExpr);
+
+    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
+    Buf *name = node_identifier_buf(fn_ref_expr);
+    auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
+
+    if (!entry) {
+        add_node_error(irb->codegen, node,
+                buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
+        return irb->codegen->invalid_inst_src;
+    }
+
+    BuiltinFnEntry *builtin_fn = entry->value;
+    size_t actual_param_count = node->data.fn_call_expr.params.length;
+
+    if (builtin_fn->param_count != SIZE_MAX && builtin_fn->param_count != actual_param_count) {
+        add_node_error(irb->codegen, node,
+                buf_sprintf("expected %" ZIG_PRI_usize " argument(s), found %" ZIG_PRI_usize,
+                    builtin_fn->param_count, actual_param_count));
+        return irb->codegen->invalid_inst_src;
+    }
+
+    switch (builtin_fn->id) {
+        case BuiltinFnIdInvalid:
+            zig_unreachable();
+        case BuiltinFnIdTypeof:
+            {
+                Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope);
+
+                size_t arg_count = node->data.fn_call_expr.params.length;
+
+                IrInstSrc *type_of;
+
+                if (arg_count == 0) {
+                    add_node_error(irb->codegen, node,
+                        buf_sprintf("expected at least 1 argument, found 0"));
+                    return irb->codegen->invalid_inst_src;
+                } else if (arg_count == 1) {
+                    AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                    IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, sub_scope);
+                    if (arg0_value == irb->codegen->invalid_inst_src)
+                        return arg0_value;
+
+                    type_of = ir_build_typeof_1(irb, scope, node, arg0_value);
+                } else {
+                    IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
+                    for (size_t i = 0; i < arg_count; i += 1) {
+                        AstNode *arg_node = node->data.fn_call_expr.params.at(i);
+                        IrInstSrc *arg = ir_gen_node(irb, arg_node, sub_scope);
+                        if (arg == irb->codegen->invalid_inst_src)
+                            return irb->codegen->invalid_inst_src;
+                        args[i] = arg;
+                    }
+
+                    type_of = ir_build_typeof_n(irb, scope, node, args, arg_count);
+                }
+                return ir_lval_wrap(irb, scope, type_of, lval, result_loc);
+            }
+        case BuiltinFnIdSetCold:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *set_cold = ir_build_set_cold(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, set_cold, lval, result_loc);
+            }
+        case BuiltinFnIdSetRuntimeSafety:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, set_safety, lval, result_loc);
+            }
+        case BuiltinFnIdSetFloatMode:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, set_float_mode, lval, result_loc);
+            }
+        case BuiltinFnIdSizeof:
+        case BuiltinFnIdBitSizeof:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *size_of = ir_build_size_of(irb, scope, node, arg0_value, builtin_fn->id == BuiltinFnIdBitSizeof);
+                return ir_lval_wrap(irb, scope, size_of, lval, result_loc);
+            }
+        case BuiltinFnIdImport:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *import = ir_build_import(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, import, lval, result_loc);
+            }
+        case BuiltinFnIdCImport:
+            {
+                IrInstSrc *c_import = ir_build_c_import(irb, scope, node);
+                return ir_lval_wrap(irb, scope, c_import, lval, result_loc);
+            }
+        case BuiltinFnIdCInclude:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                if (!exec_c_import_buf(irb->exec)) {
+                    add_node_error(irb->codegen, node, buf_sprintf("C include valid only inside C import block"));
+                    return irb->codegen->invalid_inst_src;
+                }
+
+                IrInstSrc *c_include = ir_build_c_include(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, c_include, lval, result_loc);
+            }
+        case BuiltinFnIdCDefine:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                if (!exec_c_import_buf(irb->exec)) {
+                    add_node_error(irb->codegen, node, buf_sprintf("C define valid only inside C import block"));
+                    return irb->codegen->invalid_inst_src;
+                }
+
+                IrInstSrc *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, c_define, lval, result_loc);
+            }
+        case BuiltinFnIdCUndef:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                if (!exec_c_import_buf(irb->exec)) {
+                    add_node_error(irb->codegen, node, buf_sprintf("C undef valid only inside C import block"));
+                    return irb->codegen->invalid_inst_src;
+                }
+
+                IrInstSrc *c_undef = ir_build_c_undef(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, c_undef, lval, result_loc);
+            }
+        case BuiltinFnIdCompileErr:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *compile_err = ir_build_compile_err(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, compile_err, lval, result_loc);
+            }
+        case BuiltinFnIdCompileLog:
+            {
+                IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(actual_param_count);
+
+                for (size_t i = 0; i < actual_param_count; i += 1) {
+                    AstNode *arg_node = node->data.fn_call_expr.params.at(i);
+                    args[i] = ir_gen_node(irb, arg_node, scope);
+                    if (args[i] == irb->codegen->invalid_inst_src)
+                        return irb->codegen->invalid_inst_src;
+                }
+
+                IrInstSrc *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args);
+                return ir_lval_wrap(irb, scope, compile_log, lval, result_loc);
+            }
+        case BuiltinFnIdErrName:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *err_name = ir_build_err_name(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, err_name, lval, result_loc);
+            }
+        case BuiltinFnIdEmbedFile:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *embed_file = ir_build_embed_file(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, embed_file, lval, result_loc);
+            }
+        case BuiltinFnIdCmpxchgWeak:
+        case BuiltinFnIdCmpxchgStrong:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
+                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
+                if (arg2_value == irb->codegen->invalid_inst_src)
+                    return arg2_value;
+
+                AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
+                IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope);
+                if (arg3_value == irb->codegen->invalid_inst_src)
+                    return arg3_value;
+
+                AstNode *arg4_node = node->data.fn_call_expr.params.at(4);
+                IrInstSrc *arg4_value = ir_gen_node(irb, arg4_node, scope);
+                if (arg4_value == irb->codegen->invalid_inst_src)
+                    return arg4_value;
+
+                AstNode *arg5_node = node->data.fn_call_expr.params.at(5);
+                IrInstSrc *arg5_value = ir_gen_node(irb, arg5_node, scope);
+                if (arg5_value == irb->codegen->invalid_inst_src)
+                    return arg5_value;
+
+                IrInstSrc *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value,
+                    arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak),
+                    result_loc);
+                return ir_lval_wrap(irb, scope, cmpxchg, lval, result_loc);
+            }
+        case BuiltinFnIdFence:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *fence = ir_build_fence(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, fence, lval, result_loc);
+            }
+        case BuiltinFnIdReduce:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *reduce = ir_build_reduce(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, reduce, lval, result_loc);
+            }
+        case BuiltinFnIdDivExact:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true);
+                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
+            }
+        case BuiltinFnIdDivTrunc:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true);
+                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
+            }
+        case BuiltinFnIdDivFloor:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true);
+                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
+            }
+        case BuiltinFnIdRem:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true);
+                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
+            }
+        case BuiltinFnIdMod:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);
+                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
+            }
+        case BuiltinFnIdSqrt:
+        case BuiltinFnIdSin:
+        case BuiltinFnIdCos:
+        case BuiltinFnIdExp:
+        case BuiltinFnIdExp2:
+        case BuiltinFnIdLog:
+        case BuiltinFnIdLog2:
+        case BuiltinFnIdLog10:
+        case BuiltinFnIdFabs:
+        case BuiltinFnIdFloor:
+        case BuiltinFnIdCeil:
+        case BuiltinFnIdTrunc:
+        case BuiltinFnIdNearbyInt:
+        case BuiltinFnIdRound:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *inst = ir_build_float_op_src(irb, scope, node, arg0_value, builtin_fn->id);
+                return ir_lval_wrap(irb, scope, inst, lval, result_loc);
+            }
+        case BuiltinFnIdTruncate:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, truncate, lval, result_loc);
+            }
+        case BuiltinFnIdIntCast:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, result, lval, result_loc);
+            }
+        case BuiltinFnIdFloatCast:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, result, lval, result_loc);
+            }
+        case BuiltinFnIdErrSetCast:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, result, lval, result_loc);
+            }
+        case BuiltinFnIdIntToFloat:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, result, lval, result_loc);
+            }
+        case BuiltinFnIdFloatToInt:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, result, lval, result_loc);
+            }
+        case BuiltinFnIdErrToInt:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *result = ir_build_err_to_int_src(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, result, lval, result_loc);
+            }
+        case BuiltinFnIdIntToErr:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *result = ir_build_int_to_err_src(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, result, lval, result_loc);
+            }
+        case BuiltinFnIdBoolToInt:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *result = ir_build_bool_to_int(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, result, lval, result_loc);
+            }
+        case BuiltinFnIdVectorType:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, vector_type, lval, result_loc);
+            }
+        case BuiltinFnIdShuffle:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
+                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
+                if (arg2_value == irb->codegen->invalid_inst_src)
+                    return arg2_value;
+
+                AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
+                IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope);
+                if (arg3_value == irb->codegen->invalid_inst_src)
+                    return arg3_value;
+
+                IrInstSrc *shuffle_vector = ir_build_shuffle_vector(irb, scope, node,
+                    arg0_value, arg1_value, arg2_value, arg3_value);
+                return ir_lval_wrap(irb, scope, shuffle_vector, lval, result_loc);
+            }
+        case BuiltinFnIdSplat:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *splat = ir_build_splat_src(irb, scope, node,
+                    arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, splat, lval, result_loc);
+            }
+        case BuiltinFnIdMemcpy:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
+                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
+                if (arg2_value == irb->codegen->invalid_inst_src)
+                    return arg2_value;
+
+                IrInstSrc *ir_memcpy = ir_build_memcpy_src(irb, scope, node, arg0_value, arg1_value, arg2_value);
+                return ir_lval_wrap(irb, scope, ir_memcpy, lval, result_loc);
+            }
+        case BuiltinFnIdMemset:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
+                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
+                if (arg2_value == irb->codegen->invalid_inst_src)
+                    return arg2_value;
+
+                IrInstSrc *ir_memset = ir_build_memset_src(irb, scope, node, arg0_value, arg1_value, arg2_value);
+                return ir_lval_wrap(irb, scope, ir_memset, lval, result_loc);
+            }
+        case BuiltinFnIdWasmMemorySize:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *ir_wasm_memory_size = ir_build_wasm_memory_size_src(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, ir_wasm_memory_size, lval, result_loc);
+            }
+        case BuiltinFnIdWasmMemoryGrow:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *ir_wasm_memory_grow = ir_build_wasm_memory_grow_src(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, ir_wasm_memory_grow, lval, result_loc);
+            }
+        case BuiltinFnIdField:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr, nullptr);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node,
+                        arg0_value, arg1_value, false);
+
+                if (lval == LValPtr || lval == LValAssign)
+                    return ptr_instruction;
+
+                IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
+                return ir_expr_wrap(irb, scope, load_ptr, result_loc);
+            }
+        case BuiltinFnIdHasField:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *type_info = ir_build_has_field(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, type_info, lval, result_loc);
+            }
+        case BuiltinFnIdTypeInfo:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *type_info = ir_build_type_info(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, type_info, lval, result_loc);
+            }
+        case BuiltinFnIdType:
+            {
+                AstNode *arg_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
+                if (arg == irb->codegen->invalid_inst_src)
+                    return arg;
+
+                IrInstSrc *type = ir_build_type(irb, scope, node, arg);
+                return ir_lval_wrap(irb, scope, type, lval, result_loc);
+            }
+        case BuiltinFnIdBreakpoint:
+            return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval, result_loc);
+        case BuiltinFnIdReturnAddress:
+            return ir_lval_wrap(irb, scope, ir_build_return_address_src(irb, scope, node), lval, result_loc);
+        case BuiltinFnIdFrameAddress:
+            return ir_lval_wrap(irb, scope, ir_build_frame_address_src(irb, scope, node), lval, result_loc);
+        case BuiltinFnIdFrameHandle:
+            if (!irb->exec->fn_entry) {
+                add_node_error(irb->codegen, node, buf_sprintf("@frame() called outside of function definition"));
+                return irb->codegen->invalid_inst_src;
+            }
+            return ir_lval_wrap(irb, scope, ir_build_handle_src(irb, scope, node), lval, result_loc);
+        case BuiltinFnIdFrameType: {
+            AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+            IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+            if (arg0_value == irb->codegen->invalid_inst_src)
+                return arg0_value;
+
+            IrInstSrc *frame_type = ir_build_frame_type(irb, scope, node, arg0_value);
+            return ir_lval_wrap(irb, scope, frame_type, lval, result_loc);
+        }
+        case BuiltinFnIdFrameSize: {
+            AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+            IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+            if (arg0_value == irb->codegen->invalid_inst_src)
+                return arg0_value;
+
+            IrInstSrc *frame_size = ir_build_frame_size_src(irb, scope, node, arg0_value);
+            return ir_lval_wrap(irb, scope, frame_size, lval, result_loc);
+        }
+        case BuiltinFnIdAlignOf:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *align_of = ir_build_align_of(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, align_of, lval, result_loc);
+            }
+        case BuiltinFnIdAddWithOverflow:
+            return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval, result_loc);
+        case BuiltinFnIdSubWithOverflow:
+            return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval, result_loc);
+        case BuiltinFnIdMulWithOverflow:
+            return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval, result_loc);
+        case BuiltinFnIdShlWithOverflow:
+            return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval, result_loc);
+        case BuiltinFnIdMulAdd:
+            return ir_lval_wrap(irb, scope, ir_gen_mul_add(irb, scope, node), lval, result_loc);
+        case BuiltinFnIdTypeName:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *type_name = ir_build_type_name(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, type_name, lval, result_loc);
+            }
+        case BuiltinFnIdPanic:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *panic = ir_build_panic_src(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, panic, lval, result_loc);
+            }
+        case BuiltinFnIdPtrCast:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value, true);
+                return ir_lval_wrap(irb, scope, ptr_cast, lval, result_loc);
+            }
+        case BuiltinFnIdBitCast:
+            {
+                AstNode *dest_type_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *dest_type = ir_gen_node(irb, dest_type_node, scope);
+                if (dest_type == irb->codegen->invalid_inst_src)
+                    return dest_type;
+
+                ResultLocBitCast *result_loc_bit_cast = heap::c_allocator.create<ResultLocBitCast>();
+                result_loc_bit_cast->base.id = ResultLocIdBitCast;
+                result_loc_bit_cast->base.source_instruction = dest_type;
+                result_loc_bit_cast->base.allow_write_through_const = result_loc->allow_write_through_const;
+                ir_ref_instruction(dest_type, irb->current_basic_block);
+                result_loc_bit_cast->parent = result_loc;
+
+                ir_build_reset_result(irb, scope, node, &result_loc_bit_cast->base);
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone,
+                        &result_loc_bit_cast->base);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *bitcast = ir_build_bit_cast_src(irb, scope, arg1_node, arg1_value, result_loc_bit_cast);
+                return ir_lval_wrap(irb, scope, bitcast, lval, result_loc);
+            }
+        case BuiltinFnIdAs:
+            {
+                AstNode *dest_type_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *dest_type = ir_gen_node(irb, dest_type_node, scope);
+                if (dest_type == irb->codegen->invalid_inst_src)
+                    return dest_type;
+
+                ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, dest_type, result_loc);
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone,
+                        &result_loc_cast->base);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *result = ir_build_implicit_cast(irb, scope, node, arg1_value, result_loc_cast);
+                return ir_lval_wrap(irb, scope, result, lval, result_loc);
+            }
+        case BuiltinFnIdIntToPtr:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *int_to_ptr = ir_build_int_to_ptr_src(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, int_to_ptr, lval, result_loc);
+            }
+        case BuiltinFnIdPtrToInt:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *ptr_to_int = ir_build_ptr_to_int_src(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, ptr_to_int, lval, result_loc);
+            }
+        case BuiltinFnIdTagName:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *tag_name = ir_build_tag_name_src(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, tag_name, lval, result_loc);
+            }
+        case BuiltinFnIdFieldParentPtr:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
+                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
+                if (arg2_value == irb->codegen->invalid_inst_src)
+                    return arg2_value;
+
+                IrInstSrc *field_parent_ptr = ir_build_field_parent_ptr_src(irb, scope, node,
+                        arg0_value, arg1_value, arg2_value);
+                return ir_lval_wrap(irb, scope, field_parent_ptr, lval, result_loc);
+            }
+        case BuiltinFnIdByteOffsetOf:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *offset_of = ir_build_byte_offset_of(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, offset_of, lval, result_loc);
+            }
+        case BuiltinFnIdBitOffsetOf:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *offset_of = ir_build_bit_offset_of(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, offset_of, lval, result_loc);
+            }
+        case BuiltinFnIdCall: {
+            // Cast the options parameter to the options type
+            ZigType *options_type = get_builtin_type(irb->codegen, "CallOptions");
+            IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
+            ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());
+
+            AstNode *options_node = node->data.fn_call_expr.params.at(0);
+            IrInstSrc *options_inner = ir_gen_node_extra(irb, options_node, scope,
+                    LValNone, &result_loc_cast->base);
+            if (options_inner == irb->codegen->invalid_inst_src)
+                return options_inner;
+            IrInstSrc *options = ir_build_implicit_cast(irb, scope, options_node, options_inner, result_loc_cast);
+
+            AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1);
+            AstNode *args_node = node->data.fn_call_expr.params.at(2);
+            if (args_node->type == NodeTypeContainerInitExpr) {
+                if (args_node->data.container_init_expr.kind == ContainerInitKindArray ||
+                    args_node->data.container_init_expr.entries.length == 0)
+                {
+                    return ir_gen_fn_call_with_args(irb, scope, node,
+                            fn_ref_node, CallModifierNone, options,
+                            args_node->data.container_init_expr.entries.items,
+                            args_node->data.container_init_expr.entries.length,
+                            lval, result_loc);
+                } else {
+                    exec_add_error_node(irb->codegen, irb->exec, args_node,
+                            buf_sprintf("TODO: @call with anon struct literal"));
+                    return irb->codegen->invalid_inst_src;
+                }
+            } else {
+                IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
+                if (fn_ref == irb->codegen->invalid_inst_src)
+                    return fn_ref;
+
+                IrInstSrc *args = ir_gen_node(irb, args_node, scope);
+                if (args == irb->codegen->invalid_inst_src)
+                    return args;
+
+                IrInstSrc *call = ir_build_call_extra(irb, scope, node, options, fn_ref, args, result_loc);
+                return ir_lval_wrap(irb, scope, call, lval, result_loc);
+            }
+        }
+        case BuiltinFnIdAsyncCall:
+            return ir_gen_async_call(irb, scope, nullptr, node, lval, result_loc);
+        case BuiltinFnIdShlExact:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true);
+                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
+            }
+        case BuiltinFnIdShrExact:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true);
+                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
+            }
+        case BuiltinFnIdSetEvalBranchQuota:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval, result_loc);
+            }
+        case BuiltinFnIdAlignCast:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *align_cast = ir_build_align_cast_src(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, align_cast, lval, result_loc);
+            }
+        case BuiltinFnIdThis:
+            {
+                IrInstSrc *this_inst = ir_gen_this(irb, scope, node);
+                return ir_lval_wrap(irb, scope, this_inst, lval, result_loc);
+            }
+        case BuiltinFnIdSetAlignStack:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, set_align_stack, lval, result_loc);
+            }
+        case BuiltinFnIdExport:
+            {
+                // Cast the options parameter to the options type
+                ZigType *options_type = get_builtin_type(irb->codegen, "ExportOptions");
+                IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
+                ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());
+
+                AstNode *target_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *target_value = ir_gen_node(irb, target_node, scope);
+                if (target_value == irb->codegen->invalid_inst_src)
+                    return target_value;
+
+                AstNode *options_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *options_value = ir_gen_node_extra(irb, options_node,
+                    scope, LValNone, &result_loc_cast->base);
+                if (options_value == irb->codegen->invalid_inst_src)
+                    return options_value;
+
+                IrInstSrc *casted_options_value = ir_build_implicit_cast(
+                    irb, scope, options_node, options_value, result_loc_cast);
+
+                IrInstSrc *ir_export = ir_build_export(irb, scope, node, target_value, casted_options_value);
+                return ir_lval_wrap(irb, scope, ir_export, lval, result_loc);
+            }
+        case BuiltinFnIdExtern:
+            {
+                // Cast the options parameter to the options type
+                ZigType *options_type = get_builtin_type(irb->codegen, "ExternOptions");
+                IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
+                ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());
+
+                AstNode *type_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *type_value = ir_gen_node(irb, type_node, scope);
+                if (type_value == irb->codegen->invalid_inst_src)
+                    return type_value;
+
+                AstNode *options_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *options_value = ir_gen_node_extra(irb, options_node,
+                    scope, LValNone, &result_loc_cast->base);
+                if (options_value == irb->codegen->invalid_inst_src)
+                    return options_value;
+
+                IrInstSrc *casted_options_value = ir_build_implicit_cast(
+                    irb, scope, options_node, options_value, result_loc_cast);
+
+                IrInstSrc *ir_extern = ir_build_extern(irb, scope, node, type_value, casted_options_value);
+                return ir_lval_wrap(irb, scope, ir_extern, lval, result_loc);
+            }
+        case BuiltinFnIdErrorReturnTrace:
+            {
+                IrInstSrc *error_return_trace = ir_build_error_return_trace_src(irb, scope, node,
+                        IrInstErrorReturnTraceNull);
+                return ir_lval_wrap(irb, scope, error_return_trace, lval, result_loc);
+            }
+        case BuiltinFnIdAtomicRmw:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
+                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
+                if (arg2_value == irb->codegen->invalid_inst_src)
+                    return arg2_value;
+
+                AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
+                IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope);
+                if (arg3_value == irb->codegen->invalid_inst_src)
+                    return arg3_value;
+
+                AstNode *arg4_node = node->data.fn_call_expr.params.at(4);
+                IrInstSrc *arg4_value = ir_gen_node(irb, arg4_node, scope);
+                if (arg4_value == irb->codegen->invalid_inst_src)
+                    return arg4_value;
+
+                IrInstSrc *inst = ir_build_atomic_rmw_src(irb, scope, node,
+                        arg0_value, arg1_value, arg2_value, arg3_value, arg4_value);
+                return ir_lval_wrap(irb, scope, inst, lval, result_loc);
+            }
+        case BuiltinFnIdAtomicLoad:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
+                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
+                if (arg2_value == irb->codegen->invalid_inst_src)
+                    return arg2_value;
+
+                IrInstSrc *inst = ir_build_atomic_load_src(irb, scope, node, arg0_value, arg1_value, arg2_value);
+                return ir_lval_wrap(irb, scope, inst, lval, result_loc);
+            }
+        case BuiltinFnIdAtomicStore:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
+                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
+                if (arg2_value == irb->codegen->invalid_inst_src)
+                    return arg2_value;
+
+                AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
+                IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope);
+                if (arg3_value == irb->codegen->invalid_inst_src)
+                    return arg3_value;
+
+                IrInstSrc *inst = ir_build_atomic_store_src(irb, scope, node, arg0_value, arg1_value,
+                        arg2_value, arg3_value);
+                return ir_lval_wrap(irb, scope, inst, lval, result_loc);
+            }
+        case BuiltinFnIdIntToEnum:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *result = ir_build_int_to_enum_src(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, result, lval, result_loc);
+            }
+        case BuiltinFnIdEnumToInt:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                IrInstSrc *result = ir_build_enum_to_int(irb, scope, node, arg0_value);
+                return ir_lval_wrap(irb, scope, result, lval, result_loc);
+            }
+        case BuiltinFnIdCtz:
+        case BuiltinFnIdPopCount:
+        case BuiltinFnIdClz:
+        case BuiltinFnIdBswap:
+        case BuiltinFnIdBitReverse:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *result;
+                switch (builtin_fn->id) {
+                case BuiltinFnIdCtz:
+                    result = ir_build_ctz(irb, scope, node, arg0_value, arg1_value);
+                    break;
+                case BuiltinFnIdPopCount:
+                    result = ir_build_pop_count(irb, scope, node, arg0_value, arg1_value);
+                    break;
+                case BuiltinFnIdClz:
+                    result = ir_build_clz(irb, scope, node, arg0_value, arg1_value);
+                    break;
+                case BuiltinFnIdBswap:
+                    result = ir_build_bswap(irb, scope, node, arg0_value, arg1_value);
+                    break;
+                case BuiltinFnIdBitReverse:
+                    result = ir_build_bit_reverse(irb, scope, node, arg0_value, arg1_value);
+                    break;
+                default:
+                    zig_unreachable();
+                }
+                return ir_lval_wrap(irb, scope, result, lval, result_loc);
+            }
+        case BuiltinFnIdHasDecl:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_inst_src)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_inst_src)
+                    return arg1_value;
+
+                IrInstSrc *has_decl = ir_build_has_decl(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, has_decl, lval, result_loc);
+            }
+        case BuiltinFnIdUnionInit:
+            {
+                AstNode *union_type_node = node->data.fn_call_expr.params.at(0);
+                IrInstSrc *union_type_inst = ir_gen_node(irb, union_type_node, scope);
+                if (union_type_inst == irb->codegen->invalid_inst_src)
+                    return union_type_inst;
+
+                AstNode *name_node = node->data.fn_call_expr.params.at(1);
+                IrInstSrc *name_inst = ir_gen_node(irb, name_node, scope);
+                if (name_inst == irb->codegen->invalid_inst_src)
+                    return name_inst;
+
+                AstNode *init_node = node->data.fn_call_expr.params.at(2);
+
+                return ir_gen_union_init_expr(irb, scope, node, union_type_inst, name_inst, init_node,
+                        lval, result_loc);
+            }
+        case BuiltinFnIdSrc:
+            {
+                IrInstSrc *src_inst = ir_build_src(irb, scope, node);
+                return ir_lval_wrap(irb, scope, src_inst, lval, result_loc);
+            }
+    }
+    zig_unreachable();
+}
+
+static ScopeNoSuspend *get_scope_nosuspend(Scope *scope) {
+    while (scope) {
+        if (scope->id == ScopeIdNoSuspend)
+            return (ScopeNoSuspend *)scope;
+        if (scope->id == ScopeIdFnDef)
+            return nullptr;
+
+        scope = scope->parent;
+    }
+    return nullptr;
+}
+
+static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypeFnCallExpr);
+
+    if (node->data.fn_call_expr.modifier == CallModifierBuiltin)
+        return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc);
+
+    bool is_nosuspend = get_scope_nosuspend(scope) != nullptr;
+    CallModifier modifier = node->data.fn_call_expr.modifier;
+    if (is_nosuspend && modifier != CallModifierAsync) {
+        modifier = CallModifierNoSuspend;
+    }
+
+    AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
+    return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, modifier,
+        nullptr, node->data.fn_call_expr.params.items, node->data.fn_call_expr.params.length, lval, result_loc);
+}
+
+static IrInstSrc *ir_gen_if_bool_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypeIfBoolExpr);
+
+    IrInstSrc *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope);
+    if (condition == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *is_comptime;
+    if (ir_should_inline(irb->exec, scope)) {
+        is_comptime = ir_build_const_bool(irb, scope, node, true);
+    } else {
+        is_comptime = ir_build_test_comptime(irb, scope, node, condition);
+    }
+
+    AstNode *then_node = node->data.if_bool_expr.then_block;
+    AstNode *else_node = node->data.if_bool_expr.else_node;
+
+    IrBasicBlockSrc *then_block = ir_create_basic_block(irb, scope, "Then");
+    IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "Else");
+    IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "EndIf");
+
+    IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, condition,
+            then_block, else_block, is_comptime);
+    ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
+            result_loc, is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, then_block);
+
+    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
+    IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, subexpr_scope, lval,
+            &peer_parent->peers.at(0)->base);
+    if (then_expr_result == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+    IrBasicBlockSrc *after_then_block = irb->current_basic_block;
+    if (!instr_is_unreachable(then_expr_result))
+        ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
+
+    ir_set_cursor_at_end_and_append_block(irb, else_block);
+    IrInstSrc *else_expr_result;
+    if (else_node) {
+        else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base);
+        if (else_expr_result == irb->codegen->invalid_inst_src)
+            return irb->codegen->invalid_inst_src;
+    } else {
+        else_expr_result = ir_build_const_void(irb, scope, node);
+        ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
+    }
+    IrBasicBlockSrc *after_else_block = irb->current_basic_block;
+    if (!instr_is_unreachable(else_expr_result))
+        ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
+
+    ir_set_cursor_at_end_and_append_block(irb, endif_block);
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
+    incoming_values[0] = then_expr_result;
+    incoming_values[1] = else_expr_result;
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
+    incoming_blocks[0] = after_then_block;
+    incoming_blocks[1] = after_else_block;
+
+    IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
+    return ir_expr_wrap(irb, scope, phi, result_loc);
+}
+
+static IrInstSrc *ir_gen_prefix_op_id_lval(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) {
+    assert(node->type == NodeTypePrefixOpExpr);
+    AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
+
+    IrInstSrc *value = ir_gen_node_extra(irb, expr_node, scope, lval, nullptr);
+    if (value == irb->codegen->invalid_inst_src)
+        return value;
+
+    return ir_build_un_op(irb, scope, node, op_id, value);
+}
+
+static IrInstSrc *ir_gen_prefix_op_id(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrUnOp op_id) {
+    return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone);
+}
+
+static IrInstSrc *ir_expr_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *inst, ResultLoc *result_loc) {
+    if (inst == irb->codegen->invalid_inst_src) return inst;
+    ir_build_end_expr(irb, scope, inst->base.source_node, inst, result_loc);
+    return inst;
+}
+
+static IrInstSrc *ir_lval_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *value, LVal lval,
+        ResultLoc *result_loc)
+{
+    // This logic must be kept in sync with
+    // [STMT_EXPR_TEST_THING] <--- (search this token)
+    if (value == irb->codegen->invalid_inst_src ||
+        instr_is_unreachable(value) ||
+        value->base.source_node->type == NodeTypeDefer ||
+        value->id == IrInstSrcIdDeclVar)
+    {
+        return value;
+    }
+
+    assert(lval != LValAssign);
+    if (lval == LValPtr) {
+        // We needed a pointer to a value, but we got a value. So we create
+        // an instruction which just makes a pointer of it.
+        return ir_build_ref_src(irb, scope, value->base.source_node, value);
+    } else if (result_loc != nullptr) {
+        return ir_expr_wrap(irb, scope, value, result_loc);
+    } else {
+        return value;
+    }
+
+}
+
+static PtrLen star_token_to_ptr_len(TokenId token_id) {
+    switch (token_id) {
+        case TokenIdStar:
+        case TokenIdStarStar:
+            return PtrLenSingle;
+        case TokenIdLBracket:
+            return PtrLenUnknown;
+        case TokenIdIdentifier:
+            return PtrLenC;
+        default:
+            zig_unreachable();
+    }
+}
+
+static Error token_number_literal_u32(IrBuilderSrc *irb, AstNode *source_node,
+    RootStruct *root_struct, uint32_t *result, TokenIndex token)
+{
+    BigInt bigint;
+    token_number_literal_bigint(root_struct, &bigint, token);
+
+    if (!bigint_fits_in_bits(&bigint, 32, false)) {
+        Buf *val_buf = buf_alloc();
+        bigint_append_buf(val_buf, &bigint, 10);
+        exec_add_error_node(irb->codegen, irb->exec, source_node,
+                buf_sprintf("value %s too large for u32", buf_ptr(val_buf)));
+        bigint_deinit(&bigint);
+        return ErrorSemanticAnalyzeFail;
+    }
+    *result = bigint_as_u32(&bigint);
+    bigint_deinit(&bigint);
+    return ErrorNone;
+
+}
+
+static IrInstSrc *ir_gen_pointer_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    Error err;
+    assert(node->type == NodeTypePointerType);
+
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    TokenId star_tok_id = root_struct->token_ids[node->data.pointer_type.star_token];
+    PtrLen ptr_len = star_token_to_ptr_len(star_tok_id);
+
+    bool is_const = node->data.pointer_type.is_const;
+    bool is_volatile = node->data.pointer_type.is_volatile;
+    bool is_allow_zero = node->data.pointer_type.allow_zero_token != 0;
+    AstNode *sentinel_expr = node->data.pointer_type.sentinel;
+    AstNode *expr_node = node->data.pointer_type.op_expr;
+    AstNode *align_expr = node->data.pointer_type.align_expr;
+
+    IrInstSrc *sentinel;
+    if (sentinel_expr != nullptr) {
+        sentinel = ir_gen_node(irb, sentinel_expr, scope);
+        if (sentinel == irb->codegen->invalid_inst_src)
+            return sentinel;
+    } else {
+        sentinel = nullptr;
+    }
+
+    IrInstSrc *align_value;
+    if (align_expr != nullptr) {
+        align_value = ir_gen_node(irb, align_expr, scope);
+        if (align_value == irb->codegen->invalid_inst_src)
+            return align_value;
+    } else {
+        align_value = nullptr;
+    }
+
+    IrInstSrc *child_type = ir_gen_node(irb, expr_node, scope);
+    if (child_type == irb->codegen->invalid_inst_src)
+        return child_type;
+
+    uint32_t bit_offset_start = 0;
+    if (node->data.pointer_type.bit_offset_start != 0) {
+        if ((err = token_number_literal_u32(irb, node, root_struct, &bit_offset_start,
+            node->data.pointer_type.bit_offset_start)))
+        {
+            return irb->codegen->invalid_inst_src;
+        }
+    }
+
+    uint32_t host_int_bytes = 0;
+    if (node->data.pointer_type.host_int_bytes != 0) {
+        if ((err = token_number_literal_u32(irb, node, root_struct, &host_int_bytes,
+            node->data.pointer_type.host_int_bytes)))
+        {
+            return irb->codegen->invalid_inst_src;
+        }
+    }
+
+    if (host_int_bytes != 0 && bit_offset_start >= host_int_bytes * 8) {
+        exec_add_error_node(irb->codegen, irb->exec, node,
+                buf_sprintf("bit offset starts after end of host integer"));
+        return irb->codegen->invalid_inst_src;
+    }
+
+    return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile,
+            ptr_len, sentinel, align_value, bit_offset_start, host_int_bytes, is_allow_zero);
+}
+
+static IrInstSrc *ir_gen_catch_unreachable(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+        AstNode *expr_node, LVal lval, ResultLoc *result_loc)
+{
+    IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
+    if (err_union_ptr == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, scope, source_node, err_union_ptr, true, false);
+    if (payload_ptr == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    if (lval == LValPtr)
+        return payload_ptr;
+
+    IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, source_node, payload_ptr);
+    return ir_expr_wrap(irb, scope, load_ptr, result_loc);
+}
+
+static IrInstSrc *ir_gen_bool_not(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypePrefixOpExpr);
+    AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
+
+    IrInstSrc *value = ir_gen_node(irb, expr_node, scope);
+    if (value == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    return ir_build_bool_not(irb, scope, node, value);
+}
+
+static IrInstSrc *ir_gen_prefix_op_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypePrefixOpExpr);
+
+    PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op;
+
+    switch (prefix_op) {
+        case PrefixOpInvalid:
+            zig_unreachable();
+        case PrefixOpBoolNot:
+            return ir_lval_wrap(irb, scope, ir_gen_bool_not(irb, scope, node), lval, result_loc);
+        case PrefixOpBinNot:
+            return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval, result_loc);
+        case PrefixOpNegation:
+            return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval, result_loc);
+        case PrefixOpNegationWrap:
+            return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval, result_loc);
+        case PrefixOpOptional:
+            return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval, result_loc);
+        case PrefixOpAddrOf: {
+            AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
+            return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr), lval, result_loc);
+        }
+    }
+    zig_unreachable();
+}
+
+static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
+    IrInstSrc *union_type, IrInstSrc *field_name, AstNode *expr_node,
+    LVal lval, ResultLoc *parent_result_loc)
+{
+    IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, source_node, parent_result_loc, union_type);
+    IrInstSrc *field_ptr = ir_build_field_ptr_instruction(irb, scope, source_node, container_ptr,
+            field_name, true);
+
+    ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
+    result_loc_inst->base.id = ResultLocIdInstruction;
+    result_loc_inst->base.source_instruction = field_ptr;
+    ir_ref_instruction(field_ptr, irb->current_basic_block);
+    ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
+
+    IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
+            &result_loc_inst->base);
+    if (expr_value == irb->codegen->invalid_inst_src)
+        return expr_value;
+
+    IrInstSrc *init_union = ir_build_union_init_named_field(irb, scope, source_node, union_type,
+            field_name, field_ptr, container_ptr);
+
+    return ir_lval_wrap(irb, scope, init_union, lval, parent_result_loc);
+}
+
+static IrInstSrc *ir_gen_container_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
+        ResultLoc *parent_result_loc)
+{
+    assert(node->type == NodeTypeContainerInitExpr);
+
+    AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
+    ContainerInitKind kind = container_init_expr->kind;
+
+    ResultLocCast *result_loc_cast = nullptr;
+    ResultLoc *child_result_loc;
+    AstNode *init_array_type_source_node;
+    if (container_init_expr->type != nullptr) {
+        IrInstSrc *container_type;
+        if (container_init_expr->type->type == NodeTypeInferredArrayType) {
+            if (kind == ContainerInitKindStruct) {
+                add_node_error(irb->codegen, container_init_expr->type,
+                        buf_sprintf("initializing array with struct syntax"));
+                return irb->codegen->invalid_inst_src;
+            }
+            IrInstSrc *sentinel;
+            if (container_init_expr->type->data.inferred_array_type.sentinel != nullptr) {
+                sentinel = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.sentinel, scope);
+                if (sentinel == irb->codegen->invalid_inst_src)
+                    return sentinel;
+            } else {
+                sentinel = nullptr;
+            }
+
+            IrInstSrc *elem_type = ir_gen_node(irb,
+                    container_init_expr->type->data.inferred_array_type.child_type, scope);
+            if (elem_type == irb->codegen->invalid_inst_src)
+                return elem_type;
+            size_t item_count = container_init_expr->entries.length;
+            IrInstSrc *item_count_inst = ir_build_const_usize(irb, scope, node, item_count);
+            container_type = ir_build_array_type(irb, scope, node, item_count_inst, sentinel, elem_type);
+        } else {
+            container_type = ir_gen_node(irb, container_init_expr->type, scope);
+            if (container_type == irb->codegen->invalid_inst_src)
+                return container_type;
+        }
+
+        result_loc_cast = ir_build_cast_result_loc(irb, container_type, parent_result_loc);
+        child_result_loc = &result_loc_cast->base;
+        init_array_type_source_node = container_type->base.source_node;
+    } else {
+        child_result_loc = parent_result_loc;
+        if (parent_result_loc->source_instruction != nullptr) {
+            init_array_type_source_node = parent_result_loc->source_instruction->base.source_node;
+        } else {
+            init_array_type_source_node = node;
+        }
+    }
+
+    switch (kind) {
+        case ContainerInitKindStruct: {
+            IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc,
+                    nullptr);
+
+            size_t field_count = container_init_expr->entries.length;
+            IrInstSrcContainerInitFieldsField *fields = heap::c_allocator.allocate<IrInstSrcContainerInitFieldsField>(field_count);
+            for (size_t i = 0; i < field_count; i += 1) {
+                AstNode *entry_node = container_init_expr->entries.at(i);
+                assert(entry_node->type == NodeTypeStructValueField);
+
+                Buf *name = entry_node->data.struct_val_field.name;
+                AstNode *expr_node = entry_node->data.struct_val_field.expr;
+
+                IrInstSrc *field_ptr = ir_build_field_ptr(irb, scope, entry_node, container_ptr, name, true);
+                ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
+                result_loc_inst->base.id = ResultLocIdInstruction;
+                result_loc_inst->base.source_instruction = field_ptr;
+                result_loc_inst->base.allow_write_through_const = true;
+                ir_ref_instruction(field_ptr, irb->current_basic_block);
+                ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
+
+                IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
+                        &result_loc_inst->base);
+                if (expr_value == irb->codegen->invalid_inst_src)
+                    return expr_value;
+
+                fields[i].name = name;
+                fields[i].source_node = entry_node;
+                fields[i].result_loc = field_ptr;
+            }
+            IrInstSrc *result = ir_build_container_init_fields(irb, scope, node, field_count,
+                    fields, container_ptr);
+
+            if (result_loc_cast != nullptr) {
+                result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast);
+            }
+            return ir_lval_wrap(irb, scope, result, lval, parent_result_loc);
+        }
+        case ContainerInitKindArray: {
+            size_t item_count = container_init_expr->entries.length;
+
+            IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc,
+                    nullptr);
+
+            IrInstSrc **result_locs = heap::c_allocator.allocate<IrInstSrc *>(item_count);
+            for (size_t i = 0; i < item_count; i += 1) {
+                AstNode *expr_node = container_init_expr->entries.at(i);
+
+                IrInstSrc *elem_index = ir_build_const_usize(irb, scope, expr_node, i);
+                IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr,
+                        elem_index, false, PtrLenSingle, init_array_type_source_node);
+                ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
+                result_loc_inst->base.id = ResultLocIdInstruction;
+                result_loc_inst->base.source_instruction = elem_ptr;
+                result_loc_inst->base.allow_write_through_const = true;
+                ir_ref_instruction(elem_ptr, irb->current_basic_block);
+                ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
+
+                IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
+                        &result_loc_inst->base);
+                if (expr_value == irb->codegen->invalid_inst_src)
+                    return expr_value;
+
+                result_locs[i] = elem_ptr;
+            }
+            IrInstSrc *result = ir_build_container_init_list(irb, scope, node, item_count,
+                    result_locs, container_ptr, init_array_type_source_node);
+            if (result_loc_cast != nullptr) {
+                result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast);
+            }
+            return ir_lval_wrap(irb, scope, result, lval, parent_result_loc);
+        }
+    }
+    zig_unreachable();
+}
+
+static ResultLocVar *ir_build_var_result_loc(IrBuilderSrc *irb, IrInstSrc *alloca, ZigVar *var) {
+    ResultLocVar *result_loc_var = heap::c_allocator.create<ResultLocVar>();
+    result_loc_var->base.id = ResultLocIdVar;
+    result_loc_var->base.source_instruction = alloca;
+    result_loc_var->base.allow_write_through_const = true;
+    result_loc_var->var = var;
+
+    ir_build_reset_result(irb, alloca->base.scope, alloca->base.source_node, &result_loc_var->base);
+
+    return result_loc_var;
+}
+
+static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type,
+        ResultLoc *parent_result_loc)
+{
+    ResultLocCast *result_loc_cast = heap::c_allocator.create<ResultLocCast>();
+    result_loc_cast->base.id = ResultLocIdCast;
+    result_loc_cast->base.source_instruction = dest_type;
+    result_loc_cast->base.allow_write_through_const = parent_result_loc->allow_write_through_const;
+    ir_ref_instruction(dest_type, irb->current_basic_block);
+    result_loc_cast->parent = parent_result_loc;
+
+    ir_build_reset_result(irb, dest_type->base.scope, dest_type->base.source_node, &result_loc_cast->base);
+
+    return result_loc_cast;
+}
+
+static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var,
+        IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime)
+{
+    IrInstSrc *alloca = ir_build_alloca_src(irb, scope, source_node, nullptr, name_hint, is_comptime);
+    ResultLocVar *var_result_loc = ir_build_var_result_loc(irb, alloca, var);
+    ir_build_end_expr(irb, scope, source_node, init, &var_result_loc->base);
+    ir_build_var_decl_src(irb, scope, source_node, var, nullptr, alloca);
+}
+
+static IrInstSrc *ir_gen_var_decl(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeVariableDeclaration);
+
+    AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
+
+    if (buf_eql_str(variable_declaration->symbol, "_")) {
+        add_node_error(irb->codegen, node, buf_sprintf("`_` is not a declarable symbol"));
+        return irb->codegen->invalid_inst_src;
+    }
+
+    // Used for the type expr and the align expr
+    Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
+
+    IrInstSrc *type_instruction;
+    if (variable_declaration->type != nullptr) {
+        type_instruction = ir_gen_node(irb, variable_declaration->type, comptime_scope);
+        if (type_instruction == irb->codegen->invalid_inst_src)
+            return type_instruction;
+    } else {
+        type_instruction = nullptr;
+    }
+
+    bool is_shadowable = false;
+    bool is_const = variable_declaration->is_const;
+    bool is_extern = variable_declaration->is_extern;
+
+    bool is_comptime_scalar = ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime;
+    IrInstSrc *is_comptime = ir_build_const_bool(irb, scope, node, is_comptime_scalar);
+    ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol,
+        is_const, is_const, is_shadowable, is_comptime);
+    // we detect IrInstSrcDeclVar in gen_block to make sure the next node
+    // is inside var->child_scope
+
+    if (!is_extern && !variable_declaration->expr) {
+        var->var_type = irb->codegen->builtin_types.entry_invalid;
+        add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized"));
+        return irb->codegen->invalid_inst_src;
+    }
+
+    IrInstSrc *align_value = nullptr;
+    if (variable_declaration->align_expr != nullptr) {
+        align_value = ir_gen_node(irb, variable_declaration->align_expr, comptime_scope);
+        if (align_value == irb->codegen->invalid_inst_src)
+            return align_value;
+    }
+
+    if (variable_declaration->section_expr != nullptr) {
+        add_node_error(irb->codegen, variable_declaration->section_expr,
+            buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
+    }
+
+    // Parser should ensure that this never happens
+    assert(variable_declaration->threadlocal_tok == 0);
+
+    IrInstSrc *alloca = ir_build_alloca_src(irb, scope, node, align_value,
+            buf_ptr(variable_declaration->symbol), is_comptime);
+
+    // Create a result location for the initialization expression.
+    ResultLocVar *result_loc_var = ir_build_var_result_loc(irb, alloca, var);
+    ResultLoc *init_result_loc;
+    ResultLocCast *result_loc_cast;
+    if (type_instruction != nullptr) {
+        result_loc_cast = ir_build_cast_result_loc(irb, type_instruction, &result_loc_var->base);
+        init_result_loc = &result_loc_cast->base;
+    } else {
+        result_loc_cast = nullptr;
+        init_result_loc = &result_loc_var->base;
+    }
+
+    Scope *init_scope = is_comptime_scalar ?
+        create_comptime_scope(irb->codegen, variable_declaration->expr, scope) : scope;
+
+    // Temporarily set the name of the IrExecutableSrc to the VariableDeclaration
+    // so that the struct or enum from the init expression inherits the name.
+    Buf *old_exec_name = irb->exec->name;
+    irb->exec->name = variable_declaration->symbol;
+    IrInstSrc *init_value = ir_gen_node_extra(irb, variable_declaration->expr, init_scope,
+            LValNone, init_result_loc);
+    irb->exec->name = old_exec_name;
+
+    if (init_value == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    if (result_loc_cast != nullptr) {
+        IrInstSrc *implicit_cast = ir_build_implicit_cast(irb, scope, init_value->base.source_node,
+                init_value, result_loc_cast);
+        ir_build_end_expr(irb, scope, node, implicit_cast, &result_loc_var->base);
+    }
+
+    return ir_build_var_decl_src(irb, scope, node, var, align_value, alloca);
+}
+
+static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypeWhileExpr);
+
+    AstNode *continue_expr_node = node->data.while_expr.continue_expr;
+    AstNode *else_node = node->data.while_expr.else_node;
+
+    IrBasicBlockSrc *cond_block = ir_create_basic_block(irb, scope, "WhileCond");
+    IrBasicBlockSrc *body_block = ir_create_basic_block(irb, scope, "WhileBody");
+    IrBasicBlockSrc *continue_block = continue_expr_node ?
+        ir_create_basic_block(irb, scope, "WhileContinue") : cond_block;
+    IrBasicBlockSrc *end_block = ir_create_basic_block(irb, scope, "WhileEnd");
+    IrBasicBlockSrc *else_block = else_node ?
+        ir_create_basic_block(irb, scope, "WhileElse") : end_block;
+
+    IrInstSrc *is_comptime = ir_build_const_bool(irb, scope, node,
+        ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline);
+    ir_build_br(irb, scope, node, cond_block, is_comptime);
+
+    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
+    Buf *var_symbol = node->data.while_expr.var_symbol;
+    Buf *err_symbol = node->data.while_expr.err_symbol;
+    if (err_symbol != nullptr) {
+        ir_set_cursor_at_end_and_append_block(irb, cond_block);
+
+        Scope *payload_scope;
+        AstNode *symbol_node = node; // TODO make more accurate
+        ZigVar *payload_var;
+        if (var_symbol) {
+            // TODO make it an error to write to payload variable
+            payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol,
+                    true, false, false, is_comptime);
+            payload_scope = payload_var->child_scope;
+        } else {
+            payload_scope = subexpr_scope;
+        }
+        ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, payload_scope);
+        IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope,
+                LValPtr, nullptr);
+        if (err_val_ptr == irb->codegen->invalid_inst_src)
+            return err_val_ptr;
+        IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node->data.while_expr.condition, err_val_ptr,
+                true, false);
+        IrBasicBlockSrc *after_cond_block = irb->current_basic_block;
+        IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
+        IrInstSrc *cond_br_inst;
+        if (!instr_is_unreachable(is_err)) {
+            cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err,
+                        else_block, body_block, is_comptime);
+            cond_br_inst->is_gen = true;
+        } else {
+            // for the purposes of the source instruction to ir_build_result_peers
+            cond_br_inst = irb->current_basic_block->instruction_list.last();
+        }
+
+        ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
+                is_comptime);
+
+        ir_set_cursor_at_end_and_append_block(irb, body_block);
+        if (var_symbol) {
+            IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, &spill_scope->base, symbol_node,
+                    err_val_ptr, false, false);
+            IrInstSrc *var_value = node->data.while_expr.var_is_ptr ?
+                payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, symbol_node, payload_ptr);
+            build_decl_var_and_init(irb, payload_scope, symbol_node, payload_var, var_value, buf_ptr(var_symbol), is_comptime);
+        }
+
+        ZigList<IrInstSrc *> incoming_values = {0};
+        ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
+
+        if (is_duplicate_label(irb->codegen, payload_scope, node, node->data.while_expr.name))
+            return irb->codegen->invalid_inst_src;
+
+        ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, payload_scope);
+        loop_scope->break_block = end_block;
+        loop_scope->continue_block = continue_block;
+        loop_scope->is_comptime = is_comptime;
+        loop_scope->incoming_blocks = &incoming_blocks;
+        loop_scope->incoming_values = &incoming_values;
+        loop_scope->lval = lval;
+        loop_scope->peer_parent = peer_parent;
+        loop_scope->spill_scope = spill_scope;
+
+        // Note the body block of the loop is not the place that lval and result_loc are used -
+        // it's actually in break statements, handled similarly to return statements.
+        // That is why we set those values in loop_scope above and not in this ir_gen_node call.
+        IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
+        if (body_result == irb->codegen->invalid_inst_src)
+            return body_result;
+
+        if (loop_scope->name != nullptr && loop_scope->name_used == false) {
+            add_node_error(irb->codegen, node, buf_sprintf("unused while label"));
+        }
+
+        if (!instr_is_unreachable(body_result)) {
+            ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result));
+            ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime));
+        }
+
+        if (continue_expr_node) {
+            ir_set_cursor_at_end_and_append_block(irb, continue_block);
+            IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope);
+            if (expr_result == irb->codegen->invalid_inst_src)
+                return expr_result;
+            if (!instr_is_unreachable(expr_result)) {
+                ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, continue_expr_node, expr_result));
+                ir_mark_gen(ir_build_br(irb, payload_scope, node, cond_block, is_comptime));
+            }
+        }
+
+        ir_set_cursor_at_end_and_append_block(irb, else_block);
+        assert(else_node != nullptr);
+
+        // TODO make it an error to write to error variable
+        AstNode *err_symbol_node = else_node; // TODO make more accurate
+        ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol,
+                true, false, false, is_comptime);
+        Scope *err_scope = err_var->child_scope;
+        IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, err_scope, err_symbol_node, err_val_ptr);
+        IrInstSrc *err_value = ir_build_load_ptr(irb, err_scope, err_symbol_node, err_ptr);
+        build_decl_var_and_init(irb, err_scope, err_symbol_node, err_var, err_value, buf_ptr(err_symbol), is_comptime);
+
+        if (peer_parent->peers.length != 0) {
+            peer_parent->peers.last()->next_bb = else_block;
+        }
+        ResultLocPeer *peer_result = create_peer_result(peer_parent);
+        peer_parent->peers.append(peer_result);
+        IrInstSrc *else_result = ir_gen_node_extra(irb, else_node, err_scope, lval, &peer_result->base);
+        if (else_result == irb->codegen->invalid_inst_src)
+            return else_result;
+        if (!instr_is_unreachable(else_result))
+            ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
+        IrBasicBlockSrc *after_else_block = irb->current_basic_block;
+        ir_set_cursor_at_end_and_append_block(irb, end_block);
+        if (else_result) {
+            incoming_blocks.append(after_else_block);
+            incoming_values.append(else_result);
+        } else {
+            incoming_blocks.append(after_cond_block);
+            incoming_values.append(void_else_result);
+        }
+        if (peer_parent->peers.length != 0) {
+            peer_parent->peers.last()->next_bb = end_block;
+        }
+
+        IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
+                incoming_blocks.items, incoming_values.items, peer_parent);
+        return ir_expr_wrap(irb, scope, phi, result_loc);
+    } else if (var_symbol != nullptr) {
+        ir_set_cursor_at_end_and_append_block(irb, cond_block);
+        Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
+        // TODO make it an error to write to payload variable
+        AstNode *symbol_node = node; // TODO make more accurate
+
+        ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol,
+                true, false, false, is_comptime);
+        Scope *child_scope = payload_var->child_scope;
+        ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, child_scope);
+        IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope,
+                LValPtr, nullptr);
+        if (maybe_val_ptr == irb->codegen->invalid_inst_src)
+            return maybe_val_ptr;
+        IrInstSrc *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr);
+        IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, scope, node->data.while_expr.condition, maybe_val);
+        IrBasicBlockSrc *after_cond_block = irb->current_basic_block;
+        IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
+        IrInstSrc *cond_br_inst;
+        if (!instr_is_unreachable(is_non_null)) {
+            cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null,
+                        body_block, else_block, is_comptime);
+            cond_br_inst->is_gen = true;
+        } else {
+            // for the purposes of the source instruction to ir_build_result_peers
+            cond_br_inst = irb->current_basic_block->instruction_list.last();
+        }
+
+        ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
+                is_comptime);
+
+        ir_set_cursor_at_end_and_append_block(irb, body_block);
+        IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, &spill_scope->base, symbol_node, maybe_val_ptr, false);
+        IrInstSrc *var_value = node->data.while_expr.var_is_ptr ?
+            payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, symbol_node, payload_ptr);
+        build_decl_var_and_init(irb, child_scope, symbol_node, payload_var, var_value, buf_ptr(var_symbol), is_comptime);
+
+        ZigList<IrInstSrc *> incoming_values = {0};
+        ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
+
+        if (is_duplicate_label(irb->codegen, child_scope, node, node->data.while_expr.name))
+            return irb->codegen->invalid_inst_src;
+
+        ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
+        loop_scope->break_block = end_block;
+        loop_scope->continue_block = continue_block;
+        loop_scope->is_comptime = is_comptime;
+        loop_scope->incoming_blocks = &incoming_blocks;
+        loop_scope->incoming_values = &incoming_values;
+        loop_scope->lval = lval;
+        loop_scope->peer_parent = peer_parent;
+        loop_scope->spill_scope = spill_scope;
+
+        // Note the body block of the loop is not the place that lval and result_loc are used -
+        // it's actually in break statements, handled similarly to return statements.
+        // That is why we set those values in loop_scope above and not in this ir_gen_node call.
+        IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
+        if (body_result == irb->codegen->invalid_inst_src)
+            return body_result;
+
+        if (loop_scope->name != nullptr && loop_scope->name_used == false) {
+            add_node_error(irb->codegen, node, buf_sprintf("unused while label"));
+        }
+
+        if (!instr_is_unreachable(body_result)) {
+            ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result));
+            ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
+        }
+
+        if (continue_expr_node) {
+            ir_set_cursor_at_end_and_append_block(irb, continue_block);
+            IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, child_scope);
+            if (expr_result == irb->codegen->invalid_inst_src)
+                return expr_result;
+            if (!instr_is_unreachable(expr_result)) {
+                ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, continue_expr_node, expr_result));
+                ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime));
+            }
+        }
+
+        IrInstSrc *else_result = nullptr;
+        if (else_node) {
+            ir_set_cursor_at_end_and_append_block(irb, else_block);
+
+            if (peer_parent->peers.length != 0) {
+                peer_parent->peers.last()->next_bb = else_block;
+            }
+            ResultLocPeer *peer_result = create_peer_result(peer_parent);
+            peer_parent->peers.append(peer_result);
+            else_result = ir_gen_node_extra(irb, else_node, scope, lval, &peer_result->base);
+            if (else_result == irb->codegen->invalid_inst_src)
+                return else_result;
+            if (!instr_is_unreachable(else_result))
+                ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
+        }
+        IrBasicBlockSrc *after_else_block = irb->current_basic_block;
+        ir_set_cursor_at_end_and_append_block(irb, end_block);
+        if (else_result) {
+            incoming_blocks.append(after_else_block);
+            incoming_values.append(else_result);
+        } else {
+            incoming_blocks.append(after_cond_block);
+            incoming_values.append(void_else_result);
+        }
+        if (peer_parent->peers.length != 0) {
+            peer_parent->peers.last()->next_bb = end_block;
+        }
+
+        IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
+                incoming_blocks.items, incoming_values.items, peer_parent);
+        return ir_expr_wrap(irb, scope, phi, result_loc);
+    } else {
+        ir_set_cursor_at_end_and_append_block(irb, cond_block);
+        IrInstSrc *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope);
+        if (cond_val == irb->codegen->invalid_inst_src)
+            return cond_val;
+        IrBasicBlockSrc *after_cond_block = irb->current_basic_block;
+        IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
+        IrInstSrc *cond_br_inst;
+        if (!instr_is_unreachable(cond_val)) {
+            cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val,
+                        body_block, else_block, is_comptime);
+            cond_br_inst->is_gen = true;
+        } else {
+            // for the purposes of the source instruction to ir_build_result_peers
+            cond_br_inst = irb->current_basic_block->instruction_list.last();
+        }
+
+        ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
+                is_comptime);
+        ir_set_cursor_at_end_and_append_block(irb, body_block);
+
+        ZigList<IrInstSrc *> incoming_values = {0};
+        ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
+
+        Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
+
+        if (is_duplicate_label(irb->codegen, subexpr_scope, node, node->data.while_expr.name))
+            return irb->codegen->invalid_inst_src;
+
+        ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, subexpr_scope);
+        loop_scope->break_block = end_block;
+        loop_scope->continue_block = continue_block;
+        loop_scope->is_comptime = is_comptime;
+        loop_scope->incoming_blocks = &incoming_blocks;
+        loop_scope->incoming_values = &incoming_values;
+        loop_scope->lval = lval;
+        loop_scope->peer_parent = peer_parent;
+
+        // Note the body block of the loop is not the place that lval and result_loc are used -
+        // it's actually in break statements, handled similarly to return statements.
+        // That is why we set those values in loop_scope above and not in this ir_gen_node call.
+        IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
+        if (body_result == irb->codegen->invalid_inst_src)
+            return body_result;
+
+        if (loop_scope->name != nullptr && loop_scope->name_used == false) {
+            add_node_error(irb->codegen, node, buf_sprintf("unused while label"));
+        }
+
+        if (!instr_is_unreachable(body_result)) {
+            ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result));
+            ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
+        }
+
+        if (continue_expr_node) {
+            ir_set_cursor_at_end_and_append_block(irb, continue_block);
+            IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope);
+            if (expr_result == irb->codegen->invalid_inst_src)
+                return expr_result;
+            if (!instr_is_unreachable(expr_result)) {
+                ir_mark_gen(ir_build_check_statement_is_void(irb, scope, continue_expr_node, expr_result));
+                ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime));
+            }
+        }
+
+        IrInstSrc *else_result = nullptr;
+        if (else_node) {
+            ir_set_cursor_at_end_and_append_block(irb, else_block);
+
+            if (peer_parent->peers.length != 0) {
+                peer_parent->peers.last()->next_bb = else_block;
+            }
+            ResultLocPeer *peer_result = create_peer_result(peer_parent);
+            peer_parent->peers.append(peer_result);
+
+            else_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_result->base);
+            if (else_result == irb->codegen->invalid_inst_src)
+                return else_result;
+            if (!instr_is_unreachable(else_result))
+                ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
+        }
+        IrBasicBlockSrc *after_else_block = irb->current_basic_block;
+        ir_set_cursor_at_end_and_append_block(irb, end_block);
+        if (else_result) {
+            incoming_blocks.append(after_else_block);
+            incoming_values.append(else_result);
+        } else {
+            incoming_blocks.append(after_cond_block);
+            incoming_values.append(void_else_result);
+        }
+        if (peer_parent->peers.length != 0) {
+            peer_parent->peers.last()->next_bb = end_block;
+        }
+
+        IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
+                incoming_blocks.items, incoming_values.items, peer_parent);
+        return ir_expr_wrap(irb, scope, phi, result_loc);
+    }
+}
+
+static IrInstSrc *ir_gen_for_expr(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypeForExpr);
+
+    AstNode *array_node = node->data.for_expr.array_expr;
+    AstNode *elem_node = node->data.for_expr.elem_node;
+    AstNode *index_node = node->data.for_expr.index_node;
+    AstNode *body_node = node->data.for_expr.body;
+    AstNode *else_node = node->data.for_expr.else_node;
+
+    if (!elem_node) {
+        add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter"));
+        return irb->codegen->invalid_inst_src;
+    }
+    assert(elem_node->type == NodeTypeIdentifier);
+
+    ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, parent_scope);
+
+    IrInstSrc *array_val_ptr = ir_gen_node_extra(irb, array_node, &spill_scope->base, LValPtr, nullptr);
+    if (array_val_ptr == irb->codegen->invalid_inst_src)
+        return array_val_ptr;
+
+    IrInstSrc *is_comptime = ir_build_const_bool(irb, parent_scope, node,
+        ir_should_inline(irb->exec, parent_scope) || node->data.for_expr.is_inline);
+
+    AstNode *index_var_source_node;
+    ZigVar *index_var;
+    const char *index_var_name;
+    if (index_node) {
+        index_var_source_node = index_node;
+        Buf *index_var_name_buf = node_identifier_buf(index_node);
+        index_var = ir_create_var(irb, index_node, parent_scope, index_var_name_buf, true, false, false, is_comptime);
+        index_var_name = buf_ptr(index_var_name_buf);
+    } else {
+        index_var_source_node = node;
+        index_var = ir_create_var(irb, node, parent_scope, nullptr, true, false, true, is_comptime);
+        index_var_name = "i";
+    }
+
+    IrInstSrc *zero = ir_build_const_usize(irb, parent_scope, node, 0);
+    build_decl_var_and_init(irb, parent_scope, index_var_source_node, index_var, zero, index_var_name, is_comptime);
+    parent_scope = index_var->child_scope;
+
+    IrInstSrc *one = ir_build_const_usize(irb, parent_scope, node, 1);
+    IrInstSrc *index_ptr = ir_build_var_ptr(irb, parent_scope, node, index_var);
+
+
+    IrBasicBlockSrc *cond_block = ir_create_basic_block(irb, parent_scope, "ForCond");
+    IrBasicBlockSrc *body_block = ir_create_basic_block(irb, parent_scope, "ForBody");
+    IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "ForEnd");
+    IrBasicBlockSrc *else_block = else_node ? ir_create_basic_block(irb, parent_scope, "ForElse") : end_block;
+    IrBasicBlockSrc *continue_block = ir_create_basic_block(irb, parent_scope, "ForContinue");
+
+    Buf *len_field_name = buf_create_from_str("len");
+    IrInstSrc *len_ref = ir_build_field_ptr(irb, parent_scope, node, array_val_ptr, len_field_name, false);
+    IrInstSrc *len_val = ir_build_load_ptr(irb, &spill_scope->base, node, len_ref);
+    ir_build_br(irb, parent_scope, node, cond_block, is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, cond_block);
+    IrInstSrc *index_val = ir_build_load_ptr(irb, &spill_scope->base, node, index_ptr);
+    IrInstSrc *cond = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpLessThan, index_val, len_val, false);
+    IrBasicBlockSrc *after_cond_block = irb->current_basic_block;
+    IrInstSrc *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
+    IrInstSrc *cond_br_inst = ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, cond,
+                body_block, else_block, is_comptime));
+
+    ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, body_block);
+    IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, &spill_scope->base, node, array_val_ptr, index_val,
+            false, PtrLenSingle, nullptr);
+    // TODO make it an error to write to element variable or i variable.
+    Buf *elem_var_name = node_identifier_buf(elem_node);
+    ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime);
+    Scope *child_scope = elem_var->child_scope;
+
+    IrInstSrc *elem_value = node->data.for_expr.elem_is_ptr ?
+        elem_ptr : ir_build_load_ptr(irb, &spill_scope->base, elem_node, elem_ptr);
+    build_decl_var_and_init(irb, parent_scope, elem_node, elem_var, elem_value, buf_ptr(elem_var_name), is_comptime);
+
+    if (is_duplicate_label(irb->codegen, child_scope, node, node->data.for_expr.name))
+        return irb->codegen->invalid_inst_src;
+
+    ZigList<IrInstSrc *> incoming_values = {0};
+    ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
+    ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
+    loop_scope->break_block = end_block;
+    loop_scope->continue_block = continue_block;
+    loop_scope->is_comptime = is_comptime;
+    loop_scope->incoming_blocks = &incoming_blocks;
+    loop_scope->incoming_values = &incoming_values;
+    loop_scope->lval = LValNone;
+    loop_scope->peer_parent = peer_parent;
+    loop_scope->spill_scope = spill_scope;
+
+    // Note the body block of the loop is not the place that lval and result_loc are used -
+    // it's actually in break statements, handled similarly to return statements.
+    // That is why we set those values in loop_scope above and not in this ir_gen_node call.
+    IrInstSrc *body_result = ir_gen_node(irb, body_node, &loop_scope->base);
+    if (body_result == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    if (loop_scope->name != nullptr && loop_scope->name_used == false) {
+        add_node_error(irb->codegen, node, buf_sprintf("unused for label"));
+    }
+
+    if (!instr_is_unreachable(body_result)) {
+        ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, body_result));
+        ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
+    }
+
+    ir_set_cursor_at_end_and_append_block(irb, continue_block);
+    IrInstSrc *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false);
+    ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)->allow_write_through_const = true;
+    ir_build_br(irb, child_scope, node, cond_block, is_comptime);
+
+    IrInstSrc *else_result = nullptr;
+    if (else_node) {
+        ir_set_cursor_at_end_and_append_block(irb, else_block);
+
+        if (peer_parent->peers.length != 0) {
+            peer_parent->peers.last()->next_bb = else_block;
+        }
+        ResultLocPeer *peer_result = create_peer_result(peer_parent);
+        peer_parent->peers.append(peer_result);
+        else_result = ir_gen_node_extra(irb, else_node, parent_scope, LValNone, &peer_result->base);
+        if (else_result == irb->codegen->invalid_inst_src)
+            return else_result;
+        if (!instr_is_unreachable(else_result))
+            ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
+    }
+    IrBasicBlockSrc *after_else_block = irb->current_basic_block;
+    ir_set_cursor_at_end_and_append_block(irb, end_block);
+
+    if (else_result) {
+        incoming_blocks.append(after_else_block);
+        incoming_values.append(else_result);
+    } else {
+        incoming_blocks.append(after_cond_block);
+        incoming_values.append(void_else_value);
+    }
+    if (peer_parent->peers.length != 0) {
+        peer_parent->peers.last()->next_bb = end_block;
+    }
+
+    IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, incoming_blocks.length,
+            incoming_blocks.items, incoming_values.items, peer_parent);
+    return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
+}
+
+static IrInstSrc *ir_gen_bool_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeBoolLiteral);
+    return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value);
+}
+
+static IrInstSrc *ir_gen_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeEnumLiteral);
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    Buf *name = token_identifier_buf(root_struct, node->main_token + 1);
+    return ir_build_const_enum_literal(irb, scope, node, name);
+}
+
+static IrInstSrc *ir_gen_string_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    Error err;
+    assert(node->type == NodeTypeStringLiteral);
+
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    const char *source = buf_ptr(root_struct->source_code);
+
+    TokenId *token_ids = root_struct->token_ids;
+
+    Buf *str = buf_alloc();
+    if (token_ids[node->main_token] == TokenIdStringLiteral) {
+        size_t byte_offset = root_struct->token_locs[node->main_token].offset;
+        size_t bad_index;
+        if ((err = source_string_literal_buf(source + byte_offset, str, &bad_index))) {
+            add_token_error_offset(irb->codegen, node->owner, node->main_token,
+                    buf_create_from_str("invalid string literal character"), bad_index);
+        }
+        src_assert(source[byte_offset] == '"', node);
+        byte_offset += 1;
+    } else if (token_ids[node->main_token] == TokenIdMultilineStringLiteralLine) {
+        TokenIndex tok_index = node->main_token;
+        bool first = true;
+        for (;token_ids[tok_index] == TokenIdMultilineStringLiteralLine; tok_index += 1) {
+            size_t byte_offset = root_struct->token_locs[tok_index].offset;
+            size_t end = byte_offset;
+            while (source[end] != 0 && source[end] != '\n') {
+                end += 1;
+            }
+            if (!first) {
+                buf_append_char(str, '\n');
+            } else {
+                first = false;
+            }
+            buf_append_mem(str, source + byte_offset + 2, end - byte_offset - 2);
+        }
+    } else {
+        zig_unreachable();
+    }
+    return ir_build_const_str_lit(irb, scope, node, str);
+}
+
+static IrInstSrc *ir_gen_array_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeArrayType);
+
+    AstNode *size_node = node->data.array_type.size;
+    AstNode *child_type_node = node->data.array_type.child_type;
+    bool is_const = node->data.array_type.is_const;
+    bool is_volatile = node->data.array_type.is_volatile;
+    bool is_allow_zero = node->data.array_type.allow_zero_token != 0;
+    AstNode *sentinel_expr = node->data.array_type.sentinel;
+    AstNode *align_expr = node->data.array_type.align_expr;
+
+    Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
+
+    IrInstSrc *sentinel;
+    if (sentinel_expr != nullptr) {
+        sentinel = ir_gen_node(irb, sentinel_expr, comptime_scope);
+        if (sentinel == irb->codegen->invalid_inst_src)
+            return sentinel;
+    } else {
+        sentinel = nullptr;
+    }
+
+    if (size_node) {
+        if (is_const) {
+            add_node_error(irb->codegen, node, buf_create_from_str("const qualifier invalid on array type"));
+            return irb->codegen->invalid_inst_src;
+        }
+        if (is_volatile) {
+            add_node_error(irb->codegen, node, buf_create_from_str("volatile qualifier invalid on array type"));
+            return irb->codegen->invalid_inst_src;
+        }
+        if (is_allow_zero) {
+            add_node_error(irb->codegen, node, buf_create_from_str("allowzero qualifier invalid on array type"));
+            return irb->codegen->invalid_inst_src;
+        }
+        if (align_expr != nullptr) {
+            add_node_error(irb->codegen, node, buf_create_from_str("align qualifier invalid on array type"));
+            return irb->codegen->invalid_inst_src;
+        }
+
+        IrInstSrc *size_value = ir_gen_node(irb, size_node, comptime_scope);
+        if (size_value == irb->codegen->invalid_inst_src)
+            return size_value;
+
+        IrInstSrc *child_type = ir_gen_node(irb, child_type_node, comptime_scope);
+        if (child_type == irb->codegen->invalid_inst_src)
+            return child_type;
+
+        return ir_build_array_type(irb, scope, node, size_value, sentinel, child_type);
+    } else {
+        IrInstSrc *align_value;
+        if (align_expr != nullptr) {
+            align_value = ir_gen_node(irb, align_expr, comptime_scope);
+            if (align_value == irb->codegen->invalid_inst_src)
+                return align_value;
+        } else {
+            align_value = nullptr;
+        }
+
+        IrInstSrc *child_type = ir_gen_node(irb, child_type_node, comptime_scope);
+        if (child_type == irb->codegen->invalid_inst_src)
+            return child_type;
+
+        return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, sentinel,
+                align_value, is_allow_zero);
+    }
+}
+
+static IrInstSrc *ir_gen_anyframe_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeAnyFrameType);
+
+    AstNode *payload_type_node = node->data.anyframe_type.payload_type;
+    IrInstSrc *payload_type_value = nullptr;
+
+    if (payload_type_node != nullptr) {
+        payload_type_value = ir_gen_node(irb, payload_type_node, scope);
+        if (payload_type_value == irb->codegen->invalid_inst_src)
+            return payload_type_value;
+
+    }
+
+    return ir_build_anyframe_type(irb, scope, node, payload_type_value);
+}
+
+static IrInstSrc *ir_gen_undefined_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeUndefinedLiteral);
+    return ir_build_const_undefined(irb, scope, node);
+}
+
+static IrInstSrc *ir_gen_asm_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeAsmExpr);
+    AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
+
+    IrInstSrc *asm_template = ir_gen_node(irb, asm_expr->asm_template, scope);
+    if (asm_template == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    bool is_volatile = asm_expr->volatile_token != 0;
+    bool in_fn_scope = (scope_fn_entry(scope) != nullptr);
+
+    if (!in_fn_scope) {
+        if (is_volatile) {
+            add_token_error(irb->codegen, node->owner, asm_expr->volatile_token,
+                    buf_sprintf("volatile is meaningless on global assembly"));
+            return irb->codegen->invalid_inst_src;
+        }
+
+        if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 ||
+            asm_expr->clobber_list.length != 0)
+        {
+            add_node_error(irb->codegen, node,
+                buf_sprintf("global assembly cannot have inputs, outputs, or clobbers"));
+            return irb->codegen->invalid_inst_src;
+        }
+
+        return ir_build_asm_src(irb, scope, node, asm_template, nullptr, nullptr,
+                                nullptr, 0, is_volatile, true);
+    }
+
+    IrInstSrc **input_list = heap::c_allocator.allocate<IrInstSrc *>(asm_expr->input_list.length);
+    IrInstSrc **output_types = heap::c_allocator.allocate<IrInstSrc *>(asm_expr->output_list.length);
+    ZigVar **output_vars = heap::c_allocator.allocate<ZigVar *>(asm_expr->output_list.length);
+    size_t return_count = 0;
+    if (!is_volatile && asm_expr->output_list.length == 0) {
+        add_node_error(irb->codegen, node,
+                buf_sprintf("assembly expression with no output must be marked volatile"));
+        return irb->codegen->invalid_inst_src;
+    }
+    for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
+        AsmOutput *asm_output = asm_expr->output_list.at(i);
+        if (asm_output->return_type) {
+            return_count += 1;
+
+            IrInstSrc *return_type = ir_gen_node(irb, asm_output->return_type, scope);
+            if (return_type == irb->codegen->invalid_inst_src)
+                return irb->codegen->invalid_inst_src;
+            if (return_count > 1) {
+                add_node_error(irb->codegen, node,
+                        buf_sprintf("inline assembly allows up to one output value"));
+                return irb->codegen->invalid_inst_src;
+            }
+            output_types[i] = return_type;
+        } else {
+            Buf *variable_name = asm_output->variable_name;
+            // TODO there is some duplication here with ir_gen_symbol. I need to do a full audit of how
+            // inline assembly works. https://github.com/ziglang/zig/issues/215
+            ZigVar *var = find_variable(irb->codegen, scope, variable_name, nullptr);
+            if (var) {
+                output_vars[i] = var;
+            } else {
+                add_node_error(irb->codegen, node,
+                        buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
+                return irb->codegen->invalid_inst_src;
+            }
+        }
+
+        const char modifier = *buf_ptr(asm_output->constraint);
+        if (modifier != '=') {
+            add_node_error(irb->codegen, node,
+                buf_sprintf("invalid modifier starting output constraint for '%s': '%c', only '=' is supported."
+                    " Compiler TODO: see https://github.com/ziglang/zig/issues/215",
+                    buf_ptr(asm_output->asm_symbolic_name), modifier));
+            return irb->codegen->invalid_inst_src;
+        }
+    }
+    for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
+        AsmInput *asm_input = asm_expr->input_list.at(i);
+        IrInstSrc *input_value = ir_gen_node(irb, asm_input->expr, scope);
+        if (input_value == irb->codegen->invalid_inst_src)
+            return irb->codegen->invalid_inst_src;
+
+        input_list[i] = input_value;
+    }
+
+    return ir_build_asm_src(irb, scope, node, asm_template, input_list, output_types,
+                            output_vars, return_count, is_volatile, false);
+}
+
+static IrInstSrc *ir_gen_if_optional_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypeIfOptional);
+
+    Buf *var_symbol = node->data.test_expr.var_symbol;
+    AstNode *expr_node = node->data.test_expr.target_node;
+    AstNode *then_node = node->data.test_expr.then_node;
+    AstNode *else_node = node->data.test_expr.else_node;
+    bool var_is_ptr = node->data.test_expr.var_is_ptr;
+
+    ScopeExpr *spill_scope = create_expr_scope(irb->codegen, expr_node, scope);
+    spill_scope->spill_harder = true;
+
+    IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, &spill_scope->base, LValPtr, nullptr);
+    if (maybe_val_ptr == irb->codegen->invalid_inst_src)
+        return maybe_val_ptr;
+
+    IrInstSrc *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr);
+    IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, scope, node, maybe_val);
+
+    IrBasicBlockSrc *then_block = ir_create_basic_block(irb, scope, "OptionalThen");
+    IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "OptionalElse");
+    IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "OptionalEndIf");
+
+    IrInstSrc *is_comptime;
+    if (ir_should_inline(irb->exec, scope)) {
+        is_comptime = ir_build_const_bool(irb, scope, node, true);
+    } else {
+        is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null);
+    }
+    IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, is_non_null,
+            then_block, else_block, is_comptime);
+
+    ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
+            result_loc, is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, then_block);
+
+    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, &spill_scope->base, is_comptime);
+    Scope *var_scope;
+    if (var_symbol) {
+        bool is_shadowable = false;
+        bool is_const = true;
+        ZigVar *var = ir_create_var(irb, node, subexpr_scope,
+                var_symbol, is_const, is_const, is_shadowable, is_comptime);
+
+        IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false);
+        IrInstSrc *var_value = var_is_ptr ?
+            payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, node, payload_ptr);
+        build_decl_var_and_init(irb, subexpr_scope, node, var, var_value, buf_ptr(var_symbol), is_comptime);
+        var_scope = var->child_scope;
+    } else {
+        var_scope = subexpr_scope;
+    }
+    IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval,
+            &peer_parent->peers.at(0)->base);
+    if (then_expr_result == irb->codegen->invalid_inst_src)
+        return then_expr_result;
+    IrBasicBlockSrc *after_then_block = irb->current_basic_block;
+    if (!instr_is_unreachable(then_expr_result))
+        ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
+
+    ir_set_cursor_at_end_and_append_block(irb, else_block);
+    IrInstSrc *else_expr_result;
+    if (else_node) {
+        else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base);
+        if (else_expr_result == irb->codegen->invalid_inst_src)
+            return else_expr_result;
+    } else {
+        else_expr_result = ir_build_const_void(irb, scope, node);
+        ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
+    }
+    IrBasicBlockSrc *after_else_block = irb->current_basic_block;
+    if (!instr_is_unreachable(else_expr_result))
+        ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
+
+    ir_set_cursor_at_end_and_append_block(irb, endif_block);
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
+    incoming_values[0] = then_expr_result;
+    incoming_values[1] = else_expr_result;
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
+    incoming_blocks[0] = after_then_block;
+    incoming_blocks[1] = after_else_block;
+
+    IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
+    return ir_expr_wrap(irb, scope, phi, result_loc);
+}
+
+static IrInstSrc *ir_gen_if_err_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypeIfErrorExpr);
+
+    AstNode *target_node = node->data.if_err_expr.target_node;
+    AstNode *then_node = node->data.if_err_expr.then_node;
+    AstNode *else_node = node->data.if_err_expr.else_node;
+    bool var_is_ptr = node->data.if_err_expr.var_is_ptr;
+    bool var_is_const = true;
+    Buf *var_symbol = node->data.if_err_expr.var_symbol;
+    Buf *err_symbol = node->data.if_err_expr.err_symbol;
+
+    IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr);
+    if (err_val_ptr == irb->codegen->invalid_inst_src)
+        return err_val_ptr;
+
+    IrInstSrc *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr);
+    IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node, err_val_ptr, true, false);
+
+    IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, scope, "TryOk");
+    IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "TryElse");
+    IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "TryEnd");
+
+    bool force_comptime = ir_should_inline(irb->exec, scope);
+    IrInstSrc *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err);
+    IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime);
+
+    ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
+            result_loc, is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, ok_block);
+
+    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
+    Scope *var_scope;
+    if (var_symbol) {
+        bool is_shadowable = false;
+        IrInstSrc *var_is_comptime = force_comptime ? ir_build_const_bool(irb, subexpr_scope, node, true) : ir_build_test_comptime(irb, subexpr_scope, node, err_val);
+        ZigVar *var = ir_create_var(irb, node, subexpr_scope,
+                var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime);
+
+        IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, subexpr_scope, node, err_val_ptr, false, false);
+        IrInstSrc *var_value = var_is_ptr ?
+            payload_ptr : ir_build_load_ptr(irb, subexpr_scope, node, payload_ptr);
+        build_decl_var_and_init(irb, subexpr_scope, node, var, var_value, buf_ptr(var_symbol), var_is_comptime);
+        var_scope = var->child_scope;
+    } else {
+        var_scope = subexpr_scope;
+    }
+    IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval,
+            &peer_parent->peers.at(0)->base);
+    if (then_expr_result == irb->codegen->invalid_inst_src)
+        return then_expr_result;
+    IrBasicBlockSrc *after_then_block = irb->current_basic_block;
+    if (!instr_is_unreachable(then_expr_result))
+        ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
+
+    ir_set_cursor_at_end_and_append_block(irb, else_block);
+
+    IrInstSrc *else_expr_result;
+    if (else_node) {
+        Scope *err_var_scope;
+        if (err_symbol) {
+            bool is_shadowable = false;
+            bool is_const = true;
+            ZigVar *var = ir_create_var(irb, node, subexpr_scope,
+                    err_symbol, is_const, is_const, is_shadowable, is_comptime);
+
+            IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, subexpr_scope, node, err_val_ptr);
+            IrInstSrc *err_value = ir_build_load_ptr(irb, subexpr_scope, node, err_ptr);
+            build_decl_var_and_init(irb, subexpr_scope, node, var, err_value, buf_ptr(err_symbol), is_comptime);
+            err_var_scope = var->child_scope;
+        } else {
+            err_var_scope = subexpr_scope;
+        }
+        else_expr_result = ir_gen_node_extra(irb, else_node, err_var_scope, lval, &peer_parent->peers.at(1)->base);
+        if (else_expr_result == irb->codegen->invalid_inst_src)
+            return else_expr_result;
+    } else {
+        else_expr_result = ir_build_const_void(irb, scope, node);
+        ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
+    }
+    IrBasicBlockSrc *after_else_block = irb->current_basic_block;
+    if (!instr_is_unreachable(else_expr_result))
+        ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
+
+    ir_set_cursor_at_end_and_append_block(irb, endif_block);
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
+    incoming_values[0] = then_expr_result;
+    incoming_values[1] = else_expr_result;
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
+    incoming_blocks[0] = after_then_block;
+    incoming_blocks[1] = after_else_block;
+
+    IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
+    return ir_expr_wrap(irb, scope, phi, result_loc);
+}
+
+static bool ir_gen_switch_prong_expr(IrBuilderSrc *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node,
+        IrBasicBlockSrc *end_block, IrInstSrc *is_comptime, IrInstSrc *var_is_comptime,
+        IrInstSrc *target_value_ptr, IrInstSrc **prong_values, size_t prong_values_len,
+        ZigList<IrBasicBlockSrc *> *incoming_blocks, ZigList<IrInstSrc *> *incoming_values,
+        IrInstSrcSwitchElseVar **out_switch_else_var, LVal lval, ResultLoc *result_loc)
+{
+    assert(switch_node->type == NodeTypeSwitchExpr);
+    assert(prong_node->type == NodeTypeSwitchProng);
+
+    AstNode *expr_node = prong_node->data.switch_prong.expr;
+    AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol;
+    Scope *child_scope;
+    if (var_symbol_node) {
+        assert(var_symbol_node->type == NodeTypeIdentifier);
+        Buf *var_name = node_identifier_buf(var_symbol_node);
+        bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr;
+
+        bool is_shadowable = false;
+        bool is_const = true;
+        ZigVar *var = ir_create_var(irb, var_symbol_node, scope,
+                var_name, is_const, is_const, is_shadowable, var_is_comptime);
+        child_scope = var->child_scope;
+        IrInstSrc *var_value;
+        if (out_switch_else_var != nullptr) {
+            IrInstSrcSwitchElseVar *switch_else_var = ir_build_switch_else_var(irb, scope, var_symbol_node,
+                    target_value_ptr);
+            *out_switch_else_var = switch_else_var;
+            IrInstSrc *payload_ptr = &switch_else_var->base;
+            var_value = var_is_ptr ?
+                payload_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, payload_ptr);
+        } else if (prong_values != nullptr) {
+            IrInstSrc *payload_ptr = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr,
+                    prong_values, prong_values_len);
+            var_value = var_is_ptr ?
+                payload_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, payload_ptr);
+        } else {
+            var_value = var_is_ptr ?
+                target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr);
+        }
+        build_decl_var_and_init(irb, scope, var_symbol_node, var, var_value, buf_ptr(var_name), var_is_comptime);
+    } else {
+        child_scope = scope;
+    }
+
+    IrInstSrc *expr_result = ir_gen_node_extra(irb, expr_node, child_scope, lval, result_loc);
+    if (expr_result == irb->codegen->invalid_inst_src)
+        return false;
+    if (!instr_is_unreachable(expr_result))
+        ir_mark_gen(ir_build_br(irb, scope, switch_node, end_block, is_comptime));
+    incoming_blocks->append(irb->current_basic_block);
+    incoming_values->append(expr_result);
+    return true;
+}
+
+static IrInstSrc *ir_gen_switch_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypeSwitchExpr);
+
+    AstNode *target_node = node->data.switch_expr.expr;
+    IrInstSrc *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr);
+    if (target_value_ptr == irb->codegen->invalid_inst_src)
+        return target_value_ptr;
+    IrInstSrc *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr);
+
+    IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "SwitchElse");
+    IrBasicBlockSrc *end_block = ir_create_basic_block(irb, scope, "SwitchEnd");
+
+    size_t prong_count = node->data.switch_expr.prongs.length;
+    ZigList<IrInstSrcSwitchBrCase> cases = {0};
+
+    IrInstSrc *is_comptime;
+    IrInstSrc *var_is_comptime;
+    if (ir_should_inline(irb->exec, scope)) {
+        is_comptime = ir_build_const_bool(irb, scope, node, true);
+        var_is_comptime = is_comptime;
+    } else {
+        is_comptime = ir_build_test_comptime(irb, scope, node, target_value);
+        var_is_comptime = ir_build_test_comptime(irb, scope, node, target_value_ptr);
+    }
+
+    ZigList<IrInstSrc *> incoming_values = {0};
+    ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
+    ZigList<IrInstSrcCheckSwitchProngsRange> check_ranges = {0};
+
+    IrInstSrcSwitchElseVar *switch_else_var = nullptr;
+
+    ResultLocPeerParent *peer_parent = heap::c_allocator.create<ResultLocPeerParent>();
+    peer_parent->base.id = ResultLocIdPeerParent;
+    peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const;
+    peer_parent->end_bb = end_block;
+    peer_parent->is_comptime = is_comptime;
+    peer_parent->parent = result_loc;
+
+    ir_build_reset_result(irb, scope, node, &peer_parent->base);
+
+    // First do the else and the ranges
+    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
+    Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
+    AstNode *else_prong = nullptr;
+    AstNode *underscore_prong = nullptr;
+    for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
+        AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
+        size_t prong_item_count = prong_node->data.switch_prong.items.length;
+        if (prong_node->data.switch_prong.any_items_are_range) {
+            ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
+
+            IrInstSrc *ok_bit = nullptr;
+            AstNode *last_item_node = nullptr;
+            for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
+                AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
+                last_item_node = item_node;
+                if (item_node->type == NodeTypeSwitchRange) {
+                    AstNode *start_node = item_node->data.switch_range.start;
+                    AstNode *end_node = item_node->data.switch_range.end;
+
+                    IrInstSrc *start_value = ir_gen_node(irb, start_node, comptime_scope);
+                    if (start_value == irb->codegen->invalid_inst_src)
+                        return irb->codegen->invalid_inst_src;
+
+                    IrInstSrc *end_value = ir_gen_node(irb, end_node, comptime_scope);
+                    if (end_value == irb->codegen->invalid_inst_src)
+                        return irb->codegen->invalid_inst_src;
+
+                    IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one();
+                    check_range->start = start_value;
+                    check_range->end = end_value;
+
+                    IrInstSrc *lower_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpGreaterOrEq,
+                            target_value, start_value, false);
+                    IrInstSrc *upper_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpLessOrEq,
+                            target_value, end_value, false);
+                    IrInstSrc *both_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolAnd,
+                            lower_range_ok, upper_range_ok, false);
+                    if (ok_bit) {
+                        ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, both_ok, ok_bit, false);
+                    } else {
+                        ok_bit = both_ok;
+                    }
+                } else {
+                    IrInstSrc *item_value = ir_gen_node(irb, item_node, comptime_scope);
+                    if (item_value == irb->codegen->invalid_inst_src)
+                        return irb->codegen->invalid_inst_src;
+
+                    IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one();
+                    check_range->start = item_value;
+                    check_range->end = item_value;
+
+                    IrInstSrc *cmp_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpEq,
+                            item_value, target_value, false);
+                    if (ok_bit) {
+                        ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, cmp_ok, ok_bit, false);
+                    } else {
+                        ok_bit = cmp_ok;
+                    }
+                }
+            }
+
+            IrBasicBlockSrc *range_block_yes = ir_create_basic_block(irb, scope, "SwitchRangeYes");
+            IrBasicBlockSrc *range_block_no = ir_create_basic_block(irb, scope, "SwitchRangeNo");
+
+            assert(ok_bit);
+            assert(last_item_node);
+            IrInstSrc *br_inst = ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit,
+                        range_block_yes, range_block_no, is_comptime));
+            if (peer_parent->base.source_instruction == nullptr) {
+                peer_parent->base.source_instruction = br_inst;
+            }
+
+            if (peer_parent->peers.length > 0) {
+                peer_parent->peers.last()->next_bb = range_block_yes;
+            }
+            peer_parent->peers.append(this_peer_result_loc);
+            ir_set_cursor_at_end_and_append_block(irb, range_block_yes);
+            if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
+                is_comptime, var_is_comptime, target_value_ptr, nullptr, 0,
+                &incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base))
+            {
+                return irb->codegen->invalid_inst_src;
+            }
+
+            ir_set_cursor_at_end_and_append_block(irb, range_block_no);
+        } else {
+            if (prong_item_count == 0) {
+                if (else_prong) {
+                    ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
+                            buf_sprintf("multiple else prongs in switch expression"));
+                    add_error_note(irb->codegen, msg, else_prong,
+                            buf_sprintf("previous else prong is here"));
+                    return irb->codegen->invalid_inst_src;
+                }
+                else_prong = prong_node;
+            } else if (prong_item_count == 1 &&
+                    prong_node->data.switch_prong.items.at(0)->type == NodeTypeIdentifier &&
+                    buf_eql_str(node_identifier_buf(prong_node->data.switch_prong.items.at(0)), "_")) {
+                if (underscore_prong) {
+                    ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
+                            buf_sprintf("multiple '_' prongs in switch expression"));
+                    add_error_note(irb->codegen, msg, underscore_prong,
+                            buf_sprintf("previous '_' prong is here"));
+                    return irb->codegen->invalid_inst_src;
+                }
+                underscore_prong = prong_node;
+            } else {
+                continue;
+            }
+           if (underscore_prong && else_prong) {
+                ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
+                        buf_sprintf("else and '_' prong in switch expression"));
+                if (underscore_prong == prong_node)
+                    add_error_note(irb->codegen, msg, else_prong,
+                            buf_sprintf("else prong is here"));
+                else
+                    add_error_note(irb->codegen, msg, underscore_prong,
+                            buf_sprintf("'_' prong is here"));
+                return irb->codegen->invalid_inst_src;
+            }
+            ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
+
+            IrBasicBlockSrc *prev_block = irb->current_basic_block;
+            if (peer_parent->peers.length > 0) {
+                peer_parent->peers.last()->next_bb = else_block;
+            }
+            peer_parent->peers.append(this_peer_result_loc);
+            ir_set_cursor_at_end_and_append_block(irb, else_block);
+            if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
+                is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values,
+                &switch_else_var, LValNone, &this_peer_result_loc->base))
+            {
+                return irb->codegen->invalid_inst_src;
+            }
+            ir_set_cursor_at_end(irb, prev_block);
+        }
+    }
+
+    // next do the non-else non-ranges
+    for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
+        AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
+        size_t prong_item_count = prong_node->data.switch_prong.items.length;
+        if (prong_item_count == 0)
+            continue;
+        if (prong_node->data.switch_prong.any_items_are_range)
+            continue;
+        if (underscore_prong == prong_node)
+            continue;
+
+        ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
+
+        IrBasicBlockSrc *prong_block = ir_create_basic_block(irb, scope, "SwitchProng");
+        IrInstSrc **items = heap::c_allocator.allocate<IrInstSrc *>(prong_item_count);
+
+        for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
+            AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
+            assert(item_node->type != NodeTypeSwitchRange);
+
+            IrInstSrc *item_value = ir_gen_node(irb, item_node, comptime_scope);
+            if (item_value == irb->codegen->invalid_inst_src)
+                return irb->codegen->invalid_inst_src;
+
+            IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one();
+            check_range->start = item_value;
+            check_range->end = item_value;
+
+            IrInstSrcSwitchBrCase *this_case = cases.add_one();
+            this_case->value = item_value;
+            this_case->block = prong_block;
+
+            items[item_i] = item_value;
+        }
+
+        IrBasicBlockSrc *prev_block = irb->current_basic_block;
+        if (peer_parent->peers.length > 0) {
+            peer_parent->peers.last()->next_bb = prong_block;
+        }
+        peer_parent->peers.append(this_peer_result_loc);
+        ir_set_cursor_at_end_and_append_block(irb, prong_block);
+        if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
+            is_comptime, var_is_comptime, target_value_ptr, items, prong_item_count,
+            &incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base))
+        {
+            return irb->codegen->invalid_inst_src;
+        }
+
+        ir_set_cursor_at_end(irb, prev_block);
+
+    }
+
+    IrInstSrc *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value,
+            check_ranges.items, check_ranges.length, else_prong, underscore_prong != nullptr);
+
+    IrInstSrc *br_instruction;
+    if (cases.length == 0) {
+        br_instruction = ir_build_br(irb, scope, node, else_block, is_comptime);
+    } else {
+        IrInstSrcSwitchBr *switch_br = ir_build_switch_br_src(irb, scope, node, target_value, else_block,
+                cases.length, cases.items, is_comptime, switch_prongs_void);
+        if (switch_else_var != nullptr) {
+            switch_else_var->switch_br = switch_br;
+        }
+        br_instruction = &switch_br->base;
+    }
+    if (peer_parent->base.source_instruction == nullptr) {
+        peer_parent->base.source_instruction = br_instruction;
+    }
+    for (size_t i = 0; i < peer_parent->peers.length; i += 1) {
+        peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction;
+    }
+
+    if (!else_prong && !underscore_prong) {
+        if (peer_parent->peers.length != 0) {
+            peer_parent->peers.last()->next_bb = else_block;
+        }
+        ir_set_cursor_at_end_and_append_block(irb, else_block);
+        ir_build_unreachable(irb, scope, node);
+    } else {
+        if (peer_parent->peers.length != 0) {
+            peer_parent->peers.last()->next_bb = end_block;
+        }
+    }
+
+    ir_set_cursor_at_end_and_append_block(irb, end_block);
+    assert(incoming_blocks.length == incoming_values.length);
+    IrInstSrc *result_instruction;
+    if (incoming_blocks.length == 0) {
+        result_instruction = ir_build_const_void(irb, scope, node);
+    } else {
+        result_instruction = ir_build_phi(irb, scope, node, incoming_blocks.length,
+                incoming_blocks.items, incoming_values.items, peer_parent);
+    }
+    return ir_lval_wrap(irb, scope, result_instruction, lval, result_loc);
+}
+
+static IrInstSrc *ir_gen_comptime(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) {
+    assert(node->type == NodeTypeCompTime);
+
+    Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope);
+    // purposefully pass null for result_loc and let EndExpr handle it
+    return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr);
+}
+
+static IrInstSrc *ir_gen_nosuspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) {
+    assert(node->type == NodeTypeNoSuspend);
+
+    Scope *child_scope = create_nosuspend_scope(irb->codegen, node, parent_scope);
+    // purposefully pass null for result_loc and let EndExpr handle it
+    return ir_gen_node_extra(irb, node->data.nosuspend_expr.expr, child_scope, lval, nullptr);
+}
+
+static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) {
+    IrInstSrc *is_comptime;
+    if (ir_should_inline(irb->exec, break_scope)) {
+        is_comptime = ir_build_const_bool(irb, break_scope, node, true);
+    } else {
+        is_comptime = block_scope->is_comptime;
+    }
+
+    IrInstSrc *result_value;
+    if (node->data.break_expr.expr) {
+        ResultLocPeer *peer_result = create_peer_result(block_scope->peer_parent);
+        block_scope->peer_parent->peers.append(peer_result);
+
+        result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope, block_scope->lval,
+                &peer_result->base);
+        if (result_value == irb->codegen->invalid_inst_src)
+            return irb->codegen->invalid_inst_src;
+    } else {
+        result_value = ir_build_const_void(irb, break_scope, node);
+    }
+
+    IrBasicBlockSrc *dest_block = block_scope->end_block;
+    if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr))
+        return irb->codegen->invalid_inst_src;
+
+    block_scope->incoming_blocks->append(irb->current_basic_block);
+    block_scope->incoming_values->append(result_value);
+    return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
+}
+
+static IrInstSrc *ir_gen_break(IrBuilderSrc *irb, Scope *break_scope, AstNode *node) {
+    assert(node->type == NodeTypeBreak);
+
+    // Search up the scope. We'll find one of these things first:
+    // * function definition scope or global scope => error, break outside loop
+    // * defer expression scope => error, cannot break out of defer expression
+    // * loop scope => OK
+    // * (if it's a labeled break) labeled block => OK
+
+    Scope *search_scope = break_scope;
+    ScopeLoop *loop_scope;
+    for (;;) {
+        if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
+            if (node->data.break_expr.name != nullptr) {
+                add_node_error(irb->codegen, node, buf_sprintf("label not found: '%s'", buf_ptr(node->data.break_expr.name)));
+                return irb->codegen->invalid_inst_src;
+            } else {
+                add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));
+                return irb->codegen->invalid_inst_src;
+            }
+        } else if (search_scope->id == ScopeIdDeferExpr) {
+            add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression"));
+            return irb->codegen->invalid_inst_src;
+        } else if (search_scope->id == ScopeIdLoop) {
+            ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
+            if (node->data.break_expr.name == nullptr ||
+                (this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name)))
+            {
+                this_loop_scope->name_used = true;
+                loop_scope = this_loop_scope;
+                break;
+            }
+        } else if (search_scope->id == ScopeIdBlock) {
+            ScopeBlock *this_block_scope = (ScopeBlock *)search_scope;
+            if (node->data.break_expr.name != nullptr &&
+                (this_block_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_block_scope->name)))
+            {
+                assert(this_block_scope->end_block != nullptr);
+                this_block_scope->name_used = true;
+                return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
+            }
+        } else if (search_scope->id == ScopeIdSuspend) {
+            add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block"));
+            return irb->codegen->invalid_inst_src;
+        }
+        search_scope = search_scope->parent;
+    }
+
+    IrInstSrc *is_comptime;
+    if (ir_should_inline(irb->exec, break_scope)) {
+        is_comptime = ir_build_const_bool(irb, break_scope, node, true);
+    } else {
+        is_comptime = loop_scope->is_comptime;
+    }
+
+    IrInstSrc *result_value;
+    if (node->data.break_expr.expr) {
+        ResultLocPeer *peer_result = create_peer_result(loop_scope->peer_parent);
+        loop_scope->peer_parent->peers.append(peer_result);
+
+        result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope,
+                loop_scope->lval, &peer_result->base);
+        if (result_value == irb->codegen->invalid_inst_src)
+            return irb->codegen->invalid_inst_src;
+    } else {
+        result_value = ir_build_const_void(irb, break_scope, node);
+    }
+
+    IrBasicBlockSrc *dest_block = loop_scope->break_block;
+    if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr))
+        return irb->codegen->invalid_inst_src;
+
+    loop_scope->incoming_blocks->append(irb->current_basic_block);
+    loop_scope->incoming_values->append(result_value);
+    return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
+}
+
+static IrInstSrc *ir_gen_continue(IrBuilderSrc *irb, Scope *continue_scope, AstNode *node) {
+    assert(node->type == NodeTypeContinue);
+
+    // Search up the scope. We'll find one of these things first:
+    // * function definition scope or global scope => error, break outside loop
+    // * defer expression scope => error, cannot break out of defer expression
+    // * loop scope => OK
+
+    ZigList<ScopeRuntime *> runtime_scopes = {};
+
+    Scope *search_scope = continue_scope;
+    ScopeLoop *loop_scope;
+    for (;;) {
+        if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
+            if (node->data.continue_expr.name != nullptr) {
+                add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.continue_expr.name)));
+                return irb->codegen->invalid_inst_src;
+            } else {
+                add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop"));
+                return irb->codegen->invalid_inst_src;
+            }
+        } else if (search_scope->id == ScopeIdDeferExpr) {
+            add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression"));
+            return irb->codegen->invalid_inst_src;
+        } else if (search_scope->id == ScopeIdLoop) {
+            ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
+            if (node->data.continue_expr.name == nullptr ||
+                (this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name)))
+            {
+                this_loop_scope->name_used = true;
+                loop_scope = this_loop_scope;
+                break;
+            }
+        } else if (search_scope->id == ScopeIdRuntime) {
+            ScopeRuntime *scope_runtime = (ScopeRuntime *)search_scope;
+            runtime_scopes.append(scope_runtime);
+        }
+        search_scope = search_scope->parent;
+    }
+
+    IrInstSrc *is_comptime;
+    if (ir_should_inline(irb->exec, continue_scope)) {
+        is_comptime = ir_build_const_bool(irb, continue_scope, node, true);
+    } else {
+        is_comptime = loop_scope->is_comptime;
+    }
+
+    for (size_t i = 0; i < runtime_scopes.length; i += 1) {
+        ScopeRuntime *scope_runtime = runtime_scopes.at(i);
+        ir_mark_gen(ir_build_check_runtime_scope(irb, continue_scope, node, scope_runtime->is_comptime, is_comptime));
+    }
+    runtime_scopes.deinit();
+
+    IrBasicBlockSrc *dest_block = loop_scope->continue_block;
+    if (!ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, nullptr, nullptr))
+        return irb->codegen->invalid_inst_src;
+    return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime));
+}
+
+static IrInstSrc *ir_gen_error_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeErrorType);
+    return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_global_error_set);
+}
+
+static IrInstSrc *ir_gen_defer(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
+    assert(node->type == NodeTypeDefer);
+
+    ScopeDefer *defer_child_scope = create_defer_scope(irb->codegen, node, parent_scope);
+    node->data.defer.child_scope = &defer_child_scope->base;
+
+    ScopeDeferExpr *defer_expr_scope = create_defer_expr_scope(irb->codegen, node, parent_scope);
+    node->data.defer.expr_scope = &defer_expr_scope->base;
+
+    return ir_build_const_void(irb, parent_scope, node);
+}
+
+static IrInstSrc *ir_gen_slice(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
+    assert(node->type == NodeTypeSliceExpr);
+
+    AstNodeSliceExpr *slice_expr = &node->data.slice_expr;
+    AstNode *array_node = slice_expr->array_ref_expr;
+    AstNode *start_node = slice_expr->start;
+    AstNode *end_node = slice_expr->end;
+    AstNode *sentinel_node = slice_expr->sentinel;
+
+    IrInstSrc *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr);
+    if (ptr_value == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *start_value = ir_gen_node(irb, start_node, scope);
+    if (start_value == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *end_value;
+    if (end_node) {
+        end_value = ir_gen_node(irb, end_node, scope);
+        if (end_value == irb->codegen->invalid_inst_src)
+            return irb->codegen->invalid_inst_src;
+    } else {
+        end_value = nullptr;
+    }
+
+    IrInstSrc *sentinel_value;
+    if (sentinel_node) {
+        sentinel_value = ir_gen_node(irb, sentinel_node, scope);
+        if (sentinel_value == irb->codegen->invalid_inst_src)
+            return irb->codegen->invalid_inst_src;
+    } else {
+        sentinel_value = nullptr;
+    }
+
+    IrInstSrc *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value,
+            sentinel_value, true, result_loc);
+    return ir_lval_wrap(irb, scope, slice, lval, result_loc);
+}
+
+static IrInstSrc *ir_gen_catch(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypeCatchExpr);
+
+    AstNode *op1_node = node->data.unwrap_err_expr.op1;
+    AstNode *op2_node = node->data.unwrap_err_expr.op2;
+    AstNode *var_node = node->data.unwrap_err_expr.symbol;
+
+    if (op2_node->type == NodeTypeUnreachable) {
+        if (var_node != nullptr) {
+            assert(var_node->type == NodeTypeIdentifier);
+            Buf *var_name = node_identifier_buf(var_node);
+            add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name)));
+            return irb->codegen->invalid_inst_src;
+        }
+        return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc);
+    }
+
+
+    ScopeExpr *spill_scope = create_expr_scope(irb->codegen, op1_node, parent_scope);
+    spill_scope->spill_harder = true;
+
+    IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, op1_node, &spill_scope->base, LValPtr, nullptr);
+    if (err_union_ptr == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *is_err = ir_build_test_err_src(irb, parent_scope, node, err_union_ptr, true, false);
+
+    IrInstSrc *is_comptime;
+    if (ir_should_inline(irb->exec, parent_scope)) {
+        is_comptime = ir_build_const_bool(irb, parent_scope, node, true);
+    } else {
+        is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_err);
+    }
+
+    IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrOk");
+    IrBasicBlockSrc *err_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrError");
+    IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrEnd");
+    IrInstSrc *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime);
+
+    ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block, result_loc,
+            is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, err_block);
+    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, &spill_scope->base, is_comptime);
+    Scope *err_scope;
+    if (var_node) {
+        assert(var_node->type == NodeTypeIdentifier);
+        Buf *var_name = node_identifier_buf(var_node);
+        bool is_const = true;
+        bool is_shadowable = false;
+        ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_name,
+            is_const, is_const, is_shadowable, is_comptime);
+        err_scope = var->child_scope;
+        IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, err_scope, node, err_union_ptr);
+        IrInstSrc *err_value = ir_build_load_ptr(irb, err_scope, var_node, err_ptr);
+        build_decl_var_and_init(irb, err_scope, var_node, var, err_value, buf_ptr(var_name), is_comptime);
+    } else {
+        err_scope = subexpr_scope;
+    }
+    IrInstSrc *err_result = ir_gen_node_extra(irb, op2_node, err_scope, LValNone, &peer_parent->peers.at(0)->base);
+    if (err_result == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+    IrBasicBlockSrc *after_err_block = irb->current_basic_block;
+    if (!instr_is_unreachable(err_result))
+        ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
+
+    ir_set_cursor_at_end_and_append_block(irb, ok_block);
+    IrInstSrc *unwrapped_ptr = ir_build_unwrap_err_payload_src(irb, parent_scope, node, err_union_ptr, false, false);
+    IrInstSrc *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
+    ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base);
+    IrBasicBlockSrc *after_ok_block = irb->current_basic_block;
+    ir_build_br(irb, parent_scope, node, end_block, is_comptime);
+
+    ir_set_cursor_at_end_and_append_block(irb, end_block);
+    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
+    incoming_values[0] = err_result;
+    incoming_values[1] = unwrapped_payload;
+    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
+    incoming_blocks[0] = after_err_block;
+    incoming_blocks[1] = after_ok_block;
+    IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
+    return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
+}
+
+static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) {
+    if (inner_scope == nullptr || inner_scope == outer_scope) return false;
+    bool need_comma = render_instance_name_recursive(codegen, name, outer_scope, inner_scope->parent);
+    if (inner_scope->id != ScopeIdVarDecl)
+        return need_comma;
+
+    ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope;
+    if (need_comma)
+        buf_append_char(name, ',');
+    // TODO: const ptr reinterpret here to make the var type agree with the value?
+    render_const_value(codegen, name, var_scope->var->const_value);
+    return true;
+}
+
+Buf *get_anon_type_name(CodeGen *codegen, IrExecutableSrc *exec, const char *kind_name,
+        Scope *scope, AstNode *source_node, Buf *out_bare_name)
+{
+    if (exec != nullptr && exec->name) {
+        ZigType *import = get_scope_import(scope);
+        Buf *namespace_name = buf_alloc();
+        append_namespace_qualification(codegen, namespace_name, import);
+        buf_append_buf(namespace_name, exec->name);
+        buf_init_from_buf(out_bare_name, exec->name);
+        return namespace_name;
+    } else if (exec != nullptr && exec->name_fn != nullptr) {
+        Buf *name = buf_alloc();
+        buf_append_buf(name, &exec->name_fn->symbol_name);
+        buf_appendf(name, "(");
+        render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope);
+        buf_appendf(name, ")");
+        buf_init_from_buf(out_bare_name, name);
+        return name;
+    } else {
+        ZigType *import = get_scope_import(scope);
+        Buf *namespace_name = buf_alloc();
+        append_namespace_qualification(codegen, namespace_name, import);
+        RootStruct *root_struct = source_node->owner->data.structure.root_struct;
+        TokenLoc tok_loc = root_struct->token_locs[source_node->main_token];
+        buf_appendf(namespace_name, "%s:%u:%u", kind_name,
+                tok_loc.line + 1, tok_loc.column + 1);
+        buf_init_from_buf(out_bare_name, namespace_name);
+        return namespace_name;
+    }
+}
+
+static IrInstSrc *ir_gen_container_decl(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
+    assert(node->type == NodeTypeContainerDecl);
+
+    ContainerKind kind = node->data.container_decl.kind;
+    Buf *bare_name = buf_alloc();
+    Buf *name = get_anon_type_name(irb->codegen, irb->exec, container_string(kind), parent_scope, node, bare_name);
+
+    ContainerLayout layout = node->data.container_decl.layout;
+    ZigType *container_type = get_partial_container_type(irb->codegen, parent_scope,
+            kind, node, buf_ptr(name), bare_name, layout);
+    ScopeDecls *child_scope = get_container_scope(container_type);
+
+    for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) {
+        AstNode *child_node = node->data.container_decl.decls.at(i);
+        scan_decls(irb->codegen, child_scope, child_node);
+    }
+
+    TldContainer *tld_container = heap::c_allocator.create<TldContainer>();
+    init_tld(&tld_container->base, TldIdContainer, bare_name, VisibModPub, node, parent_scope);
+    tld_container->type_entry = container_type;
+    tld_container->decls_scope = child_scope;
+    irb->codegen->resolve_queue.append(&tld_container->base);
+
+    // Add this to the list to mark as invalid if analyzing this exec fails.
+    irb->exec->tld_list.append(&tld_container->base);
+
+    return ir_build_const_type(irb, parent_scope, node, container_type);
+}
+
+static IrInstSrc *ir_gen_err_set_decl(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
+    assert(node->type == NodeTypeErrorSetDecl);
+
+    uint32_t err_count = node->data.err_set_decl.decls.length;
+
+    Buf bare_name = BUF_INIT;
+    Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", parent_scope, node, &bare_name);
+    ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
+    buf_init_from_buf(&err_set_type->name, type_name);
+    err_set_type->data.error_set.err_count = err_count;
+    err_set_type->size_in_bits = irb->codegen->builtin_types.entry_global_error_set->size_in_bits;
+    err_set_type->abi_align = irb->codegen->builtin_types.entry_global_error_set->abi_align;
+    err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size;
+    err_set_type->data.error_set.errors = heap::c_allocator.allocate<ErrorTableEntry *>(err_count);
+
+    size_t errors_count = irb->codegen->errors_by_index.length + err_count;
+    ErrorTableEntry **errors = heap::c_allocator.allocate<ErrorTableEntry *>(errors_count);
+
+    for (uint32_t i = 0; i < err_count; i += 1) {
+        AstNode *field_node = node->data.err_set_decl.decls.at(i);
+        AstNode *symbol_node = ast_field_to_symbol_node(field_node);
+        Buf *err_name = node_identifier_buf(symbol_node);
+        ErrorTableEntry *err = heap::c_allocator.create<ErrorTableEntry>();
+        err->decl_node = field_node;
+        buf_init_from_buf(&err->name, err_name);
+
+        auto existing_entry = irb->codegen->error_table.put_unique(err_name, err);
+        if (existing_entry) {
+            err->value = existing_entry->value->value;
+        } else {
+            size_t error_value_count = irb->codegen->errors_by_index.length;
+            assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)irb->codegen->err_tag_type->data.integral.bit_count));
+            err->value = error_value_count;
+            irb->codegen->errors_by_index.append(err);
+        }
+        err_set_type->data.error_set.errors[i] = err;
+
+        ErrorTableEntry *prev_err = errors[err->value];
+        if (prev_err != nullptr) {
+            ErrorMsg *msg = add_node_error(irb->codegen, ast_field_to_symbol_node(err->decl_node),
+                    buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name)));
+            add_error_note(irb->codegen, msg, ast_field_to_symbol_node(prev_err->decl_node),
+                    buf_sprintf("other error here"));
+            return irb->codegen->invalid_inst_src;
+        }
+        errors[err->value] = err;
+    }
+    heap::c_allocator.deallocate(errors, errors_count);
+    return ir_build_const_type(irb, parent_scope, node, err_set_type);
+}
+
+static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
+    assert(node->type == NodeTypeFnProto);
+
+    size_t param_count = node->data.fn_proto.params.length;
+    IrInstSrc **param_types = heap::c_allocator.allocate<IrInstSrc*>(param_count);
+
+    bool is_var_args = false;
+    for (size_t i = 0; i < param_count; i += 1) {
+        AstNode *param_node = node->data.fn_proto.params.at(i);
+        if (param_node->data.param_decl.is_var_args) {
+            is_var_args = true;
+            break;
+        }
+        if (param_node->data.param_decl.anytype_token == 0) {
+            AstNode *type_node = param_node->data.param_decl.type;
+            IrInstSrc *type_value = ir_gen_node(irb, type_node, parent_scope);
+            if (type_value == irb->codegen->invalid_inst_src)
+                return irb->codegen->invalid_inst_src;
+            param_types[i] = type_value;
+        } else {
+            param_types[i] = nullptr;
+        }
+    }
+
+    IrInstSrc *align_value = nullptr;
+    if (node->data.fn_proto.align_expr != nullptr) {
+        align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope);
+        if (align_value == irb->codegen->invalid_inst_src)
+            return irb->codegen->invalid_inst_src;
+    }
+
+    IrInstSrc *callconv_value = nullptr;
+    if (node->data.fn_proto.callconv_expr != nullptr) {
+        callconv_value = ir_gen_node(irb, node->data.fn_proto.callconv_expr, parent_scope);
+        if (callconv_value == irb->codegen->invalid_inst_src)
+            return irb->codegen->invalid_inst_src;
+    }
+
+    IrInstSrc *return_type;
+    if (node->data.fn_proto.return_type == nullptr) {
+        return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void);
+    } else {
+        return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope);
+        if (return_type == irb->codegen->invalid_inst_src)
+            return irb->codegen->invalid_inst_src;
+    }
+
+    return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, callconv_value, return_type, is_var_args);
+}
+
+static IrInstSrc *ir_gen_resume(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeResume);
+
+    IrInstSrc *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr);
+    if (target_inst == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    return ir_build_resume_src(irb, scope, node, target_inst);
+}
+
+static IrInstSrc *ir_gen_await_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
+        ResultLoc *result_loc)
+{
+    assert(node->type == NodeTypeAwaitExpr);
+
+    bool is_nosuspend = get_scope_nosuspend(scope) != nullptr;
+
+    AstNode *expr_node = node->data.await_expr.expr;
+    if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.modifier == CallModifierBuiltin) {
+        AstNode *fn_ref_expr = expr_node->data.fn_call_expr.fn_ref_expr;
+        Buf *name = node_identifier_buf(fn_ref_expr);
+        auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
+        if (entry != nullptr) {
+            BuiltinFnEntry *builtin_fn = entry->value;
+            if (builtin_fn->id == BuiltinFnIdAsyncCall) {
+                return ir_gen_async_call(irb, scope, node, expr_node, lval, result_loc);
+            }
+        }
+    }
+
+    ZigFn *fn_entry = exec_fn_entry(irb->exec);
+    if (!fn_entry) {
+        add_node_error(irb->codegen, node, buf_sprintf("await outside function definition"));
+        return irb->codegen->invalid_inst_src;
+    }
+    ScopeSuspend *existing_suspend_scope = get_scope_suspend(scope);
+    if (existing_suspend_scope) {
+        if (!existing_suspend_scope->reported_err) {
+            ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot await inside suspend block"));
+            add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("suspend block here"));
+            existing_suspend_scope->reported_err = true;
+        }
+        return irb->codegen->invalid_inst_src;
+    }
+
+    IrInstSrc *target_inst = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
+    if (target_inst == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+
+    IrInstSrc *await_inst = ir_build_await_src(irb, scope, node, target_inst, result_loc, is_nosuspend);
+    return ir_lval_wrap(irb, scope, await_inst, lval, result_loc);
+}
+
+static IrInstSrc *ir_gen_suspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
+    assert(node->type == NodeTypeSuspend);
+
+    ZigFn *fn_entry = exec_fn_entry(irb->exec);
+    if (!fn_entry) {
+        add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition"));
+        return irb->codegen->invalid_inst_src;
+    }
+    if (get_scope_nosuspend(parent_scope) != nullptr) {
+        add_node_error(irb->codegen, node, buf_sprintf("suspend in nosuspend scope"));
+        return irb->codegen->invalid_inst_src;
+    }
+
+    ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope);
+    if (existing_suspend_scope) {
+        if (!existing_suspend_scope->reported_err) {
+            ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block"));
+            add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here"));
+            existing_suspend_scope->reported_err = true;
+        }
+        return irb->codegen->invalid_inst_src;
+    }
+
+    IrInstSrcSuspendBegin *begin = ir_build_suspend_begin_src(irb, parent_scope, node);
+    ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope);
+    Scope *child_scope = &suspend_scope->base;
+    IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope);
+    if (susp_res == irb->codegen->invalid_inst_src)
+        return irb->codegen->invalid_inst_src;
+    ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res));
+
+    return ir_mark_gen(ir_build_suspend_finish_src(irb, parent_scope, node, begin));
+}
+
+static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope,
+        LVal lval, ResultLoc *result_loc)
+{
+    assert(scope);
+    switch (node->type) {
+        case NodeTypeStructValueField:
+        case NodeTypeParamDecl:
+        case NodeTypeUsingNamespace:
+        case NodeTypeSwitchProng:
+        case NodeTypeSwitchRange:
+        case NodeTypeStructField:
+        case NodeTypeErrorSetField:
+        case NodeTypeFnDef:
+        case NodeTypeTestDecl:
+            zig_unreachable();
+        case NodeTypeBlock:
+            return ir_gen_block(irb, scope, node, lval, result_loc);
+        case NodeTypeGroupedExpr:
+            return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_loc);
+        case NodeTypeBinOpExpr:
+            return ir_gen_bin_op(irb, scope, node, lval, result_loc);
+        case NodeTypeIntLiteral:
+            return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval, result_loc);
+        case NodeTypeFloatLiteral:
+            return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval, result_loc);
+        case NodeTypeCharLiteral:
+            return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval, result_loc);
+        case NodeTypeIdentifier:
+            return ir_gen_symbol(irb, scope, node, lval, result_loc);
+        case NodeTypeFnCallExpr:
+            return ir_gen_fn_call(irb, scope, node, lval, result_loc);
+        case NodeTypeIfBoolExpr:
+            return ir_gen_if_bool_expr(irb, scope, node, lval, result_loc);
+        case NodeTypePrefixOpExpr:
+            return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc);
+        case NodeTypeContainerInitExpr:
+            return ir_gen_container_init_expr(irb, scope, node, lval, result_loc);
+        case NodeTypeVariableDeclaration:
+            return ir_gen_var_decl(irb, scope, node);
+        case NodeTypeWhileExpr:
+            return ir_gen_while_expr(irb, scope, node, lval, result_loc);
+        case NodeTypeForExpr:
+            return ir_gen_for_expr(irb, scope, node, lval, result_loc);
+        case NodeTypeArrayAccessExpr:
+            return ir_gen_array_access(irb, scope, node, lval, result_loc);
+        case NodeTypeReturnExpr:
+            return ir_gen_return(irb, scope, node, lval, result_loc);
+        case NodeTypeFieldAccessExpr:
+            {
+                IrInstSrc *ptr_instruction = ir_gen_field_access(irb, scope, node);
+                if (ptr_instruction == irb->codegen->invalid_inst_src)
+                    return ptr_instruction;
+                if (lval == LValPtr || lval == LValAssign)
+                    return ptr_instruction;
+
+                IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
+                return ir_expr_wrap(irb, scope, load_ptr, result_loc);
+            }
+        case NodeTypePtrDeref: {
+            AstNode *expr_node = node->data.ptr_deref_expr.target;
+
+            LVal child_lval = lval;
+            if (child_lval == LValAssign)
+                child_lval = LValPtr;
+
+            IrInstSrc *value = ir_gen_node_extra(irb, expr_node, scope, child_lval, nullptr);
+            if (value == irb->codegen->invalid_inst_src)
+                return value;
+
+            // We essentially just converted any lvalue from &(x.*) to (&x).*;
+            // this inhibits checking that x is a pointer later, so we directly
+            // record whether the pointer check is needed
+            IrInstSrc *un_op = ir_build_un_op_lval(irb, scope, node, IrUnOpDereference, value, lval, result_loc);
+            return ir_expr_wrap(irb, scope, un_op, result_loc);
+        }
+        case NodeTypeUnwrapOptional: {
+            AstNode *expr_node = node->data.unwrap_optional.expr;
+
+            IrInstSrc *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
+            if (maybe_ptr == irb->codegen->invalid_inst_src)
+                return irb->codegen->invalid_inst_src;
+
+            IrInstSrc *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, maybe_ptr, true );
+            if (lval == LValPtr || lval == LValAssign)
+                return unwrapped_ptr;
+
+            IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
+            return ir_expr_wrap(irb, scope, load_ptr, result_loc);
+        }
+        case NodeTypeBoolLiteral:
+            return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval, result_loc);
+        case NodeTypeArrayType:
+            return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval, result_loc);
+        case NodeTypePointerType:
+            return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval, result_loc);
+        case NodeTypeAnyFrameType:
+            return ir_lval_wrap(irb, scope, ir_gen_anyframe_type(irb, scope, node), lval, result_loc);
+        case NodeTypeStringLiteral:
+            return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval, result_loc);
+        case NodeTypeUndefinedLiteral:
+            return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval, result_loc);
+        case NodeTypeAsmExpr:
+            return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval, result_loc);
+        case NodeTypeNullLiteral:
+            return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval, result_loc);
+        case NodeTypeIfErrorExpr:
+            return ir_gen_if_err_expr(irb, scope, node, lval, result_loc);
+        case NodeTypeIfOptional:
+            return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc);
+        case NodeTypeSwitchExpr:
+            return ir_gen_switch_expr(irb, scope, node, lval, result_loc);
+        case NodeTypeCompTime:
+            return ir_expr_wrap(irb, scope, ir_gen_comptime(irb, scope, node, lval), result_loc);
+        case NodeTypeNoSuspend:
+            return ir_expr_wrap(irb, scope, ir_gen_nosuspend(irb, scope, node, lval), result_loc);
+        case NodeTypeErrorType:
+            return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval, result_loc);
+        case NodeTypeBreak:
+            return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval, result_loc);
+        case NodeTypeContinue:
+            return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval, result_loc);
+        case NodeTypeUnreachable:
+            return ir_build_unreachable(irb, scope, node);
+        case NodeTypeDefer:
+            return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval, result_loc);
+        case NodeTypeSliceExpr:
+            return ir_gen_slice(irb, scope, node, lval, result_loc);
+        case NodeTypeCatchExpr:
+            return ir_gen_catch(irb, scope, node, lval, result_loc);
+        case NodeTypeContainerDecl:
+            return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval, result_loc);
+        case NodeTypeFnProto:
+            return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval, result_loc);
+        case NodeTypeErrorSetDecl:
+            return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval, result_loc);
+        case NodeTypeResume:
+            return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval, result_loc);
+        case NodeTypeAwaitExpr:
+            return ir_gen_await_expr(irb, scope, node, lval, result_loc);
+        case NodeTypeSuspend:
+            return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval, result_loc);
+        case NodeTypeEnumLiteral:
+            return ir_lval_wrap(irb, scope, ir_gen_enum_literal(irb, scope, node), lval, result_loc);
+        case NodeTypeInferredArrayType:
+            add_node_error(irb->codegen, node,
+                buf_sprintf("inferred array size invalid here"));
+            return irb->codegen->invalid_inst_src;
+        case NodeTypeAnyTypeField:
+            return ir_lval_wrap(irb, scope,
+                    ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_anytype), lval, result_loc);
+    }
+    zig_unreachable();
+}
+
+ResultLoc *no_result_loc(void) {
+    ResultLocNone *result_loc_none = heap::c_allocator.create<ResultLocNone>();
+    result_loc_none->base.id = ResultLocIdNone;
+    return &result_loc_none->base;
+}
+
+static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval,
+        ResultLoc *result_loc)
+{
+    if (lval == LValAssign) {
+        switch (node->type) {
+            case NodeTypeStructValueField:
+            case NodeTypeParamDecl:
+            case NodeTypeUsingNamespace:
+            case NodeTypeSwitchProng:
+            case NodeTypeSwitchRange:
+            case NodeTypeStructField:
+            case NodeTypeErrorSetField:
+            case NodeTypeFnDef:
+            case NodeTypeTestDecl:
+                zig_unreachable();
+
+            // cannot be assigned to
+            case NodeTypeBlock:
+            case NodeTypeGroupedExpr:
+            case NodeTypeBinOpExpr:
+            case NodeTypeIntLiteral:
+            case NodeTypeFloatLiteral:
+            case NodeTypeCharLiteral:
+            case NodeTypeIfBoolExpr:
+            case NodeTypeContainerInitExpr:
+            case NodeTypeVariableDeclaration:
+            case NodeTypeWhileExpr:
+            case NodeTypeForExpr:
+            case NodeTypeReturnExpr:
+            case NodeTypeBoolLiteral:
+            case NodeTypeArrayType:
+            case NodeTypePointerType:
+            case NodeTypeAnyFrameType:
+            case NodeTypeStringLiteral:
+            case NodeTypeUndefinedLiteral:
+            case NodeTypeAsmExpr:
+            case NodeTypeNullLiteral:
+            case NodeTypeIfErrorExpr:
+            case NodeTypeIfOptional:
+            case NodeTypeSwitchExpr:
+            case NodeTypeCompTime:
+            case NodeTypeNoSuspend:
+            case NodeTypeErrorType:
+            case NodeTypeBreak:
+            case NodeTypeContinue:
+            case NodeTypeUnreachable:
+            case NodeTypeDefer:
+            case NodeTypeSliceExpr:
+            case NodeTypeCatchExpr:
+            case NodeTypeContainerDecl:
+            case NodeTypeFnProto:
+            case NodeTypeErrorSetDecl:
+            case NodeTypeResume:
+            case NodeTypeAwaitExpr:
+            case NodeTypeSuspend:
+            case NodeTypeEnumLiteral:
+            case NodeTypeInferredArrayType:
+            case NodeTypeAnyTypeField:
+            case NodeTypePrefixOpExpr:
+                add_node_error(irb->codegen, node,
+                    buf_sprintf("invalid left-hand side to assignment"));
+                return irb->codegen->invalid_inst_src;
+
+            // @field can be assigned to
+            case NodeTypeFnCallExpr:
+                if (node->data.fn_call_expr.modifier == CallModifierBuiltin) {
+                    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
+                    Buf *name = node_identifier_buf(fn_ref_expr);
+                    auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
+
+                    if (!entry) {
+                        add_node_error(irb->codegen, node,
+                                buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
+                        return irb->codegen->invalid_inst_src;
+                    }
+
+                    if (entry->value->id == BuiltinFnIdField) {
+                        break;
+                    }
+                }
+                add_node_error(irb->codegen, node,
+                    buf_sprintf("invalid left-hand side to assignment"));
+                return irb->codegen->invalid_inst_src;
+
+
+            // can be assigned to
+            case NodeTypeUnwrapOptional:
+            case NodeTypePtrDeref:
+            case NodeTypeFieldAccessExpr:
+            case NodeTypeArrayAccessExpr:
+            case NodeTypeIdentifier:
+                break;
+        }
+    }
+    if (result_loc == nullptr) {
+        // Create a result location indicating there is none - but if one gets created
+        // it will be properly distributed.
+        result_loc = no_result_loc();
+        ir_build_reset_result(irb, scope, node, result_loc);
+    }
+    Scope *child_scope;
+    if (irb->exec->is_inline ||
+        (irb->exec->fn_entry != nullptr && irb->exec->fn_entry->child_scope == scope))
+    {
+        child_scope = scope;
+    } else {
+        child_scope = &create_expr_scope(irb->codegen, node, scope)->base;
+    }
+    IrInstSrc *result = ir_gen_node_raw(irb, node, child_scope, lval, result_loc);
+    if (result == irb->codegen->invalid_inst_src) {
+        if (irb->exec->first_err_trace_msg == nullptr) {
+            irb->exec->first_err_trace_msg = irb->codegen->trace_err;
+        }
+    }
+    return result;
+}
+
+static IrInstSrc *ir_gen_node(IrBuilderSrc *irb, AstNode *node, Scope *scope) {
+    return ir_gen_node_extra(irb, node, scope, LValNone, nullptr);
+}
+
+bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutableSrc *ir_executable) {
+    assert(node->owner);
+
+    IrBuilderSrc ir_builder = {0};
+    IrBuilderSrc *irb = &ir_builder;
+
+    irb->codegen = codegen;
+    irb->exec = ir_executable;
+    irb->main_block_node = node;
+
+    IrBasicBlockSrc *entry_block = ir_create_basic_block(irb, scope, "Entry");
+    ir_set_cursor_at_end_and_append_block(irb, entry_block);
+    // Entry block gets a reference because we enter it to begin.
+    ir_ref_bb(irb->current_basic_block);
+
+    IrInstSrc *result = ir_gen_node_extra(irb, node, scope, LValNone, nullptr);
+
+    if (result == irb->codegen->invalid_inst_src)
+        return false;
+
+    if (irb->exec->first_err_trace_msg != nullptr) {
+        codegen->trace_err = irb->exec->first_err_trace_msg;
+        return false;
+    }
+
+    if (!instr_is_unreachable(result)) {
+        ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, result->base.source_node, result, nullptr));
+        // no need for save_err_ret_addr because this cannot return error
+        ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
+        result_loc_ret->base.id = ResultLocIdReturn;
+        ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
+        ir_mark_gen(ir_build_end_expr(irb, scope, node, result, &result_loc_ret->base));
+        ir_mark_gen(ir_build_return_src(irb, scope, result->base.source_node, result));
+    }
+
+    return true;
+}
+
+bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) {
+    assert(fn_entry);
+
+    IrExecutableSrc *ir_executable = fn_entry->ir_executable;
+    AstNode *body_node = fn_entry->body_node;
+
+    assert(fn_entry->child_scope);
+
+    return ir_gen(codegen, body_node, fn_entry->child_scope, ir_executable);
+}
+
+void invalidate_exec(IrExecutableSrc *exec, ErrorMsg *msg) {
+    if (exec->first_err_trace_msg != nullptr)
+        return;
+
+    exec->first_err_trace_msg = msg;
+
+    for (size_t i = 0; i < exec->tld_list.length; i += 1) {
+        exec->tld_list.items[i]->resolution = TldResolutionInvalid;
+    }
+}
+
+AstNode *ast_field_to_symbol_node(AstNode *err_set_field_node) {
+    if (err_set_field_node->type == NodeTypeIdentifier) {
+        return err_set_field_node;
+    } else if (err_set_field_node->type == NodeTypeErrorSetField) {
+        assert(err_set_field_node->data.err_set_field.field_name->type == NodeTypeIdentifier);
+        return err_set_field_node->data.err_set_field.field_name;
+    } else {
+        return err_set_field_node;
+    }
+}
+
+void ir_add_call_stack_errors_gen(CodeGen *codegen, IrExecutableGen *exec, ErrorMsg *err_msg, int limit) {
+    if (!exec || !exec->source_node || limit < 0) return;
+    add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here"));
+
+    ir_add_call_stack_errors_gen(codegen, exec->parent_exec, err_msg, limit - 1);
+}
+
src/stage1/astgen.hpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#ifndef ZIG_ASTGEN_HPP
+#define ZIG_ASTGEN_HPP
+
+#include "all_types.hpp"
+
+bool ir_gen(CodeGen *g, AstNode *node, Scope *scope, IrExecutableSrc *ir_executable);
+bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry);
+
+bool ir_inst_src_has_side_effects(IrInstSrc *inst);
+
+ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope,
+        Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime,
+        bool skip_name_check);
+
+ResultLoc *no_result_loc(void);
+
+void invalidate_exec(IrExecutableSrc *exec, ErrorMsg *msg);
+
+AstNode *ast_field_to_symbol_node(AstNode *err_set_field_node);
+void ir_add_call_stack_errors_gen(CodeGen *codegen, IrExecutableGen *exec, ErrorMsg *err_msg,
+        int limit);
+
+void destroy_instruction_src(IrInstSrc *inst);
+
+struct IrBuilderSrc {
+    CodeGen *codegen;
+    IrExecutableSrc *exec;
+    IrBasicBlockSrc *current_basic_block;
+    AstNode *main_block_node;
+};
+
+bool ir_should_inline(IrExecutableSrc *exec, Scope *scope);
+Buf *get_anon_type_name(CodeGen *codegen, IrExecutableSrc *exec, const char *kind_name,
+        Scope *scope, AstNode *source_node, Buf *out_bare_name);
+
+#endif
src/stage1/bigfloat.cpp
@@ -69,7 +69,7 @@ void bigfloat_init_bigint(BigFloat *dest, const BigInt *op) {
     }
 }
 
-Error bigfloat_init_buf(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len) {
+Error bigfloat_init_buf(BigFloat *dest, const uint8_t *buf_ptr) {
     char *str_begin = (char *)buf_ptr;
     char *str_end;
 
@@ -79,7 +79,6 @@ Error bigfloat_init_buf(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len)
         return ErrorOverflow;
     }
 
-    assert(str_end <= ((char*)buf_ptr) + buf_len);
     return ErrorNone;
 }
 
src/stage1/bigfloat.hpp
@@ -28,7 +28,7 @@ void bigfloat_init_64(BigFloat *dest, double x);
 void bigfloat_init_128(BigFloat *dest, float128_t x);
 void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x);
 void bigfloat_init_bigint(BigFloat *dest, const BigInt *op);
-Error bigfloat_init_buf(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len);
+Error bigfloat_init_buf(BigFloat *dest, const uint8_t *buf_ptr);
 
 float16_t bigfloat_to_f16(const BigFloat *bigfloat);
 float bigfloat_to_f32(const BigFloat *bigfloat);
src/stage1/codegen.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "analyze.hpp"
-#include "ast_render.hpp"
 #include "codegen.hpp"
 #include "errmsg.hpp"
 #include "error.hpp"
@@ -642,6 +641,18 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn) {
     return fn->llvm_value;
 }
 
+static uint32_t node_line_onebased(AstNode *node) {
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    assert(node->main_token < root_struct->token_count);
+    return root_struct->token_locs[node->main_token].line + 1;
+}
+
+static uint32_t node_column_onebased(AstNode *node) {
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    assert(node->main_token < root_struct->token_count);
+    return root_struct->token_locs[node->main_token].column + 1;
+}
+
 static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
     if (scope->di_scope)
         return scope->di_scope;
@@ -657,8 +668,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
             ZigFn *fn_table_entry = fn_scope->fn_entry;
             if (!fn_table_entry->proto_node)
                 return get_di_scope(g, scope->parent);
-            unsigned line_number = (unsigned)(fn_table_entry->proto_node->line == 0) ?
-                0 : (fn_table_entry->proto_node->line + 1);
+            unsigned line_number = node_line_onebased(fn_table_entry->proto_node);
             unsigned scope_line = line_number;
             bool is_definition = fn_table_entry->body_node != nullptr;
             bool is_optimized = g->build_mode != BuildModeDebug;
@@ -696,8 +706,8 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
             ZigLLVMDILexicalBlock *di_block = ZigLLVMCreateLexicalBlock(g->dbuilder,
                 get_di_scope(g, scope->parent),
                 import->data.structure.root_struct->di_file,
-                (unsigned)scope->source_node->line + 1,
-                (unsigned)scope->source_node->column + 1);
+                node_line_onebased(scope->source_node),
+                node_column_onebased(scope->source_node));
             scope->di_scope = ZigLLVMLexicalBlockToScope(di_block);
             return scope->di_scope;
         }
@@ -1798,8 +1808,8 @@ static void gen_var_debug_decl(CodeGen *g, ZigVar *var) {
     if (g->strip_debug_symbols) return;
     assert(var->di_loc_var != nullptr);
     AstNode *source_node = var->decl_node;
-    ZigLLVMDILocation *debug_loc = ZigLLVMGetDebugLoc((unsigned)source_node->line + 1,
-            (unsigned)source_node->column + 1, get_di_scope(g, var->parent_scope));
+    ZigLLVMDILocation *debug_loc = ZigLLVMGetDebugLoc(node_line_onebased(source_node),
+            node_column_onebased(source_node), get_di_scope(g, var->parent_scope));
     ZigLLVMInsertDeclareAtEnd(g->dbuilder, var->value_ref, var->di_loc_var, debug_loc,
             LLVMGetInsertBlock(g->builder));
 }
@@ -2198,7 +2208,7 @@ var_ok:
         // arg index + 1 because the 0 index is return value
         var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
                 var->name, fn_walk->data.vars.import->data.structure.root_struct->di_file,
-                (unsigned)(var->decl_node->line + 1),
+                node_line_onebased(var->decl_node),
                 get_llvm_di_type(g, dest_ty), !g->strip_debug_symbols, 0, di_arg_index + 1);
     }
     return true;
@@ -4126,7 +4136,7 @@ static void render_async_spills(CodeGen *g) {
         if (var->decl_node) {
             var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
                 var->name, import->data.structure.root_struct->di_file,
-                (unsigned)(var->decl_node->line + 1),
+                node_line_onebased(var->decl_node),
                 get_llvm_di_type(g, var->var_type), !g->strip_debug_symbols, 0);
             gen_var_debug_decl(g, var);
         }
@@ -6797,8 +6807,8 @@ static void set_debug_location(CodeGen *g, IrInstGen *instruction) {
     assert(source_node);
     assert(scope);
 
-    ZigLLVMSetCurrentDebugLocation(g->builder, (int)source_node->line + 1,
-            (int)source_node->column + 1, get_di_scope(g, scope));
+    ZigLLVMSetCurrentDebugLocation(g->builder, node_line_onebased(source_node),
+            node_column_onebased(source_node), get_di_scope(g, scope));
 }
 
 static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executable, IrInstGen *instruction) {
@@ -8021,7 +8031,7 @@ static void gen_global_var(CodeGen *g, ZigVar *var, LLVMValueRef init_val,
     bool is_local_to_unit = true;
     ZigLLVMCreateGlobalVariable(g->dbuilder, get_di_scope(g, var->parent_scope), var->name,
         var->name, import->data.structure.root_struct->di_file,
-        (unsigned)(var->decl_node->line + 1),
+        node_line_onebased(var->decl_node),
         get_llvm_di_type(g, type_entry), is_local_to_unit);
 
     // TODO ^^ make an actual global variable
@@ -8296,7 +8306,8 @@ static void do_code_gen(CodeGen *g) {
 
             if (var->src_arg_index == SIZE_MAX) {
                 var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
-                        var->name, import->data.structure.root_struct->di_file, (unsigned)(var->decl_node->line + 1),
+                        var->name, import->data.structure.root_struct->di_file,
+                        node_line_onebased(var->decl_node),
                         get_llvm_di_type(g, var->var_type), !g->strip_debug_symbols, 0);
 
             } else if (is_c_abi) {
@@ -8321,7 +8332,7 @@ static void do_code_gen(CodeGen *g) {
                 if (var->decl_node) {
                     var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
                         var->name, import->data.structure.root_struct->di_file,
-                        (unsigned)(var->decl_node->line + 1),
+                        node_line_onebased(var->decl_node),
                         get_llvm_di_type(g, gen_type), !g->strip_debug_symbols, 0, (unsigned)(gen_info->gen_index+1));
                 }
 
@@ -8365,8 +8376,9 @@ static void do_code_gen(CodeGen *g) {
 
             if (!g->strip_debug_symbols) {
                 AstNode *source_node = fn_table_entry->proto_node;
-                ZigLLVMSetCurrentDebugLocation(g->builder, (int)source_node->line + 1,
-                        (int)source_node->column + 1, get_di_scope(g, fn_table_entry->child_scope));
+                ZigLLVMSetCurrentDebugLocation(g->builder,
+                        node_line_onebased(source_node), node_column_onebased(source_node),
+                        get_di_scope(g, fn_table_entry->child_scope));
             }
             IrExecutableGen *executable = &fn_table_entry->analyzed_executable;
             LLVMBasicBlockRef bad_resume_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadResume");
src/stage1/dump_analysis.cpp
@@ -1084,19 +1084,43 @@ static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) {
     jw_end_object(jw);
 }
 
+static Buf *collect_doc_comments(RootStruct *root_struct, TokenIndex first_token) {
+    if (first_token == 0)
+        return nullptr;
+
+    TokenId *token_ids = root_struct->token_ids;
+    TokenLoc *token_locs = root_struct->token_locs;
+    Buf *str = buf_alloc();
+    const char *source = buf_ptr(root_struct->source_code);
+    TokenIndex doc_token = first_token;
+    for (;token_ids[doc_token] == TokenIdDocComment; doc_token += 1) {
+        // chops off '///' but leaves '\n'
+        uint32_t start_pos = token_locs[doc_token].offset;
+        uint32_t token_len = 0;
+        while (source[start_pos + token_len] != '\n' &&
+               source[start_pos + token_len] != 0)
+        {
+            token_len += 1;
+        }
+        buf_append_mem(str, source + start_pos + 3, token_len - 3);
+    }
+    return str;
+}
+
 static void anal_dump_node(AnalDumpCtx *ctx, const AstNode *node) {
     JsonWriter *jw = &ctx->jw;
 
     jw_begin_object(jw);
 
     jw_object_field(jw, "file");
-    anal_dump_file_ref(ctx, node->owner->data.structure.root_struct->path);
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    anal_dump_file_ref(ctx, root_struct->path);
 
     jw_object_field(jw, "line");
-    jw_int(jw, node->line);
+    jw_int(jw, root_struct->token_locs[node->main_token].line);
 
     jw_object_field(jw, "col");
-    jw_int(jw, node->column);
+    jw_int(jw, root_struct->token_locs[node->main_token].column);
 
     const Buf *doc_comments_buf = nullptr;
     const Buf *name_buf = nullptr;
@@ -1107,30 +1131,30 @@ static void anal_dump_node(AnalDumpCtx *ctx, const AstNode *node) {
 
     switch (node->type) {
         case NodeTypeParamDecl:
-            doc_comments_buf = &node->data.param_decl.doc_comments;
+            doc_comments_buf = collect_doc_comments(root_struct, node->data.param_decl.doc_comments);
             name_buf = node->data.param_decl.name;
             is_var_args = node->data.param_decl.is_var_args;
             is_noalias = node->data.param_decl.is_noalias;
             is_comptime = node->data.param_decl.is_comptime;
             break;
         case NodeTypeFnProto:
-            doc_comments_buf = &node->data.fn_proto.doc_comments;
+            doc_comments_buf = collect_doc_comments(root_struct, node->data.fn_proto.doc_comments);
             field_nodes = &node->data.fn_proto.params;
             is_var_args = node->data.fn_proto.is_var_args;
             break;
         case NodeTypeVariableDeclaration:
-            doc_comments_buf = &node->data.variable_declaration.doc_comments;
+            doc_comments_buf = collect_doc_comments(root_struct, node->data.variable_declaration.doc_comments);
             break;
         case NodeTypeErrorSetField:
-            doc_comments_buf = &node->data.err_set_field.doc_comments;
+            doc_comments_buf = collect_doc_comments(root_struct, node->data.err_set_field.doc_comments);
             break;
         case NodeTypeStructField:
-            doc_comments_buf = &node->data.struct_field.doc_comments;
+            doc_comments_buf = collect_doc_comments(root_struct, node->data.struct_field.doc_comments);
             name_buf = node->data.struct_field.name;
             break;
         case NodeTypeContainerDecl:
             field_nodes = &node->data.container_decl.fields;
-            doc_comments_buf = &node->data.container_decl.doc_comments;
+            doc_comments_buf = collect_doc_comments(root_struct, node->data.container_decl.doc_comments);
             break;
         default:
             break;
src/stage1/errmsg.cpp
@@ -96,14 +96,14 @@ void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note) {
     parent->notes.append(note);
 }
 
-ErrorMsg *err_msg_create_with_offset(Buf *path, size_t line, size_t column, size_t offset,
-        const char *source, Buf *msg)
+ErrorMsg *err_msg_create_with_offset(Buf *path, uint32_t byte_offset, const char *source,
+    Buf *msg)
 {
     ErrorMsg *err_msg = heap::c_allocator.create<ErrorMsg>();
     err_msg->path = path;
-    err_msg->line_start = line;
-    err_msg->column_start = column;
     err_msg->msg = msg;
+    err_msg->line_start = 0;
+    err_msg->column_start = 0;
 
     if (source == nullptr) {
         // Must initialize the buffer anyway
@@ -111,46 +111,25 @@ ErrorMsg *err_msg_create_with_offset(Buf *path, size_t line, size_t column, size
         return err_msg;
     }
 
-    size_t line_start_offset = offset;
-    for (;;) {
-        if (line_start_offset == 0) {
-            break;
-        }
-
-        line_start_offset -= 1;
-
-        if (source[line_start_offset] == '\n') {
-            line_start_offset += 1;
-            break;
+    size_t line_start = 0;
+    size_t i = 0;
+    for (;i < byte_offset; i += 1) {
+        switch (source[i]) {
+            case '\n':
+                err_msg->line_start += 1;
+                err_msg->column_start = 0;
+                line_start = i + 1;
+                continue;
+            default:
+                err_msg->column_start += 1;
+                continue;
         }
     }
-
-    size_t line_end_offset = offset;
-    while (source[line_end_offset] && source[line_end_offset] != '\n') {
-        line_end_offset += 1;
+    while (source[i] != '\n' && source[i] != 0) {
+        i += 1;
     }
 
-    buf_init_from_mem(&err_msg->line_buf, source + line_start_offset, line_end_offset - line_start_offset);
-
-    return err_msg;
-}
-
-ErrorMsg *err_msg_create_with_line(Buf *path, size_t line, size_t column,
-        Buf *source, ZigList<size_t> *line_offsets, Buf *msg)
-{
-    ErrorMsg *err_msg = heap::c_allocator.create<ErrorMsg>();
-    err_msg->path = path;
-    err_msg->line_start = line;
-    err_msg->column_start = column;
-    err_msg->msg = msg;
-
-    size_t line_start_offset = line_offsets->at(line);
-    size_t end_line = line + 1;
-    size_t line_end_offset = (end_line >= line_offsets->length) ? buf_len(source) : line_offsets->at(line + 1);
-    size_t len = (line_end_offset + 1 > line_start_offset) ? (line_end_offset - line_start_offset - 1) : 0;
-    if (len == SIZE_MAX) len = 0;
-
-    buf_init_from_mem(&err_msg->line_buf, buf_ptr(source) + line_start_offset, len);
+    buf_init_from_mem(&err_msg->line_buf, source + line_start, i - line_start);
 
     return err_msg;
 }
src/stage1/errmsg.hpp
@@ -25,10 +25,6 @@ struct ErrorMsg {
 void print_err_msg(ErrorMsg *msg, ErrColor color);
 
 void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note);
-ErrorMsg *err_msg_create_with_offset(Buf *path, size_t line, size_t column, size_t offset,
-        const char *source, Buf *msg);
-
-ErrorMsg *err_msg_create_with_line(Buf *path, size_t line, size_t column,
-        Buf *source, ZigList<size_t> *line_offsets, Buf *msg);
+ErrorMsg *err_msg_create_with_offset(Buf *path, uint32_t byte_offset, const char *source, Buf *msg);
 
 #endif
src/stage1/error.cpp
@@ -88,6 +88,8 @@ const char *err_str(Error err) {
         case ErrorZigIsTheCCompiler: return "Zig was not provided with libc installation information, and so it does not know where the libc paths are on the system. Zig attempted to use the system C compiler to find out where the libc paths are, but discovered that Zig is being used as the system C compiler.";
         case ErrorFileBusy: return "file is busy";
         case ErrorLocked: return "file is locked by another process";
+        case ErrorInvalidCharacter: return "invalid character";
+        case ErrorUnicodePointTooLarge: return "unicode codepoint too large";
     }
     return "(invalid error)";
 }
src/stage1/ir.cpp
@@ -5,8 +5,8 @@
  * See http://opensource.org/licenses/MIT
  */
 
+#include "astgen.hpp"
 #include "analyze.hpp"
-#include "ast_render.hpp"
 #include "error.hpp"
 #include "ir.hpp"
 #include "ir_print.hpp"
@@ -22,13 +22,6 @@
 #include <errno.h>
 #include <math.h>
 
-struct IrBuilderSrc {
-    CodeGen *codegen;
-    IrExecutableSrc *exec;
-    IrBasicBlockSrc *current_basic_block;
-    AstNode *main_block_node;
-};
-
 struct IrBuilderGen {
     CodeGen *codegen;
     IrExecutableGen *exec;
@@ -216,27 +209,18 @@ struct DbgIrBreakPoint {
     const char *src_file;
     uint32_t line;
 };
-DbgIrBreakPoint dbg_ir_breakpoints_buf[20];
-size_t dbg_ir_breakpoints_count = 0;
 
-static IrInstSrc *ir_gen_node(IrBuilderSrc *irb, AstNode *node, Scope *scope);
-static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval,
-        ResultLoc *result_loc);
 static IrInstGen *ir_implicit_cast(IrAnalyze *ira, IrInstGen *value, ZigType *expected_type);
 static IrInstGen *ir_implicit_cast2(IrAnalyze *ira, IrInst *value_source_instr,
         IrInstGen *value, ZigType *expected_type);
 static IrInstGen *ir_get_deref(IrAnalyze *ira, IrInst *source_instr, IrInstGen *ptr,
         ResultLoc *result_loc);
-static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutableSrc *exec, AstNode *source_node, Buf *msg);
 static IrInstGen *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
     IrInst* source_instr, IrInstGen *container_ptr, IrInst *container_ptr_src,
     ZigType *container_type, bool initializing);
-static void ir_assert_impl(bool ok, IrInst* source_instruction, const char *file, unsigned int line);
 static void ir_assert_gen_impl(bool ok, IrInstGen *source_instruction, const char *file, unsigned int line);
 static IrInstGen *ir_get_var_ptr(IrAnalyze *ira, IrInst *source_instr, ZigVar *var);
 static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstGen *op);
-static IrInstSrc *ir_lval_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *value, LVal lval, ResultLoc *result_loc);
-static IrInstSrc *ir_expr_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *inst, ResultLoc *result_loc);
 static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align);
 static ZigType *adjust_ptr_const(CodeGen *g, ZigType *ptr_type, bool is_const);
 static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align);
@@ -265,26 +249,14 @@ static IrInstGen *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInst* source_inst
         IrInstGen *base_ptr, bool initializing);
 static IrInstGen *ir_analyze_store_ptr(IrAnalyze *ira, IrInst* source_instr,
         IrInstGen *ptr, IrInstGen *uncasted_value, bool allow_write_through_const);
-static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *union_type, IrInstSrc *field_name, AstNode *expr_node,
-    LVal lval, ResultLoc *parent_result_loc);
 static void ir_reset_result(ResultLoc *result_loc);
-static Buf *get_anon_type_name(CodeGen *codegen, IrExecutableSrc *exec, const char *kind_name,
-        Scope *scope, AstNode *source_node, Buf *out_bare_name);
-static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type,
-        ResultLoc *parent_result_loc);
 static IrInstGen *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInst* source_instr,
         TypeStructField *field, IrInstGen *struct_ptr, ZigType *struct_type, bool initializing);
 static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name,
     IrInst* source_instr, IrInstGen *container_ptr, ZigType *container_type);
-static ResultLoc *no_result_loc(void);
 static IrInstGen *ir_analyze_test_non_null(IrAnalyze *ira, IrInst *source_inst, IrInstGen *value);
 static IrInstGen *ir_error_dependency_loop(IrAnalyze *ira, IrInst *source_instr);
 static IrInstGen *ir_const_undef(IrAnalyze *ira, IrInst *source_instruction, ZigType *ty);
-static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name,
-        bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime);
-static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var,
-        IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime);
 static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instruction,
     AstNode *field_source_node, ZigType *union_type, Buf *field_name, IrInstGen *field_result_loc,
     IrInstGen *result_loc);
@@ -295,292 +267,15 @@ static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *r
 static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field);
 static void value_to_bigfloat(BigFloat *out, ZigValue *val);
 
+static void ir_assert_impl(bool ok, IrInst *source_instruction, char const *file, unsigned int line) {
+    if (ok) return;
+    src_assert_impl(ok, source_instruction->source_node, file, line);
+}
+
+
 #define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
 #define ir_assert_gen(OK, SOURCE_INSTRUCTION) ir_assert_gen_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
 
-static void destroy_instruction_src(IrInstSrc *inst) {
-    switch (inst->id) {
-        case IrInstSrcIdInvalid:
-            zig_unreachable();
-        case IrInstSrcIdReturn:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReturn *>(inst));
-        case IrInstSrcIdConst:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcConst *>(inst));
-        case IrInstSrcIdBinOp:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBinOp *>(inst));
-        case IrInstSrcIdMergeErrSets:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMergeErrSets *>(inst));
-        case IrInstSrcIdDeclVar:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclVar *>(inst));
-        case IrInstSrcIdCall:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCall *>(inst));
-        case IrInstSrcIdCallExtra:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallExtra *>(inst));
-        case IrInstSrcIdAsyncCallExtra:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAsyncCallExtra *>(inst));
-        case IrInstSrcIdUnOp:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnOp *>(inst));
-        case IrInstSrcIdCondBr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCondBr *>(inst));
-        case IrInstSrcIdBr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBr *>(inst));
-        case IrInstSrcIdPhi:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPhi *>(inst));
-        case IrInstSrcIdContainerInitList:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcContainerInitList *>(inst));
-        case IrInstSrcIdContainerInitFields:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcContainerInitFields *>(inst));
-        case IrInstSrcIdUnreachable:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnreachable *>(inst));
-        case IrInstSrcIdElemPtr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcElemPtr *>(inst));
-        case IrInstSrcIdVarPtr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcVarPtr *>(inst));
-        case IrInstSrcIdLoadPtr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcLoadPtr *>(inst));
-        case IrInstSrcIdStorePtr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcStorePtr *>(inst));
-        case IrInstSrcIdTypeOf:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeOf *>(inst));
-        case IrInstSrcIdFieldPtr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFieldPtr *>(inst));
-        case IrInstSrcIdSetCold:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetCold *>(inst));
-        case IrInstSrcIdSetRuntimeSafety:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetRuntimeSafety *>(inst));
-        case IrInstSrcIdSetFloatMode:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetFloatMode *>(inst));
-        case IrInstSrcIdArrayType:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArrayType *>(inst));
-        case IrInstSrcIdSliceType:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSliceType *>(inst));
-        case IrInstSrcIdAnyFrameType:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAnyFrameType *>(inst));
-        case IrInstSrcIdAsm:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAsm *>(inst));
-        case IrInstSrcIdSizeOf:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSizeOf *>(inst));
-        case IrInstSrcIdTestNonNull:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTestNonNull *>(inst));
-        case IrInstSrcIdOptionalUnwrapPtr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcOptionalUnwrapPtr *>(inst));
-        case IrInstSrcIdPopCount:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPopCount *>(inst));
-        case IrInstSrcIdClz:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcClz *>(inst));
-        case IrInstSrcIdCtz:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCtz *>(inst));
-        case IrInstSrcIdBswap:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBswap *>(inst));
-        case IrInstSrcIdBitReverse:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBitReverse *>(inst));
-        case IrInstSrcIdSwitchBr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchBr *>(inst));
-        case IrInstSrcIdSwitchVar:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchVar *>(inst));
-        case IrInstSrcIdSwitchElseVar:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchElseVar *>(inst));
-        case IrInstSrcIdSwitchTarget:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchTarget *>(inst));
-        case IrInstSrcIdImport:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcImport *>(inst));
-        case IrInstSrcIdRef:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcRef *>(inst));
-        case IrInstSrcIdCompileErr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCompileErr *>(inst));
-        case IrInstSrcIdCompileLog:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCompileLog *>(inst));
-        case IrInstSrcIdErrName:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrName *>(inst));
-        case IrInstSrcIdCImport:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCImport *>(inst));
-        case IrInstSrcIdCInclude:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCInclude *>(inst));
-        case IrInstSrcIdCDefine:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCDefine *>(inst));
-        case IrInstSrcIdCUndef:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCUndef *>(inst));
-        case IrInstSrcIdEmbedFile:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcEmbedFile *>(inst));
-        case IrInstSrcIdCmpxchg:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCmpxchg *>(inst));
-        case IrInstSrcIdFence:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFence *>(inst));
-        case IrInstSrcIdReduce:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReduce *>(inst));
-        case IrInstSrcIdTruncate:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTruncate *>(inst));
-        case IrInstSrcIdIntCast:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntCast *>(inst));
-        case IrInstSrcIdFloatCast:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFloatCast *>(inst));
-        case IrInstSrcIdErrSetCast:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrSetCast *>(inst));
-        case IrInstSrcIdIntToFloat:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToFloat *>(inst));
-        case IrInstSrcIdFloatToInt:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFloatToInt *>(inst));
-        case IrInstSrcIdBoolToInt:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBoolToInt *>(inst));
-        case IrInstSrcIdVectorType:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcVectorType *>(inst));
-        case IrInstSrcIdShuffleVector:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcShuffleVector *>(inst));
-        case IrInstSrcIdSplat:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSplat *>(inst));
-        case IrInstSrcIdBoolNot:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBoolNot *>(inst));
-        case IrInstSrcIdMemset:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMemset *>(inst));
-        case IrInstSrcIdMemcpy:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMemcpy *>(inst));
-        case IrInstSrcIdSlice:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSlice *>(inst));
-        case IrInstSrcIdBreakpoint:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBreakpoint *>(inst));
-        case IrInstSrcIdReturnAddress:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReturnAddress *>(inst));
-        case IrInstSrcIdFrameAddress:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameAddress *>(inst));
-        case IrInstSrcIdFrameHandle:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameHandle *>(inst));
-        case IrInstSrcIdFrameType:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameType *>(inst));
-        case IrInstSrcIdFrameSize:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameSize *>(inst));
-        case IrInstSrcIdAlignOf:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAlignOf *>(inst));
-        case IrInstSrcIdOverflowOp:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcOverflowOp *>(inst));
-        case IrInstSrcIdTestErr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTestErr *>(inst));
-        case IrInstSrcIdUnwrapErrCode:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnwrapErrCode *>(inst));
-        case IrInstSrcIdUnwrapErrPayload:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnwrapErrPayload *>(inst));
-        case IrInstSrcIdFnProto:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFnProto *>(inst));
-        case IrInstSrcIdTestComptime:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTestComptime *>(inst));
-        case IrInstSrcIdPtrCast:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrCast *>(inst));
-        case IrInstSrcIdBitCast:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBitCast *>(inst));
-        case IrInstSrcIdPtrToInt:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrToInt *>(inst));
-        case IrInstSrcIdIntToPtr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToPtr *>(inst));
-        case IrInstSrcIdIntToEnum:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToEnum *>(inst));
-        case IrInstSrcIdIntToErr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToErr *>(inst));
-        case IrInstSrcIdErrToInt:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrToInt *>(inst));
-        case IrInstSrcIdCheckSwitchProngsUnderNo:
-        case IrInstSrcIdCheckSwitchProngsUnderYes:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckSwitchProngs *>(inst));
-        case IrInstSrcIdCheckStatementIsVoid:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckStatementIsVoid *>(inst));
-        case IrInstSrcIdTypeName:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeName *>(inst));
-        case IrInstSrcIdTagName:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagName *>(inst));
-        case IrInstSrcIdPtrType:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrType *>(inst));
-        case IrInstSrcIdPtrTypeSimple:
-        case IrInstSrcIdPtrTypeSimpleConst:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrTypeSimple *>(inst));
-        case IrInstSrcIdDeclRef:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclRef *>(inst));
-        case IrInstSrcIdPanic:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPanic *>(inst));
-        case IrInstSrcIdFieldParentPtr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFieldParentPtr *>(inst));
-        case IrInstSrcIdByteOffsetOf:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcByteOffsetOf *>(inst));
-        case IrInstSrcIdBitOffsetOf:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBitOffsetOf *>(inst));
-        case IrInstSrcIdTypeInfo:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeInfo *>(inst));
-        case IrInstSrcIdType:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcType *>(inst));
-        case IrInstSrcIdHasField:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcHasField *>(inst));
-        case IrInstSrcIdSetEvalBranchQuota:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetEvalBranchQuota *>(inst));
-        case IrInstSrcIdAlignCast:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAlignCast *>(inst));
-        case IrInstSrcIdImplicitCast:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcImplicitCast *>(inst));
-        case IrInstSrcIdResolveResult:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResolveResult *>(inst));
-        case IrInstSrcIdResetResult:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResetResult *>(inst));
-        case IrInstSrcIdSetAlignStack:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetAlignStack *>(inst));
-        case IrInstSrcIdArgTypeAllowVarFalse:
-        case IrInstSrcIdArgTypeAllowVarTrue:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArgType *>(inst));
-        case IrInstSrcIdExport:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst));
-        case IrInstSrcIdExtern:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExtern *>(inst));
-        case IrInstSrcIdErrorReturnTrace:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrorReturnTrace *>(inst));
-        case IrInstSrcIdErrorUnion:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrorUnion *>(inst));
-        case IrInstSrcIdAtomicRmw:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAtomicRmw *>(inst));
-        case IrInstSrcIdSaveErrRetAddr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSaveErrRetAddr *>(inst));
-        case IrInstSrcIdAddImplicitReturnType:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAddImplicitReturnType *>(inst));
-        case IrInstSrcIdFloatOp:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFloatOp *>(inst));
-        case IrInstSrcIdMulAdd:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMulAdd *>(inst));
-        case IrInstSrcIdAtomicLoad:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAtomicLoad *>(inst));
-        case IrInstSrcIdAtomicStore:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAtomicStore *>(inst));
-        case IrInstSrcIdEnumToInt:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcEnumToInt *>(inst));
-        case IrInstSrcIdCheckRuntimeScope:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckRuntimeScope *>(inst));
-        case IrInstSrcIdHasDecl:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcHasDecl *>(inst));
-        case IrInstSrcIdUndeclaredIdent:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUndeclaredIdent *>(inst));
-        case IrInstSrcIdAlloca:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAlloca *>(inst));
-        case IrInstSrcIdEndExpr:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcEndExpr *>(inst));
-        case IrInstSrcIdUnionInitNamedField:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnionInitNamedField *>(inst));
-        case IrInstSrcIdSuspendBegin:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSuspendBegin *>(inst));
-        case IrInstSrcIdSuspendFinish:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSuspendFinish *>(inst));
-        case IrInstSrcIdResume:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResume *>(inst));
-        case IrInstSrcIdAwait:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAwait *>(inst));
-        case IrInstSrcIdSpillBegin:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSpillBegin *>(inst));
-        case IrInstSrcIdSpillEnd:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSpillEnd *>(inst));
-        case IrInstSrcIdCallArgs:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallArgs *>(inst));
-        case IrInstSrcIdWasmMemorySize:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemorySize *>(inst));
-        case IrInstSrcIdWasmMemoryGrow:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemoryGrow *>(inst));
-        case IrInstSrcIdSrc:
-            return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSrc *>(inst));
-    }
-    zig_unreachable();
-}
-
 void destroy_instruction_gen(IrInstGen *inst) {
     switch (inst->id) {
         case IrInstGenIdInvalid:
@@ -970,54 +665,18 @@ static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expecte
     zig_unreachable();
 }
 
-static bool ir_should_inline(IrExecutableSrc *exec, Scope *scope) {
-    if (exec->is_inline)
-        return true;
-
-    while (scope != nullptr) {
-        if (scope->id == ScopeIdCompTime)
-            return true;
-        if (scope->id == ScopeIdTypeOf)
-            return false;
-        if (scope->id == ScopeIdFnDef)
-            break;
-        scope = scope->parent;
-    }
-    return false;
-}
-
-static void ir_instruction_append(IrBasicBlockSrc *basic_block, IrInstSrc *instruction) {
-    assert(basic_block);
-    assert(instruction);
-    basic_block->instruction_list.append(instruction);
-}
-
 static void ir_inst_gen_append(IrBasicBlockGen *basic_block, IrInstGen *instruction) {
     assert(basic_block);
     assert(instruction);
     basic_block->instruction_list.append(instruction);
 }
 
-static size_t exec_next_debug_id(IrExecutableSrc *exec) {
-    size_t result = exec->next_debug_id;
-    exec->next_debug_id += 1;
-    return result;
-}
-
 static size_t exec_next_debug_id_gen(IrExecutableGen *exec) {
     size_t result = exec->next_debug_id;
     exec->next_debug_id += 1;
     return result;
 }
 
-static ZigFn *exec_fn_entry(IrExecutableSrc *exec) {
-    return exec->fn_entry;
-}
-
-static Buf *exec_c_import_buf(IrExecutableSrc *exec) {
-    return exec->c_import_buf;
-}
-
 static bool value_is_comptime(ZigValue *const_val) {
     return const_val->special != ConstValSpecialRuntime;
 }
@@ -1026,33 +685,11 @@ static bool instr_is_comptime(IrInstGen *instruction) {
     return value_is_comptime(instruction->value);
 }
 
-static bool instr_is_unreachable(IrInstSrc *instruction) {
-    return instruction->is_noreturn;
-}
-
-static void ir_ref_bb(IrBasicBlockSrc *bb) {
-    bb->ref_count += 1;
-}
-
-static void ir_ref_instruction(IrInstSrc *instruction, IrBasicBlockSrc *cur_bb) {
-    assert(instruction->id != IrInstSrcIdInvalid);
-    instruction->base.ref_count += 1;
-    if (instruction->owner_bb != cur_bb && !instr_is_unreachable(instruction)
-        && instruction->id != IrInstSrcIdConst)
-    {
-        ir_ref_bb(instruction->owner_bb);
-    }
-}
-
 static void ir_ref_inst_gen(IrInstGen *instruction) {
     assert(instruction->id != IrInstGenIdInvalid);
     instruction->base.ref_count += 1;
 }
 
-static void ir_ref_var(ZigVar *var) {
-    var->ref_count += 1;
-}
-
 static void create_result_ptr(CodeGen *codegen, ZigType *expected_type,
         ZigValue **out_result, ZigValue **out_result_ptr)
 {
@@ -1092,15 +729,6 @@ ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) {
     return res_type;
 }
 
-static IrBasicBlockSrc *ir_create_basic_block(IrBuilderSrc *irb, Scope *scope, const char *name_hint) {
-    IrBasicBlockSrc *result = heap::c_allocator.create<IrBasicBlockSrc>();
-    result->scope = scope;
-    result->name_hint = name_hint;
-    result->debug_id = exec_next_debug_id(irb->exec);
-    result->index = UINT32_MAX; // set later
-    return result;
-}
-
 static IrBasicBlockGen *ir_create_basic_block_gen(IrAnalyze *ira, Scope *scope, const char *name_hint) {
     IrBasicBlockGen *result = heap::c_allocator.create<IrBasicBlockGen>();
     result->scope = scope;
@@ -1115,8807 +743,1687 @@ static IrBasicBlockGen *ir_build_bb_from(IrAnalyze *ira, IrBasicBlockSrc *other_
     return new_bb;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcDeclVar *) {
-    return IrInstSrcIdDeclVar;
+static constexpr IrInstGenId ir_inst_id(IrInstGenDeclVar *) {
+    return IrInstGenIdDeclVar;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcBr *) {
-    return IrInstSrcIdBr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenBr *) {
+    return IrInstGenIdBr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCondBr *) {
-    return IrInstSrcIdCondBr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenCondBr *) {
+    return IrInstGenIdCondBr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchBr *) {
-    return IrInstSrcIdSwitchBr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenSwitchBr *) {
+    return IrInstGenIdSwitchBr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchVar *) {
-    return IrInstSrcIdSwitchVar;
+static constexpr IrInstGenId ir_inst_id(IrInstGenPhi *) {
+    return IrInstGenIdPhi;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchElseVar *) {
-    return IrInstSrcIdSwitchElseVar;
+static constexpr IrInstGenId ir_inst_id(IrInstGenBinaryNot *) {
+    return IrInstGenIdBinaryNot;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchTarget *) {
-    return IrInstSrcIdSwitchTarget;
+static constexpr IrInstGenId ir_inst_id(IrInstGenNegation *) {
+    return IrInstGenIdNegation;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcPhi *) {
-    return IrInstSrcIdPhi;
+static constexpr IrInstGenId ir_inst_id(IrInstGenBinOp *) {
+    return IrInstGenIdBinOp;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnOp *) {
-    return IrInstSrcIdUnOp;
+static constexpr IrInstGenId ir_inst_id(IrInstGenLoadPtr *) {
+    return IrInstGenIdLoadPtr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcBinOp *) {
-    return IrInstSrcIdBinOp;
+static constexpr IrInstGenId ir_inst_id(IrInstGenStorePtr *) {
+    return IrInstGenIdStorePtr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcMergeErrSets *) {
-    return IrInstSrcIdMergeErrSets;
+static constexpr IrInstGenId ir_inst_id(IrInstGenVectorStoreElem *) {
+    return IrInstGenIdVectorStoreElem;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcLoadPtr *) {
-    return IrInstSrcIdLoadPtr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenStructFieldPtr *) {
+    return IrInstGenIdStructFieldPtr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcStorePtr *) {
-    return IrInstSrcIdStorePtr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenUnionFieldPtr *) {
+    return IrInstGenIdUnionFieldPtr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcFieldPtr *) {
-    return IrInstSrcIdFieldPtr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenElemPtr *) {
+    return IrInstGenIdElemPtr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcElemPtr *) {
-    return IrInstSrcIdElemPtr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenVarPtr *) {
+    return IrInstGenIdVarPtr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcVarPtr *) {
-    return IrInstSrcIdVarPtr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenReturnPtr *) {
+    return IrInstGenIdReturnPtr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCall *) {
-    return IrInstSrcIdCall;
+static constexpr IrInstGenId ir_inst_id(IrInstGenCall *) {
+    return IrInstGenIdCall;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallArgs *) {
-    return IrInstSrcIdCallArgs;
+static constexpr IrInstGenId ir_inst_id(IrInstGenReturn *) {
+    return IrInstGenIdReturn;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallExtra *) {
-    return IrInstSrcIdCallExtra;
+static constexpr IrInstGenId ir_inst_id(IrInstGenCast *) {
+    return IrInstGenIdCast;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsyncCallExtra *) {
-    return IrInstSrcIdAsyncCallExtra;
+static constexpr IrInstGenId ir_inst_id(IrInstGenUnreachable *) {
+    return IrInstGenIdUnreachable;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcConst *) {
-    return IrInstSrcIdConst;
+static constexpr IrInstGenId ir_inst_id(IrInstGenAsm *) {
+    return IrInstGenIdAsm;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcReturn *) {
-    return IrInstSrcIdReturn;
+static constexpr IrInstGenId ir_inst_id(IrInstGenTestNonNull *) {
+    return IrInstGenIdTestNonNull;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcContainerInitList *) {
-    return IrInstSrcIdContainerInitList;
+static constexpr IrInstGenId ir_inst_id(IrInstGenOptionalUnwrapPtr *) {
+    return IrInstGenIdOptionalUnwrapPtr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcContainerInitFields *) {
-    return IrInstSrcIdContainerInitFields;
+static constexpr IrInstGenId ir_inst_id(IrInstGenOptionalWrap *) {
+    return IrInstGenIdOptionalWrap;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnreachable *) {
-    return IrInstSrcIdUnreachable;
+static constexpr IrInstGenId ir_inst_id(IrInstGenUnionTag *) {
+    return IrInstGenIdUnionTag;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeOf *) {
-    return IrInstSrcIdTypeOf;
+static constexpr IrInstGenId ir_inst_id(IrInstGenClz *) {
+    return IrInstGenIdClz;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetCold *) {
-    return IrInstSrcIdSetCold;
+static constexpr IrInstGenId ir_inst_id(IrInstGenCtz *) {
+    return IrInstGenIdCtz;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetRuntimeSafety *) {
-    return IrInstSrcIdSetRuntimeSafety;
+static constexpr IrInstGenId ir_inst_id(IrInstGenPopCount *) {
+    return IrInstGenIdPopCount;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetFloatMode *) {
-    return IrInstSrcIdSetFloatMode;
+static constexpr IrInstGenId ir_inst_id(IrInstGenBswap *) {
+    return IrInstGenIdBswap;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcArrayType *) {
-    return IrInstSrcIdArrayType;
-}
-
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcAnyFrameType *) {
-    return IrInstSrcIdAnyFrameType;
-}
-
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSliceType *) {
-    return IrInstSrcIdSliceType;
-}
-
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsm *) {
-    return IrInstSrcIdAsm;
+static constexpr IrInstGenId ir_inst_id(IrInstGenBitReverse *) {
+    return IrInstGenIdBitReverse;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSizeOf *) {
-    return IrInstSrcIdSizeOf;
+static constexpr IrInstGenId ir_inst_id(IrInstGenRef *) {
+    return IrInstGenIdRef;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestNonNull *) {
-    return IrInstSrcIdTestNonNull;
+static constexpr IrInstGenId ir_inst_id(IrInstGenErrName *) {
+    return IrInstGenIdErrName;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcOptionalUnwrapPtr *) {
-    return IrInstSrcIdOptionalUnwrapPtr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenCmpxchg *) {
+    return IrInstGenIdCmpxchg;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcClz *) {
-    return IrInstSrcIdClz;
+static constexpr IrInstGenId ir_inst_id(IrInstGenFence *) {
+    return IrInstGenIdFence;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCtz *) {
-    return IrInstSrcIdCtz;
+static constexpr IrInstGenId ir_inst_id(IrInstGenReduce *) {
+    return IrInstGenIdReduce;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcPopCount *) {
-    return IrInstSrcIdPopCount;
+static constexpr IrInstGenId ir_inst_id(IrInstGenTruncate *) {
+    return IrInstGenIdTruncate;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcBswap *) {
-    return IrInstSrcIdBswap;
+static constexpr IrInstGenId ir_inst_id(IrInstGenShuffleVector *) {
+    return IrInstGenIdShuffleVector;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitReverse *) {
-    return IrInstSrcIdBitReverse;
+static constexpr IrInstGenId ir_inst_id(IrInstGenSplat *) {
+    return IrInstGenIdSplat;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcImport *) {
-    return IrInstSrcIdImport;
+static constexpr IrInstGenId ir_inst_id(IrInstGenBoolNot *) {
+    return IrInstGenIdBoolNot;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCImport *) {
-    return IrInstSrcIdCImport;
+static constexpr IrInstGenId ir_inst_id(IrInstGenMemset *) {
+    return IrInstGenIdMemset;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCInclude *) {
-    return IrInstSrcIdCInclude;
+static constexpr IrInstGenId ir_inst_id(IrInstGenMemcpy *) {
+    return IrInstGenIdMemcpy;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCDefine *) {
-    return IrInstSrcIdCDefine;
+static constexpr IrInstGenId ir_inst_id(IrInstGenSlice *) {
+    return IrInstGenIdSlice;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCUndef *) {
-    return IrInstSrcIdCUndef;
+static constexpr IrInstGenId ir_inst_id(IrInstGenBreakpoint *) {
+    return IrInstGenIdBreakpoint;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcRef *) {
-    return IrInstSrcIdRef;
+static constexpr IrInstGenId ir_inst_id(IrInstGenReturnAddress *) {
+    return IrInstGenIdReturnAddress;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCompileErr *) {
-    return IrInstSrcIdCompileErr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenFrameAddress *) {
+    return IrInstGenIdFrameAddress;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCompileLog *) {
-    return IrInstSrcIdCompileLog;
+static constexpr IrInstGenId ir_inst_id(IrInstGenFrameHandle *) {
+    return IrInstGenIdFrameHandle;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrName *) {
-    return IrInstSrcIdErrName;
+static constexpr IrInstGenId ir_inst_id(IrInstGenFrameSize *) {
+    return IrInstGenIdFrameSize;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcEmbedFile *) {
-    return IrInstSrcIdEmbedFile;
+static constexpr IrInstGenId ir_inst_id(IrInstGenOverflowOp *) {
+    return IrInstGenIdOverflowOp;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCmpxchg *) {
-    return IrInstSrcIdCmpxchg;
+static constexpr IrInstGenId ir_inst_id(IrInstGenTestErr *) {
+    return IrInstGenIdTestErr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcFence *) {
-    return IrInstSrcIdFence;
+static constexpr IrInstGenId ir_inst_id(IrInstGenMulAdd *) {
+    return IrInstGenIdMulAdd;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcReduce *) {
-    return IrInstSrcIdReduce;
+static constexpr IrInstGenId ir_inst_id(IrInstGenFloatOp *) {
+    return IrInstGenIdFloatOp;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcTruncate *) {
-    return IrInstSrcIdTruncate;
+static constexpr IrInstGenId ir_inst_id(IrInstGenUnwrapErrCode *) {
+    return IrInstGenIdUnwrapErrCode;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntCast *) {
-    return IrInstSrcIdIntCast;
+static constexpr IrInstGenId ir_inst_id(IrInstGenUnwrapErrPayload *) {
+    return IrInstGenIdUnwrapErrPayload;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatCast *) {
-    return IrInstSrcIdFloatCast;
+static constexpr IrInstGenId ir_inst_id(IrInstGenErrWrapCode *) {
+    return IrInstGenIdErrWrapCode;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToFloat *) {
-    return IrInstSrcIdIntToFloat;
+static constexpr IrInstGenId ir_inst_id(IrInstGenErrWrapPayload *) {
+    return IrInstGenIdErrWrapPayload;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatToInt *) {
-    return IrInstSrcIdFloatToInt;
+static constexpr IrInstGenId ir_inst_id(IrInstGenPtrCast *) {
+    return IrInstGenIdPtrCast;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolToInt *) {
-    return IrInstSrcIdBoolToInt;
+static constexpr IrInstGenId ir_inst_id(IrInstGenBitCast *) {
+    return IrInstGenIdBitCast;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcVectorType *) {
-    return IrInstSrcIdVectorType;
+static constexpr IrInstGenId ir_inst_id(IrInstGenWidenOrShorten *) {
+    return IrInstGenIdWidenOrShorten;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcShuffleVector *) {
-    return IrInstSrcIdShuffleVector;
+static constexpr IrInstGenId ir_inst_id(IrInstGenIntToPtr *) {
+    return IrInstGenIdIntToPtr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSplat *) {
-    return IrInstSrcIdSplat;
+static constexpr IrInstGenId ir_inst_id(IrInstGenPtrToInt *) {
+    return IrInstGenIdPtrToInt;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolNot *) {
-    return IrInstSrcIdBoolNot;
+static constexpr IrInstGenId ir_inst_id(IrInstGenIntToEnum *) {
+    return IrInstGenIdIntToEnum;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemset *) {
-    return IrInstSrcIdMemset;
+static constexpr IrInstGenId ir_inst_id(IrInstGenIntToErr *) {
+    return IrInstGenIdIntToErr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemcpy *) {
-    return IrInstSrcIdMemcpy;
+static constexpr IrInstGenId ir_inst_id(IrInstGenErrToInt *) {
+    return IrInstGenIdErrToInt;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSlice *) {
-    return IrInstSrcIdSlice;
+static constexpr IrInstGenId ir_inst_id(IrInstGenPanic *) {
+    return IrInstGenIdPanic;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcBreakpoint *) {
-    return IrInstSrcIdBreakpoint;
+static constexpr IrInstGenId ir_inst_id(IrInstGenTagName *) {
+    return IrInstGenIdTagName;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcReturnAddress *) {
-    return IrInstSrcIdReturnAddress;
+static constexpr IrInstGenId ir_inst_id(IrInstGenFieldParentPtr *) {
+    return IrInstGenIdFieldParentPtr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameAddress *) {
-    return IrInstSrcIdFrameAddress;
+static constexpr IrInstGenId ir_inst_id(IrInstGenAlignCast *) {
+    return IrInstGenIdAlignCast;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameHandle *) {
-    return IrInstSrcIdFrameHandle;
+static constexpr IrInstGenId ir_inst_id(IrInstGenErrorReturnTrace *) {
+    return IrInstGenIdErrorReturnTrace;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameType *) {
-    return IrInstSrcIdFrameType;
+static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicRmw *) {
+    return IrInstGenIdAtomicRmw;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameSize *) {
-    return IrInstSrcIdFrameSize;
+static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicLoad *) {
+    return IrInstGenIdAtomicLoad;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlignOf *) {
-    return IrInstSrcIdAlignOf;
+static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicStore *) {
+    return IrInstGenIdAtomicStore;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcOverflowOp *) {
-    return IrInstSrcIdOverflowOp;
+static constexpr IrInstGenId ir_inst_id(IrInstGenSaveErrRetAddr *) {
+    return IrInstGenIdSaveErrRetAddr;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestErr *) {
-    return IrInstSrcIdTestErr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenVectorToArray *) {
+    return IrInstGenIdVectorToArray;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcMulAdd *) {
-    return IrInstSrcIdMulAdd;
+static constexpr IrInstGenId ir_inst_id(IrInstGenArrayToVector *) {
+    return IrInstGenIdArrayToVector;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatOp *) {
-    return IrInstSrcIdFloatOp;
+static constexpr IrInstGenId ir_inst_id(IrInstGenAssertZero *) {
+    return IrInstGenIdAssertZero;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnwrapErrCode *) {
-    return IrInstSrcIdUnwrapErrCode;
+static constexpr IrInstGenId ir_inst_id(IrInstGenAssertNonNull *) {
+    return IrInstGenIdAssertNonNull;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnwrapErrPayload *) {
-    return IrInstSrcIdUnwrapErrPayload;
+static constexpr IrInstGenId ir_inst_id(IrInstGenPtrOfArrayToSlice *) {
+    return IrInstGenIdPtrOfArrayToSlice;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcFnProto *) {
-    return IrInstSrcIdFnProto;
+static constexpr IrInstGenId ir_inst_id(IrInstGenSuspendBegin *) {
+    return IrInstGenIdSuspendBegin;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestComptime *) {
-    return IrInstSrcIdTestComptime;
+static constexpr IrInstGenId ir_inst_id(IrInstGenSuspendFinish *) {
+    return IrInstGenIdSuspendFinish;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrCast *) {
-    return IrInstSrcIdPtrCast;
+static constexpr IrInstGenId ir_inst_id(IrInstGenAwait *) {
+    return IrInstGenIdAwait;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitCast *) {
-    return IrInstSrcIdBitCast;
+static constexpr IrInstGenId ir_inst_id(IrInstGenResume *) {
+    return IrInstGenIdResume;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToPtr *) {
-    return IrInstSrcIdIntToPtr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenSpillBegin *) {
+    return IrInstGenIdSpillBegin;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrToInt *) {
-    return IrInstSrcIdPtrToInt;
+static constexpr IrInstGenId ir_inst_id(IrInstGenSpillEnd *) {
+    return IrInstGenIdSpillEnd;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToEnum *) {
-    return IrInstSrcIdIntToEnum;
+static constexpr IrInstGenId ir_inst_id(IrInstGenVectorExtractElem *) {
+    return IrInstGenIdVectorExtractElem;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcEnumToInt *) {
-    return IrInstSrcIdEnumToInt;
+static constexpr IrInstGenId ir_inst_id(IrInstGenAlloca *) {
+    return IrInstGenIdAlloca;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToErr *) {
-    return IrInstSrcIdIntToErr;
+static constexpr IrInstGenId ir_inst_id(IrInstGenConst *) {
+    return IrInstGenIdConst;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) {
-    return IrInstSrcIdErrToInt;
+static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemorySize *) {
+  return IrInstGenIdWasmMemorySize;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) {
-    return IrInstSrcIdCheckStatementIsVoid;
+static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemoryGrow *) {
+  return IrInstGenIdWasmMemoryGrow;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeName *) {
-    return IrInstSrcIdTypeName;
+static constexpr IrInstGenId ir_inst_id(IrInstGenExtern *) {
+    return IrInstGenIdExtern;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcDeclRef *) {
-    return IrInstSrcIdDeclRef;
+template<typename T>
+static T *ir_create_inst_gen(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
+    T *special_instruction = heap::c_allocator.create<T>();
+    special_instruction->base.id = ir_inst_id(special_instruction);
+    special_instruction->base.base.scope = scope;
+    special_instruction->base.base.source_node = source_node;
+    special_instruction->base.base.debug_id = exec_next_debug_id_gen(irb->exec);
+    special_instruction->base.owner_bb = irb->current_basic_block;
+    special_instruction->base.value = irb->codegen->pass1_arena->create<ZigValue>();
+    return special_instruction;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcPanic *) {
-    return IrInstSrcIdPanic;
+template<typename T>
+static T *ir_create_inst_noval(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
+    T *special_instruction = heap::c_allocator.create<T>();
+    special_instruction->base.id = ir_inst_id(special_instruction);
+    special_instruction->base.base.scope = scope;
+    special_instruction->base.base.source_node = source_node;
+    special_instruction->base.base.debug_id = exec_next_debug_id_gen(irb->exec);
+    special_instruction->base.owner_bb = irb->current_basic_block;
+    return special_instruction;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcTagName *) {
-    return IrInstSrcIdTagName;
+template<typename T>
+static T *ir_build_inst_gen(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
+    T *special_instruction = ir_create_inst_gen<T>(irb, scope, source_node);
+    ir_inst_gen_append(irb->current_basic_block, &special_instruction->base);
+    return special_instruction;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcFieldParentPtr *) {
-    return IrInstSrcIdFieldParentPtr;
+template<typename T>
+static T *ir_build_inst_noreturn(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
+    T *special_instruction = ir_create_inst_noval<T>(irb, scope, source_node);
+    special_instruction->base.value = irb->codegen->intern.for_unreachable();
+    ir_inst_gen_append(irb->current_basic_block, &special_instruction->base);
+    return special_instruction;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcByteOffsetOf *) {
-    return IrInstSrcIdByteOffsetOf;
+template<typename T>
+static T *ir_build_inst_void(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
+    T *special_instruction = ir_create_inst_noval<T>(irb, scope, source_node);
+    special_instruction->base.value = irb->codegen->intern.for_void();
+    ir_inst_gen_append(irb->current_basic_block, &special_instruction->base);
+    return special_instruction;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitOffsetOf *) {
-    return IrInstSrcIdBitOffsetOf;
+IrInstGen *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn,
+        ZigType *var_type, const char *name_hint)
+{
+    IrInstGenAlloca *alloca_gen = heap::c_allocator.create<IrInstGenAlloca>();
+    alloca_gen->base.id = IrInstGenIdAlloca;
+    alloca_gen->base.base.source_node = source_node;
+    alloca_gen->base.base.scope = scope;
+    alloca_gen->base.value = g->pass1_arena->create<ZigValue>();
+    alloca_gen->base.value->type = get_pointer_to_type(g, var_type, false);
+    alloca_gen->base.base.ref_count = 1;
+    alloca_gen->name_hint = name_hint;
+    fn->alloca_gen_list.append(alloca_gen);
+    return &alloca_gen->base;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeInfo *) {
-    return IrInstSrcIdTypeInfo;
-}
+static IrInstGen *ir_build_cast(IrAnalyze *ira, IrInst *source_instr,ZigType *dest_type,
+    IrInstGen *value, CastOp cast_op)
+{
+    IrInstGenCast *inst = ir_build_inst_gen<IrInstGenCast>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    inst->base.value->type = dest_type;
+    inst->value = value;
+    inst->cast_op = cast_op;
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcType *) {
-    return IrInstSrcIdType;
-}
+    ir_ref_inst_gen(value);
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcHasField *) {
-    return IrInstSrcIdHasField;
+    return &inst->base;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetEvalBranchQuota *) {
-    return IrInstSrcIdSetEvalBranchQuota;
-}
+static IrInstGen *ir_build_cond_br_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *condition,
+        IrBasicBlockGen *then_block, IrBasicBlockGen *else_block)
+{
+    IrInstGenCondBr *inst = ir_build_inst_noreturn<IrInstGenCondBr>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    inst->condition = condition;
+    inst->then_block = then_block;
+    inst->else_block = else_block;
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrType *) {
-    return IrInstSrcIdPtrType;
-}
+    ir_ref_inst_gen(condition);
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlignCast *) {
-    return IrInstSrcIdAlignCast;
+    return &inst->base;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcImplicitCast *) {
-    return IrInstSrcIdImplicitCast;
-}
+static IrInstGen *ir_build_return_gen(IrAnalyze *ira, IrInst *source_inst, IrInstGen *operand) {
+    IrInstGenReturn *inst = ir_build_inst_noreturn<IrInstGenReturn>(&ira->new_irb,
+            source_inst->scope, source_inst->source_node);
+    inst->operand = operand;
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcResolveResult *) {
-    return IrInstSrcIdResolveResult;
-}
+    if (operand != nullptr) ir_ref_inst_gen(operand);
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcResetResult *) {
-    return IrInstSrcIdResetResult;
+    return &inst->base;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) {
-    return IrInstSrcIdSetAlignStack;
-}
+static IrInstGen *ir_build_bin_op_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *res_type,
+        IrBinOp op_id, IrInstGen *op1, IrInstGen *op2, bool safety_check_on)
+{
+    IrInstGenBinOp *inst = ir_build_inst_gen<IrInstGenBinOp>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    inst->base.value->type = res_type;
+    inst->op_id = op_id;
+    inst->op1 = op1;
+    inst->op2 = op2;
+    inst->safety_check_on = safety_check_on;
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) {
-    return IrInstSrcIdExport;
-}
+    ir_ref_inst_gen(op1);
+    ir_ref_inst_gen(op2);
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcExtern *) {
-    return IrInstSrcIdExtern;
+    return &inst->base;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorReturnTrace *) {
-    return IrInstSrcIdErrorReturnTrace;
-}
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorUnion *) {
-    return IrInstSrcIdErrorUnion;
-}
+static IrInstGen *ir_build_var_ptr_gen(IrAnalyze *ira, IrInst *source_instr, ZigVar *var) {
+    IrInstGenVarPtr *instruction = ir_build_inst_gen<IrInstGenVarPtr>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    instruction->var = var;
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicRmw *) {
-    return IrInstSrcIdAtomicRmw;
-}
+    var->ref_count += 1;
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicLoad *) {
-    return IrInstSrcIdAtomicLoad;
+    return &instruction->base;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicStore *) {
-    return IrInstSrcIdAtomicStore;
+static IrInstGen *ir_build_return_ptr(IrAnalyze *ira, Scope *scope, AstNode *source_node, ZigType *ty) {
+    IrInstGenReturnPtr *instruction = ir_build_inst_gen<IrInstGenReturnPtr>(&ira->new_irb, scope, source_node);
+    instruction->base.value->type = ty;
+    return &instruction->base;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSaveErrRetAddr *) {
-    return IrInstSrcIdSaveErrRetAddr;
-}
+static IrInstGen *ir_build_elem_ptr_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
+        IrInstGen *array_ptr, IrInstGen *elem_index, bool safety_check_on, ZigType *return_type)
+{
+    IrInstGenElemPtr *instruction = ir_build_inst_gen<IrInstGenElemPtr>(&ira->new_irb, scope, source_node);
+    instruction->base.value->type = return_type;
+    instruction->array_ptr = array_ptr;
+    instruction->elem_index = elem_index;
+    instruction->safety_check_on = safety_check_on;
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcAddImplicitReturnType *) {
-    return IrInstSrcIdAddImplicitReturnType;
-}
+    ir_ref_inst_gen(array_ptr);
+    ir_ref_inst_gen(elem_index);
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrSetCast *) {
-    return IrInstSrcIdErrSetCast;
+    return &instruction->base;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckRuntimeScope *) {
-    return IrInstSrcIdCheckRuntimeScope;
-}
+static IrInstGen *ir_build_struct_field_ptr(IrAnalyze *ira, IrInst *source_instr,
+    IrInstGen *struct_ptr, TypeStructField *field, ZigType *ptr_type)
+{
+    IrInstGenStructFieldPtr *inst = ir_build_inst_gen<IrInstGenStructFieldPtr>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    inst->base.value->type = ptr_type;
+    inst->struct_ptr = struct_ptr;
+    inst->field = field;
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcHasDecl *) {
-    return IrInstSrcIdHasDecl;
-}
+    ir_ref_inst_gen(struct_ptr);
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcUndeclaredIdent *) {
-    return IrInstSrcIdUndeclaredIdent;
+    return &inst->base;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlloca *) {
-    return IrInstSrcIdAlloca;
-}
+static IrInstGen *ir_build_union_field_ptr(IrAnalyze *ira, IrInst *source_instr,
+    IrInstGen *union_ptr, TypeUnionField *field, bool safety_check_on, bool initializing, ZigType *ptr_type)
+{
+    IrInstGenUnionFieldPtr *inst = ir_build_inst_gen<IrInstGenUnionFieldPtr>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    inst->base.value->type = ptr_type;
+    inst->initializing = initializing;
+    inst->safety_check_on = safety_check_on;
+    inst->union_ptr = union_ptr;
+    inst->field = field;
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcEndExpr *) {
-    return IrInstSrcIdEndExpr;
-}
+    ir_ref_inst_gen(union_ptr);
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnionInitNamedField *) {
-    return IrInstSrcIdUnionInitNamedField;
+    return &inst->base;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSuspendBegin *) {
-    return IrInstSrcIdSuspendBegin;
-}
+static IrInstGenCall *ir_build_call_gen(IrAnalyze *ira, IrInst *source_instruction,
+        ZigFn *fn_entry, IrInstGen *fn_ref, size_t arg_count, IrInstGen **args,
+        CallModifier modifier, IrInstGen *new_stack, bool is_async_call_builtin,
+        IrInstGen *result_loc, ZigType *return_type)
+{
+    IrInstGenCall *call_instruction = ir_build_inst_gen<IrInstGenCall>(&ira->new_irb,
+            source_instruction->scope, source_instruction->source_node);
+    call_instruction->base.value->type = return_type;
+    call_instruction->fn_entry = fn_entry;
+    call_instruction->fn_ref = fn_ref;
+    call_instruction->args = args;
+    call_instruction->arg_count = arg_count;
+    call_instruction->modifier = modifier;
+    call_instruction->is_async_call_builtin = is_async_call_builtin;
+    call_instruction->new_stack = new_stack;
+    call_instruction->result_loc = result_loc;
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSuspendFinish *) {
-    return IrInstSrcIdSuspendFinish;
-}
+    if (fn_ref != nullptr) ir_ref_inst_gen(fn_ref);
+    for (size_t i = 0; i < arg_count; i += 1)
+        ir_ref_inst_gen(args[i]);
+    if (new_stack != nullptr) ir_ref_inst_gen(new_stack);
+    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcAwait *) {
-    return IrInstSrcIdAwait;
+    return call_instruction;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcResume *) {
-    return IrInstSrcIdResume;
-}
+static IrInstGen *ir_build_phi_gen(IrAnalyze *ira, IrInst *source_instr, size_t incoming_count,
+        IrBasicBlockGen **incoming_blocks, IrInstGen **incoming_values, ZigType *result_type)
+{
+    assert(incoming_count != 0);
+    assert(incoming_count != SIZE_MAX);
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillBegin *) {
-    return IrInstSrcIdSpillBegin;
-}
+    IrInstGenPhi *phi_instruction = ir_build_inst_gen<IrInstGenPhi>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    phi_instruction->base.value->type = result_type;
+    phi_instruction->incoming_count = incoming_count;
+    phi_instruction->incoming_blocks = incoming_blocks;
+    phi_instruction->incoming_values = incoming_values;
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillEnd *) {
-    return IrInstSrcIdSpillEnd;
-}
+    for (size_t i = 0; i < incoming_count; i += 1) {
+        ir_ref_inst_gen(incoming_values[i]);
+    }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemorySize *) {
-    return IrInstSrcIdWasmMemorySize;
+    return &phi_instruction->base;
 }
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemoryGrow *) {
-    return IrInstSrcIdWasmMemoryGrow;
-}
+static IrInstGen *ir_build_br_gen(IrAnalyze *ira, IrInst *source_instr, IrBasicBlockGen *dest_block) {
+    IrInstGenBr *inst = ir_build_inst_noreturn<IrInstGenBr>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    inst->dest_block = dest_block;
 
-static constexpr IrInstSrcId ir_inst_id(IrInstSrcSrc *) {
-    return IrInstSrcIdSrc;
+    return &inst->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenDeclVar *) {
-    return IrInstGenIdDeclVar;
-}
+static IrInstGen *ir_build_negation(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, ZigType *expr_type, bool wrapping) {
+    IrInstGenNegation *instruction = ir_build_inst_gen<IrInstGenNegation>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = expr_type;
+    instruction->operand = operand;
+    instruction->wrapping = wrapping;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenBr *) {
-    return IrInstGenIdBr;
-}
+    ir_ref_inst_gen(operand);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenCondBr *) {
-    return IrInstGenIdCondBr;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenSwitchBr *) {
-    return IrInstGenIdSwitchBr;
-}
+static IrInstGen *ir_build_binary_not(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand,
+        ZigType *expr_type)
+{
+    IrInstGenBinaryNot *instruction = ir_build_inst_gen<IrInstGenBinaryNot>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = expr_type;
+    instruction->operand = operand;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenPhi *) {
-    return IrInstGenIdPhi;
-}
+    ir_ref_inst_gen(operand);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenBinaryNot *) {
-    return IrInstGenIdBinaryNot;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenNegation *) {
-    return IrInstGenIdNegation;
+static IrInstGen *ir_build_unreachable_gen(IrAnalyze *ira, IrInst *source_instr) {
+    IrInstGenUnreachable *inst = ir_build_inst_noreturn<IrInstGenUnreachable>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    return &inst->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenBinOp *) {
-    return IrInstGenIdBinOp;
-}
+static IrInstGen *ir_build_store_ptr_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *ptr, IrInstGen *value) {
+    IrInstGenStorePtr *instruction = ir_build_inst_void<IrInstGenStorePtr>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->ptr = ptr;
+    instruction->value = value;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenLoadPtr *) {
-    return IrInstGenIdLoadPtr;
-}
+    ir_ref_inst_gen(ptr);
+    ir_ref_inst_gen(value);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenStorePtr *) {
-    return IrInstGenIdStorePtr;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenVectorStoreElem *) {
-    return IrInstGenIdVectorStoreElem;
-}
+static IrInstGen *ir_build_vector_store_elem(IrAnalyze *ira, IrInst *src_inst,
+        IrInstGen *vector_ptr, IrInstGen *index, IrInstGen *value)
+{
+    IrInstGenVectorStoreElem *inst = ir_build_inst_void<IrInstGenVectorStoreElem>(
+            &ira->new_irb, src_inst->scope, src_inst->source_node);
+    inst->vector_ptr = vector_ptr;
+    inst->index = index;
+    inst->value = value;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenStructFieldPtr *) {
-    return IrInstGenIdStructFieldPtr;
-}
+    ir_ref_inst_gen(vector_ptr);
+    ir_ref_inst_gen(index);
+    ir_ref_inst_gen(value);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenUnionFieldPtr *) {
-    return IrInstGenIdUnionFieldPtr;
+    return &inst->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenElemPtr *) {
-    return IrInstGenIdElemPtr;
-}
+static IrInstGen *ir_build_var_decl_gen(IrAnalyze *ira, IrInst *source_instruction,
+        ZigVar *var, IrInstGen *var_ptr)
+{
+    IrInstGenDeclVar *inst = ir_build_inst_gen<IrInstGenDeclVar>(&ira->new_irb,
+            source_instruction->scope, source_instruction->source_node);
+    inst->base.value->special = ConstValSpecialStatic;
+    inst->base.value->type = ira->codegen->builtin_types.entry_void;
+    inst->var = var;
+    inst->var_ptr = var_ptr;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenVarPtr *) {
-    return IrInstGenIdVarPtr;
-}
+    ir_ref_inst_gen(var_ptr);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenReturnPtr *) {
-    return IrInstGenIdReturnPtr;
+    return &inst->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenCall *) {
-    return IrInstGenIdCall;
-}
+static IrInstGen *ir_build_extern_gen(IrAnalyze *ira, IrInst *source_instr, Buf *name,
+        GlobalLinkageId linkage, bool is_thread_local, ZigType *expr_type)
+{
+    IrInstGenExtern *instruction = ir_build_inst_gen<IrInstGenExtern>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = expr_type;
+    instruction->name = name;
+    instruction->linkage = linkage;
+    instruction->is_thread_local = is_thread_local;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenReturn *) {
-    return IrInstGenIdReturn;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenCast *) {
-    return IrInstGenIdCast;
-}
+static IrInstGen *ir_build_load_ptr_gen(IrAnalyze *ira, IrInst *source_instruction,
+        IrInstGen *ptr, ZigType *ty, IrInstGen *result_loc)
+{
+    IrInstGenLoadPtr *instruction = ir_build_inst_gen<IrInstGenLoadPtr>(
+            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = ty;
+    instruction->ptr = ptr;
+    instruction->result_loc = result_loc;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenUnreachable *) {
-    return IrInstGenIdUnreachable;
-}
+    ir_ref_inst_gen(ptr);
+    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenAsm *) {
-    return IrInstGenIdAsm;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenTestNonNull *) {
-    return IrInstGenIdTestNonNull;
-}
+static IrInstGen *ir_build_asm_gen(IrAnalyze *ira, IrInst *source_instr,
+        Buf *asm_template, AsmToken *token_list, size_t token_list_len,
+        IrInstGen **input_list, IrInstGen **output_types, ZigVar **output_vars, size_t return_count,
+        bool has_side_effects, ZigType *return_type)
+{
+    IrInstGenAsm *instruction = ir_build_inst_gen<IrInstGenAsm>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = return_type;
+    instruction->asm_template = asm_template;
+    instruction->token_list = token_list;
+    instruction->token_list_len = token_list_len;
+    instruction->input_list = input_list;
+    instruction->output_types = output_types;
+    instruction->output_vars = output_vars;
+    instruction->return_count = return_count;
+    instruction->has_side_effects = has_side_effects;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenOptionalUnwrapPtr *) {
-    return IrInstGenIdOptionalUnwrapPtr;
-}
+    assert(source_instr->source_node->type == NodeTypeAsmExpr);
+    for (size_t i = 0; i < source_instr->source_node->data.asm_expr.output_list.length; i += 1) {
+        IrInstGen *output_type = output_types[i];
+        if (output_type) ir_ref_inst_gen(output_type);
+    }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenOptionalWrap *) {
-    return IrInstGenIdOptionalWrap;
-}
+    for (size_t i = 0; i < source_instr->source_node->data.asm_expr.input_list.length; i += 1) {
+        IrInstGen *input_value = input_list[i];
+        ir_ref_inst_gen(input_value);
+    }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenUnionTag *) {
-    return IrInstGenIdUnionTag;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenClz *) {
-    return IrInstGenIdClz;
-}
+static IrInstGen *ir_build_test_non_null_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value) {
+    IrInstGenTestNonNull *inst = ir_build_inst_gen<IrInstGenTestNonNull>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    inst->base.value->type = ira->codegen->builtin_types.entry_bool;
+    inst->value = value;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenCtz *) {
-    return IrInstGenIdCtz;
-}
+    ir_ref_inst_gen(value);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenPopCount *) {
-    return IrInstGenIdPopCount;
+    return &inst->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenBswap *) {
-    return IrInstGenIdBswap;
-}
+static IrInstGen *ir_build_optional_unwrap_ptr_gen(IrAnalyze *ira, IrInst *source_instr,
+        IrInstGen *base_ptr, bool safety_check_on, bool initializing, ZigType *result_type)
+{
+    IrInstGenOptionalUnwrapPtr *inst = ir_build_inst_gen<IrInstGenOptionalUnwrapPtr>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    inst->base.value->type = result_type;
+    inst->base_ptr = base_ptr;
+    inst->safety_check_on = safety_check_on;
+    inst->initializing = initializing;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenBitReverse *) {
-    return IrInstGenIdBitReverse;
+    ir_ref_inst_gen(base_ptr);
+
+    return &inst->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenRef *) {
-    return IrInstGenIdRef;
-}
+static IrInstGen *ir_build_optional_wrap(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_ty,
+        IrInstGen *operand, IrInstGen *result_loc)
+{
+    IrInstGenOptionalWrap *instruction = ir_build_inst_gen<IrInstGenOptionalWrap>(
+            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = result_ty;
+    instruction->operand = operand;
+    instruction->result_loc = result_loc;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenErrName *) {
-    return IrInstGenIdErrName;
-}
+    ir_ref_inst_gen(operand);
+    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenCmpxchg *) {
-    return IrInstGenIdCmpxchg;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenFence *) {
-    return IrInstGenIdFence;
-}
+static IrInstGen *ir_build_err_wrap_payload(IrAnalyze *ira, IrInst *source_instruction,
+        ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc)
+{
+    IrInstGenErrWrapPayload *instruction = ir_build_inst_gen<IrInstGenErrWrapPayload>(
+            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = result_type;
+    instruction->operand = operand;
+    instruction->result_loc = result_loc;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenReduce *) {
-    return IrInstGenIdReduce;
-}
+    ir_ref_inst_gen(operand);
+    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenTruncate *) {
-    return IrInstGenIdTruncate;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenShuffleVector *) {
-    return IrInstGenIdShuffleVector;
-}
+static IrInstGen *ir_build_err_wrap_code(IrAnalyze *ira, IrInst *source_instruction,
+        ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc)
+{
+    IrInstGenErrWrapCode *instruction = ir_build_inst_gen<IrInstGenErrWrapCode>(
+            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = result_type;
+    instruction->operand = operand;
+    instruction->result_loc = result_loc;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenSplat *) {
-    return IrInstGenIdSplat;
-}
+    ir_ref_inst_gen(operand);
+    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenBoolNot *) {
-    return IrInstGenIdBoolNot;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenMemset *) {
-    return IrInstGenIdMemset;
-}
+static IrInstGen *ir_build_clz_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, IrInstGen *op) {
+    IrInstGenClz *instruction = ir_build_inst_gen<IrInstGenClz>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = result_type;
+    instruction->op = op;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenMemcpy *) {
-    return IrInstGenIdMemcpy;
-}
+    ir_ref_inst_gen(op);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenSlice *) {
-    return IrInstGenIdSlice;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenBreakpoint *) {
-    return IrInstGenIdBreakpoint;
-}
+static IrInstGen *ir_build_ctz_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, IrInstGen *op) {
+    IrInstGenCtz *instruction = ir_build_inst_gen<IrInstGenCtz>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = result_type;
+    instruction->op = op;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenReturnAddress *) {
-    return IrInstGenIdReturnAddress;
-}
+    ir_ref_inst_gen(op);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenFrameAddress *) {
-    return IrInstGenIdFrameAddress;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenFrameHandle *) {
-    return IrInstGenIdFrameHandle;
-}
+static IrInstGen *ir_build_pop_count_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type,
+        IrInstGen *op)
+{
+    IrInstGenPopCount *instruction = ir_build_inst_gen<IrInstGenPopCount>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = result_type;
+    instruction->op = op;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenFrameSize *) {
-    return IrInstGenIdFrameSize;
-}
+    ir_ref_inst_gen(op);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenOverflowOp *) {
-    return IrInstGenIdOverflowOp;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenTestErr *) {
-    return IrInstGenIdTestErr;
-}
+static IrInstGen *ir_build_bswap_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *op_type,
+        IrInstGen *op)
+{
+    IrInstGenBswap *instruction = ir_build_inst_gen<IrInstGenBswap>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = op_type;
+    instruction->op = op;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenMulAdd *) {
-    return IrInstGenIdMulAdd;
-}
+    ir_ref_inst_gen(op);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenFloatOp *) {
-    return IrInstGenIdFloatOp;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenUnwrapErrCode *) {
-    return IrInstGenIdUnwrapErrCode;
-}
+static IrInstGen *ir_build_bit_reverse_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *int_type,
+        IrInstGen *op)
+{
+    IrInstGenBitReverse *instruction = ir_build_inst_gen<IrInstGenBitReverse>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = int_type;
+    instruction->op = op;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenUnwrapErrPayload *) {
-    return IrInstGenIdUnwrapErrPayload;
-}
+    ir_ref_inst_gen(op);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenErrWrapCode *) {
-    return IrInstGenIdErrWrapCode;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenErrWrapPayload *) {
-    return IrInstGenIdErrWrapPayload;
-}
+static IrInstGenSwitchBr *ir_build_switch_br_gen(IrAnalyze *ira, IrInst *source_instr,
+        IrInstGen *target_value, IrBasicBlockGen *else_block, size_t case_count, IrInstGenSwitchBrCase *cases)
+{
+    IrInstGenSwitchBr *instruction = ir_build_inst_noreturn<IrInstGenSwitchBr>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->target_value = target_value;
+    instruction->else_block = else_block;
+    instruction->case_count = case_count;
+    instruction->cases = cases;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenPtrCast *) {
-    return IrInstGenIdPtrCast;
-}
+    ir_ref_inst_gen(target_value);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenBitCast *) {
-    return IrInstGenIdBitCast;
-}
+    for (size_t i = 0; i < case_count; i += 1) {
+        ir_ref_inst_gen(cases[i].value);
+    }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenWidenOrShorten *) {
-    return IrInstGenIdWidenOrShorten;
+    return instruction;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenIntToPtr *) {
-    return IrInstGenIdIntToPtr;
-}
+static IrInstGen *ir_build_union_tag(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value,
+        ZigType *tag_type)
+{
+    IrInstGenUnionTag *instruction = ir_build_inst_gen<IrInstGenUnionTag>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->value = value;
+    instruction->base.value->type = tag_type;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenPtrToInt *) {
-    return IrInstGenIdPtrToInt;
-}
+    ir_ref_inst_gen(value);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenIntToEnum *) {
-    return IrInstGenIdIntToEnum;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenIntToErr *) {
-    return IrInstGenIdIntToErr;
-}
+static IrInstGen *ir_build_ref_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type,
+        IrInstGen *operand, IrInstGen *result_loc)
+{
+    IrInstGenRef *instruction = ir_build_inst_gen<IrInstGenRef>(&ira->new_irb,
+            source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = result_type;
+    instruction->operand = operand;
+    instruction->result_loc = result_loc;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenErrToInt *) {
-    return IrInstGenIdErrToInt;
-}
+    ir_ref_inst_gen(operand);
+    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenPanic *) {
-    return IrInstGenIdPanic;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenTagName *) {
-    return IrInstGenIdTagName;
-}
+static IrInstGen *ir_build_err_name_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value,
+        ZigType *str_type)
+{
+    IrInstGenErrName *instruction = ir_build_inst_gen<IrInstGenErrName>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = str_type;
+    instruction->value = value;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenFieldParentPtr *) {
-    return IrInstGenIdFieldParentPtr;
-}
+    ir_ref_inst_gen(value);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenAlignCast *) {
-    return IrInstGenIdAlignCast;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenErrorReturnTrace *) {
-    return IrInstGenIdErrorReturnTrace;
-}
+static IrInstGen *ir_build_cmpxchg_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type,
+    IrInstGen *ptr, IrInstGen *cmp_value, IrInstGen *new_value,
+    AtomicOrder success_order, AtomicOrder failure_order, bool is_weak, IrInstGen *result_loc)
+{
+    IrInstGenCmpxchg *instruction = ir_build_inst_gen<IrInstGenCmpxchg>(&ira->new_irb,
+            source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = result_type;
+    instruction->ptr = ptr;
+    instruction->cmp_value = cmp_value;
+    instruction->new_value = new_value;
+    instruction->success_order = success_order;
+    instruction->failure_order = failure_order;
+    instruction->is_weak = is_weak;
+    instruction->result_loc = result_loc;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicRmw *) {
-    return IrInstGenIdAtomicRmw;
-}
+    ir_ref_inst_gen(ptr);
+    ir_ref_inst_gen(cmp_value);
+    ir_ref_inst_gen(new_value);
+    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicLoad *) {
-    return IrInstGenIdAtomicLoad;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicStore *) {
-    return IrInstGenIdAtomicStore;
-}
+static IrInstGen *ir_build_fence_gen(IrAnalyze *ira, IrInst *source_instr, AtomicOrder order) {
+    IrInstGenFence *instruction = ir_build_inst_void<IrInstGenFence>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->order = order;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenSaveErrRetAddr *) {
-    return IrInstGenIdSaveErrRetAddr;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenVectorToArray *) {
-    return IrInstGenIdVectorToArray;
-}
-
-static constexpr IrInstGenId ir_inst_id(IrInstGenArrayToVector *) {
-    return IrInstGenIdArrayToVector;
-}
+static IrInstGen *ir_build_reduce_gen(IrAnalyze *ira, IrInst *source_instruction, ReduceOp op, IrInstGen *value, ZigType *result_type) {
+    IrInstGenReduce *instruction = ir_build_inst_gen<IrInstGenReduce>(&ira->new_irb,
+            source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = result_type;
+    instruction->op = op;
+    instruction->value = value;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenAssertZero *) {
-    return IrInstGenIdAssertZero;
-}
+    ir_ref_inst_gen(value);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenAssertNonNull *) {
-    return IrInstGenIdAssertNonNull;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenPtrOfArrayToSlice *) {
-    return IrInstGenIdPtrOfArrayToSlice;
+static void ir_set_cursor_at_end_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) {
+    assert(basic_block);
+    irb->current_basic_block = basic_block;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenSuspendBegin *) {
-    return IrInstGenIdSuspendBegin;
+static void ir_append_basic_block_gen(IrBuilderGen *irb, IrBasicBlockGen *bb) {
+    assert(!bb->already_appended);
+    bb->already_appended = true;
+    irb->exec->basic_block_list.append(bb);
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenSuspendFinish *) {
-    return IrInstGenIdSuspendFinish;
+static void ir_set_cursor_at_end_and_append_block_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) {
+    ir_append_basic_block_gen(irb, basic_block);
+    ir_set_cursor_at_end_gen(irb, basic_block);
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenAwait *) {
-    return IrInstGenIdAwait;
+static IrInstGen *ir_build_suspend_begin_gen(IrAnalyze *ira, IrInst *source_instr) {
+    IrInstGenSuspendBegin *inst = ir_build_inst_void<IrInstGenSuspendBegin>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    return &inst->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenResume *) {
-    return IrInstGenIdResume;
+static IrInstGen *ir_build_save_err_ret_addr_gen(IrAnalyze *ira, IrInst *source_instr) {
+    IrInstGenSaveErrRetAddr *inst = ir_build_inst_void<IrInstGenSaveErrRetAddr>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    return &inst->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenSpillBegin *) {
-    return IrInstGenIdSpillBegin;
-}
+static IrInstGen *ir_build_truncate_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *dest_type,
+        IrInstGen *target)
+{
+    IrInstGenTruncate *instruction = ir_build_inst_gen<IrInstGenTruncate>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = dest_type;
+    instruction->target = target;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenSpillEnd *) {
-    return IrInstGenIdSpillEnd;
-}
+    ir_ref_inst_gen(target);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenVectorExtractElem *) {
-    return IrInstGenIdVectorExtractElem;
+    return &instruction->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenAlloca *) {
-    return IrInstGenIdAlloca;
-}
+static IrInstGen *ir_build_shuffle_vector_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
+        ZigType *result_type, IrInstGen *a, IrInstGen *b, IrInstGen *mask)
+{
+    IrInstGenShuffleVector *inst = ir_build_inst_gen<IrInstGenShuffleVector>(&ira->new_irb, scope, source_node);
+    inst->base.value->type = result_type;
+    inst->a = a;
+    inst->b = b;
+    inst->mask = mask;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenConst *) {
-    return IrInstGenIdConst;
-}
+    ir_ref_inst_gen(a);
+    ir_ref_inst_gen(b);
+    ir_ref_inst_gen(mask);
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemorySize *) {
-  return IrInstGenIdWasmMemorySize;
+    return &inst->base;
 }
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemoryGrow *) {
-  return IrInstGenIdWasmMemoryGrow;
-}
+static IrInstGen *ir_build_splat_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type,
+    IrInstGen *scalar)
+{
+    IrInstGenSplat *instruction = ir_build_inst_gen<IrInstGenSplat>(
+            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = result_type;
+    instruction->scalar = scalar;
 
-static constexpr IrInstGenId ir_inst_id(IrInstGenExtern *) {
-    return IrInstGenIdExtern;
-}
+    ir_ref_inst_gen(scalar);
 
-template<typename T>
-static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    T *special_instruction = heap::c_allocator.create<T>();
-    special_instruction->base.id = ir_inst_id(special_instruction);
-    special_instruction->base.base.scope = scope;
-    special_instruction->base.base.source_node = source_node;
-    special_instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
-    special_instruction->base.owner_bb = irb->current_basic_block;
-    return special_instruction;
+    return &instruction->base;
 }
 
-template<typename T>
-static T *ir_create_inst_gen(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
-    T *special_instruction = heap::c_allocator.create<T>();
-    special_instruction->base.id = ir_inst_id(special_instruction);
-    special_instruction->base.base.scope = scope;
-    special_instruction->base.base.source_node = source_node;
-    special_instruction->base.base.debug_id = exec_next_debug_id_gen(irb->exec);
-    special_instruction->base.owner_bb = irb->current_basic_block;
-    special_instruction->base.value = irb->codegen->pass1_arena->create<ZigValue>();
-    return special_instruction;
-}
+static IrInstGen *ir_build_bool_not_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value) {
+    IrInstGenBoolNot *instruction = ir_build_inst_gen<IrInstGenBoolNot>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = ira->codegen->builtin_types.entry_bool;
+    instruction->value = value;
 
-template<typename T>
-static T *ir_create_inst_noval(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
-    T *special_instruction = heap::c_allocator.create<T>();
-    special_instruction->base.id = ir_inst_id(special_instruction);
-    special_instruction->base.base.scope = scope;
-    special_instruction->base.base.source_node = source_node;
-    special_instruction->base.base.debug_id = exec_next_debug_id_gen(irb->exec);
-    special_instruction->base.owner_bb = irb->current_basic_block;
-    return special_instruction;
-}
+    ir_ref_inst_gen(value);
 
-template<typename T>
-static T *ir_build_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    T *special_instruction = ir_create_instruction<T>(irb, scope, source_node);
-    ir_instruction_append(irb->current_basic_block, &special_instruction->base);
-    return special_instruction;
+    return &instruction->base;
 }
 
-template<typename T>
-static T *ir_build_inst_gen(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
-    T *special_instruction = ir_create_inst_gen<T>(irb, scope, source_node);
-    ir_inst_gen_append(irb->current_basic_block, &special_instruction->base);
-    return special_instruction;
-}
+static IrInstGen *ir_build_memset_gen(IrAnalyze *ira, IrInst *source_instr,
+    IrInstGen *dest_ptr, IrInstGen *byte, IrInstGen *count)
+{
+    IrInstGenMemset *instruction = ir_build_inst_void<IrInstGenMemset>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->dest_ptr = dest_ptr;
+    instruction->byte = byte;
+    instruction->count = count;
 
-template<typename T>
-static T *ir_build_inst_noreturn(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
-    T *special_instruction = ir_create_inst_noval<T>(irb, scope, source_node);
-    special_instruction->base.value = irb->codegen->intern.for_unreachable();
-    ir_inst_gen_append(irb->current_basic_block, &special_instruction->base);
-    return special_instruction;
-}
+    ir_ref_inst_gen(dest_ptr);
+    ir_ref_inst_gen(byte);
+    ir_ref_inst_gen(count);
 
-template<typename T>
-static T *ir_build_inst_void(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
-    T *special_instruction = ir_create_inst_noval<T>(irb, scope, source_node);
-    special_instruction->base.value = irb->codegen->intern.for_void();
-    ir_inst_gen_append(irb->current_basic_block, &special_instruction->base);
-    return special_instruction;
+    return &instruction->base;
 }
 
-IrInstGen *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn,
-        ZigType *var_type, const char *name_hint)
+static IrInstGen *ir_build_memcpy_gen(IrAnalyze *ira, IrInst *source_instr,
+    IrInstGen *dest_ptr, IrInstGen *src_ptr, IrInstGen *count)
 {
-    IrInstGenAlloca *alloca_gen = heap::c_allocator.create<IrInstGenAlloca>();
-    alloca_gen->base.id = IrInstGenIdAlloca;
-    alloca_gen->base.base.source_node = source_node;
-    alloca_gen->base.base.scope = scope;
-    alloca_gen->base.value = g->pass1_arena->create<ZigValue>();
-    alloca_gen->base.value->type = get_pointer_to_type(g, var_type, false);
-    alloca_gen->base.base.ref_count = 1;
-    alloca_gen->name_hint = name_hint;
-    fn->alloca_gen_list.append(alloca_gen);
-    return &alloca_gen->base;
+    IrInstGenMemcpy *instruction = ir_build_inst_void<IrInstGenMemcpy>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->dest_ptr = dest_ptr;
+    instruction->src_ptr = src_ptr;
+    instruction->count = count;
+
+    ir_ref_inst_gen(dest_ptr);
+    ir_ref_inst_gen(src_ptr);
+    ir_ref_inst_gen(count);
+
+    return &instruction->base;
 }
 
-static IrInstGen *ir_build_cast(IrAnalyze *ira, IrInst *source_instr,ZigType *dest_type,
-    IrInstGen *value, CastOp cast_op)
+static IrInstGen *ir_build_slice_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *slice_type,
+    IrInstGen *ptr, IrInstGen *start, IrInstGen *end, bool safety_check_on, IrInstGen *result_loc,
+    ZigValue *sentinel)
 {
-    IrInstGenCast *inst = ir_build_inst_gen<IrInstGenCast>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    inst->base.value->type = dest_type;
-    inst->value = value;
-    inst->cast_op = cast_op;
+    IrInstGenSlice *instruction = ir_build_inst_gen<IrInstGenSlice>(
+            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = slice_type;
+    instruction->ptr = ptr;
+    instruction->start = start;
+    instruction->end = end;
+    instruction->safety_check_on = safety_check_on;
+    instruction->result_loc = result_loc;
+    instruction->sentinel = sentinel;
 
-    ir_ref_inst_gen(value);
+    ir_ref_inst_gen(ptr);
+    ir_ref_inst_gen(start);
+    if (end != nullptr) ir_ref_inst_gen(end);
+    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
 
-    return &inst->base;
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_build_cond_br(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *condition,
-        IrBasicBlockSrc *then_block, IrBasicBlockSrc *else_block, IrInstSrc *is_comptime)
-{
-    IrInstSrcCondBr *inst = ir_build_instruction<IrInstSrcCondBr>(irb, scope, source_node);
-    inst->base.is_noreturn = true;
-    inst->condition = condition;
-    inst->then_block = then_block;
-    inst->else_block = else_block;
-    inst->is_comptime = is_comptime;
-
-    ir_ref_instruction(condition, irb->current_basic_block);
-    ir_ref_bb(then_block);
-    ir_ref_bb(else_block);
-    if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block);
+static IrInstGen *ir_build_breakpoint_gen(IrAnalyze *ira, IrInst *source_instr) {
+    IrInstGenBreakpoint *instruction = ir_build_inst_void<IrInstGenBreakpoint>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    return &instruction->base;
+}
 
+static IrInstGen *ir_build_return_address_gen(IrAnalyze *ira, IrInst *source_instr) {
+    IrInstGenReturnAddress *inst = ir_build_inst_gen<IrInstGenReturnAddress>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    inst->base.value->type = ira->codegen->builtin_types.entry_usize;
     return &inst->base;
 }
 
-static IrInstGen *ir_build_cond_br_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *condition,
-        IrBasicBlockGen *then_block, IrBasicBlockGen *else_block)
-{
-    IrInstGenCondBr *inst = ir_build_inst_noreturn<IrInstGenCondBr>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    inst->condition = condition;
-    inst->then_block = then_block;
-    inst->else_block = else_block;
-
-    ir_ref_inst_gen(condition);
+static IrInstGen *ir_build_frame_address_gen(IrAnalyze *ira, IrInst *source_instr) {
+    IrInstGenFrameAddress *inst = ir_build_inst_gen<IrInstGenFrameAddress>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    inst->base.value->type = ira->codegen->builtin_types.entry_usize;
+    return &inst->base;
+}
 
+static IrInstGen *ir_build_handle_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *ty) {
+    IrInstGenFrameHandle *inst = ir_build_inst_gen<IrInstGenFrameHandle>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    inst->base.value->type = ty;
     return &inst->base;
 }
 
-static IrInstSrc *ir_build_return_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand) {
-    IrInstSrcReturn *inst = ir_build_instruction<IrInstSrcReturn>(irb, scope, source_node);
-    inst->base.is_noreturn = true;
-    inst->operand = operand;
+static IrInstGen *ir_build_frame_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *fn)
+{
+    IrInstGenFrameSize *inst = ir_build_inst_gen<IrInstGenFrameSize>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    inst->base.value->type = ira->codegen->builtin_types.entry_usize;
+    inst->fn = fn;
 
-    if (operand != nullptr) ir_ref_instruction(operand, irb->current_basic_block);
+    ir_ref_inst_gen(fn);
 
     return &inst->base;
 }
 
-static IrInstGen *ir_build_return_gen(IrAnalyze *ira, IrInst *source_inst, IrInstGen *operand) {
-    IrInstGenReturn *inst = ir_build_inst_noreturn<IrInstGenReturn>(&ira->new_irb,
-            source_inst->scope, source_inst->source_node);
-    inst->operand = operand;
-
-    if (operand != nullptr) ir_ref_inst_gen(operand);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_const_void(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
-    ir_instruction_append(irb->current_basic_block, &const_instruction->base);
-    const_instruction->value = irb->codegen->intern.for_void();
-    return &const_instruction->base;
-}
-
-static IrInstSrc *ir_build_const_undefined(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
-    ir_instruction_append(irb->current_basic_block, &const_instruction->base);
-    const_instruction->value = irb->codegen->intern.for_undefined();
-    const_instruction->value->special = ConstValSpecialUndef;
-    return &const_instruction->base;
-}
-
-static IrInstSrc *ir_build_const_uint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) {
-    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
-    const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int;
-    const_instruction->value->special = ConstValSpecialStatic;
-    bigint_init_unsigned(&const_instruction->value->data.x_bigint, value);
-    return &const_instruction->base;
-}
+static IrInstGen *ir_build_overflow_op_gen(IrAnalyze *ira, IrInst *source_instr,
+        IrOverflowOp op, IrInstGen *op1, IrInstGen *op2, IrInstGen *result_ptr,
+        ZigType *result_ptr_type)
+{
+    IrInstGenOverflowOp *instruction = ir_build_inst_gen<IrInstGenOverflowOp>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = ira->codegen->builtin_types.entry_bool;
+    instruction->op = op;
+    instruction->op1 = op1;
+    instruction->op2 = op2;
+    instruction->result_ptr = result_ptr;
+    instruction->result_ptr_type = result_ptr_type;
 
-static IrInstSrc *ir_build_const_bigint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, BigInt *bigint) {
-    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
-    const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int;
-    const_instruction->value->special = ConstValSpecialStatic;
-    bigint_init_bigint(&const_instruction->value->data.x_bigint, bigint);
-    return &const_instruction->base;
-}
+    ir_ref_inst_gen(op1);
+    ir_ref_inst_gen(op2);
+    ir_ref_inst_gen(result_ptr);
 
-static IrInstSrc *ir_build_const_bigfloat(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, BigFloat *bigfloat) {
-    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
-    const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_float;
-    const_instruction->value->special = ConstValSpecialStatic;
-    bigfloat_init_bigfloat(&const_instruction->value->data.x_bigfloat, bigfloat);
-    return &const_instruction->base;
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_build_const_null(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
-    ir_instruction_append(irb->current_basic_block, &const_instruction->base);
-    const_instruction->value = irb->codegen->intern.for_null();
-    return &const_instruction->base;
-}
+static IrInstGen *ir_build_float_op_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand,
+        BuiltinFnId fn_id, ZigType *operand_type)
+{
+    IrInstGenFloatOp *instruction = ir_build_inst_gen<IrInstGenFloatOp>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = operand_type;
+    instruction->operand = operand;
+    instruction->fn_id = fn_id;
 
-static IrInstSrc *ir_build_const_usize(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) {
-    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
-    const_instruction->value->type = irb->codegen->builtin_types.entry_usize;
-    const_instruction->value->special = ConstValSpecialStatic;
-    bigint_init_unsigned(&const_instruction->value->data.x_bigint, value);
-    return &const_instruction->base;
-}
+    ir_ref_inst_gen(operand);
 
-static IrInstSrc *ir_create_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        ZigType *type_entry)
-{
-    IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
-    const_instruction->value->type = irb->codegen->builtin_types.entry_type;
-    const_instruction->value->special = ConstValSpecialStatic;
-    const_instruction->value->data.x_type = type_entry;
-    return &const_instruction->base;
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_build_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        ZigType *type_entry)
+static IrInstGen *ir_build_mul_add_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2,
+        IrInstGen *op3, ZigType *expr_type)
 {
-    IrInstSrc *instruction = ir_create_const_type(irb, scope, source_node, type_entry);
-    ir_instruction_append(irb->current_basic_block, instruction);
-    return instruction;
-}
-
-static IrInstSrc *ir_build_const_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigType *import) {
-    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
-    const_instruction->value->type = irb->codegen->builtin_types.entry_type;
-    const_instruction->value->special = ConstValSpecialStatic;
-    const_instruction->value->data.x_type = import;
-    return &const_instruction->base;
-}
+    IrInstGenMulAdd *instruction = ir_build_inst_gen<IrInstGenMulAdd>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = expr_type;
+    instruction->op1 = op1;
+    instruction->op2 = op2;
+    instruction->op3 = op3;
 
-static IrInstSrc *ir_build_const_bool(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, bool value) {
-    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
-    const_instruction->value->type = irb->codegen->builtin_types.entry_bool;
-    const_instruction->value->special = ConstValSpecialStatic;
-    const_instruction->value->data.x_bool = value;
-    return &const_instruction->base;
-}
+    ir_ref_inst_gen(op1);
+    ir_ref_inst_gen(op2);
+    ir_ref_inst_gen(op3);
 
-static IrInstSrc *ir_build_const_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) {
-    IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
-    const_instruction->value->type = irb->codegen->builtin_types.entry_enum_literal;
-    const_instruction->value->special = ConstValSpecialStatic;
-    const_instruction->value->data.x_enum_literal = name;
-    return &const_instruction->base;
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_create_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) {
-    IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
-    const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
-    init_const_str_lit(irb->codegen, const_instruction->value, str);
+static IrInstGen *ir_build_test_err_gen(IrAnalyze *ira, IrInst *source_instruction, IrInstGen *err_union) {
+    IrInstGenTestErr *instruction = ir_build_inst_gen<IrInstGenTestErr>(
+            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = ira->codegen->builtin_types.entry_bool;
+    instruction->err_union = err_union;
 
-    return &const_instruction->base;
-}
+    ir_ref_inst_gen(err_union);
 
-static IrInstSrc *ir_build_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) {
-    IrInstSrc *instruction = ir_create_const_str_lit(irb, scope, source_node, str);
-    ir_instruction_append(irb->current_basic_block, instruction);
-    return instruction;
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_build_bin_op(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrBinOp op_id,
-        IrInstSrc *op1, IrInstSrc *op2, bool safety_check_on)
+static IrInstGen *ir_build_unwrap_err_code_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
+    IrInstGen *err_union_ptr, ZigType *result_type)
 {
-    IrInstSrcBinOp *inst = ir_build_instruction<IrInstSrcBinOp>(irb, scope, source_node);
-    inst->op_id = op_id;
-    inst->op1 = op1;
-    inst->op2 = op2;
-    inst->safety_check_on = safety_check_on;
+    IrInstGenUnwrapErrCode *inst = ir_build_inst_gen<IrInstGenUnwrapErrCode>(&ira->new_irb, scope, source_node);
+    inst->base.value->type = result_type;
+    inst->err_union_ptr = err_union_ptr;
 
-    ir_ref_instruction(op1, irb->current_basic_block);
-    ir_ref_instruction(op2, irb->current_basic_block);
+    ir_ref_inst_gen(err_union_ptr);
 
     return &inst->base;
 }
 
-static IrInstGen *ir_build_bin_op_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *res_type,
-        IrBinOp op_id, IrInstGen *op1, IrInstGen *op2, bool safety_check_on)
+static IrInstGen *ir_build_unwrap_err_payload_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
+    IrInstGen *value, bool safety_check_on, bool initializing, ZigType *result_type)
 {
-    IrInstGenBinOp *inst = ir_build_inst_gen<IrInstGenBinOp>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    inst->base.value->type = res_type;
-    inst->op_id = op_id;
-    inst->op1 = op1;
-    inst->op2 = op2;
+    IrInstGenUnwrapErrPayload *inst = ir_build_inst_gen<IrInstGenUnwrapErrPayload>(&ira->new_irb, scope, source_node);
+    inst->base.value->type = result_type;
+    inst->value = value;
     inst->safety_check_on = safety_check_on;
+    inst->initializing = initializing;
 
-    ir_ref_inst_gen(op1);
-    ir_ref_inst_gen(op2);
+    ir_ref_inst_gen(value);
 
     return &inst->base;
 }
 
-
-static IrInstSrc *ir_build_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *op1, IrInstSrc *op2, Buf *type_name)
+static IrInstGen *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInst *source_instruction,
+        ZigType *ptr_type, IrInstGen *ptr, bool safety_check_on)
 {
-    IrInstSrcMergeErrSets *inst = ir_build_instruction<IrInstSrcMergeErrSets>(irb, scope, source_node);
-    inst->op1 = op1;
-    inst->op2 = op2;
-    inst->type_name = type_name;
+    IrInstGenPtrCast *instruction = ir_build_inst_gen<IrInstGenPtrCast>(
+            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = ptr_type;
+    instruction->ptr = ptr;
+    instruction->safety_check_on = safety_check_on;
 
-    ir_ref_instruction(op1, irb->current_basic_block);
-    ir_ref_instruction(op2, irb->current_basic_block);
+    ir_ref_inst_gen(ptr);
 
-    return &inst->base;
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_build_var_ptr_x(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var,
-        ScopeFnDef *crossed_fndef_scope)
+static IrInstGen *ir_build_bit_cast_gen(IrAnalyze *ira, IrInst *source_instruction,
+        IrInstGen *operand, ZigType *ty)
 {
-    IrInstSrcVarPtr *instruction = ir_build_instruction<IrInstSrcVarPtr>(irb, scope, source_node);
-    instruction->var = var;
-    instruction->crossed_fndef_scope = crossed_fndef_scope;
+    IrInstGenBitCast *instruction = ir_build_inst_gen<IrInstGenBitCast>(
+            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = ty;
+    instruction->operand = operand;
 
-    ir_ref_var(var);
+    ir_ref_inst_gen(operand);
 
     return &instruction->base;
 }
 
-static IrInstSrc *ir_build_var_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var) {
-    return ir_build_var_ptr_x(irb, scope, source_node, var, nullptr);
-}
-
-static IrInstGen *ir_build_var_ptr_gen(IrAnalyze *ira, IrInst *source_instr, ZigVar *var) {
-    IrInstGenVarPtr *instruction = ir_build_inst_gen<IrInstGenVarPtr>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    instruction->var = var;
+static IrInstGen *ir_build_widen_or_shorten(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target,
+        ZigType *result_type)
+{
+    IrInstGenWidenOrShorten *inst = ir_build_inst_gen<IrInstGenWidenOrShorten>(&ira->new_irb, scope, source_node);
+    inst->base.value->type = result_type;
+    inst->target = target;
 
-    ir_ref_var(var);
+    ir_ref_inst_gen(target);
 
-    return &instruction->base;
+    return &inst->base;
 }
 
-static IrInstGen *ir_build_return_ptr(IrAnalyze *ira, Scope *scope, AstNode *source_node, ZigType *ty) {
-    IrInstGenReturnPtr *instruction = ir_build_inst_gen<IrInstGenReturnPtr>(&ira->new_irb, scope, source_node);
-    instruction->base.value->type = ty;
+static IrInstGen *ir_build_int_to_ptr_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
+        IrInstGen *target, ZigType *ptr_type)
+{
+    IrInstGenIntToPtr *instruction = ir_build_inst_gen<IrInstGenIntToPtr>(&ira->new_irb, scope, source_node);
+    instruction->base.value->type = ptr_type;
+    instruction->target = target;
+
+    ir_ref_inst_gen(target);
+
     return &instruction->base;
 }
 
-static IrInstSrc *ir_build_elem_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *array_ptr, IrInstSrc *elem_index, bool safety_check_on, PtrLen ptr_len,
-        AstNode *init_array_type_source_node)
-{
-    IrInstSrcElemPtr *instruction = ir_build_instruction<IrInstSrcElemPtr>(irb, scope, source_node);
-    instruction->array_ptr = array_ptr;
-    instruction->elem_index = elem_index;
-    instruction->safety_check_on = safety_check_on;
-    instruction->ptr_len = ptr_len;
-    instruction->init_array_type_source_node = init_array_type_source_node;
+static IrInstGen *ir_build_ptr_to_int_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target) {
+    IrInstGenPtrToInt *inst = ir_build_inst_gen<IrInstGenPtrToInt>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    inst->base.value->type = ira->codegen->builtin_types.entry_usize;
+    inst->target = target;
 
-    ir_ref_instruction(array_ptr, irb->current_basic_block);
-    ir_ref_instruction(elem_index, irb->current_basic_block);
+    ir_ref_inst_gen(target);
 
-    return &instruction->base;
+    return &inst->base;
 }
 
-static IrInstGen *ir_build_elem_ptr_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
-        IrInstGen *array_ptr, IrInstGen *elem_index, bool safety_check_on, ZigType *return_type)
+static IrInstGen *ir_build_int_to_enum_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
+        ZigType *dest_type, IrInstGen *target)
 {
-    IrInstGenElemPtr *instruction = ir_build_inst_gen<IrInstGenElemPtr>(&ira->new_irb, scope, source_node);
-    instruction->base.value->type = return_type;
-    instruction->array_ptr = array_ptr;
-    instruction->elem_index = elem_index;
-    instruction->safety_check_on = safety_check_on;
+    IrInstGenIntToEnum *instruction = ir_build_inst_gen<IrInstGenIntToEnum>(&ira->new_irb, scope, source_node);
+    instruction->base.value->type = dest_type;
+    instruction->target = target;
 
-    ir_ref_inst_gen(array_ptr);
-    ir_ref_inst_gen(elem_index);
+    ir_ref_inst_gen(target);
 
     return &instruction->base;
 }
 
-static IrInstSrc *ir_build_field_ptr_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *container_ptr, IrInstSrc *field_name_expr, bool initializing)
+static IrInstGen *ir_build_int_to_err_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target,
+        ZigType *wanted_type)
 {
-    IrInstSrcFieldPtr *instruction = ir_build_instruction<IrInstSrcFieldPtr>(irb, scope, source_node);
-    instruction->container_ptr = container_ptr;
-    instruction->field_name_buffer = nullptr;
-    instruction->field_name_expr = field_name_expr;
-    instruction->initializing = initializing;
+    IrInstGenIntToErr *instruction = ir_build_inst_gen<IrInstGenIntToErr>(&ira->new_irb, scope, source_node);
+    instruction->base.value->type = wanted_type;
+    instruction->target = target;
 
-    ir_ref_instruction(container_ptr, irb->current_basic_block);
-    ir_ref_instruction(field_name_expr, irb->current_basic_block);
+    ir_ref_inst_gen(target);
 
     return &instruction->base;
 }
 
-static IrInstSrc *ir_build_field_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *container_ptr, Buf *field_name, bool initializing)
+static IrInstGen *ir_build_err_to_int_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target,
+        ZigType *wanted_type)
 {
-    IrInstSrcFieldPtr *instruction = ir_build_instruction<IrInstSrcFieldPtr>(irb, scope, source_node);
-    instruction->container_ptr = container_ptr;
-    instruction->field_name_buffer = field_name;
-    instruction->field_name_expr = nullptr;
-    instruction->initializing = initializing;
+    IrInstGenErrToInt *instruction = ir_build_inst_gen<IrInstGenErrToInt>(&ira->new_irb, scope, source_node);
+    instruction->base.value->type = wanted_type;
+    instruction->target = target;
 
-    ir_ref_instruction(container_ptr, irb->current_basic_block);
+    ir_ref_inst_gen(target);
 
     return &instruction->base;
 }
 
-static IrInstSrc *ir_build_has_field(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *container_type, IrInstSrc *field_name)
-{
-    IrInstSrcHasField *instruction = ir_build_instruction<IrInstSrcHasField>(irb, scope, source_node);
-    instruction->container_type = container_type;
-    instruction->field_name = field_name;
+static IrInstGen *ir_build_panic_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *msg) {
+    IrInstGenPanic *instruction = ir_build_inst_noreturn<IrInstGenPanic>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->msg = msg;
 
-    ir_ref_instruction(container_type, irb->current_basic_block);
-    ir_ref_instruction(field_name, irb->current_basic_block);
+    ir_ref_inst_gen(msg);
 
     return &instruction->base;
 }
 
-static IrInstGen *ir_build_struct_field_ptr(IrAnalyze *ira, IrInst *source_instr,
-    IrInstGen *struct_ptr, TypeStructField *field, ZigType *ptr_type)
+static IrInstGen *ir_build_tag_name_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target,
+        ZigType *result_type)
 {
-    IrInstGenStructFieldPtr *inst = ir_build_inst_gen<IrInstGenStructFieldPtr>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    inst->base.value->type = ptr_type;
-    inst->struct_ptr = struct_ptr;
-    inst->field = field;
+    IrInstGenTagName *instruction = ir_build_inst_gen<IrInstGenTagName>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = result_type;
+    instruction->target = target;
 
-    ir_ref_inst_gen(struct_ptr);
+    ir_ref_inst_gen(target);
 
-    return &inst->base;
+    return &instruction->base;
 }
 
-static IrInstGen *ir_build_union_field_ptr(IrAnalyze *ira, IrInst *source_instr,
-    IrInstGen *union_ptr, TypeUnionField *field, bool safety_check_on, bool initializing, ZigType *ptr_type)
+static IrInstGen *ir_build_field_parent_ptr_gen(IrAnalyze *ira, IrInst *source_instr,
+        IrInstGen *field_ptr, TypeStructField *field, ZigType *result_type)
 {
-    IrInstGenUnionFieldPtr *inst = ir_build_inst_gen<IrInstGenUnionFieldPtr>(&ira->new_irb,
+    IrInstGenFieldParentPtr *inst = ir_build_inst_gen<IrInstGenFieldParentPtr>(&ira->new_irb,
             source_instr->scope, source_instr->source_node);
-    inst->base.value->type = ptr_type;
-    inst->initializing = initializing;
-    inst->safety_check_on = safety_check_on;
-    inst->union_ptr = union_ptr;
+    inst->base.value->type = result_type;
+    inst->field_ptr = field_ptr;
     inst->field = field;
 
-    ir_ref_inst_gen(union_ptr);
+    ir_ref_inst_gen(field_ptr);
 
     return &inst->base;
 }
 
-static IrInstSrc *ir_build_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc *args, ResultLoc *result_loc)
+static IrInstGen *ir_build_align_cast_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target,
+        ZigType *result_type)
 {
-    IrInstSrcCallExtra *call_instruction = ir_build_instruction<IrInstSrcCallExtra>(irb, scope, source_node);
-    call_instruction->options = options;
-    call_instruction->fn_ref = fn_ref;
-    call_instruction->args = args;
-    call_instruction->result_loc = result_loc;
+    IrInstGenAlignCast *instruction = ir_build_inst_gen<IrInstGenAlignCast>(&ira->new_irb, scope, source_node);
+    instruction->base.value->type = result_type;
+    instruction->target = target;
 
-    ir_ref_instruction(options, irb->current_basic_block);
-    ir_ref_instruction(fn_ref, irb->current_basic_block);
-    ir_ref_instruction(args, irb->current_basic_block);
+    ir_ref_inst_gen(target);
 
-    return &call_instruction->base;
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_build_async_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        CallModifier modifier, IrInstSrc *fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstSrc *args, ResultLoc *result_loc)
+static IrInstGen *ir_build_error_return_trace_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
+        IrInstErrorReturnTraceOptional optional, ZigType *result_type)
 {
-    IrInstSrcAsyncCallExtra *call_instruction = ir_build_instruction<IrInstSrcAsyncCallExtra>(irb, scope, source_node);
-    call_instruction->modifier = modifier;
-    call_instruction->fn_ref = fn_ref;
-    call_instruction->ret_ptr = ret_ptr;
-    call_instruction->new_stack = new_stack;
-    call_instruction->args = args;
-    call_instruction->result_loc = result_loc;
-
-    ir_ref_instruction(fn_ref, irb->current_basic_block);
-    if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block);
-    ir_ref_instruction(new_stack, irb->current_basic_block);
-    ir_ref_instruction(args, irb->current_basic_block);
+    IrInstGenErrorReturnTrace *inst = ir_build_inst_gen<IrInstGenErrorReturnTrace>(&ira->new_irb, scope, source_node);
+    inst->base.value->type = result_type;
+    inst->optional = optional;
 
-    return &call_instruction->base;
+    return &inst->base;
 }
 
-static IrInstSrc *ir_build_call_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc **args_ptr, size_t args_len,
-        ResultLoc *result_loc)
+static IrInstGen *ir_build_atomic_rmw_gen(IrAnalyze *ira, IrInst *source_instr,
+        IrInstGen *ptr, IrInstGen *operand, AtomicRmwOp op, AtomicOrder ordering, ZigType *operand_type)
 {
-    IrInstSrcCallArgs *call_instruction = ir_build_instruction<IrInstSrcCallArgs>(irb, scope, source_node);
-    call_instruction->options = options;
-    call_instruction->fn_ref = fn_ref;
-    call_instruction->args_ptr = args_ptr;
-    call_instruction->args_len = args_len;
-    call_instruction->result_loc = result_loc;
-
-    ir_ref_instruction(options, irb->current_basic_block);
-    ir_ref_instruction(fn_ref, irb->current_basic_block);
-    for (size_t i = 0; i < args_len; i += 1)
-        ir_ref_instruction(args_ptr[i], irb->current_basic_block);
-
-    return &call_instruction->base;
-}
-
-static IrInstSrc *ir_build_call_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        ZigFn *fn_entry, IrInstSrc *fn_ref, size_t arg_count, IrInstSrc **args,
-        IrInstSrc *ret_ptr, CallModifier modifier, bool is_async_call_builtin,
-        IrInstSrc *new_stack, ResultLoc *result_loc)
-{
-    IrInstSrcCall *call_instruction = ir_build_instruction<IrInstSrcCall>(irb, scope, source_node);
-    call_instruction->fn_entry = fn_entry;
-    call_instruction->fn_ref = fn_ref;
-    call_instruction->args = args;
-    call_instruction->arg_count = arg_count;
-    call_instruction->modifier = modifier;
-    call_instruction->is_async_call_builtin = is_async_call_builtin;
-    call_instruction->new_stack = new_stack;
-    call_instruction->result_loc = result_loc;
-    call_instruction->ret_ptr = ret_ptr;
-
-    if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block);
-    for (size_t i = 0; i < arg_count; i += 1)
-        ir_ref_instruction(args[i], irb->current_basic_block);
-    if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block);
-    if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block);
-
-    return &call_instruction->base;
-}
-
-static IrInstGenCall *ir_build_call_gen(IrAnalyze *ira, IrInst *source_instruction,
-        ZigFn *fn_entry, IrInstGen *fn_ref, size_t arg_count, IrInstGen **args,
-        CallModifier modifier, IrInstGen *new_stack, bool is_async_call_builtin,
-        IrInstGen *result_loc, ZigType *return_type)
-{
-    IrInstGenCall *call_instruction = ir_build_inst_gen<IrInstGenCall>(&ira->new_irb,
-            source_instruction->scope, source_instruction->source_node);
-    call_instruction->base.value->type = return_type;
-    call_instruction->fn_entry = fn_entry;
-    call_instruction->fn_ref = fn_ref;
-    call_instruction->args = args;
-    call_instruction->arg_count = arg_count;
-    call_instruction->modifier = modifier;
-    call_instruction->is_async_call_builtin = is_async_call_builtin;
-    call_instruction->new_stack = new_stack;
-    call_instruction->result_loc = result_loc;
-
-    if (fn_ref != nullptr) ir_ref_inst_gen(fn_ref);
-    for (size_t i = 0; i < arg_count; i += 1)
-        ir_ref_inst_gen(args[i]);
-    if (new_stack != nullptr) ir_ref_inst_gen(new_stack);
-    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
-
-    return call_instruction;
-}
-
-static IrInstSrc *ir_build_phi(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        size_t incoming_count, IrBasicBlockSrc **incoming_blocks, IrInstSrc **incoming_values,
-        ResultLocPeerParent *peer_parent)
-{
-    assert(incoming_count != 0);
-    assert(incoming_count != SIZE_MAX);
-
-    IrInstSrcPhi *phi_instruction = ir_build_instruction<IrInstSrcPhi>(irb, scope, source_node);
-    phi_instruction->incoming_count = incoming_count;
-    phi_instruction->incoming_blocks = incoming_blocks;
-    phi_instruction->incoming_values = incoming_values;
-    phi_instruction->peer_parent = peer_parent;
-
-    for (size_t i = 0; i < incoming_count; i += 1) {
-        ir_ref_bb(incoming_blocks[i]);
-        ir_ref_instruction(incoming_values[i], irb->current_basic_block);
-    }
-
-    return &phi_instruction->base;
-}
-
-static IrInstGen *ir_build_phi_gen(IrAnalyze *ira, IrInst *source_instr, size_t incoming_count,
-        IrBasicBlockGen **incoming_blocks, IrInstGen **incoming_values, ZigType *result_type)
-{
-    assert(incoming_count != 0);
-    assert(incoming_count != SIZE_MAX);
-
-    IrInstGenPhi *phi_instruction = ir_build_inst_gen<IrInstGenPhi>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    phi_instruction->base.value->type = result_type;
-    phi_instruction->incoming_count = incoming_count;
-    phi_instruction->incoming_blocks = incoming_blocks;
-    phi_instruction->incoming_values = incoming_values;
-
-    for (size_t i = 0; i < incoming_count; i += 1) {
-        ir_ref_inst_gen(incoming_values[i]);
-    }
-
-    return &phi_instruction->base;
-}
-
-static IrInstSrc *ir_build_br(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrBasicBlockSrc *dest_block, IrInstSrc *is_comptime)
-{
-    IrInstSrcBr *inst = ir_build_instruction<IrInstSrcBr>(irb, scope, source_node);
-    inst->base.is_noreturn = true;
-    inst->dest_block = dest_block;
-    inst->is_comptime = is_comptime;
-
-    ir_ref_bb(dest_block);
-    if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_br_gen(IrAnalyze *ira, IrInst *source_instr, IrBasicBlockGen *dest_block) {
-    IrInstGenBr *inst = ir_build_inst_noreturn<IrInstGenBr>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    inst->dest_block = dest_block;
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_ptr_type_simple(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *child_type, bool is_const)
-{
-    IrInstSrcPtrTypeSimple *inst = heap::c_allocator.create<IrInstSrcPtrTypeSimple>();
-    inst->base.id = is_const ? IrInstSrcIdPtrTypeSimpleConst : IrInstSrcIdPtrTypeSimple;
-    inst->base.base.scope = scope;
-    inst->base.base.source_node = source_node;
-    inst->base.base.debug_id = exec_next_debug_id(irb->exec);
-    inst->base.owner_bb = irb->current_basic_block;
-    ir_instruction_append(irb->current_basic_block, &inst->base);
-
-    inst->child_type = child_type;
-
-    ir_ref_instruction(child_type, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len,
-        IrInstSrc *sentinel, IrInstSrc *align_value,
-        uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero)
-{
-    if (!is_volatile && ptr_len == PtrLenSingle && sentinel == nullptr && align_value == nullptr &&
-            bit_offset_start == 0 && host_int_bytes == 0 && is_allow_zero == 0)
-    {
-        return ir_build_ptr_type_simple(irb, scope, source_node, child_type, is_const);
-    }
-
-    IrInstSrcPtrType *inst = ir_build_instruction<IrInstSrcPtrType>(irb, scope, source_node);
-    inst->sentinel = sentinel;
-    inst->align_value = align_value;
-    inst->child_type = child_type;
-    inst->is_const = is_const;
-    inst->is_volatile = is_volatile;
-    inst->ptr_len = ptr_len;
-    inst->bit_offset_start = bit_offset_start;
-    inst->host_int_bytes = host_int_bytes;
-    inst->is_allow_zero = is_allow_zero;
-
-    if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block);
-    if (align_value) ir_ref_instruction(align_value, irb->current_basic_block);
-    ir_ref_instruction(child_type, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_un_op_lval(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrUnOp op_id,
-        IrInstSrc *value, LVal lval, ResultLoc *result_loc)
-{
-    IrInstSrcUnOp *instruction = ir_build_instruction<IrInstSrcUnOp>(irb, scope, source_node);
-    instruction->op_id = op_id;
-    instruction->value = value;
-    instruction->lval = lval;
-    instruction->result_loc = result_loc;
-
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_un_op(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrUnOp op_id,
-        IrInstSrc *value)
-{
-    return ir_build_un_op_lval(irb, scope, source_node, op_id, value, LValNone, nullptr);
-}
-
-static IrInstGen *ir_build_negation(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, ZigType *expr_type, bool wrapping) {
-    IrInstGenNegation *instruction = ir_build_inst_gen<IrInstGenNegation>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = expr_type;
-    instruction->operand = operand;
-    instruction->wrapping = wrapping;
-
-    ir_ref_inst_gen(operand);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_binary_not(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand,
-        ZigType *expr_type)
-{
-    IrInstGenBinaryNot *instruction = ir_build_inst_gen<IrInstGenBinaryNot>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = expr_type;
-    instruction->operand = operand;
-
-    ir_ref_inst_gen(operand);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_container_init_list(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        size_t item_count, IrInstSrc **elem_result_loc_list, IrInstSrc *result_loc,
-        AstNode *init_array_type_source_node)
-{
-    IrInstSrcContainerInitList *container_init_list_instruction =
-        ir_build_instruction<IrInstSrcContainerInitList>(irb, scope, source_node);
-    container_init_list_instruction->item_count = item_count;
-    container_init_list_instruction->elem_result_loc_list = elem_result_loc_list;
-    container_init_list_instruction->result_loc = result_loc;
-    container_init_list_instruction->init_array_type_source_node = init_array_type_source_node;
-
-    for (size_t i = 0; i < item_count; i += 1) {
-        ir_ref_instruction(elem_result_loc_list[i], irb->current_basic_block);
-    }
-    if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
-
-    return &container_init_list_instruction->base;
-}
-
-static IrInstSrc *ir_build_container_init_fields(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        size_t field_count, IrInstSrcContainerInitFieldsField *fields, IrInstSrc *result_loc)
-{
-    IrInstSrcContainerInitFields *container_init_fields_instruction =
-        ir_build_instruction<IrInstSrcContainerInitFields>(irb, scope, source_node);
-    container_init_fields_instruction->field_count = field_count;
-    container_init_fields_instruction->fields = fields;
-    container_init_fields_instruction->result_loc = result_loc;
-
-    for (size_t i = 0; i < field_count; i += 1) {
-        ir_ref_instruction(fields[i].result_loc, irb->current_basic_block);
-    }
-    if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
-
-    return &container_init_fields_instruction->base;
-}
-
-static IrInstSrc *ir_build_unreachable(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    IrInstSrcUnreachable *inst = ir_build_instruction<IrInstSrcUnreachable>(irb, scope, source_node);
-    inst->base.is_noreturn = true;
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_unreachable_gen(IrAnalyze *ira, IrInst *source_instr) {
-    IrInstGenUnreachable *inst = ir_build_inst_noreturn<IrInstGenUnreachable>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    return &inst->base;
-}
-
-static IrInstSrcStorePtr *ir_build_store_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *ptr, IrInstSrc *value)
-{
-    IrInstSrcStorePtr *instruction = ir_build_instruction<IrInstSrcStorePtr>(irb, scope, source_node);
-    instruction->ptr = ptr;
-    instruction->value = value;
-
-    ir_ref_instruction(ptr, irb->current_basic_block);
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return instruction;
-}
-
-static IrInstGen *ir_build_store_ptr_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *ptr, IrInstGen *value) {
-    IrInstGenStorePtr *instruction = ir_build_inst_void<IrInstGenStorePtr>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
+    IrInstGenAtomicRmw *instruction = ir_build_inst_gen<IrInstGenAtomicRmw>(&ira->new_irb, source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = operand_type;
     instruction->ptr = ptr;
-    instruction->value = value;
+    instruction->op = op;
+    instruction->operand = operand;
+    instruction->ordering = ordering;
 
     ir_ref_inst_gen(ptr);
-    ir_ref_inst_gen(value);
+    ir_ref_inst_gen(operand);
 
     return &instruction->base;
 }
 
-static IrInstGen *ir_build_vector_store_elem(IrAnalyze *ira, IrInst *src_inst,
-        IrInstGen *vector_ptr, IrInstGen *index, IrInstGen *value)
-{
-    IrInstGenVectorStoreElem *inst = ir_build_inst_void<IrInstGenVectorStoreElem>(
-            &ira->new_irb, src_inst->scope, src_inst->source_node);
-    inst->vector_ptr = vector_ptr;
-    inst->index = index;
-    inst->value = value;
-
-    ir_ref_inst_gen(vector_ptr);
-    ir_ref_inst_gen(index);
-    ir_ref_inst_gen(value);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_var_decl_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        ZigVar *var, IrInstSrc *align_value, IrInstSrc *ptr)
-{
-    IrInstSrcDeclVar *inst = ir_build_instruction<IrInstSrcDeclVar>(irb, scope, source_node);
-    inst->var = var;
-    inst->align_value = align_value;
-    inst->ptr = ptr;
-
-    if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
-    ir_ref_instruction(ptr, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_var_decl_gen(IrAnalyze *ira, IrInst *source_instruction,
-        ZigVar *var, IrInstGen *var_ptr)
-{
-    IrInstGenDeclVar *inst = ir_build_inst_gen<IrInstGenDeclVar>(&ira->new_irb,
-            source_instruction->scope, source_instruction->source_node);
-    inst->base.value->special = ConstValSpecialStatic;
-    inst->base.value->type = ira->codegen->builtin_types.entry_void;
-    inst->var = var;
-    inst->var_ptr = var_ptr;
-
-    ir_ref_inst_gen(var_ptr);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_export(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *target, IrInstSrc *options)
-{
-    IrInstSrcExport *export_instruction = ir_build_instruction<IrInstSrcExport>(
-            irb, scope, source_node);
-    export_instruction->target = target;
-    export_instruction->options = options;
-
-    ir_ref_instruction(target, irb->current_basic_block);
-    ir_ref_instruction(options, irb->current_basic_block);
-
-    return &export_instruction->base;
-}
-
-static IrInstSrc *ir_build_extern(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *type, IrInstSrc *options)
-{
-    IrInstSrcExtern *extern_instruction = ir_build_instruction<IrInstSrcExtern>(
-            irb, scope, source_node);
-    extern_instruction->type = type;
-    extern_instruction->options = options;
-
-    ir_ref_instruction(type, irb->current_basic_block);
-    ir_ref_instruction(options, irb->current_basic_block);
-
-    return &extern_instruction->base;
-}
-
-static IrInstGen *ir_build_extern_gen(IrAnalyze *ira, IrInst *source_instr, Buf *name,
-        GlobalLinkageId linkage, bool is_thread_local, ZigType *expr_type)
+static IrInstGen *ir_build_atomic_load_gen(IrAnalyze *ira, IrInst *source_instr,
+        IrInstGen *ptr, AtomicOrder ordering, ZigType *operand_type)
 {
-    IrInstGenExtern *instruction = ir_build_inst_gen<IrInstGenExtern>(&ira->new_irb,
+    IrInstGenAtomicLoad *instruction = ir_build_inst_gen<IrInstGenAtomicLoad>(&ira->new_irb,
             source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = expr_type;
-    instruction->name = name;
-    instruction->linkage = linkage;
-    instruction->is_thread_local = is_thread_local;
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_load_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *ptr) {
-    IrInstSrcLoadPtr *instruction = ir_build_instruction<IrInstSrcLoadPtr>(irb, scope, source_node);
-    instruction->ptr = ptr;
-
-    ir_ref_instruction(ptr, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_load_ptr_gen(IrAnalyze *ira, IrInst *source_instruction,
-        IrInstGen *ptr, ZigType *ty, IrInstGen *result_loc)
-{
-    IrInstGenLoadPtr *instruction = ir_build_inst_gen<IrInstGenLoadPtr>(
-            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = ty;
-    instruction->ptr = ptr;
-    instruction->result_loc = result_loc;
-
-    ir_ref_inst_gen(ptr);
-    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_typeof_n(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc **values, size_t value_count)
-{
-    assert(value_count >= 2);
-
-    IrInstSrcTypeOf *instruction = ir_build_instruction<IrInstSrcTypeOf>(irb, scope, source_node);
-    instruction->value.list = values;
-    instruction->value_count = value_count;
-
-    for (size_t i = 0; i < value_count; i++)
-        ir_ref_instruction(values[i], irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_typeof_1(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
-    IrInstSrcTypeOf *instruction = ir_build_instruction<IrInstSrcTypeOf>(irb, scope, source_node);
-    instruction->value.scalar = value;
-
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_set_cold(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *is_cold) {
-    IrInstSrcSetCold *instruction = ir_build_instruction<IrInstSrcSetCold>(irb, scope, source_node);
-    instruction->is_cold = is_cold;
-
-    ir_ref_instruction(is_cold, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_set_runtime_safety(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *safety_on)
-{
-    IrInstSrcSetRuntimeSafety *inst = ir_build_instruction<IrInstSrcSetRuntimeSafety>(irb, scope, source_node);
-    inst->safety_on = safety_on;
-
-    ir_ref_instruction(safety_on, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_set_float_mode(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *mode_value)
-{
-    IrInstSrcSetFloatMode *instruction = ir_build_instruction<IrInstSrcSetFloatMode>(irb, scope, source_node);
-    instruction->mode_value = mode_value;
-
-    ir_ref_instruction(mode_value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_array_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *size,
-        IrInstSrc *sentinel, IrInstSrc *child_type)
-{
-    IrInstSrcArrayType *instruction = ir_build_instruction<IrInstSrcArrayType>(irb, scope, source_node);
-    instruction->size = size;
-    instruction->sentinel = sentinel;
-    instruction->child_type = child_type;
-
-    ir_ref_instruction(size, irb->current_basic_block);
-    if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block);
-    ir_ref_instruction(child_type, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_anyframe_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *payload_type)
-{
-    IrInstSrcAnyFrameType *instruction = ir_build_instruction<IrInstSrcAnyFrameType>(irb, scope, source_node);
-    instruction->payload_type = payload_type;
-
-    if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_slice_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *child_type, bool is_const, bool is_volatile,
-        IrInstSrc *sentinel, IrInstSrc *align_value, bool is_allow_zero)
-{
-    IrInstSrcSliceType *instruction = ir_build_instruction<IrInstSrcSliceType>(irb, scope, source_node);
-    instruction->is_const = is_const;
-    instruction->is_volatile = is_volatile;
-    instruction->child_type = child_type;
-    instruction->sentinel = sentinel;
-    instruction->align_value = align_value;
-    instruction->is_allow_zero = is_allow_zero;
-
-    if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block);
-    if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
-    ir_ref_instruction(child_type, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_asm_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *asm_template, IrInstSrc **input_list, IrInstSrc **output_types,
-        ZigVar **output_vars, size_t return_count, bool has_side_effects, bool is_global)
-{
-    IrInstSrcAsm *instruction = ir_build_instruction<IrInstSrcAsm>(irb, scope, source_node);
-    instruction->asm_template = asm_template;
-    instruction->input_list = input_list;
-    instruction->output_types = output_types;
-    instruction->output_vars = output_vars;
-    instruction->return_count = return_count;
-    instruction->has_side_effects = has_side_effects;
-    instruction->is_global = is_global;
-
-    assert(source_node->type == NodeTypeAsmExpr);
-    for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) {
-        IrInstSrc *output_type = output_types[i];
-        if (output_type) ir_ref_instruction(output_type, irb->current_basic_block);
-    }
-
-    for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) {
-        IrInstSrc *input_value = input_list[i];
-        ir_ref_instruction(input_value, irb->current_basic_block);
-    }
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_asm_gen(IrAnalyze *ira, IrInst *source_instr,
-        Buf *asm_template, AsmToken *token_list, size_t token_list_len,
-        IrInstGen **input_list, IrInstGen **output_types, ZigVar **output_vars, size_t return_count,
-        bool has_side_effects, ZigType *return_type)
-{
-    IrInstGenAsm *instruction = ir_build_inst_gen<IrInstGenAsm>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = return_type;
-    instruction->asm_template = asm_template;
-    instruction->token_list = token_list;
-    instruction->token_list_len = token_list_len;
-    instruction->input_list = input_list;
-    instruction->output_types = output_types;
-    instruction->output_vars = output_vars;
-    instruction->return_count = return_count;
-    instruction->has_side_effects = has_side_effects;
-
-    assert(source_instr->source_node->type == NodeTypeAsmExpr);
-    for (size_t i = 0; i < source_instr->source_node->data.asm_expr.output_list.length; i += 1) {
-        IrInstGen *output_type = output_types[i];
-        if (output_type) ir_ref_inst_gen(output_type);
-    }
-
-    for (size_t i = 0; i < source_instr->source_node->data.asm_expr.input_list.length; i += 1) {
-        IrInstGen *input_value = input_list[i];
-        ir_ref_inst_gen(input_value);
-    }
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_size_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value,
-        bool bit_size)
-{
-    IrInstSrcSizeOf *instruction = ir_build_instruction<IrInstSrcSizeOf>(irb, scope, source_node);
-    instruction->type_value = type_value;
-    instruction->bit_size = bit_size;
-
-    ir_ref_instruction(type_value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_test_non_null_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *value)
-{
-    IrInstSrcTestNonNull *instruction = ir_build_instruction<IrInstSrcTestNonNull>(irb, scope, source_node);
-    instruction->value = value;
-
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_test_non_null_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value) {
-    IrInstGenTestNonNull *inst = ir_build_inst_gen<IrInstGenTestNonNull>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    inst->base.value->type = ira->codegen->builtin_types.entry_bool;
-    inst->value = value;
-
-    ir_ref_inst_gen(value);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_optional_unwrap_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *base_ptr, bool safety_check_on)
-{
-    IrInstSrcOptionalUnwrapPtr *instruction = ir_build_instruction<IrInstSrcOptionalUnwrapPtr>(irb, scope, source_node);
-    instruction->base_ptr = base_ptr;
-    instruction->safety_check_on = safety_check_on;
-
-    ir_ref_instruction(base_ptr, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_optional_unwrap_ptr_gen(IrAnalyze *ira, IrInst *source_instr,
-        IrInstGen *base_ptr, bool safety_check_on, bool initializing, ZigType *result_type)
-{
-    IrInstGenOptionalUnwrapPtr *inst = ir_build_inst_gen<IrInstGenOptionalUnwrapPtr>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    inst->base.value->type = result_type;
-    inst->base_ptr = base_ptr;
-    inst->safety_check_on = safety_check_on;
-    inst->initializing = initializing;
-
-    ir_ref_inst_gen(base_ptr);
-
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_optional_wrap(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_ty,
-        IrInstGen *operand, IrInstGen *result_loc)
-{
-    IrInstGenOptionalWrap *instruction = ir_build_inst_gen<IrInstGenOptionalWrap>(
-            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = result_ty;
-    instruction->operand = operand;
-    instruction->result_loc = result_loc;
-
-    ir_ref_inst_gen(operand);
-    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_err_wrap_payload(IrAnalyze *ira, IrInst *source_instruction,
-        ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc)
-{
-    IrInstGenErrWrapPayload *instruction = ir_build_inst_gen<IrInstGenErrWrapPayload>(
-            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = result_type;
-    instruction->operand = operand;
-    instruction->result_loc = result_loc;
-
-    ir_ref_inst_gen(operand);
-    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_err_wrap_code(IrAnalyze *ira, IrInst *source_instruction,
-        ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc)
-{
-    IrInstGenErrWrapCode *instruction = ir_build_inst_gen<IrInstGenErrWrapCode>(
-            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = result_type;
-    instruction->operand = operand;
-    instruction->result_loc = result_loc;
-
-    ir_ref_inst_gen(operand);
-    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_clz(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
-        IrInstSrc *op)
-{
-    IrInstSrcClz *instruction = ir_build_instruction<IrInstSrcClz>(irb, scope, source_node);
-    instruction->type = type;
-    instruction->op = op;
-
-    ir_ref_instruction(type, irb->current_basic_block);
-    ir_ref_instruction(op, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_clz_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, IrInstGen *op) {
-    IrInstGenClz *instruction = ir_build_inst_gen<IrInstGenClz>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = result_type;
-    instruction->op = op;
-
-    ir_ref_inst_gen(op);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_ctz(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
-        IrInstSrc *op)
-{
-    IrInstSrcCtz *instruction = ir_build_instruction<IrInstSrcCtz>(irb, scope, source_node);
-    instruction->type = type;
-    instruction->op = op;
-
-    ir_ref_instruction(type, irb->current_basic_block);
-    ir_ref_instruction(op, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_ctz_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, IrInstGen *op) {
-    IrInstGenCtz *instruction = ir_build_inst_gen<IrInstGenCtz>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = result_type;
-    instruction->op = op;
-
-    ir_ref_inst_gen(op);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_pop_count(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
-        IrInstSrc *op)
-{
-    IrInstSrcPopCount *instruction = ir_build_instruction<IrInstSrcPopCount>(irb, scope, source_node);
-    instruction->type = type;
-    instruction->op = op;
-
-    ir_ref_instruction(type, irb->current_basic_block);
-    ir_ref_instruction(op, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_pop_count_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type,
-        IrInstGen *op)
-{
-    IrInstGenPopCount *instruction = ir_build_inst_gen<IrInstGenPopCount>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = result_type;
-    instruction->op = op;
-
-    ir_ref_inst_gen(op);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_bswap(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
-        IrInstSrc *op)
-{
-    IrInstSrcBswap *instruction = ir_build_instruction<IrInstSrcBswap>(irb, scope, source_node);
-    instruction->type = type;
-    instruction->op = op;
-
-    ir_ref_instruction(type, irb->current_basic_block);
-    ir_ref_instruction(op, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_bswap_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *op_type,
-        IrInstGen *op)
-{
-    IrInstGenBswap *instruction = ir_build_inst_gen<IrInstGenBswap>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = op_type;
-    instruction->op = op;
-
-    ir_ref_inst_gen(op);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_bit_reverse(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
-        IrInstSrc *op)
-{
-    IrInstSrcBitReverse *instruction = ir_build_instruction<IrInstSrcBitReverse>(irb, scope, source_node);
-    instruction->type = type;
-    instruction->op = op;
-
-    ir_ref_instruction(type, irb->current_basic_block);
-    ir_ref_instruction(op, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_bit_reverse_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *int_type,
-        IrInstGen *op)
-{
-    IrInstGenBitReverse *instruction = ir_build_inst_gen<IrInstGenBitReverse>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = int_type;
-    instruction->op = op;
-
-    ir_ref_inst_gen(op);
-
-    return &instruction->base;
-}
-
-static IrInstSrcSwitchBr *ir_build_switch_br_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *target_value, IrBasicBlockSrc *else_block, size_t case_count, IrInstSrcSwitchBrCase *cases,
-        IrInstSrc *is_comptime, IrInstSrc *switch_prongs_void)
-{
-    IrInstSrcSwitchBr *instruction = ir_build_instruction<IrInstSrcSwitchBr>(irb, scope, source_node);
-    instruction->base.is_noreturn = true;
-    instruction->target_value = target_value;
-    instruction->else_block = else_block;
-    instruction->case_count = case_count;
-    instruction->cases = cases;
-    instruction->is_comptime = is_comptime;
-    instruction->switch_prongs_void = switch_prongs_void;
-
-    ir_ref_instruction(target_value, irb->current_basic_block);
-    ir_ref_instruction(is_comptime, irb->current_basic_block);
-    ir_ref_bb(else_block);
-    ir_ref_instruction(switch_prongs_void, irb->current_basic_block);
-
-    for (size_t i = 0; i < case_count; i += 1) {
-        ir_ref_instruction(cases[i].value, irb->current_basic_block);
-        ir_ref_bb(cases[i].block);
-    }
-
-    return instruction;
-}
-
-static IrInstGenSwitchBr *ir_build_switch_br_gen(IrAnalyze *ira, IrInst *source_instr,
-        IrInstGen *target_value, IrBasicBlockGen *else_block, size_t case_count, IrInstGenSwitchBrCase *cases)
-{
-    IrInstGenSwitchBr *instruction = ir_build_inst_noreturn<IrInstGenSwitchBr>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->target_value = target_value;
-    instruction->else_block = else_block;
-    instruction->case_count = case_count;
-    instruction->cases = cases;
-
-    ir_ref_inst_gen(target_value);
-
-    for (size_t i = 0; i < case_count; i += 1) {
-        ir_ref_inst_gen(cases[i].value);
-    }
-
-    return instruction;
-}
-
-static IrInstSrc *ir_build_switch_target(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *target_value_ptr)
-{
-    IrInstSrcSwitchTarget *instruction = ir_build_instruction<IrInstSrcSwitchTarget>(irb, scope, source_node);
-    instruction->target_value_ptr = target_value_ptr;
-
-    ir_ref_instruction(target_value_ptr, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_switch_var(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *target_value_ptr, IrInstSrc **prongs_ptr, size_t prongs_len)
-{
-    IrInstSrcSwitchVar *instruction = ir_build_instruction<IrInstSrcSwitchVar>(irb, scope, source_node);
-    instruction->target_value_ptr = target_value_ptr;
-    instruction->prongs_ptr = prongs_ptr;
-    instruction->prongs_len = prongs_len;
-
-    ir_ref_instruction(target_value_ptr, irb->current_basic_block);
-    for (size_t i = 0; i < prongs_len; i += 1) {
-        ir_ref_instruction(prongs_ptr[i], irb->current_basic_block);
-    }
-
-    return &instruction->base;
-}
-
-// For this instruction the switch_br must be set later.
-static IrInstSrcSwitchElseVar *ir_build_switch_else_var(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *target_value_ptr)
-{
-    IrInstSrcSwitchElseVar *instruction = ir_build_instruction<IrInstSrcSwitchElseVar>(irb, scope, source_node);
-    instruction->target_value_ptr = target_value_ptr;
-
-    ir_ref_instruction(target_value_ptr, irb->current_basic_block);
-
-    return instruction;
-}
-
-static IrInstGen *ir_build_union_tag(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value,
-        ZigType *tag_type)
-{
-    IrInstGenUnionTag *instruction = ir_build_inst_gen<IrInstGenUnionTag>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->value = value;
-    instruction->base.value->type = tag_type;
-
-    ir_ref_inst_gen(value);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) {
-    IrInstSrcImport *instruction = ir_build_instruction<IrInstSrcImport>(irb, scope, source_node);
-    instruction->name = name;
-
-    ir_ref_instruction(name, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_ref_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
-    IrInstSrcRef *instruction = ir_build_instruction<IrInstSrcRef>(irb, scope, source_node);
-    instruction->value = value;
-
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_ref_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type,
-        IrInstGen *operand, IrInstGen *result_loc)
-{
-    IrInstGenRef *instruction = ir_build_inst_gen<IrInstGenRef>(&ira->new_irb,
-            source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = result_type;
-    instruction->operand = operand;
-    instruction->result_loc = result_loc;
-
-    ir_ref_inst_gen(operand);
-    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_compile_err(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *msg) {
-    IrInstSrcCompileErr *instruction = ir_build_instruction<IrInstSrcCompileErr>(irb, scope, source_node);
-    instruction->msg = msg;
-
-    ir_ref_instruction(msg, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_compile_log(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        size_t msg_count, IrInstSrc **msg_list)
-{
-    IrInstSrcCompileLog *instruction = ir_build_instruction<IrInstSrcCompileLog>(irb, scope, source_node);
-    instruction->msg_count = msg_count;
-    instruction->msg_list = msg_list;
-
-    for (size_t i = 0; i < msg_count; i += 1) {
-        ir_ref_instruction(msg_list[i], irb->current_basic_block);
-    }
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_err_name(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
-    IrInstSrcErrName *instruction = ir_build_instruction<IrInstSrcErrName>(irb, scope, source_node);
-    instruction->value = value;
-
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_err_name_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value,
-        ZigType *str_type)
-{
-    IrInstGenErrName *instruction = ir_build_inst_gen<IrInstGenErrName>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = str_type;
-    instruction->value = value;
-
-    ir_ref_inst_gen(value);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_c_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    IrInstSrcCImport *instruction = ir_build_instruction<IrInstSrcCImport>(irb, scope, source_node);
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_c_include(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) {
-    IrInstSrcCInclude *instruction = ir_build_instruction<IrInstSrcCInclude>(irb, scope, source_node);
-    instruction->name = name;
-
-    ir_ref_instruction(name, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_c_define(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name, IrInstSrc *value) {
-    IrInstSrcCDefine *instruction = ir_build_instruction<IrInstSrcCDefine>(irb, scope, source_node);
-    instruction->name = name;
-    instruction->value = value;
-
-    ir_ref_instruction(name, irb->current_basic_block);
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_c_undef(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) {
-    IrInstSrcCUndef *instruction = ir_build_instruction<IrInstSrcCUndef>(irb, scope, source_node);
-    instruction->name = name;
-
-    ir_ref_instruction(name, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_embed_file(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) {
-    IrInstSrcEmbedFile *instruction = ir_build_instruction<IrInstSrcEmbedFile>(irb, scope, source_node);
-    instruction->name = name;
-
-    ir_ref_instruction(name, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_cmpxchg_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *type_value, IrInstSrc *ptr, IrInstSrc *cmp_value, IrInstSrc *new_value,
-    IrInstSrc *success_order_value, IrInstSrc *failure_order_value, bool is_weak, ResultLoc *result_loc)
-{
-    IrInstSrcCmpxchg *instruction = ir_build_instruction<IrInstSrcCmpxchg>(irb, scope, source_node);
-    instruction->type_value = type_value;
-    instruction->ptr = ptr;
-    instruction->cmp_value = cmp_value;
-    instruction->new_value = new_value;
-    instruction->success_order_value = success_order_value;
-    instruction->failure_order_value = failure_order_value;
-    instruction->is_weak = is_weak;
-    instruction->result_loc = result_loc;
-
-    ir_ref_instruction(type_value, irb->current_basic_block);
-    ir_ref_instruction(ptr, irb->current_basic_block);
-    ir_ref_instruction(cmp_value, irb->current_basic_block);
-    ir_ref_instruction(new_value, irb->current_basic_block);
-    ir_ref_instruction(success_order_value, irb->current_basic_block);
-    ir_ref_instruction(failure_order_value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_cmpxchg_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type,
-    IrInstGen *ptr, IrInstGen *cmp_value, IrInstGen *new_value,
-    AtomicOrder success_order, AtomicOrder failure_order, bool is_weak, IrInstGen *result_loc)
-{
-    IrInstGenCmpxchg *instruction = ir_build_inst_gen<IrInstGenCmpxchg>(&ira->new_irb,
-            source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = result_type;
-    instruction->ptr = ptr;
-    instruction->cmp_value = cmp_value;
-    instruction->new_value = new_value;
-    instruction->success_order = success_order;
-    instruction->failure_order = failure_order;
-    instruction->is_weak = is_weak;
-    instruction->result_loc = result_loc;
-
-    ir_ref_inst_gen(ptr);
-    ir_ref_inst_gen(cmp_value);
-    ir_ref_inst_gen(new_value);
-    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_fence(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *order) {
-    IrInstSrcFence *instruction = ir_build_instruction<IrInstSrcFence>(irb, scope, source_node);
-    instruction->order = order;
-
-    ir_ref_instruction(order, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_fence_gen(IrAnalyze *ira, IrInst *source_instr, AtomicOrder order) {
-    IrInstGenFence *instruction = ir_build_inst_void<IrInstGenFence>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->order = order;
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_reduce(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *op, IrInstSrc *value) {
-    IrInstSrcReduce *instruction = ir_build_instruction<IrInstSrcReduce>(irb, scope, source_node);
-    instruction->op = op;
-    instruction->value = value;
-
-    ir_ref_instruction(op, irb->current_basic_block);
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_reduce_gen(IrAnalyze *ira, IrInst *source_instruction, ReduceOp op, IrInstGen *value, ZigType *result_type) {
-    IrInstGenReduce *instruction = ir_build_inst_gen<IrInstGenReduce>(&ira->new_irb,
-            source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = result_type;
-    instruction->op = op;
-    instruction->value = value;
-
-    ir_ref_inst_gen(value);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_truncate(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *dest_type, IrInstSrc *target)
-{
-    IrInstSrcTruncate *instruction = ir_build_instruction<IrInstSrcTruncate>(irb, scope, source_node);
-    instruction->dest_type = dest_type;
-    instruction->target = target;
-
-    ir_ref_instruction(dest_type, irb->current_basic_block);
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_truncate_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *dest_type,
-        IrInstGen *target)
-{
-    IrInstGenTruncate *instruction = ir_build_inst_gen<IrInstGenTruncate>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = dest_type;
-    instruction->target = target;
-
-    ir_ref_inst_gen(target);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_int_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type,
-        IrInstSrc *target)
-{
-    IrInstSrcIntCast *instruction = ir_build_instruction<IrInstSrcIntCast>(irb, scope, source_node);
-    instruction->dest_type = dest_type;
-    instruction->target = target;
-
-    ir_ref_instruction(dest_type, irb->current_basic_block);
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_float_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type,
-        IrInstSrc *target)
-{
-    IrInstSrcFloatCast *instruction = ir_build_instruction<IrInstSrcFloatCast>(irb, scope, source_node);
-    instruction->dest_type = dest_type;
-    instruction->target = target;
-
-    ir_ref_instruction(dest_type, irb->current_basic_block);
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_err_set_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *dest_type, IrInstSrc *target)
-{
-    IrInstSrcErrSetCast *instruction = ir_build_instruction<IrInstSrcErrSetCast>(irb, scope, source_node);
-    instruction->dest_type = dest_type;
-    instruction->target = target;
-
-    ir_ref_instruction(dest_type, irb->current_basic_block);
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_int_to_float(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *dest_type, IrInstSrc *target)
-{
-    IrInstSrcIntToFloat *instruction = ir_build_instruction<IrInstSrcIntToFloat>(irb, scope, source_node);
-    instruction->dest_type = dest_type;
-    instruction->target = target;
-
-    ir_ref_instruction(dest_type, irb->current_basic_block);
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_float_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *dest_type, IrInstSrc *target)
-{
-    IrInstSrcFloatToInt *instruction = ir_build_instruction<IrInstSrcFloatToInt>(irb, scope, source_node);
-    instruction->dest_type = dest_type;
-    instruction->target = target;
-
-    ir_ref_instruction(dest_type, irb->current_basic_block);
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_bool_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) {
-    IrInstSrcBoolToInt *instruction = ir_build_instruction<IrInstSrcBoolToInt>(irb, scope, source_node);
-    instruction->target = target;
-
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_vector_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *len,
-        IrInstSrc *elem_type)
-{
-    IrInstSrcVectorType *instruction = ir_build_instruction<IrInstSrcVectorType>(irb, scope, source_node);
-    instruction->len = len;
-    instruction->elem_type = elem_type;
-
-    ir_ref_instruction(len, irb->current_basic_block);
-    ir_ref_instruction(elem_type, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_shuffle_vector(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *scalar_type, IrInstSrc *a, IrInstSrc *b, IrInstSrc *mask)
-{
-    IrInstSrcShuffleVector *instruction = ir_build_instruction<IrInstSrcShuffleVector>(irb, scope, source_node);
-    instruction->scalar_type = scalar_type;
-    instruction->a = a;
-    instruction->b = b;
-    instruction->mask = mask;
-
-    if (scalar_type != nullptr) ir_ref_instruction(scalar_type, irb->current_basic_block);
-    ir_ref_instruction(a, irb->current_basic_block);
-    ir_ref_instruction(b, irb->current_basic_block);
-    ir_ref_instruction(mask, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_shuffle_vector_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
-        ZigType *result_type, IrInstGen *a, IrInstGen *b, IrInstGen *mask)
-{
-    IrInstGenShuffleVector *inst = ir_build_inst_gen<IrInstGenShuffleVector>(&ira->new_irb, scope, source_node);
-    inst->base.value->type = result_type;
-    inst->a = a;
-    inst->b = b;
-    inst->mask = mask;
-
-    ir_ref_inst_gen(a);
-    ir_ref_inst_gen(b);
-    ir_ref_inst_gen(mask);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_splat_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *len, IrInstSrc *scalar)
-{
-    IrInstSrcSplat *instruction = ir_build_instruction<IrInstSrcSplat>(irb, scope, source_node);
-    instruction->len = len;
-    instruction->scalar = scalar;
-
-    ir_ref_instruction(len, irb->current_basic_block);
-    ir_ref_instruction(scalar, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_splat_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type,
-    IrInstGen *scalar)
-{
-    IrInstGenSplat *instruction = ir_build_inst_gen<IrInstGenSplat>(
-            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = result_type;
-    instruction->scalar = scalar;
-
-    ir_ref_inst_gen(scalar);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_bool_not(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
-    IrInstSrcBoolNot *instruction = ir_build_instruction<IrInstSrcBoolNot>(irb, scope, source_node);
-    instruction->value = value;
-
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_bool_not_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value) {
-    IrInstGenBoolNot *instruction = ir_build_inst_gen<IrInstGenBoolNot>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = ira->codegen->builtin_types.entry_bool;
-    instruction->value = value;
-
-    ir_ref_inst_gen(value);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_memset_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *dest_ptr, IrInstSrc *byte, IrInstSrc *count)
-{
-    IrInstSrcMemset *instruction = ir_build_instruction<IrInstSrcMemset>(irb, scope, source_node);
-    instruction->dest_ptr = dest_ptr;
-    instruction->byte = byte;
-    instruction->count = count;
-
-    ir_ref_instruction(dest_ptr, irb->current_basic_block);
-    ir_ref_instruction(byte, irb->current_basic_block);
-    ir_ref_instruction(count, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_memset_gen(IrAnalyze *ira, IrInst *source_instr,
-    IrInstGen *dest_ptr, IrInstGen *byte, IrInstGen *count)
-{
-    IrInstGenMemset *instruction = ir_build_inst_void<IrInstGenMemset>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->dest_ptr = dest_ptr;
-    instruction->byte = byte;
-    instruction->count = count;
-
-    ir_ref_inst_gen(dest_ptr);
-    ir_ref_inst_gen(byte);
-    ir_ref_inst_gen(count);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_memcpy_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *dest_ptr, IrInstSrc *src_ptr, IrInstSrc *count)
-{
-    IrInstSrcMemcpy *instruction = ir_build_instruction<IrInstSrcMemcpy>(irb, scope, source_node);
-    instruction->dest_ptr = dest_ptr;
-    instruction->src_ptr = src_ptr;
-    instruction->count = count;
-
-    ir_ref_instruction(dest_ptr, irb->current_basic_block);
-    ir_ref_instruction(src_ptr, irb->current_basic_block);
-    ir_ref_instruction(count, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_memcpy_gen(IrAnalyze *ira, IrInst *source_instr,
-    IrInstGen *dest_ptr, IrInstGen *src_ptr, IrInstGen *count)
-{
-    IrInstGenMemcpy *instruction = ir_build_inst_void<IrInstGenMemcpy>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->dest_ptr = dest_ptr;
-    instruction->src_ptr = src_ptr;
-    instruction->count = count;
-
-    ir_ref_inst_gen(dest_ptr);
-    ir_ref_inst_gen(src_ptr);
-    ir_ref_inst_gen(count);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_slice_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *ptr, IrInstSrc *start, IrInstSrc *end, IrInstSrc *sentinel,
-    bool safety_check_on, ResultLoc *result_loc)
-{
-    IrInstSrcSlice *instruction = ir_build_instruction<IrInstSrcSlice>(irb, scope, source_node);
-    instruction->ptr = ptr;
-    instruction->start = start;
-    instruction->end = end;
-    instruction->sentinel = sentinel;
-    instruction->safety_check_on = safety_check_on;
-    instruction->result_loc = result_loc;
-
-    ir_ref_instruction(ptr, irb->current_basic_block);
-    ir_ref_instruction(start, irb->current_basic_block);
-    if (end) ir_ref_instruction(end, irb->current_basic_block);
-    if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_slice_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *slice_type,
-    IrInstGen *ptr, IrInstGen *start, IrInstGen *end, bool safety_check_on, IrInstGen *result_loc,
-    ZigValue *sentinel)
-{
-    IrInstGenSlice *instruction = ir_build_inst_gen<IrInstGenSlice>(
-            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = slice_type;
-    instruction->ptr = ptr;
-    instruction->start = start;
-    instruction->end = end;
-    instruction->safety_check_on = safety_check_on;
-    instruction->result_loc = result_loc;
-    instruction->sentinel = sentinel;
-
-    ir_ref_inst_gen(ptr);
-    ir_ref_inst_gen(start);
-    if (end != nullptr) ir_ref_inst_gen(end);
-    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_breakpoint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    IrInstSrcBreakpoint *instruction = ir_build_instruction<IrInstSrcBreakpoint>(irb, scope, source_node);
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_breakpoint_gen(IrAnalyze *ira, IrInst *source_instr) {
-    IrInstGenBreakpoint *instruction = ir_build_inst_void<IrInstGenBreakpoint>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_return_address_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    IrInstSrcReturnAddress *instruction = ir_build_instruction<IrInstSrcReturnAddress>(irb, scope, source_node);
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_return_address_gen(IrAnalyze *ira, IrInst *source_instr) {
-    IrInstGenReturnAddress *inst = ir_build_inst_gen<IrInstGenReturnAddress>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    inst->base.value->type = ira->codegen->builtin_types.entry_usize;
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_frame_address_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    IrInstSrcFrameAddress *inst = ir_build_instruction<IrInstSrcFrameAddress>(irb, scope, source_node);
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_frame_address_gen(IrAnalyze *ira, IrInst *source_instr) {
-    IrInstGenFrameAddress *inst = ir_build_inst_gen<IrInstGenFrameAddress>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    inst->base.value->type = ira->codegen->builtin_types.entry_usize;
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_handle_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    IrInstSrcFrameHandle *inst = ir_build_instruction<IrInstSrcFrameHandle>(irb, scope, source_node);
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_handle_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *ty) {
-    IrInstGenFrameHandle *inst = ir_build_inst_gen<IrInstGenFrameHandle>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    inst->base.value->type = ty;
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_frame_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn) {
-    IrInstSrcFrameType *inst = ir_build_instruction<IrInstSrcFrameType>(irb, scope, source_node);
-    inst->fn = fn;
-
-    ir_ref_instruction(fn, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_frame_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn) {
-    IrInstSrcFrameSize *inst = ir_build_instruction<IrInstSrcFrameSize>(irb, scope, source_node);
-    inst->fn = fn;
-
-    ir_ref_instruction(fn, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_frame_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *fn)
-{
-    IrInstGenFrameSize *inst = ir_build_inst_gen<IrInstGenFrameSize>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    inst->base.value->type = ira->codegen->builtin_types.entry_usize;
-    inst->fn = fn;
-
-    ir_ref_inst_gen(fn);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_overflow_op_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrOverflowOp op, IrInstSrc *type_value, IrInstSrc *op1, IrInstSrc *op2, IrInstSrc *result_ptr)
-{
-    IrInstSrcOverflowOp *instruction = ir_build_instruction<IrInstSrcOverflowOp>(irb, scope, source_node);
-    instruction->op = op;
-    instruction->type_value = type_value;
-    instruction->op1 = op1;
-    instruction->op2 = op2;
-    instruction->result_ptr = result_ptr;
-
-    ir_ref_instruction(type_value, irb->current_basic_block);
-    ir_ref_instruction(op1, irb->current_basic_block);
-    ir_ref_instruction(op2, irb->current_basic_block);
-    ir_ref_instruction(result_ptr, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_overflow_op_gen(IrAnalyze *ira, IrInst *source_instr,
-        IrOverflowOp op, IrInstGen *op1, IrInstGen *op2, IrInstGen *result_ptr,
-        ZigType *result_ptr_type)
-{
-    IrInstGenOverflowOp *instruction = ir_build_inst_gen<IrInstGenOverflowOp>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = ira->codegen->builtin_types.entry_bool;
-    instruction->op = op;
-    instruction->op1 = op1;
-    instruction->op2 = op2;
-    instruction->result_ptr = result_ptr;
-    instruction->result_ptr_type = result_ptr_type;
-
-    ir_ref_inst_gen(op1);
-    ir_ref_inst_gen(op2);
-    ir_ref_inst_gen(result_ptr);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_float_op_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand,
-        BuiltinFnId fn_id)
-{
-    IrInstSrcFloatOp *instruction = ir_build_instruction<IrInstSrcFloatOp>(irb, scope, source_node);
-    instruction->operand = operand;
-    instruction->fn_id = fn_id;
-
-    ir_ref_instruction(operand, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_float_op_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand,
-        BuiltinFnId fn_id, ZigType *operand_type)
-{
-    IrInstGenFloatOp *instruction = ir_build_inst_gen<IrInstGenFloatOp>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = operand_type;
-    instruction->operand = operand;
-    instruction->fn_id = fn_id;
-
-    ir_ref_inst_gen(operand);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_mul_add_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *type_value, IrInstSrc *op1, IrInstSrc *op2, IrInstSrc *op3)
-{
-    IrInstSrcMulAdd *instruction = ir_build_instruction<IrInstSrcMulAdd>(irb, scope, source_node);
-    instruction->type_value = type_value;
-    instruction->op1 = op1;
-    instruction->op2 = op2;
-    instruction->op3 = op3;
-
-    ir_ref_instruction(type_value, irb->current_basic_block);
-    ir_ref_instruction(op1, irb->current_basic_block);
-    ir_ref_instruction(op2, irb->current_basic_block);
-    ir_ref_instruction(op3, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_mul_add_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2,
-        IrInstGen *op3, ZigType *expr_type)
-{
-    IrInstGenMulAdd *instruction = ir_build_inst_gen<IrInstGenMulAdd>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = expr_type;
-    instruction->op1 = op1;
-    instruction->op2 = op2;
-    instruction->op3 = op3;
-
-    ir_ref_inst_gen(op1);
-    ir_ref_inst_gen(op2);
-    ir_ref_inst_gen(op3);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_align_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) {
-    IrInstSrcAlignOf *instruction = ir_build_instruction<IrInstSrcAlignOf>(irb, scope, source_node);
-    instruction->type_value = type_value;
-
-    ir_ref_instruction(type_value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_test_err_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *base_ptr, bool resolve_err_set, bool base_ptr_is_payload)
-{
-    IrInstSrcTestErr *instruction = ir_build_instruction<IrInstSrcTestErr>(irb, scope, source_node);
-    instruction->base_ptr = base_ptr;
-    instruction->resolve_err_set = resolve_err_set;
-    instruction->base_ptr_is_payload = base_ptr_is_payload;
-
-    ir_ref_instruction(base_ptr, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_test_err_gen(IrAnalyze *ira, IrInst *source_instruction, IrInstGen *err_union) {
-    IrInstGenTestErr *instruction = ir_build_inst_gen<IrInstGenTestErr>(
-            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = ira->codegen->builtin_types.entry_bool;
-    instruction->err_union = err_union;
-
-    ir_ref_inst_gen(err_union);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_unwrap_err_code_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *err_union_ptr)
-{
-    IrInstSrcUnwrapErrCode *inst = ir_build_instruction<IrInstSrcUnwrapErrCode>(irb, scope, source_node);
-    inst->err_union_ptr = err_union_ptr;
-
-    ir_ref_instruction(err_union_ptr, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_unwrap_err_code_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
-    IrInstGen *err_union_ptr, ZigType *result_type)
-{
-    IrInstGenUnwrapErrCode *inst = ir_build_inst_gen<IrInstGenUnwrapErrCode>(&ira->new_irb, scope, source_node);
-    inst->base.value->type = result_type;
-    inst->err_union_ptr = err_union_ptr;
-
-    ir_ref_inst_gen(err_union_ptr);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_unwrap_err_payload_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *value, bool safety_check_on, bool initializing)
-{
-    IrInstSrcUnwrapErrPayload *inst = ir_build_instruction<IrInstSrcUnwrapErrPayload>(irb, scope, source_node);
-    inst->value = value;
-    inst->safety_check_on = safety_check_on;
-    inst->initializing = initializing;
-
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_unwrap_err_payload_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
-    IrInstGen *value, bool safety_check_on, bool initializing, ZigType *result_type)
-{
-    IrInstGenUnwrapErrPayload *inst = ir_build_inst_gen<IrInstGenUnwrapErrPayload>(&ira->new_irb, scope, source_node);
-    inst->base.value->type = result_type;
-    inst->value = value;
-    inst->safety_check_on = safety_check_on;
-    inst->initializing = initializing;
-
-    ir_ref_inst_gen(value);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_fn_proto(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc **param_types, IrInstSrc *align_value, IrInstSrc *callconv_value,
-    IrInstSrc *return_type, bool is_var_args)
-{
-    IrInstSrcFnProto *instruction = ir_build_instruction<IrInstSrcFnProto>(irb, scope, source_node);
-    instruction->param_types = param_types;
-    instruction->align_value = align_value;
-    instruction->callconv_value = callconv_value;
-    instruction->return_type = return_type;
-    instruction->is_var_args = is_var_args;
-
-    assert(source_node->type == NodeTypeFnProto);
-    size_t param_count = source_node->data.fn_proto.params.length;
-    if (is_var_args) param_count -= 1;
-    for (size_t i = 0; i < param_count; i += 1) {
-        if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block);
-    }
-    if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
-    if (callconv_value != nullptr) ir_ref_instruction(callconv_value, irb->current_basic_block);
-    ir_ref_instruction(return_type, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_test_comptime(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
-    IrInstSrcTestComptime *instruction = ir_build_instruction<IrInstSrcTestComptime>(irb, scope, source_node);
-    instruction->value = value;
-
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_ptr_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *dest_type, IrInstSrc *ptr, bool safety_check_on)
-{
-    IrInstSrcPtrCast *instruction = ir_build_instruction<IrInstSrcPtrCast>(
-            irb, scope, source_node);
-    instruction->dest_type = dest_type;
-    instruction->ptr = ptr;
-    instruction->safety_check_on = safety_check_on;
-
-    ir_ref_instruction(dest_type, irb->current_basic_block);
-    ir_ref_instruction(ptr, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInst *source_instruction,
-        ZigType *ptr_type, IrInstGen *ptr, bool safety_check_on)
-{
-    IrInstGenPtrCast *instruction = ir_build_inst_gen<IrInstGenPtrCast>(
-            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = ptr_type;
-    instruction->ptr = ptr;
-    instruction->safety_check_on = safety_check_on;
-
-    ir_ref_inst_gen(ptr);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_implicit_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *operand, ResultLocCast *result_loc_cast)
-{
-    IrInstSrcImplicitCast *instruction = ir_build_instruction<IrInstSrcImplicitCast>(irb, scope, source_node);
-    instruction->operand = operand;
-    instruction->result_loc_cast = result_loc_cast;
-
-    ir_ref_instruction(operand, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_bit_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *operand, ResultLocBitCast *result_loc_bit_cast)
-{
-    IrInstSrcBitCast *instruction = ir_build_instruction<IrInstSrcBitCast>(irb, scope, source_node);
-    instruction->operand = operand;
-    instruction->result_loc_bit_cast = result_loc_bit_cast;
-
-    ir_ref_instruction(operand, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_bit_cast_gen(IrAnalyze *ira, IrInst *source_instruction,
-        IrInstGen *operand, ZigType *ty)
-{
-    IrInstGenBitCast *instruction = ir_build_inst_gen<IrInstGenBitCast>(
-            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = ty;
-    instruction->operand = operand;
-
-    ir_ref_inst_gen(operand);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_widen_or_shorten(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target,
-        ZigType *result_type)
-{
-    IrInstGenWidenOrShorten *inst = ir_build_inst_gen<IrInstGenWidenOrShorten>(&ira->new_irb, scope, source_node);
-    inst->base.value->type = result_type;
-    inst->target = target;
-
-    ir_ref_inst_gen(target);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_int_to_ptr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *dest_type, IrInstSrc *target)
-{
-    IrInstSrcIntToPtr *instruction = ir_build_instruction<IrInstSrcIntToPtr>(irb, scope, source_node);
-    instruction->dest_type = dest_type;
-    instruction->target = target;
-
-    ir_ref_instruction(dest_type, irb->current_basic_block);
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_int_to_ptr_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
-        IrInstGen *target, ZigType *ptr_type)
-{
-    IrInstGenIntToPtr *instruction = ir_build_inst_gen<IrInstGenIntToPtr>(&ira->new_irb, scope, source_node);
-    instruction->base.value->type = ptr_type;
-    instruction->target = target;
-
-    ir_ref_inst_gen(target);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_ptr_to_int_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *target)
-{
-    IrInstSrcPtrToInt *inst = ir_build_instruction<IrInstSrcPtrToInt>(irb, scope, source_node);
-    inst->target = target;
-
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_ptr_to_int_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target) {
-    IrInstGenPtrToInt *inst = ir_build_inst_gen<IrInstGenPtrToInt>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    inst->base.value->type = ira->codegen->builtin_types.entry_usize;
-    inst->target = target;
-
-    ir_ref_inst_gen(target);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_int_to_enum_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *dest_type, IrInstSrc *target)
-{
-    IrInstSrcIntToEnum *instruction = ir_build_instruction<IrInstSrcIntToEnum>(irb, scope, source_node);
-    instruction->dest_type = dest_type;
-    instruction->target = target;
-
-    if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block);
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_int_to_enum_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
-        ZigType *dest_type, IrInstGen *target)
-{
-    IrInstGenIntToEnum *instruction = ir_build_inst_gen<IrInstGenIntToEnum>(&ira->new_irb, scope, source_node);
-    instruction->base.value->type = dest_type;
-    instruction->target = target;
-
-    ir_ref_inst_gen(target);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_enum_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *target)
-{
-    IrInstSrcEnumToInt *instruction = ir_build_instruction<IrInstSrcEnumToInt>(
-            irb, scope, source_node);
-    instruction->target = target;
-
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_int_to_err_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *target)
-{
-    IrInstSrcIntToErr *instruction = ir_build_instruction<IrInstSrcIntToErr>(irb, scope, source_node);
-    instruction->target = target;
-
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_int_to_err_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target,
-        ZigType *wanted_type)
-{
-    IrInstGenIntToErr *instruction = ir_build_inst_gen<IrInstGenIntToErr>(&ira->new_irb, scope, source_node);
-    instruction->base.value->type = wanted_type;
-    instruction->target = target;
-
-    ir_ref_inst_gen(target);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_err_to_int_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *target)
-{
-    IrInstSrcErrToInt *instruction = ir_build_instruction<IrInstSrcErrToInt>(
-            irb, scope, source_node);
-    instruction->target = target;
-
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_err_to_int_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target,
-        ZigType *wanted_type)
-{
-    IrInstGenErrToInt *instruction = ir_build_inst_gen<IrInstGenErrToInt>(&ira->new_irb, scope, source_node);
-    instruction->base.value->type = wanted_type;
-    instruction->target = target;
-
-    ir_ref_inst_gen(target);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,
-        AstNode* else_prong, bool have_underscore_prong)
-{
-    IrInstSrcCheckSwitchProngs *instruction = heap::c_allocator.create<IrInstSrcCheckSwitchProngs>();
-    instruction->base.id = have_underscore_prong ?
-        IrInstSrcIdCheckSwitchProngsUnderYes : IrInstSrcIdCheckSwitchProngsUnderNo;
-    instruction->base.base.scope = scope;
-    instruction->base.base.source_node = source_node;
-    instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
-    instruction->base.owner_bb = irb->current_basic_block;
-    ir_instruction_append(irb->current_basic_block, &instruction->base);
-
-    instruction->target_value = target_value;
-    instruction->ranges = ranges;
-    instruction->range_count = range_count;
-    instruction->else_prong = else_prong;
-
-    ir_ref_instruction(target_value, irb->current_basic_block);
-    for (size_t i = 0; i < range_count; i += 1) {
-        ir_ref_instruction(ranges[i].start, irb->current_basic_block);
-        ir_ref_instruction(ranges[i].end, irb->current_basic_block);
-    }
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_check_statement_is_void(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc* statement_value)
-{
-    IrInstSrcCheckStatementIsVoid *instruction = ir_build_instruction<IrInstSrcCheckStatementIsVoid>(
-            irb, scope, source_node);
-    instruction->statement_value = statement_value;
-
-    ir_ref_instruction(statement_value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_type_name(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *type_value)
-{
-    IrInstSrcTypeName *instruction = ir_build_instruction<IrInstSrcTypeName>(irb, scope, source_node);
-    instruction->type_value = type_value;
-
-    ir_ref_instruction(type_value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_decl_ref(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Tld *tld, LVal lval) {
-    IrInstSrcDeclRef *instruction = ir_build_instruction<IrInstSrcDeclRef>(irb, scope, source_node);
-    instruction->tld = tld;
-    instruction->lval = lval;
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_panic_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *msg) {
-    IrInstSrcPanic *instruction = ir_build_instruction<IrInstSrcPanic>(irb, scope, source_node);
-    instruction->base.is_noreturn = true;
-    instruction->msg = msg;
-
-    ir_ref_instruction(msg, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_panic_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *msg) {
-    IrInstGenPanic *instruction = ir_build_inst_noreturn<IrInstGenPanic>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->msg = msg;
-
-    ir_ref_inst_gen(msg);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_tag_name_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) {
-    IrInstSrcTagName *instruction = ir_build_instruction<IrInstSrcTagName>(irb, scope, source_node);
-    instruction->target = target;
-
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_tag_name_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target,
-        ZigType *result_type)
-{
-    IrInstGenTagName *instruction = ir_build_inst_gen<IrInstGenTagName>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = result_type;
-    instruction->target = target;
-
-    ir_ref_inst_gen(target);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_field_parent_ptr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *type_value, IrInstSrc *field_name, IrInstSrc *field_ptr)
-{
-    IrInstSrcFieldParentPtr *inst = ir_build_instruction<IrInstSrcFieldParentPtr>(
-            irb, scope, source_node);
-    inst->type_value = type_value;
-    inst->field_name = field_name;
-    inst->field_ptr = field_ptr;
-
-    ir_ref_instruction(type_value, irb->current_basic_block);
-    ir_ref_instruction(field_name, irb->current_basic_block);
-    ir_ref_instruction(field_ptr, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_field_parent_ptr_gen(IrAnalyze *ira, IrInst *source_instr,
-        IrInstGen *field_ptr, TypeStructField *field, ZigType *result_type)
-{
-    IrInstGenFieldParentPtr *inst = ir_build_inst_gen<IrInstGenFieldParentPtr>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    inst->base.value->type = result_type;
-    inst->field_ptr = field_ptr;
-    inst->field = field;
-
-    ir_ref_inst_gen(field_ptr);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_byte_offset_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *type_value, IrInstSrc *field_name)
-{
-    IrInstSrcByteOffsetOf *instruction = ir_build_instruction<IrInstSrcByteOffsetOf>(irb, scope, source_node);
-    instruction->type_value = type_value;
-    instruction->field_name = field_name;
-
-    ir_ref_instruction(type_value, irb->current_basic_block);
-    ir_ref_instruction(field_name, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_bit_offset_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *type_value, IrInstSrc *field_name)
-{
-    IrInstSrcBitOffsetOf *instruction = ir_build_instruction<IrInstSrcBitOffsetOf>(irb, scope, source_node);
-    instruction->type_value = type_value;
-    instruction->field_name = field_name;
-
-    ir_ref_instruction(type_value, irb->current_basic_block);
-    ir_ref_instruction(field_name, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_type_info(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) {
-    IrInstSrcTypeInfo *instruction = ir_build_instruction<IrInstSrcTypeInfo>(irb, scope, source_node);
-    instruction->type_value = type_value;
-
-    ir_ref_instruction(type_value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_info) {
-    IrInstSrcType *instruction = ir_build_instruction<IrInstSrcType>(irb, scope, source_node);
-    instruction->type_info = type_info;
-
-    ir_ref_instruction(type_info, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_set_eval_branch_quota(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *new_quota)
-{
-    IrInstSrcSetEvalBranchQuota *instruction = ir_build_instruction<IrInstSrcSetEvalBranchQuota>(irb, scope, source_node);
-    instruction->new_quota = new_quota;
-
-    ir_ref_instruction(new_quota, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_align_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *align_bytes, IrInstSrc *target)
-{
-    IrInstSrcAlignCast *instruction = ir_build_instruction<IrInstSrcAlignCast>(irb, scope, source_node);
-    instruction->align_bytes = align_bytes;
-    instruction->target = target;
-
-    ir_ref_instruction(align_bytes, irb->current_basic_block);
-    ir_ref_instruction(target, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_align_cast_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target,
-        ZigType *result_type)
-{
-    IrInstGenAlignCast *instruction = ir_build_inst_gen<IrInstGenAlignCast>(&ira->new_irb, scope, source_node);
-    instruction->base.value->type = result_type;
-    instruction->target = target;
-
-    ir_ref_inst_gen(target);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_resolve_result(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        ResultLoc *result_loc, IrInstSrc *ty)
-{
-    IrInstSrcResolveResult *instruction = ir_build_instruction<IrInstSrcResolveResult>(irb, scope, source_node);
-    instruction->result_loc = result_loc;
-    instruction->ty = ty;
-
-    if (ty != nullptr) ir_ref_instruction(ty, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_reset_result(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        ResultLoc *result_loc)
-{
-    IrInstSrcResetResult *instruction = ir_build_instruction<IrInstSrcResetResult>(irb, scope, source_node);
-    instruction->result_loc = result_loc;
-    instruction->base.is_gen = true;
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_set_align_stack(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *align_bytes)
-{
-    IrInstSrcSetAlignStack *instruction = ir_build_instruction<IrInstSrcSetAlignStack>(irb, scope, source_node);
-    instruction->align_bytes = align_bytes;
-
-    ir_ref_instruction(align_bytes, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var)
-{
-    IrInstSrcArgType *instruction = heap::c_allocator.create<IrInstSrcArgType>();
-    instruction->base.id = allow_var ?
-        IrInstSrcIdArgTypeAllowVarTrue : IrInstSrcIdArgTypeAllowVarFalse;
-    instruction->base.base.scope = scope;
-    instruction->base.base.source_node = source_node;
-    instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
-    instruction->base.owner_bb = irb->current_basic_block;
-    ir_instruction_append(irb->current_basic_block, &instruction->base);
-
-    instruction->fn_type = fn_type;
-    instruction->arg_index = arg_index;
-
-    ir_ref_instruction(fn_type, irb->current_basic_block);
-    ir_ref_instruction(arg_index, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_error_return_trace_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstErrorReturnTraceOptional optional)
-{
-    IrInstSrcErrorReturnTrace *inst = ir_build_instruction<IrInstSrcErrorReturnTrace>(irb, scope, source_node);
-    inst->optional = optional;
-
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_error_return_trace_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node,
-        IrInstErrorReturnTraceOptional optional, ZigType *result_type)
-{
-    IrInstGenErrorReturnTrace *inst = ir_build_inst_gen<IrInstGenErrorReturnTrace>(&ira->new_irb, scope, source_node);
-    inst->base.value->type = result_type;
-    inst->optional = optional;
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_error_union(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *err_set, IrInstSrc *payload)
-{
-    IrInstSrcErrorUnion *instruction = ir_build_instruction<IrInstSrcErrorUnion>(irb, scope, source_node);
-    instruction->err_set = err_set;
-    instruction->payload = payload;
-
-    ir_ref_instruction(err_set, irb->current_basic_block);
-    ir_ref_instruction(payload, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_atomic_rmw_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *op, IrInstSrc *operand,
-        IrInstSrc *ordering)
-{
-    IrInstSrcAtomicRmw *instruction = ir_build_instruction<IrInstSrcAtomicRmw>(irb, scope, source_node);
-    instruction->operand_type = operand_type;
-    instruction->ptr = ptr;
-    instruction->op = op;
-    instruction->operand = operand;
-    instruction->ordering = ordering;
-
-    ir_ref_instruction(operand_type, irb->current_basic_block);
-    ir_ref_instruction(ptr, irb->current_basic_block);
-    ir_ref_instruction(op, irb->current_basic_block);
-    ir_ref_instruction(operand, irb->current_basic_block);
-    ir_ref_instruction(ordering, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_atomic_rmw_gen(IrAnalyze *ira, IrInst *source_instr,
-        IrInstGen *ptr, IrInstGen *operand, AtomicRmwOp op, AtomicOrder ordering, ZigType *operand_type)
-{
-    IrInstGenAtomicRmw *instruction = ir_build_inst_gen<IrInstGenAtomicRmw>(&ira->new_irb, source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = operand_type;
-    instruction->ptr = ptr;
-    instruction->op = op;
-    instruction->operand = operand;
-    instruction->ordering = ordering;
-
-    ir_ref_inst_gen(ptr);
-    ir_ref_inst_gen(operand);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_atomic_load_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *ordering)
-{
-    IrInstSrcAtomicLoad *instruction = ir_build_instruction<IrInstSrcAtomicLoad>(irb, scope, source_node);
-    instruction->operand_type = operand_type;
-    instruction->ptr = ptr;
-    instruction->ordering = ordering;
-
-    ir_ref_instruction(operand_type, irb->current_basic_block);
-    ir_ref_instruction(ptr, irb->current_basic_block);
-    ir_ref_instruction(ordering, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_atomic_load_gen(IrAnalyze *ira, IrInst *source_instr,
-        IrInstGen *ptr, AtomicOrder ordering, ZigType *operand_type)
-{
-    IrInstGenAtomicLoad *instruction = ir_build_inst_gen<IrInstGenAtomicLoad>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = operand_type;
-    instruction->ptr = ptr;
-    instruction->ordering = ordering;
-
-    ir_ref_inst_gen(ptr);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_atomic_store_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *value, IrInstSrc *ordering)
-{
-    IrInstSrcAtomicStore *instruction = ir_build_instruction<IrInstSrcAtomicStore>(irb, scope, source_node);
-    instruction->operand_type = operand_type;
-    instruction->ptr = ptr;
-    instruction->value = value;
-    instruction->ordering = ordering;
-
-    ir_ref_instruction(operand_type, irb->current_basic_block);
-    ir_ref_instruction(ptr, irb->current_basic_block);
-    ir_ref_instruction(value, irb->current_basic_block);
-    ir_ref_instruction(ordering, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_atomic_store_gen(IrAnalyze *ira, IrInst *source_instr,
-        IrInstGen *ptr, IrInstGen *value, AtomicOrder ordering)
-{
-    IrInstGenAtomicStore *instruction = ir_build_inst_void<IrInstGenAtomicStore>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->ptr = ptr;
-    instruction->value = value;
-    instruction->ordering = ordering;
-
-    ir_ref_inst_gen(ptr);
-    ir_ref_inst_gen(value);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_save_err_ret_addr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    IrInstSrcSaveErrRetAddr *inst = ir_build_instruction<IrInstSrcSaveErrRetAddr>(irb, scope, source_node);
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_save_err_ret_addr_gen(IrAnalyze *ira, IrInst *source_instr) {
-    IrInstGenSaveErrRetAddr *inst = ir_build_inst_void<IrInstGenSaveErrRetAddr>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_add_implicit_return_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *value, ResultLocReturn *result_loc_ret)
-{
-    IrInstSrcAddImplicitReturnType *inst = ir_build_instruction<IrInstSrcAddImplicitReturnType>(irb, scope, source_node);
-    inst->value = value;
-    inst->result_loc_ret = result_loc_ret;
-
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_has_decl(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *container, IrInstSrc *name)
-{
-    IrInstSrcHasDecl *instruction = ir_build_instruction<IrInstSrcHasDecl>(irb, scope, source_node);
-    instruction->container = container;
-    instruction->name = name;
-
-    ir_ref_instruction(container, irb->current_basic_block);
-    ir_ref_instruction(name, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_undeclared_identifier(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) {
-    IrInstSrcUndeclaredIdent *instruction = ir_build_instruction<IrInstSrcUndeclaredIdent>(irb, scope, source_node);
-    instruction->name = name;
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_check_runtime_scope(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *scope_is_comptime, IrInstSrc *is_comptime) {
-    IrInstSrcCheckRuntimeScope *instruction = ir_build_instruction<IrInstSrcCheckRuntimeScope>(irb, scope, source_node);
-    instruction->scope_is_comptime = scope_is_comptime;
-    instruction->is_comptime = is_comptime;
-
-    ir_ref_instruction(scope_is_comptime, irb->current_basic_block);
-    ir_ref_instruction(is_comptime, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_union_init_named_field(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *union_type, IrInstSrc *field_name, IrInstSrc *field_result_loc, IrInstSrc *result_loc)
-{
-    IrInstSrcUnionInitNamedField *instruction = ir_build_instruction<IrInstSrcUnionInitNamedField>(irb, scope, source_node);
-    instruction->union_type = union_type;
-    instruction->field_name = field_name;
-    instruction->field_result_loc = field_result_loc;
-    instruction->result_loc = result_loc;
-
-    ir_ref_instruction(union_type, irb->current_basic_block);
-    ir_ref_instruction(field_name, irb->current_basic_block);
-    ir_ref_instruction(field_result_loc, irb->current_basic_block);
-    if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-
-static IrInstGen *ir_build_vector_to_array(IrAnalyze *ira, IrInst *source_instruction,
-        ZigType *result_type, IrInstGen *vector, IrInstGen *result_loc)
-{
-    IrInstGenVectorToArray *instruction = ir_build_inst_gen<IrInstGenVectorToArray>(&ira->new_irb,
-        source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = result_type;
-    instruction->vector = vector;
-    instruction->result_loc = result_loc;
-
-    ir_ref_inst_gen(vector);
-    ir_ref_inst_gen(result_loc);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_ptr_of_array_to_slice(IrAnalyze *ira, IrInst *source_instruction,
-        ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc)
-{
-    IrInstGenPtrOfArrayToSlice *instruction = ir_build_inst_gen<IrInstGenPtrOfArrayToSlice>(&ira->new_irb,
-        source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = result_type;
-    instruction->operand = operand;
-    instruction->result_loc = result_loc;
-
-    ir_ref_inst_gen(operand);
-    ir_ref_inst_gen(result_loc);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_array_to_vector(IrAnalyze *ira, IrInst *source_instruction,
-        IrInstGen *array, ZigType *result_type)
-{
-    IrInstGenArrayToVector *instruction = ir_build_inst_gen<IrInstGenArrayToVector>(&ira->new_irb,
-        source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = result_type;
-    instruction->array = array;
-
-    ir_ref_inst_gen(array);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_assert_zero(IrAnalyze *ira, IrInst *source_instruction,
-        IrInstGen *target)
-{
-    IrInstGenAssertZero *instruction = ir_build_inst_gen<IrInstGenAssertZero>(&ira->new_irb,
-        source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = ira->codegen->builtin_types.entry_void;
-    instruction->target = target;
-
-    ir_ref_inst_gen(target);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_assert_non_null(IrAnalyze *ira, IrInst *source_instruction,
-        IrInstGen *target)
-{
-    IrInstGenAssertNonNull *instruction = ir_build_inst_gen<IrInstGenAssertNonNull>(&ira->new_irb,
-        source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = ira->codegen->builtin_types.entry_void;
-    instruction->target = target;
-
-    ir_ref_inst_gen(target);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_alloca_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *align, const char *name_hint, IrInstSrc *is_comptime)
-{
-    IrInstSrcAlloca *instruction = ir_build_instruction<IrInstSrcAlloca>(irb, scope, source_node);
-    instruction->base.is_gen = true;
-    instruction->align = align;
-    instruction->name_hint = name_hint;
-    instruction->is_comptime = is_comptime;
-
-    if (align != nullptr) ir_ref_instruction(align, irb->current_basic_block);
-    if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGenAlloca *ir_build_alloca_gen(IrAnalyze *ira, IrInst *source_instruction,
-        uint32_t align, const char *name_hint)
-{
-    IrInstGenAlloca *instruction = ir_create_inst_gen<IrInstGenAlloca>(&ira->new_irb,
-            source_instruction->scope, source_instruction->source_node);
-    instruction->align = align;
-    instruction->name_hint = name_hint;
-
-    return instruction;
-}
-
-static IrInstSrc *ir_build_end_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *value, ResultLoc *result_loc)
-{
-    IrInstSrcEndExpr *instruction = ir_build_instruction<IrInstSrcEndExpr>(irb, scope, source_node);
-    instruction->base.is_gen = true;
-    instruction->value = value;
-    instruction->result_loc = result_loc;
-
-    ir_ref_instruction(value, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstSrcSuspendBegin *ir_build_suspend_begin_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    return ir_build_instruction<IrInstSrcSuspendBegin>(irb, scope, source_node);
-}
-
-static IrInstGen *ir_build_suspend_begin_gen(IrAnalyze *ira, IrInst *source_instr) {
-    IrInstGenSuspendBegin *inst = ir_build_inst_void<IrInstGenSuspendBegin>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_suspend_finish_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrcSuspendBegin *begin)
-{
-    IrInstSrcSuspendFinish *inst = ir_build_instruction<IrInstSrcSuspendFinish>(irb, scope, source_node);
-    inst->begin = begin;
-
-    ir_ref_instruction(&begin->base, irb->current_basic_block);
-
-    return &inst->base;
-}
-
-static IrInstGen *ir_build_suspend_finish_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGenSuspendBegin *begin) {
-    IrInstGenSuspendFinish *inst = ir_build_inst_void<IrInstGenSuspendFinish>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    inst->begin = begin;
-
-    ir_ref_inst_gen(&begin->base);
-
-    return &inst->base;
-}
-
-static IrInstSrc *ir_build_await_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *frame, ResultLoc *result_loc, bool is_nosuspend)
-{
-    IrInstSrcAwait *instruction = ir_build_instruction<IrInstSrcAwait>(irb, scope, source_node);
-    instruction->frame = frame;
-    instruction->result_loc = result_loc;
-    instruction->is_nosuspend = is_nosuspend;
-
-    ir_ref_instruction(frame, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGenAwait *ir_build_await_gen(IrAnalyze *ira, IrInst *source_instruction,
-        IrInstGen *frame, ZigType *result_type, IrInstGen *result_loc, bool is_nosuspend)
-{
-    IrInstGenAwait *instruction = ir_build_inst_gen<IrInstGenAwait>(&ira->new_irb,
-            source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = result_type;
-    instruction->frame = frame;
-    instruction->result_loc = result_loc;
-    instruction->is_nosuspend = is_nosuspend;
-
-    ir_ref_inst_gen(frame);
-    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
-
-    return instruction;
-}
-
-static IrInstSrc *ir_build_resume_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *frame) {
-    IrInstSrcResume *instruction = ir_build_instruction<IrInstSrcResume>(irb, scope, source_node);
-    instruction->frame = frame;
-
-    ir_ref_instruction(frame, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_resume_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *frame) {
-    IrInstGenResume *instruction = ir_build_inst_void<IrInstGenResume>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->frame = frame;
-
-    ir_ref_inst_gen(frame);
-
-    return &instruction->base;
-}
-
-static IrInstSrcSpillBegin *ir_build_spill_begin_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrc *operand, SpillId spill_id)
-{
-    IrInstSrcSpillBegin *instruction = ir_build_instruction<IrInstSrcSpillBegin>(irb, scope, source_node);
-    instruction->operand = operand;
-    instruction->spill_id = spill_id;
-
-    ir_ref_instruction(operand, irb->current_basic_block);
-
-    return instruction;
-}
-
-static IrInstGen *ir_build_spill_begin_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand,
-        SpillId spill_id)
-{
-    IrInstGenSpillBegin *instruction = ir_build_inst_void<IrInstGenSpillBegin>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->operand = operand;
-    instruction->spill_id = spill_id;
-
-    ir_ref_inst_gen(operand);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_spill_end_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        IrInstSrcSpillBegin *begin)
-{
-    IrInstSrcSpillEnd *instruction = ir_build_instruction<IrInstSrcSpillEnd>(irb, scope, source_node);
-    instruction->begin = begin;
-
-    ir_ref_instruction(&begin->base, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_spill_end_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGenSpillBegin *begin,
-        ZigType *result_type)
-{
-    IrInstGenSpillEnd *instruction = ir_build_inst_gen<IrInstGenSpillEnd>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = result_type;
-    instruction->begin = begin;
-
-    ir_ref_inst_gen(&begin->base);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_vector_extract_elem(IrAnalyze *ira, IrInst *source_instruction,
-        IrInstGen *vector, IrInstGen *index)
-{
-    IrInstGenVectorExtractElem *instruction = ir_build_inst_gen<IrInstGenVectorExtractElem>(
-            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
-    instruction->base.value->type = vector->value->type->data.vector.elem_type;
-    instruction->vector = vector;
-    instruction->index = index;
-
-    ir_ref_inst_gen(vector);
-    ir_ref_inst_gen(index);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index) {
-    IrInstSrcWasmMemorySize *instruction = ir_build_instruction<IrInstSrcWasmMemorySize>(irb, scope, source_node);
-    instruction->index = index;
-
-    ir_ref_instruction(index, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index) {
-    IrInstGenWasmMemorySize *instruction = ir_build_inst_gen<IrInstGenWasmMemorySize>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = ira->codegen->builtin_types.entry_u32;
-    instruction->index = index;
-
-    ir_ref_inst_gen(index);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_wasm_memory_grow_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index, IrInstSrc *delta) {
-    IrInstSrcWasmMemoryGrow *instruction = ir_build_instruction<IrInstSrcWasmMemoryGrow>(irb, scope, source_node);
-    instruction->index = index;
-    instruction->delta = delta;
-
-    ir_ref_instruction(index, irb->current_basic_block);
-    ir_ref_instruction(delta, irb->current_basic_block);
-
-    return &instruction->base;
-}
-
-static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index, IrInstGen *delta) {
-    IrInstGenWasmMemoryGrow *instruction = ir_build_inst_gen<IrInstGenWasmMemoryGrow>(&ira->new_irb,
-            source_instr->scope, source_instr->source_node);
-    instruction->base.value->type = ira->codegen->builtin_types.entry_i32;
-    instruction->index = index;
-    instruction->delta = delta;
-
-    ir_ref_inst_gen(index);
-    ir_ref_inst_gen(delta);
-
-    return &instruction->base;
-}
-
-static IrInstSrc *ir_build_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
-    IrInstSrcSrc *instruction = ir_build_instruction<IrInstSrcSrc>(irb, scope, source_node);
-
-    return &instruction->base;
-}
-
-static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
-    results[ReturnKindUnconditional] = 0;
-    results[ReturnKindError] = 0;
-
-    Scope *scope = inner_scope;
-
-    while (scope != outer_scope) {
-        assert(scope);
-        switch (scope->id) {
-            case ScopeIdDefer: {
-                AstNode *defer_node = scope->source_node;
-                assert(defer_node->type == NodeTypeDefer);
-                ReturnKind defer_kind = defer_node->data.defer.kind;
-                results[defer_kind] += 1;
-                scope = scope->parent;
-                continue;
-            }
-            case ScopeIdDecls:
-            case ScopeIdFnDef:
-                return;
-            case ScopeIdBlock:
-            case ScopeIdVarDecl:
-            case ScopeIdLoop:
-            case ScopeIdSuspend:
-            case ScopeIdCompTime:
-            case ScopeIdNoSuspend:
-            case ScopeIdRuntime:
-            case ScopeIdTypeOf:
-            case ScopeIdExpr:
-                scope = scope->parent;
-                continue;
-            case ScopeIdDeferExpr:
-            case ScopeIdCImport:
-                zig_unreachable();
-        }
-    }
-}
-
-static IrInstSrc *ir_mark_gen(IrInstSrc *instruction) {
-    instruction->is_gen = true;
-    return instruction;
-}
-
-static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, bool *is_noreturn, IrInstSrc *err_value) {
-    Scope *scope = inner_scope;
-    if (is_noreturn != nullptr) *is_noreturn = false;
-    while (scope != outer_scope) {
-        if (!scope)
-            return true;
-
-        switch (scope->id) {
-            case ScopeIdDefer: {
-                AstNode *defer_node = scope->source_node;
-                assert(defer_node->type == NodeTypeDefer);
-                ReturnKind defer_kind = defer_node->data.defer.kind;
-                AstNode *defer_expr_node = defer_node->data.defer.expr;
-                AstNode *defer_var_node = defer_node->data.defer.err_payload;
-
-                if (defer_kind == ReturnKindError && err_value == nullptr) {
-                    // This is an `errdefer` but we're generating code for a
-                    // `return` that doesn't return an error, skip it
-                    scope = scope->parent;
-                    continue;
-                }
-
-                Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
-                if (defer_var_node != nullptr) {
-                    assert(defer_kind == ReturnKindError);
-                    assert(defer_var_node->type == NodeTypeSymbol);
-                    Buf *var_name = defer_var_node->data.symbol_expr.symbol;
-
-                    if (defer_expr_node->type == NodeTypeUnreachable) {
-                        add_node_error(irb->codegen, defer_var_node,
-                            buf_sprintf("unused variable: '%s'", buf_ptr(var_name)));
-                        return false;
-                    }
-
-                    IrInstSrc *is_comptime;
-                    if (ir_should_inline(irb->exec, defer_expr_scope)) {
-                        is_comptime = ir_build_const_bool(irb, defer_expr_scope,
-                            defer_expr_node, true);
-                    } else {
-                        is_comptime = ir_build_test_comptime(irb, defer_expr_scope,
-                            defer_expr_node, err_value);
-                    }
-
-                    ZigVar *err_var = ir_create_var(irb, defer_var_node, defer_expr_scope,
-                        var_name, true, true, false, is_comptime);
-                    build_decl_var_and_init(irb, defer_expr_scope, defer_var_node, err_var, err_value,
-                        buf_ptr(var_name), is_comptime);
-
-                    defer_expr_scope = err_var->child_scope;
-                }
-
-                IrInstSrc *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
-                if (defer_expr_value == irb->codegen->invalid_inst_src)
-                    return irb->codegen->invalid_inst_src;
-
-                if (defer_expr_value->is_noreturn) {
-                    if (is_noreturn != nullptr) *is_noreturn = true;
-                } else {
-                    ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node,
-                                defer_expr_value));
-                }
-                scope = scope->parent;
-                continue;
-            }
-            case ScopeIdDecls:
-            case ScopeIdFnDef:
-                return true;
-            case ScopeIdBlock:
-            case ScopeIdVarDecl:
-            case ScopeIdLoop:
-            case ScopeIdSuspend:
-            case ScopeIdCompTime:
-            case ScopeIdNoSuspend:
-            case ScopeIdRuntime:
-            case ScopeIdTypeOf:
-            case ScopeIdExpr:
-                scope = scope->parent;
-                continue;
-            case ScopeIdDeferExpr:
-            case ScopeIdCImport:
-                zig_unreachable();
-        }
-    }
-    return true;
-}
-
-static void ir_set_cursor_at_end_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) {
-    assert(basic_block);
-    irb->current_basic_block = basic_block;
-}
-
-static void ir_set_cursor_at_end(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) {
-    assert(basic_block);
-    irb->current_basic_block = basic_block;
-}
-
-static void ir_append_basic_block_gen(IrBuilderGen *irb, IrBasicBlockGen *bb) {
-    assert(!bb->already_appended);
-    bb->already_appended = true;
-    irb->exec->basic_block_list.append(bb);
-}
-
-static void ir_set_cursor_at_end_and_append_block_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) {
-    ir_append_basic_block_gen(irb, basic_block);
-    ir_set_cursor_at_end_gen(irb, basic_block);
-}
-
-static void ir_set_cursor_at_end_and_append_block(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) {
-    basic_block->index = irb->exec->basic_block_list.length;
-    irb->exec->basic_block_list.append(basic_block);
-    ir_set_cursor_at_end(irb, basic_block);
-}
-
-static ScopeSuspend *get_scope_suspend(Scope *scope) {
-    while (scope) {
-        if (scope->id == ScopeIdSuspend)
-            return (ScopeSuspend *)scope;
-        if (scope->id == ScopeIdFnDef)
-            return nullptr;
-
-        scope = scope->parent;
-    }
-    return nullptr;
-}
-
-static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {
-    while (scope) {
-        if (scope->id == ScopeIdDeferExpr)
-            return (ScopeDeferExpr *)scope;
-        if (scope->id == ScopeIdFnDef)
-            return nullptr;
-
-        scope = scope->parent;
-    }
-    return nullptr;
-}
-
-static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
-    assert(node->type == NodeTypeReturnExpr);
-
-    ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope);
-    if (scope_defer_expr) {
-        if (!scope_defer_expr->reported_err) {
-            add_node_error(irb->codegen, node, buf_sprintf("cannot return from defer expression"));
-            scope_defer_expr->reported_err = true;
-        }
-        return irb->codegen->invalid_inst_src;
-    }
-
-    Scope *outer_scope = irb->exec->begin_scope;
-
-    AstNode *expr_node = node->data.return_expr.expr;
-    switch (node->data.return_expr.kind) {
-        case ReturnKindUnconditional:
-            {
-                ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
-                result_loc_ret->base.id = ResultLocIdReturn;
-                ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
-
-                IrInstSrc *return_value;
-                if (expr_node) {
-                    // Temporarily set this so that if we return a type it gets the name of the function
-                    ZigFn *prev_name_fn = irb->exec->name_fn;
-                    irb->exec->name_fn = exec_fn_entry(irb->exec);
-                    return_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, &result_loc_ret->base);
-                    irb->exec->name_fn = prev_name_fn;
-                    if (return_value == irb->codegen->invalid_inst_src)
-                        return irb->codegen->invalid_inst_src;
-                } else {
-                    return_value = ir_build_const_void(irb, scope, node);
-                    ir_build_end_expr(irb, scope, node, return_value, &result_loc_ret->base);
-                }
-
-                ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value, result_loc_ret));
-
-                size_t defer_counts[2];
-                ir_count_defers(irb, scope, outer_scope, defer_counts);
-                bool have_err_defers = defer_counts[ReturnKindError] > 0;
-                if (!have_err_defers && !irb->codegen->have_err_ret_tracing) {
-                    // only generate unconditional defers
-                    if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr))
-                        return irb->codegen->invalid_inst_src;
-                    IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr);
-                    result_loc_ret->base.source_instruction = result;
-                    return result;
-                }
-                bool should_inline = ir_should_inline(irb->exec, scope);
-
-                IrBasicBlockSrc *err_block = ir_create_basic_block(irb, scope, "ErrRetErr");
-                IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk");
-
-                IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node, return_value, false, true);
-
-                IrInstSrc *is_comptime;
-                if (should_inline) {
-                    is_comptime = ir_build_const_bool(irb, scope, node, should_inline);
-                } else {
-                    is_comptime = ir_build_test_comptime(irb, scope, node, is_err);
-                }
-
-                ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime));
-                IrBasicBlockSrc *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt");
-
-                ir_set_cursor_at_end_and_append_block(irb, err_block);
-                if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, return_value))
-                    return irb->codegen->invalid_inst_src;
-                if (irb->codegen->have_err_ret_tracing && !should_inline) {
-                    ir_build_save_err_ret_addr_src(irb, scope, node);
-                }
-                ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
-
-                ir_set_cursor_at_end_and_append_block(irb, ok_block);
-                if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr))
-                    return irb->codegen->invalid_inst_src;
-                ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
-
-                ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block);
-                IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr);
-                result_loc_ret->base.source_instruction = result;
-                return result;
-            }
-        case ReturnKindError:
-            {
-                assert(expr_node);
-                IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
-                if (err_union_ptr == irb->codegen->invalid_inst_src)
-                    return irb->codegen->invalid_inst_src;
-                IrInstSrc *is_err_val = ir_build_test_err_src(irb, scope, node, err_union_ptr, true, false);
-
-                IrBasicBlockSrc *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn");
-                IrBasicBlockSrc *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue");
-                IrInstSrc *is_comptime;
-                bool should_inline = ir_should_inline(irb->exec, scope);
-                if (should_inline) {
-                    is_comptime = ir_build_const_bool(irb, scope, node, true);
-                } else {
-                    is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val);
-                }
-                ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime));
-
-                ir_set_cursor_at_end_and_append_block(irb, return_block);
-                IrInstSrc *err_val_ptr = ir_build_unwrap_err_code_src(irb, scope, node, err_union_ptr);
-                IrInstSrc *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr);
-                ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr));
-                IrInstSrcSpillBegin *spill_begin = ir_build_spill_begin_src(irb, scope, node, err_val,
-                        SpillIdRetErrCode);
-                ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
-                result_loc_ret->base.id = ResultLocIdReturn;
-                ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
-                ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base);
-
-                bool is_noreturn = false;
-                if (!ir_gen_defers_for_block(irb, scope, outer_scope, &is_noreturn, err_val)) {
-                    return irb->codegen->invalid_inst_src;
-                }
-                if (!is_noreturn) {
-                    if (irb->codegen->have_err_ret_tracing && !should_inline) {
-                        ir_build_save_err_ret_addr_src(irb, scope, node);
-                    }
-                    err_val = ir_build_spill_end_src(irb, scope, node, spill_begin);
-                    IrInstSrc *ret_inst = ir_build_return_src(irb, scope, node, err_val);
-                    result_loc_ret->base.source_instruction = ret_inst;
-                }
-
-                ir_set_cursor_at_end_and_append_block(irb, continue_block);
-                IrInstSrc *unwrapped_ptr = ir_build_unwrap_err_payload_src(irb, scope, node, err_union_ptr, false, false);
-                if (lval == LValPtr)
-                    return unwrapped_ptr;
-                else
-                    return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, unwrapped_ptr), result_loc);
-            }
-    }
-    zig_unreachable();
-}
-
-static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope,
-        Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime,
-        bool skip_name_check)
-{
-    ZigVar *variable_entry = heap::c_allocator.create<ZigVar>();
-    variable_entry->parent_scope = parent_scope;
-    variable_entry->shadowable = is_shadowable;
-    variable_entry->is_comptime = is_comptime;
-    variable_entry->src_arg_index = SIZE_MAX;
-    variable_entry->const_value = codegen->pass1_arena->create<ZigValue>();
-
-    if (is_comptime != nullptr) {
-        is_comptime->base.ref_count += 1;
-    }
-
-    if (name) {
-        variable_entry->name = strdup(buf_ptr(name));
-
-        if (!skip_name_check) {
-            ZigVar *existing_var = find_variable(codegen, parent_scope, name, nullptr);
-            if (existing_var && !existing_var->shadowable) {
-                if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) {
-                    ErrorMsg *msg = add_node_error(codegen, node,
-                            buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
-                    add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
-                }
-                variable_entry->var_type = codegen->builtin_types.entry_invalid;
-            } else {
-                ZigType *type;
-                if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) {
-                    add_node_error(codegen, node,
-                            buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name)));
-                    variable_entry->var_type = codegen->builtin_types.entry_invalid;
-                } else {
-                    Tld *tld = find_decl(codegen, parent_scope, name);
-                    if (tld != nullptr) {
-                        bool want_err_msg = true;
-                        if (tld->id == TldIdVar) {
-                            ZigVar *var = reinterpret_cast<TldVar *>(tld)->var;
-                            if (var != nullptr && var->var_type != nullptr && type_is_invalid(var->var_type)) {
-                                want_err_msg = false;
-                            }
-                        }
-                        if (want_err_msg) {
-                            ErrorMsg *msg = add_node_error(codegen, node,
-                                    buf_sprintf("redefinition of '%s'", buf_ptr(name)));
-                            add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here"));
-                        }
-                        variable_entry->var_type = codegen->builtin_types.entry_invalid;
-                    }
-                }
-            }
-        }
-    } else {
-        assert(is_shadowable);
-        // TODO make this name not actually be in scope. user should be able to make a variable called "_anon"
-        // might already be solved, let's just make sure it has test coverage
-        // maybe we put a prefix on this so the debug info doesn't clobber user debug info for same named variables
-        variable_entry->name = "_anon";
-    }
-
-    variable_entry->src_is_const = src_is_const;
-    variable_entry->gen_is_const = gen_is_const;
-    variable_entry->decl_node = node;
-    variable_entry->child_scope = create_var_scope(codegen, node, parent_scope, variable_entry);
-
-    return variable_entry;
-}
-
-// Set name to nullptr to make the variable anonymous (not visible to programmer).
-// After you call this function var->child_scope has the variable in scope
-static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name,
-        bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime)
-{
-    bool is_underscored = name ? buf_eql_str(name, "_") : false;
-    ZigVar *var = create_local_var(irb->codegen, node, scope,
-            (is_underscored ? nullptr : name), src_is_const, gen_is_const,
-            (is_underscored ? true : is_shadowable), is_comptime, false);
-    assert(var->child_scope);
-    return var;
-}
-
-static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) {
-    ResultLocPeer *result = heap::c_allocator.create<ResultLocPeer>();
-    result->base.id = ResultLocIdPeer;
-    result->base.source_instruction = peer_parent->base.source_instruction;
-    result->parent = peer_parent;
-    result->base.allow_write_through_const = peer_parent->parent->allow_write_through_const;
-    return result;
-}
-
-static bool is_duplicate_label(CodeGen *g, Scope *scope, AstNode *node, Buf *name) {
-    if (name == nullptr) return false;
-
-    for (;;) {
-        if (scope == nullptr || scope->id == ScopeIdFnDef) {
-            break;
-        } else if (scope->id == ScopeIdBlock || scope->id == ScopeIdLoop) {
-            Buf *this_block_name = scope->id == ScopeIdBlock ? ((ScopeBlock *)scope)->name : ((ScopeLoop *)scope)->name;
-            if (this_block_name != nullptr && buf_eql_buf(name, this_block_name)) {
-                ErrorMsg *msg = add_node_error(g, node, buf_sprintf("redeclaration of label '%s'", buf_ptr(name)));
-                add_error_note(g, msg, scope->source_node, buf_sprintf("previous declaration is here"));
-                return true;
-            }
-        }
-        scope = scope->parent;
-    }
-    return false;
-}
-
-static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *block_node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(block_node->type == NodeTypeBlock);
-
-    ZigList<IrInstSrc *> incoming_values = {0};
-    ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
-
-    if (is_duplicate_label(irb->codegen, parent_scope, block_node, block_node->data.block.name))
-        return irb->codegen->invalid_inst_src;
-
-    ScopeBlock *scope_block = create_block_scope(irb->codegen, block_node, parent_scope);
-
-    Scope *outer_block_scope = &scope_block->base;
-    Scope *child_scope = outer_block_scope;
-
-    ZigFn *fn_entry = scope_fn_entry(parent_scope);
-    if (fn_entry && fn_entry->child_scope == parent_scope) {
-        fn_entry->def_scope = scope_block;
-    }
-
-    if (block_node->data.block.statements.length == 0) {
-        if (scope_block->name != nullptr) {
-            add_node_error(irb->codegen, block_node, buf_sprintf("unused block label"));
-        }
-        // {}
-        return ir_lval_wrap(irb, parent_scope, ir_build_const_void(irb, child_scope, block_node), lval, result_loc);
-    }
-
-    if (block_node->data.block.name != nullptr) {
-        scope_block->lval = lval;
-        scope_block->incoming_blocks = &incoming_blocks;
-        scope_block->incoming_values = &incoming_values;
-        scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd");
-        scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node,
-                ir_should_inline(irb->exec, parent_scope));
-
-        scope_block->peer_parent = heap::c_allocator.create<ResultLocPeerParent>();
-        scope_block->peer_parent->base.id = ResultLocIdPeerParent;
-        scope_block->peer_parent->base.source_instruction = scope_block->is_comptime;
-        scope_block->peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const;
-        scope_block->peer_parent->end_bb = scope_block->end_block;
-        scope_block->peer_parent->is_comptime = scope_block->is_comptime;
-        scope_block->peer_parent->parent = result_loc;
-        ir_build_reset_result(irb, parent_scope, block_node, &scope_block->peer_parent->base);
-    }
-
-    bool is_continuation_unreachable = false;
-    bool found_invalid_inst = false;
-    IrInstSrc *noreturn_return_value = nullptr;
-    for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) {
-        AstNode *statement_node = block_node->data.block.statements.at(i);
-
-        IrInstSrc *statement_value = ir_gen_node(irb, statement_node, child_scope);
-        if (statement_value == irb->codegen->invalid_inst_src) {
-            // keep generating all the elements of the block in case of error,
-            // we want to collect other compile errors
-            found_invalid_inst = true;
-            continue;
-        }
-
-        is_continuation_unreachable = instr_is_unreachable(statement_value);
-        if (is_continuation_unreachable) {
-            // keep the last noreturn statement value around in case we need to return it
-            noreturn_return_value = statement_value;
-        }
-        // This logic must be kept in sync with
-        // [STMT_EXPR_TEST_THING] <--- (search this token)
-        if (statement_node->type == NodeTypeDefer) {
-            // defer starts a new scope
-            child_scope = statement_node->data.defer.child_scope;
-            assert(child_scope);
-        } else if (statement_value->id == IrInstSrcIdDeclVar) {
-            // variable declarations start a new scope
-            IrInstSrcDeclVar *decl_var_instruction = (IrInstSrcDeclVar *)statement_value;
-            child_scope = decl_var_instruction->var->child_scope;
-        } else if (!is_continuation_unreachable) {
-            // this statement's value must be void
-            ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value));
-        }
-    }
-
-    if (scope_block->name != nullptr && scope_block->name_used == false) {
-        add_node_error(irb->codegen, block_node, buf_sprintf("unused block label"));
-    }
-
-    if (found_invalid_inst)
-        return irb->codegen->invalid_inst_src;
-
-    if (is_continuation_unreachable) {
-        assert(noreturn_return_value != nullptr);
-        if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) {
-            return noreturn_return_value;
-        }
-
-        if (scope_block->peer_parent != nullptr && scope_block->peer_parent->peers.length != 0) {
-            scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block;
-        }
-        ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
-        IrInstSrc *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length,
-                incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
-        return ir_expr_wrap(irb, parent_scope, phi, result_loc);
-    } else {
-        incoming_blocks.append(irb->current_basic_block);
-        IrInstSrc *else_expr_result = ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node));
-
-        if (scope_block->peer_parent != nullptr) {
-            ResultLocPeer *peer_result = create_peer_result(scope_block->peer_parent);
-            scope_block->peer_parent->peers.append(peer_result);
-            ir_build_end_expr(irb, parent_scope, block_node, else_expr_result, &peer_result->base);
-
-            if (scope_block->peer_parent->peers.length != 0) {
-                scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block;
-            }
-        }
-
-        incoming_values.append(else_expr_result);
-    }
-
-    bool is_return_from_fn = block_node == irb->main_block_node;
-    if (!is_return_from_fn) {
-        if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr))
-            return irb->codegen->invalid_inst_src;
-    }
-
-    IrInstSrc *result;
-    if (block_node->data.block.name != nullptr) {
-        ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime));
-        ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
-        IrInstSrc *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length,
-                incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
-        result = ir_expr_wrap(irb, parent_scope, phi, result_loc);
-    } else {
-        IrInstSrc *void_inst = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
-        result = ir_lval_wrap(irb, parent_scope, void_inst, lval, result_loc);
-    }
-    if (!is_return_from_fn)
-        return result;
-
-    // no need for save_err_ret_addr because this cannot return error
-    // only generate unconditional defers
-
-    ir_mark_gen(ir_build_add_implicit_return_type(irb, child_scope, block_node, result, nullptr));
-    ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
-    result_loc_ret->base.id = ResultLocIdReturn;
-    ir_build_reset_result(irb, parent_scope, block_node, &result_loc_ret->base);
-    ir_mark_gen(ir_build_end_expr(irb, parent_scope, block_node, result, &result_loc_ret->base));
-    if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr))
-        return irb->codegen->invalid_inst_src;
-    return ir_mark_gen(ir_build_return_src(irb, child_scope, result->base.source_node, result));
-}
-
-static IrInstSrc *ir_gen_bin_op_id(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
-    Scope *inner_scope = scope;
-    if (op_id == IrBinOpArrayCat || op_id == IrBinOpArrayMult) {
-        inner_scope = create_comptime_scope(irb->codegen, node, scope);
-    }
-
-    IrInstSrc *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, inner_scope);
-    IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, inner_scope);
-
-    if (op1 == irb->codegen->invalid_inst_src || op2 == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    return ir_build_bin_op(irb, scope, node, op_id, op1, op2, true);
-}
-
-static IrInstSrc *ir_gen_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    IrInstSrc *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
-    IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
-
-    if (op1 == irb->codegen->invalid_inst_src || op2 == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    // TODO only pass type_name when the || operator is the top level AST node in the var decl expr
-    Buf bare_name = BUF_INIT;
-    Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", scope, node, &bare_name);
-
-    return ir_build_merge_err_sets(irb, scope, node, op1, op2, type_name);
-}
-
-static IrInstSrc *ir_gen_assign(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr);
-    if (lvalue == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
-    result_loc_inst->base.id = ResultLocIdInstruction;
-    result_loc_inst->base.source_instruction = lvalue;
-    ir_ref_instruction(lvalue, irb->current_basic_block);
-    ir_build_reset_result(irb, scope, node, &result_loc_inst->base);
-
-    IrInstSrc *rvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op2, scope, LValNone,
-            &result_loc_inst->base);
-    if (rvalue == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    return ir_build_const_void(irb, scope, node);
-}
-
-static IrInstSrc *ir_gen_assign_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
-    IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr);
-    if (lvalue == irb->codegen->invalid_inst_src)
-        return lvalue;
-    IrInstSrc *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue);
-    IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
-    if (op2 == irb->codegen->invalid_inst_src)
-        return op2;
-    IrInstSrc *result = ir_build_bin_op(irb, scope, node, op_id, op1, op2, true);
-    ir_build_store_ptr(irb, scope, node, lvalue, result);
-    return ir_build_const_void(irb, scope, node);
-}
-
-static IrInstSrc *ir_gen_bool_or(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeBinOpExpr);
-
-    IrInstSrc *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
-    if (val1 == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-    IrBasicBlockSrc *post_val1_block = irb->current_basic_block;
-
-    IrInstSrc *is_comptime;
-    if (ir_should_inline(irb->exec, scope)) {
-        is_comptime = ir_build_const_bool(irb, scope, node, true);
-    } else {
-        is_comptime = ir_build_test_comptime(irb, scope, node, val1);
-    }
-
-    // block for when val1 == false
-    IrBasicBlockSrc *false_block = ir_create_basic_block(irb, scope, "BoolOrFalse");
-    // block for when val1 == true (don't even evaluate the second part)
-    IrBasicBlockSrc *true_block = ir_create_basic_block(irb, scope, "BoolOrTrue");
-
-    ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, false_block);
-    IrInstSrc *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
-    if (val2 == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-    IrBasicBlockSrc *post_val2_block = irb->current_basic_block;
-
-    ir_build_br(irb, scope, node, true_block, is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, true_block);
-
-    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
-    incoming_values[0] = val1;
-    incoming_values[1] = val2;
-    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
-    incoming_blocks[0] = post_val1_block;
-    incoming_blocks[1] = post_val2_block;
-
-    return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr);
-}
-
-static IrInstSrc *ir_gen_bool_and(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeBinOpExpr);
-
-    IrInstSrc *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
-    if (val1 == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-    IrBasicBlockSrc *post_val1_block = irb->current_basic_block;
-
-    IrInstSrc *is_comptime;
-    if (ir_should_inline(irb->exec, scope)) {
-        is_comptime = ir_build_const_bool(irb, scope, node, true);
-    } else {
-        is_comptime = ir_build_test_comptime(irb, scope, node, val1);
-    }
-
-    // block for when val1 == true
-    IrBasicBlockSrc *true_block = ir_create_basic_block(irb, scope, "BoolAndTrue");
-    // block for when val1 == false (don't even evaluate the second part)
-    IrBasicBlockSrc *false_block = ir_create_basic_block(irb, scope, "BoolAndFalse");
-
-    ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, true_block);
-    IrInstSrc *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
-    if (val2 == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-    IrBasicBlockSrc *post_val2_block = irb->current_basic_block;
-
-    ir_build_br(irb, scope, node, false_block, is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, false_block);
-
-    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
-    incoming_values[0] = val1;
-    incoming_values[1] = val2;
-    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
-    incoming_blocks[0] = post_val1_block;
-    incoming_blocks[1] = post_val2_block;
-
-    return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr);
-}
-
-static ResultLocPeerParent *ir_build_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst,
-        IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime)
-{
-    ResultLocPeerParent *peer_parent = heap::c_allocator.create<ResultLocPeerParent>();
-    peer_parent->base.id = ResultLocIdPeerParent;
-    peer_parent->base.source_instruction = cond_br_inst;
-    peer_parent->base.allow_write_through_const = parent->allow_write_through_const;
-    peer_parent->end_bb = end_block;
-    peer_parent->is_comptime = is_comptime;
-    peer_parent->parent = parent;
-
-    IrInstSrc *popped_inst = irb->current_basic_block->instruction_list.pop();
-    ir_assert(popped_inst == cond_br_inst, &cond_br_inst->base);
-
-    ir_build_reset_result(irb, cond_br_inst->base.scope, cond_br_inst->base.source_node, &peer_parent->base);
-    irb->current_basic_block->instruction_list.append(popped_inst);
-
-    return peer_parent;
-}
-
-static ResultLocPeerParent *ir_build_binary_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst,
-        IrBasicBlockSrc *else_block, IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime)
-{
-    ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, parent, is_comptime);
-
-    peer_parent->peers.append(create_peer_result(peer_parent));
-    peer_parent->peers.last()->next_bb = else_block;
-
-    peer_parent->peers.append(create_peer_result(peer_parent));
-    peer_parent->peers.last()->next_bb = end_block;
-
-    return peer_parent;
-}
-
-static IrInstSrc *ir_gen_orelse(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(node->type == NodeTypeBinOpExpr);
-
-    AstNode *op1_node = node->data.bin_op_expr.op1;
-    AstNode *op2_node = node->data.bin_op_expr.op2;
-
-    IrInstSrc *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr, nullptr);
-    if (maybe_ptr == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr);
-    IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, parent_scope, node, maybe_val);
-
-    IrInstSrc *is_comptime;
-    if (ir_should_inline(irb->exec, parent_scope)) {
-        is_comptime = ir_build_const_bool(irb, parent_scope, node, true);
-    } else {
-        is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null);
-    }
-
-    IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull");
-    IrBasicBlockSrc *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull");
-    IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd");
-    IrInstSrc *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime);
-
-    ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block,
-            result_loc, is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, null_block);
-    IrInstSrc *null_result = ir_gen_node_extra(irb, op2_node, parent_scope, LValNone,
-            &peer_parent->peers.at(0)->base);
-    if (null_result == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-    IrBasicBlockSrc *after_null_block = irb->current_basic_block;
-    if (!instr_is_unreachable(null_result))
-        ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
-
-    ir_set_cursor_at_end_and_append_block(irb, ok_block);
-    IrInstSrc *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, parent_scope, node, maybe_ptr, false);
-    IrInstSrc *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
-    ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base);
-    IrBasicBlockSrc *after_ok_block = irb->current_basic_block;
-    ir_build_br(irb, parent_scope, node, end_block, is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, end_block);
-    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
-    incoming_values[0] = null_result;
-    incoming_values[1] = unwrapped_payload;
-    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
-    incoming_blocks[0] = after_null_block;
-    incoming_blocks[1] = after_ok_block;
-    IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
-    return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
-}
-
-static IrInstSrc *ir_gen_error_union(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
-    assert(node->type == NodeTypeBinOpExpr);
-
-    AstNode *op1_node = node->data.bin_op_expr.op1;
-    AstNode *op2_node = node->data.bin_op_expr.op2;
-
-    IrInstSrc *err_set = ir_gen_node(irb, op1_node, parent_scope);
-    if (err_set == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *payload = ir_gen_node(irb, op2_node, parent_scope);
-    if (payload == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    return ir_build_error_union(irb, parent_scope, node, err_set, payload);
-}
-
-static IrInstSrc *ir_gen_bin_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
-    assert(node->type == NodeTypeBinOpExpr);
-
-    BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
-    switch (bin_op_type) {
-        case BinOpTypeInvalid:
-            zig_unreachable();
-        case BinOpTypeAssign:
-            return ir_lval_wrap(irb, scope, ir_gen_assign(irb, scope, node), lval, result_loc);
-        case BinOpTypeAssignTimes:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMult), lval, result_loc);
-        case BinOpTypeAssignTimesWrap:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap), lval, result_loc);
-        case BinOpTypeAssignDiv:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc);
-        case BinOpTypeAssignMod:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc);
-        case BinOpTypeAssignPlus:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAdd), lval, result_loc);
-        case BinOpTypeAssignPlusWrap:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap), lval, result_loc);
-        case BinOpTypeAssignMinus:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSub), lval, result_loc);
-        case BinOpTypeAssignMinusWrap:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap), lval, result_loc);
-        case BinOpTypeAssignBitShiftLeft:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
-        case BinOpTypeAssignBitShiftRight:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
-        case BinOpTypeAssignBitAnd:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd), lval, result_loc);
-        case BinOpTypeAssignBitXor:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinXor), lval, result_loc);
-        case BinOpTypeAssignBitOr:
-            return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinOr), lval, result_loc);
-        case BinOpTypeBoolOr:
-            return ir_lval_wrap(irb, scope, ir_gen_bool_or(irb, scope, node), lval, result_loc);
-        case BinOpTypeBoolAnd:
-            return ir_lval_wrap(irb, scope, ir_gen_bool_and(irb, scope, node), lval, result_loc);
-        case BinOpTypeCmpEq:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq), lval, result_loc);
-        case BinOpTypeCmpNotEq:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq), lval, result_loc);
-        case BinOpTypeCmpLessThan:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan), lval, result_loc);
-        case BinOpTypeCmpGreaterThan:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan), lval, result_loc);
-        case BinOpTypeCmpLessOrEq:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq), lval, result_loc);
-        case BinOpTypeCmpGreaterOrEq:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq), lval, result_loc);
-        case BinOpTypeBinOr:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr), lval, result_loc);
-        case BinOpTypeBinXor:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor), lval, result_loc);
-        case BinOpTypeBinAnd:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd), lval, result_loc);
-        case BinOpTypeBitShiftLeft:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
-        case BinOpTypeBitShiftRight:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
-        case BinOpTypeAdd:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd), lval, result_loc);
-        case BinOpTypeAddWrap:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap), lval, result_loc);
-        case BinOpTypeSub:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSub), lval, result_loc);
-        case BinOpTypeSubWrap:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap), lval, result_loc);
-        case BinOpTypeMult:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMult), lval, result_loc);
-        case BinOpTypeMultWrap:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap), lval, result_loc);
-        case BinOpTypeDiv:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc);
-        case BinOpTypeMod:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc);
-        case BinOpTypeArrayCat:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat), lval, result_loc);
-        case BinOpTypeArrayMult:
-            return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult), lval, result_loc);
-        case BinOpTypeMergeErrorSets:
-            return ir_lval_wrap(irb, scope, ir_gen_merge_err_sets(irb, scope, node), lval, result_loc);
-        case BinOpTypeUnwrapOptional:
-            return ir_gen_orelse(irb, scope, node, lval, result_loc);
-        case BinOpTypeErrorUnion:
-            return ir_lval_wrap(irb, scope, ir_gen_error_union(irb, scope, node), lval, result_loc);
-    }
-    zig_unreachable();
-}
-
-static IrInstSrc *ir_gen_int_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeIntLiteral);
-
-    return ir_build_const_bigint(irb, scope, node, node->data.int_literal.bigint);
-}
-
-static IrInstSrc *ir_gen_float_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeFloatLiteral);
-
-    if (node->data.float_literal.overflow) {
-        add_node_error(irb->codegen, node, buf_sprintf("float literal out of range of any type"));
-        return irb->codegen->invalid_inst_src;
-    }
-
-    return ir_build_const_bigfloat(irb, scope, node, node->data.float_literal.bigfloat);
-}
-
-static IrInstSrc *ir_gen_char_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeCharLiteral);
-
-    return ir_build_const_uint(irb, scope, node, node->data.char_literal.value);
-}
-
-static IrInstSrc *ir_gen_null_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeNullLiteral);
-
-    return ir_build_const_null(irb, scope, node);
-}
-
-static void populate_invalid_variable_in_scope(CodeGen *g, Scope *scope, AstNode *node, Buf *var_name) {
-    ScopeDecls *scope_decls = nullptr;
-    while (scope != nullptr) {
-        if (scope->id == ScopeIdDecls) {
-            scope_decls = reinterpret_cast<ScopeDecls *>(scope);
-        }
-        scope = scope->parent;
-    }
-    TldVar *tld_var = heap::c_allocator.create<TldVar>();
-    init_tld(&tld_var->base, TldIdVar, var_name, VisibModPub, node, &scope_decls->base);
-    tld_var->base.resolution = TldResolutionInvalid;
-    tld_var->var = add_variable(g, node, &scope_decls->base, var_name, false,
-            g->invalid_inst_gen->value, &tld_var->base, g->builtin_types.entry_invalid);
-    scope_decls->decl_table.put(var_name, &tld_var->base);
-}
-
-static IrInstSrc *ir_gen_symbol(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
-    Error err;
-    assert(node->type == NodeTypeSymbol);
-
-    Buf *variable_name = node->data.symbol_expr.symbol;
-
-    if (buf_eql_str(variable_name, "_")) {
-        if (lval == LValAssign) {
-            IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, node);
-            const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
-            const_instruction->value->type = get_pointer_to_type(irb->codegen,
-                    irb->codegen->builtin_types.entry_void, false);
-            const_instruction->value->special = ConstValSpecialStatic;
-            const_instruction->value->data.x_ptr.special = ConstPtrSpecialDiscard;
-            return &const_instruction->base;
-        } else {
-            add_node_error(irb->codegen, node, buf_sprintf("`_` may only be used to assign things to"));
-            return irb->codegen->invalid_inst_src;
-        }
-    }
-
-    ZigType *primitive_type;
-    if ((err = get_primitive_type(irb->codegen, variable_name, &primitive_type))) {
-        if (err == ErrorOverflow) {
-            add_node_error(irb->codegen, node,
-                buf_sprintf("primitive integer type '%s' exceeds maximum bit width of 65535",
-                    buf_ptr(variable_name)));
-            return irb->codegen->invalid_inst_src;
-        }
-        assert(err == ErrorPrimitiveTypeNotFound);
-    } else {
-        IrInstSrc *value = ir_build_const_type(irb, scope, node, primitive_type);
-        if (lval == LValPtr || lval == LValAssign) {
-            return ir_build_ref_src(irb, scope, node, value);
-        } else {
-            return ir_expr_wrap(irb, scope, value, result_loc);
-        }
-    }
-
-    ScopeFnDef *crossed_fndef_scope;
-    ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope);
-    if (var) {
-        IrInstSrc *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope);
-        if (lval == LValPtr || lval == LValAssign) {
-            return var_ptr;
-        } else {
-            return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, var_ptr), result_loc);
-        }
-    }
-
-    Tld *tld = find_decl(irb->codegen, scope, variable_name);
-    if (tld) {
-        IrInstSrc *decl_ref = ir_build_decl_ref(irb, scope, node, tld, lval);
-        if (lval == LValPtr || lval == LValAssign) {
-            return decl_ref;
-        } else {
-            return ir_expr_wrap(irb, scope, decl_ref, result_loc);
-        }
-    }
-
-    if (get_container_scope(node->owner)->any_imports_failed) {
-        // skip the error message since we had a failing import in this file
-        // if an import breaks we don't need redundant undeclared identifier errors
-        return irb->codegen->invalid_inst_src;
-    }
-
-    return ir_build_undeclared_identifier(irb, scope, node, variable_name);
-}
-
-static IrInstSrc *ir_gen_array_access(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(node->type == NodeTypeArrayAccessExpr);
-
-    AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr;
-    IrInstSrc *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr, nullptr);
-    if (array_ref_instruction == irb->codegen->invalid_inst_src)
-        return array_ref_instruction;
-
-    // Create an usize-typed result location to hold the subscript value, this
-    // makes it possible for the compiler to infer the subscript expression type
-    // if needed
-    IrInstSrc *usize_type_inst = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
-    ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, usize_type_inst, no_result_loc());
-
-    AstNode *subscript_node = node->data.array_access_expr.subscript;
-    IrInstSrc *subscript_value = ir_gen_node_extra(irb, subscript_node, scope, LValNone, &result_loc_cast->base);
-    if (subscript_value == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *subscript_instruction = ir_build_implicit_cast(irb, scope, subscript_node, subscript_value, result_loc_cast);
-
-    IrInstSrc *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction,
-            subscript_instruction, true, PtrLenSingle, nullptr);
-    if (lval == LValPtr || lval == LValAssign)
-        return ptr_instruction;
-
-    IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
-    return ir_expr_wrap(irb, scope, load_ptr, result_loc);
-}
-
-static IrInstSrc *ir_gen_field_access(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeFieldAccessExpr);
-
-    AstNode *container_ref_node = node->data.field_access_expr.struct_expr;
-    Buf *field_name = node->data.field_access_expr.field_name;
-
-    IrInstSrc *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr, nullptr);
-    if (container_ref_instruction == irb->codegen->invalid_inst_src)
-        return container_ref_instruction;
-
-    return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name, false);
-}
-
-static IrInstSrc *ir_gen_overflow_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrOverflowOp op) {
-    assert(node->type == NodeTypeFnCallExpr);
-
-    AstNode *type_node = node->data.fn_call_expr.params.at(0);
-    AstNode *op1_node = node->data.fn_call_expr.params.at(1);
-    AstNode *op2_node = node->data.fn_call_expr.params.at(2);
-    AstNode *result_ptr_node = node->data.fn_call_expr.params.at(3);
-
-
-    IrInstSrc *type_value = ir_gen_node(irb, type_node, scope);
-    if (type_value == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *op1 = ir_gen_node(irb, op1_node, scope);
-    if (op1 == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *op2 = ir_gen_node(irb, op2_node, scope);
-    if (op2 == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *result_ptr = ir_gen_node(irb, result_ptr_node, scope);
-    if (result_ptr == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    return ir_build_overflow_op_src(irb, scope, node, op, type_value, op1, op2, result_ptr);
-}
-
-static IrInstSrc *ir_gen_mul_add(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeFnCallExpr);
-
-    AstNode *type_node = node->data.fn_call_expr.params.at(0);
-    AstNode *op1_node = node->data.fn_call_expr.params.at(1);
-    AstNode *op2_node = node->data.fn_call_expr.params.at(2);
-    AstNode *op3_node = node->data.fn_call_expr.params.at(3);
-
-    IrInstSrc *type_value = ir_gen_node(irb, type_node, scope);
-    if (type_value == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *op1 = ir_gen_node(irb, op1_node, scope);
-    if (op1 == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *op2 = ir_gen_node(irb, op2_node, scope);
-    if (op2 == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *op3 = ir_gen_node(irb, op3_node, scope);
-    if (op3 == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    return ir_build_mul_add_src(irb, scope, node, type_value, op1, op2, op3);
-}
-
-static IrInstSrc *ir_gen_this(IrBuilderSrc *irb, Scope *orig_scope, AstNode *node) {
-    for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) {
-        if (it_scope->id == ScopeIdDecls) {
-            ScopeDecls *decls_scope = (ScopeDecls *)it_scope;
-            ZigType *container_type = decls_scope->container_type;
-            if (container_type != nullptr) {
-                return ir_build_const_type(irb, orig_scope, node, container_type);
-            } else {
-                return ir_build_const_import(irb, orig_scope, node, decls_scope->import);
-            }
-        }
-    }
-    zig_unreachable();
-}
-
-static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *await_node, AstNode *call_node,
-        LVal lval, ResultLoc *result_loc)
-{
-    if (call_node->data.fn_call_expr.params.length != 4) {
-        add_node_error(irb->codegen, call_node,
-            buf_sprintf("expected 4 arguments, found %" ZIG_PRI_usize,
-                call_node->data.fn_call_expr.params.length));
-        return irb->codegen->invalid_inst_src;
-    }
-
-    AstNode *bytes_node = call_node->data.fn_call_expr.params.at(0);
-    IrInstSrc *bytes = ir_gen_node(irb, bytes_node, scope);
-    if (bytes == irb->codegen->invalid_inst_src)
-        return bytes;
-
-    AstNode *ret_ptr_node = call_node->data.fn_call_expr.params.at(1);
-    IrInstSrc *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope);
-    if (ret_ptr == irb->codegen->invalid_inst_src)
-        return ret_ptr;
-
-    AstNode *fn_ref_node = call_node->data.fn_call_expr.params.at(2);
-    IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
-    if (fn_ref == irb->codegen->invalid_inst_src)
-        return fn_ref;
-
-    CallModifier modifier = (await_node == nullptr) ? CallModifierAsync : CallModifierNone;
-    bool is_async_call_builtin = true;
-    AstNode *args_node = call_node->data.fn_call_expr.params.at(3);
-    if (args_node->type == NodeTypeContainerInitExpr) {
-        if (args_node->data.container_init_expr.kind == ContainerInitKindArray ||
-            args_node->data.container_init_expr.entries.length == 0)
-        {
-            size_t arg_count = args_node->data.container_init_expr.entries.length;
-            IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
-            for (size_t i = 0; i < arg_count; i += 1) {
-                AstNode *arg_node = args_node->data.container_init_expr.entries.at(i);
-                IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
-                if (arg == irb->codegen->invalid_inst_src)
-                    return arg;
-                args[i] = arg;
-            }
-
-            IrInstSrc *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args,
-                ret_ptr, modifier, is_async_call_builtin, bytes, result_loc);
-            return ir_lval_wrap(irb, scope, call, lval, result_loc);
-        } else {
-            exec_add_error_node(irb->codegen, irb->exec, args_node,
-                    buf_sprintf("TODO: @asyncCall with anon struct literal"));
-            return irb->codegen->invalid_inst_src;
-        }
-    }
-    IrInstSrc *args = ir_gen_node(irb, args_node, scope);
-    if (args == irb->codegen->invalid_inst_src)
-        return args;
-
-    IrInstSrc *call = ir_build_async_call_extra(irb, scope, call_node, modifier, fn_ref, ret_ptr, bytes, args, result_loc);
-    return ir_lval_wrap(irb, scope, call, lval, result_loc);
-}
-
-static IrInstSrc *ir_gen_fn_call_with_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        AstNode *fn_ref_node, CallModifier modifier, IrInstSrc *options,
-        AstNode **args_ptr, size_t args_len, LVal lval, ResultLoc *result_loc)
-{
-    IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
-    if (fn_ref == irb->codegen->invalid_inst_src)
-        return fn_ref;
-
-    IrInstSrc *fn_type = ir_build_typeof_1(irb, scope, source_node, fn_ref);
-
-    IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(args_len);
-    for (size_t i = 0; i < args_len; i += 1) {
-        AstNode *arg_node = args_ptr[i];
-
-        IrInstSrc *arg_index = ir_build_const_usize(irb, scope, arg_node, i);
-        IrInstSrc *arg_type = ir_build_arg_type(irb, scope, source_node, fn_type, arg_index, true);
-        ResultLoc *no_result = no_result_loc();
-        ir_build_reset_result(irb, scope, source_node, no_result);
-        ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, arg_type, no_result);
-
-        IrInstSrc *arg = ir_gen_node_extra(irb, arg_node, scope, LValNone, &result_loc_cast->base);
-        if (arg == irb->codegen->invalid_inst_src)
-            return arg;
-
-        args[i] = ir_build_implicit_cast(irb, scope, arg_node, arg, result_loc_cast);
-    }
-
-    IrInstSrc *fn_call;
-    if (options != nullptr) {
-        fn_call = ir_build_call_args(irb, scope, source_node, options, fn_ref, args, args_len, result_loc);
-    } else {
-        fn_call = ir_build_call_src(irb, scope, source_node, nullptr, fn_ref, args_len, args, nullptr,
-                modifier, false, nullptr, result_loc);
-    }
-    return ir_lval_wrap(irb, scope, fn_call, lval, result_loc);
-}
-
-static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(node->type == NodeTypeFnCallExpr);
-
-    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-    Buf *name = fn_ref_expr->data.symbol_expr.symbol;
-    auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
-
-    if (!entry) {
-        add_node_error(irb->codegen, node,
-                buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
-        return irb->codegen->invalid_inst_src;
-    }
-
-    BuiltinFnEntry *builtin_fn = entry->value;
-    size_t actual_param_count = node->data.fn_call_expr.params.length;
-
-    if (builtin_fn->param_count != SIZE_MAX && builtin_fn->param_count != actual_param_count) {
-        add_node_error(irb->codegen, node,
-                buf_sprintf("expected %" ZIG_PRI_usize " argument(s), found %" ZIG_PRI_usize,
-                    builtin_fn->param_count, actual_param_count));
-        return irb->codegen->invalid_inst_src;
-    }
-
-    switch (builtin_fn->id) {
-        case BuiltinFnIdInvalid:
-            zig_unreachable();
-        case BuiltinFnIdTypeof:
-            {
-                Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope);
-
-                size_t arg_count = node->data.fn_call_expr.params.length;
-
-                IrInstSrc *type_of;
-
-                if (arg_count == 0) {
-                    add_node_error(irb->codegen, node,
-                        buf_sprintf("expected at least 1 argument, found 0"));
-                    return irb->codegen->invalid_inst_src;
-                } else if (arg_count == 1) {
-                    AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                    IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, sub_scope);
-                    if (arg0_value == irb->codegen->invalid_inst_src)
-                        return arg0_value;
-
-                    type_of = ir_build_typeof_1(irb, scope, node, arg0_value);
-                } else {
-                    IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
-                    for (size_t i = 0; i < arg_count; i += 1) {
-                        AstNode *arg_node = node->data.fn_call_expr.params.at(i);
-                        IrInstSrc *arg = ir_gen_node(irb, arg_node, sub_scope);
-                        if (arg == irb->codegen->invalid_inst_src)
-                            return irb->codegen->invalid_inst_src;
-                        args[i] = arg;
-                    }
-
-                    type_of = ir_build_typeof_n(irb, scope, node, args, arg_count);
-                }
-                return ir_lval_wrap(irb, scope, type_of, lval, result_loc);
-            }
-        case BuiltinFnIdSetCold:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *set_cold = ir_build_set_cold(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, set_cold, lval, result_loc);
-            }
-        case BuiltinFnIdSetRuntimeSafety:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, set_safety, lval, result_loc);
-            }
-        case BuiltinFnIdSetFloatMode:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, set_float_mode, lval, result_loc);
-            }
-        case BuiltinFnIdSizeof:
-        case BuiltinFnIdBitSizeof:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *size_of = ir_build_size_of(irb, scope, node, arg0_value, builtin_fn->id == BuiltinFnIdBitSizeof);
-                return ir_lval_wrap(irb, scope, size_of, lval, result_loc);
-            }
-        case BuiltinFnIdImport:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *import = ir_build_import(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, import, lval, result_loc);
-            }
-        case BuiltinFnIdCImport:
-            {
-                IrInstSrc *c_import = ir_build_c_import(irb, scope, node);
-                return ir_lval_wrap(irb, scope, c_import, lval, result_loc);
-            }
-        case BuiltinFnIdCInclude:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                if (!exec_c_import_buf(irb->exec)) {
-                    add_node_error(irb->codegen, node, buf_sprintf("C include valid only inside C import block"));
-                    return irb->codegen->invalid_inst_src;
-                }
-
-                IrInstSrc *c_include = ir_build_c_include(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, c_include, lval, result_loc);
-            }
-        case BuiltinFnIdCDefine:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                if (!exec_c_import_buf(irb->exec)) {
-                    add_node_error(irb->codegen, node, buf_sprintf("C define valid only inside C import block"));
-                    return irb->codegen->invalid_inst_src;
-                }
-
-                IrInstSrc *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, c_define, lval, result_loc);
-            }
-        case BuiltinFnIdCUndef:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                if (!exec_c_import_buf(irb->exec)) {
-                    add_node_error(irb->codegen, node, buf_sprintf("C undef valid only inside C import block"));
-                    return irb->codegen->invalid_inst_src;
-                }
-
-                IrInstSrc *c_undef = ir_build_c_undef(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, c_undef, lval, result_loc);
-            }
-        case BuiltinFnIdCompileErr:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *compile_err = ir_build_compile_err(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, compile_err, lval, result_loc);
-            }
-        case BuiltinFnIdCompileLog:
-            {
-                IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(actual_param_count);
-
-                for (size_t i = 0; i < actual_param_count; i += 1) {
-                    AstNode *arg_node = node->data.fn_call_expr.params.at(i);
-                    args[i] = ir_gen_node(irb, arg_node, scope);
-                    if (args[i] == irb->codegen->invalid_inst_src)
-                        return irb->codegen->invalid_inst_src;
-                }
-
-                IrInstSrc *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args);
-                return ir_lval_wrap(irb, scope, compile_log, lval, result_loc);
-            }
-        case BuiltinFnIdErrName:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *err_name = ir_build_err_name(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, err_name, lval, result_loc);
-            }
-        case BuiltinFnIdEmbedFile:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *embed_file = ir_build_embed_file(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, embed_file, lval, result_loc);
-            }
-        case BuiltinFnIdCmpxchgWeak:
-        case BuiltinFnIdCmpxchgStrong:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
-                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
-                if (arg2_value == irb->codegen->invalid_inst_src)
-                    return arg2_value;
-
-                AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
-                IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope);
-                if (arg3_value == irb->codegen->invalid_inst_src)
-                    return arg3_value;
-
-                AstNode *arg4_node = node->data.fn_call_expr.params.at(4);
-                IrInstSrc *arg4_value = ir_gen_node(irb, arg4_node, scope);
-                if (arg4_value == irb->codegen->invalid_inst_src)
-                    return arg4_value;
-
-                AstNode *arg5_node = node->data.fn_call_expr.params.at(5);
-                IrInstSrc *arg5_value = ir_gen_node(irb, arg5_node, scope);
-                if (arg5_value == irb->codegen->invalid_inst_src)
-                    return arg5_value;
-
-                IrInstSrc *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value,
-                    arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak),
-                    result_loc);
-                return ir_lval_wrap(irb, scope, cmpxchg, lval, result_loc);
-            }
-        case BuiltinFnIdFence:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *fence = ir_build_fence(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, fence, lval, result_loc);
-            }
-        case BuiltinFnIdReduce:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *reduce = ir_build_reduce(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, reduce, lval, result_loc);
-            }
-        case BuiltinFnIdDivExact:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true);
-                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
-            }
-        case BuiltinFnIdDivTrunc:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true);
-                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
-            }
-        case BuiltinFnIdDivFloor:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true);
-                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
-            }
-        case BuiltinFnIdRem:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true);
-                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
-            }
-        case BuiltinFnIdMod:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);
-                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
-            }
-        case BuiltinFnIdSqrt:
-        case BuiltinFnIdSin:
-        case BuiltinFnIdCos:
-        case BuiltinFnIdExp:
-        case BuiltinFnIdExp2:
-        case BuiltinFnIdLog:
-        case BuiltinFnIdLog2:
-        case BuiltinFnIdLog10:
-        case BuiltinFnIdFabs:
-        case BuiltinFnIdFloor:
-        case BuiltinFnIdCeil:
-        case BuiltinFnIdTrunc:
-        case BuiltinFnIdNearbyInt:
-        case BuiltinFnIdRound:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *inst = ir_build_float_op_src(irb, scope, node, arg0_value, builtin_fn->id);
-                return ir_lval_wrap(irb, scope, inst, lval, result_loc);
-            }
-        case BuiltinFnIdTruncate:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, truncate, lval, result_loc);
-            }
-        case BuiltinFnIdIntCast:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, result, lval, result_loc);
-            }
-        case BuiltinFnIdFloatCast:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, result, lval, result_loc);
-            }
-        case BuiltinFnIdErrSetCast:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, result, lval, result_loc);
-            }
-        case BuiltinFnIdIntToFloat:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, result, lval, result_loc);
-            }
-        case BuiltinFnIdFloatToInt:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, result, lval, result_loc);
-            }
-        case BuiltinFnIdErrToInt:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *result = ir_build_err_to_int_src(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, result, lval, result_loc);
-            }
-        case BuiltinFnIdIntToErr:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *result = ir_build_int_to_err_src(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, result, lval, result_loc);
-            }
-        case BuiltinFnIdBoolToInt:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *result = ir_build_bool_to_int(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, result, lval, result_loc);
-            }
-        case BuiltinFnIdVectorType:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, vector_type, lval, result_loc);
-            }
-        case BuiltinFnIdShuffle:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
-                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
-                if (arg2_value == irb->codegen->invalid_inst_src)
-                    return arg2_value;
-
-                AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
-                IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope);
-                if (arg3_value == irb->codegen->invalid_inst_src)
-                    return arg3_value;
-
-                IrInstSrc *shuffle_vector = ir_build_shuffle_vector(irb, scope, node,
-                    arg0_value, arg1_value, arg2_value, arg3_value);
-                return ir_lval_wrap(irb, scope, shuffle_vector, lval, result_loc);
-            }
-        case BuiltinFnIdSplat:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *splat = ir_build_splat_src(irb, scope, node,
-                    arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, splat, lval, result_loc);
-            }
-        case BuiltinFnIdMemcpy:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
-                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
-                if (arg2_value == irb->codegen->invalid_inst_src)
-                    return arg2_value;
-
-                IrInstSrc *ir_memcpy = ir_build_memcpy_src(irb, scope, node, arg0_value, arg1_value, arg2_value);
-                return ir_lval_wrap(irb, scope, ir_memcpy, lval, result_loc);
-            }
-        case BuiltinFnIdMemset:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
-                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
-                if (arg2_value == irb->codegen->invalid_inst_src)
-                    return arg2_value;
-
-                IrInstSrc *ir_memset = ir_build_memset_src(irb, scope, node, arg0_value, arg1_value, arg2_value);
-                return ir_lval_wrap(irb, scope, ir_memset, lval, result_loc);
-            }
-        case BuiltinFnIdWasmMemorySize:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *ir_wasm_memory_size = ir_build_wasm_memory_size_src(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, ir_wasm_memory_size, lval, result_loc);
-            }
-        case BuiltinFnIdWasmMemoryGrow:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *ir_wasm_memory_grow = ir_build_wasm_memory_grow_src(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, ir_wasm_memory_grow, lval, result_loc);
-            }
-        case BuiltinFnIdField:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr, nullptr);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node,
-                        arg0_value, arg1_value, false);
-
-                if (lval == LValPtr || lval == LValAssign)
-                    return ptr_instruction;
-
-                IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
-                return ir_expr_wrap(irb, scope, load_ptr, result_loc);
-            }
-        case BuiltinFnIdHasField:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *type_info = ir_build_has_field(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, type_info, lval, result_loc);
-            }
-        case BuiltinFnIdTypeInfo:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *type_info = ir_build_type_info(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, type_info, lval, result_loc);
-            }
-        case BuiltinFnIdType:
-            {
-                AstNode *arg_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
-                if (arg == irb->codegen->invalid_inst_src)
-                    return arg;
-
-                IrInstSrc *type = ir_build_type(irb, scope, node, arg);
-                return ir_lval_wrap(irb, scope, type, lval, result_loc);
-            }
-        case BuiltinFnIdBreakpoint:
-            return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval, result_loc);
-        case BuiltinFnIdReturnAddress:
-            return ir_lval_wrap(irb, scope, ir_build_return_address_src(irb, scope, node), lval, result_loc);
-        case BuiltinFnIdFrameAddress:
-            return ir_lval_wrap(irb, scope, ir_build_frame_address_src(irb, scope, node), lval, result_loc);
-        case BuiltinFnIdFrameHandle:
-            if (!irb->exec->fn_entry) {
-                add_node_error(irb->codegen, node, buf_sprintf("@frame() called outside of function definition"));
-                return irb->codegen->invalid_inst_src;
-            }
-            return ir_lval_wrap(irb, scope, ir_build_handle_src(irb, scope, node), lval, result_loc);
-        case BuiltinFnIdFrameType: {
-            AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-            IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-            if (arg0_value == irb->codegen->invalid_inst_src)
-                return arg0_value;
-
-            IrInstSrc *frame_type = ir_build_frame_type(irb, scope, node, arg0_value);
-            return ir_lval_wrap(irb, scope, frame_type, lval, result_loc);
-        }
-        case BuiltinFnIdFrameSize: {
-            AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-            IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-            if (arg0_value == irb->codegen->invalid_inst_src)
-                return arg0_value;
-
-            IrInstSrc *frame_size = ir_build_frame_size_src(irb, scope, node, arg0_value);
-            return ir_lval_wrap(irb, scope, frame_size, lval, result_loc);
-        }
-        case BuiltinFnIdAlignOf:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *align_of = ir_build_align_of(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, align_of, lval, result_loc);
-            }
-        case BuiltinFnIdAddWithOverflow:
-            return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval, result_loc);
-        case BuiltinFnIdSubWithOverflow:
-            return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval, result_loc);
-        case BuiltinFnIdMulWithOverflow:
-            return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval, result_loc);
-        case BuiltinFnIdShlWithOverflow:
-            return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval, result_loc);
-        case BuiltinFnIdMulAdd:
-            return ir_lval_wrap(irb, scope, ir_gen_mul_add(irb, scope, node), lval, result_loc);
-        case BuiltinFnIdTypeName:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *type_name = ir_build_type_name(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, type_name, lval, result_loc);
-            }
-        case BuiltinFnIdPanic:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *panic = ir_build_panic_src(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, panic, lval, result_loc);
-            }
-        case BuiltinFnIdPtrCast:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value, true);
-                return ir_lval_wrap(irb, scope, ptr_cast, lval, result_loc);
-            }
-        case BuiltinFnIdBitCast:
-            {
-                AstNode *dest_type_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *dest_type = ir_gen_node(irb, dest_type_node, scope);
-                if (dest_type == irb->codegen->invalid_inst_src)
-                    return dest_type;
-
-                ResultLocBitCast *result_loc_bit_cast = heap::c_allocator.create<ResultLocBitCast>();
-                result_loc_bit_cast->base.id = ResultLocIdBitCast;
-                result_loc_bit_cast->base.source_instruction = dest_type;
-                result_loc_bit_cast->base.allow_write_through_const = result_loc->allow_write_through_const;
-                ir_ref_instruction(dest_type, irb->current_basic_block);
-                result_loc_bit_cast->parent = result_loc;
-
-                ir_build_reset_result(irb, scope, node, &result_loc_bit_cast->base);
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone,
-                        &result_loc_bit_cast->base);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *bitcast = ir_build_bit_cast_src(irb, scope, arg1_node, arg1_value, result_loc_bit_cast);
-                return ir_lval_wrap(irb, scope, bitcast, lval, result_loc);
-            }
-        case BuiltinFnIdAs:
-            {
-                AstNode *dest_type_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *dest_type = ir_gen_node(irb, dest_type_node, scope);
-                if (dest_type == irb->codegen->invalid_inst_src)
-                    return dest_type;
-
-                ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, dest_type, result_loc);
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone,
-                        &result_loc_cast->base);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *result = ir_build_implicit_cast(irb, scope, node, arg1_value, result_loc_cast);
-                return ir_lval_wrap(irb, scope, result, lval, result_loc);
-            }
-        case BuiltinFnIdIntToPtr:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *int_to_ptr = ir_build_int_to_ptr_src(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, int_to_ptr, lval, result_loc);
-            }
-        case BuiltinFnIdPtrToInt:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *ptr_to_int = ir_build_ptr_to_int_src(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, ptr_to_int, lval, result_loc);
-            }
-        case BuiltinFnIdTagName:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *tag_name = ir_build_tag_name_src(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, tag_name, lval, result_loc);
-            }
-        case BuiltinFnIdFieldParentPtr:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
-                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
-                if (arg2_value == irb->codegen->invalid_inst_src)
-                    return arg2_value;
-
-                IrInstSrc *field_parent_ptr = ir_build_field_parent_ptr_src(irb, scope, node,
-                        arg0_value, arg1_value, arg2_value);
-                return ir_lval_wrap(irb, scope, field_parent_ptr, lval, result_loc);
-            }
-        case BuiltinFnIdByteOffsetOf:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *offset_of = ir_build_byte_offset_of(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, offset_of, lval, result_loc);
-            }
-        case BuiltinFnIdBitOffsetOf:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *offset_of = ir_build_bit_offset_of(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, offset_of, lval, result_loc);
-            }
-        case BuiltinFnIdCall: {
-            // Cast the options parameter to the options type
-            ZigType *options_type = get_builtin_type(irb->codegen, "CallOptions");
-            IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
-            ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());
-
-            AstNode *options_node = node->data.fn_call_expr.params.at(0);
-            IrInstSrc *options_inner = ir_gen_node_extra(irb, options_node, scope,
-                    LValNone, &result_loc_cast->base);
-            if (options_inner == irb->codegen->invalid_inst_src)
-                return options_inner;
-            IrInstSrc *options = ir_build_implicit_cast(irb, scope, options_node, options_inner, result_loc_cast);
-
-            AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1);
-            AstNode *args_node = node->data.fn_call_expr.params.at(2);
-            if (args_node->type == NodeTypeContainerInitExpr) {
-                if (args_node->data.container_init_expr.kind == ContainerInitKindArray ||
-                    args_node->data.container_init_expr.entries.length == 0)
-                {
-                    return ir_gen_fn_call_with_args(irb, scope, node,
-                            fn_ref_node, CallModifierNone, options,
-                            args_node->data.container_init_expr.entries.items,
-                            args_node->data.container_init_expr.entries.length,
-                            lval, result_loc);
-                } else {
-                    exec_add_error_node(irb->codegen, irb->exec, args_node,
-                            buf_sprintf("TODO: @call with anon struct literal"));
-                    return irb->codegen->invalid_inst_src;
-                }
-            } else {
-                IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
-                if (fn_ref == irb->codegen->invalid_inst_src)
-                    return fn_ref;
-
-                IrInstSrc *args = ir_gen_node(irb, args_node, scope);
-                if (args == irb->codegen->invalid_inst_src)
-                    return args;
-
-                IrInstSrc *call = ir_build_call_extra(irb, scope, node, options, fn_ref, args, result_loc);
-                return ir_lval_wrap(irb, scope, call, lval, result_loc);
-            }
-        }
-        case BuiltinFnIdAsyncCall:
-            return ir_gen_async_call(irb, scope, nullptr, node, lval, result_loc);
-        case BuiltinFnIdShlExact:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true);
-                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
-            }
-        case BuiltinFnIdShrExact:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true);
-                return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
-            }
-        case BuiltinFnIdSetEvalBranchQuota:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval, result_loc);
-            }
-        case BuiltinFnIdAlignCast:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *align_cast = ir_build_align_cast_src(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, align_cast, lval, result_loc);
-            }
-        case BuiltinFnIdThis:
-            {
-                IrInstSrc *this_inst = ir_gen_this(irb, scope, node);
-                return ir_lval_wrap(irb, scope, this_inst, lval, result_loc);
-            }
-        case BuiltinFnIdSetAlignStack:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, set_align_stack, lval, result_loc);
-            }
-        case BuiltinFnIdExport:
-            {
-                // Cast the options parameter to the options type
-                ZigType *options_type = get_builtin_type(irb->codegen, "ExportOptions");
-                IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
-                ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());
-
-                AstNode *target_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *target_value = ir_gen_node(irb, target_node, scope);
-                if (target_value == irb->codegen->invalid_inst_src)
-                    return target_value;
-
-                AstNode *options_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *options_value = ir_gen_node_extra(irb, options_node,
-                    scope, LValNone, &result_loc_cast->base);
-                if (options_value == irb->codegen->invalid_inst_src)
-                    return options_value;
-
-                IrInstSrc *casted_options_value = ir_build_implicit_cast(
-                    irb, scope, options_node, options_value, result_loc_cast);
-
-                IrInstSrc *ir_export = ir_build_export(irb, scope, node, target_value, casted_options_value);
-                return ir_lval_wrap(irb, scope, ir_export, lval, result_loc);
-            }
-        case BuiltinFnIdExtern:
-            {
-                // Cast the options parameter to the options type
-                ZigType *options_type = get_builtin_type(irb->codegen, "ExternOptions");
-                IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
-                ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());
-
-                AstNode *type_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *type_value = ir_gen_node(irb, type_node, scope);
-                if (type_value == irb->codegen->invalid_inst_src)
-                    return type_value;
-
-                AstNode *options_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *options_value = ir_gen_node_extra(irb, options_node,
-                    scope, LValNone, &result_loc_cast->base);
-                if (options_value == irb->codegen->invalid_inst_src)
-                    return options_value;
-
-                IrInstSrc *casted_options_value = ir_build_implicit_cast(
-                    irb, scope, options_node, options_value, result_loc_cast);
-
-                IrInstSrc *ir_extern = ir_build_extern(irb, scope, node, type_value, casted_options_value);
-                return ir_lval_wrap(irb, scope, ir_extern, lval, result_loc);
-            }
-        case BuiltinFnIdErrorReturnTrace:
-            {
-                IrInstSrc *error_return_trace = ir_build_error_return_trace_src(irb, scope, node,
-                        IrInstErrorReturnTraceNull);
-                return ir_lval_wrap(irb, scope, error_return_trace, lval, result_loc);
-            }
-        case BuiltinFnIdAtomicRmw:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
-                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
-                if (arg2_value == irb->codegen->invalid_inst_src)
-                    return arg2_value;
-
-                AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
-                IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope);
-                if (arg3_value == irb->codegen->invalid_inst_src)
-                    return arg3_value;
-
-                AstNode *arg4_node = node->data.fn_call_expr.params.at(4);
-                IrInstSrc *arg4_value = ir_gen_node(irb, arg4_node, scope);
-                if (arg4_value == irb->codegen->invalid_inst_src)
-                    return arg4_value;
-
-                IrInstSrc *inst = ir_build_atomic_rmw_src(irb, scope, node,
-                        arg0_value, arg1_value, arg2_value, arg3_value, arg4_value);
-                return ir_lval_wrap(irb, scope, inst, lval, result_loc);
-            }
-        case BuiltinFnIdAtomicLoad:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
-                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
-                if (arg2_value == irb->codegen->invalid_inst_src)
-                    return arg2_value;
-
-                IrInstSrc *inst = ir_build_atomic_load_src(irb, scope, node, arg0_value, arg1_value, arg2_value);
-                return ir_lval_wrap(irb, scope, inst, lval, result_loc);
-            }
-        case BuiltinFnIdAtomicStore:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
-                IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
-                if (arg2_value == irb->codegen->invalid_inst_src)
-                    return arg2_value;
-
-                AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
-                IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope);
-                if (arg3_value == irb->codegen->invalid_inst_src)
-                    return arg3_value;
-
-                IrInstSrc *inst = ir_build_atomic_store_src(irb, scope, node, arg0_value, arg1_value,
-                        arg2_value, arg3_value);
-                return ir_lval_wrap(irb, scope, inst, lval, result_loc);
-            }
-        case BuiltinFnIdIntToEnum:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *result = ir_build_int_to_enum_src(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, result, lval, result_loc);
-            }
-        case BuiltinFnIdEnumToInt:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                IrInstSrc *result = ir_build_enum_to_int(irb, scope, node, arg0_value);
-                return ir_lval_wrap(irb, scope, result, lval, result_loc);
-            }
-        case BuiltinFnIdCtz:
-        case BuiltinFnIdPopCount:
-        case BuiltinFnIdClz:
-        case BuiltinFnIdBswap:
-        case BuiltinFnIdBitReverse:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *result;
-                switch (builtin_fn->id) {
-                case BuiltinFnIdCtz:
-                    result = ir_build_ctz(irb, scope, node, arg0_value, arg1_value);
-                    break;
-                case BuiltinFnIdPopCount:
-                    result = ir_build_pop_count(irb, scope, node, arg0_value, arg1_value);
-                    break;
-                case BuiltinFnIdClz:
-                    result = ir_build_clz(irb, scope, node, arg0_value, arg1_value);
-                    break;
-                case BuiltinFnIdBswap:
-                    result = ir_build_bswap(irb, scope, node, arg0_value, arg1_value);
-                    break;
-                case BuiltinFnIdBitReverse:
-                    result = ir_build_bit_reverse(irb, scope, node, arg0_value, arg1_value);
-                    break;
-                default:
-                    zig_unreachable();
-                }
-                return ir_lval_wrap(irb, scope, result, lval, result_loc);
-            }
-        case BuiltinFnIdHasDecl:
-            {
-                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
-                if (arg0_value == irb->codegen->invalid_inst_src)
-                    return arg0_value;
-
-                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
-                if (arg1_value == irb->codegen->invalid_inst_src)
-                    return arg1_value;
-
-                IrInstSrc *has_decl = ir_build_has_decl(irb, scope, node, arg0_value, arg1_value);
-                return ir_lval_wrap(irb, scope, has_decl, lval, result_loc);
-            }
-        case BuiltinFnIdUnionInit:
-            {
-                AstNode *union_type_node = node->data.fn_call_expr.params.at(0);
-                IrInstSrc *union_type_inst = ir_gen_node(irb, union_type_node, scope);
-                if (union_type_inst == irb->codegen->invalid_inst_src)
-                    return union_type_inst;
-
-                AstNode *name_node = node->data.fn_call_expr.params.at(1);
-                IrInstSrc *name_inst = ir_gen_node(irb, name_node, scope);
-                if (name_inst == irb->codegen->invalid_inst_src)
-                    return name_inst;
-
-                AstNode *init_node = node->data.fn_call_expr.params.at(2);
-
-                return ir_gen_union_init_expr(irb, scope, node, union_type_inst, name_inst, init_node,
-                        lval, result_loc);
-            }
-        case BuiltinFnIdSrc:
-            {
-                IrInstSrc *src_inst = ir_build_src(irb, scope, node);
-                return ir_lval_wrap(irb, scope, src_inst, lval, result_loc);
-            }
-    }
-    zig_unreachable();
-}
-
-static ScopeNoSuspend *get_scope_nosuspend(Scope *scope) {
-    while (scope) {
-        if (scope->id == ScopeIdNoSuspend)
-            return (ScopeNoSuspend *)scope;
-        if (scope->id == ScopeIdFnDef)
-            return nullptr;
-
-        scope = scope->parent;
-    }
-    return nullptr;
-}
-
-static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(node->type == NodeTypeFnCallExpr);
-
-    if (node->data.fn_call_expr.modifier == CallModifierBuiltin)
-        return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc);
-
-    bool is_nosuspend = get_scope_nosuspend(scope) != nullptr;
-    CallModifier modifier = node->data.fn_call_expr.modifier;
-    if (is_nosuspend && modifier != CallModifierAsync) {
-        modifier = CallModifierNoSuspend;
-    }
-
-    AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
-    return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, modifier,
-        nullptr, node->data.fn_call_expr.params.items, node->data.fn_call_expr.params.length, lval, result_loc);
-}
-
-static IrInstSrc *ir_gen_if_bool_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(node->type == NodeTypeIfBoolExpr);
-
-    IrInstSrc *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope);
-    if (condition == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *is_comptime;
-    if (ir_should_inline(irb->exec, scope)) {
-        is_comptime = ir_build_const_bool(irb, scope, node, true);
-    } else {
-        is_comptime = ir_build_test_comptime(irb, scope, node, condition);
-    }
-
-    AstNode *then_node = node->data.if_bool_expr.then_block;
-    AstNode *else_node = node->data.if_bool_expr.else_node;
-
-    IrBasicBlockSrc *then_block = ir_create_basic_block(irb, scope, "Then");
-    IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "Else");
-    IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "EndIf");
-
-    IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, condition,
-            then_block, else_block, is_comptime);
-    ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
-            result_loc, is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, then_block);
-
-    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
-    IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, subexpr_scope, lval,
-            &peer_parent->peers.at(0)->base);
-    if (then_expr_result == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-    IrBasicBlockSrc *after_then_block = irb->current_basic_block;
-    if (!instr_is_unreachable(then_expr_result))
-        ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
-
-    ir_set_cursor_at_end_and_append_block(irb, else_block);
-    IrInstSrc *else_expr_result;
-    if (else_node) {
-        else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base);
-        if (else_expr_result == irb->codegen->invalid_inst_src)
-            return irb->codegen->invalid_inst_src;
-    } else {
-        else_expr_result = ir_build_const_void(irb, scope, node);
-        ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
-    }
-    IrBasicBlockSrc *after_else_block = irb->current_basic_block;
-    if (!instr_is_unreachable(else_expr_result))
-        ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
-
-    ir_set_cursor_at_end_and_append_block(irb, endif_block);
-    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
-    incoming_values[0] = then_expr_result;
-    incoming_values[1] = else_expr_result;
-    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
-    incoming_blocks[0] = after_then_block;
-    incoming_blocks[1] = after_else_block;
-
-    IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
-    return ir_expr_wrap(irb, scope, phi, result_loc);
-}
-
-static IrInstSrc *ir_gen_prefix_op_id_lval(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) {
-    assert(node->type == NodeTypePrefixOpExpr);
-    AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
-
-    IrInstSrc *value = ir_gen_node_extra(irb, expr_node, scope, lval, nullptr);
-    if (value == irb->codegen->invalid_inst_src)
-        return value;
-
-    return ir_build_un_op(irb, scope, node, op_id, value);
-}
-
-static IrInstSrc *ir_gen_prefix_op_id(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrUnOp op_id) {
-    return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone);
-}
-
-static IrInstSrc *ir_expr_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *inst, ResultLoc *result_loc) {
-    if (inst == irb->codegen->invalid_inst_src) return inst;
-    ir_build_end_expr(irb, scope, inst->base.source_node, inst, result_loc);
-    return inst;
-}
-
-static IrInstSrc *ir_lval_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *value, LVal lval,
-        ResultLoc *result_loc)
-{
-    // This logic must be kept in sync with
-    // [STMT_EXPR_TEST_THING] <--- (search this token)
-    if (value == irb->codegen->invalid_inst_src ||
-        instr_is_unreachable(value) ||
-        value->base.source_node->type == NodeTypeDefer ||
-        value->id == IrInstSrcIdDeclVar)
-    {
-        return value;
-    }
-
-    assert(lval != LValAssign);
-    if (lval == LValPtr) {
-        // We needed a pointer to a value, but we got a value. So we create
-        // an instruction which just makes a pointer of it.
-        return ir_build_ref_src(irb, scope, value->base.source_node, value);
-    } else if (result_loc != nullptr) {
-        return ir_expr_wrap(irb, scope, value, result_loc);
-    } else {
-        return value;
-    }
-
-}
-
-static PtrLen star_token_to_ptr_len(TokenId token_id) {
-    switch (token_id) {
-        case TokenIdStar:
-        case TokenIdStarStar:
-            return PtrLenSingle;
-        case TokenIdLBracket:
-            return PtrLenUnknown;
-        case TokenIdSymbol:
-            return PtrLenC;
-        default:
-            zig_unreachable();
-    }
-}
-
-static IrInstSrc *ir_gen_pointer_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypePointerType);
-
-    PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id);
-
-    bool is_const = node->data.pointer_type.is_const;
-    bool is_volatile = node->data.pointer_type.is_volatile;
-    bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr;
-    AstNode *sentinel_expr = node->data.pointer_type.sentinel;
-    AstNode *expr_node = node->data.pointer_type.op_expr;
-    AstNode *align_expr = node->data.pointer_type.align_expr;
-
-    IrInstSrc *sentinel;
-    if (sentinel_expr != nullptr) {
-        sentinel = ir_gen_node(irb, sentinel_expr, scope);
-        if (sentinel == irb->codegen->invalid_inst_src)
-            return sentinel;
-    } else {
-        sentinel = nullptr;
-    }
-
-    IrInstSrc *align_value;
-    if (align_expr != nullptr) {
-        align_value = ir_gen_node(irb, align_expr, scope);
-        if (align_value == irb->codegen->invalid_inst_src)
-            return align_value;
-    } else {
-        align_value = nullptr;
-    }
-
-    IrInstSrc *child_type = ir_gen_node(irb, expr_node, scope);
-    if (child_type == irb->codegen->invalid_inst_src)
-        return child_type;
-
-    uint32_t bit_offset_start = 0;
-    if (node->data.pointer_type.bit_offset_start != nullptr) {
-        if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) {
-            Buf *val_buf = buf_alloc();
-            bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10);
-            exec_add_error_node(irb->codegen, irb->exec, node,
-                    buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
-            return irb->codegen->invalid_inst_src;
-        }
-        bit_offset_start = bigint_as_u32(node->data.pointer_type.bit_offset_start);
-    }
-
-    uint32_t host_int_bytes = 0;
-    if (node->data.pointer_type.host_int_bytes != nullptr) {
-        if (!bigint_fits_in_bits(node->data.pointer_type.host_int_bytes, 32, false)) {
-            Buf *val_buf = buf_alloc();
-            bigint_append_buf(val_buf, node->data.pointer_type.host_int_bytes, 10);
-            exec_add_error_node(irb->codegen, irb->exec, node,
-                    buf_sprintf("value %s too large for u32 byte count", buf_ptr(val_buf)));
-            return irb->codegen->invalid_inst_src;
-        }
-        host_int_bytes = bigint_as_u32(node->data.pointer_type.host_int_bytes);
-    }
-
-    if (host_int_bytes != 0 && bit_offset_start >= host_int_bytes * 8) {
-        exec_add_error_node(irb->codegen, irb->exec, node,
-                buf_sprintf("bit offset starts after end of host integer"));
-        return irb->codegen->invalid_inst_src;
-    }
-
-    return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile,
-            ptr_len, sentinel, align_value, bit_offset_start, host_int_bytes, is_allow_zero);
-}
-
-static IrInstSrc *ir_gen_catch_unreachable(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-        AstNode *expr_node, LVal lval, ResultLoc *result_loc)
-{
-    IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
-    if (err_union_ptr == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, scope, source_node, err_union_ptr, true, false);
-    if (payload_ptr == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    if (lval == LValPtr)
-        return payload_ptr;
-
-    IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, source_node, payload_ptr);
-    return ir_expr_wrap(irb, scope, load_ptr, result_loc);
-}
-
-static IrInstSrc *ir_gen_bool_not(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypePrefixOpExpr);
-    AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
-
-    IrInstSrc *value = ir_gen_node(irb, expr_node, scope);
-    if (value == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    return ir_build_bool_not(irb, scope, node, value);
-}
-
-static IrInstSrc *ir_gen_prefix_op_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(node->type == NodeTypePrefixOpExpr);
-
-    PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op;
-
-    switch (prefix_op) {
-        case PrefixOpInvalid:
-            zig_unreachable();
-        case PrefixOpBoolNot:
-            return ir_lval_wrap(irb, scope, ir_gen_bool_not(irb, scope, node), lval, result_loc);
-        case PrefixOpBinNot:
-            return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval, result_loc);
-        case PrefixOpNegation:
-            return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval, result_loc);
-        case PrefixOpNegationWrap:
-            return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval, result_loc);
-        case PrefixOpOptional:
-            return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval, result_loc);
-        case PrefixOpAddrOf: {
-            AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
-            return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr), lval, result_loc);
-        }
-    }
-    zig_unreachable();
-}
-
-static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
-    IrInstSrc *union_type, IrInstSrc *field_name, AstNode *expr_node,
-    LVal lval, ResultLoc *parent_result_loc)
-{
-    IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, source_node, parent_result_loc, union_type);
-    IrInstSrc *field_ptr = ir_build_field_ptr_instruction(irb, scope, source_node, container_ptr,
-            field_name, true);
-
-    ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
-    result_loc_inst->base.id = ResultLocIdInstruction;
-    result_loc_inst->base.source_instruction = field_ptr;
-    ir_ref_instruction(field_ptr, irb->current_basic_block);
-    ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
-
-    IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
-            &result_loc_inst->base);
-    if (expr_value == irb->codegen->invalid_inst_src)
-        return expr_value;
-
-    IrInstSrc *init_union = ir_build_union_init_named_field(irb, scope, source_node, union_type,
-            field_name, field_ptr, container_ptr);
-
-    return ir_lval_wrap(irb, scope, init_union, lval, parent_result_loc);
-}
-
-static IrInstSrc *ir_gen_container_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
-        ResultLoc *parent_result_loc)
-{
-    assert(node->type == NodeTypeContainerInitExpr);
-
-    AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
-    ContainerInitKind kind = container_init_expr->kind;
-
-    ResultLocCast *result_loc_cast = nullptr;
-    ResultLoc *child_result_loc;
-    AstNode *init_array_type_source_node;
-    if (container_init_expr->type != nullptr) {
-        IrInstSrc *container_type;
-        if (container_init_expr->type->type == NodeTypeInferredArrayType) {
-            if (kind == ContainerInitKindStruct) {
-                add_node_error(irb->codegen, container_init_expr->type,
-                        buf_sprintf("initializing array with struct syntax"));
-                return irb->codegen->invalid_inst_src;
-            }
-            IrInstSrc *sentinel;
-            if (container_init_expr->type->data.inferred_array_type.sentinel != nullptr) {
-                sentinel = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.sentinel, scope);
-                if (sentinel == irb->codegen->invalid_inst_src)
-                    return sentinel;
-            } else {
-                sentinel = nullptr;
-            }
-
-            IrInstSrc *elem_type = ir_gen_node(irb,
-                    container_init_expr->type->data.inferred_array_type.child_type, scope);
-            if (elem_type == irb->codegen->invalid_inst_src)
-                return elem_type;
-            size_t item_count = container_init_expr->entries.length;
-            IrInstSrc *item_count_inst = ir_build_const_usize(irb, scope, node, item_count);
-            container_type = ir_build_array_type(irb, scope, node, item_count_inst, sentinel, elem_type);
-        } else {
-            container_type = ir_gen_node(irb, container_init_expr->type, scope);
-            if (container_type == irb->codegen->invalid_inst_src)
-                return container_type;
-        }
-
-        result_loc_cast = ir_build_cast_result_loc(irb, container_type, parent_result_loc);
-        child_result_loc = &result_loc_cast->base;
-        init_array_type_source_node = container_type->base.source_node;
-    } else {
-        child_result_loc = parent_result_loc;
-        if (parent_result_loc->source_instruction != nullptr) {
-            init_array_type_source_node = parent_result_loc->source_instruction->base.source_node;
-        } else {
-            init_array_type_source_node = node;
-        }
-    }
-
-    switch (kind) {
-        case ContainerInitKindStruct: {
-            IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc,
-                    nullptr);
-
-            size_t field_count = container_init_expr->entries.length;
-            IrInstSrcContainerInitFieldsField *fields = heap::c_allocator.allocate<IrInstSrcContainerInitFieldsField>(field_count);
-            for (size_t i = 0; i < field_count; i += 1) {
-                AstNode *entry_node = container_init_expr->entries.at(i);
-                assert(entry_node->type == NodeTypeStructValueField);
-
-                Buf *name = entry_node->data.struct_val_field.name;
-                AstNode *expr_node = entry_node->data.struct_val_field.expr;
-
-                IrInstSrc *field_ptr = ir_build_field_ptr(irb, scope, entry_node, container_ptr, name, true);
-                ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
-                result_loc_inst->base.id = ResultLocIdInstruction;
-                result_loc_inst->base.source_instruction = field_ptr;
-                result_loc_inst->base.allow_write_through_const = true;
-                ir_ref_instruction(field_ptr, irb->current_basic_block);
-                ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
-
-                IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
-                        &result_loc_inst->base);
-                if (expr_value == irb->codegen->invalid_inst_src)
-                    return expr_value;
-
-                fields[i].name = name;
-                fields[i].source_node = entry_node;
-                fields[i].result_loc = field_ptr;
-            }
-            IrInstSrc *result = ir_build_container_init_fields(irb, scope, node, field_count,
-                    fields, container_ptr);
-
-            if (result_loc_cast != nullptr) {
-                result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast);
-            }
-            return ir_lval_wrap(irb, scope, result, lval, parent_result_loc);
-        }
-        case ContainerInitKindArray: {
-            size_t item_count = container_init_expr->entries.length;
-
-            IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc,
-                    nullptr);
-
-            IrInstSrc **result_locs = heap::c_allocator.allocate<IrInstSrc *>(item_count);
-            for (size_t i = 0; i < item_count; i += 1) {
-                AstNode *expr_node = container_init_expr->entries.at(i);
-
-                IrInstSrc *elem_index = ir_build_const_usize(irb, scope, expr_node, i);
-                IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr,
-                        elem_index, false, PtrLenSingle, init_array_type_source_node);
-                ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
-                result_loc_inst->base.id = ResultLocIdInstruction;
-                result_loc_inst->base.source_instruction = elem_ptr;
-                result_loc_inst->base.allow_write_through_const = true;
-                ir_ref_instruction(elem_ptr, irb->current_basic_block);
-                ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
-
-                IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
-                        &result_loc_inst->base);
-                if (expr_value == irb->codegen->invalid_inst_src)
-                    return expr_value;
-
-                result_locs[i] = elem_ptr;
-            }
-            IrInstSrc *result = ir_build_container_init_list(irb, scope, node, item_count,
-                    result_locs, container_ptr, init_array_type_source_node);
-            if (result_loc_cast != nullptr) {
-                result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast);
-            }
-            return ir_lval_wrap(irb, scope, result, lval, parent_result_loc);
-        }
-    }
-    zig_unreachable();
-}
-
-static ResultLocVar *ir_build_var_result_loc(IrBuilderSrc *irb, IrInstSrc *alloca, ZigVar *var) {
-    ResultLocVar *result_loc_var = heap::c_allocator.create<ResultLocVar>();
-    result_loc_var->base.id = ResultLocIdVar;
-    result_loc_var->base.source_instruction = alloca;
-    result_loc_var->base.allow_write_through_const = true;
-    result_loc_var->var = var;
-
-    ir_build_reset_result(irb, alloca->base.scope, alloca->base.source_node, &result_loc_var->base);
-
-    return result_loc_var;
-}
-
-static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type,
-        ResultLoc *parent_result_loc)
-{
-    ResultLocCast *result_loc_cast = heap::c_allocator.create<ResultLocCast>();
-    result_loc_cast->base.id = ResultLocIdCast;
-    result_loc_cast->base.source_instruction = dest_type;
-    result_loc_cast->base.allow_write_through_const = parent_result_loc->allow_write_through_const;
-    ir_ref_instruction(dest_type, irb->current_basic_block);
-    result_loc_cast->parent = parent_result_loc;
-
-    ir_build_reset_result(irb, dest_type->base.scope, dest_type->base.source_node, &result_loc_cast->base);
-
-    return result_loc_cast;
-}
-
-static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var,
-        IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime)
-{
-    IrInstSrc *alloca = ir_build_alloca_src(irb, scope, source_node, nullptr, name_hint, is_comptime);
-    ResultLocVar *var_result_loc = ir_build_var_result_loc(irb, alloca, var);
-    ir_build_end_expr(irb, scope, source_node, init, &var_result_loc->base);
-    ir_build_var_decl_src(irb, scope, source_node, var, nullptr, alloca);
-}
-
-static IrInstSrc *ir_gen_var_decl(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeVariableDeclaration);
-
-    AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
-
-    if (buf_eql_str(variable_declaration->symbol, "_")) {
-        add_node_error(irb->codegen, node, buf_sprintf("`_` is not a declarable symbol"));
-        return irb->codegen->invalid_inst_src;
-    }
-
-    // Used for the type expr and the align expr
-    Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
-
-    IrInstSrc *type_instruction;
-    if (variable_declaration->type != nullptr) {
-        type_instruction = ir_gen_node(irb, variable_declaration->type, comptime_scope);
-        if (type_instruction == irb->codegen->invalid_inst_src)
-            return type_instruction;
-    } else {
-        type_instruction = nullptr;
-    }
-
-    bool is_shadowable = false;
-    bool is_const = variable_declaration->is_const;
-    bool is_extern = variable_declaration->is_extern;
-
-    bool is_comptime_scalar = ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime;
-    IrInstSrc *is_comptime = ir_build_const_bool(irb, scope, node, is_comptime_scalar);
-    ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol,
-        is_const, is_const, is_shadowable, is_comptime);
-    // we detect IrInstSrcDeclVar in gen_block to make sure the next node
-    // is inside var->child_scope
-
-    if (!is_extern && !variable_declaration->expr) {
-        var->var_type = irb->codegen->builtin_types.entry_invalid;
-        add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized"));
-        return irb->codegen->invalid_inst_src;
-    }
-
-    IrInstSrc *align_value = nullptr;
-    if (variable_declaration->align_expr != nullptr) {
-        align_value = ir_gen_node(irb, variable_declaration->align_expr, comptime_scope);
-        if (align_value == irb->codegen->invalid_inst_src)
-            return align_value;
-    }
-
-    if (variable_declaration->section_expr != nullptr) {
-        add_node_error(irb->codegen, variable_declaration->section_expr,
-            buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
-    }
-
-    // Parser should ensure that this never happens
-    assert(variable_declaration->threadlocal_tok == nullptr);
-
-    IrInstSrc *alloca = ir_build_alloca_src(irb, scope, node, align_value,
-            buf_ptr(variable_declaration->symbol), is_comptime);
-
-    // Create a result location for the initialization expression.
-    ResultLocVar *result_loc_var = ir_build_var_result_loc(irb, alloca, var);
-    ResultLoc *init_result_loc;
-    ResultLocCast *result_loc_cast;
-    if (type_instruction != nullptr) {
-        result_loc_cast = ir_build_cast_result_loc(irb, type_instruction, &result_loc_var->base);
-        init_result_loc = &result_loc_cast->base;
-    } else {
-        result_loc_cast = nullptr;
-        init_result_loc = &result_loc_var->base;
-    }
-
-    Scope *init_scope = is_comptime_scalar ?
-        create_comptime_scope(irb->codegen, variable_declaration->expr, scope) : scope;
-
-    // Temporarily set the name of the IrExecutableSrc to the VariableDeclaration
-    // so that the struct or enum from the init expression inherits the name.
-    Buf *old_exec_name = irb->exec->name;
-    irb->exec->name = variable_declaration->symbol;
-    IrInstSrc *init_value = ir_gen_node_extra(irb, variable_declaration->expr, init_scope,
-            LValNone, init_result_loc);
-    irb->exec->name = old_exec_name;
-
-    if (init_value == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    if (result_loc_cast != nullptr) {
-        IrInstSrc *implicit_cast = ir_build_implicit_cast(irb, scope, init_value->base.source_node,
-                init_value, result_loc_cast);
-        ir_build_end_expr(irb, scope, node, implicit_cast, &result_loc_var->base);
-    }
-
-    return ir_build_var_decl_src(irb, scope, node, var, align_value, alloca);
-}
-
-static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(node->type == NodeTypeWhileExpr);
-
-    AstNode *continue_expr_node = node->data.while_expr.continue_expr;
-    AstNode *else_node = node->data.while_expr.else_node;
-
-    IrBasicBlockSrc *cond_block = ir_create_basic_block(irb, scope, "WhileCond");
-    IrBasicBlockSrc *body_block = ir_create_basic_block(irb, scope, "WhileBody");
-    IrBasicBlockSrc *continue_block = continue_expr_node ?
-        ir_create_basic_block(irb, scope, "WhileContinue") : cond_block;
-    IrBasicBlockSrc *end_block = ir_create_basic_block(irb, scope, "WhileEnd");
-    IrBasicBlockSrc *else_block = else_node ?
-        ir_create_basic_block(irb, scope, "WhileElse") : end_block;
-
-    IrInstSrc *is_comptime = ir_build_const_bool(irb, scope, node,
-        ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline);
-    ir_build_br(irb, scope, node, cond_block, is_comptime);
-
-    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
-    Buf *var_symbol = node->data.while_expr.var_symbol;
-    Buf *err_symbol = node->data.while_expr.err_symbol;
-    if (err_symbol != nullptr) {
-        ir_set_cursor_at_end_and_append_block(irb, cond_block);
-
-        Scope *payload_scope;
-        AstNode *symbol_node = node; // TODO make more accurate
-        ZigVar *payload_var;
-        if (var_symbol) {
-            // TODO make it an error to write to payload variable
-            payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol,
-                    true, false, false, is_comptime);
-            payload_scope = payload_var->child_scope;
-        } else {
-            payload_scope = subexpr_scope;
-        }
-        ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, payload_scope);
-        IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope,
-                LValPtr, nullptr);
-        if (err_val_ptr == irb->codegen->invalid_inst_src)
-            return err_val_ptr;
-        IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node->data.while_expr.condition, err_val_ptr,
-                true, false);
-        IrBasicBlockSrc *after_cond_block = irb->current_basic_block;
-        IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
-        IrInstSrc *cond_br_inst;
-        if (!instr_is_unreachable(is_err)) {
-            cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err,
-                        else_block, body_block, is_comptime);
-            cond_br_inst->is_gen = true;
-        } else {
-            // for the purposes of the source instruction to ir_build_result_peers
-            cond_br_inst = irb->current_basic_block->instruction_list.last();
-        }
-
-        ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
-                is_comptime);
-
-        ir_set_cursor_at_end_and_append_block(irb, body_block);
-        if (var_symbol) {
-            IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, &spill_scope->base, symbol_node,
-                    err_val_ptr, false, false);
-            IrInstSrc *var_value = node->data.while_expr.var_is_ptr ?
-                payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, symbol_node, payload_ptr);
-            build_decl_var_and_init(irb, payload_scope, symbol_node, payload_var, var_value, buf_ptr(var_symbol), is_comptime);
-        }
-
-        ZigList<IrInstSrc *> incoming_values = {0};
-        ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
-
-        if (is_duplicate_label(irb->codegen, payload_scope, node, node->data.while_expr.name))
-            return irb->codegen->invalid_inst_src;
-
-        ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, payload_scope);
-        loop_scope->break_block = end_block;
-        loop_scope->continue_block = continue_block;
-        loop_scope->is_comptime = is_comptime;
-        loop_scope->incoming_blocks = &incoming_blocks;
-        loop_scope->incoming_values = &incoming_values;
-        loop_scope->lval = lval;
-        loop_scope->peer_parent = peer_parent;
-        loop_scope->spill_scope = spill_scope;
-
-        // Note the body block of the loop is not the place that lval and result_loc are used -
-        // it's actually in break statements, handled similarly to return statements.
-        // That is why we set those values in loop_scope above and not in this ir_gen_node call.
-        IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
-        if (body_result == irb->codegen->invalid_inst_src)
-            return body_result;
-
-        if (loop_scope->name != nullptr && loop_scope->name_used == false) {
-            add_node_error(irb->codegen, node, buf_sprintf("unused while label"));
-        }
-
-        if (!instr_is_unreachable(body_result)) {
-            ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result));
-            ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime));
-        }
-
-        if (continue_expr_node) {
-            ir_set_cursor_at_end_and_append_block(irb, continue_block);
-            IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope);
-            if (expr_result == irb->codegen->invalid_inst_src)
-                return expr_result;
-            if (!instr_is_unreachable(expr_result)) {
-                ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, continue_expr_node, expr_result));
-                ir_mark_gen(ir_build_br(irb, payload_scope, node, cond_block, is_comptime));
-            }
-        }
-
-        ir_set_cursor_at_end_and_append_block(irb, else_block);
-        assert(else_node != nullptr);
-
-        // TODO make it an error to write to error variable
-        AstNode *err_symbol_node = else_node; // TODO make more accurate
-        ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol,
-                true, false, false, is_comptime);
-        Scope *err_scope = err_var->child_scope;
-        IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, err_scope, err_symbol_node, err_val_ptr);
-        IrInstSrc *err_value = ir_build_load_ptr(irb, err_scope, err_symbol_node, err_ptr);
-        build_decl_var_and_init(irb, err_scope, err_symbol_node, err_var, err_value, buf_ptr(err_symbol), is_comptime);
-
-        if (peer_parent->peers.length != 0) {
-            peer_parent->peers.last()->next_bb = else_block;
-        }
-        ResultLocPeer *peer_result = create_peer_result(peer_parent);
-        peer_parent->peers.append(peer_result);
-        IrInstSrc *else_result = ir_gen_node_extra(irb, else_node, err_scope, lval, &peer_result->base);
-        if (else_result == irb->codegen->invalid_inst_src)
-            return else_result;
-        if (!instr_is_unreachable(else_result))
-            ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
-        IrBasicBlockSrc *after_else_block = irb->current_basic_block;
-        ir_set_cursor_at_end_and_append_block(irb, end_block);
-        if (else_result) {
-            incoming_blocks.append(after_else_block);
-            incoming_values.append(else_result);
-        } else {
-            incoming_blocks.append(after_cond_block);
-            incoming_values.append(void_else_result);
-        }
-        if (peer_parent->peers.length != 0) {
-            peer_parent->peers.last()->next_bb = end_block;
-        }
-
-        IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
-                incoming_blocks.items, incoming_values.items, peer_parent);
-        return ir_expr_wrap(irb, scope, phi, result_loc);
-    } else if (var_symbol != nullptr) {
-        ir_set_cursor_at_end_and_append_block(irb, cond_block);
-        Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
-        // TODO make it an error to write to payload variable
-        AstNode *symbol_node = node; // TODO make more accurate
-
-        ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol,
-                true, false, false, is_comptime);
-        Scope *child_scope = payload_var->child_scope;
-        ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, child_scope);
-        IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope,
-                LValPtr, nullptr);
-        if (maybe_val_ptr == irb->codegen->invalid_inst_src)
-            return maybe_val_ptr;
-        IrInstSrc *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr);
-        IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, scope, node->data.while_expr.condition, maybe_val);
-        IrBasicBlockSrc *after_cond_block = irb->current_basic_block;
-        IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
-        IrInstSrc *cond_br_inst;
-        if (!instr_is_unreachable(is_non_null)) {
-            cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null,
-                        body_block, else_block, is_comptime);
-            cond_br_inst->is_gen = true;
-        } else {
-            // for the purposes of the source instruction to ir_build_result_peers
-            cond_br_inst = irb->current_basic_block->instruction_list.last();
-        }
-
-        ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
-                is_comptime);
-
-        ir_set_cursor_at_end_and_append_block(irb, body_block);
-        IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, &spill_scope->base, symbol_node, maybe_val_ptr, false);
-        IrInstSrc *var_value = node->data.while_expr.var_is_ptr ?
-            payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, symbol_node, payload_ptr);
-        build_decl_var_and_init(irb, child_scope, symbol_node, payload_var, var_value, buf_ptr(var_symbol), is_comptime);
-
-        ZigList<IrInstSrc *> incoming_values = {0};
-        ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
-
-        if (is_duplicate_label(irb->codegen, child_scope, node, node->data.while_expr.name))
-            return irb->codegen->invalid_inst_src;
-
-        ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
-        loop_scope->break_block = end_block;
-        loop_scope->continue_block = continue_block;
-        loop_scope->is_comptime = is_comptime;
-        loop_scope->incoming_blocks = &incoming_blocks;
-        loop_scope->incoming_values = &incoming_values;
-        loop_scope->lval = lval;
-        loop_scope->peer_parent = peer_parent;
-        loop_scope->spill_scope = spill_scope;
-
-        // Note the body block of the loop is not the place that lval and result_loc are used -
-        // it's actually in break statements, handled similarly to return statements.
-        // That is why we set those values in loop_scope above and not in this ir_gen_node call.
-        IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
-        if (body_result == irb->codegen->invalid_inst_src)
-            return body_result;
-
-        if (loop_scope->name != nullptr && loop_scope->name_used == false) {
-            add_node_error(irb->codegen, node, buf_sprintf("unused while label"));
-        }
-
-        if (!instr_is_unreachable(body_result)) {
-            ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result));
-            ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
-        }
-
-        if (continue_expr_node) {
-            ir_set_cursor_at_end_and_append_block(irb, continue_block);
-            IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, child_scope);
-            if (expr_result == irb->codegen->invalid_inst_src)
-                return expr_result;
-            if (!instr_is_unreachable(expr_result)) {
-                ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, continue_expr_node, expr_result));
-                ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime));
-            }
-        }
-
-        IrInstSrc *else_result = nullptr;
-        if (else_node) {
-            ir_set_cursor_at_end_and_append_block(irb, else_block);
-
-            if (peer_parent->peers.length != 0) {
-                peer_parent->peers.last()->next_bb = else_block;
-            }
-            ResultLocPeer *peer_result = create_peer_result(peer_parent);
-            peer_parent->peers.append(peer_result);
-            else_result = ir_gen_node_extra(irb, else_node, scope, lval, &peer_result->base);
-            if (else_result == irb->codegen->invalid_inst_src)
-                return else_result;
-            if (!instr_is_unreachable(else_result))
-                ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
-        }
-        IrBasicBlockSrc *after_else_block = irb->current_basic_block;
-        ir_set_cursor_at_end_and_append_block(irb, end_block);
-        if (else_result) {
-            incoming_blocks.append(after_else_block);
-            incoming_values.append(else_result);
-        } else {
-            incoming_blocks.append(after_cond_block);
-            incoming_values.append(void_else_result);
-        }
-        if (peer_parent->peers.length != 0) {
-            peer_parent->peers.last()->next_bb = end_block;
-        }
-
-        IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
-                incoming_blocks.items, incoming_values.items, peer_parent);
-        return ir_expr_wrap(irb, scope, phi, result_loc);
-    } else {
-        ir_set_cursor_at_end_and_append_block(irb, cond_block);
-        IrInstSrc *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope);
-        if (cond_val == irb->codegen->invalid_inst_src)
-            return cond_val;
-        IrBasicBlockSrc *after_cond_block = irb->current_basic_block;
-        IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
-        IrInstSrc *cond_br_inst;
-        if (!instr_is_unreachable(cond_val)) {
-            cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val,
-                        body_block, else_block, is_comptime);
-            cond_br_inst->is_gen = true;
-        } else {
-            // for the purposes of the source instruction to ir_build_result_peers
-            cond_br_inst = irb->current_basic_block->instruction_list.last();
-        }
-
-        ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
-                is_comptime);
-        ir_set_cursor_at_end_and_append_block(irb, body_block);
-
-        ZigList<IrInstSrc *> incoming_values = {0};
-        ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
-
-        Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
-
-        if (is_duplicate_label(irb->codegen, subexpr_scope, node, node->data.while_expr.name))
-            return irb->codegen->invalid_inst_src;
-
-        ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, subexpr_scope);
-        loop_scope->break_block = end_block;
-        loop_scope->continue_block = continue_block;
-        loop_scope->is_comptime = is_comptime;
-        loop_scope->incoming_blocks = &incoming_blocks;
-        loop_scope->incoming_values = &incoming_values;
-        loop_scope->lval = lval;
-        loop_scope->peer_parent = peer_parent;
-
-        // Note the body block of the loop is not the place that lval and result_loc are used -
-        // it's actually in break statements, handled similarly to return statements.
-        // That is why we set those values in loop_scope above and not in this ir_gen_node call.
-        IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
-        if (body_result == irb->codegen->invalid_inst_src)
-            return body_result;
-
-        if (loop_scope->name != nullptr && loop_scope->name_used == false) {
-            add_node_error(irb->codegen, node, buf_sprintf("unused while label"));
-        }
-
-        if (!instr_is_unreachable(body_result)) {
-            ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result));
-            ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
-        }
-
-        if (continue_expr_node) {
-            ir_set_cursor_at_end_and_append_block(irb, continue_block);
-            IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope);
-            if (expr_result == irb->codegen->invalid_inst_src)
-                return expr_result;
-            if (!instr_is_unreachable(expr_result)) {
-                ir_mark_gen(ir_build_check_statement_is_void(irb, scope, continue_expr_node, expr_result));
-                ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime));
-            }
-        }
-
-        IrInstSrc *else_result = nullptr;
-        if (else_node) {
-            ir_set_cursor_at_end_and_append_block(irb, else_block);
-
-            if (peer_parent->peers.length != 0) {
-                peer_parent->peers.last()->next_bb = else_block;
-            }
-            ResultLocPeer *peer_result = create_peer_result(peer_parent);
-            peer_parent->peers.append(peer_result);
-
-            else_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_result->base);
-            if (else_result == irb->codegen->invalid_inst_src)
-                return else_result;
-            if (!instr_is_unreachable(else_result))
-                ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
-        }
-        IrBasicBlockSrc *after_else_block = irb->current_basic_block;
-        ir_set_cursor_at_end_and_append_block(irb, end_block);
-        if (else_result) {
-            incoming_blocks.append(after_else_block);
-            incoming_values.append(else_result);
-        } else {
-            incoming_blocks.append(after_cond_block);
-            incoming_values.append(void_else_result);
-        }
-        if (peer_parent->peers.length != 0) {
-            peer_parent->peers.last()->next_bb = end_block;
-        }
-
-        IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
-                incoming_blocks.items, incoming_values.items, peer_parent);
-        return ir_expr_wrap(irb, scope, phi, result_loc);
-    }
-}
-
-static IrInstSrc *ir_gen_for_expr(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(node->type == NodeTypeForExpr);
-
-    AstNode *array_node = node->data.for_expr.array_expr;
-    AstNode *elem_node = node->data.for_expr.elem_node;
-    AstNode *index_node = node->data.for_expr.index_node;
-    AstNode *body_node = node->data.for_expr.body;
-    AstNode *else_node = node->data.for_expr.else_node;
-
-    if (!elem_node) {
-        add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter"));
-        return irb->codegen->invalid_inst_src;
-    }
-    assert(elem_node->type == NodeTypeSymbol);
-
-    ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, parent_scope);
-
-    IrInstSrc *array_val_ptr = ir_gen_node_extra(irb, array_node, &spill_scope->base, LValPtr, nullptr);
-    if (array_val_ptr == irb->codegen->invalid_inst_src)
-        return array_val_ptr;
-
-    IrInstSrc *is_comptime = ir_build_const_bool(irb, parent_scope, node,
-        ir_should_inline(irb->exec, parent_scope) || node->data.for_expr.is_inline);
-
-    AstNode *index_var_source_node;
-    ZigVar *index_var;
-    const char *index_var_name;
-    if (index_node) {
-        index_var_source_node = index_node;
-        Buf *index_var_name_buf = index_node->data.symbol_expr.symbol;
-        index_var = ir_create_var(irb, index_node, parent_scope, index_var_name_buf, true, false, false, is_comptime);
-        index_var_name = buf_ptr(index_var_name_buf);
-    } else {
-        index_var_source_node = node;
-        index_var = ir_create_var(irb, node, parent_scope, nullptr, true, false, true, is_comptime);
-        index_var_name = "i";
-    }
-
-    IrInstSrc *zero = ir_build_const_usize(irb, parent_scope, node, 0);
-    build_decl_var_and_init(irb, parent_scope, index_var_source_node, index_var, zero, index_var_name, is_comptime);
-    parent_scope = index_var->child_scope;
-
-    IrInstSrc *one = ir_build_const_usize(irb, parent_scope, node, 1);
-    IrInstSrc *index_ptr = ir_build_var_ptr(irb, parent_scope, node, index_var);
-
-
-    IrBasicBlockSrc *cond_block = ir_create_basic_block(irb, parent_scope, "ForCond");
-    IrBasicBlockSrc *body_block = ir_create_basic_block(irb, parent_scope, "ForBody");
-    IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "ForEnd");
-    IrBasicBlockSrc *else_block = else_node ? ir_create_basic_block(irb, parent_scope, "ForElse") : end_block;
-    IrBasicBlockSrc *continue_block = ir_create_basic_block(irb, parent_scope, "ForContinue");
-
-    Buf *len_field_name = buf_create_from_str("len");
-    IrInstSrc *len_ref = ir_build_field_ptr(irb, parent_scope, node, array_val_ptr, len_field_name, false);
-    IrInstSrc *len_val = ir_build_load_ptr(irb, &spill_scope->base, node, len_ref);
-    ir_build_br(irb, parent_scope, node, cond_block, is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, cond_block);
-    IrInstSrc *index_val = ir_build_load_ptr(irb, &spill_scope->base, node, index_ptr);
-    IrInstSrc *cond = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpLessThan, index_val, len_val, false);
-    IrBasicBlockSrc *after_cond_block = irb->current_basic_block;
-    IrInstSrc *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
-    IrInstSrc *cond_br_inst = ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, cond,
-                body_block, else_block, is_comptime));
-
-    ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, body_block);
-    IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, &spill_scope->base, node, array_val_ptr, index_val,
-            false, PtrLenSingle, nullptr);
-    // TODO make it an error to write to element variable or i variable.
-    Buf *elem_var_name = elem_node->data.symbol_expr.symbol;
-    ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime);
-    Scope *child_scope = elem_var->child_scope;
-
-    IrInstSrc *elem_value = node->data.for_expr.elem_is_ptr ?
-        elem_ptr : ir_build_load_ptr(irb, &spill_scope->base, elem_node, elem_ptr);
-    build_decl_var_and_init(irb, parent_scope, elem_node, elem_var, elem_value, buf_ptr(elem_var_name), is_comptime);
-
-    if (is_duplicate_label(irb->codegen, child_scope, node, node->data.for_expr.name))
-        return irb->codegen->invalid_inst_src;
-
-    ZigList<IrInstSrc *> incoming_values = {0};
-    ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
-    ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
-    loop_scope->break_block = end_block;
-    loop_scope->continue_block = continue_block;
-    loop_scope->is_comptime = is_comptime;
-    loop_scope->incoming_blocks = &incoming_blocks;
-    loop_scope->incoming_values = &incoming_values;
-    loop_scope->lval = LValNone;
-    loop_scope->peer_parent = peer_parent;
-    loop_scope->spill_scope = spill_scope;
-
-    // Note the body block of the loop is not the place that lval and result_loc are used -
-    // it's actually in break statements, handled similarly to return statements.
-    // That is why we set those values in loop_scope above and not in this ir_gen_node call.
-    IrInstSrc *body_result = ir_gen_node(irb, body_node, &loop_scope->base);
-    if (body_result == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    if (loop_scope->name != nullptr && loop_scope->name_used == false) {
-        add_node_error(irb->codegen, node, buf_sprintf("unused for label"));
-    }
-
-    if (!instr_is_unreachable(body_result)) {
-        ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, body_result));
-        ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
-    }
-
-    ir_set_cursor_at_end_and_append_block(irb, continue_block);
-    IrInstSrc *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false);
-    ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)->allow_write_through_const = true;
-    ir_build_br(irb, child_scope, node, cond_block, is_comptime);
-
-    IrInstSrc *else_result = nullptr;
-    if (else_node) {
-        ir_set_cursor_at_end_and_append_block(irb, else_block);
-
-        if (peer_parent->peers.length != 0) {
-            peer_parent->peers.last()->next_bb = else_block;
-        }
-        ResultLocPeer *peer_result = create_peer_result(peer_parent);
-        peer_parent->peers.append(peer_result);
-        else_result = ir_gen_node_extra(irb, else_node, parent_scope, LValNone, &peer_result->base);
-        if (else_result == irb->codegen->invalid_inst_src)
-            return else_result;
-        if (!instr_is_unreachable(else_result))
-            ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
-    }
-    IrBasicBlockSrc *after_else_block = irb->current_basic_block;
-    ir_set_cursor_at_end_and_append_block(irb, end_block);
-
-    if (else_result) {
-        incoming_blocks.append(after_else_block);
-        incoming_values.append(else_result);
-    } else {
-        incoming_blocks.append(after_cond_block);
-        incoming_values.append(void_else_value);
-    }
-    if (peer_parent->peers.length != 0) {
-        peer_parent->peers.last()->next_bb = end_block;
-    }
-
-    IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, incoming_blocks.length,
-            incoming_blocks.items, incoming_values.items, peer_parent);
-    return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
-}
-
-static IrInstSrc *ir_gen_bool_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeBoolLiteral);
-    return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value);
-}
-
-static IrInstSrc *ir_gen_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeEnumLiteral);
-    Buf *name = &node->data.enum_literal.identifier->data.str_lit.str;
-    return ir_build_const_enum_literal(irb, scope, node, name);
-}
-
-static IrInstSrc *ir_gen_string_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeStringLiteral);
-
-    return ir_build_const_str_lit(irb, scope, node, node->data.string_literal.buf);
-}
-
-static IrInstSrc *ir_gen_array_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeArrayType);
-
-    AstNode *size_node = node->data.array_type.size;
-    AstNode *child_type_node = node->data.array_type.child_type;
-    bool is_const = node->data.array_type.is_const;
-    bool is_volatile = node->data.array_type.is_volatile;
-    bool is_allow_zero = node->data.array_type.allow_zero_token != nullptr;
-    AstNode *sentinel_expr = node->data.array_type.sentinel;
-    AstNode *align_expr = node->data.array_type.align_expr;
-
-    Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
-
-    IrInstSrc *sentinel;
-    if (sentinel_expr != nullptr) {
-        sentinel = ir_gen_node(irb, sentinel_expr, comptime_scope);
-        if (sentinel == irb->codegen->invalid_inst_src)
-            return sentinel;
-    } else {
-        sentinel = nullptr;
-    }
-
-    if (size_node) {
-        if (is_const) {
-            add_node_error(irb->codegen, node, buf_create_from_str("const qualifier invalid on array type"));
-            return irb->codegen->invalid_inst_src;
-        }
-        if (is_volatile) {
-            add_node_error(irb->codegen, node, buf_create_from_str("volatile qualifier invalid on array type"));
-            return irb->codegen->invalid_inst_src;
-        }
-        if (is_allow_zero) {
-            add_node_error(irb->codegen, node, buf_create_from_str("allowzero qualifier invalid on array type"));
-            return irb->codegen->invalid_inst_src;
-        }
-        if (align_expr != nullptr) {
-            add_node_error(irb->codegen, node, buf_create_from_str("align qualifier invalid on array type"));
-            return irb->codegen->invalid_inst_src;
-        }
-
-        IrInstSrc *size_value = ir_gen_node(irb, size_node, comptime_scope);
-        if (size_value == irb->codegen->invalid_inst_src)
-            return size_value;
-
-        IrInstSrc *child_type = ir_gen_node(irb, child_type_node, comptime_scope);
-        if (child_type == irb->codegen->invalid_inst_src)
-            return child_type;
-
-        return ir_build_array_type(irb, scope, node, size_value, sentinel, child_type);
-    } else {
-        IrInstSrc *align_value;
-        if (align_expr != nullptr) {
-            align_value = ir_gen_node(irb, align_expr, comptime_scope);
-            if (align_value == irb->codegen->invalid_inst_src)
-                return align_value;
-        } else {
-            align_value = nullptr;
-        }
-
-        IrInstSrc *child_type = ir_gen_node(irb, child_type_node, comptime_scope);
-        if (child_type == irb->codegen->invalid_inst_src)
-            return child_type;
-
-        return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, sentinel,
-                align_value, is_allow_zero);
-    }
-}
-
-static IrInstSrc *ir_gen_anyframe_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeAnyFrameType);
-
-    AstNode *payload_type_node = node->data.anyframe_type.payload_type;
-    IrInstSrc *payload_type_value = nullptr;
-
-    if (payload_type_node != nullptr) {
-        payload_type_value = ir_gen_node(irb, payload_type_node, scope);
-        if (payload_type_value == irb->codegen->invalid_inst_src)
-            return payload_type_value;
-
-    }
-
-    return ir_build_anyframe_type(irb, scope, node, payload_type_value);
-}
-
-static IrInstSrc *ir_gen_undefined_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeUndefinedLiteral);
-    return ir_build_const_undefined(irb, scope, node);
-}
-
-static Error parse_asm_template(IrAnalyze *ira, AstNode *source_node, Buf *asm_template,
-        ZigList<AsmToken> *tok_list)
-{
-    // TODO Connect the errors in this function back up to the actual source location
-    // rather than just the token. https://github.com/ziglang/zig/issues/2080
-    enum State {
-        StateStart,
-        StatePercent,
-        StateTemplate,
-        StateVar,
-    };
-
-    assert(tok_list->length == 0);
-
-    AsmToken *cur_tok = nullptr;
-
-    enum State state = StateStart;
-
-    for (size_t i = 0; i < buf_len(asm_template); i += 1) {
-        uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i);
-        switch (state) {
-            case StateStart:
-                if (c == '%') {
-                    tok_list->add_one();
-                    cur_tok = &tok_list->last();
-                    cur_tok->id = AsmTokenIdPercent;
-                    cur_tok->start = i;
-                    state = StatePercent;
-                } else {
-                    tok_list->add_one();
-                    cur_tok = &tok_list->last();
-                    cur_tok->id = AsmTokenIdTemplate;
-                    cur_tok->start = i;
-                    state = StateTemplate;
-                }
-                break;
-            case StatePercent:
-                if (c == '%') {
-                    cur_tok->end = i;
-                    state = StateStart;
-                } else if (c == '[') {
-                    cur_tok->id = AsmTokenIdVar;
-                    state = StateVar;
-                } else if (c == '=') {
-                    cur_tok->id = AsmTokenIdUniqueId;
-                    cur_tok->end = i;
-                    state = StateStart;
-                } else {
-                    add_node_error(ira->codegen, source_node,
-                        buf_create_from_str("expected a '%' or '['"));
-                    return ErrorSemanticAnalyzeFail;
-                }
-                break;
-            case StateTemplate:
-                if (c == '%') {
-                    cur_tok->end = i;
-                    i -= 1;
-                    cur_tok = nullptr;
-                    state = StateStart;
-                }
-                break;
-            case StateVar:
-                if (c == ']') {
-                    cur_tok->end = i;
-                    state = StateStart;
-                } else if ((c >= 'a' && c <= 'z') ||
-                        (c >= '0' && c <= '9') ||
-                        (c == '_'))
-                {
-                    // do nothing
-                } else {
-                    add_node_error(ira->codegen, source_node,
-                        buf_sprintf("invalid substitution character: '%c'", c));
-                    return ErrorSemanticAnalyzeFail;
-                }
-                break;
-        }
-    }
-
-    switch (state) {
-        case StateStart:
-            break;
-        case StatePercent:
-        case StateVar:
-            add_node_error(ira->codegen, source_node, buf_sprintf("unexpected end of assembly template"));
-            return ErrorSemanticAnalyzeFail;
-        case StateTemplate:
-            cur_tok->end = buf_len(asm_template);
-            break;
-    }
-    return ErrorNone;
-}
-
-static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_template) {
-    const char *ptr = buf_ptr(src_template) + tok->start + 2;
-    size_t len = tok->end - tok->start - 2;
-    size_t result = 0;
-    for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) {
-        AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
-        if (buf_eql_mem(asm_output->asm_symbolic_name, ptr, len)) {
-            return result;
-        }
-    }
-    for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) {
-        AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
-        if (buf_eql_mem(asm_input->asm_symbolic_name, ptr, len)) {
-            return result;
-        }
-    }
-    return SIZE_MAX;
-}
-
-static IrInstSrc *ir_gen_asm_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeAsmExpr);
-    AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
-
-    IrInstSrc *asm_template = ir_gen_node(irb, asm_expr->asm_template, scope);
-    if (asm_template == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    bool is_volatile = asm_expr->volatile_token != nullptr;
-    bool in_fn_scope = (scope_fn_entry(scope) != nullptr);
-
-    if (!in_fn_scope) {
-        if (is_volatile) {
-            add_token_error(irb->codegen, node->owner, asm_expr->volatile_token,
-                    buf_sprintf("volatile is meaningless on global assembly"));
-            return irb->codegen->invalid_inst_src;
-        }
-
-        if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 ||
-            asm_expr->clobber_list.length != 0)
-        {
-            add_node_error(irb->codegen, node,
-                buf_sprintf("global assembly cannot have inputs, outputs, or clobbers"));
-            return irb->codegen->invalid_inst_src;
-        }
-
-        return ir_build_asm_src(irb, scope, node, asm_template, nullptr, nullptr,
-                                nullptr, 0, is_volatile, true);
-    }
-
-    IrInstSrc **input_list = heap::c_allocator.allocate<IrInstSrc *>(asm_expr->input_list.length);
-    IrInstSrc **output_types = heap::c_allocator.allocate<IrInstSrc *>(asm_expr->output_list.length);
-    ZigVar **output_vars = heap::c_allocator.allocate<ZigVar *>(asm_expr->output_list.length);
-    size_t return_count = 0;
-    if (!is_volatile && asm_expr->output_list.length == 0) {
-        add_node_error(irb->codegen, node,
-                buf_sprintf("assembly expression with no output must be marked volatile"));
-        return irb->codegen->invalid_inst_src;
-    }
-    for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
-        AsmOutput *asm_output = asm_expr->output_list.at(i);
-        if (asm_output->return_type) {
-            return_count += 1;
-
-            IrInstSrc *return_type = ir_gen_node(irb, asm_output->return_type, scope);
-            if (return_type == irb->codegen->invalid_inst_src)
-                return irb->codegen->invalid_inst_src;
-            if (return_count > 1) {
-                add_node_error(irb->codegen, node,
-                        buf_sprintf("inline assembly allows up to one output value"));
-                return irb->codegen->invalid_inst_src;
-            }
-            output_types[i] = return_type;
-        } else {
-            Buf *variable_name = asm_output->variable_name;
-            // TODO there is some duplication here with ir_gen_symbol. I need to do a full audit of how
-            // inline assembly works. https://github.com/ziglang/zig/issues/215
-            ZigVar *var = find_variable(irb->codegen, scope, variable_name, nullptr);
-            if (var) {
-                output_vars[i] = var;
-            } else {
-                add_node_error(irb->codegen, node,
-                        buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
-                return irb->codegen->invalid_inst_src;
-            }
-        }
-
-        const char modifier = *buf_ptr(asm_output->constraint);
-        if (modifier != '=') {
-            add_node_error(irb->codegen, node,
-                buf_sprintf("invalid modifier starting output constraint for '%s': '%c', only '=' is supported."
-                    " Compiler TODO: see https://github.com/ziglang/zig/issues/215",
-                    buf_ptr(asm_output->asm_symbolic_name), modifier));
-            return irb->codegen->invalid_inst_src;
-        }
-    }
-    for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
-        AsmInput *asm_input = asm_expr->input_list.at(i);
-        IrInstSrc *input_value = ir_gen_node(irb, asm_input->expr, scope);
-        if (input_value == irb->codegen->invalid_inst_src)
-            return irb->codegen->invalid_inst_src;
-
-        input_list[i] = input_value;
-    }
-
-    return ir_build_asm_src(irb, scope, node, asm_template, input_list, output_types,
-                            output_vars, return_count, is_volatile, false);
-}
-
-static IrInstSrc *ir_gen_if_optional_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(node->type == NodeTypeIfOptional);
-
-    Buf *var_symbol = node->data.test_expr.var_symbol;
-    AstNode *expr_node = node->data.test_expr.target_node;
-    AstNode *then_node = node->data.test_expr.then_node;
-    AstNode *else_node = node->data.test_expr.else_node;
-    bool var_is_ptr = node->data.test_expr.var_is_ptr;
-
-    ScopeExpr *spill_scope = create_expr_scope(irb->codegen, expr_node, scope);
-    spill_scope->spill_harder = true;
-
-    IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, &spill_scope->base, LValPtr, nullptr);
-    if (maybe_val_ptr == irb->codegen->invalid_inst_src)
-        return maybe_val_ptr;
-
-    IrInstSrc *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr);
-    IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, scope, node, maybe_val);
-
-    IrBasicBlockSrc *then_block = ir_create_basic_block(irb, scope, "OptionalThen");
-    IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "OptionalElse");
-    IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "OptionalEndIf");
-
-    IrInstSrc *is_comptime;
-    if (ir_should_inline(irb->exec, scope)) {
-        is_comptime = ir_build_const_bool(irb, scope, node, true);
-    } else {
-        is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null);
-    }
-    IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, is_non_null,
-            then_block, else_block, is_comptime);
-
-    ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
-            result_loc, is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, then_block);
-
-    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, &spill_scope->base, is_comptime);
-    Scope *var_scope;
-    if (var_symbol) {
-        bool is_shadowable = false;
-        bool is_const = true;
-        ZigVar *var = ir_create_var(irb, node, subexpr_scope,
-                var_symbol, is_const, is_const, is_shadowable, is_comptime);
-
-        IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false);
-        IrInstSrc *var_value = var_is_ptr ?
-            payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, node, payload_ptr);
-        build_decl_var_and_init(irb, subexpr_scope, node, var, var_value, buf_ptr(var_symbol), is_comptime);
-        var_scope = var->child_scope;
-    } else {
-        var_scope = subexpr_scope;
-    }
-    IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval,
-            &peer_parent->peers.at(0)->base);
-    if (then_expr_result == irb->codegen->invalid_inst_src)
-        return then_expr_result;
-    IrBasicBlockSrc *after_then_block = irb->current_basic_block;
-    if (!instr_is_unreachable(then_expr_result))
-        ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
-
-    ir_set_cursor_at_end_and_append_block(irb, else_block);
-    IrInstSrc *else_expr_result;
-    if (else_node) {
-        else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base);
-        if (else_expr_result == irb->codegen->invalid_inst_src)
-            return else_expr_result;
-    } else {
-        else_expr_result = ir_build_const_void(irb, scope, node);
-        ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
-    }
-    IrBasicBlockSrc *after_else_block = irb->current_basic_block;
-    if (!instr_is_unreachable(else_expr_result))
-        ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
-
-    ir_set_cursor_at_end_and_append_block(irb, endif_block);
-    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
-    incoming_values[0] = then_expr_result;
-    incoming_values[1] = else_expr_result;
-    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
-    incoming_blocks[0] = after_then_block;
-    incoming_blocks[1] = after_else_block;
-
-    IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
-    return ir_expr_wrap(irb, scope, phi, result_loc);
-}
-
-static IrInstSrc *ir_gen_if_err_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(node->type == NodeTypeIfErrorExpr);
-
-    AstNode *target_node = node->data.if_err_expr.target_node;
-    AstNode *then_node = node->data.if_err_expr.then_node;
-    AstNode *else_node = node->data.if_err_expr.else_node;
-    bool var_is_ptr = node->data.if_err_expr.var_is_ptr;
-    bool var_is_const = true;
-    Buf *var_symbol = node->data.if_err_expr.var_symbol;
-    Buf *err_symbol = node->data.if_err_expr.err_symbol;
-
-    IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr);
-    if (err_val_ptr == irb->codegen->invalid_inst_src)
-        return err_val_ptr;
-
-    IrInstSrc *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr);
-    IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node, err_val_ptr, true, false);
-
-    IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, scope, "TryOk");
-    IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "TryElse");
-    IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "TryEnd");
-
-    bool force_comptime = ir_should_inline(irb->exec, scope);
-    IrInstSrc *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err);
-    IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime);
-
-    ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
-            result_loc, is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, ok_block);
-
-    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
-    Scope *var_scope;
-    if (var_symbol) {
-        bool is_shadowable = false;
-        IrInstSrc *var_is_comptime = force_comptime ? ir_build_const_bool(irb, subexpr_scope, node, true) : ir_build_test_comptime(irb, subexpr_scope, node, err_val);
-        ZigVar *var = ir_create_var(irb, node, subexpr_scope,
-                var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime);
-
-        IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, subexpr_scope, node, err_val_ptr, false, false);
-        IrInstSrc *var_value = var_is_ptr ?
-            payload_ptr : ir_build_load_ptr(irb, subexpr_scope, node, payload_ptr);
-        build_decl_var_and_init(irb, subexpr_scope, node, var, var_value, buf_ptr(var_symbol), var_is_comptime);
-        var_scope = var->child_scope;
-    } else {
-        var_scope = subexpr_scope;
-    }
-    IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval,
-            &peer_parent->peers.at(0)->base);
-    if (then_expr_result == irb->codegen->invalid_inst_src)
-        return then_expr_result;
-    IrBasicBlockSrc *after_then_block = irb->current_basic_block;
-    if (!instr_is_unreachable(then_expr_result))
-        ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
-
-    ir_set_cursor_at_end_and_append_block(irb, else_block);
-
-    IrInstSrc *else_expr_result;
-    if (else_node) {
-        Scope *err_var_scope;
-        if (err_symbol) {
-            bool is_shadowable = false;
-            bool is_const = true;
-            ZigVar *var = ir_create_var(irb, node, subexpr_scope,
-                    err_symbol, is_const, is_const, is_shadowable, is_comptime);
-
-            IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, subexpr_scope, node, err_val_ptr);
-            IrInstSrc *err_value = ir_build_load_ptr(irb, subexpr_scope, node, err_ptr);
-            build_decl_var_and_init(irb, subexpr_scope, node, var, err_value, buf_ptr(err_symbol), is_comptime);
-            err_var_scope = var->child_scope;
-        } else {
-            err_var_scope = subexpr_scope;
-        }
-        else_expr_result = ir_gen_node_extra(irb, else_node, err_var_scope, lval, &peer_parent->peers.at(1)->base);
-        if (else_expr_result == irb->codegen->invalid_inst_src)
-            return else_expr_result;
-    } else {
-        else_expr_result = ir_build_const_void(irb, scope, node);
-        ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
-    }
-    IrBasicBlockSrc *after_else_block = irb->current_basic_block;
-    if (!instr_is_unreachable(else_expr_result))
-        ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
-
-    ir_set_cursor_at_end_and_append_block(irb, endif_block);
-    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
-    incoming_values[0] = then_expr_result;
-    incoming_values[1] = else_expr_result;
-    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
-    incoming_blocks[0] = after_then_block;
-    incoming_blocks[1] = after_else_block;
-
-    IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
-    return ir_expr_wrap(irb, scope, phi, result_loc);
-}
-
-static bool ir_gen_switch_prong_expr(IrBuilderSrc *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node,
-        IrBasicBlockSrc *end_block, IrInstSrc *is_comptime, IrInstSrc *var_is_comptime,
-        IrInstSrc *target_value_ptr, IrInstSrc **prong_values, size_t prong_values_len,
-        ZigList<IrBasicBlockSrc *> *incoming_blocks, ZigList<IrInstSrc *> *incoming_values,
-        IrInstSrcSwitchElseVar **out_switch_else_var, LVal lval, ResultLoc *result_loc)
-{
-    assert(switch_node->type == NodeTypeSwitchExpr);
-    assert(prong_node->type == NodeTypeSwitchProng);
-
-    AstNode *expr_node = prong_node->data.switch_prong.expr;
-    AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol;
-    Scope *child_scope;
-    if (var_symbol_node) {
-        assert(var_symbol_node->type == NodeTypeSymbol);
-        Buf *var_name = var_symbol_node->data.symbol_expr.symbol;
-        bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr;
-
-        bool is_shadowable = false;
-        bool is_const = true;
-        ZigVar *var = ir_create_var(irb, var_symbol_node, scope,
-                var_name, is_const, is_const, is_shadowable, var_is_comptime);
-        child_scope = var->child_scope;
-        IrInstSrc *var_value;
-        if (out_switch_else_var != nullptr) {
-            IrInstSrcSwitchElseVar *switch_else_var = ir_build_switch_else_var(irb, scope, var_symbol_node,
-                    target_value_ptr);
-            *out_switch_else_var = switch_else_var;
-            IrInstSrc *payload_ptr = &switch_else_var->base;
-            var_value = var_is_ptr ?
-                payload_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, payload_ptr);
-        } else if (prong_values != nullptr) {
-            IrInstSrc *payload_ptr = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr,
-                    prong_values, prong_values_len);
-            var_value = var_is_ptr ?
-                payload_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, payload_ptr);
-        } else {
-            var_value = var_is_ptr ?
-                target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr);
-        }
-        build_decl_var_and_init(irb, scope, var_symbol_node, var, var_value, buf_ptr(var_name), var_is_comptime);
-    } else {
-        child_scope = scope;
-    }
-
-    IrInstSrc *expr_result = ir_gen_node_extra(irb, expr_node, child_scope, lval, result_loc);
-    if (expr_result == irb->codegen->invalid_inst_src)
-        return false;
-    if (!instr_is_unreachable(expr_result))
-        ir_mark_gen(ir_build_br(irb, scope, switch_node, end_block, is_comptime));
-    incoming_blocks->append(irb->current_basic_block);
-    incoming_values->append(expr_result);
-    return true;
-}
-
-static IrInstSrc *ir_gen_switch_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(node->type == NodeTypeSwitchExpr);
-
-    AstNode *target_node = node->data.switch_expr.expr;
-    IrInstSrc *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr);
-    if (target_value_ptr == irb->codegen->invalid_inst_src)
-        return target_value_ptr;
-    IrInstSrc *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr);
-
-    IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "SwitchElse");
-    IrBasicBlockSrc *end_block = ir_create_basic_block(irb, scope, "SwitchEnd");
-
-    size_t prong_count = node->data.switch_expr.prongs.length;
-    ZigList<IrInstSrcSwitchBrCase> cases = {0};
-
-    IrInstSrc *is_comptime;
-    IrInstSrc *var_is_comptime;
-    if (ir_should_inline(irb->exec, scope)) {
-        is_comptime = ir_build_const_bool(irb, scope, node, true);
-        var_is_comptime = is_comptime;
-    } else {
-        is_comptime = ir_build_test_comptime(irb, scope, node, target_value);
-        var_is_comptime = ir_build_test_comptime(irb, scope, node, target_value_ptr);
-    }
-
-    ZigList<IrInstSrc *> incoming_values = {0};
-    ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
-    ZigList<IrInstSrcCheckSwitchProngsRange> check_ranges = {0};
-
-    IrInstSrcSwitchElseVar *switch_else_var = nullptr;
-
-    ResultLocPeerParent *peer_parent = heap::c_allocator.create<ResultLocPeerParent>();
-    peer_parent->base.id = ResultLocIdPeerParent;
-    peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const;
-    peer_parent->end_bb = end_block;
-    peer_parent->is_comptime = is_comptime;
-    peer_parent->parent = result_loc;
-
-    ir_build_reset_result(irb, scope, node, &peer_parent->base);
-
-    // First do the else and the ranges
-    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
-    Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
-    AstNode *else_prong = nullptr;
-    AstNode *underscore_prong = nullptr;
-    for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
-        AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
-        size_t prong_item_count = prong_node->data.switch_prong.items.length;
-        if (prong_node->data.switch_prong.any_items_are_range) {
-            ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
-
-            IrInstSrc *ok_bit = nullptr;
-            AstNode *last_item_node = nullptr;
-            for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
-                AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
-                last_item_node = item_node;
-                if (item_node->type == NodeTypeSwitchRange) {
-                    AstNode *start_node = item_node->data.switch_range.start;
-                    AstNode *end_node = item_node->data.switch_range.end;
-
-                    IrInstSrc *start_value = ir_gen_node(irb, start_node, comptime_scope);
-                    if (start_value == irb->codegen->invalid_inst_src)
-                        return irb->codegen->invalid_inst_src;
-
-                    IrInstSrc *end_value = ir_gen_node(irb, end_node, comptime_scope);
-                    if (end_value == irb->codegen->invalid_inst_src)
-                        return irb->codegen->invalid_inst_src;
-
-                    IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one();
-                    check_range->start = start_value;
-                    check_range->end = end_value;
-
-                    IrInstSrc *lower_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpGreaterOrEq,
-                            target_value, start_value, false);
-                    IrInstSrc *upper_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpLessOrEq,
-                            target_value, end_value, false);
-                    IrInstSrc *both_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolAnd,
-                            lower_range_ok, upper_range_ok, false);
-                    if (ok_bit) {
-                        ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, both_ok, ok_bit, false);
-                    } else {
-                        ok_bit = both_ok;
-                    }
-                } else {
-                    IrInstSrc *item_value = ir_gen_node(irb, item_node, comptime_scope);
-                    if (item_value == irb->codegen->invalid_inst_src)
-                        return irb->codegen->invalid_inst_src;
-
-                    IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one();
-                    check_range->start = item_value;
-                    check_range->end = item_value;
-
-                    IrInstSrc *cmp_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpEq,
-                            item_value, target_value, false);
-                    if (ok_bit) {
-                        ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, cmp_ok, ok_bit, false);
-                    } else {
-                        ok_bit = cmp_ok;
-                    }
-                }
-            }
-
-            IrBasicBlockSrc *range_block_yes = ir_create_basic_block(irb, scope, "SwitchRangeYes");
-            IrBasicBlockSrc *range_block_no = ir_create_basic_block(irb, scope, "SwitchRangeNo");
-
-            assert(ok_bit);
-            assert(last_item_node);
-            IrInstSrc *br_inst = ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit,
-                        range_block_yes, range_block_no, is_comptime));
-            if (peer_parent->base.source_instruction == nullptr) {
-                peer_parent->base.source_instruction = br_inst;
-            }
-
-            if (peer_parent->peers.length > 0) {
-                peer_parent->peers.last()->next_bb = range_block_yes;
-            }
-            peer_parent->peers.append(this_peer_result_loc);
-            ir_set_cursor_at_end_and_append_block(irb, range_block_yes);
-            if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
-                is_comptime, var_is_comptime, target_value_ptr, nullptr, 0,
-                &incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base))
-            {
-                return irb->codegen->invalid_inst_src;
-            }
-
-            ir_set_cursor_at_end_and_append_block(irb, range_block_no);
-        } else {
-            if (prong_item_count == 0) {
-                if (else_prong) {
-                    ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
-                            buf_sprintf("multiple else prongs in switch expression"));
-                    add_error_note(irb->codegen, msg, else_prong,
-                            buf_sprintf("previous else prong is here"));
-                    return irb->codegen->invalid_inst_src;
-                }
-                else_prong = prong_node;
-            } else if (prong_item_count == 1 &&
-                    prong_node->data.switch_prong.items.at(0)->type == NodeTypeSymbol &&
-                    buf_eql_str(prong_node->data.switch_prong.items.at(0)->data.symbol_expr.symbol, "_")) {
-                if (underscore_prong) {
-                    ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
-                            buf_sprintf("multiple '_' prongs in switch expression"));
-                    add_error_note(irb->codegen, msg, underscore_prong,
-                            buf_sprintf("previous '_' prong is here"));
-                    return irb->codegen->invalid_inst_src;
-                }
-                underscore_prong = prong_node;
-            } else {
-                continue;
-            }
-           if (underscore_prong && else_prong) {
-                ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
-                        buf_sprintf("else and '_' prong in switch expression"));
-                if (underscore_prong == prong_node)
-                    add_error_note(irb->codegen, msg, else_prong,
-                            buf_sprintf("else prong is here"));
-                else
-                    add_error_note(irb->codegen, msg, underscore_prong,
-                            buf_sprintf("'_' prong is here"));
-                return irb->codegen->invalid_inst_src;
-            }
-            ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
-
-            IrBasicBlockSrc *prev_block = irb->current_basic_block;
-            if (peer_parent->peers.length > 0) {
-                peer_parent->peers.last()->next_bb = else_block;
-            }
-            peer_parent->peers.append(this_peer_result_loc);
-            ir_set_cursor_at_end_and_append_block(irb, else_block);
-            if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
-                is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values,
-                &switch_else_var, LValNone, &this_peer_result_loc->base))
-            {
-                return irb->codegen->invalid_inst_src;
-            }
-            ir_set_cursor_at_end(irb, prev_block);
-        }
-    }
+    instruction->base.value->type = operand_type;
+    instruction->ptr = ptr;
+    instruction->ordering = ordering;
 
-    // next do the non-else non-ranges
-    for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
-        AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
-        size_t prong_item_count = prong_node->data.switch_prong.items.length;
-        if (prong_item_count == 0)
-            continue;
-        if (prong_node->data.switch_prong.any_items_are_range)
-            continue;
-        if (underscore_prong == prong_node)
-            continue;
+    ir_ref_inst_gen(ptr);
 
-        ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
+    return &instruction->base;
+}
 
-        IrBasicBlockSrc *prong_block = ir_create_basic_block(irb, scope, "SwitchProng");
-        IrInstSrc **items = heap::c_allocator.allocate<IrInstSrc *>(prong_item_count);
+static IrInstGen *ir_build_atomic_store_gen(IrAnalyze *ira, IrInst *source_instr,
+        IrInstGen *ptr, IrInstGen *value, AtomicOrder ordering)
+{
+    IrInstGenAtomicStore *instruction = ir_build_inst_void<IrInstGenAtomicStore>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->ptr = ptr;
+    instruction->value = value;
+    instruction->ordering = ordering;
 
-        for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
-            AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
-            assert(item_node->type != NodeTypeSwitchRange);
+    ir_ref_inst_gen(ptr);
+    ir_ref_inst_gen(value);
 
-            IrInstSrc *item_value = ir_gen_node(irb, item_node, comptime_scope);
-            if (item_value == irb->codegen->invalid_inst_src)
-                return irb->codegen->invalid_inst_src;
+    return &instruction->base;
+}
 
-            IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one();
-            check_range->start = item_value;
-            check_range->end = item_value;
 
-            IrInstSrcSwitchBrCase *this_case = cases.add_one();
-            this_case->value = item_value;
-            this_case->block = prong_block;
+static IrInstGen *ir_build_vector_to_array(IrAnalyze *ira, IrInst *source_instruction,
+        ZigType *result_type, IrInstGen *vector, IrInstGen *result_loc)
+{
+    IrInstGenVectorToArray *instruction = ir_build_inst_gen<IrInstGenVectorToArray>(&ira->new_irb,
+        source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = result_type;
+    instruction->vector = vector;
+    instruction->result_loc = result_loc;
 
-            items[item_i] = item_value;
-        }
+    ir_ref_inst_gen(vector);
+    ir_ref_inst_gen(result_loc);
 
-        IrBasicBlockSrc *prev_block = irb->current_basic_block;
-        if (peer_parent->peers.length > 0) {
-            peer_parent->peers.last()->next_bb = prong_block;
-        }
-        peer_parent->peers.append(this_peer_result_loc);
-        ir_set_cursor_at_end_and_append_block(irb, prong_block);
-        if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
-            is_comptime, var_is_comptime, target_value_ptr, items, prong_item_count,
-            &incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base))
-        {
-            return irb->codegen->invalid_inst_src;
-        }
+    return &instruction->base;
+}
 
-        ir_set_cursor_at_end(irb, prev_block);
+static IrInstGen *ir_build_ptr_of_array_to_slice(IrAnalyze *ira, IrInst *source_instruction,
+        ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc)
+{
+    IrInstGenPtrOfArrayToSlice *instruction = ir_build_inst_gen<IrInstGenPtrOfArrayToSlice>(&ira->new_irb,
+        source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = result_type;
+    instruction->operand = operand;
+    instruction->result_loc = result_loc;
 
-    }
+    ir_ref_inst_gen(operand);
+    ir_ref_inst_gen(result_loc);
 
-    IrInstSrc *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value,
-            check_ranges.items, check_ranges.length, else_prong, underscore_prong != nullptr);
+    return &instruction->base;
+}
 
-    IrInstSrc *br_instruction;
-    if (cases.length == 0) {
-        br_instruction = ir_build_br(irb, scope, node, else_block, is_comptime);
-    } else {
-        IrInstSrcSwitchBr *switch_br = ir_build_switch_br_src(irb, scope, node, target_value, else_block,
-                cases.length, cases.items, is_comptime, switch_prongs_void);
-        if (switch_else_var != nullptr) {
-            switch_else_var->switch_br = switch_br;
-        }
-        br_instruction = &switch_br->base;
-    }
-    if (peer_parent->base.source_instruction == nullptr) {
-        peer_parent->base.source_instruction = br_instruction;
-    }
-    for (size_t i = 0; i < peer_parent->peers.length; i += 1) {
-        peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction;
-    }
+static IrInstGen *ir_build_array_to_vector(IrAnalyze *ira, IrInst *source_instruction,
+        IrInstGen *array, ZigType *result_type)
+{
+    IrInstGenArrayToVector *instruction = ir_build_inst_gen<IrInstGenArrayToVector>(&ira->new_irb,
+        source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = result_type;
+    instruction->array = array;
 
-    if (!else_prong && !underscore_prong) {
-        if (peer_parent->peers.length != 0) {
-            peer_parent->peers.last()->next_bb = else_block;
-        }
-        ir_set_cursor_at_end_and_append_block(irb, else_block);
-        ir_build_unreachable(irb, scope, node);
-    } else {
-        if (peer_parent->peers.length != 0) {
-            peer_parent->peers.last()->next_bb = end_block;
-        }
-    }
+    ir_ref_inst_gen(array);
 
-    ir_set_cursor_at_end_and_append_block(irb, end_block);
-    assert(incoming_blocks.length == incoming_values.length);
-    IrInstSrc *result_instruction;
-    if (incoming_blocks.length == 0) {
-        result_instruction = ir_build_const_void(irb, scope, node);
-    } else {
-        result_instruction = ir_build_phi(irb, scope, node, incoming_blocks.length,
-                incoming_blocks.items, incoming_values.items, peer_parent);
-    }
-    return ir_lval_wrap(irb, scope, result_instruction, lval, result_loc);
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_gen_comptime(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) {
-    assert(node->type == NodeTypeCompTime);
-
-    Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope);
-    // purposefully pass null for result_loc and let EndExpr handle it
-    return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr);
-}
+static IrInstGen *ir_build_assert_zero(IrAnalyze *ira, IrInst *source_instruction,
+        IrInstGen *target)
+{
+    IrInstGenAssertZero *instruction = ir_build_inst_gen<IrInstGenAssertZero>(&ira->new_irb,
+        source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = ira->codegen->builtin_types.entry_void;
+    instruction->target = target;
 
-static IrInstSrc *ir_gen_nosuspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) {
-    assert(node->type == NodeTypeNoSuspend);
+    ir_ref_inst_gen(target);
 
-    Scope *child_scope = create_nosuspend_scope(irb->codegen, node, parent_scope);
-    // purposefully pass null for result_loc and let EndExpr handle it
-    return ir_gen_node_extra(irb, node->data.nosuspend_expr.expr, child_scope, lval, nullptr);
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) {
-    IrInstSrc *is_comptime;
-    if (ir_should_inline(irb->exec, break_scope)) {
-        is_comptime = ir_build_const_bool(irb, break_scope, node, true);
-    } else {
-        is_comptime = block_scope->is_comptime;
-    }
-
-    IrInstSrc *result_value;
-    if (node->data.break_expr.expr) {
-        ResultLocPeer *peer_result = create_peer_result(block_scope->peer_parent);
-        block_scope->peer_parent->peers.append(peer_result);
-
-        result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope, block_scope->lval,
-                &peer_result->base);
-        if (result_value == irb->codegen->invalid_inst_src)
-            return irb->codegen->invalid_inst_src;
-    } else {
-        result_value = ir_build_const_void(irb, break_scope, node);
-    }
+static IrInstGen *ir_build_assert_non_null(IrAnalyze *ira, IrInst *source_instruction,
+        IrInstGen *target)
+{
+    IrInstGenAssertNonNull *instruction = ir_build_inst_gen<IrInstGenAssertNonNull>(&ira->new_irb,
+        source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = ira->codegen->builtin_types.entry_void;
+    instruction->target = target;
 
-    IrBasicBlockSrc *dest_block = block_scope->end_block;
-    if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr))
-        return irb->codegen->invalid_inst_src;
+    ir_ref_inst_gen(target);
 
-    block_scope->incoming_blocks->append(irb->current_basic_block);
-    block_scope->incoming_values->append(result_value);
-    return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_gen_break(IrBuilderSrc *irb, Scope *break_scope, AstNode *node) {
-    assert(node->type == NodeTypeBreak);
+static IrInstGenAlloca *ir_build_alloca_gen(IrAnalyze *ira, IrInst *source_instruction,
+        uint32_t align, const char *name_hint)
+{
+    IrInstGenAlloca *instruction = ir_create_inst_gen<IrInstGenAlloca>(&ira->new_irb,
+            source_instruction->scope, source_instruction->source_node);
+    instruction->align = align;
+    instruction->name_hint = name_hint;
 
-    // Search up the scope. We'll find one of these things first:
-    // * function definition scope or global scope => error, break outside loop
-    // * defer expression scope => error, cannot break out of defer expression
-    // * loop scope => OK
-    // * (if it's a labeled break) labeled block => OK
+    return instruction;
+}
 
-    Scope *search_scope = break_scope;
-    ScopeLoop *loop_scope;
-    for (;;) {
-        if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
-            if (node->data.break_expr.name != nullptr) {
-                add_node_error(irb->codegen, node, buf_sprintf("label not found: '%s'", buf_ptr(node->data.break_expr.name)));
-                return irb->codegen->invalid_inst_src;
-            } else {
-                add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));
-                return irb->codegen->invalid_inst_src;
-            }
-        } else if (search_scope->id == ScopeIdDeferExpr) {
-            add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression"));
-            return irb->codegen->invalid_inst_src;
-        } else if (search_scope->id == ScopeIdLoop) {
-            ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
-            if (node->data.break_expr.name == nullptr ||
-                (this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name)))
-            {
-                this_loop_scope->name_used = true;
-                loop_scope = this_loop_scope;
-                break;
-            }
-        } else if (search_scope->id == ScopeIdBlock) {
-            ScopeBlock *this_block_scope = (ScopeBlock *)search_scope;
-            if (node->data.break_expr.name != nullptr &&
-                (this_block_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_block_scope->name)))
-            {
-                assert(this_block_scope->end_block != nullptr);
-                this_block_scope->name_used = true;
-                return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
-            }
-        } else if (search_scope->id == ScopeIdSuspend) {
-            add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block"));
-            return irb->codegen->invalid_inst_src;
-        }
-        search_scope = search_scope->parent;
-    }
+static IrInstGen *ir_build_suspend_finish_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGenSuspendBegin *begin) {
+    IrInstGenSuspendFinish *inst = ir_build_inst_void<IrInstGenSuspendFinish>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    inst->begin = begin;
 
-    IrInstSrc *is_comptime;
-    if (ir_should_inline(irb->exec, break_scope)) {
-        is_comptime = ir_build_const_bool(irb, break_scope, node, true);
-    } else {
-        is_comptime = loop_scope->is_comptime;
-    }
+    ir_ref_inst_gen(&begin->base);
 
-    IrInstSrc *result_value;
-    if (node->data.break_expr.expr) {
-        ResultLocPeer *peer_result = create_peer_result(loop_scope->peer_parent);
-        loop_scope->peer_parent->peers.append(peer_result);
+    return &inst->base;
+}
 
-        result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope,
-                loop_scope->lval, &peer_result->base);
-        if (result_value == irb->codegen->invalid_inst_src)
-            return irb->codegen->invalid_inst_src;
-    } else {
-        result_value = ir_build_const_void(irb, break_scope, node);
-    }
+static IrInstGenAwait *ir_build_await_gen(IrAnalyze *ira, IrInst *source_instruction,
+        IrInstGen *frame, ZigType *result_type, IrInstGen *result_loc, bool is_nosuspend)
+{
+    IrInstGenAwait *instruction = ir_build_inst_gen<IrInstGenAwait>(&ira->new_irb,
+            source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = result_type;
+    instruction->frame = frame;
+    instruction->result_loc = result_loc;
+    instruction->is_nosuspend = is_nosuspend;
 
-    IrBasicBlockSrc *dest_block = loop_scope->break_block;
-    if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr))
-        return irb->codegen->invalid_inst_src;
+    ir_ref_inst_gen(frame);
+    if (result_loc != nullptr) ir_ref_inst_gen(result_loc);
 
-    loop_scope->incoming_blocks->append(irb->current_basic_block);
-    loop_scope->incoming_values->append(result_value);
-    return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
+    return instruction;
 }
 
-static IrInstSrc *ir_gen_continue(IrBuilderSrc *irb, Scope *continue_scope, AstNode *node) {
-    assert(node->type == NodeTypeContinue);
-
-    // Search up the scope. We'll find one of these things first:
-    // * function definition scope or global scope => error, break outside loop
-    // * defer expression scope => error, cannot break out of defer expression
-    // * loop scope => OK
+static IrInstGen *ir_build_resume_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *frame) {
+    IrInstGenResume *instruction = ir_build_inst_void<IrInstGenResume>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->frame = frame;
 
-    ZigList<ScopeRuntime *> runtime_scopes = {};
+    ir_ref_inst_gen(frame);
 
-    Scope *search_scope = continue_scope;
-    ScopeLoop *loop_scope;
-    for (;;) {
-        if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
-            if (node->data.continue_expr.name != nullptr) {
-                add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.continue_expr.name)));
-                return irb->codegen->invalid_inst_src;
-            } else {
-                add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop"));
-                return irb->codegen->invalid_inst_src;
-            }
-        } else if (search_scope->id == ScopeIdDeferExpr) {
-            add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression"));
-            return irb->codegen->invalid_inst_src;
-        } else if (search_scope->id == ScopeIdLoop) {
-            ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
-            if (node->data.continue_expr.name == nullptr ||
-                (this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name)))
-            {
-                this_loop_scope->name_used = true;
-                loop_scope = this_loop_scope;
-                break;
-            }
-        } else if (search_scope->id == ScopeIdRuntime) {
-            ScopeRuntime *scope_runtime = (ScopeRuntime *)search_scope;
-            runtime_scopes.append(scope_runtime);
-        }
-        search_scope = search_scope->parent;
-    }
+    return &instruction->base;
+}
 
-    IrInstSrc *is_comptime;
-    if (ir_should_inline(irb->exec, continue_scope)) {
-        is_comptime = ir_build_const_bool(irb, continue_scope, node, true);
-    } else {
-        is_comptime = loop_scope->is_comptime;
-    }
+static IrInstGen *ir_build_spill_begin_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand,
+        SpillId spill_id)
+{
+    IrInstGenSpillBegin *instruction = ir_build_inst_void<IrInstGenSpillBegin>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->operand = operand;
+    instruction->spill_id = spill_id;
 
-    for (size_t i = 0; i < runtime_scopes.length; i += 1) {
-        ScopeRuntime *scope_runtime = runtime_scopes.at(i);
-        ir_mark_gen(ir_build_check_runtime_scope(irb, continue_scope, node, scope_runtime->is_comptime, is_comptime));
-    }
-    runtime_scopes.deinit();
+    ir_ref_inst_gen(operand);
 
-    IrBasicBlockSrc *dest_block = loop_scope->continue_block;
-    if (!ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, nullptr, nullptr))
-        return irb->codegen->invalid_inst_src;
-    return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime));
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_gen_error_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeErrorType);
-    return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_global_error_set);
-}
+static IrInstGen *ir_build_spill_end_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGenSpillBegin *begin,
+        ZigType *result_type)
+{
+    IrInstGenSpillEnd *instruction = ir_build_inst_gen<IrInstGenSpillEnd>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = result_type;
+    instruction->begin = begin;
+
+    ir_ref_inst_gen(&begin->base);
 
-static IrInstSrc *ir_gen_defer(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
-    assert(node->type == NodeTypeDefer);
+    return &instruction->base;
+}
 
-    ScopeDefer *defer_child_scope = create_defer_scope(irb->codegen, node, parent_scope);
-    node->data.defer.child_scope = &defer_child_scope->base;
+static IrInstGen *ir_build_vector_extract_elem(IrAnalyze *ira, IrInst *source_instruction,
+        IrInstGen *vector, IrInstGen *index)
+{
+    IrInstGenVectorExtractElem *instruction = ir_build_inst_gen<IrInstGenVectorExtractElem>(
+            &ira->new_irb, source_instruction->scope, source_instruction->source_node);
+    instruction->base.value->type = vector->value->type->data.vector.elem_type;
+    instruction->vector = vector;
+    instruction->index = index;
 
-    ScopeDeferExpr *defer_expr_scope = create_defer_expr_scope(irb->codegen, node, parent_scope);
-    node->data.defer.expr_scope = &defer_expr_scope->base;
+    ir_ref_inst_gen(vector);
+    ir_ref_inst_gen(index);
 
-    return ir_build_const_void(irb, parent_scope, node);
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_gen_slice(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
-    assert(node->type == NodeTypeSliceExpr);
-
-    AstNodeSliceExpr *slice_expr = &node->data.slice_expr;
-    AstNode *array_node = slice_expr->array_ref_expr;
-    AstNode *start_node = slice_expr->start;
-    AstNode *end_node = slice_expr->end;
-    AstNode *sentinel_node = slice_expr->sentinel;
+static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index) {
+    IrInstGenWasmMemorySize *instruction = ir_build_inst_gen<IrInstGenWasmMemorySize>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = ira->codegen->builtin_types.entry_u32;
+    instruction->index = index;
 
-    IrInstSrc *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr);
-    if (ptr_value == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
+    ir_ref_inst_gen(index);
 
-    IrInstSrc *start_value = ir_gen_node(irb, start_node, scope);
-    if (start_value == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
+    return &instruction->base;
+}
 
-    IrInstSrc *end_value;
-    if (end_node) {
-        end_value = ir_gen_node(irb, end_node, scope);
-        if (end_value == irb->codegen->invalid_inst_src)
-            return irb->codegen->invalid_inst_src;
-    } else {
-        end_value = nullptr;
-    }
+static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index, IrInstGen *delta) {
+    IrInstGenWasmMemoryGrow *instruction = ir_build_inst_gen<IrInstGenWasmMemoryGrow>(&ira->new_irb,
+            source_instr->scope, source_instr->source_node);
+    instruction->base.value->type = ira->codegen->builtin_types.entry_i32;
+    instruction->index = index;
+    instruction->delta = delta;
 
-    IrInstSrc *sentinel_value;
-    if (sentinel_node) {
-        sentinel_value = ir_gen_node(irb, sentinel_node, scope);
-        if (sentinel_value == irb->codegen->invalid_inst_src)
-            return irb->codegen->invalid_inst_src;
-    } else {
-        sentinel_value = nullptr;
-    }
+    ir_ref_inst_gen(index);
+    ir_ref_inst_gen(delta);
 
-    IrInstSrc *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value,
-            sentinel_value, true, result_loc);
-    return ir_lval_wrap(irb, scope, slice, lval, result_loc);
+    return &instruction->base;
 }
 
-static IrInstSrc *ir_gen_catch(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
+static Error parse_asm_template(IrAnalyze *ira, AstNode *source_node, Buf *asm_template,
+        ZigList<AsmToken> *tok_list)
 {
-    assert(node->type == NodeTypeCatchExpr);
+    // TODO Connect the errors in this function back up to the actual source location
+    // rather than just the token. https://github.com/ziglang/zig/issues/2080
+    enum State {
+        StateStart,
+        StatePercent,
+        StateTemplate,
+        StateVar,
+    };
+
+    assert(tok_list->length == 0);
+
+    AsmToken *cur_tok = nullptr;
 
-    AstNode *op1_node = node->data.unwrap_err_expr.op1;
-    AstNode *op2_node = node->data.unwrap_err_expr.op2;
-    AstNode *var_node = node->data.unwrap_err_expr.symbol;
+    enum State state = StateStart;
 
-    if (op2_node->type == NodeTypeUnreachable) {
-        if (var_node != nullptr) {
-            assert(var_node->type == NodeTypeSymbol);
-            Buf *var_name = var_node->data.symbol_expr.symbol;
-            add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name)));
-            return irb->codegen->invalid_inst_src;
+    for (size_t i = 0; i < buf_len(asm_template); i += 1) {
+        uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i);
+        switch (state) {
+            case StateStart:
+                if (c == '%') {
+                    tok_list->add_one();
+                    cur_tok = &tok_list->last();
+                    cur_tok->id = AsmTokenIdPercent;
+                    cur_tok->start = i;
+                    state = StatePercent;
+                } else {
+                    tok_list->add_one();
+                    cur_tok = &tok_list->last();
+                    cur_tok->id = AsmTokenIdTemplate;
+                    cur_tok->start = i;
+                    state = StateTemplate;
+                }
+                break;
+            case StatePercent:
+                if (c == '%') {
+                    cur_tok->end = i;
+                    state = StateStart;
+                } else if (c == '[') {
+                    cur_tok->id = AsmTokenIdVar;
+                    state = StateVar;
+                } else if (c == '=') {
+                    cur_tok->id = AsmTokenIdUniqueId;
+                    cur_tok->end = i;
+                    state = StateStart;
+                } else {
+                    add_node_error(ira->codegen, source_node,
+                        buf_create_from_str("expected a '%' or '['"));
+                    return ErrorSemanticAnalyzeFail;
+                }
+                break;
+            case StateTemplate:
+                if (c == '%') {
+                    cur_tok->end = i;
+                    i -= 1;
+                    cur_tok = nullptr;
+                    state = StateStart;
+                }
+                break;
+            case StateVar:
+                if (c == ']') {
+                    cur_tok->end = i;
+                    state = StateStart;
+                } else if ((c >= 'a' && c <= 'z') ||
+                        (c >= '0' && c <= '9') ||
+                        (c == '_'))
+                {
+                    // do nothing
+                } else {
+                    add_node_error(ira->codegen, source_node,
+                        buf_sprintf("invalid substitution character: '%c'", c));
+                    return ErrorSemanticAnalyzeFail;
+                }
+                break;
         }
-        return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc);
-    }
-
-
-    ScopeExpr *spill_scope = create_expr_scope(irb->codegen, op1_node, parent_scope);
-    spill_scope->spill_harder = true;
-
-    IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, op1_node, &spill_scope->base, LValPtr, nullptr);
-    if (err_union_ptr == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *is_err = ir_build_test_err_src(irb, parent_scope, node, err_union_ptr, true, false);
-
-    IrInstSrc *is_comptime;
-    if (ir_should_inline(irb->exec, parent_scope)) {
-        is_comptime = ir_build_const_bool(irb, parent_scope, node, true);
-    } else {
-        is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_err);
-    }
-
-    IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrOk");
-    IrBasicBlockSrc *err_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrError");
-    IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrEnd");
-    IrInstSrc *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime);
-
-    ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block, result_loc,
-            is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, err_block);
-    Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, &spill_scope->base, is_comptime);
-    Scope *err_scope;
-    if (var_node) {
-        assert(var_node->type == NodeTypeSymbol);
-        Buf *var_name = var_node->data.symbol_expr.symbol;
-        bool is_const = true;
-        bool is_shadowable = false;
-        ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_name,
-            is_const, is_const, is_shadowable, is_comptime);
-        err_scope = var->child_scope;
-        IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, err_scope, node, err_union_ptr);
-        IrInstSrc *err_value = ir_build_load_ptr(irb, err_scope, var_node, err_ptr);
-        build_decl_var_and_init(irb, err_scope, var_node, var, err_value, buf_ptr(var_name), is_comptime);
-    } else {
-        err_scope = subexpr_scope;
-    }
-    IrInstSrc *err_result = ir_gen_node_extra(irb, op2_node, err_scope, LValNone, &peer_parent->peers.at(0)->base);
-    if (err_result == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-    IrBasicBlockSrc *after_err_block = irb->current_basic_block;
-    if (!instr_is_unreachable(err_result))
-        ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
-
-    ir_set_cursor_at_end_and_append_block(irb, ok_block);
-    IrInstSrc *unwrapped_ptr = ir_build_unwrap_err_payload_src(irb, parent_scope, node, err_union_ptr, false, false);
-    IrInstSrc *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
-    ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base);
-    IrBasicBlockSrc *after_ok_block = irb->current_basic_block;
-    ir_build_br(irb, parent_scope, node, end_block, is_comptime);
-
-    ir_set_cursor_at_end_and_append_block(irb, end_block);
-    IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
-    incoming_values[0] = err_result;
-    incoming_values[1] = unwrapped_payload;
-    IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
-    incoming_blocks[0] = after_err_block;
-    incoming_blocks[1] = after_ok_block;
-    IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
-    return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
-}
-
-static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) {
-    if (inner_scope == nullptr || inner_scope == outer_scope) return false;
-    bool need_comma = render_instance_name_recursive(codegen, name, outer_scope, inner_scope->parent);
-    if (inner_scope->id != ScopeIdVarDecl)
-        return need_comma;
-
-    ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope;
-    if (need_comma)
-        buf_append_char(name, ',');
-    // TODO: const ptr reinterpret here to make the var type agree with the value?
-    render_const_value(codegen, name, var_scope->var->const_value);
-    return true;
-}
-
-static Buf *get_anon_type_name(CodeGen *codegen, IrExecutableSrc *exec, const char *kind_name,
-        Scope *scope, AstNode *source_node, Buf *out_bare_name)
-{
-    if (exec != nullptr && exec->name) {
-        ZigType *import = get_scope_import(scope);
-        Buf *namespace_name = buf_alloc();
-        append_namespace_qualification(codegen, namespace_name, import);
-        buf_append_buf(namespace_name, exec->name);
-        buf_init_from_buf(out_bare_name, exec->name);
-        return namespace_name;
-    } else if (exec != nullptr && exec->name_fn != nullptr) {
-        Buf *name = buf_alloc();
-        buf_append_buf(name, &exec->name_fn->symbol_name);
-        buf_appendf(name, "(");
-        render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope);
-        buf_appendf(name, ")");
-        buf_init_from_buf(out_bare_name, name);
-        return name;
-    } else {
-        ZigType *import = get_scope_import(scope);
-        Buf *namespace_name = buf_alloc();
-        append_namespace_qualification(codegen, namespace_name, import);
-        buf_appendf(namespace_name, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize, kind_name,
-                source_node->line + 1, source_node->column + 1);
-        buf_init_from_buf(out_bare_name, namespace_name);
-        return namespace_name;
     }
-}
 
-static IrInstSrc *ir_gen_container_decl(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
-    assert(node->type == NodeTypeContainerDecl);
-
-    ContainerKind kind = node->data.container_decl.kind;
-    Buf *bare_name = buf_alloc();
-    Buf *name = get_anon_type_name(irb->codegen, irb->exec, container_string(kind), parent_scope, node, bare_name);
-
-    ContainerLayout layout = node->data.container_decl.layout;
-    ZigType *container_type = get_partial_container_type(irb->codegen, parent_scope,
-            kind, node, buf_ptr(name), bare_name, layout);
-    ScopeDecls *child_scope = get_container_scope(container_type);
-
-    for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) {
-        AstNode *child_node = node->data.container_decl.decls.at(i);
-        scan_decls(irb->codegen, child_scope, child_node);
+    switch (state) {
+        case StateStart:
+            break;
+        case StatePercent:
+        case StateVar:
+            add_node_error(ira->codegen, source_node, buf_sprintf("unexpected end of assembly template"));
+            return ErrorSemanticAnalyzeFail;
+        case StateTemplate:
+            cur_tok->end = buf_len(asm_template);
+            break;
     }
-
-    TldContainer *tld_container = heap::c_allocator.create<TldContainer>();
-    init_tld(&tld_container->base, TldIdContainer, bare_name, VisibModPub, node, parent_scope);
-    tld_container->type_entry = container_type;
-    tld_container->decls_scope = child_scope;
-    irb->codegen->resolve_queue.append(&tld_container->base);
-
-    // Add this to the list to mark as invalid if analyzing this exec fails.
-    irb->exec->tld_list.append(&tld_container->base);
-
-    return ir_build_const_type(irb, parent_scope, node, container_type);
+    return ErrorNone;
 }
 
 // errors should be populated with set1's values
@@ -10003,497 +2511,6 @@ static ZigType *make_err_set_with_one_item(CodeGen *g, Scope *parent_scope, AstN
     return err_set_type;
 }
 
-static AstNode *ast_field_to_symbol_node(AstNode *err_set_field_node) {
-    if (err_set_field_node->type == NodeTypeSymbol) {
-        return err_set_field_node;
-    } else if (err_set_field_node->type == NodeTypeErrorSetField) {
-        assert(err_set_field_node->data.err_set_field.field_name->type == NodeTypeSymbol);
-        return err_set_field_node->data.err_set_field.field_name;
-    } else {
-        return err_set_field_node;
-    }
-}
-
-static IrInstSrc *ir_gen_err_set_decl(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
-    assert(node->type == NodeTypeErrorSetDecl);
-
-    uint32_t err_count = node->data.err_set_decl.decls.length;
-
-    Buf bare_name = BUF_INIT;
-    Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", parent_scope, node, &bare_name);
-    ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
-    buf_init_from_buf(&err_set_type->name, type_name);
-    err_set_type->data.error_set.err_count = err_count;
-    err_set_type->size_in_bits = irb->codegen->builtin_types.entry_global_error_set->size_in_bits;
-    err_set_type->abi_align = irb->codegen->builtin_types.entry_global_error_set->abi_align;
-    err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size;
-    err_set_type->data.error_set.errors = heap::c_allocator.allocate<ErrorTableEntry *>(err_count);
-
-    size_t errors_count = irb->codegen->errors_by_index.length + err_count;
-    ErrorTableEntry **errors = heap::c_allocator.allocate<ErrorTableEntry *>(errors_count);
-
-    for (uint32_t i = 0; i < err_count; i += 1) {
-        AstNode *field_node = node->data.err_set_decl.decls.at(i);
-        AstNode *symbol_node = ast_field_to_symbol_node(field_node);
-        Buf *err_name = symbol_node->data.symbol_expr.symbol;
-        ErrorTableEntry *err = heap::c_allocator.create<ErrorTableEntry>();
-        err->decl_node = field_node;
-        buf_init_from_buf(&err->name, err_name);
-
-        auto existing_entry = irb->codegen->error_table.put_unique(err_name, err);
-        if (existing_entry) {
-            err->value = existing_entry->value->value;
-        } else {
-            size_t error_value_count = irb->codegen->errors_by_index.length;
-            assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)irb->codegen->err_tag_type->data.integral.bit_count));
-            err->value = error_value_count;
-            irb->codegen->errors_by_index.append(err);
-        }
-        err_set_type->data.error_set.errors[i] = err;
-
-        ErrorTableEntry *prev_err = errors[err->value];
-        if (prev_err != nullptr) {
-            ErrorMsg *msg = add_node_error(irb->codegen, ast_field_to_symbol_node(err->decl_node),
-                    buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name)));
-            add_error_note(irb->codegen, msg, ast_field_to_symbol_node(prev_err->decl_node),
-                    buf_sprintf("other error here"));
-            return irb->codegen->invalid_inst_src;
-        }
-        errors[err->value] = err;
-    }
-    heap::c_allocator.deallocate(errors, errors_count);
-    return ir_build_const_type(irb, parent_scope, node, err_set_type);
-}
-
-static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
-    assert(node->type == NodeTypeFnProto);
-
-    size_t param_count = node->data.fn_proto.params.length;
-    IrInstSrc **param_types = heap::c_allocator.allocate<IrInstSrc*>(param_count);
-
-    bool is_var_args = false;
-    for (size_t i = 0; i < param_count; i += 1) {
-        AstNode *param_node = node->data.fn_proto.params.at(i);
-        if (param_node->data.param_decl.is_var_args) {
-            is_var_args = true;
-            break;
-        }
-        if (param_node->data.param_decl.anytype_token == nullptr) {
-            AstNode *type_node = param_node->data.param_decl.type;
-            IrInstSrc *type_value = ir_gen_node(irb, type_node, parent_scope);
-            if (type_value == irb->codegen->invalid_inst_src)
-                return irb->codegen->invalid_inst_src;
-            param_types[i] = type_value;
-        } else {
-            param_types[i] = nullptr;
-        }
-    }
-
-    IrInstSrc *align_value = nullptr;
-    if (node->data.fn_proto.align_expr != nullptr) {
-        align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope);
-        if (align_value == irb->codegen->invalid_inst_src)
-            return irb->codegen->invalid_inst_src;
-    }
-
-    IrInstSrc *callconv_value = nullptr;
-    if (node->data.fn_proto.callconv_expr != nullptr) {
-        callconv_value = ir_gen_node(irb, node->data.fn_proto.callconv_expr, parent_scope);
-        if (callconv_value == irb->codegen->invalid_inst_src)
-            return irb->codegen->invalid_inst_src;
-    }
-
-    IrInstSrc *return_type;
-    if (node->data.fn_proto.return_type == nullptr) {
-        return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void);
-    } else {
-        return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope);
-        if (return_type == irb->codegen->invalid_inst_src)
-            return irb->codegen->invalid_inst_src;
-    }
-
-    return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, callconv_value, return_type, is_var_args);
-}
-
-static IrInstSrc *ir_gen_resume(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
-    assert(node->type == NodeTypeResume);
-
-    IrInstSrc *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr);
-    if (target_inst == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    return ir_build_resume_src(irb, scope, node, target_inst);
-}
-
-static IrInstSrc *ir_gen_await_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
-        ResultLoc *result_loc)
-{
-    assert(node->type == NodeTypeAwaitExpr);
-
-    bool is_nosuspend = get_scope_nosuspend(scope) != nullptr;
-
-    AstNode *expr_node = node->data.await_expr.expr;
-    if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.modifier == CallModifierBuiltin) {
-        AstNode *fn_ref_expr = expr_node->data.fn_call_expr.fn_ref_expr;
-        Buf *name = fn_ref_expr->data.symbol_expr.symbol;
-        auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
-        if (entry != nullptr) {
-            BuiltinFnEntry *builtin_fn = entry->value;
-            if (builtin_fn->id == BuiltinFnIdAsyncCall) {
-                return ir_gen_async_call(irb, scope, node, expr_node, lval, result_loc);
-            }
-        }
-    }
-
-    ZigFn *fn_entry = exec_fn_entry(irb->exec);
-    if (!fn_entry) {
-        add_node_error(irb->codegen, node, buf_sprintf("await outside function definition"));
-        return irb->codegen->invalid_inst_src;
-    }
-    ScopeSuspend *existing_suspend_scope = get_scope_suspend(scope);
-    if (existing_suspend_scope) {
-        if (!existing_suspend_scope->reported_err) {
-            ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot await inside suspend block"));
-            add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("suspend block here"));
-            existing_suspend_scope->reported_err = true;
-        }
-        return irb->codegen->invalid_inst_src;
-    }
-
-    IrInstSrc *target_inst = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
-    if (target_inst == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-
-    IrInstSrc *await_inst = ir_build_await_src(irb, scope, node, target_inst, result_loc, is_nosuspend);
-    return ir_lval_wrap(irb, scope, await_inst, lval, result_loc);
-}
-
-static IrInstSrc *ir_gen_suspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
-    assert(node->type == NodeTypeSuspend);
-
-    ZigFn *fn_entry = exec_fn_entry(irb->exec);
-    if (!fn_entry) {
-        add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition"));
-        return irb->codegen->invalid_inst_src;
-    }
-    if (get_scope_nosuspend(parent_scope) != nullptr) {
-        add_node_error(irb->codegen, node, buf_sprintf("suspend in nosuspend scope"));
-        return irb->codegen->invalid_inst_src;
-    }
-
-    ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope);
-    if (existing_suspend_scope) {
-        if (!existing_suspend_scope->reported_err) {
-            ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block"));
-            add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here"));
-            existing_suspend_scope->reported_err = true;
-        }
-        return irb->codegen->invalid_inst_src;
-    }
-
-    IrInstSrcSuspendBegin *begin = ir_build_suspend_begin_src(irb, parent_scope, node);
-    ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope);
-    Scope *child_scope = &suspend_scope->base;
-    IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope);
-    if (susp_res == irb->codegen->invalid_inst_src)
-        return irb->codegen->invalid_inst_src;
-    ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res));
-
-    return ir_mark_gen(ir_build_suspend_finish_src(irb, parent_scope, node, begin));
-}
-
-static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope,
-        LVal lval, ResultLoc *result_loc)
-{
-    assert(scope);
-    switch (node->type) {
-        case NodeTypeStructValueField:
-        case NodeTypeParamDecl:
-        case NodeTypeUsingNamespace:
-        case NodeTypeSwitchProng:
-        case NodeTypeSwitchRange:
-        case NodeTypeStructField:
-        case NodeTypeErrorSetField:
-        case NodeTypeFnDef:
-        case NodeTypeTestDecl:
-            zig_unreachable();
-        case NodeTypeBlock:
-            return ir_gen_block(irb, scope, node, lval, result_loc);
-        case NodeTypeGroupedExpr:
-            return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_loc);
-        case NodeTypeBinOpExpr:
-            return ir_gen_bin_op(irb, scope, node, lval, result_loc);
-        case NodeTypeIntLiteral:
-            return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval, result_loc);
-        case NodeTypeFloatLiteral:
-            return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval, result_loc);
-        case NodeTypeCharLiteral:
-            return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval, result_loc);
-        case NodeTypeSymbol:
-            return ir_gen_symbol(irb, scope, node, lval, result_loc);
-        case NodeTypeFnCallExpr:
-            return ir_gen_fn_call(irb, scope, node, lval, result_loc);
-        case NodeTypeIfBoolExpr:
-            return ir_gen_if_bool_expr(irb, scope, node, lval, result_loc);
-        case NodeTypePrefixOpExpr:
-            return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc);
-        case NodeTypeContainerInitExpr:
-            return ir_gen_container_init_expr(irb, scope, node, lval, result_loc);
-        case NodeTypeVariableDeclaration:
-            return ir_gen_var_decl(irb, scope, node);
-        case NodeTypeWhileExpr:
-            return ir_gen_while_expr(irb, scope, node, lval, result_loc);
-        case NodeTypeForExpr:
-            return ir_gen_for_expr(irb, scope, node, lval, result_loc);
-        case NodeTypeArrayAccessExpr:
-            return ir_gen_array_access(irb, scope, node, lval, result_loc);
-        case NodeTypeReturnExpr:
-            return ir_gen_return(irb, scope, node, lval, result_loc);
-        case NodeTypeFieldAccessExpr:
-            {
-                IrInstSrc *ptr_instruction = ir_gen_field_access(irb, scope, node);
-                if (ptr_instruction == irb->codegen->invalid_inst_src)
-                    return ptr_instruction;
-                if (lval == LValPtr || lval == LValAssign)
-                    return ptr_instruction;
-
-                IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
-                return ir_expr_wrap(irb, scope, load_ptr, result_loc);
-            }
-        case NodeTypePtrDeref: {
-            AstNode *expr_node = node->data.ptr_deref_expr.target;
-
-            LVal child_lval = lval;
-            if (child_lval == LValAssign)
-                child_lval = LValPtr;
-
-            IrInstSrc *value = ir_gen_node_extra(irb, expr_node, scope, child_lval, nullptr);
-            if (value == irb->codegen->invalid_inst_src)
-                return value;
-
-            // We essentially just converted any lvalue from &(x.*) to (&x).*;
-            // this inhibits checking that x is a pointer later, so we directly
-            // record whether the pointer check is needed
-            IrInstSrc *un_op = ir_build_un_op_lval(irb, scope, node, IrUnOpDereference, value, lval, result_loc);
-            return ir_expr_wrap(irb, scope, un_op, result_loc);
-        }
-        case NodeTypeUnwrapOptional: {
-            AstNode *expr_node = node->data.unwrap_optional.expr;
-
-            IrInstSrc *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
-            if (maybe_ptr == irb->codegen->invalid_inst_src)
-                return irb->codegen->invalid_inst_src;
-
-            IrInstSrc *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, maybe_ptr, true );
-            if (lval == LValPtr || lval == LValAssign)
-                return unwrapped_ptr;
-
-            IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
-            return ir_expr_wrap(irb, scope, load_ptr, result_loc);
-        }
-        case NodeTypeBoolLiteral:
-            return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval, result_loc);
-        case NodeTypeArrayType:
-            return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval, result_loc);
-        case NodeTypePointerType:
-            return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval, result_loc);
-        case NodeTypeAnyFrameType:
-            return ir_lval_wrap(irb, scope, ir_gen_anyframe_type(irb, scope, node), lval, result_loc);
-        case NodeTypeStringLiteral:
-            return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval, result_loc);
-        case NodeTypeUndefinedLiteral:
-            return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval, result_loc);
-        case NodeTypeAsmExpr:
-            return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval, result_loc);
-        case NodeTypeNullLiteral:
-            return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval, result_loc);
-        case NodeTypeIfErrorExpr:
-            return ir_gen_if_err_expr(irb, scope, node, lval, result_loc);
-        case NodeTypeIfOptional:
-            return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc);
-        case NodeTypeSwitchExpr:
-            return ir_gen_switch_expr(irb, scope, node, lval, result_loc);
-        case NodeTypeCompTime:
-            return ir_expr_wrap(irb, scope, ir_gen_comptime(irb, scope, node, lval), result_loc);
-        case NodeTypeNoSuspend:
-            return ir_expr_wrap(irb, scope, ir_gen_nosuspend(irb, scope, node, lval), result_loc);
-        case NodeTypeErrorType:
-            return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval, result_loc);
-        case NodeTypeBreak:
-            return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval, result_loc);
-        case NodeTypeContinue:
-            return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval, result_loc);
-        case NodeTypeUnreachable:
-            return ir_build_unreachable(irb, scope, node);
-        case NodeTypeDefer:
-            return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval, result_loc);
-        case NodeTypeSliceExpr:
-            return ir_gen_slice(irb, scope, node, lval, result_loc);
-        case NodeTypeCatchExpr:
-            return ir_gen_catch(irb, scope, node, lval, result_loc);
-        case NodeTypeContainerDecl:
-            return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval, result_loc);
-        case NodeTypeFnProto:
-            return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval, result_loc);
-        case NodeTypeErrorSetDecl:
-            return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval, result_loc);
-        case NodeTypeResume:
-            return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval, result_loc);
-        case NodeTypeAwaitExpr:
-            return ir_gen_await_expr(irb, scope, node, lval, result_loc);
-        case NodeTypeSuspend:
-            return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval, result_loc);
-        case NodeTypeEnumLiteral:
-            return ir_lval_wrap(irb, scope, ir_gen_enum_literal(irb, scope, node), lval, result_loc);
-        case NodeTypeInferredArrayType:
-            add_node_error(irb->codegen, node,
-                buf_sprintf("inferred array size invalid here"));
-            return irb->codegen->invalid_inst_src;
-        case NodeTypeAnyTypeField:
-            return ir_lval_wrap(irb, scope,
-                    ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_anytype), lval, result_loc);
-    }
-    zig_unreachable();
-}
-
-static ResultLoc *no_result_loc(void) {
-    ResultLocNone *result_loc_none = heap::c_allocator.create<ResultLocNone>();
-    result_loc_none->base.id = ResultLocIdNone;
-    return &result_loc_none->base;
-}
-
-static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval,
-        ResultLoc *result_loc)
-{
-    if (lval == LValAssign) {
-        switch (node->type) {
-            case NodeTypeStructValueField:
-            case NodeTypeParamDecl:
-            case NodeTypeUsingNamespace:
-            case NodeTypeSwitchProng:
-            case NodeTypeSwitchRange:
-            case NodeTypeStructField:
-            case NodeTypeErrorSetField:
-            case NodeTypeFnDef:
-            case NodeTypeTestDecl:
-                zig_unreachable();
-
-            // cannot be assigned to
-            case NodeTypeBlock:
-            case NodeTypeGroupedExpr:
-            case NodeTypeBinOpExpr:
-            case NodeTypeIntLiteral:
-            case NodeTypeFloatLiteral:
-            case NodeTypeCharLiteral:
-            case NodeTypeIfBoolExpr:
-            case NodeTypeContainerInitExpr:
-            case NodeTypeVariableDeclaration:
-            case NodeTypeWhileExpr:
-            case NodeTypeForExpr:
-            case NodeTypeReturnExpr:
-            case NodeTypeBoolLiteral:
-            case NodeTypeArrayType:
-            case NodeTypePointerType:
-            case NodeTypeAnyFrameType:
-            case NodeTypeStringLiteral:
-            case NodeTypeUndefinedLiteral:
-            case NodeTypeAsmExpr:
-            case NodeTypeNullLiteral:
-            case NodeTypeIfErrorExpr:
-            case NodeTypeIfOptional:
-            case NodeTypeSwitchExpr:
-            case NodeTypeCompTime:
-            case NodeTypeNoSuspend:
-            case NodeTypeErrorType:
-            case NodeTypeBreak:
-            case NodeTypeContinue:
-            case NodeTypeUnreachable:
-            case NodeTypeDefer:
-            case NodeTypeSliceExpr:
-            case NodeTypeCatchExpr:
-            case NodeTypeContainerDecl:
-            case NodeTypeFnProto:
-            case NodeTypeErrorSetDecl:
-            case NodeTypeResume:
-            case NodeTypeAwaitExpr:
-            case NodeTypeSuspend:
-            case NodeTypeEnumLiteral:
-            case NodeTypeInferredArrayType:
-            case NodeTypeAnyTypeField:
-            case NodeTypePrefixOpExpr:
-                add_node_error(irb->codegen, node,
-                    buf_sprintf("invalid left-hand side to assignment"));
-                return irb->codegen->invalid_inst_src;
-
-            // @field can be assigned to
-            case NodeTypeFnCallExpr:
-                if (node->data.fn_call_expr.modifier == CallModifierBuiltin) {
-                    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-                    Buf *name = fn_ref_expr->data.symbol_expr.symbol;
-                    auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
-
-                    if (!entry) {
-                        add_node_error(irb->codegen, node,
-                                buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
-                        return irb->codegen->invalid_inst_src;
-                    }
-
-                    if (entry->value->id == BuiltinFnIdField) {
-                        break;
-                    }
-                }
-                add_node_error(irb->codegen, node,
-                    buf_sprintf("invalid left-hand side to assignment"));
-                return irb->codegen->invalid_inst_src;
-
-
-            // can be assigned to
-            case NodeTypeUnwrapOptional:
-            case NodeTypePtrDeref:
-            case NodeTypeFieldAccessExpr:
-            case NodeTypeArrayAccessExpr:
-            case NodeTypeSymbol:
-                break;
-        }
-    }
-    if (result_loc == nullptr) {
-        // Create a result location indicating there is none - but if one gets created
-        // it will be properly distributed.
-        result_loc = no_result_loc();
-        ir_build_reset_result(irb, scope, node, result_loc);
-    }
-    Scope *child_scope;
-    if (irb->exec->is_inline ||
-        (irb->exec->fn_entry != nullptr && irb->exec->fn_entry->child_scope == scope))
-    {
-        child_scope = scope;
-    } else {
-        child_scope = &create_expr_scope(irb->codegen, node, scope)->base;
-    }
-    IrInstSrc *result = ir_gen_node_raw(irb, node, child_scope, lval, result_loc);
-    if (result == irb->codegen->invalid_inst_src) {
-        if (irb->exec->first_err_trace_msg == nullptr) {
-            irb->exec->first_err_trace_msg = irb->codegen->trace_err;
-        }
-    }
-    return result;
-}
-
-static IrInstSrc *ir_gen_node(IrBuilderSrc *irb, AstNode *node, Scope *scope) {
-    return ir_gen_node_extra(irb, node, scope, LValNone, nullptr);
-}
-
-static void invalidate_exec(IrExecutableSrc *exec, ErrorMsg *msg) {
-    if (exec->first_err_trace_msg != nullptr)
-        return;
-
-    exec->first_err_trace_msg = msg;
-
-    for (size_t i = 0; i < exec->tld_list.length; i += 1) {
-        exec->tld_list.items[i]->resolution = TldResolutionInvalid;
-    }
-}
-
 static void invalidate_exec_gen(IrExecutableGen *exec, ErrorMsg *msg) {
     if (exec->first_err_trace_msg != nullptr)
         return;
@@ -10509,78 +2526,6 @@ static void invalidate_exec_gen(IrExecutableGen *exec, ErrorMsg *msg) {
 }
 
 
-bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutableSrc *ir_executable) {
-    assert(node->owner);
-
-    IrBuilderSrc ir_builder = {0};
-    IrBuilderSrc *irb = &ir_builder;
-
-    irb->codegen = codegen;
-    irb->exec = ir_executable;
-    irb->main_block_node = node;
-
-    IrBasicBlockSrc *entry_block = ir_create_basic_block(irb, scope, "Entry");
-    ir_set_cursor_at_end_and_append_block(irb, entry_block);
-    // Entry block gets a reference because we enter it to begin.
-    ir_ref_bb(irb->current_basic_block);
-
-    IrInstSrc *result = ir_gen_node_extra(irb, node, scope, LValNone, nullptr);
-
-    if (result == irb->codegen->invalid_inst_src)
-        return false;
-
-    if (irb->exec->first_err_trace_msg != nullptr) {
-        codegen->trace_err = irb->exec->first_err_trace_msg;
-        return false;
-    }
-
-    if (!instr_is_unreachable(result)) {
-        ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, result->base.source_node, result, nullptr));
-        // no need for save_err_ret_addr because this cannot return error
-        ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
-        result_loc_ret->base.id = ResultLocIdReturn;
-        ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
-        ir_mark_gen(ir_build_end_expr(irb, scope, node, result, &result_loc_ret->base));
-        ir_mark_gen(ir_build_return_src(irb, scope, result->base.source_node, result));
-    }
-
-    return true;
-}
-
-bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) {
-    assert(fn_entry);
-
-    IrExecutableSrc *ir_executable = fn_entry->ir_executable;
-    AstNode *body_node = fn_entry->body_node;
-
-    assert(fn_entry->child_scope);
-
-    return ir_gen(codegen, body_node, fn_entry->child_scope, ir_executable);
-}
-
-static void ir_add_call_stack_errors_gen(CodeGen *codegen, IrExecutableGen *exec, ErrorMsg *err_msg, int limit) {
-    if (!exec || !exec->source_node || limit < 0) return;
-    add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here"));
-
-    ir_add_call_stack_errors_gen(codegen, exec->parent_exec, err_msg, limit - 1);
-}
-
-static void ir_add_call_stack_errors(CodeGen *codegen, IrExecutableSrc *exec, ErrorMsg *err_msg, int limit) {
-    if (!exec || !exec->source_node || limit < 0) return;
-    add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here"));
-
-    ir_add_call_stack_errors_gen(codegen, exec->parent_exec, err_msg, limit - 1);
-}
-
-static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutableSrc *exec, AstNode *source_node, Buf *msg) {
-    ErrorMsg *err_msg = add_node_error(codegen, source_node, msg);
-    invalidate_exec(exec, err_msg);
-    if (exec->parent_exec) {
-        ir_add_call_stack_errors(codegen, exec, err_msg, 10);
-    }
-    return err_msg;
-}
-
 static ErrorMsg *exec_add_error_node_gen(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, Buf *msg) {
     ErrorMsg *err_msg = add_node_error(codegen, source_node, msg);
     invalidate_exec_gen(exec, err_msg);
@@ -10605,11 +2550,6 @@ static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInst *source_instruction, Buf *m
     return ir_add_error_node(ira, source_instruction->source_node, msg);
 }
 
-static void ir_assert_impl(bool ok, IrInst *source_instruction, char const *file, unsigned int line) {
-    if (ok) return;
-    src_assert_impl(ok, source_instruction->source_node, file, line);
-}
-
 static void ir_assert_gen_impl(bool ok, IrInstGen *source_instruction, char const *file, unsigned int line) {
     if (ok) return;
     src_assert_impl(ok, source_instruction->base.source_node, file, line);
@@ -13665,8 +5605,6 @@ Error ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
     }
 
     if (codegen->verbose_ir) {
-        fprintf(stderr, "\nSource: ");
-        ast_render(stderr, node, 4);
         fprintf(stderr, "\n{ // (IR)\n");
         ir_print_src(codegen, stderr, ir_executable, 2);
         fprintf(stderr, "}\n");
@@ -19482,7 +11420,7 @@ static IrInstGen *ir_analyze_instruction_extern(IrAnalyze *ira, IrInstSrcExtern
 }
 
 static bool exec_has_err_ret_trace(CodeGen *g, IrExecutableSrc *exec) {
-    ZigFn *fn_entry = exec_fn_entry(exec);
+    ZigFn *fn_entry = exec->fn_entry;
     return fn_entry != nullptr && fn_entry->calls_or_awaits_errorable_fn && g->have_err_ret_tracing;
 }
 
@@ -20319,7 +12257,7 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node
     assert(param_decl_node->type == NodeTypeParamDecl);
 
     IrInstGen *casted_arg;
-    if (param_decl_node->data.param_decl.anytype_token == nullptr) {
+    if (param_decl_node->data.param_decl.anytype_token == 0) {
         AstNode *param_type_node = param_decl_node->data.param_decl.type;
         ZigType *param_type = ir_analyze_type_expr(ira, *exec_scope, param_type_node);
         if (type_is_invalid(param_type))
@@ -20362,7 +12300,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
         casted_arg = arg;
         param_info_type = arg->value->type;
     } else {
-        if (param_decl_node->data.param_decl.anytype_token == nullptr) {
+        if (param_decl_node->data.param_decl.anytype_token == 0) {
             AstNode *param_type_node = param_decl_node->data.param_decl.type;
             ZigType *param_type = ir_analyze_type_expr(ira, *child_scope, param_type_node);
             if (type_is_invalid(param_type))
@@ -23593,6 +15531,25 @@ static IrInstGen *ir_analyze_instruction_slice_type(IrAnalyze *ira, IrInstSrcSli
     return result;
 }
 
+static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_template) {
+    const char *ptr = buf_ptr(src_template) + tok->start + 2;
+    size_t len = tok->end - tok->start - 2;
+    size_t result = 0;
+    for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) {
+        AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
+        if (buf_eql_mem(asm_output->asm_symbolic_name, ptr, len)) {
+            return result;
+        }
+    }
+    for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) {
+        AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
+        if (buf_eql_mem(asm_input->asm_symbolic_name, ptr, len)) {
+            return result;
+        }
+    }
+    return SIZE_MAX;
+}
+
 static IrInstGen *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstSrcAsm *asm_instruction) {
     Error err;
 
@@ -27066,8 +19023,10 @@ static IrInstGen *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstSrcCImpo
         return ira->codegen->invalid_inst_gen;
 
     ZigPackage *cur_scope_pkg = scope_package(instruction->base.base.scope);
-    Buf *namespace_name = buf_sprintf("%s.cimport:%" ZIG_PRI_usize ":%" ZIG_PRI_usize,
-            buf_ptr(&cur_scope_pkg->pkg_path), node->line + 1, node->column + 1);
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    TokenLoc tok_loc = root_struct->token_locs[node->main_token];
+    Buf *namespace_name = buf_sprintf("%s.cimport:%" PRIu32 ":%" PRIu32,
+            buf_ptr(&cur_scope_pkg->pkg_path), tok_loc.line + 1, tok_loc.column + 1);
 
     ZigPackage *cimport_pkg = new_anonymous_package();
     cimport_pkg->package_table.put(buf_create_from_str("builtin"), ira->codegen->compile_var_package);
@@ -27095,12 +19054,14 @@ static IrInstGen *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstSrcCImpo
         }
         for (size_t i = 0; i < errors_len; i += 1) {
             Stage2ErrorMsg *clang_err = &errors_ptr[i];
-            // Clang can emit "too many errors, stopping now", in which case `source` and `filename_ptr` are null
+            // Clang can emit "too many errors, stopping now", in which case
+            // `source` and `filename_ptr` are null
             if (clang_err->source && clang_err->filename_ptr) {
                 ErrorMsg *err_msg = err_msg_create_with_offset(
                     clang_err->filename_ptr ?
-                        buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(),
-                    clang_err->line, clang_err->column, clang_err->offset, clang_err->source,
+                        buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) :
+                        buf_alloc(),
+                    clang_err->offset, clang_err->source,
                     buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len));
                 err_msg_add_note(parent_err_msg, err_msg);
             }
@@ -27225,7 +19186,7 @@ static IrInstGen *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstSrcEmb
     }
 
     IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr);
-    init_const_str_lit(ira->codegen, result->value, file_contents);
+    init_const_str_lit(ira->codegen, result->value, file_contents, true);
     return result;
 }
 
@@ -31851,6 +23812,22 @@ static IrInstGen *ir_analyze_instruction_has_decl(IrAnalyze *ira, IrInstSrcHasDe
     return ir_const_bool(ira, &instruction->base.base, true);
 }
 
+static void populate_invalid_variable_in_scope(CodeGen *g, Scope *scope, AstNode *node, Buf *var_name) {
+    ScopeDecls *scope_decls = nullptr;
+    while (scope != nullptr) {
+        if (scope->id == ScopeIdDecls) {
+            scope_decls = reinterpret_cast<ScopeDecls *>(scope);
+        }
+        scope = scope->parent;
+    }
+    TldVar *tld_var = heap::c_allocator.create<TldVar>();
+    init_tld(&tld_var->base, TldIdVar, var_name, VisibModPub, node, &scope_decls->base);
+    tld_var->base.resolution = TldResolutionInvalid;
+    tld_var->var = add_variable(g, node, &scope_decls->base, var_name, false,
+            g->invalid_inst_gen->value, &tld_var->base, g->builtin_types.entry_invalid);
+    scope_decls->decl_table.put(var_name, &tld_var->base);
+}
+
 static IrInstGen *ir_analyze_instruction_undeclared_ident(IrAnalyze *ira, IrInstSrcUndeclaredIdent *instruction) {
     // put a variable of same name with invalid type in global scope
     // so that future references to this same name will find a variable with an invalid type
@@ -32169,7 +24146,8 @@ static IrInstGen *ir_analyze_instruction_src(IrAnalyze *ira, IrInstSrcSrc *instr
     fields[0]->special = ConstValSpecialStatic;
 
     ZigType *import = instruction->base.base.source_node->owner;
-    Buf *path = import->data.structure.root_struct->path;
+    RootStruct *root_struct = import->data.structure.root_struct;
+    Buf *path = root_struct->path;
     ZigValue *file_name = create_const_str_lit(ira->codegen, path)->data.x_ptr.data.ref.pointee;
     init_const_slice(ira->codegen, fields[0], file_name, 0, buf_len(path), true);
     fields[0]->type = u8_slice;
@@ -32182,17 +24160,20 @@ static IrInstGen *ir_analyze_instruction_src(IrAnalyze *ira, IrInstSrcSrc *instr
     init_const_slice(ira->codegen, fields[1], fn_name, 0, buf_len(&fn_entry->symbol_name), true);
     fields[1]->type = u8_slice;
 
+
+    TokenLoc tok_loc = root_struct->token_locs[instruction->base.base.source_node->main_token];
+
     // line: u32
     ensure_field_index(source_location_type, "line", 2);
     fields[2]->special = ConstValSpecialStatic;
     fields[2]->type = ira->codegen->builtin_types.entry_u32;
-    bigint_init_unsigned(&fields[2]->data.x_bigint, instruction->base.base.source_node->line + 1);
+    bigint_init_unsigned(&fields[2]->data.x_bigint, tok_loc.line + 1);
 
     // column: u32
     ensure_field_index(source_location_type, "column", 3);
     fields[3]->special = ConstValSpecialStatic;
     fields[3]->type = ira->codegen->builtin_types.entry_u32;
-    bigint_init_unsigned(&fields[3]->data.x_bigint, instruction->base.base.source_node->column + 1);
+    bigint_init_unsigned(&fields[3]->data.x_bigint, tok_loc.column + 1);
 
     return ir_const_move(ira, &instruction->base.base, result);
 }
@@ -32539,20 +24520,6 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutableSrc *old_exec, IrExecutableGen
             old_instruction->src();
             fprintf(stderr, "~ ");
             ir_print_inst_src(codegen, stderr, old_instruction, 0);
-            bool want_break = false;
-            if (ira->break_debug_id == old_instruction->base.debug_id) {
-                want_break = true;
-            } else if (old_instruction->base.source_node != nullptr) {
-                for (size_t i = 0; i < dbg_ir_breakpoints_count; i += 1) {
-                    if (dbg_ir_breakpoints_buf[i].line == old_instruction->base.source_node->line + 1 &&
-                        buf_ends_with_str(old_instruction->base.source_node->owner->data.structure.root_struct->path,
-                                dbg_ir_breakpoints_buf[i].src_file))
-                    {
-                        want_break = true;
-                    }
-                }
-            }
-            if (want_break) BREAKPOINT;
         }
         IrInstGen *new_instruction = ir_analyze_instruction_base(ira, old_instruction);
         if (new_instruction != nullptr) {
@@ -33541,11 +25508,3 @@ void IrAnalyze::dump() {
         ir_print_basic_block_gen(this->codegen, stderr, this->new_irb.current_basic_block, 1);
     }
 }
-
-void dbg_ir_break(const char *src_file, uint32_t line) {
-    dbg_ir_breakpoints_buf[dbg_ir_breakpoints_count] = {src_file, line};
-    dbg_ir_breakpoints_count += 1;
-}
-void dbg_ir_clear(void) {
-    dbg_ir_breakpoints_count = 0;
-}
src/stage1/ir.hpp
@@ -10,9 +10,6 @@
 
 #include "all_types.hpp"
 
-bool ir_gen(CodeGen *g, AstNode *node, Scope *scope, IrExecutableSrc *ir_executable);
-bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry);
-
 IrInstGen *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn,
         ZigType *var_type, const char *name_hint);
 
@@ -33,8 +30,4 @@ struct IrAnalyze;
 ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_val,
         AstNode *source_node);
 
-// for debugging purposes
-void dbg_ir_break(const char *src_file, uint32_t line);
-void dbg_ir_clear(void);
-
 #endif
src/stage1/parser.cpp
@@ -16,28 +16,34 @@
 
 struct ParseContext {
     Buf *buf;
-    size_t current_token;
-    ZigList<Token> *tokens;
+    // Shortcut to `owner->data.structure.root_struct->token_ids`.
+    TokenId *token_ids;
+    // Shortcut to `owner->data.structure.root_struct->token_locs`.
+    TokenLoc *token_locs;
     ZigType *owner;
+    TokenIndex current_token;
     ErrColor err_color;
+    // Shortcut to `owner->data.structure.root_struct->token_count`.
+    uint32_t token_count;
 };
 
 struct PtrPayload {
-    Token *asterisk;
-    Token *payload;
+    TokenIndex asterisk;
+    TokenIndex payload;
 };
 
 struct PtrIndexPayload {
-    Token *asterisk;
-    Token *payload;
-    Token *index;
+    TokenIndex asterisk;
+    TokenIndex payload;
+    TokenIndex index;
 };
 
 static AstNode *ast_parse_root(ParseContext *pc);
 static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc);
 static AstNode *ast_parse_test_decl(ParseContext *pc);
 static AstNode *ast_parse_top_level_comptime(ParseContext *pc);
-static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, Buf *doc_comments);
+static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod,
+        TokenIndex doc_comments);
 static AstNode *ast_parse_fn_proto(ParseContext *pc);
 static AstNode *ast_parse_var_decl(ParseContext *pc);
 static AstNode *ast_parse_container_field(ParseContext *pc);
@@ -87,8 +93,8 @@ static AsmOutput *ast_parse_asm_output_item(ParseContext *pc);
 static AstNode *ast_parse_asm_input(ParseContext *pc);
 static AsmInput *ast_parse_asm_input_item(ParseContext *pc);
 static AstNode *ast_parse_asm_clobbers(ParseContext *pc);
-static Token *ast_parse_break_label(ParseContext *pc);
-static Token *ast_parse_block_label(ParseContext *pc);
+static TokenIndex ast_parse_break_label(ParseContext *pc);
+static TokenIndex ast_parse_block_label(ParseContext *pc);
 static AstNode *ast_parse_field_init(ParseContext *pc);
 static AstNode *ast_parse_while_continue_expr(ParseContext *pc);
 static AstNode *ast_parse_link_section(ParseContext *pc);
@@ -98,7 +104,7 @@ static AstNode *ast_parse_param_type(ParseContext *pc);
 static AstNode *ast_parse_if_prefix(ParseContext *pc);
 static AstNode *ast_parse_while_prefix(ParseContext *pc);
 static AstNode *ast_parse_for_prefix(ParseContext *pc);
-static Token *ast_parse_payload(ParseContext *pc);
+static TokenIndex ast_parse_payload(ParseContext *pc);
 static Optional<PtrPayload> ast_parse_ptr_payload(ParseContext *pc);
 static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc);
 static AstNode *ast_parse_switch_prong(ParseContext *pc);
@@ -122,27 +128,25 @@ static AstNode *ast_parse_byte_align(ParseContext *pc);
 
 ATTRIBUTE_PRINTF(3, 4)
 ATTRIBUTE_NORETURN
-static void ast_error(ParseContext *pc, Token *token, const char *format, ...) {
+static void ast_error(ParseContext *pc, TokenIndex token, const char *format, ...) {
     va_list ap;
     va_start(ap, format);
     Buf *msg = buf_vprintf(format, ap);
     va_end(ap);
 
-
-    ErrorMsg *err = err_msg_create_with_line(pc->owner->data.structure.root_struct->path,
-            token->start_line, token->start_column,
-            pc->owner->data.structure.root_struct->source_code,
-            pc->owner->data.structure.root_struct->line_offsets, msg);
-    err->line_start = token->start_line;
-    err->column_start = token->start_column;
+    RootStruct *root_struct = pc->owner->data.structure.root_struct;
+    assert(token < root_struct->token_count);
+    uint32_t byte_offset = root_struct->token_locs[token].offset;
+    ErrorMsg *err = err_msg_create_with_offset(root_struct->path,
+            byte_offset, buf_ptr(root_struct->source_code), msg);
 
     print_err_msg(err, pc->err_color);
     exit(EXIT_FAILURE);
 }
 
 ATTRIBUTE_NORETURN
-static void ast_invalid_token_error(ParseContext *pc, Token *token) {
-    ast_error(pc, token, "invalid token: '%s'", token_name(token->id));
+static void ast_invalid_token_error(ParseContext *pc, TokenIndex token) {
+    ast_error(pc, token, "invalid token: '%s'", token_name(pc->token_ids[token]));
 }
 
 static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) {
@@ -152,48 +156,44 @@ static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) {
     return node;
 }
 
-static AstNode *ast_create_node(ParseContext *pc, NodeType type, Token *first_token) {
+static AstNode *ast_create_node(ParseContext *pc, NodeType type, TokenIndex first_token) {
     assert(first_token);
     AstNode *node = ast_create_node_no_line_info(pc, type);
-    node->line = first_token->start_line;
-    node->column = first_token->start_column;
+    node->main_token = first_token;
     return node;
 }
 
 static AstNode *ast_create_node_copy_line_info(ParseContext *pc, NodeType type, AstNode *from) {
     assert(from);
     AstNode *node = ast_create_node_no_line_info(pc, type);
-    node->line = from->line;
-    node->column = from->column;
+    node->main_token = from->main_token;
     return node;
 }
 
-static Token *peek_token_i(ParseContext *pc, size_t i) {
-    return &pc->tokens->at(pc->current_token + i);
-}
-
-static Token *peek_token(ParseContext *pc) {
-    return peek_token_i(pc, 0);
+static TokenIndex peek_token(ParseContext *pc) {
+    return pc->current_token;
 }
 
-static Token *eat_token(ParseContext *pc) {
-    Token *res = peek_token(pc);
+static TokenIndex eat_token(ParseContext *pc) {
+    TokenIndex res = peek_token(pc);
     pc->current_token += 1;
     return res;
 }
 
-static Token *eat_token_if(ParseContext *pc, TokenId id) {
-    Token *res = peek_token(pc);
-    if (res->id == id)
+static TokenIndex eat_token_if(ParseContext *pc, TokenId id) {
+    TokenIndex res = peek_token(pc);
+    if (pc->token_ids[res] == id) {
         return eat_token(pc);
+    }
 
-    return nullptr;
+    return 0;
 }
 
-static Token *expect_token(ParseContext *pc, TokenId id) {
-    Token *res = eat_token(pc);
-    if (res->id != id)
-        ast_error(pc, res, "expected token '%s', found '%s'", token_name(id), token_name(res->id));
+static TokenIndex expect_token(ParseContext *pc, TokenId id) {
+    TokenIndex res = eat_token(pc);
+    TokenId actual_id = pc->token_ids[res];
+    if (actual_id != id)
+        ast_error(pc, res, "expected token '%s', found '%s'", token_name(id), token_name(actual_id));
 
     return res;
 }
@@ -202,23 +202,23 @@ static void put_back_token(ParseContext *pc) {
     pc->current_token -= 1;
 }
 
-static Buf *token_buf(Token *token) {
-    if (token == nullptr)
+static Buf *token_buf(ParseContext *pc, TokenIndex token) {
+    if (token == 0)
         return nullptr;
-    assert(token->id == TokenIdStringLiteral || token->id == TokenIdMultilineStringLiteral || token->id == TokenIdSymbol);
-    return &token->data.str_lit.str;
-}
 
-static BigInt *token_bigint(Token *token) {
-    assert(token->id == TokenIdIntLiteral);
-    return &token->data.int_lit.bigint;
+    RootStruct *root_struct = pc->owner->data.structure.root_struct;
+    if (root_struct->token_ids[token] == TokenIdIdentifier) {
+        return token_identifier_buf(root_struct, token);
+    } else if (root_struct->token_ids[token] == TokenIdStringLiteral) {
+        return token_string_literal_buf(root_struct, token);
+    } else {
+        zig_unreachable();
+    }
 }
 
-static AstNode *token_symbol(ParseContext *pc, Token *token) {
-    assert(token->id == TokenIdSymbol);
-    AstNode *res = ast_create_node(pc, NodeTypeSymbol, token);
-    res->data.symbol_expr.symbol = token_buf(token);
-    return res;
+static AstNode *token_identifier(ParseContext *pc, TokenIndex token) {
+    assert(pc->token_ids[token] == TokenIdIdentifier);
+    return ast_create_node(pc, NodeTypeIdentifier, token);
 }
 
 // (Rule SEP)* Rule?
@@ -231,7 +231,7 @@ static ZigList<T *> ast_parse_list(ParseContext *pc, TokenId sep, T *(*parser)(P
             break;
 
         res.append(curr);
-        if (eat_token_if(pc, sep) == nullptr)
+        if (eat_token_if(pc, sep) == 0)
             break;
     }
 
@@ -355,22 +355,22 @@ static AstNode *ast_parse_if_expr_helper(ParseContext *pc, AstNode *(*body_parse
         return nullptr;
 
     AstNode *body = ast_expect(pc, body_parser);
-    Token *err_payload = nullptr;
+    TokenIndex err_payload = 0;
     AstNode *else_body = nullptr;
-    if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) {
+    if (eat_token_if(pc, TokenIdKeywordElse) != 0) {
         err_payload = ast_parse_payload(pc);
         else_body = ast_expect(pc, body_parser);
     }
 
     assert(res->type == NodeTypeIfOptional);
-    if (err_payload != nullptr) {
+    if (err_payload != 0) {
         AstNodeTestExpr old = res->data.test_expr;
         res->type = NodeTypeIfErrorExpr;
         res->data.if_err_expr.target_node = old.target_node;
         res->data.if_err_expr.var_is_ptr = old.var_is_ptr;
         res->data.if_err_expr.var_symbol = old.var_symbol;
         res->data.if_err_expr.then_node = body;
-        res->data.if_err_expr.err_symbol = token_buf(err_payload);
+        res->data.if_err_expr.err_symbol = token_buf(pc, err_payload);
         res->data.if_err_expr.else_node = else_body;
         return res;
     }
@@ -395,22 +395,22 @@ static AstNode *ast_parse_loop_expr_helper(
     AstNode *(*for_parser)(ParseContext *),
     AstNode *(*while_parser)(ParseContext *)
 ) {
-    Token *inline_token = eat_token_if(pc, TokenIdKeywordInline);
+    TokenIndex inline_token = eat_token_if(pc, TokenIdKeywordInline);
     AstNode *for_expr = for_parser(pc);
     if (for_expr != nullptr) {
         assert(for_expr->type == NodeTypeForExpr);
-        for_expr->data.for_expr.is_inline = inline_token != nullptr;
+        for_expr->data.for_expr.is_inline = inline_token != 0;
         return for_expr;
     }
 
     AstNode *while_expr = while_parser(pc);
     if (while_expr != nullptr) {
         assert(while_expr->type == NodeTypeWhileExpr);
-        while_expr->data.while_expr.is_inline = inline_token != nullptr;
+        while_expr->data.while_expr.is_inline = inline_token != 0;
         return while_expr;
     }
 
-    if (inline_token != nullptr)
+    if (inline_token != 0)
         ast_invalid_token_error(pc, peek_token(pc));
     return nullptr;
 }
@@ -423,7 +423,7 @@ static AstNode *ast_parse_for_expr_helper(ParseContext *pc, AstNode *(*body_pars
 
     AstNode *body = ast_expect(pc, body_parser);
     AstNode *else_body = nullptr;
-    if (eat_token_if(pc, TokenIdKeywordElse) != nullptr)
+    if (eat_token_if(pc, TokenIdKeywordElse) != 0)
         else_body = ast_expect(pc, body_parser);
 
     assert(res->type == NodeTypeForExpr);
@@ -439,24 +439,24 @@ static AstNode *ast_parse_while_expr_helper(ParseContext *pc, AstNode *(*body_pa
         return nullptr;
 
     AstNode *body = ast_expect(pc, body_parser);
-    Token *err_payload = nullptr;
+    TokenIndex err_payload = 0;
     AstNode *else_body = nullptr;
-    if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) {
+    if (eat_token_if(pc, TokenIdKeywordElse) != 0) {
         err_payload = ast_parse_payload(pc);
         else_body = ast_expect(pc, body_parser);
     }
 
     assert(res->type == NodeTypeWhileExpr);
     res->data.while_expr.body = body;
-    res->data.while_expr.err_symbol = token_buf(err_payload);
+    res->data.while_expr.err_symbol = token_buf(pc, err_payload);
     res->data.while_expr.else_node = else_body;
     return res;
 }
 
 template<TokenId id, BinOpType op>
 AstNode *ast_parse_bin_op_simple(ParseContext *pc) {
-    Token *op_token = eat_token_if(pc, id);
-    if (op_token == nullptr)
+    TokenIndex op_token = eat_token_if(pc, id);
+    if (op_token == 0)
         return nullptr;
 
     AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token);
@@ -464,20 +464,25 @@ AstNode *ast_parse_bin_op_simple(ParseContext *pc) {
     return res;
 }
 
-AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ZigType *owner, ErrColor err_color) {
+AstNode *ast_parse(Buf *buf, ZigType *owner, ErrColor err_color) {
+    RootStruct *root_struct = owner->data.structure.root_struct;
+
     ParseContext pc = {};
     pc.err_color = err_color;
     pc.owner = owner;
     pc.buf = buf;
-    pc.tokens = tokens;
+    pc.token_ids = root_struct->token_ids;
+    pc.token_locs = root_struct->token_locs;
+    pc.token_count = root_struct->token_count;
+    pc.current_token = 1; // Skip over the first (invalid) token.
     return ast_parse_root(&pc);
 }
 
 // Root <- skip ContainerMembers eof
 static AstNode *ast_parse_root(ParseContext *pc) {
-    Token *first = peek_token(pc);
+    TokenIndex first = peek_token(pc);
     AstNodeContainerDecl members = ast_parse_container_members(pc);
-    if (pc->current_token != pc->tokens->length - 1)
+    if (pc->current_token != pc->token_count - 1)
         ast_invalid_token_error(pc, peek_token(pc));
 
     AstNode *node = ast_create_node(pc, NodeTypeContainerDecl, first);
@@ -486,70 +491,26 @@ static AstNode *ast_parse_root(ParseContext *pc) {
     node->data.container_decl.layout = ContainerLayoutAuto;
     node->data.container_decl.kind = ContainerKindStruct;
     node->data.container_decl.is_root = true;
-    if (buf_len(&members.doc_comments) != 0) {
-        node->data.container_decl.doc_comments = members.doc_comments;
-    }
+    node->data.container_decl.doc_comments = members.doc_comments;
 
     return node;
 }
 
-static Token *ast_parse_multiline_string_literal(ParseContext *pc, Buf *buf) {
-    Token *first_str_token = nullptr;
-    Token *str_token = nullptr;
-    while ((str_token = eat_token_if(pc, TokenIdMultilineStringLiteral))) {
-        if (first_str_token == nullptr) {
-            first_str_token = str_token;
-        }
-        if (buf->list.length == 0) {
-            buf_resize(buf, 0);
-        }
-        buf_append_buf(buf, token_buf(str_token));
-
-        // Ignore inline comments
-        size_t cur_token = pc->current_token;
-        while (eat_token_if(pc, TokenIdDocComment));
-
-        // Lookahead to see if there's another multilne string literal,
-        // if not, we have to revert back to before the doc comment
-        if (peek_token(pc)->id != TokenIdMultilineStringLiteral) {
-            pc->current_token = cur_token;
-        } else {
-            buf_append_char(buf, '\n'); // Add a newline between comments
-        }
+static TokenIndex ast_parse_multi_tok(ParseContext *pc, TokenId token_id) {
+    TokenIndex first_token = eat_token_if(pc, token_id);
+    TokenIndex token = first_token;
+    while (token != 0) {
+        token = eat_token_if(pc, token_id);
     }
-    return first_str_token;
+    return first_token;
 }
 
-static Token *ast_parse_doc_comments(ParseContext *pc, Buf *buf) {
-    Token *first_doc_token = nullptr;
-    Token *doc_token = nullptr;
-    while ((doc_token = eat_token_if(pc, TokenIdDocComment))) {
-        if (first_doc_token == nullptr) {
-            first_doc_token = doc_token;
-        }
-        if (buf->list.length == 0) {
-            buf_resize(buf, 0);
-        }
-        // chops off '///' but leaves '\n'
-        buf_append_mem(buf, buf_ptr(pc->buf) + doc_token->start_pos + 3,
-                doc_token->end_pos - doc_token->start_pos - 3);
-    }
-    return first_doc_token;
+static TokenIndex ast_parse_doc_comments(ParseContext *pc) {
+    return ast_parse_multi_tok(pc, TokenIdDocComment);
 }
 
-static void ast_parse_container_doc_comments(ParseContext *pc, Buf *buf) {
-    if (buf_len(buf) != 0 && peek_token(pc)->id == TokenIdContainerDocComment) {
-        buf_append_char(buf, '\n');
-    }
-    Token *doc_token = nullptr;
-    while ((doc_token = eat_token_if(pc, TokenIdContainerDocComment))) {
-        if (buf->list.length == 0) {
-            buf_resize(buf, 0);
-        }
-        // chops off '//!' but leaves '\n'
-        buf_append_mem(buf, buf_ptr(pc->buf) + doc_token->start_pos + 3,
-                doc_token->end_pos - doc_token->start_pos - 3);
-    }
+static TokenIndex ast_parse_container_doc_comments(ParseContext *pc) {
+    return ast_parse_multi_tok(pc, TokenIdContainerDocComment);
 }
 
 enum ContainerFieldState {
@@ -570,14 +531,11 @@ enum ContainerFieldState {
 //      /
 static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
     AstNodeContainerDecl res = {};
-    Buf tld_doc_comment_buf = BUF_INIT;
-    buf_resize(&tld_doc_comment_buf, 0);
     ContainerFieldState field_state = ContainerFieldStateNone;
-    Token *first_token = nullptr;
+    TokenIndex first_token = 0;
+    res.doc_comments = ast_parse_container_doc_comments(pc);
     for (;;) {
-        ast_parse_container_doc_comments(pc, &tld_doc_comment_buf);
-
-        Token *peeked_token = peek_token(pc);
+        TokenIndex peeked_token = peek_token(pc);
 
         AstNode *test_decl = ast_parse_test_decl(pc);
         if (test_decl != nullptr) {
@@ -599,15 +557,14 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
             continue;
         }
 
-        Buf doc_comment_buf = BUF_INIT;
-        ast_parse_doc_comments(pc, &doc_comment_buf);
+        TokenIndex first_doc_token = ast_parse_doc_comments(pc);
 
         peeked_token = peek_token(pc);
 
-        Token *visib_token = eat_token_if(pc, TokenIdKeywordPub);
-        VisibMod visib_mod = visib_token != nullptr ? VisibModPub : VisibModPrivate;
+        TokenIndex visib_token = eat_token_if(pc, TokenIdKeywordPub);
+        VisibMod visib_mod = (visib_token != 0) ? VisibModPub : VisibModPrivate;
 
-        AstNode *top_level_decl = ast_parse_top_level_decl(pc, visib_mod, &doc_comment_buf);
+        AstNode *top_level_decl = ast_parse_top_level_decl(pc, visib_mod, first_doc_token);
         if (top_level_decl != nullptr) {
             if (field_state == ContainerFieldStateSeen) {
                 field_state = ContainerFieldStateEnd;
@@ -617,11 +574,11 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
             continue;
         }
 
-        if (visib_token != nullptr) {
+        if (visib_token != 0) {
             ast_error(pc, peek_token(pc), "expected function or variable declaration after pub");
         }
 
-        Token *comptime_token = eat_token_if(pc, TokenIdKeywordCompTime);
+        TokenIndex comptime_token = eat_token_if(pc, TokenIdKeywordCompTime);
 
         AstNode *container_field = ast_parse_container_field(pc);
         if (container_field != nullptr) {
@@ -636,10 +593,10 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
             }
 
             assert(container_field->type == NodeTypeStructField);
-            container_field->data.struct_field.doc_comments = doc_comment_buf;
+            container_field->data.struct_field.doc_comments = first_doc_token;
             container_field->data.struct_field.comptime_token = comptime_token;
             res.fields.append(container_field);
-            if (eat_token_if(pc, TokenIdComma) != nullptr) {
+            if (eat_token_if(pc, TokenIdComma) != 0) {
                 continue;
             } else {
                 break;
@@ -648,33 +605,32 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
 
         break;
     }
-    res.doc_comments = tld_doc_comment_buf;
     return res;
 }
 
 // TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block
 static AstNode *ast_parse_test_decl(ParseContext *pc) {
-    Token *test = eat_token_if(pc, TokenIdKeywordTest);
-    if (test == nullptr)
+    TokenIndex test = eat_token_if(pc, TokenIdKeywordTest);
+    if (test == 0)
         return nullptr;
 
-    Token *name = eat_token_if(pc, TokenIdStringLiteral);
+    TokenIndex name = eat_token_if(pc, TokenIdStringLiteral);
     AstNode *block = ast_expect(pc, ast_parse_block);
     AstNode *res = ast_create_node(pc, NodeTypeTestDecl, test);
-    res->data.test_decl.name = name ? token_buf(name) : nullptr;
+    res->data.test_decl.name = name ? token_buf(pc, name) : nullptr;
     res->data.test_decl.body = block;
     return res;
 }
 
 // TopLevelComptime <- KEYWORD_comptime BlockExpr
 static AstNode *ast_parse_top_level_comptime(ParseContext *pc) {
-    Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime);
-    if (comptime == nullptr)
+    TokenIndex comptime = eat_token_if(pc, TokenIdKeywordCompTime);
+    if (comptime == 0)
         return nullptr;
 
     // 1 token lookahead because it could be a comptime struct field
-    Token *lbrace = peek_token(pc);
-    if (lbrace->id != TokenIdLBrace) {
+    TokenIndex lbrace = peek_token(pc);
+    if (pc->token_ids[lbrace] != TokenIdLBrace) {
         put_back_token(pc);
         return nullptr;
     }
@@ -689,39 +645,40 @@ static AstNode *ast_parse_top_level_comptime(ParseContext *pc) {
 //     <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
 //      / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
 //      / KEYWORD_use Expr SEMICOLON
-static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, Buf *doc_comments) {
-    Token *first = eat_token_if(pc, TokenIdKeywordExport);
-    if (first == nullptr)
+static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod,
+    TokenIndex doc_comments)
+{
+    TokenIndex first = eat_token_if(pc, TokenIdKeywordExport);
+    if (first == 0)
         first = eat_token_if(pc, TokenIdKeywordExtern);
-    if (first == nullptr)
+    if (first == 0)
         first = eat_token_if(pc, TokenIdKeywordInline);
-    if (first == nullptr)
+    if (first == 0)
         first = eat_token_if(pc, TokenIdKeywordNoInline);
-    if (first != nullptr) {
-        Token *lib_name = nullptr;
-        if (first->id == TokenIdKeywordExtern)
+    if (first != 0) {
+        TokenIndex lib_name = 0;
+        if (pc->token_ids[first] == TokenIdKeywordExtern)
             lib_name = eat_token_if(pc, TokenIdStringLiteral);
 
-        if (first->id != TokenIdKeywordNoInline && first->id != TokenIdKeywordInline) {
-            Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
+        if (pc->token_ids[first] != TokenIdKeywordNoInline && pc->token_ids[first] != TokenIdKeywordInline) {
+            TokenIndex thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
             AstNode *var_decl = ast_parse_var_decl(pc);
             if (var_decl != nullptr) {
                 assert(var_decl->type == NodeTypeVariableDeclaration);
-                if (first->id == TokenIdKeywordExtern && var_decl->data.variable_declaration.expr != nullptr) {
+                if (pc->token_ids[first] == TokenIdKeywordExtern && var_decl->data.variable_declaration.expr != nullptr) {
                     ast_error(pc, first, "extern variables have no initializers");
                 }
-                var_decl->line = first->start_line;
-                var_decl->column = first->start_column;
+                var_decl->main_token = first;
                 var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw;
                 var_decl->data.variable_declaration.visib_mod = visib_mod;
-                var_decl->data.variable_declaration.doc_comments = *doc_comments;
-                var_decl->data.variable_declaration.is_extern = first->id == TokenIdKeywordExtern;
-                var_decl->data.variable_declaration.is_export = first->id == TokenIdKeywordExport;
-                var_decl->data.variable_declaration.lib_name = token_buf(lib_name);
+                var_decl->data.variable_declaration.doc_comments = doc_comments;
+                var_decl->data.variable_declaration.is_extern = pc->token_ids[first] == TokenIdKeywordExtern;
+                var_decl->data.variable_declaration.is_export = pc->token_ids[first] == TokenIdKeywordExport;
+                var_decl->data.variable_declaration.lib_name = token_buf(pc, lib_name);
                 return var_decl;
             }
 
-            if (thread_local_kw != nullptr)
+            if (thread_local_kw != 0)
                 put_back_token(pc);
         }
 
@@ -732,14 +689,13 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
                 expect_token(pc, TokenIdSemicolon);
 
             assert(fn_proto->type == NodeTypeFnProto);
-            fn_proto->line = first->start_line;
-            fn_proto->column = first->start_column;
+            fn_proto->main_token = first;
             fn_proto->data.fn_proto.visib_mod = visib_mod;
-            fn_proto->data.fn_proto.doc_comments = *doc_comments;
+            fn_proto->data.fn_proto.doc_comments = doc_comments;
             if (!fn_proto->data.fn_proto.is_extern)
-                fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern;
-            fn_proto->data.fn_proto.is_export = first->id == TokenIdKeywordExport;
-            switch (first->id) {
+                fn_proto->data.fn_proto.is_extern = pc->token_ids[first] == TokenIdKeywordExtern;
+            fn_proto->data.fn_proto.is_export = pc->token_ids[first] == TokenIdKeywordExport;
+            switch (pc->token_ids[first]) {
                 case TokenIdKeywordInline:
                     fn_proto->data.fn_proto.fn_inline = FnInlineAlways;
                     break;
@@ -750,7 +706,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
                     fn_proto->data.fn_proto.fn_inline = FnInlineAuto;
                     break;
             }
-            fn_proto->data.fn_proto.lib_name = token_buf(lib_name);
+            fn_proto->data.fn_proto.lib_name = token_buf(pc, lib_name);
 
             AstNode *res = fn_proto;
             if (body != nullptr) {
@@ -769,17 +725,17 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
         ast_invalid_token_error(pc, peek_token(pc));
     }
 
-    Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
+    TokenIndex thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
     AstNode *var_decl = ast_parse_var_decl(pc);
     if (var_decl != nullptr) {
         assert(var_decl->type == NodeTypeVariableDeclaration);
         var_decl->data.variable_declaration.visib_mod = visib_mod;
-        var_decl->data.variable_declaration.doc_comments = *doc_comments;
+        var_decl->data.variable_declaration.doc_comments = doc_comments;
         var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw;
         return var_decl;
     }
 
-    if (thread_local_kw != nullptr)
+    if (thread_local_kw != 0)
         put_back_token(pc);
 
     AstNode *fn_proto = ast_parse_fn_proto(pc);
@@ -790,7 +746,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
 
         assert(fn_proto->type == NodeTypeFnProto);
         fn_proto->data.fn_proto.visib_mod = visib_mod;
-        fn_proto->data.fn_proto.doc_comments = *doc_comments;
+        fn_proto->data.fn_proto.doc_comments = doc_comments;
         AstNode *res = fn_proto;
         if (body != nullptr) {
             res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto);
@@ -802,8 +758,8 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
         return res;
     }
 
-    Token *usingnamespace = eat_token_if(pc, TokenIdKeywordUsingNamespace);
-    if (usingnamespace != nullptr) {
+    TokenIndex usingnamespace = eat_token_if(pc, TokenIdKeywordUsingNamespace);
+    if (usingnamespace != 0) {
         AstNode *expr = ast_expect(pc, ast_parse_expr);
         expect_token(pc, TokenIdSemicolon);
 
@@ -818,12 +774,12 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
 
 // FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_anytype / TypeExpr)
 static AstNode *ast_parse_fn_proto(ParseContext *pc) {
-    Token *first = eat_token_if(pc, TokenIdKeywordFn);
-    if (first == nullptr) {
+    TokenIndex first = eat_token_if(pc, TokenIdKeywordFn);
+    if (first == 0) {
         return nullptr;
     }
 
-    Token *identifier = eat_token_if(pc, TokenIdSymbol);
+    TokenIndex identifier = eat_token_if(pc, TokenIdIdentifier);
     expect_token(pc, TokenIdLParen);
     ZigList<AstNode *> params = ast_parse_list(pc, TokenIdComma, ast_parse_param_decl);
     expect_token(pc, TokenIdRParen);
@@ -831,29 +787,29 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
     AstNode *align_expr = ast_parse_byte_align(pc);
     AstNode *section_expr = ast_parse_link_section(pc);
     AstNode *callconv_expr = ast_parse_callconv(pc);
-    Token *exmark = nullptr;
+    TokenIndex exmark = 0;
     AstNode *return_type = nullptr;
 
     exmark = eat_token_if(pc, TokenIdBang);
     return_type = ast_parse_type_expr(pc);
     if (return_type == nullptr) {
-        Token *next = peek_token(pc);
+        TokenIndex next = peek_token(pc);
         ast_error(
             pc,
             next,
             "expected return type (use 'void' to return nothing), found: '%s'",
-            token_name(next->id)
+            token_name(pc->token_ids[next])
         );
     }
 
     AstNode *res = ast_create_node(pc, NodeTypeFnProto, first);
     res->data.fn_proto = {};
-    res->data.fn_proto.name = token_buf(identifier);
+    res->data.fn_proto.name = token_buf(pc, identifier);
     res->data.fn_proto.params = params;
     res->data.fn_proto.align_expr = align_expr;
     res->data.fn_proto.section_expr = section_expr;
     res->data.fn_proto.callconv_expr = callconv_expr;
-    res->data.fn_proto.auto_err_set = exmark != nullptr;
+    res->data.fn_proto.auto_err_set = exmark != 0;
     res->data.fn_proto.return_type = return_type;
 
     for (size_t i = 0; i < params.length; i++) {
@@ -869,28 +825,28 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
 
 // VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
 static AstNode *ast_parse_var_decl(ParseContext *pc) {
-    Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst);
-    if (mut_kw == nullptr)
+    TokenIndex mut_kw = eat_token_if(pc, TokenIdKeywordConst);
+    if (mut_kw == 0)
         mut_kw = eat_token_if(pc, TokenIdKeywordVar);
-    if (mut_kw == nullptr)
+    if (mut_kw == 0)
         return nullptr;
 
-    Token *identifier = expect_token(pc, TokenIdSymbol);
+    TokenIndex identifier = expect_token(pc, TokenIdIdentifier);
     AstNode *type_expr = nullptr;
-    if (eat_token_if(pc, TokenIdColon) != nullptr)
+    if (eat_token_if(pc, TokenIdColon) != 0)
         type_expr = ast_expect(pc, ast_parse_type_expr);
 
     AstNode *align_expr = ast_parse_byte_align(pc);
     AstNode *section_expr = ast_parse_link_section(pc);
     AstNode *expr = nullptr;
-    if (eat_token_if(pc, TokenIdEq) != nullptr)
+    if (eat_token_if(pc, TokenIdEq) != 0)
         expr = ast_expect(pc, ast_parse_expr);
 
     expect_token(pc, TokenIdSemicolon);
 
     AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw);
-    res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst;
-    res->data.variable_declaration.symbol = token_buf(identifier);
+    res->data.variable_declaration.is_const = pc->token_ids[mut_kw] == TokenIdKeywordConst;
+    res->data.variable_declaration.symbol = token_buf(pc, identifier);
     res->data.variable_declaration.type = type_expr;
     res->data.variable_declaration.align_expr = align_expr;
     res->data.variable_declaration.section_expr = section_expr;
@@ -900,14 +856,14 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) {
 
 // ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?
 static AstNode *ast_parse_container_field(ParseContext *pc) {
-    Token *identifier = eat_token_if(pc, TokenIdSymbol);
-    if (identifier == nullptr)
+    TokenIndex identifier = eat_token_if(pc, TokenIdIdentifier);
+    if (identifier == 0)
         return nullptr;
 
     AstNode *type_expr = nullptr;
-    if (eat_token_if(pc, TokenIdColon) != nullptr) {
-        Token *anytype_tok = eat_token_if(pc, TokenIdKeywordAnyType);
-        if (anytype_tok != nullptr) {
+    if (eat_token_if(pc, TokenIdColon) != 0) {
+        TokenIndex anytype_tok = eat_token_if(pc, TokenIdKeywordAnyType);
+        if (anytype_tok != 0) {
             type_expr = ast_create_node(pc, NodeTypeAnyTypeField, anytype_tok);
         } else {
             type_expr = ast_expect(pc, ast_parse_type_expr);
@@ -915,11 +871,11 @@ static AstNode *ast_parse_container_field(ParseContext *pc) {
     }
     AstNode *align_expr = ast_parse_byte_align(pc);
     AstNode *expr = nullptr;
-    if (eat_token_if(pc, TokenIdEq) != nullptr)
+    if (eat_token_if(pc, TokenIdEq) != 0)
         expr = ast_expect(pc, ast_parse_expr);
 
     AstNode *res = ast_create_node(pc, NodeTypeStructField, identifier);
-    res->data.struct_field.name = token_buf(identifier);
+    res->data.struct_field.name = token_buf(pc, identifier);
     res->data.struct_field.type = type_expr;
     res->data.struct_field.value = expr;
     res->data.struct_field.align_expr = align_expr;
@@ -938,52 +894,52 @@ static AstNode *ast_parse_container_field(ParseContext *pc) {
 //      / SwitchExpr
 //      / AssignExpr SEMICOLON
 static AstNode *ast_parse_statement(ParseContext *pc) {
-    Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime);
+    TokenIndex comptime = eat_token_if(pc, TokenIdKeywordCompTime);
     AstNode *var_decl = ast_parse_var_decl(pc);
     if (var_decl != nullptr) {
         assert(var_decl->type == NodeTypeVariableDeclaration);
-        var_decl->data.variable_declaration.is_comptime = comptime != nullptr;
+        var_decl->data.variable_declaration.is_comptime = comptime != 0;
         return var_decl;
     }
 
-    if (comptime != nullptr) {
+    if (comptime != 0) {
         AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement);
         AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime);
         res->data.comptime_expr.expr = statement;
         return res;
     }
 
-    Token *nosuspend = eat_token_if(pc, TokenIdKeywordNoSuspend);
-    if (nosuspend != nullptr) {
+    TokenIndex nosuspend = eat_token_if(pc, TokenIdKeywordNoSuspend);
+    if (nosuspend != 0) {
         AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement);
         AstNode *res = ast_create_node(pc, NodeTypeNoSuspend, nosuspend);
         res->data.nosuspend_expr.expr = statement;
         return res;
     }
 
-    Token *suspend = eat_token_if(pc, TokenIdKeywordSuspend);
-    if (suspend != nullptr) {
+    TokenIndex suspend = eat_token_if(pc, TokenIdKeywordSuspend);
+    if (suspend != 0) {
         AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement);
         AstNode *res = ast_create_node(pc, NodeTypeSuspend, suspend);
         res->data.suspend.block = statement;
         return res;
     }
 
-    Token *defer = eat_token_if(pc, TokenIdKeywordDefer);
-    if (defer == nullptr)
+    TokenIndex defer = eat_token_if(pc, TokenIdKeywordDefer);
+    if (defer == 0)
         defer = eat_token_if(pc, TokenIdKeywordErrdefer);
-    if (defer != nullptr) {
-        Token *payload = (defer->id == TokenIdKeywordErrdefer) ?
-            ast_parse_payload(pc) : nullptr;
+    if (defer != 0) {
+        TokenIndex payload = (pc->token_ids[defer] == TokenIdKeywordErrdefer) ?
+            ast_parse_payload(pc) : 0;
         AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement);
         AstNode *res = ast_create_node(pc, NodeTypeDefer, defer);
 
         res->data.defer.kind = ReturnKindUnconditional;
         res->data.defer.expr = statement;
-        if (defer->id == TokenIdKeywordErrdefer) {
+        if (pc->token_ids[defer] == TokenIdKeywordErrdefer) {
             res->data.defer.kind = ReturnKindError;
-            if (payload != nullptr)
-                res->data.defer.err_payload = token_symbol(pc, payload);
+            if (payload != 0)
+                res->data.defer.err_payload = token_identifier(pc, payload);
         }
         return res;
     }
@@ -1025,13 +981,13 @@ static AstNode *ast_parse_if_statement(ParseContext *pc) {
     }
 
     if (body == nullptr) {
-        Token *tok = eat_token(pc);
-        ast_error(pc, tok, "expected if body, found '%s'", token_name(tok->id));
+        TokenIndex tok = eat_token(pc);
+        ast_error(pc, tok, "expected if body, found '%s'", token_name(pc->token_ids[tok]));
     }
 
-    Token *err_payload = nullptr;
+    TokenIndex err_payload = 0;
     AstNode *else_body = nullptr;
-    if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) {
+    if (eat_token_if(pc, TokenIdKeywordElse) != 0) {
         err_payload = ast_parse_payload(pc);
         else_body = ast_expect(pc, ast_parse_statement);
     }
@@ -1040,14 +996,14 @@ static AstNode *ast_parse_if_statement(ParseContext *pc) {
         expect_token(pc, TokenIdSemicolon);
 
     assert(res->type == NodeTypeIfOptional);
-    if (err_payload != nullptr) {
+    if (err_payload != 0) {
         AstNodeTestExpr old = res->data.test_expr;
         res->type = NodeTypeIfErrorExpr;
         res->data.if_err_expr.target_node = old.target_node;
         res->data.if_err_expr.var_is_ptr = old.var_is_ptr;
         res->data.if_err_expr.var_symbol = old.var_symbol;
         res->data.if_err_expr.then_node = body;
-        res->data.if_err_expr.err_symbol = token_buf(err_payload);
+        res->data.if_err_expr.err_symbol = token_buf(pc, err_payload);
         res->data.if_err_expr.else_node = else_body;
         return res;
     }
@@ -1068,11 +1024,11 @@ static AstNode *ast_parse_if_statement(ParseContext *pc) {
 
 // LabeledStatement <- BlockLabel? (Block / LoopStatement)
 static AstNode *ast_parse_labeled_statement(ParseContext *pc) {
-    Token *label = ast_parse_block_label(pc);
+    TokenIndex label = ast_parse_block_label(pc);
     AstNode *block = ast_parse_block(pc);
     if (block != nullptr) {
         assert(block->type == NodeTypeBlock);
-        block->data.block.name = token_buf(label);
+        block->data.block.name = token_buf(pc, label);
         return block;
     }
 
@@ -1080,10 +1036,10 @@ static AstNode *ast_parse_labeled_statement(ParseContext *pc) {
     if (loop != nullptr) {
         switch (loop->type) {
             case NodeTypeForExpr:
-                loop->data.for_expr.name = token_buf(label);
+                loop->data.for_expr.name = token_buf(pc, label);
                 break;
             case NodeTypeWhileExpr:
-                loop->data.while_expr.name = token_buf(label);
+                loop->data.while_expr.name = token_buf(pc, label);
                 break;
             default:
                 zig_unreachable();
@@ -1091,29 +1047,29 @@ static AstNode *ast_parse_labeled_statement(ParseContext *pc) {
         return loop;
     }
 
-    if (label != nullptr)
+    if (label != 0)
         ast_invalid_token_error(pc, peek_token(pc));
     return nullptr;
 }
 
 // LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
 static AstNode *ast_parse_loop_statement(ParseContext *pc) {
-    Token *inline_token = eat_token_if(pc, TokenIdKeywordInline);
+    TokenIndex inline_token = eat_token_if(pc, TokenIdKeywordInline);
     AstNode *for_statement = ast_parse_for_statement(pc);
     if (for_statement != nullptr) {
         assert(for_statement->type == NodeTypeForExpr);
-        for_statement->data.for_expr.is_inline = inline_token != nullptr;
+        for_statement->data.for_expr.is_inline = inline_token != 0;
         return for_statement;
     }
 
     AstNode *while_statement = ast_parse_while_statement(pc);
     if (while_statement != nullptr) {
         assert(while_statement->type == NodeTypeWhileExpr);
-        while_statement->data.while_expr.is_inline = inline_token != nullptr;
+        while_statement->data.while_expr.is_inline = inline_token != 0;
         return while_statement;
     }
 
-    if (inline_token != nullptr)
+    if (inline_token != 0)
         ast_invalid_token_error(pc, peek_token(pc));
     return nullptr;
 }
@@ -1134,12 +1090,12 @@ static AstNode *ast_parse_for_statement(ParseContext *pc) {
     }
 
     if (body == nullptr) {
-        Token *tok = eat_token(pc);
-        ast_error(pc, tok, "expected loop body, found '%s'", token_name(tok->id));
+        TokenIndex tok = eat_token(pc);
+        ast_error(pc, tok, "expected loop body, found '%s'", token_name(pc->token_ids[tok]));
     }
 
     AstNode *else_body = nullptr;
-    if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) {
+    if (eat_token_if(pc, TokenIdKeywordElse) != 0) {
         else_body = ast_expect(pc, ast_parse_statement);
     }
 
@@ -1168,13 +1124,13 @@ static AstNode *ast_parse_while_statement(ParseContext *pc) {
     }
 
     if (body == nullptr) {
-        Token *tok = eat_token(pc);
-        ast_error(pc, tok, "expected loop body, found '%s'", token_name(tok->id));
+        TokenIndex tok = eat_token(pc);
+        ast_error(pc, tok, "expected loop body, found '%s'", token_name(pc->token_ids[tok]));
     }
 
-    Token *err_payload = nullptr;
+    TokenIndex err_payload = 0;
     AstNode *else_body = nullptr;
-    if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) {
+    if (eat_token_if(pc, TokenIdKeywordElse) != 0) {
         err_payload = ast_parse_payload(pc);
         else_body = ast_expect(pc, ast_parse_statement);
     }
@@ -1184,7 +1140,7 @@ static AstNode *ast_parse_while_statement(ParseContext *pc) {
 
     assert(res->type == NodeTypeWhileExpr);
     res->data.while_expr.body = body;
-    res->data.while_expr.err_symbol = token_buf(err_payload);
+    res->data.while_expr.err_symbol = token_buf(pc, err_payload);
     res->data.while_expr.else_node = else_body;
     return res;
 }
@@ -1209,11 +1165,11 @@ static AstNode *ast_parse_block_expr_statement(ParseContext *pc) {
 
 // BlockExpr <- BlockLabel? Block
 static AstNode *ast_parse_block_expr(ParseContext *pc) {
-    Token *label = ast_parse_block_label(pc);
-    if (label != nullptr) {
+    TokenIndex label = ast_parse_block_label(pc);
+    if (label != 0) {
         AstNode *res = ast_expect(pc, ast_parse_block);
         assert(res->type == NodeTypeBlock);
-        res->data.block.name = token_buf(label);
+        res->data.block.name = token_buf(pc, label);
         return res;
     }
 
@@ -1230,8 +1186,8 @@ static AstNode *ast_parse_expr(ParseContext *pc) {
     return ast_parse_prefix_op_expr(
         pc,
         [](ParseContext *context) {
-            Token *try_token = eat_token_if(context, TokenIdKeywordTry);
-            if (try_token != nullptr) {
+            TokenIndex try_token = eat_token_if(context, TokenIdKeywordTry);
+            if (try_token != 0) {
                 AstNode *res = ast_create_node(context, NodeTypeReturnExpr, try_token);
                 res->data.return_expr.kind = ReturnKindError;
                 return res;
@@ -1318,72 +1274,72 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc) {
     if (if_expr != nullptr)
         return if_expr;
 
-    Token *break_token = eat_token_if(pc, TokenIdKeywordBreak);
-    if (break_token != nullptr) {
-        Token *label = ast_parse_break_label(pc);
+    TokenIndex break_token = eat_token_if(pc, TokenIdKeywordBreak);
+    if (break_token != 0) {
+        TokenIndex label = ast_parse_break_label(pc);
         AstNode *expr = ast_parse_expr(pc);
 
         AstNode *res = ast_create_node(pc, NodeTypeBreak, break_token);
-        res->data.break_expr.name = token_buf(label);
+        res->data.break_expr.name = token_buf(pc, label);
         res->data.break_expr.expr = expr;
         return res;
     }
 
-    Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime);
-    if (comptime != nullptr) {
+    TokenIndex comptime = eat_token_if(pc, TokenIdKeywordCompTime);
+    if (comptime != 0) {
         AstNode *expr = ast_expect(pc, ast_parse_expr);
         AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime);
         res->data.comptime_expr.expr = expr;
         return res;
     }
 
-    Token *nosuspend = eat_token_if(pc, TokenIdKeywordNoSuspend);
-    if (nosuspend != nullptr) {
+    TokenIndex nosuspend = eat_token_if(pc, TokenIdKeywordNoSuspend);
+    if (nosuspend != 0) {
         AstNode *expr = ast_expect(pc, ast_parse_expr);
         AstNode *res = ast_create_node(pc, NodeTypeNoSuspend, nosuspend);
         res->data.nosuspend_expr.expr = expr;
         return res;
     }
 
-    Token *continue_token = eat_token_if(pc, TokenIdKeywordContinue);
-    if (continue_token != nullptr) {
-        Token *label = ast_parse_break_label(pc);
+    TokenIndex continue_token = eat_token_if(pc, TokenIdKeywordContinue);
+    if (continue_token != 0) {
+        TokenIndex label = ast_parse_break_label(pc);
         AstNode *res = ast_create_node(pc, NodeTypeContinue, continue_token);
-        res->data.continue_expr.name = token_buf(label);
+        res->data.continue_expr.name = token_buf(pc, label);
         return res;
     }
 
-    Token *resume = eat_token_if(pc, TokenIdKeywordResume);
-    if (resume != nullptr) {
+    TokenIndex resume = eat_token_if(pc, TokenIdKeywordResume);
+    if (resume != 0) {
         AstNode *expr = ast_expect(pc, ast_parse_expr);
         AstNode *res = ast_create_node(pc, NodeTypeResume, resume);
         res->data.resume_expr.expr = expr;
         return res;
     }
 
-    Token *return_token = eat_token_if(pc, TokenIdKeywordReturn);
-    if (return_token != nullptr) {
+    TokenIndex return_token = eat_token_if(pc, TokenIdKeywordReturn);
+    if (return_token != 0) {
         AstNode *expr = ast_parse_expr(pc);
         AstNode *res = ast_create_node(pc, NodeTypeReturnExpr, return_token);
         res->data.return_expr.expr = expr;
         return res;
     }
 
-    Token *label = ast_parse_block_label(pc);
+    TokenIndex label = ast_parse_block_label(pc);
     AstNode *loop = ast_parse_loop_expr(pc);
     if (loop != nullptr) {
         switch (loop->type) {
             case NodeTypeForExpr:
-                loop->data.for_expr.name = token_buf(label);
+                loop->data.for_expr.name = token_buf(pc, label);
                 break;
             case NodeTypeWhileExpr:
-                loop->data.while_expr.name = token_buf(label);
+                loop->data.while_expr.name = token_buf(pc, label);
                 break;
             default:
                 zig_unreachable();
         }
         return loop;
-    } else if (label != nullptr) {
+    } else if (label != 0) {
         // Restore the tokens that we eaten by ast_parse_block_label.
         put_back_token(pc);
         put_back_token(pc);
@@ -1407,8 +1363,8 @@ static AstNode *ast_parse_if_expr(ParseContext *pc) {
 
 // Block <- LBRACE Statement* RBRACE
 static AstNode *ast_parse_block(ParseContext *pc) {
-    Token *lbrace = eat_token_if(pc, TokenIdLBrace);
-    if (lbrace == nullptr)
+    TokenIndex lbrace = eat_token_if(pc, TokenIdLBrace);
+    if (lbrace == 0)
         return nullptr;
 
     ZigList<AstNode *> statements = {};
@@ -1462,8 +1418,8 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc) {
 //      / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
 //      / LBRACE RBRACE
 static AstNode *ast_parse_init_list(ParseContext *pc) {
-    Token *lbrace = eat_token_if(pc, TokenIdLBrace);
-    if (lbrace == nullptr)
+    TokenIndex lbrace = eat_token_if(pc, TokenIdLBrace);
+    if (lbrace == 0)
         return nullptr;
 
     AstNode *first = ast_parse_field_init(pc);
@@ -1472,7 +1428,7 @@ static AstNode *ast_parse_init_list(ParseContext *pc) {
         res->data.container_init_expr.kind = ContainerInitKindStruct;
         res->data.container_init_expr.entries.append(first);
 
-        while (eat_token_if(pc, TokenIdComma) != nullptr) {
+        while (eat_token_if(pc, TokenIdComma) != 0) {
             AstNode *field_init = ast_parse_field_init(pc);
             if (field_init == nullptr)
                 break;
@@ -1490,7 +1446,7 @@ static AstNode *ast_parse_init_list(ParseContext *pc) {
     if (first != nullptr) {
         res->data.container_init_expr.entries.append(first);
 
-        while (eat_token_if(pc, TokenIdComma) != nullptr) {
+        while (eat_token_if(pc, TokenIdComma) != 0) {
             AstNode *expr = ast_parse_expr(pc);
             if (expr == nullptr)
                 break;
@@ -1535,7 +1491,7 @@ static AstNode *ast_parse_error_union_expr(ParseContext *pc) {
 //     <- KEYWORD_async   PrimaryTypeExpr SuffixOp* FnCallArguments
 //      / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
 static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
-    Token *async_token = eat_token_if(pc, TokenIdKeywordAsync);
+    TokenIndex async_token = eat_token_if(pc, TokenIdKeywordAsync);
     if (async_token) {
         AstNode *child = ast_expect(pc, ast_parse_primary_type_expr);
         while (true) {
@@ -1652,43 +1608,21 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
 //      / STRINGLITERAL
 //      / SwitchExpr
 static AstNode *ast_parse_primary_type_expr(ParseContext *pc) {
-    // TODO: This is not in line with the grammar.
-    //       Because the prev stage 1 tokenizer does not parse
-    //       @[a-zA-Z_][a-zA-Z0-9_] as one token, it has to do a
-    //       hack, where it accepts '@' (IDENTIFIER / KEYWORD_export /
-    //       KEYWORD_extern).
-    //       I'd say that it's better if '@' is part of the builtin
-    //       identifier token.
-    Token *at_sign = eat_token_if(pc, TokenIdAtSign);
-    if (at_sign != nullptr) {
-        Buf *name;
-        Token *token;
-        if ((token = eat_token_if(pc, TokenIdKeywordExport)) != nullptr) {
-            name = buf_create_from_str("export");
-        } else if ((token = eat_token_if(pc, TokenIdKeywordExtern)) != nullptr) {
-            name = buf_create_from_str("extern");
-        } else {
-            token = expect_token(pc, TokenIdSymbol);
-            name = token_buf(token);
-        }
-
+    TokenIndex builtin_tok = eat_token_if(pc, TokenIdBuiltin);
+    if (builtin_tok != 0) {
         AstNode *res = ast_expect(pc, ast_parse_fn_call_arguments);
-        AstNode *name_sym = ast_create_node(pc, NodeTypeSymbol, token);
-        name_sym->data.symbol_expr.symbol = name;
+        AstNode *name_sym = ast_create_node(pc, NodeTypeIdentifier, builtin_tok);
 
         assert(res->type == NodeTypeFnCallExpr);
-        res->line = at_sign->start_line;
-        res->column = at_sign->start_column;
+        res->main_token = builtin_tok;
         res->data.fn_call_expr.fn_ref_expr = name_sym;
         res->data.fn_call_expr.modifier = CallModifierBuiltin;
         return res;
     }
 
-    Token *char_lit = eat_token_if(pc, TokenIdCharLiteral);
-    if (char_lit != nullptr) {
-        AstNode *res = ast_create_node(pc, NodeTypeCharLiteral, char_lit);
-        res->data.char_literal.value = char_lit->data.char_lit.c;
-        return res;
+    TokenIndex char_lit = eat_token_if(pc, TokenIdCharLiteral);
+    if (char_lit != 0) {
+        return ast_create_node(pc, NodeTypeCharLiteral, char_lit);
     }
 
     AstNode *container_decl = ast_parse_container_decl(pc);
@@ -1703,12 +1637,9 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) {
     if (error_set_decl != nullptr)
         return error_set_decl;
 
-    Token *float_lit = eat_token_if(pc, TokenIdFloatLiteral);
-    if (float_lit != nullptr) {
-        AstNode *res = ast_create_node(pc, NodeTypeFloatLiteral, float_lit);
-        res->data.float_literal.bigfloat = &float_lit->data.float_lit.bigfloat;
-        res->data.float_literal.overflow = float_lit->data.float_lit.overflow;
-        return res;
+    TokenIndex float_lit = eat_token_if(pc, TokenIdFloatLiteral);
+    if (float_lit != 0) {
+        return ast_create_node(pc, NodeTypeFloatLiteral, float_lit);
     }
 
     AstNode *fn_proto = ast_parse_fn_proto(pc);
@@ -1723,86 +1654,77 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) {
     if (labeled_type_expr != nullptr)
         return labeled_type_expr;
 
-    Token *identifier = eat_token_if(pc, TokenIdSymbol);
-    if (identifier != nullptr)
-        return token_symbol(pc, identifier);
+    TokenIndex identifier = eat_token_if(pc, TokenIdIdentifier);
+    if (identifier != 0)
+        return token_identifier(pc, identifier);
 
     AstNode *if_type_expr = ast_parse_if_type_expr(pc);
     if (if_type_expr != nullptr)
         return if_type_expr;
 
-    Token *int_lit = eat_token_if(pc, TokenIdIntLiteral);
-    if (int_lit != nullptr) {
-        AstNode *res = ast_create_node(pc, NodeTypeIntLiteral, int_lit);
-        res->data.int_literal.bigint = &int_lit->data.int_lit.bigint;
-        return res;
+    TokenIndex int_lit = eat_token_if(pc, TokenIdIntLiteral);
+    if (int_lit != 0) {
+        return ast_create_node(pc, NodeTypeIntLiteral, int_lit);
     }
 
-    Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime);
-    if (comptime != nullptr) {
+    TokenIndex comptime = eat_token_if(pc, TokenIdKeywordCompTime);
+    if (comptime != 0) {
         AstNode *expr = ast_expect(pc, ast_parse_type_expr);
         AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime);
         res->data.comptime_expr.expr = expr;
         return res;
     }
 
-    Token *error = eat_token_if(pc, TokenIdKeywordError);
-    if (error != nullptr) {
-        Token *dot = expect_token(pc, TokenIdDot);
-        Token *name = expect_token(pc, TokenIdSymbol);
+    TokenIndex error = eat_token_if(pc, TokenIdKeywordError);
+    if (error != 0) {
+        TokenIndex dot = expect_token(pc, TokenIdDot);
+        TokenIndex name = expect_token(pc, TokenIdIdentifier);
         AstNode *left = ast_create_node(pc, NodeTypeErrorType, error);
         AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot);
         res->data.field_access_expr.struct_expr = left;
-        res->data.field_access_expr.field_name = token_buf(name);
+        res->data.field_access_expr.field_name = token_buf(pc, name);
         return res;
     }
 
-    Token *false_token = eat_token_if(pc, TokenIdKeywordFalse);
-    if (false_token != nullptr) {
+    TokenIndex false_token = eat_token_if(pc, TokenIdKeywordFalse);
+    if (false_token != 0) {
         AstNode *res = ast_create_node(pc, NodeTypeBoolLiteral, false_token);
         res->data.bool_literal.value = false;
         return res;
     }
 
-    Token *null = eat_token_if(pc, TokenIdKeywordNull);
-    if (null != nullptr)
+    TokenIndex null = eat_token_if(pc, TokenIdKeywordNull);
+    if (null != 0)
         return ast_create_node(pc, NodeTypeNullLiteral, null);
 
-    Token *anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame);
-    if (anyframe != nullptr)
+    TokenIndex anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame);
+    if (anyframe != 0)
         return ast_create_node(pc, NodeTypeAnyFrameType, anyframe);
 
-    Token *true_token = eat_token_if(pc, TokenIdKeywordTrue);
-    if (true_token != nullptr) {
+    TokenIndex true_token = eat_token_if(pc, TokenIdKeywordTrue);
+    if (true_token != 0) {
         AstNode *res = ast_create_node(pc, NodeTypeBoolLiteral, true_token);
         res->data.bool_literal.value = true;
         return res;
     }
 
-    Token *undefined = eat_token_if(pc, TokenIdKeywordUndefined);
-    if (undefined != nullptr)
+    TokenIndex undefined = eat_token_if(pc, TokenIdKeywordUndefined);
+    if (undefined != 0)
         return ast_create_node(pc, NodeTypeUndefinedLiteral, undefined);
 
-    Token *unreachable = eat_token_if(pc, TokenIdKeywordUnreachable);
-    if (unreachable != nullptr)
+    TokenIndex unreachable = eat_token_if(pc, TokenIdKeywordUnreachable);
+    if (unreachable != 0)
         return ast_create_node(pc, NodeTypeUnreachable, unreachable);
 
 
-    Buf *string_buf;
-    Token *string_lit = eat_token_if(pc, TokenIdStringLiteral);
-    if (string_lit != nullptr) {
-      string_buf = token_buf(string_lit);
-    } else {
-        Buf multiline_string_buf = BUF_INIT;
-        string_lit = ast_parse_multiline_string_literal(pc, &multiline_string_buf);
-        if (string_lit != nullptr) {
-            string_buf = buf_create_from_buf(&multiline_string_buf);
-        }
+    TokenIndex string_lit = eat_token_if(pc, TokenIdStringLiteral);
+    if (string_lit != 0) {
+        return ast_create_node(pc, NodeTypeStringLiteral, string_lit);
     }
-    if (string_lit != nullptr) {
-        AstNode *res = ast_create_node(pc, NodeTypeStringLiteral, string_lit);
-        res->data.string_literal.buf = string_buf;
-        return res;
+
+    TokenIndex multiline_str_lit = ast_parse_multi_tok(pc, TokenIdMultilineStringLiteralLine);
+    if (multiline_str_lit != 0) {
+        return ast_create_node(pc, NodeTypeStringLiteral, multiline_str_lit);
     }
 
     AstNode *switch_expr = ast_parse_switch_expr(pc);
@@ -1814,22 +1736,21 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) {
 
 // ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
 static AstNode *ast_parse_container_decl(ParseContext *pc) {
-    Token *layout_token = eat_token_if(pc, TokenIdKeywordExtern);
-    if (layout_token == nullptr)
+    TokenIndex layout_token = eat_token_if(pc, TokenIdKeywordExtern);
+    if (layout_token == 0)
         layout_token = eat_token_if(pc, TokenIdKeywordPacked);
 
     AstNode *res = ast_parse_container_decl_auto(pc);
     if (res == nullptr) {
-        if (layout_token != nullptr)
+        if (layout_token != 0)
             put_back_token(pc);
         return nullptr;
     }
 
     assert(res->type == NodeTypeContainerDecl);
-    if (layout_token != nullptr) {
-        res->line = layout_token->start_line;
-        res->column = layout_token->start_column;
-        res->data.container_decl.layout = layout_token->id == TokenIdKeywordExtern
+    if (layout_token != 0) {
+        res->main_token = layout_token;
+        res->data.container_decl.layout = pc->token_ids[layout_token] == TokenIdKeywordExtern
             ? ContainerLayoutExtern
             : ContainerLayoutPacked;
     }
@@ -1838,28 +1759,27 @@ static AstNode *ast_parse_container_decl(ParseContext *pc) {
 
 // ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE
 static AstNode *ast_parse_error_set_decl(ParseContext *pc) {
-    Token *first = eat_token_if(pc, TokenIdKeywordError);
-    if (first == nullptr)
+    TokenIndex first = eat_token_if(pc, TokenIdKeywordError);
+    if (first == 0)
         return nullptr;
-    if (eat_token_if(pc, TokenIdLBrace) == nullptr) {
+    if (eat_token_if(pc, TokenIdLBrace) == 0) {
         put_back_token(pc);
         return nullptr;
     }
 
     ZigList<AstNode *> decls = ast_parse_list<AstNode>(pc, TokenIdComma, [](ParseContext *context) {
-        Buf doc_comment_buf = BUF_INIT;
-        Token *doc_token = ast_parse_doc_comments(context, &doc_comment_buf);
-        Token *ident = eat_token_if(context, TokenIdSymbol);
-        if (ident == nullptr)
+        TokenIndex doc_token = ast_parse_doc_comments(context);
+        TokenIndex ident = eat_token_if(context, TokenIdIdentifier);
+        if (ident == 0)
             return (AstNode*)nullptr;
 
-        AstNode *symbol_node = token_symbol(context, ident);
-        if (doc_token == nullptr)
+        AstNode *symbol_node = token_identifier(context, ident);
+        if (doc_token == 0)
             return symbol_node;
 
         AstNode *field_node = ast_create_node(context, NodeTypeErrorSetField, doc_token);
         field_node->data.err_set_field.field_name = symbol_node;
-        field_node->data.err_set_field.doc_comments = doc_comment_buf;
+        field_node->data.err_set_field.doc_comments = doc_token;
         return field_node;
     });
     expect_token(pc, TokenIdRBrace);
@@ -1871,8 +1791,8 @@ static AstNode *ast_parse_error_set_decl(ParseContext *pc) {
 
 // GroupedExpr <- LPAREN Expr RPAREN
 static AstNode *ast_parse_grouped_expr(ParseContext *pc) {
-    Token *lparen = eat_token_if(pc, TokenIdLParen);
-    if (lparen == nullptr)
+    TokenIndex lparen = eat_token_if(pc, TokenIdLParen);
+    if (lparen == 0)
         return nullptr;
 
     AstNode *expr = ast_expect(pc, ast_parse_expr);
@@ -1892,12 +1812,12 @@ static AstNode *ast_parse_if_type_expr(ParseContext *pc) {
 //     <- BlockLabel Block
 //      / BlockLabel? LoopTypeExpr
 static AstNode *ast_parse_labeled_type_expr(ParseContext *pc) {
-    Token *label = ast_parse_block_label(pc);
-    if (label != nullptr) {
+    TokenIndex label = ast_parse_block_label(pc);
+    if (label != 0) {
         AstNode *block = ast_parse_block(pc);
         if (block != nullptr) {
             assert(block->type == NodeTypeBlock);
-            block->data.block.name = token_buf(label);
+            block->data.block.name = token_buf(pc, label);
             return block;
         }
     }
@@ -1906,10 +1826,10 @@ static AstNode *ast_parse_labeled_type_expr(ParseContext *pc) {
     if (loop != nullptr) {
         switch (loop->type) {
             case NodeTypeForExpr:
-                loop->data.for_expr.name = token_buf(label);
+                loop->data.for_expr.name = token_buf(pc, label);
                 break;
             case NodeTypeWhileExpr:
-                loop->data.while_expr.name = token_buf(label);
+                loop->data.while_expr.name = token_buf(pc, label);
                 break;
             default:
                 zig_unreachable();
@@ -1917,7 +1837,7 @@ static AstNode *ast_parse_labeled_type_expr(ParseContext *pc) {
         return loop;
     }
 
-    if (label != nullptr) {
+    if (label != 0) {
         put_back_token(pc);
         put_back_token(pc);
     }
@@ -1945,8 +1865,8 @@ static AstNode *ast_parse_while_type_expr(ParseContext *pc) {
 
 // SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
 static AstNode *ast_parse_switch_expr(ParseContext *pc) {
-    Token *switch_token = eat_token_if(pc, TokenIdKeywordSwitch);
-    if (switch_token == nullptr)
+    TokenIndex switch_token = eat_token_if(pc, TokenIdKeywordSwitch);
+    if (switch_token == 0)
         return nullptr;
 
     expect_token(pc, TokenIdLParen);
@@ -1964,11 +1884,11 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc) {
 
 // AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN
 static AstNode *ast_parse_asm_expr(ParseContext *pc) {
-    Token *asm_token = eat_token_if(pc, TokenIdKeywordAsm);
-    if (asm_token == nullptr)
+    TokenIndex asm_token = eat_token_if(pc, TokenIdKeywordAsm);
+    if (asm_token == 0)
         return nullptr;
 
-    Token *volatile_token = eat_token_if(pc, TokenIdKeywordVolatile);
+    TokenIndex volatile_token = eat_token_if(pc, TokenIdKeywordVolatile);
     expect_token(pc, TokenIdLParen);
     AstNode *asm_template = ast_expect(pc, ast_parse_expr);
     AstNode *res = ast_parse_asm_output(pc);
@@ -1976,25 +1896,21 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc) {
         res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr);
     expect_token(pc, TokenIdRParen);
 
-    res->line = asm_token->start_line;
-    res->column = asm_token->start_column;
+    res->main_token = asm_token;
     res->data.asm_expr.volatile_token = volatile_token;
     res->data.asm_expr.asm_template = asm_template;
     return res;
 }
 
 static AstNode *ast_parse_anon_lit(ParseContext *pc) {
-    Token *period = eat_token_if(pc, TokenIdDot);
-    if (period == nullptr)
+    TokenIndex period = eat_token_if(pc, TokenIdDot);
+    if (period == 0)
         return nullptr;
 
     // anon enum literal
-    Token *identifier = eat_token_if(pc, TokenIdSymbol);
-    if (identifier != nullptr) {
-        AstNode *res = ast_create_node(pc, NodeTypeEnumLiteral, period);
-        res->data.enum_literal.period = period;
-        res->data.enum_literal.identifier = identifier;
-        return res;
+    TokenIndex identifier = eat_token_if(pc, TokenIdIdentifier);
+    if (identifier != 0) {
+        return ast_create_node(pc, NodeTypeEnumLiteral, period);
     }
 
     // anon container literal
@@ -2007,7 +1923,7 @@ static AstNode *ast_parse_anon_lit(ParseContext *pc) {
 
 // AsmOutput <- COLON AsmOutputList AsmInput?
 static AstNode *ast_parse_asm_output(ParseContext *pc) {
-    if (eat_token_if(pc, TokenIdColon) == nullptr)
+    if (eat_token_if(pc, TokenIdColon) == 0)
         return nullptr;
 
     ZigList<AsmOutput *> output_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_output_item);
@@ -2021,20 +1937,20 @@ static AstNode *ast_parse_asm_output(ParseContext *pc) {
 
 // AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
 static AsmOutput *ast_parse_asm_output_item(ParseContext *pc) {
-    if (eat_token_if(pc, TokenIdLBracket) == nullptr)
+    if (eat_token_if(pc, TokenIdLBracket) == 0)
         return nullptr;
 
-    Token *sym_name = expect_token(pc, TokenIdSymbol);
+    TokenIndex sym_name = expect_token(pc, TokenIdIdentifier);
     expect_token(pc, TokenIdRBracket);
 
-    Token *str = eat_token_if(pc, TokenIdMultilineStringLiteral);
-    if (str == nullptr)
+    TokenIndex str = ast_parse_multi_tok(pc, TokenIdMultilineStringLiteralLine);
+    if (str == 0)
         str = expect_token(pc, TokenIdStringLiteral);
     expect_token(pc, TokenIdLParen);
 
-    Token *var_name = eat_token_if(pc, TokenIdSymbol);
+    TokenIndex var_name = eat_token_if(pc, TokenIdIdentifier);
     AstNode *return_type = nullptr;
-    if (var_name == nullptr) {
+    if (var_name == 0) {
         expect_token(pc, TokenIdArrow);
         return_type = ast_expect(pc, ast_parse_type_expr);
     }
@@ -2042,16 +1958,16 @@ static AsmOutput *ast_parse_asm_output_item(ParseContext *pc) {
     expect_token(pc, TokenIdRParen);
 
     AsmOutput *res = heap::c_allocator.create<AsmOutput>();
-    res->asm_symbolic_name = token_buf(sym_name);
-    res->constraint = token_buf(str);
-    res->variable_name = token_buf(var_name);
+    res->asm_symbolic_name = token_buf(pc, sym_name);
+    res->constraint = token_buf(pc, str);
+    res->variable_name = token_buf(pc, var_name);
     res->return_type = return_type;
     return res;
 }
 
 // AsmInput <- COLON AsmInputList AsmClobbers?
 static AstNode *ast_parse_asm_input(ParseContext *pc) {
-    if (eat_token_if(pc, TokenIdColon) == nullptr)
+    if (eat_token_if(pc, TokenIdColon) == 0)
         return nullptr;
 
     ZigList<AsmInput *> input_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_input_item);
@@ -2065,37 +1981,35 @@ static AstNode *ast_parse_asm_input(ParseContext *pc) {
 
 // AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
 static AsmInput *ast_parse_asm_input_item(ParseContext *pc) {
-    if (eat_token_if(pc, TokenIdLBracket) == nullptr)
+    if (eat_token_if(pc, TokenIdLBracket) == 0)
         return nullptr;
 
-    Token *sym_name = expect_token(pc, TokenIdSymbol);
+    TokenIndex sym_name = expect_token(pc, TokenIdIdentifier);
     expect_token(pc, TokenIdRBracket);
 
-    Token *constraint = eat_token_if(pc, TokenIdMultilineStringLiteral);
-    if (constraint == nullptr)
-        constraint = expect_token(pc, TokenIdStringLiteral);
+    TokenIndex constraint = expect_token(pc, TokenIdStringLiteral);
     expect_token(pc, TokenIdLParen);
     AstNode *expr = ast_expect(pc, ast_parse_expr);
     expect_token(pc, TokenIdRParen);
 
     AsmInput *res = heap::c_allocator.create<AsmInput>();
-    res->asm_symbolic_name = token_buf(sym_name);
-    res->constraint = token_buf(constraint);
+    res->asm_symbolic_name = token_buf(pc, sym_name);
+    res->constraint = token_buf(pc, constraint);
     res->expr = expr;
     return res;
 }
 
 // AsmClobbers <- COLON StringList
 static AstNode *ast_parse_asm_clobbers(ParseContext *pc) {
-    if (eat_token_if(pc, TokenIdColon) == nullptr)
+    if (eat_token_if(pc, TokenIdColon) == 0)
         return nullptr;
 
     ZigList<Buf *> clobber_list = ast_parse_list<Buf>(pc, TokenIdComma, [](ParseContext *context) {
-        Token *str = eat_token_if(context, TokenIdStringLiteral);
-        if (str == nullptr)
-            str = eat_token_if(context, TokenIdMultilineStringLiteral);
-        if (str != nullptr)
-            return token_buf(str);
+        TokenIndex str = eat_token_if(context, TokenIdStringLiteral);
+        if (str == 0)
+            str = ast_parse_multi_tok(context, TokenIdMultilineStringLiteralLine);
+        if (str != 0)
+            return token_buf(context, str);
         return (Buf*)nullptr;
     });
 
@@ -2105,24 +2019,24 @@ static AstNode *ast_parse_asm_clobbers(ParseContext *pc) {
 }
 
 // BreakLabel <- COLON IDENTIFIER
-static Token *ast_parse_break_label(ParseContext *pc) {
-    if (eat_token_if(pc, TokenIdColon) == nullptr)
-        return nullptr;
+static TokenIndex ast_parse_break_label(ParseContext *pc) {
+    if (eat_token_if(pc, TokenIdColon) == 0)
+        return 0;
 
-    return expect_token(pc, TokenIdSymbol);
+    return expect_token(pc, TokenIdIdentifier);
 }
 
 // BlockLabel <- IDENTIFIER COLON
-static Token *ast_parse_block_label(ParseContext *pc) {
-    Token *ident = eat_token_if(pc, TokenIdSymbol);
-    if (ident == nullptr)
-        return nullptr;
+static TokenIndex ast_parse_block_label(ParseContext *pc) {
+    TokenIndex ident = eat_token_if(pc, TokenIdIdentifier);
+    if (ident == 0)
+        return 0;
 
     // We do 2 token lookahead here, as we don't want to error when
     // parsing identifiers.
-    if (eat_token_if(pc, TokenIdColon) == nullptr) {
+    if (eat_token_if(pc, TokenIdColon) == 0) {
         put_back_token(pc);
-        return nullptr;
+        return 0;
     }
 
     return ident;
@@ -2130,17 +2044,17 @@ static Token *ast_parse_block_label(ParseContext *pc) {
 
 // FieldInit <- DOT IDENTIFIER EQUAL Expr
 static AstNode *ast_parse_field_init(ParseContext *pc) {
-    Token *first = eat_token_if(pc, TokenIdDot);
-    if (first == nullptr)
+    TokenIndex first = eat_token_if(pc, TokenIdDot);
+    if (first == 0)
         return nullptr;
 
-    Token *name = eat_token_if(pc, TokenIdSymbol);
-    if (name == nullptr) {
+    TokenIndex name = eat_token_if(pc, TokenIdIdentifier);
+    if (name == 0) {
         // Because of anon literals ".{" is also valid.
         put_back_token(pc);
         return nullptr;
     }
-    if (eat_token_if(pc, TokenIdEq) == nullptr) {
+    if (eat_token_if(pc, TokenIdEq) == 0) {
         // Because ".Name" can also be intepreted as an enum literal, we should put back
         // those two tokens again so that the parser can try to parse them as the enum
         // literal later.
@@ -2151,15 +2065,15 @@ static AstNode *ast_parse_field_init(ParseContext *pc) {
     AstNode *expr = ast_expect(pc, ast_parse_expr);
 
     AstNode *res = ast_create_node(pc, NodeTypeStructValueField, first);
-    res->data.struct_val_field.name = token_buf(name);
+    res->data.struct_val_field.name = token_buf(pc, name);
     res->data.struct_val_field.expr = expr;
     return res;
 }
 
 // WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
 static AstNode *ast_parse_while_continue_expr(ParseContext *pc) {
-    Token *first = eat_token_if(pc, TokenIdColon);
-    if (first == nullptr)
+    TokenIndex first = eat_token_if(pc, TokenIdColon);
+    if (first == 0)
         return nullptr;
 
     expect_token(pc, TokenIdLParen);
@@ -2170,8 +2084,8 @@ static AstNode *ast_parse_while_continue_expr(ParseContext *pc) {
 
 // LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN
 static AstNode *ast_parse_link_section(ParseContext *pc) {
-    Token *first = eat_token_if(pc, TokenIdKeywordLinkSection);
-    if (first == nullptr)
+    TokenIndex first = eat_token_if(pc, TokenIdKeywordLinkSection);
+    if (first == 0)
         return nullptr;
 
     expect_token(pc, TokenIdLParen);
@@ -2182,8 +2096,8 @@ static AstNode *ast_parse_link_section(ParseContext *pc) {
 
 // CallConv <- KEYWORD_callconv LPAREN Expr RPAREN
 static AstNode *ast_parse_callconv(ParseContext *pc) {
-    Token *first = eat_token_if(pc, TokenIdKeywordCallconv);
-    if (first == nullptr)
+    TokenIndex first = eat_token_if(pc, TokenIdKeywordCallconv);
+    if (first == 0)
         return nullptr;
 
     expect_token(pc, TokenIdLParen);
@@ -2194,28 +2108,27 @@ static AstNode *ast_parse_callconv(ParseContext *pc) {
 
 // ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
 static AstNode *ast_parse_param_decl(ParseContext *pc) {
-    Buf doc_comments = BUF_INIT;
-    ast_parse_doc_comments(pc, &doc_comments);
+    TokenIndex first_doc_comment = ast_parse_doc_comments(pc);
 
-    Token *first = eat_token_if(pc, TokenIdKeywordNoAlias);
-    if (first == nullptr)
+    TokenIndex first = eat_token_if(pc, TokenIdKeywordNoAlias);
+    if (first == 0)
         first = eat_token_if(pc, TokenIdKeywordCompTime);
 
-    Token *name = eat_token_if(pc, TokenIdSymbol);
-    if (name != nullptr) {
-        if (eat_token_if(pc, TokenIdColon) != nullptr) {
-            if (first == nullptr)
+    TokenIndex name = eat_token_if(pc, TokenIdIdentifier);
+    if (name != 0) {
+        if (eat_token_if(pc, TokenIdColon) != 0) {
+            if (first == 0)
                 first = name;
         } else {
             // We put back the ident, so it can be parsed as a ParamType
             // later.
             put_back_token(pc);
-            name = nullptr;
+            name = 0;
         }
     }
 
     AstNode *res;
-    if (first == nullptr) {
+    if (first == 0) {
         first = peek_token(pc);
         res = ast_parse_param_type(pc);
     } else {
@@ -2226,12 +2139,11 @@ static AstNode *ast_parse_param_decl(ParseContext *pc) {
         return nullptr;
 
     assert(res->type == NodeTypeParamDecl);
-    res->line = first->start_line;
-    res->column = first->start_column;
-    res->data.param_decl.name = token_buf(name);
-    res->data.param_decl.doc_comments = doc_comments;
-    res->data.param_decl.is_noalias = first->id == TokenIdKeywordNoAlias;
-    res->data.param_decl.is_comptime = first->id == TokenIdKeywordCompTime;
+    res->main_token = first;
+    res->data.param_decl.name = token_buf(pc, name);
+    res->data.param_decl.doc_comments = first_doc_comment;
+    res->data.param_decl.is_noalias = pc->token_ids[first] == TokenIdKeywordNoAlias;
+    res->data.param_decl.is_comptime = pc->token_ids[first] == TokenIdKeywordCompTime;
     return res;
 }
 
@@ -2240,15 +2152,15 @@ static AstNode *ast_parse_param_decl(ParseContext *pc) {
 //      / DOT3
 //      / TypeExpr
 static AstNode *ast_parse_param_type(ParseContext *pc) {
-    Token *anytype_token = eat_token_if(pc, TokenIdKeywordAnyType);
-    if (anytype_token != nullptr) {
+    TokenIndex anytype_token = eat_token_if(pc, TokenIdKeywordAnyType);
+    if (anytype_token != 0) {
         AstNode *res = ast_create_node(pc, NodeTypeParamDecl, anytype_token);
         res->data.param_decl.anytype_token = anytype_token;
         return res;
     }
 
-    Token *dots = eat_token_if(pc, TokenIdEllipsis3);
-    if (dots != nullptr) {
+    TokenIndex dots = eat_token_if(pc, TokenIdEllipsis3);
+    if (dots != 0) {
         AstNode *res = ast_create_node(pc, NodeTypeParamDecl, dots);
         res->data.param_decl.is_var_args = true;
         return res;
@@ -2266,8 +2178,8 @@ static AstNode *ast_parse_param_type(ParseContext *pc) {
 
 // IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?
 static AstNode *ast_parse_if_prefix(ParseContext *pc) {
-    Token *first = eat_token_if(pc, TokenIdKeywordIf);
-    if (first == nullptr)
+    TokenIndex first = eat_token_if(pc, TokenIdKeywordIf);
+    if (first == 0)
         return nullptr;
 
     expect_token(pc, TokenIdLParen);
@@ -2279,16 +2191,16 @@ static AstNode *ast_parse_if_prefix(ParseContext *pc) {
     AstNode *res = ast_create_node(pc, NodeTypeIfOptional, first);
     res->data.test_expr.target_node = condition;
     if (opt_payload.unwrap(&payload)) {
-        res->data.test_expr.var_symbol = token_buf(payload.payload);
-        res->data.test_expr.var_is_ptr = payload.asterisk != nullptr;
+        res->data.test_expr.var_symbol = token_buf(pc, payload.payload);
+        res->data.test_expr.var_is_ptr = payload.asterisk != 0;
     }
     return res;
 }
 
 // WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
 static AstNode *ast_parse_while_prefix(ParseContext *pc) {
-    Token *while_token = eat_token_if(pc, TokenIdKeywordWhile);
-    if (while_token == nullptr)
+    TokenIndex while_token = eat_token_if(pc, TokenIdKeywordWhile);
+    if (while_token == 0)
         return nullptr;
 
     expect_token(pc, TokenIdLParen);
@@ -2302,8 +2214,8 @@ static AstNode *ast_parse_while_prefix(ParseContext *pc) {
     res->data.while_expr.condition = condition;
     res->data.while_expr.continue_expr = continue_expr;
     if (opt_payload.unwrap(&payload)) {
-        res->data.while_expr.var_symbol = token_buf(payload.payload);
-        res->data.while_expr.var_is_ptr = payload.asterisk != nullptr;
+        res->data.while_expr.var_symbol = token_buf(pc, payload.payload);
+        res->data.while_expr.var_is_ptr = payload.asterisk != 0;
     }
 
     return res;
@@ -2311,8 +2223,8 @@ static AstNode *ast_parse_while_prefix(ParseContext *pc) {
 
 // ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
 static AstNode *ast_parse_for_prefix(ParseContext *pc) {
-    Token *for_token = eat_token_if(pc, TokenIdKeywordFor);
-    if (for_token == nullptr)
+    TokenIndex for_token = eat_token_if(pc, TokenIdKeywordFor);
+    if (for_token == 0)
         return nullptr;
 
     expect_token(pc, TokenIdLParen);
@@ -2324,31 +2236,31 @@ static AstNode *ast_parse_for_prefix(ParseContext *pc) {
 
     AstNode *res = ast_create_node(pc, NodeTypeForExpr, for_token);
     res->data.for_expr.array_expr = array_expr;
-    res->data.for_expr.elem_node = token_symbol(pc, payload.payload);
-    res->data.for_expr.elem_is_ptr = payload.asterisk != nullptr;
-    if (payload.index != nullptr)
-        res->data.for_expr.index_node = token_symbol(pc, payload.index);
+    res->data.for_expr.elem_node = token_identifier(pc, payload.payload);
+    res->data.for_expr.elem_is_ptr = payload.asterisk != 0;
+    if (payload.index != 0)
+        res->data.for_expr.index_node = token_identifier(pc, payload.index);
 
     return res;
 }
 
 // Payload <- PIPE IDENTIFIER PIPE
-static Token *ast_parse_payload(ParseContext *pc) {
-    if (eat_token_if(pc, TokenIdBinOr) == nullptr)
-        return nullptr;
+static TokenIndex ast_parse_payload(ParseContext *pc) {
+    if (eat_token_if(pc, TokenIdBinOr) == 0)
+        return 0;
 
-    Token *res = expect_token(pc, TokenIdSymbol);
+    TokenIndex res = expect_token(pc, TokenIdIdentifier);
     expect_token(pc, TokenIdBinOr);
     return res;
 }
 
 // PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE
 static Optional<PtrPayload> ast_parse_ptr_payload(ParseContext *pc) {
-    if (eat_token_if(pc, TokenIdBinOr) == nullptr)
+    if (eat_token_if(pc, TokenIdBinOr) == 0)
         return Optional<PtrPayload>::none();
 
-    Token *asterisk = eat_token_if(pc, TokenIdStar);
-    Token *payload = expect_token(pc, TokenIdSymbol);
+    TokenIndex asterisk = eat_token_if(pc, TokenIdStar);
+    TokenIndex payload = expect_token(pc, TokenIdIdentifier);
     expect_token(pc, TokenIdBinOr);
 
     PtrPayload res;
@@ -2359,14 +2271,14 @@ static Optional<PtrPayload> ast_parse_ptr_payload(ParseContext *pc) {
 
 // PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE
 static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc) {
-    if (eat_token_if(pc, TokenIdBinOr) == nullptr)
+    if (eat_token_if(pc, TokenIdBinOr) == 0)
         return Optional<PtrIndexPayload>::none();
 
-    Token *asterisk = eat_token_if(pc, TokenIdStar);
-    Token *payload = expect_token(pc, TokenIdSymbol);
-    Token *index = nullptr;
-    if (eat_token_if(pc, TokenIdComma) != nullptr)
-        index = expect_token(pc, TokenIdSymbol);
+    TokenIndex asterisk = eat_token_if(pc, TokenIdStar);
+    TokenIndex payload = expect_token(pc, TokenIdIdentifier);
+    TokenIndex index = 0;
+    if (eat_token_if(pc, TokenIdComma) != 0)
+        index = expect_token(pc, TokenIdIdentifier);
     expect_token(pc, TokenIdBinOr);
 
     PtrIndexPayload res;
@@ -2390,8 +2302,8 @@ static AstNode *ast_parse_switch_prong(ParseContext *pc) {
     assert(res->type == NodeTypeSwitchProng);
     res->data.switch_prong.expr = expr;
     if (opt_payload.unwrap(&payload)) {
-        res->data.switch_prong.var_symbol = token_symbol(pc, payload.payload);
-        res->data.switch_prong.var_is_ptr = payload.asterisk != nullptr;
+        res->data.switch_prong.var_symbol = token_identifier(pc, payload.payload);
+        res->data.switch_prong.var_is_ptr = payload.asterisk != 0;
     }
 
     return res;
@@ -2407,7 +2319,7 @@ static AstNode *ast_parse_switch_case(ParseContext *pc) {
         res->data.switch_prong.items.append(first);
         res->data.switch_prong.any_items_are_range = first->type == NodeTypeSwitchRange;
 
-        while (eat_token_if(pc, TokenIdComma) != nullptr) {
+        while (eat_token_if(pc, TokenIdComma) != 0) {
             AstNode *item = ast_parse_switch_item(pc);
             if (item == nullptr)
                 break;
@@ -2419,8 +2331,8 @@ static AstNode *ast_parse_switch_case(ParseContext *pc) {
         return res;
     }
 
-    Token *else_token = eat_token_if(pc, TokenIdKeywordElse);
-    if (else_token != nullptr)
+    TokenIndex else_token = eat_token_if(pc, TokenIdKeywordElse);
+    if (else_token != 0)
         return ast_create_node(pc, NodeTypeSwitchProng, else_token);
 
     return nullptr;
@@ -2432,8 +2344,8 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
     if (expr == nullptr)
         return nullptr;
 
-    Token *dots = eat_token_if(pc, TokenIdEllipsis3);
-    if (dots != nullptr) {
+    TokenIndex dots = eat_token_if(pc, TokenIdEllipsis3);
+    if (dots != 0) {
         AstNode *expr2 = ast_expect(pc, ast_parse_expr);
         AstNode *res = ast_create_node(pc, NodeTypeSwitchRange, dots);
         res->data.switch_range.start = expr;
@@ -2478,9 +2390,9 @@ static AstNode *ast_parse_assign_op(ParseContext *pc) {
     table[TokenIdTimesEq] = BinOpTypeAssignTimes;
     table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap;
 
-    BinOpType op = table[peek_token(pc)->id];
+    BinOpType op = table[pc->token_ids[pc->current_token]];
     if (op != BinOpTypeInvalid) {
-        Token *op_token = eat_token(pc);
+        TokenIndex op_token = eat_token(pc);
         AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token);
         res->data.bin_op_expr.bin_op = op;
         return res;
@@ -2506,9 +2418,9 @@ static AstNode *ast_parse_compare_op(ParseContext *pc) {
     table[TokenIdCmpLessOrEq] = BinOpTypeCmpLessOrEq;
     table[TokenIdCmpGreaterOrEq] = BinOpTypeCmpGreaterOrEq;
 
-    BinOpType op = table[peek_token(pc)->id];
+    BinOpType op = table[pc->token_ids[pc->current_token]];
     if (op != BinOpTypeInvalid) {
-        Token *op_token = eat_token(pc);
+        TokenIndex op_token = eat_token(pc);
         AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token);
         res->data.bin_op_expr.bin_op = op;
         return res;
@@ -2530,20 +2442,20 @@ static AstNode *ast_parse_bitwise_op(ParseContext *pc) {
     table[TokenIdBinOr] = BinOpTypeBinOr;
     table[TokenIdKeywordOrElse] = BinOpTypeUnwrapOptional;
 
-    BinOpType op = table[peek_token(pc)->id];
+    BinOpType op = table[pc->token_ids[pc->current_token]];
     if (op != BinOpTypeInvalid) {
-        Token *op_token = eat_token(pc);
+        TokenIndex op_token = eat_token(pc);
         AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token);
         res->data.bin_op_expr.bin_op = op;
         return res;
     }
 
-    Token *catch_token = eat_token_if(pc, TokenIdKeywordCatch);
-    if (catch_token != nullptr) {
-        Token *payload = ast_parse_payload(pc);
+    TokenIndex catch_token = eat_token_if(pc, TokenIdKeywordCatch);
+    if (catch_token != 0) {
+        TokenIndex payload = ast_parse_payload(pc);
         AstNode *res = ast_create_node(pc, NodeTypeCatchExpr, catch_token);
-        if (payload != nullptr)
-            res->data.unwrap_err_expr.symbol = token_symbol(pc, payload);
+        if (payload != 0)
+            res->data.unwrap_err_expr.symbol = token_identifier(pc, payload);
 
         return res;
     }
@@ -2559,9 +2471,9 @@ static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
     table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft;
     table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight;
 
-    BinOpType op = table[peek_token(pc)->id];
+    BinOpType op = table[pc->token_ids[pc->current_token]];
     if (op != BinOpTypeInvalid) {
-        Token *op_token = eat_token(pc);
+        TokenIndex op_token = eat_token(pc);
         AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token);
         res->data.bin_op_expr.bin_op = op;
         return res;
@@ -2584,9 +2496,9 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
     table[TokenIdPlusPercent] = BinOpTypeAddWrap;
     table[TokenIdMinusPercent] = BinOpTypeSubWrap;
 
-    BinOpType op = table[peek_token(pc)->id];
+    BinOpType op = table[pc->token_ids[pc->current_token]];
     if (op != BinOpTypeInvalid) {
-        Token *op_token = eat_token(pc);
+        TokenIndex op_token = eat_token(pc);
         AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token);
         res->data.bin_op_expr.bin_op = op;
         return res;
@@ -2611,9 +2523,9 @@ static AstNode *ast_parse_multiply_op(ParseContext *pc) {
     table[TokenIdStarStar] = BinOpTypeArrayMult;
     table[TokenIdTimesPercent] = BinOpTypeMultWrap;
 
-    BinOpType op = table[peek_token(pc)->id];
+    BinOpType op = table[pc->token_ids[pc->current_token]];
     if (op != BinOpTypeInvalid) {
-        Token *op_token = eat_token(pc);
+        TokenIndex op_token = eat_token(pc);
         AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token);
         res->data.bin_op_expr.bin_op = op;
         return res;
@@ -2638,23 +2550,23 @@ static AstNode *ast_parse_prefix_op(ParseContext *pc) {
     table[TokenIdMinusPercent] = PrefixOpNegationWrap;
     table[TokenIdAmpersand] = PrefixOpAddrOf;
 
-    PrefixOp op = table[peek_token(pc)->id];
+    PrefixOp op = table[pc->token_ids[pc->current_token]];
     if (op != PrefixOpInvalid) {
-        Token *op_token = eat_token(pc);
+        TokenIndex op_token = eat_token(pc);
         AstNode *res = ast_create_node(pc, NodeTypePrefixOpExpr, op_token);
         res->data.prefix_op_expr.prefix_op = op;
         return res;
     }
 
-    Token *try_token = eat_token_if(pc, TokenIdKeywordTry);
-    if (try_token != nullptr) {
+    TokenIndex try_token = eat_token_if(pc, TokenIdKeywordTry);
+    if (try_token != 0) {
         AstNode *res = ast_create_node(pc, NodeTypeReturnExpr, try_token);
         res->data.return_expr.kind = ReturnKindError;
         return res;
     }
 
-    Token *await = eat_token_if(pc, TokenIdKeywordAwait);
-    if (await != nullptr) {
+    TokenIndex await = eat_token_if(pc, TokenIdKeywordAwait);
+    if (await != 0) {
         AstNode *res = ast_create_node(pc, NodeTypeAwaitExpr, await);
         return res;
     }
@@ -2668,16 +2580,16 @@ static AstNode *ast_parse_prefix_op(ParseContext *pc) {
 //      / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)*
 //      / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)*
 static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
-    Token *questionmark = eat_token_if(pc, TokenIdQuestion);
-    if (questionmark != nullptr) {
+    TokenIndex questionmark = eat_token_if(pc, TokenIdQuestion);
+    if (questionmark != 0) {
         AstNode *res = ast_create_node(pc, NodeTypePrefixOpExpr, questionmark);
         res->data.prefix_op_expr.prefix_op = PrefixOpOptional;
         return res;
     }
 
-    Token *anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame);
-    if (anyframe != nullptr) {
-        if (eat_token_if(pc, TokenIdArrow) != nullptr) {
+    TokenIndex anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame);
+    if (anyframe != 0) {
+        if (eat_token_if(pc, TokenIdArrow) != 0) {
             AstNode *res = ast_create_node(pc, NodeTypeAnyFrameType, anyframe);
             return res;
         }
@@ -2685,18 +2597,18 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
         put_back_token(pc);
     }
 
-    Token *arr_init_lbracket = eat_token_if(pc, TokenIdLBracket);
-    if (arr_init_lbracket != nullptr) {
-        Token *underscore = eat_token_if(pc, TokenIdSymbol);
-        if (underscore == nullptr) {
+    TokenIndex arr_init_lbracket = eat_token_if(pc, TokenIdLBracket);
+    if (arr_init_lbracket != 0) {
+        TokenIndex underscore = eat_token_if(pc, TokenIdIdentifier);
+        if (underscore == 0) {
             put_back_token(pc);
-        } else if (!buf_eql_str(token_buf(underscore), "_")) {
+        } else if (!buf_eql_str(token_buf(pc, underscore), "_")) {
             put_back_token(pc);
             put_back_token(pc);
         } else {
             AstNode *sentinel = nullptr;
-            Token *colon = eat_token_if(pc, TokenIdColon);
-            if (colon != nullptr) {
+            TokenIndex colon = eat_token_if(pc, TokenIdColon);
+            if (colon != 0) {
                 sentinel = ast_expect(pc, ast_parse_expr);
             }
             expect_token(pc, TokenIdRBracket);
@@ -2715,33 +2627,33 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
         if (child == nullptr)
             child = ptr;
         while (true) {
-            Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);
-            if (allowzero_token != nullptr) {
+            TokenIndex allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);
+            if (allowzero_token != 0) {
                 child->data.pointer_type.allow_zero_token = allowzero_token;
                 continue;
             }
 
-            if (eat_token_if(pc, TokenIdKeywordAlign) != nullptr) {
+            if (eat_token_if(pc, TokenIdKeywordAlign) != 0) {
                 expect_token(pc, TokenIdLParen);
                 AstNode *align_expr = ast_expect(pc, ast_parse_expr);
                 child->data.pointer_type.align_expr = align_expr;
-                if (eat_token_if(pc, TokenIdColon) != nullptr) {
-                    Token *bit_offset_start = expect_token(pc, TokenIdIntLiteral);
+                if (eat_token_if(pc, TokenIdColon) != 0) {
+                    TokenIndex bit_offset_start = expect_token(pc, TokenIdIntLiteral);
                     expect_token(pc, TokenIdColon);
-                    Token *host_int_bytes = expect_token(pc, TokenIdIntLiteral);
-                    child->data.pointer_type.bit_offset_start = token_bigint(bit_offset_start);
-                    child->data.pointer_type.host_int_bytes = token_bigint(host_int_bytes);
+                    TokenIndex host_int_bytes = expect_token(pc, TokenIdIntLiteral);
+                    child->data.pointer_type.bit_offset_start = bit_offset_start;
+                    child->data.pointer_type.host_int_bytes = host_int_bytes;
                 }
                 expect_token(pc, TokenIdRParen);
                 continue;
             }
 
-            if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) {
+            if (eat_token_if(pc, TokenIdKeywordConst) != 0) {
                 child->data.pointer_type.is_const = true;
                 continue;
             }
 
-            if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) {
+            if (eat_token_if(pc, TokenIdKeywordVolatile) != 0) {
                 child->data.pointer_type.is_volatile = true;
                 continue;
             }
@@ -2756,8 +2668,8 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
     if (array != nullptr) {
         assert(array->type == NodeTypeArrayType);
         while (true) {
-            Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);
-            if (allowzero_token != nullptr) {
+            TokenIndex allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);
+            if (allowzero_token != 0) {
                 array->data.array_type.allow_zero_token = allowzero_token;
                 continue;
             }
@@ -2768,12 +2680,12 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
                 continue;
             }
 
-            if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) {
+            if (eat_token_if(pc, TokenIdKeywordConst) != 0) {
                 array->data.array_type.is_const = true;
                 continue;
             }
 
-            if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) {
+            if (eat_token_if(pc, TokenIdKeywordVolatile) != 0) {
                 array->data.array_type.is_volatile = true;
                 continue;
             }
@@ -2793,14 +2705,14 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
 //      / DOTASTERISK
 //      / DOTQUESTIONMARK
 static AstNode *ast_parse_suffix_op(ParseContext *pc) {
-    Token *lbracket = eat_token_if(pc, TokenIdLBracket);
-    if (lbracket != nullptr) {
+    TokenIndex lbracket = eat_token_if(pc, TokenIdLBracket);
+    if (lbracket != 0) {
         AstNode *start = ast_expect(pc, ast_parse_expr);
         AstNode *end = nullptr;
-        if (eat_token_if(pc, TokenIdEllipsis2) != nullptr) {
+        if (eat_token_if(pc, TokenIdEllipsis2) != 0) {
             AstNode *sentinel = nullptr;
             end = ast_parse_expr(pc);
-            if (eat_token_if(pc, TokenIdColon) != nullptr) {
+            if (eat_token_if(pc, TokenIdColon) != 0) {
                 sentinel = ast_parse_expr(pc);
             }
             expect_token(pc, TokenIdRBracket);
@@ -2819,18 +2731,18 @@ static AstNode *ast_parse_suffix_op(ParseContext *pc) {
         return res;
     }
 
-    Token *dot_asterisk = eat_token_if(pc, TokenIdDotStar);
-    if (dot_asterisk != nullptr)
+    TokenIndex dot_asterisk = eat_token_if(pc, TokenIdDotStar);
+    if (dot_asterisk != 0)
         return ast_create_node(pc, NodeTypePtrDeref, dot_asterisk);
 
-    Token *dot = eat_token_if(pc, TokenIdDot);
-    if (dot != nullptr) {
-        if (eat_token_if(pc, TokenIdQuestion) != nullptr)
+    TokenIndex dot = eat_token_if(pc, TokenIdDot);
+    if (dot != 0) {
+        if (eat_token_if(pc, TokenIdQuestion) != 0)
             return ast_create_node(pc, NodeTypeUnwrapOptional, dot);
 
-        Token *ident = expect_token(pc, TokenIdSymbol);
+        TokenIndex ident = expect_token(pc, TokenIdIdentifier);
         AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot);
-        res->data.field_access_expr.field_name = token_buf(ident);
+        res->data.field_access_expr.field_name = token_buf(pc, ident);
         return res;
     }
 
@@ -2839,8 +2751,8 @@ static AstNode *ast_parse_suffix_op(ParseContext *pc) {
 
 // FnCallArguments <- LPAREN ExprList RPAREN
 static AstNode *ast_parse_fn_call_arguments(ParseContext *pc) {
-    Token *paren = eat_token_if(pc, TokenIdLParen);
-    if (paren == nullptr)
+    TokenIndex paren = eat_token_if(pc, TokenIdLParen);
+    if (paren == 0)
         return nullptr;
 
     ZigList<AstNode *> params = ast_parse_list(pc, TokenIdComma, ast_parse_expr);
@@ -2854,14 +2766,14 @@ static AstNode *ast_parse_fn_call_arguments(ParseContext *pc) {
 
 // ArrayTypeStart <- LBRACKET Expr? RBRACKET
 static AstNode *ast_parse_array_type_start(ParseContext *pc) {
-    Token *lbracket = eat_token_if(pc, TokenIdLBracket);
-    if (lbracket == nullptr)
+    TokenIndex lbracket = eat_token_if(pc, TokenIdLBracket);
+    if (lbracket == 0)
         return nullptr;
 
     AstNode *size = ast_parse_expr(pc);
     AstNode *sentinel = nullptr;
-    Token *colon = eat_token_if(pc, TokenIdColon);
-    if (colon != nullptr) {
+    TokenIndex colon = eat_token_if(pc, TokenIdColon);
+    if (colon != 0) {
         sentinel = ast_expect(pc, ast_parse_expr);
     }
     expect_token(pc, TokenIdRBracket);
@@ -2879,10 +2791,10 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) {
 static AstNode *ast_parse_ptr_type_start(ParseContext *pc) {
     AstNode *sentinel = nullptr;
 
-    Token *asterisk = eat_token_if(pc, TokenIdStar);
-    if (asterisk != nullptr) {
-        Token *colon = eat_token_if(pc, TokenIdColon);
-        if (colon != nullptr) {
+    TokenIndex asterisk = eat_token_if(pc, TokenIdStar);
+    if (asterisk != 0) {
+        TokenIndex colon = eat_token_if(pc, TokenIdColon);
+        if (colon != 0) {
             sentinel = ast_expect(pc, ast_parse_expr);
         }
         AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk);
@@ -2891,10 +2803,10 @@ static AstNode *ast_parse_ptr_type_start(ParseContext *pc) {
         return res;
     }
 
-    Token *asterisk2 = eat_token_if(pc, TokenIdStarStar);
-    if (asterisk2 != nullptr) {
-        Token *colon = eat_token_if(pc, TokenIdColon);
-        if (colon != nullptr) {
+    TokenIndex asterisk2 = eat_token_if(pc, TokenIdStarStar);
+    if (asterisk2 != 0) {
+        TokenIndex colon = eat_token_if(pc, TokenIdColon);
+        if (colon != 0) {
             sentinel = ast_expect(pc, ast_parse_expr);
         }
         AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk2);
@@ -2906,15 +2818,15 @@ static AstNode *ast_parse_ptr_type_start(ParseContext *pc) {
         return res;
     }
 
-    Token *lbracket = eat_token_if(pc, TokenIdLBracket);
-    if (lbracket != nullptr) {
-        Token *star = eat_token_if(pc, TokenIdStar);
-        if (star == nullptr) {
+    TokenIndex lbracket = eat_token_if(pc, TokenIdLBracket);
+    if (lbracket != 0) {
+        TokenIndex star = eat_token_if(pc, TokenIdStar);
+        if (star == 0) {
             put_back_token(pc);
         } else {
-            Token *c_tok = eat_token_if(pc, TokenIdSymbol);
-            if (c_tok != nullptr) {
-                if (!buf_eql_str(token_buf(c_tok), "c")) {
+            TokenIndex c_tok = eat_token_if(pc, TokenIdIdentifier);
+            if (c_tok != 0) {
+                if (!buf_eql_str(token_buf(pc, c_tok), "c")) {
                     put_back_token(pc); // c symbol
                 } else {
                     expect_token(pc, TokenIdRBracket);
@@ -2924,8 +2836,8 @@ static AstNode *ast_parse_ptr_type_start(ParseContext *pc) {
                 }
             }
 
-            Token *colon = eat_token_if(pc, TokenIdColon);
-            if (colon != nullptr) {
+            TokenIndex colon = eat_token_if(pc, TokenIdColon);
+            if (colon != 0) {
                 sentinel = ast_expect(pc, ast_parse_expr);
             }
             expect_token(pc, TokenIdRBracket);
@@ -2951,9 +2863,7 @@ static AstNode *ast_parse_container_decl_auto(ParseContext *pc) {
 
     res->data.container_decl.fields = members.fields;
     res->data.container_decl.decls = members.decls;
-    if (buf_len(&members.doc_comments) != 0) {
-        res->data.container_decl.doc_comments = members.doc_comments;
-    }
+    res->data.container_decl.doc_comments = members.doc_comments;
     return res;
 }
 
@@ -2963,8 +2873,8 @@ static AstNode *ast_parse_container_decl_auto(ParseContext *pc) {
 //      / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
 //      / KEYWORD_opaque
 static AstNode *ast_parse_container_decl_type(ParseContext *pc) {
-    Token *first = eat_token_if(pc, TokenIdKeywordStruct);
-    if (first != nullptr) {
+    TokenIndex first = eat_token_if(pc, TokenIdKeywordStruct);
+    if (first != 0) {
         AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first);
         res->data.container_decl.init_arg_expr = nullptr;
         res->data.container_decl.kind = ContainerKindStruct;
@@ -2972,7 +2882,7 @@ static AstNode *ast_parse_container_decl_type(ParseContext *pc) {
     }
 
     first = eat_token_if(pc, TokenIdKeywordOpaque);
-    if (first != nullptr) {
+    if (first != 0) {
         AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first);
         res->data.container_decl.init_arg_expr = nullptr;
         res->data.container_decl.kind = ContainerKindOpaque;
@@ -2980,9 +2890,9 @@ static AstNode *ast_parse_container_decl_type(ParseContext *pc) {
     }
 
     first = eat_token_if(pc, TokenIdKeywordEnum);
-    if (first != nullptr) {
+    if (first != 0) {
         AstNode *init_arg_expr = nullptr;
-        if (eat_token_if(pc, TokenIdLParen) != nullptr) {
+        if (eat_token_if(pc, TokenIdLParen) != 0) {
             init_arg_expr = ast_expect(pc, ast_parse_expr);
             expect_token(pc, TokenIdRParen);
         }
@@ -2993,13 +2903,13 @@ static AstNode *ast_parse_container_decl_type(ParseContext *pc) {
     }
 
     first = eat_token_if(pc, TokenIdKeywordUnion);
-    if (first != nullptr) {
+    if (first != 0) {
         AstNode *init_arg_expr = nullptr;
         bool auto_enum = false;
-        if (eat_token_if(pc, TokenIdLParen) != nullptr) {
-            if (eat_token_if(pc, TokenIdKeywordEnum) != nullptr) {
+        if (eat_token_if(pc, TokenIdLParen) != 0) {
+            if (eat_token_if(pc, TokenIdKeywordEnum) != 0) {
                 auto_enum = true;
-                if (eat_token_if(pc, TokenIdLParen) != nullptr) {
+                if (eat_token_if(pc, TokenIdLParen) != 0) {
                     init_arg_expr = ast_expect(pc, ast_parse_expr);
                     expect_token(pc, TokenIdRParen);
                 }
@@ -3022,7 +2932,7 @@ static AstNode *ast_parse_container_decl_type(ParseContext *pc) {
 
 // ByteAlign <- KEYWORD_align LPAREN Expr RPAREN
 static AstNode *ast_parse_byte_align(ParseContext *pc) {
-    if (eat_token_if(pc, TokenIdKeywordAlign) == nullptr)
+    if (eat_token_if(pc, TokenIdKeywordAlign) == 0)
         return nullptr;
 
     expect_token(pc, TokenIdLParen);
@@ -3103,7 +3013,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
         case NodeTypeCharLiteral:
             // none
             break;
-        case NodeTypeSymbol:
+        case NodeTypeIdentifier:
             // none
             break;
         case NodeTypePrefixOpExpr:
@@ -3264,3 +3174,414 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
             break;
     }
 }
+
+Error source_string_literal_buf(const char *source, Buf *out, size_t *bad_index) {
+    size_t byte_offset = 0;
+
+    assert(source[byte_offset] == '"');
+    byte_offset += 1;
+
+    buf_resize(out, 0);
+
+    uint32_t codepoint;
+
+    enum {
+        StateStart,
+        StateBackslash,
+        StateUnicodeLBrace,
+        StateUnicodeDigit,
+    } state = StateStart;
+    for (;;byte_offset += 1) {
+        switch (state) {
+            case StateStart: switch (source[byte_offset]) {
+                case '\\':
+                    state = StateBackslash;
+                    continue;
+                case '\n':
+                    *bad_index = byte_offset;
+                    return ErrorInvalidCharacter;
+                case '"':
+                    return ErrorNone;
+                default:
+                    buf_append_char(out, source[byte_offset]);
+                    continue;
+            }
+            case StateBackslash: switch (source[byte_offset]) {
+                case 'n':
+                    buf_append_char(out, '\n');
+                    state = StateStart;
+                    continue;
+                case 'r':
+                    buf_append_char(out, '\r');
+                    state = StateStart;
+                    continue;
+                case '\\':
+                    buf_append_char(out, '\\');
+                    state = StateStart;
+                    continue;
+                case 't':
+                    buf_append_char(out, '\t');
+                    state = StateStart;
+                    continue;
+                case '\'':
+                    buf_append_char(out, '\'');
+                    state = StateStart;
+                    continue;
+                case '"':
+                    buf_append_char(out, '"');
+                    state = StateStart;
+                    continue;
+                case 'x': {
+                    byte_offset += 1;
+                    uint8_t digit1;
+                    if (source[byte_offset] >= '0' && source[byte_offset] <= '9') {
+                        digit1 = source[byte_offset] - '0';
+                    } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') {
+                        digit1 = source[byte_offset] - 'a' + 10;
+                    } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') {
+                        digit1 = source[byte_offset] - 'A' + 10;
+                    } else {
+                        *bad_index = byte_offset;
+                        return ErrorInvalidCharacter;
+                    }
+
+                    byte_offset += 1;
+                    uint8_t digit0;
+                    if (source[byte_offset] >= '0' && source[byte_offset] <= '9') {
+                        digit0 = source[byte_offset] - '0';
+                    } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') {
+                        digit0 = source[byte_offset] - 'a' + 10;
+                    } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') {
+                        digit0 = source[byte_offset] - 'A' + 10;
+                    } else {
+                        *bad_index = byte_offset;
+                        return ErrorInvalidCharacter;
+                    }
+
+                    buf_append_char(out, digit1 * 16 + digit0);
+                    state = StateStart;
+                    continue;
+                }
+                case 'u':
+                    state = StateUnicodeLBrace;
+                    continue;
+                default:
+                    *bad_index = byte_offset;
+                    return ErrorInvalidCharacter;
+            }
+            case StateUnicodeLBrace: switch (source[byte_offset]) {
+                case '{':
+                    state = StateUnicodeDigit;
+                    codepoint = 0;
+                    continue;
+                default:
+                    *bad_index = byte_offset;
+                    return ErrorInvalidCharacter;
+            }
+            case StateUnicodeDigit: {
+                uint8_t digit;
+                if (source[byte_offset] >= '0' && source[byte_offset] <= '9') {
+                    digit = source[byte_offset] - '0';
+                } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') {
+                    digit = source[byte_offset] - 'a' + 10;
+                } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') {
+                    digit = source[byte_offset] - 'A' + 10;
+                } else if (source[byte_offset] == '}') {
+                    if (codepoint < 0x80) {
+                        buf_append_char(out, codepoint);
+                    } else if (codepoint < 0x800) {
+                        buf_append_char(out, 0xc0 | (codepoint >> 6));
+                        buf_append_char(out, 0x80 | (codepoint & 0x3f));
+                    } else if (codepoint < 0x10000) {
+                        buf_append_char(out, 0xe0 | (codepoint >> 12));
+                        buf_append_char(out, 0x80 | ((codepoint >> 6) & 0x3f));
+                        buf_append_char(out, 0x80 | (codepoint & 0x3f));
+                    } else if (codepoint < 0x110000) {
+                        buf_append_char(out, 0xf0 | (codepoint >> 18));
+                        buf_append_char(out, 0x80 | ((codepoint >> 12) & 0x3f));
+                        buf_append_char(out, 0x80 | ((codepoint >> 6) & 0x3f));
+                        buf_append_char(out, 0x80 | (codepoint & 0x3f));
+                    } else {
+                        *bad_index = byte_offset;
+                        return ErrorUnicodePointTooLarge;
+                    }
+                    state = StateStart;
+                    continue;
+                } else {
+                    *bad_index = byte_offset;
+                    return ErrorInvalidCharacter;
+                }
+                codepoint = codepoint * 16 + digit;
+                continue;
+            }
+        }
+    }
+    zig_unreachable();
+}
+
+static uint32_t utf8_code_point(const uint8_t *bytes) {
+    if (bytes[0] <= 0x7f) {
+        return bytes[0];
+    } else if (bytes[0] >= 0xc0 && bytes[0] <= 0xdf) {
+        uint32_t result = bytes[0] & 0x1f;
+        result <<= 6;
+        result |= bytes[1] & 0x3f;
+        return result;
+    } else if (bytes[0] >= 0xe0 && bytes[0] <= 0xef) {
+        uint32_t result = bytes[0] & 0xf;
+
+        result <<= 6;
+        result |= bytes[1] & 0x3f;
+
+        result <<= 6;
+        result |= bytes[2] & 0x3f;
+
+        return result;
+    } else if (bytes[0] >= 0xf0 && bytes[0] <= 0xf7) {
+        uint32_t result = bytes[0] & 0x7;
+
+        result <<= 6;
+        result |= bytes[1] & 0x3f;
+
+        result <<= 6;
+        result |= bytes[2] & 0x3f;
+
+        result <<= 6;
+        result |= bytes[3] & 0x3f;
+
+        return result;
+    } else {
+        zig_unreachable();
+    }
+}
+
+Error source_char_literal(const char *source, uint32_t *result, size_t *bad_index) {
+    if (source[0] != '\\') {
+        *result = utf8_code_point((const uint8_t *)source);
+        return ErrorNone;
+    }
+
+    uint32_t byte_offset = 1;
+    uint32_t codepoint;
+
+    enum State {
+        StateBackslash,
+        StateUnicodeLBrace,
+        StateUnicodeDigit,
+    } state = StateBackslash;
+
+    for (;;byte_offset += 1) {
+        switch (state) {
+            case StateBackslash: switch (source[byte_offset]) {
+                case 'n':
+                    *result = '\n';
+                    return ErrorNone;
+                case 'r':
+                    *result = '\r';
+                    return ErrorNone;
+                case '\\':
+                    *result = '\\';
+                    return ErrorNone;
+                case 't':
+                    *result = '\t';
+                    return ErrorNone;
+                case '\'':
+                    *result = '\'';
+                    return ErrorNone;
+                case '"':
+                    *result = '"';
+                    return ErrorNone;
+                case 'x': {
+                    byte_offset += 1;
+                    uint8_t digit1;
+                    if (source[byte_offset] >= '0' && source[byte_offset] <= '9') {
+                        digit1 = source[byte_offset] - '0';
+                    } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') {
+                        digit1 = source[byte_offset] - 'a' + 10;
+                    } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') {
+                        digit1 = source[byte_offset] - 'A' + 10;
+                    } else {
+                        *bad_index = byte_offset;
+                        return ErrorInvalidCharacter;
+                    }
+
+                    byte_offset += 1;
+                    uint8_t digit0;
+                    if (source[byte_offset] >= '0' && source[byte_offset] <= '9') {
+                        digit0 = source[byte_offset] - '0';
+                    } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') {
+                        digit0 = source[byte_offset] - 'a' + 10;
+                    } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') {
+                        digit0 = source[byte_offset] - 'A' + 10;
+                    } else {
+                        *bad_index = byte_offset;
+                        return ErrorInvalidCharacter;
+                    }
+
+                    *result = digit1 * 16 + digit0;
+                    return ErrorNone;
+                }
+                case 'u':
+                    state = StateUnicodeLBrace;
+                    continue;
+                default:
+                    *bad_index = byte_offset;
+                    return ErrorInvalidCharacter;
+            }
+            case StateUnicodeLBrace: switch (source[byte_offset]) {
+                case '{':
+                    state = StateUnicodeDigit;
+                    codepoint = 0;
+                    continue;
+                default:
+                    *bad_index = byte_offset;
+                    return ErrorInvalidCharacter;
+            }
+            case StateUnicodeDigit: {
+                uint8_t digit;
+                if (source[byte_offset] >= '0' && source[byte_offset] <= '9') {
+                    digit = source[byte_offset] - '0';
+                } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') {
+                    digit = source[byte_offset] - 'a' + 10;
+                } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') {
+                    digit = source[byte_offset] - 'A' + 10;
+                } else if (source[byte_offset] == '}') {
+                    if (codepoint < 0x110000) {
+                        *result = codepoint;
+                        return ErrorNone;
+                    } else {
+                        *bad_index = byte_offset;
+                        return ErrorUnicodePointTooLarge;
+                    }
+                } else {
+                    *bad_index = byte_offset;
+                    return ErrorInvalidCharacter;
+                }
+                codepoint = codepoint * 16 + digit;
+                continue;
+            }
+        }
+    }
+}
+
+
+Buf *token_string_literal_buf(RootStruct *root_struct, TokenIndex token) {
+    Error err;
+    assert(root_struct->token_ids[token] == TokenIdStringLiteral);
+    const char *source = buf_ptr(root_struct->source_code);
+    size_t byte_offset = root_struct->token_locs[token].offset;
+    size_t bad_index;
+    Buf *str = buf_alloc();
+    if ((err = source_string_literal_buf(source + byte_offset, str, &bad_index))) {
+        zig_panic("TODO handle string literal parse error");
+    }
+    return str;
+}
+
+Buf *token_identifier_buf(RootStruct *root_struct, TokenIndex token) {
+    Error err;
+    const char *source = buf_ptr(root_struct->source_code);
+    size_t byte_offset = root_struct->token_locs[token].offset;
+    if (root_struct->token_ids[token] == TokenIdBuiltin) {
+        byte_offset += 1;
+    } else {
+        assert(root_struct->token_ids[token] == TokenIdIdentifier);
+    }
+    assert(source[byte_offset] != '.'); // wrong token index
+
+    if (source[byte_offset] == '@') {
+        size_t bad_index;
+        Buf *str = buf_alloc();
+        if ((err = source_string_literal_buf(source + byte_offset + 1, str, &bad_index))) {
+            zig_panic("TODO handle string literal parse error");
+        }
+        return str;
+    } else {
+        size_t start = byte_offset;
+        for (;; byte_offset += 1) {
+            if (source[byte_offset] == 0) break;
+            if ((source[byte_offset] >= 'a' && source[byte_offset] <= 'z') ||
+                (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') ||
+                (source[byte_offset] >= '0' && source[byte_offset] <= '9') ||
+                source[byte_offset] == '_')
+            {
+                continue;
+            }
+            break;
+        }
+        return buf_create_from_mem(source + start, byte_offset - start);
+    }
+}
+
+Buf *node_identifier_buf(AstNode *node) {
+    assert(node->type == NodeTypeIdentifier);
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    return token_identifier_buf(root_struct, node->main_token);
+}
+
+Buf *node_string_literal_buf(AstNode *node) {
+    assert(node->type == NodeTypeStringLiteral);
+    RootStruct *root_struct = node->owner->data.structure.root_struct;
+    return token_string_literal_buf(root_struct, node->main_token);
+}
+
+void token_number_literal_bigint(RootStruct *root_struct, BigInt *result, TokenIndex token) {
+    const char *source = buf_ptr(root_struct->source_code);
+    uint32_t byte_offset = root_struct->token_locs[token].offset;
+
+    bigint_init_unsigned(result, 0);
+    BigInt radix_bi;
+
+    if (source[byte_offset] == '0') {
+        byte_offset += 1;
+        switch (source[byte_offset]) {
+            case 'b':
+                byte_offset += 1;
+                bigint_init_unsigned(&radix_bi, 2);
+                break;
+            case 'o':
+                byte_offset += 1;
+                bigint_init_unsigned(&radix_bi, 8);
+                break;
+            case 'x':
+                byte_offset += 1;
+                bigint_init_unsigned(&radix_bi, 16);
+                break;
+            default:
+                bigint_init_unsigned(&radix_bi, 10);
+                break;
+        }
+    } else {
+        bigint_init_unsigned(&radix_bi, 10);
+    }
+
+    BigInt digit_value_bi = {};
+    BigInt multiplied = {};
+
+    for (;source[byte_offset] != 0; byte_offset += 1) {
+        uint8_t digit;
+        if (source[byte_offset] >= '0' && source[byte_offset] <= '9') {
+            digit = source[byte_offset] - '0';
+        } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') {
+            digit = source[byte_offset] - 'a' + 10;
+        } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') {
+            digit = source[byte_offset] - 'A' + 10;
+        } else if (source[byte_offset] == '_') {
+            continue;
+        } else {
+            break;
+        }
+        bigint_deinit(&digit_value_bi);
+        bigint_init_unsigned(&digit_value_bi, digit);
+
+        bigint_deinit(&multiplied);
+        bigint_mul(&multiplied, result, &radix_bi);
+
+        bigint_add(result, &multiplied, &digit_value_bi);
+    }
+
+    bigint_deinit(&digit_value_bi);
+    bigint_deinit(&multiplied);
+    bigint_deinit(&radix_bi);
+}
+
src/stage1/parser.hpp
@@ -12,14 +12,21 @@
 #include "tokenizer.hpp"
 #include "errmsg.hpp"
 
-ATTRIBUTE_PRINTF(2, 3)
-void ast_token_error(Token *token, const char *format, ...);
-
-
-AstNode * ast_parse(Buf *buf, ZigList<Token> *tokens, ZigType *owner, ErrColor err_color);
+AstNode * ast_parse(Buf *buf, ZigType *owner, ErrColor err_color);
 
 void ast_print(AstNode *node, int indent);
 
 void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context);
 
+Buf *node_identifier_buf(AstNode *node);
+Buf *node_string_literal_buf(AstNode *node);
+
+Buf *token_identifier_buf(RootStruct *root_struct, TokenIndex token);
+Buf *token_string_literal_buf(RootStruct *root_struct, TokenIndex token);
+
+void token_number_literal_bigint(RootStruct *root_struct, BigInt *result, TokenIndex token);
+
+Error source_string_literal_buf(const char *source, Buf *out, size_t *bad_index);
+Error source_char_literal(const char *source, uint32_t *out, size_t *bad_index);
+
 #endif
src/stage1/stage2.h
@@ -112,6 +112,8 @@ enum Error {
     ErrorZigIsTheCCompiler,
     ErrorFileBusy,
     ErrorLocked,
+    ErrorInvalidCharacter,
+    ErrorUnicodePointTooLarge,
 };
 
 // ABI warning
src/stage1/tokenizer.cpp
@@ -30,18 +30,28 @@
     case '7': \
     case '8': \
     case '9'
+
 #define DIGIT \
          '0': \
     case DIGIT_NON_ZERO
 
-#define ALPHA \
+#define HEXDIGIT \
          'a': \
     case 'b': \
     case 'c': \
     case 'd': \
     case 'e': \
     case 'f': \
-    case 'g': \
+    case 'A': \
+    case 'B': \
+    case 'C': \
+    case 'D': \
+    case 'E': \
+    case 'F': \
+    case DIGIT
+
+#define ALPHA_EXCEPT_HEX_P_O_X \
+         'g': \
     case 'h': \
     case 'i': \
     case 'j': \
@@ -49,8 +59,6 @@
     case 'l': \
     case 'm': \
     case 'n': \
-    case 'o': \
-    case 'p': \
     case 'q': \
     case 'r': \
     case 's': \
@@ -58,15 +66,8 @@
     case 'u': \
     case 'v': \
     case 'w': \
-    case 'x': \
     case 'y': \
     case 'z': \
-    case 'A': \
-    case 'B': \
-    case 'C': \
-    case 'D': \
-    case 'E': \
-    case 'F': \
     case 'G': \
     case 'H': \
     case 'I': \
@@ -76,7 +77,6 @@
     case 'M': \
     case 'N': \
     case 'O': \
-    case 'P': \
     case 'Q': \
     case 'R': \
     case 'S': \
@@ -88,7 +88,46 @@
     case 'Y': \
     case 'Z'
 
-#define SYMBOL_CHAR \
+#define ALPHA_EXCEPT_E_B_O_X \
+         ALPHA_EXCEPT_HEX_P_O_X: \
+    case 'a': \
+    case 'c': \
+    case 'd': \
+    case 'f': \
+    case 'A': \
+    case 'B': \
+    case 'C': \
+    case 'D': \
+    case 'F': \
+    case 'p': \
+    case 'P'
+
+#define ALPHA_EXCEPT_HEX_AND_P \
+         ALPHA_EXCEPT_HEX_P_O_X: \
+    case 'o': \
+    case 'x'
+
+#define ALPHA_EXCEPT_E \
+         ALPHA_EXCEPT_HEX_AND_P: \
+    case 'a': \
+    case 'b': \
+    case 'c': \
+    case 'd': \
+    case 'f': \
+    case 'A': \
+    case 'B': \
+    case 'C': \
+    case 'D': \
+    case 'F': \
+    case 'p': \
+    case 'P'
+
+#define ALPHA \
+    ALPHA_EXCEPT_E: \
+    case 'e': \
+    case 'E'
+
+#define IDENTIFIER_CHAR \
     ALPHA: \
     case DIGIT: \
     case '_'
@@ -157,101 +196,92 @@ static const struct ZigKeyword zig_keywords[] = {
     {"while", TokenIdKeywordWhile},
 };
 
-bool is_zig_keyword(Buf *buf) {
+// Returns TokenIdIdentifier if it is not a keyword.
+static TokenId zig_keyword_token(const char *name_ptr, size_t name_len) {
     for (size_t i = 0; i < array_length(zig_keywords); i += 1) {
-        if (buf_eql_str(buf, zig_keywords[i].text)) {
-            return true;
+        if (mem_eql_str(name_ptr, name_len, zig_keywords[i].text)) {
+            return zig_keywords[i].token_id;
         }
     }
-    return false;
-}
-
-static bool is_symbol_char(uint8_t c) {
-    switch (c) {
-        case SYMBOL_CHAR:
-            return true;
-        default:
-            return false;
-    }
+    return TokenIdIdentifier;
 }
 
 enum TokenizeState {
-    TokenizeStateStart,
-    TokenizeStateSymbol,
-    TokenizeStateZero, // "0", which might lead to "0x"
-    TokenizeStateNumber, // "123", "0x123"
-    TokenizeStateNumberNoUnderscore, // "12_", "0x12_" next char must be digit
-    TokenizeStateNumberDot,
-    TokenizeStateFloatFraction, // "123.456", "0x123.456"
-    TokenizeStateFloatFractionNoUnderscore, // "123.45_", "0x123.45_"
-    TokenizeStateFloatExponentUnsigned, // "123.456e", "123e", "0x123p"
-    TokenizeStateFloatExponentNumber, // "123.456e7", "123.456e+7", "123.456e-7"
-    TokenizeStateFloatExponentNumberNoUnderscore, // "123.456e7_", "123.456e+7_", "123.456e-7_"
-    TokenizeStateString,
-    TokenizeStateStringEscape,
-    TokenizeStateStringEscapeUnicodeStart,
-    TokenizeStateCharLiteral,
-    TokenizeStateCharLiteralEnd,
-    TokenizeStateCharLiteralUnicode,
-    TokenizeStateSawStar,
-    TokenizeStateSawStarPercent,
-    TokenizeStateSawSlash,
-    TokenizeStateSawSlash2,
-    TokenizeStateSawSlash3,
-    TokenizeStateSawSlashBang,
-    TokenizeStateSawBackslash,
-    TokenizeStateSawPercent,
-    TokenizeStateSawPlus,
-    TokenizeStateSawPlusPercent,
-    TokenizeStateSawDash,
-    TokenizeStateSawMinusPercent,
-    TokenizeStateSawAmpersand,
-    TokenizeStateSawCaret,
-    TokenizeStateSawBar,
-    TokenizeStateDocComment,
-    TokenizeStateContainerDocComment,
-    TokenizeStateLineComment,
-    TokenizeStateLineString,
-    TokenizeStateLineStringEnd,
-    TokenizeStateLineStringContinue,
-    TokenizeStateSawEq,
-    TokenizeStateSawBang,
-    TokenizeStateSawLessThan,
-    TokenizeStateSawLessThanLessThan,
-    TokenizeStateSawGreaterThan,
-    TokenizeStateSawGreaterThanGreaterThan,
-    TokenizeStateSawDot,
-    TokenizeStateSawDotDot,
-    TokenizeStateSawDotStar,
-    TokenizeStateSawAtSign,
-    TokenizeStateCharCode,
-    TokenizeStateError,
+    TokenizeState_start,
+    TokenizeState_identifier,
+    TokenizeState_builtin,
+    TokenizeState_string_literal,
+    TokenizeState_string_literal_backslash,
+    TokenizeState_multiline_string_literal_line,
+    TokenizeState_char_literal,
+    TokenizeState_char_literal_backslash,
+    TokenizeState_char_literal_hex_escape,
+    TokenizeState_char_literal_unicode_escape_saw_u,
+    TokenizeState_char_literal_unicode_escape,
+    TokenizeState_char_literal_unicode,
+    TokenizeState_char_literal_end,
+    TokenizeState_backslash,
+    TokenizeState_equal,
+    TokenizeState_bang,
+    TokenizeState_pipe,
+    TokenizeState_minus,
+    TokenizeState_minus_percent,
+    TokenizeState_asterisk,
+    TokenizeState_asterisk_percent,
+    TokenizeState_slash,
+    TokenizeState_line_comment_start,
+    TokenizeState_line_comment,
+    TokenizeState_doc_comment_start,
+    TokenizeState_doc_comment,
+    TokenizeState_container_doc_comment,
+    TokenizeState_zero,
+    TokenizeState_int_literal_dec,
+    TokenizeState_int_literal_dec_no_underscore,
+    TokenizeState_int_literal_bin,
+    TokenizeState_int_literal_bin_no_underscore,
+    TokenizeState_int_literal_oct,
+    TokenizeState_int_literal_oct_no_underscore,
+    TokenizeState_int_literal_hex,
+    TokenizeState_int_literal_hex_no_underscore,
+    TokenizeState_num_dot_dec,
+    TokenizeState_num_dot_hex,
+    TokenizeState_float_fraction_dec,
+    TokenizeState_float_fraction_dec_no_underscore,
+    TokenizeState_float_fraction_hex,
+    TokenizeState_float_fraction_hex_no_underscore,
+    TokenizeState_float_exponent_unsigned,
+    TokenizeState_float_exponent_num,
+    TokenizeState_float_exponent_num_no_underscore,
+    TokenizeState_ampersand,
+    TokenizeState_caret,
+    TokenizeState_percent,
+    TokenizeState_plus,
+    TokenizeState_plus_percent,
+    TokenizeState_angle_bracket_left,
+    TokenizeState_angle_bracket_angle_bracket_left,
+    TokenizeState_angle_bracket_right,
+    TokenizeState_angle_bracket_angle_bracket_right,
+    TokenizeState_period,
+    TokenizeState_period_2,
+    TokenizeState_period_asterisk,
+    TokenizeState_saw_at_sign,
+    TokenizeState_error,
 };
 
 
 struct Tokenize {
-    Buf *buf;
+    Tokenization *out;
     size_t pos;
     TokenizeState state;
-    ZigList<Token> *tokens;
-    int line;
-    int column;
-    Token *cur_tok;
-    Tokenization *out;
-    uint32_t radix;
-    bool is_trailing_underscore;
-    size_t char_code_index;
-    bool unicode;
-    uint32_t char_code;
-    size_t remaining_code_units;
+    uint32_t line;
+    uint32_t column;
 };
 
 ATTRIBUTE_PRINTF(2, 3)
 static void tokenize_error(Tokenize *t, const char *format, ...) {
-    t->state = TokenizeStateError;
+    t->state = TokenizeState_error;
 
-    t->out->err_line = t->line;
-    t->out->err_column = t->column;
+    t->out->err_byte_offset = t->pos;
 
     va_list ap;
     va_start(ap, format);
@@ -259,98 +289,18 @@ static void tokenize_error(Tokenize *t, const char *format, ...) {
     va_end(ap);
 }
 
-static void set_token_id(Tokenize *t, Token *token, TokenId id) {
-    token->id = id;
-
-    if (id == TokenIdIntLiteral) {
-        bigint_init_unsigned(&token->data.int_lit.bigint, 0);
-    } else if (id == TokenIdFloatLiteral) {
-        bigfloat_init_32(&token->data.float_lit.bigfloat, 0.0f);
-        token->data.float_lit.overflow = false;
-    } else if (id == TokenIdStringLiteral || id == TokenIdMultilineStringLiteral || id == TokenIdSymbol) {
-        memset(&token->data.str_lit.str, 0, sizeof(Buf));
-        buf_resize(&token->data.str_lit.str, 0);
-    }
-}
-
 static void begin_token(Tokenize *t, TokenId id) {
-    assert(!t->cur_tok);
-    t->tokens->add_one();
-    Token *token = &t->tokens->last();
-    token->start_line = t->line;
-    token->start_column = t->column;
-    token->start_pos = t->pos;
-
-    set_token_id(t, token, id);
-
-    t->cur_tok = token;
+    t->out->ids.append(id);
+    t->out->locs.append({
+        .offset = (uint32_t) t->pos,
+        .line = t->line,
+        .column = t->column,
+    });
 }
 
 static void cancel_token(Tokenize *t) {
-    t->tokens->pop();
-    t->cur_tok = nullptr;
-}
-
-static void end_float_token(Tokenize *t) {
-    uint8_t *ptr_buf = (uint8_t*)buf_ptr(t->buf) + t->cur_tok->start_pos;
-    size_t buf_len = t->cur_tok->end_pos - t->cur_tok->start_pos;
-    if (bigfloat_init_buf(&t->cur_tok->data.float_lit.bigfloat, ptr_buf, buf_len)) {
-        t->cur_tok->data.float_lit.overflow = true;
-    }
-}
-
-static void end_token(Tokenize *t) {
-    assert(t->cur_tok);
-    t->cur_tok->end_pos = t->pos + 1;
-
-    if (t->cur_tok->id == TokenIdFloatLiteral) {
-        end_float_token(t);
-    } else if (t->cur_tok->id == TokenIdSymbol) {
-        char *token_mem = buf_ptr(t->buf) + t->cur_tok->start_pos;
-        int token_len = (int)(t->cur_tok->end_pos - t->cur_tok->start_pos);
-
-        for (size_t i = 0; i < array_length(zig_keywords); i += 1) {
-            if (mem_eql_str(token_mem, token_len, zig_keywords[i].text)) {
-                t->cur_tok->id = zig_keywords[i].token_id;
-                break;
-            }
-        }
-    }
-
-    t->cur_tok = nullptr;
-}
-
-static bool is_exponent_signifier(uint8_t c, int radix) {
-    if (radix == 16) {
-        return c == 'p' || c == 'P';
-    } else {
-        return c == 'e' || c == 'E';
-    }
-}
-
-static uint32_t get_digit_value(uint8_t c) {
-    if ('0' <= c && c <= '9') {
-        return c - '0';
-    }
-    if ('A' <= c && c <= 'Z') {
-        return c - 'A' + 10;
-    }
-    if ('a' <= c && c <= 'z') {
-        return c - 'a' + 10;
-    }
-    return UINT32_MAX;
-}
-
-static void handle_string_escape(Tokenize *t, uint8_t c) {
-    if (t->cur_tok->id == TokenIdCharLiteral) {
-        t->cur_tok->data.char_lit.c = c;
-        t->state = TokenizeStateCharLiteralEnd;
-    } else if (t->cur_tok->id == TokenIdStringLiteral || t->cur_tok->id == TokenIdSymbol) {
-        buf_append_char(&t->cur_tok->data.str_lit.str, c);
-        t->state = TokenizeStateString;
-    } else {
-        zig_unreachable();
-    }
+    t->out->ids.pop();
+    t->out->locs.pop();
 }
 
 static const char* get_escape_shorthand(uint8_t c) {
@@ -376,7 +326,15 @@ static const char* get_escape_shorthand(uint8_t c) {
     }
 }
 
+static void invalid_eof(Tokenize *t) {
+    return tokenize_error(t, "unexpected End-Of-File");
+}
+
 static void invalid_char_error(Tokenize *t, uint8_t c) {
+    if (c == 0) {
+        return invalid_eof(t);
+    }
+
     if (c == '\r') {
         tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported");
         return;
@@ -396,1139 +354,1092 @@ static void invalid_char_error(Tokenize *t, uint8_t c) {
     tokenize_error(t, "invalid character: '\\x%02x'", c);
 }
 
-void tokenize(Buf *buf, Tokenization *out) {
+void tokenize(const char *source, Tokenization *out) {
     Tokenize t = {0};
     t.out = out;
-    t.tokens = out->tokens = heap::c_allocator.create<ZigList<Token>>();
-    t.buf = buf;
 
-    out->line_offsets = heap::c_allocator.create<ZigList<size_t>>();
-    out->line_offsets->append(0);
+    size_t remaining_code_units;
+    size_t seen_escape_digits;
 
-    // Skip the UTF-8 BOM if present
-    if (buf_starts_with_mem(buf, "\xEF\xBB\xBF", 3)) {
+    // Skip the UTF-8 BOM if present.
+    if (source[0] == (char)0xef &&
+        source[1] == (char)0xbb &&
+        source[2] == (char)0xbf)
+    {
         t.pos += 3;
     }
 
-    for (; t.pos < buf_len(t.buf); t.pos += 1) {
-        uint8_t c = buf_ptr(t.buf)[t.pos];
+    // Invalid token takes up index 0 so that index 0 can mean "none".
+    begin_token(&t, TokenIdCount);
+
+    for (;;) {
+        uint8_t c = source[t.pos];
         switch (t.state) {
-            case TokenizeStateError:
-                break;
-            case TokenizeStateStart:
+            case TokenizeState_error:
+                goto eof;
+            case TokenizeState_start:
                 switch (c) {
+                    case 0:
+                        goto eof;
                     case WHITESPACE:
                         break;
+                    case '"':
+                        begin_token(&t, TokenIdStringLiteral);
+                        t.state = TokenizeState_string_literal;
+                        break;
+                    case '\'':
+                        begin_token(&t, TokenIdCharLiteral);
+                        t.state = TokenizeState_char_literal;
+                        break;
                     case ALPHA:
                     case '_':
-                        t.state = TokenizeStateSymbol;
-                        begin_token(&t, TokenIdSymbol);
-                        buf_append_char(&t.cur_tok->data.str_lit.str, c);
+                        t.state = TokenizeState_identifier;
+                        begin_token(&t, TokenIdIdentifier);
                         break;
-                    case '0':
-                        t.state = TokenizeStateZero;
-                        begin_token(&t, TokenIdIntLiteral);
-                        t.is_trailing_underscore = false;
-                        t.radix = 10;
-                        bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, 0);
+                    case '@':
+                        begin_token(&t, TokenIdBuiltin);
+                        t.state = TokenizeState_saw_at_sign;
                         break;
-                    case DIGIT_NON_ZERO:
-                        t.state = TokenizeStateNumber;
-                        begin_token(&t, TokenIdIntLiteral);
-                        t.is_trailing_underscore = false;
-                        t.radix = 10;
-                        bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, get_digit_value(c));
+                    case '=':
+                        begin_token(&t, TokenIdEq);
+                        t.state = TokenizeState_equal;
                         break;
-                    case '"':
-                        begin_token(&t, TokenIdStringLiteral);
-                        t.state = TokenizeStateString;
+                    case '!':
+                        begin_token(&t, TokenIdBang);
+                        t.state = TokenizeState_bang;
                         break;
-                    case '\'':
-                        begin_token(&t, TokenIdCharLiteral);
-                        t.state = TokenizeStateCharLiteral;
+                    case '|':
+                        begin_token(&t, TokenIdBinOr);
+                        t.state = TokenizeState_pipe;
                         break;
                     case '(':
                         begin_token(&t, TokenIdLParen);
-                        end_token(&t);
                         break;
                     case ')':
                         begin_token(&t, TokenIdRParen);
-                        end_token(&t);
-                        break;
-                    case ',':
-                        begin_token(&t, TokenIdComma);
-                        end_token(&t);
-                        break;
-                    case '?':
-                        begin_token(&t, TokenIdQuestion);
-                        end_token(&t);
-                        break;
-                    case '{':
-                        begin_token(&t, TokenIdLBrace);
-                        end_token(&t);
-                        break;
-                    case '}':
-                        begin_token(&t, TokenIdRBrace);
-                        end_token(&t);
                         break;
                     case '[':
                         begin_token(&t, TokenIdLBracket);
-                        end_token(&t);
                         break;
                     case ']':
                         begin_token(&t, TokenIdRBracket);
-                        end_token(&t);
                         break;
                     case ';':
                         begin_token(&t, TokenIdSemicolon);
-                        end_token(&t);
+                        break;
+                    case ',':
+                        begin_token(&t, TokenIdComma);
+                        break;
+                    case '?':
+                        begin_token(&t, TokenIdQuestion);
                         break;
                     case ':':
                         begin_token(&t, TokenIdColon);
-                        end_token(&t);
                         break;
-                    case '#':
-                        begin_token(&t, TokenIdNumberSign);
-                        end_token(&t);
+                    case '%':
+                        begin_token(&t, TokenIdPercent);
+                        t.state = TokenizeState_percent;
                         break;
                     case '*':
                         begin_token(&t, TokenIdStar);
-                        t.state = TokenizeStateSawStar;
+                        t.state = TokenizeState_asterisk;
                         break;
-                    case '/':
-                        begin_token(&t, TokenIdSlash);
-                        t.state = TokenizeStateSawSlash;
+                    case '+':
+                        begin_token(&t, TokenIdPlus);
+                        t.state = TokenizeState_plus;
+                        break;
+                    case '<':
+                        begin_token(&t, TokenIdCmpLessThan);
+                        t.state = TokenizeState_angle_bracket_left;
+                        break;
+                    case '>':
+                        begin_token(&t, TokenIdCmpGreaterThan);
+                        t.state = TokenizeState_angle_bracket_right;
+                        break;
+                    case '^':
+                        begin_token(&t, TokenIdBinXor);
+                        t.state = TokenizeState_caret;
                         break;
                     case '\\':
-                        begin_token(&t, TokenIdMultilineStringLiteral);
-                        t.state = TokenizeStateSawBackslash;
+                        begin_token(&t, TokenIdMultilineStringLiteralLine);
+                        t.state = TokenizeState_backslash;
                         break;
-                    case '%':
-                        begin_token(&t, TokenIdPercent);
-                        t.state = TokenizeStateSawPercent;
+                    case '{':
+                        begin_token(&t, TokenIdLBrace);
                         break;
-                    case '+':
-                        begin_token(&t, TokenIdPlus);
-                        t.state = TokenizeStateSawPlus;
+                    case '}':
+                        begin_token(&t, TokenIdRBrace);
                         break;
                     case '~':
                         begin_token(&t, TokenIdTilde);
-                        end_token(&t);
                         break;
-                    case '@':
-                        begin_token(&t, TokenIdAtSign);
-                        t.state = TokenizeStateSawAtSign;
+                    case '.':
+                        begin_token(&t, TokenIdDot);
+                        t.state = TokenizeState_period;
                         break;
                     case '-':
                         begin_token(&t, TokenIdDash);
-                        t.state = TokenizeStateSawDash;
+                        t.state = TokenizeState_minus;
+                        break;
+                    case '/':
+                        begin_token(&t, TokenIdSlash);
+                        t.state = TokenizeState_slash;
                         break;
                     case '&':
                         begin_token(&t, TokenIdAmpersand);
-                        t.state = TokenizeStateSawAmpersand;
+                        t.state = TokenizeState_ampersand;
                         break;
-                    case '^':
-                        begin_token(&t, TokenIdBinXor);
-                        t.state = TokenizeStateSawCaret;
-                        break;
-                    case '|':
-                        begin_token(&t, TokenIdBinOr);
-                        t.state = TokenizeStateSawBar;
+                    case '0':
+                        t.state = TokenizeState_zero;
+                        begin_token(&t, TokenIdIntLiteral);
                         break;
-                    case '=':
-                        begin_token(&t, TokenIdEq);
-                        t.state = TokenizeStateSawEq;
+                    case DIGIT_NON_ZERO:
+                        t.state = TokenizeState_int_literal_dec;
+                        begin_token(&t, TokenIdIntLiteral);
                         break;
-                    case '!':
-                        begin_token(&t, TokenIdBang);
-                        t.state = TokenizeStateSawBang;
+                    default:
+                        invalid_char_error(&t, c);
+                }
+                break;
+            case TokenizeState_saw_at_sign:
+                switch (c) {
+                    case 0:
+                        invalid_eof(&t);
+                        goto eof;
+                    case '"':
+                        t.out->ids.last() = TokenIdIdentifier;
+                        t.state = TokenizeState_string_literal;
                         break;
-                    case '<':
-                        begin_token(&t, TokenIdCmpLessThan);
-                        t.state = TokenizeStateSawLessThan;
+                    case IDENTIFIER_CHAR:
+                        t.state = TokenizeState_builtin;
                         break;
-                    case '>':
-                        begin_token(&t, TokenIdCmpGreaterThan);
-                        t.state = TokenizeStateSawGreaterThan;
+                    default:
+                        invalid_char_error(&t, c);
+                }
+                break;
+            case TokenizeState_ampersand:
+                switch (c) {
+                    case 0:
+                        goto eof;
+                    case '&':
+                        tokenize_error(&t, "`&&` is invalid. Note that `and` is boolean AND");
                         break;
-                    case '.':
-                        begin_token(&t, TokenIdDot);
-                        t.state = TokenizeStateSawDot;
+                    case '=':
+                        t.out->ids.last() = TokenIdBitAndEq;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        invalid_char_error(&t, c);
+                        t.state = TokenizeState_start;
+                        continue;
                 }
                 break;
-            case TokenizeStateSawDot:
+            case TokenizeState_asterisk:
                 switch (c) {
-                    case '.':
-                        t.state = TokenizeStateSawDotDot;
-                        set_token_id(&t, t.cur_tok, TokenIdEllipsis2);
+                    case 0:
+                        goto eof;
+                    case '=':
+                        t.out->ids.last() = TokenIdTimesEq;
+                        t.state = TokenizeState_start;
                         break;
                     case '*':
-                        t.state = TokenizeStateSawDotStar;
-                        set_token_id(&t, t.cur_tok, TokenIdDotStar);
+                        t.out->ids.last() = TokenIdStarStar;
+                        t.state = TokenizeState_start;
+                        break;
+                    case '%':
+                        t.state = TokenizeState_asterisk_percent;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawDotDot:
+            case TokenizeState_asterisk_percent:
                 switch (c) {
-                    case '.':
-                        t.state = TokenizeStateStart;
-                        set_token_id(&t, t.cur_tok, TokenIdEllipsis3);
-                        end_token(&t);
+                    case 0:
+                        t.out->ids.last() = TokenIdTimesPercent;
+                        goto eof;
+                    case '=':
+                        t.out->ids.last() = TokenIdTimesPercentEq;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdTimesPercent;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawDotStar:
+            case TokenizeState_percent:
                 switch (c) {
-                    case '*':
-                        tokenize_error(&t, "`.*` can't be followed by `*`. Are you missing a space?");
+                    case 0:
+                        goto eof;
+                    case '=':
+                        t.out->ids.last() = TokenIdModEq;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawGreaterThan:
+            case TokenizeState_plus:
                 switch (c) {
+                    case 0:
+                        goto eof;
                     case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdCmpGreaterOrEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdPlusEq;
+                        t.state = TokenizeState_start;
                         break;
-                    case '>':
-                        set_token_id(&t, t.cur_tok, TokenIdBitShiftRight);
-                        t.state = TokenizeStateSawGreaterThanGreaterThan;
+                    case '+':
+                        t.out->ids.last() = TokenIdPlusPlus;
+                        t.state = TokenizeState_start;
+                        break;
+                    case '%':
+                        t.state = TokenizeState_plus_percent;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawGreaterThanGreaterThan:
+            case TokenizeState_plus_percent:
                 switch (c) {
+                    case 0:
+                        t.out->ids.last() = TokenIdPlusPercent;
+                        goto eof;
                     case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdBitShiftRightEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdPlusPercentEq;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdPlusPercent;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawLessThan:
+            case TokenizeState_caret:
                 switch (c) {
+                    case 0:
+                        goto eof;
                     case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdCmpLessOrEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdBitXorEq;
+                        t.state = TokenizeState_start;
                         break;
-                    case '<':
-                        set_token_id(&t, t.cur_tok, TokenIdBitShiftLeft);
-                        t.state = TokenizeStateSawLessThanLessThan;
+                    default:
+                        t.state = TokenizeState_start;
+                        continue;
+                }
+                break;
+            case TokenizeState_identifier:
+                switch (c) {
+                    case 0: {
+                        uint32_t start_pos = t.out->locs.last().offset;
+                        t.out->ids.last() = zig_keyword_token(
+                                source + start_pos, t.pos - start_pos);
+                        goto eof;
+                    }
+                    case IDENTIFIER_CHAR:
+                        break;
+                    default: {
+                        uint32_t start_pos = t.out->locs.last().offset;
+                        t.out->ids.last() = zig_keyword_token(
+                                source + start_pos, t.pos - start_pos);
+
+                        t.state = TokenizeState_start;
+                        continue;
+                    }
+                }
+                break;
+            case TokenizeState_builtin:
+                switch (c) {
+                    case 0:
+                        goto eof;
+                    case IDENTIFIER_CHAR:
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawLessThanLessThan:
+            case TokenizeState_backslash:
+                switch (c) {
+                    case '\\':
+                        t.state = TokenizeState_multiline_string_literal_line;
+                        break;
+                    default:
+                        invalid_char_error(&t, c);
+                        break;
+                }
+                break;
+            case TokenizeState_string_literal:
+                switch (c) {
+                    case 0:
+                        invalid_eof(&t);
+                        goto eof;
+                    case '\\':
+                        t.state = TokenizeState_string_literal_backslash;
+                        break;
+                    case '"':
+                        t.state = TokenizeState_start;
+                        break;
+                    case '\n':
+                    case '\r':
+                        tokenize_error(&t, "newline not allowed in string literal");
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            case TokenizeState_string_literal_backslash:
+                switch (c) {
+                    case 0:
+                        invalid_eof(&t);
+                        goto eof;
+                    case '\n':
+                    case '\r':
+                        tokenize_error(&t, "newline not allowed in string literal");
+                        break;
+                    default:
+                        t.state = TokenizeState_string_literal;
+                        break;
+                }
+                break;
+            case TokenizeState_char_literal:
+                if (c == 0) {
+                    invalid_eof(&t);
+                    goto eof;
+                } else if (c == '\\') {
+                    t.state = TokenizeState_char_literal_backslash;
+                } else if (c == '\'') {
+                    tokenize_error(&t, "expected character");
+                } else if ((c >= 0x80 && c <= 0xbf) || c >= 0xf8) {
+                    // 10xxxxxx
+                    // 11111xxx
+                    invalid_char_error(&t, c);
+                } else if (c >= 0xc0 && c <= 0xdf) {
+                    // 110xxxxx
+                    remaining_code_units = 1;
+                    t.state = TokenizeState_char_literal_unicode;
+                } else if (c >= 0xe0 && c <= 0xef) {
+                    // 1110xxxx
+                    remaining_code_units = 2;
+                    t.state = TokenizeState_char_literal_unicode;
+                } else if (c >= 0xf0 && c <= 0xf7) {
+                    // 11110xxx
+                    remaining_code_units = 3;
+                    t.state = TokenizeState_char_literal_unicode;
+                } else {
+                    t.state = TokenizeState_char_literal_end;
+                }
+                break;
+            case TokenizeState_char_literal_backslash:
+                switch (c) {
+                    case 0:
+                        invalid_eof(&t);
+                        goto eof;
+                    case '\n':
+                    case '\r':
+                        tokenize_error(&t, "newline not allowed in character literal");
+                        break;
+                    case 'x':
+                        t.state = TokenizeState_char_literal_hex_escape;
+                        seen_escape_digits = 0;
+                        break;
+                    case 'u':
+                        t.state = TokenizeState_char_literal_unicode_escape_saw_u;
+                        break;
+                    default:
+                        t.state = TokenizeState_char_literal_end;
+                        break;
+                }
+                break;
+            case TokenizeState_char_literal_hex_escape:
+                switch (c) {
+                    case ALPHA:
+                    case DIGIT:
+                        seen_escape_digits += 1;
+                        if (seen_escape_digits == 2) {
+                            t.state = TokenizeState_char_literal_end;
+                        }
+                        break;
+                    default:
+                        tokenize_error(&t, "expected hex digit");
+                        break;
+                }
+                break;
+            case TokenizeState_char_literal_unicode_escape_saw_u:
+                switch (c) {
+                    case '{':
+                        t.state = TokenizeState_char_literal_unicode_escape;
+                        seen_escape_digits = 0;
+                        break;
+                    default:
+                        tokenize_error(&t, "expected '{' to begin unicode escape sequence");
+                        break;
+                }
+                break;
+            case TokenizeState_char_literal_unicode_escape:
+                switch (c) {
+                    case ALPHA:
+                    case DIGIT:
+                        seen_escape_digits += 1;
+                        break;
+                    case '}':
+                        if (seen_escape_digits == 0) {
+                            tokenize_error(&t, "missing unicode escape sequence");
+                            break;
+                        }
+                        t.state = TokenizeState_char_literal_end;
+                        break;
+                    default:
+                        tokenize_error(&t, "expected hex digit");
+                        break;
+                }
+                break;
+            case TokenizeState_char_literal_end:
+                switch (c) {
+                    case '\'':
+                        t.state = TokenizeState_start;
+                        break;
+                    default:
+                        invalid_char_error(&t, c);
+                        break;
+                }
+                break;
+            case TokenizeState_char_literal_unicode:
+                if (c >= 0x80 && c <= 0xbf) {
+                    remaining_code_units -= 1;
+                    if (remaining_code_units == 0) {
+                        t.state = TokenizeState_char_literal_end;
+                    }
+                } else {
+                    invalid_char_error(&t, c);
+                }
+                break;
+            case TokenizeState_multiline_string_literal_line:
+                switch (c) {
+                    case 0:
+                        goto eof;
+                    case '\n':
+                        t.state = TokenizeState_start;
+                        break;
+                    default:
+                        break;
+                }
+                break;
+            case TokenizeState_bang:
                 switch (c) {
+                    case 0:
+                        goto eof;
                     case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdBitShiftLeftEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdCmpNotEq;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawBang:
+            case TokenizeState_pipe:
                 switch (c) {
+                    case 0:
+                        goto eof;
                     case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdCmpNotEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdBitOrEq;
+                        t.state = TokenizeState_start;
+                        break;
+                    case '|':
+                        t.out->ids.last() = TokenIdBarBar;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawEq:
+            case TokenizeState_equal:
                 switch (c) {
+                    case 0:
+                        goto eof;
                     case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdCmpEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdCmpEq;
+                        t.state = TokenizeState_start;
                         break;
                     case '>':
-                        set_token_id(&t, t.cur_tok, TokenIdFatArrow);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdFatArrow;
+                        t.state = TokenizeState_start;
+                        break;
+                    default:
+                        t.state = TokenizeState_start;
+                        continue;
+                }
+                break;
+            case TokenizeState_minus:
+                switch (c) {
+                    case 0:
+                        goto eof;
+                    case '>':
+                        t.out->ids.last() = TokenIdArrow;
+                        t.state = TokenizeState_start;
+                        break;
+                    case '=':
+                        t.out->ids.last() = TokenIdMinusEq;
+                        t.state = TokenizeState_start;
+                        break;
+                    case '%':
+                        t.state = TokenizeState_minus_percent;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawStar:
+            case TokenizeState_minus_percent:
                 switch (c) {
+                    case 0:
+                        t.out->ids.last() = TokenIdMinusPercent;
+                        goto eof;
                     case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdTimesEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        break;
-                    case '*':
-                        set_token_id(&t, t.cur_tok, TokenIdStarStar);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        break;
-                    case '%':
-                        set_token_id(&t, t.cur_tok, TokenIdTimesPercent);
-                        t.state = TokenizeStateSawStarPercent;
+                        t.out->ids.last() = TokenIdMinusPercentEq;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdMinusPercent;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawStarPercent:
+            case TokenizeState_angle_bracket_left:
                 switch (c) {
+                    case 0:
+                        goto eof;
                     case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdTimesPercentEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdCmpLessOrEq;
+                        t.state = TokenizeState_start;
+                        break;
+                    case '<':
+                        t.state = TokenizeState_angle_bracket_angle_bracket_left;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawPercent:
+            case TokenizeState_angle_bracket_angle_bracket_left:
                 switch (c) {
+                    case 0:
+                        t.out->ids.last() = TokenIdBitShiftLeft;
+                        goto eof;
                     case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdModEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        break;
-                    case '.':
-                        set_token_id(&t, t.cur_tok, TokenIdPercentDot);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdBitShiftLeftEq;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdBitShiftLeft;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawPlus:
+            case TokenizeState_angle_bracket_right:
                 switch (c) {
+                    case 0:
+                        goto eof;
                     case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdPlusEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdCmpGreaterOrEq;
+                        t.state = TokenizeState_start;
                         break;
-                    case '+':
-                        set_token_id(&t, t.cur_tok, TokenIdPlusPlus);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        break;
-                    case '%':
-                        set_token_id(&t, t.cur_tok, TokenIdPlusPercent);
-                        t.state = TokenizeStateSawPlusPercent;
+                    case '>':
+                        t.state = TokenizeState_angle_bracket_angle_bracket_right;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawPlusPercent:
+            case TokenizeState_angle_bracket_angle_bracket_right:
                 switch (c) {
+                    case 0:
+                        t.out->ids.last() = TokenIdBitShiftRight;
+                        goto eof;
                     case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdPlusPercentEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdBitShiftRightEq;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdBitShiftRight;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawAmpersand:
+            case TokenizeState_period:
                 switch (c) {
-                    case '&':
-                        tokenize_error(&t, "`&&` is invalid. Note that `and` is boolean AND");
+                    case 0:
+                        goto eof;
+                    case '.':
+                        t.state = TokenizeState_period_2;
                         break;
-                    case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdBitAndEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                    case '*':
+                        t.state = TokenizeState_period_asterisk;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawCaret:
+            case TokenizeState_period_2:
                 switch (c) {
-                    case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdBitXorEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                    case 0:
+                        t.out->ids.last() = TokenIdEllipsis2;
+                        goto eof;
+                    case '.':
+                        t.out->ids.last() = TokenIdEllipsis3;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdEllipsis2;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawBar:
+            case TokenizeState_period_asterisk:
                 switch (c) {
-                    case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdBitOrEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        break;
-                    case '|':
-                        set_token_id(&t, t.cur_tok, TokenIdBarBar);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                    case 0:
+                        t.out->ids.last() = TokenIdDotStar;
+                        goto eof;
+                    case '*':
+                        tokenize_error(&t, "`.*` cannot be followed by `*`. Are you missing a space?");
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdDotStar;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawSlash:
+            case TokenizeState_slash:
                 switch (c) {
+                    case 0:
+                        goto eof;
                     case '/':
-                        t.state = TokenizeStateSawSlash2;
+                        t.state = TokenizeState_line_comment_start;
                         break;
                     case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdDivEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdDivEq;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSawSlash2:
+            case TokenizeState_line_comment_start:
                 switch (c) {
+                    case 0:
+                        goto eof;
                     case '/':
-                        t.state = TokenizeStateSawSlash3;
+                        t.state = TokenizeState_doc_comment_start;
                         break;
                     case '!':
-                        t.state = TokenizeStateSawSlashBang;
+                        t.out->ids.last() = TokenIdContainerDocComment;
+                        t.state = TokenizeState_container_doc_comment;
                         break;
                     case '\n':
                         cancel_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         break;
                     default:
                         cancel_token(&t);
-                        t.state = TokenizeStateLineComment;
+                        t.state = TokenizeState_line_comment;
                         break;
                 }
                 break;
-            case TokenizeStateSawSlash3:
+            case TokenizeState_doc_comment_start:
                 switch (c) {
+                    case 0:
+                        t.out->ids.last() = TokenIdDocComment;
+                        goto eof;
                     case '/':
                         cancel_token(&t);
-                        t.state = TokenizeStateLineComment;
+                        t.state = TokenizeState_line_comment;
                         break;
                     case '\n':
-                        set_token_id(&t, t.cur_tok, TokenIdDocComment);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.out->ids.last() = TokenIdDocComment;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        set_token_id(&t, t.cur_tok, TokenIdDocComment);
-                        t.state = TokenizeStateDocComment;
+                        t.out->ids.last() = TokenIdDocComment;
+                        t.state = TokenizeState_doc_comment;
                         break;
                 }
                 break;
-            case TokenizeStateSawSlashBang:
+            case TokenizeState_line_comment:
                 switch (c) {
+                    case 0:
+                        goto eof;
                     case '\n':
-                        set_token_id(&t, t.cur_tok, TokenIdContainerDocComment);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        set_token_id(&t, t.cur_tok, TokenIdContainerDocComment);
-                        t.state = TokenizeStateContainerDocComment;
                         break;
                 }
                 break;
-            case TokenizeStateSawBackslash:
+            case TokenizeState_doc_comment:
+            case TokenizeState_container_doc_comment:
                 switch (c) {
-                    case '\\':
-                        t.state = TokenizeStateLineString;
+                    case 0:
+                        goto eof;
+                    case '\n':
+                        t.state = TokenizeState_start;
                         break;
                     default:
-                        invalid_char_error(&t, c);
+                        // do nothing
                         break;
                 }
                 break;
-            case TokenizeStateLineString:
+            case TokenizeState_zero:
                 switch (c) {
-                    case '\n':
-                        t.state = TokenizeStateLineStringEnd;
+                    case 0:
+                        goto eof;
+                    case 'b':
+                        t.state = TokenizeState_int_literal_bin_no_underscore;
                         break;
-                    default:
-                        buf_append_char(&t.cur_tok->data.str_lit.str, c);
+                    case 'o':
+                        t.state = TokenizeState_int_literal_oct_no_underscore;
                         break;
-                }
-                break;
-            case TokenizeStateLineStringEnd:
-                switch (c) {
-                    case WHITESPACE:
+                    case 'x':
+                        t.state = TokenizeState_int_literal_hex_no_underscore;
                         break;
-                    case '\\':
-                        t.state = TokenizeStateLineStringContinue;
+                    case DIGIT:
+                    case '_':
+                    case '.':
+                    case 'e':
+                    case 'E':
+                        // Reinterpret as a decimal number.
+                        t.state = TokenizeState_int_literal_dec;
+                        continue;
+                    case ALPHA_EXCEPT_E_B_O_X:
+                        invalid_char_error(&t, c);
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateLineStringContinue:
+            case TokenizeState_int_literal_bin_no_underscore:
                 switch (c) {
-                    case '\\':
-                        t.state = TokenizeStateLineString;
-                        buf_append_char(&t.cur_tok->data.str_lit.str, '\n');
+                    case '0':
+                    case '1':
+                        t.state = TokenizeState_int_literal_bin;
                         break;
                     default:
                         invalid_char_error(&t, c);
-                        break;
                 }
                 break;
-            case TokenizeStateLineComment:
+            case TokenizeState_int_literal_bin:
                 switch (c) {
-                    case '\n':
-                        t.state = TokenizeStateStart;
+                    case 0:
+                        goto eof;
+                    case '_':
+                        t.state = TokenizeState_int_literal_bin_no_underscore;
                         break;
-                    default:
-                        // do nothing
+                    case '0':
+                    case '1':
+                        break;
+                    case '2':
+                    case '3':
+                    case '4':
+                    case '5':
+                    case '6':
+                    case '7':
+                    case '8':
+                    case '9':
+                    case ALPHA:
+                        invalid_char_error(&t, c);
                         break;
+                    default:
+                        t.state = TokenizeState_start;
+                        continue;
                 }
                 break;
-            case TokenizeStateDocComment:
+            case TokenizeState_int_literal_oct_no_underscore:
                 switch (c) {
-                    case '\n':
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                    case '0':
+                    case '1':
+                    case '2':
+                    case '3':
+                    case '4':
+                    case '5':
+                    case '6':
+                    case '7':
+                        t.state = TokenizeState_int_literal_oct;
                         break;
                     default:
-                        // do nothing
+                        invalid_char_error(&t, c);
                         break;
                 }
                 break;
-            case TokenizeStateContainerDocComment:
+            case TokenizeState_int_literal_oct:
                 switch (c) {
-                    case '\n':
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                    case 0:
+                        goto eof;
+                    case '_':
+                        t.state = TokenizeState_int_literal_oct_no_underscore;
                         break;
-                    default:
-                        // do nothing
+                    case '0':
+                    case '1':
+                    case '2':
+                    case '3':
+                    case '4':
+                    case '5':
+                    case '6':
+                    case '7':
                         break;
-                }
-                break;
-            case TokenizeStateSawAtSign:
-                switch (c) {
-                    case '"':
-                        set_token_id(&t, t.cur_tok, TokenIdSymbol);
-                        t.state = TokenizeStateString;
+                    case ALPHA:
+                    case '8':
+                    case '9':
+                        invalid_char_error(&t, c);
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateSymbol:
+            case TokenizeState_int_literal_dec_no_underscore:
                 switch (c) {
-                    case SYMBOL_CHAR:
-                        buf_append_char(&t.cur_tok->data.str_lit.str, c);
+                    case DIGIT:
+                        t.state = TokenizeState_int_literal_dec;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        continue;
+                        invalid_char_error(&t, c);
+                        break;
                 }
                 break;
-            case TokenizeStateString:
+            case TokenizeState_int_literal_dec:
                 switch (c) {
-                    case '"':
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                    case 0:
+                        goto eof;
+                    case '_':
+                        t.state = TokenizeState_int_literal_dec_no_underscore;
                         break;
-                    case '\n':
-                        tokenize_error(&t, "newline not allowed in string literal");
+                    case '.':
+                        t.state = TokenizeState_num_dot_dec;
+                        t.out->ids.last() = TokenIdFloatLiteral;
                         break;
-                    case '\\':
-                        t.state = TokenizeStateStringEscape;
+                    case 'e':
+                    case 'E':
+                        t.state = TokenizeState_float_exponent_unsigned;
+                        t.out->ids.last() = TokenIdFloatLiteral;
                         break;
-                    default:
-                        buf_append_char(&t.cur_tok->data.str_lit.str, c);
+                    case DIGIT:
+                        break;
+                    case ALPHA_EXCEPT_E:
+                        invalid_char_error(&t, c);
                         break;
+                    default:
+                        t.state = TokenizeState_start;
+                        continue;
                 }
                 break;
-            case TokenizeStateStringEscape:
+            case TokenizeState_int_literal_hex_no_underscore:
                 switch (c) {
-                    case 'x':
-                        t.state = TokenizeStateCharCode;
-                        t.radix = 16;
-                        t.char_code = 0;
-                        t.char_code_index = 0;
-                        t.unicode = false;
+                    case HEXDIGIT:
+                        t.state = TokenizeState_int_literal_hex;
                         break;
-                    case 'u':
-                        t.state = TokenizeStateStringEscapeUnicodeStart;
-                        break;
-                    case 'n':
-                        handle_string_escape(&t, '\n');
-                        break;
-                    case 'r':
-                        handle_string_escape(&t, '\r');
+                    default:
+                        invalid_char_error(&t, c);
+                }
+                break;
+            case TokenizeState_int_literal_hex:
+                switch (c) {
+                    case 0:
+                        goto eof;
+                    case '_':
+                        t.state = TokenizeState_int_literal_hex_no_underscore;
                         break;
-                    case '\\':
-                        handle_string_escape(&t, '\\');
+                    case '.':
+                        t.state = TokenizeState_num_dot_hex;
+                        t.out->ids.last() = TokenIdFloatLiteral;
                         break;
-                    case 't':
-                        handle_string_escape(&t, '\t');
+                    case 'p':
+                    case 'P':
+                        t.state = TokenizeState_float_exponent_unsigned;
+                        t.out->ids.last() = TokenIdFloatLiteral;
                         break;
-                    case '\'':
-                        handle_string_escape(&t, '\'');
+                    case HEXDIGIT:
                         break;
-                    case '"':
-                        handle_string_escape(&t, '\"');
+                    case ALPHA_EXCEPT_HEX_AND_P:
+                        invalid_char_error(&t, c);
                         break;
                     default:
-                        invalid_char_error(&t, c);
+                        t.state = TokenizeState_start;
+                        continue;
                 }
                 break;
-            case TokenizeStateStringEscapeUnicodeStart:
+            case TokenizeState_num_dot_dec:
                 switch (c) {
-                    case '{':
-                        t.state = TokenizeStateCharCode;
-                        t.radix = 16;
-                        t.char_code = 0;
-                        t.char_code_index = 0;
-                        t.unicode = true;
+                    case 0:
+                        goto eof;
+                    case '.':
+                        t.out->ids.last() = TokenIdIntLiteral;
+                        t.pos -= 1;
+                        t.column -= 1;
+                        t.state = TokenizeState_start;
+                        continue;
+                    case 'e':
+                    case 'E':
+                        t.state = TokenizeState_float_exponent_unsigned;
                         break;
-                    default:
+                    case DIGIT:
+                        t.state = TokenizeState_float_fraction_dec;
+                        break;
+                    case ALPHA_EXCEPT_E:
                         invalid_char_error(&t, c);
+                        break;
+                    default:
+                        t.state = TokenizeState_start;
+                        continue;
                 }
                 break;
-            case TokenizeStateCharCode:
-                {
-                    if (t.unicode && c == '}') {
-                        if (t.char_code_index == 0) {
-                            tokenize_error(&t, "empty unicode escape sequence");
-                            break;
-                        }
-                        if (t.char_code > 0x10ffff) {
-                            tokenize_error(&t, "unicode value out of range: %x", t.char_code);
-                            break;
-                        }
-                        if (t.cur_tok->id == TokenIdCharLiteral) {
-                            t.cur_tok->data.char_lit.c = t.char_code;
-                            t.state = TokenizeStateCharLiteralEnd;
-                        } else if (t.char_code <= 0x7f) {
-                            // 00000000 00000000 00000000 0xxxxxxx
-                            handle_string_escape(&t, (uint8_t)t.char_code);
-                        } else if (t.char_code <= 0x7ff) {
-                            // 00000000 00000000 00000xxx xx000000
-                            handle_string_escape(&t, (uint8_t)(0xc0 | (t.char_code >> 6)));
-                            // 00000000 00000000 00000000 00xxxxxx
-                            handle_string_escape(&t, (uint8_t)(0x80 | (t.char_code & 0x3f)));
-                        } else if (t.char_code <= 0xffff) {
-                            // 00000000 00000000 xxxx0000 00000000
-                            handle_string_escape(&t, (uint8_t)(0xe0 | (t.char_code >> 12)));
-                            // 00000000 00000000 0000xxxx xx000000
-                            handle_string_escape(&t, (uint8_t)(0x80 | ((t.char_code >> 6) & 0x3f)));
-                            // 00000000 00000000 00000000 00xxxxxx
-                            handle_string_escape(&t, (uint8_t)(0x80 | (t.char_code & 0x3f)));
-                        } else if (t.char_code <= 0x10ffff) {
-                            // 00000000 000xxx00 00000000 00000000
-                            handle_string_escape(&t, (uint8_t)(0xf0 | (t.char_code >> 18)));
-                            // 00000000 000000xx xxxx0000 00000000
-                            handle_string_escape(&t, (uint8_t)(0x80 | ((t.char_code >> 12) & 0x3f)));
-                            // 00000000 00000000 0000xxxx xx000000
-                            handle_string_escape(&t, (uint8_t)(0x80 | ((t.char_code >> 6) & 0x3f)));
-                            // 00000000 00000000 00000000 00xxxxxx
-                            handle_string_escape(&t, (uint8_t)(0x80 | (t.char_code & 0x3f)));
-                        } else {
-                            zig_unreachable();
-                        }
+            case TokenizeState_num_dot_hex:
+                switch (c) {
+                    case 0:
+                        goto eof;
+                    case '.':
+                        t.out->ids.last() = TokenIdIntLiteral;
+                        t.pos -= 1;
+                        t.column -= 1;
+                        t.state = TokenizeState_start;
+                        continue;
+                    case 'p':
+                    case 'P':
+                        t.state = TokenizeState_float_exponent_unsigned;
                         break;
-                    }
-
-                    uint32_t digit_value = get_digit_value(c);
-                    if (digit_value >= t.radix) {
-                        tokenize_error(&t, "invalid digit: '%c'", c);
+                    case HEXDIGIT:
+                        t.out->ids.last() = TokenIdFloatLiteral;
+                        t.state = TokenizeState_float_fraction_hex;
                         break;
-                    }
-                    t.char_code *= t.radix;
-                    t.char_code += digit_value;
-                    t.char_code_index += 1;
-
-                    if (!t.unicode && t.char_code_index >= 2) {
-                        assert(t.char_code <= 255);
-                        handle_string_escape(&t, (uint8_t)t.char_code);
-                    }
-                }
-                break;
-            case TokenizeStateCharLiteral:
-                if (c == '\'') {
-                    tokenize_error(&t, "expected character");
-                } else if (c == '\\') {
-                    t.state = TokenizeStateStringEscape;
-                } else if ((c >= 0x80 && c <= 0xbf) || c >= 0xf8) {
-                    // 10xxxxxx
-                    // 11111xxx
-                    invalid_char_error(&t, c);
-                } else if (c >= 0xc0 && c <= 0xdf) {
-                    // 110xxxxx
-                    t.cur_tok->data.char_lit.c = c & 0x1f;
-                    t.remaining_code_units = 1;
-                    t.state = TokenizeStateCharLiteralUnicode;
-                } else if (c >= 0xe0 && c <= 0xef) {
-                    // 1110xxxx
-                    t.cur_tok->data.char_lit.c = c & 0x0f;
-                    t.remaining_code_units = 2;
-                    t.state = TokenizeStateCharLiteralUnicode;
-                } else if (c >= 0xf0 && c <= 0xf7) {
-                    // 11110xxx
-                    t.cur_tok->data.char_lit.c = c & 0x07;
-                    t.remaining_code_units = 3;
-                    t.state = TokenizeStateCharLiteralUnicode;
-                } else {
-                    t.cur_tok->data.char_lit.c = c;
-                    t.state = TokenizeStateCharLiteralEnd;
+                    case ALPHA_EXCEPT_HEX_AND_P:
+                        invalid_char_error(&t, c);
+                        break;
+                    default:
+                        t.state = TokenizeState_start;
+                        continue;
                 }
                 break;
-            case TokenizeStateCharLiteralEnd:
+            case TokenizeState_float_fraction_dec_no_underscore:
                 switch (c) {
-                    case '\'':
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                    case DIGIT:
+                        t.state = TokenizeState_float_fraction_dec;
                         break;
                     default:
                         invalid_char_error(&t, c);
                 }
                 break;
-            case TokenizeStateCharLiteralUnicode:
-                if (c <= 0x7f || c >= 0xc0) {
-                    invalid_char_error(&t, c);
-                }
-                t.cur_tok->data.char_lit.c <<= 6;
-                t.cur_tok->data.char_lit.c += c & 0x3f;
-                t.remaining_code_units--;
-                if (t.remaining_code_units == 0) {
-                    t.state = TokenizeStateCharLiteralEnd;
-                }
-                break;
-            case TokenizeStateZero:
+            case TokenizeState_float_fraction_dec:
                 switch (c) {
-                    case 'b':
-                        t.radix = 2;
-                        t.state = TokenizeStateNumberNoUnderscore;
+                    case 0:
+                        goto eof;
+                    case '_':
+                        t.state = TokenizeState_float_fraction_dec_no_underscore;
                         break;
-                    case 'o':
-                        t.radix = 8;
-                        t.state = TokenizeStateNumberNoUnderscore;
+                    case 'e':
+                    case 'E':
+                        t.state = TokenizeState_float_exponent_unsigned;
                         break;
-                    case 'x':
-                        t.radix = 16;
-                        t.state = TokenizeStateNumberNoUnderscore;
+                    case DIGIT:
+                        break;
+                    case ALPHA_EXCEPT_E:
+                        invalid_char_error(&t, c);
                         break;
                     default:
-                        // reinterpret as normal number
-                        t.pos -= 1;
-                        t.state = TokenizeStateNumber;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
-            case TokenizeStateNumberNoUnderscore:
-                if (c == '_') {
-                    invalid_char_error(&t, c);
-                    break;
-                } else if (get_digit_value(c) < t.radix) {
-                    t.is_trailing_underscore = false;
-                    t.state = TokenizeStateNumber;
+            case TokenizeState_float_fraction_hex_no_underscore:
+                switch (c) {
+                    case HEXDIGIT:
+                        t.state = TokenizeState_float_fraction_hex;
+                        break;
+                    default:
+                        invalid_char_error(&t, c);
                 }
-                ZIG_FALLTHROUGH;
-            case TokenizeStateNumber:
-                {
-                    if (c == '_') {
-                        t.is_trailing_underscore = true;
-                        t.state = TokenizeStateNumberNoUnderscore;
+                break;
+            case TokenizeState_float_fraction_hex:
+                switch (c) {
+                    case 0:
+                        goto eof;
+                    case '_':
+                        t.state = TokenizeState_float_fraction_hex_no_underscore;
                         break;
-                    }
-                    if (c == '.') {
-                        if (t.is_trailing_underscore) {
-                            invalid_char_error(&t, c);
-                            break;
-                        }
-                        t.state = TokenizeStateNumberDot;
+                    case 'p':
+                    case 'P':
+                        t.state = TokenizeState_float_exponent_unsigned;
                         break;
-                    }
-                    if (is_exponent_signifier(c, t.radix)) {
-                        if (t.is_trailing_underscore) {
-                            invalid_char_error(&t, c);
-                            break;
-                        }
-                        if (t.radix != 16 && t.radix != 10) {
-                            invalid_char_error(&t, c);
-                        }
-                        t.state = TokenizeStateFloatExponentUnsigned;
-                        t.radix = 10; // exponent is always base 10
-                        assert(t.cur_tok->id == TokenIdIntLiteral);
-                        set_token_id(&t, t.cur_tok, TokenIdFloatLiteral);
+                    case HEXDIGIT:
                         break;
-                    }
-                    uint32_t digit_value = get_digit_value(c);
-                    if (digit_value >= t.radix) {
-                        if (t.is_trailing_underscore) {
-                            invalid_char_error(&t, c);
-                            break;
-                        }
-
-                        if (is_symbol_char(c)) {
-                            invalid_char_error(&t, c);
-                        }
-                        // not my char
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        continue;
-                    }
-                    BigInt digit_value_bi;
-                    bigint_init_unsigned(&digit_value_bi, digit_value);
-
-                    BigInt radix_bi;
-                    bigint_init_unsigned(&radix_bi, t.radix);
-
-                    BigInt multiplied;
-                    bigint_mul(&multiplied, &t.cur_tok->data.int_lit.bigint, &radix_bi);
-
-                    bigint_add(&t.cur_tok->data.int_lit.bigint, &multiplied, &digit_value_bi);
-                    break;
-                }
-            case TokenizeStateNumberDot:
-                {
-                    if (c == '.') {
-                        t.pos -= 2;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        continue;
-                    }
-                    if (t.radix != 16 && t.radix != 10) {
+                    case ALPHA_EXCEPT_HEX_AND_P:
                         invalid_char_error(&t, c);
-                    }
-                    t.pos -= 1;
-                    t.state = TokenizeStateFloatFractionNoUnderscore;
-                    assert(t.cur_tok->id == TokenIdIntLiteral);
-                    set_token_id(&t, t.cur_tok, TokenIdFloatLiteral);
-                    continue;
-                }
-            case TokenizeStateFloatFractionNoUnderscore:
-                if (c == '_') {
-                    invalid_char_error(&t, c);
-                } else if (get_digit_value(c) < t.radix) {
-                    t.is_trailing_underscore = false;
-                    t.state = TokenizeStateFloatFraction;
-                }
-                ZIG_FALLTHROUGH;
-            case TokenizeStateFloatFraction:
-                {
-                    if (c == '_') {
-                        t.is_trailing_underscore = true;
-                        t.state = TokenizeStateFloatFractionNoUnderscore;
-                        break;
-                    }
-                    if (is_exponent_signifier(c, t.radix)) {
-                        if (t.is_trailing_underscore) {
-                            invalid_char_error(&t, c);
-                            break;
-                        }
-                        t.state = TokenizeStateFloatExponentUnsigned;
-                        t.radix = 10; // exponent is always base 10
                         break;
-                    }
-                    uint32_t digit_value = get_digit_value(c);
-                    if (digit_value >= t.radix) {
-                        if (t.is_trailing_underscore) {
-                            invalid_char_error(&t, c);
-                            break;
-                        }
-                        if (is_symbol_char(c)) {
-                            invalid_char_error(&t, c);
-                        }
-                        // not my char
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                    default:
+                        t.state = TokenizeState_start;
                         continue;
-                    }
-
-                    // we use parse_f128 to generate the float literal, so just
-                    // need to get to the end of the token
                 }
                 break;
-            case TokenizeStateFloatExponentUnsigned:
+            case TokenizeState_float_exponent_unsigned:
                 switch (c) {
                     case '+':
-                        t.state = TokenizeStateFloatExponentNumberNoUnderscore;
-                        break;
                     case '-':
-                        t.state = TokenizeStateFloatExponentNumberNoUnderscore;
+                        t.state = TokenizeState_float_exponent_num_no_underscore;
                         break;
                     default:
-                        // reinterpret as normal exponent number
-                        t.pos -= 1;
-                        t.state = TokenizeStateFloatExponentNumberNoUnderscore;
-                        continue;
-                }
-                break;
-            case TokenizeStateFloatExponentNumberNoUnderscore:
-                if (c == '_') {
-                    invalid_char_error(&t, c);
-                } else if (get_digit_value(c) < t.radix) {
-                    t.is_trailing_underscore = false;
-                    t.state = TokenizeStateFloatExponentNumber;
-                }
-                ZIG_FALLTHROUGH;
-            case TokenizeStateFloatExponentNumber:
-                {
-                    if (c == '_') {
-                        t.is_trailing_underscore = true;
-                        t.state = TokenizeStateFloatExponentNumberNoUnderscore;
-                        break;
-                    }
-                    uint32_t digit_value = get_digit_value(c);
-                    if (digit_value >= t.radix) {
-                        if (t.is_trailing_underscore) {
-                            invalid_char_error(&t, c);
-                            break;
-                        }
-                        if (is_symbol_char(c)) {
-                            invalid_char_error(&t, c);
-                        }
-                        // not my char
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        // Reinterpret as a normal exponent number.
+                        t.state = TokenizeState_float_exponent_num_no_underscore;
                         continue;
-                    }
-
-                    // we use parse_f128 to generate the float literal, so just
-                    // need to get to the end of the token
                 }
                 break;
-            case TokenizeStateSawDash:
+            case TokenizeState_float_exponent_num_no_underscore:
                 switch (c) {
-                    case '>':
-                        set_token_id(&t, t.cur_tok, TokenIdArrow);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        break;
-                    case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdMinusEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        break;
-                    case '%':
-                        set_token_id(&t, t.cur_tok, TokenIdMinusPercent);
-                        t.state = TokenizeStateSawMinusPercent;
+                    case DIGIT:
+                        t.state = TokenizeState_float_exponent_num;
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
-                        continue;
+                        invalid_char_error(&t, c);
                 }
                 break;
-            case TokenizeStateSawMinusPercent:
+            case TokenizeState_float_exponent_num:
                 switch (c) {
-                    case '=':
-                        set_token_id(&t, t.cur_tok, TokenIdMinusPercentEq);
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                    case 0:
+                        goto eof;
+                    case '_':
+                        t.state = TokenizeState_float_exponent_num_no_underscore;
+                        break;
+                    case DIGIT:
+                        break;
+                    case ALPHA:
+                        invalid_char_error(&t, c);
                         break;
                     default:
-                        t.pos -= 1;
-                        end_token(&t);
-                        t.state = TokenizeStateStart;
+                        t.state = TokenizeState_start;
                         continue;
                 }
                 break;
         }
+        t.pos += 1;
         if (c == '\n') {
-            out->line_offsets->append(t.pos + 1);
             t.line += 1;
             t.column = 0;
         } else {
             t.column += 1;
         }
     }
-    // EOF
-    switch (t.state) {
-        case TokenizeStateStart:
-        case TokenizeStateError:
-            break;
-        case TokenizeStateNumberNoUnderscore:
-        case TokenizeStateFloatFractionNoUnderscore:
-        case TokenizeStateFloatExponentNumberNoUnderscore:
-        case TokenizeStateNumberDot:
-            tokenize_error(&t, "unterminated number literal");
-            break;
-        case TokenizeStateString:
-            tokenize_error(&t, "unterminated string");
-            break;
-        case TokenizeStateStringEscape:
-        case TokenizeStateStringEscapeUnicodeStart:
-        case TokenizeStateCharCode:
-            if (t.cur_tok->id == TokenIdStringLiteral) {
-                tokenize_error(&t, "unterminated string");
-                break;
-            } else if (t.cur_tok->id == TokenIdCharLiteral) {
-                tokenize_error(&t, "unterminated Unicode code point literal");
-                break;
-            } else {
-                zig_unreachable();
-            }
-            break;
-        case TokenizeStateCharLiteral:
-        case TokenizeStateCharLiteralEnd:
-        case TokenizeStateCharLiteralUnicode:
-            tokenize_error(&t, "unterminated Unicode code point literal");
-            break;
-        case TokenizeStateSymbol:
-        case TokenizeStateZero:
-        case TokenizeStateNumber:
-        case TokenizeStateFloatFraction:
-        case TokenizeStateFloatExponentUnsigned:
-        case TokenizeStateFloatExponentNumber:
-        case TokenizeStateSawStar:
-        case TokenizeStateSawSlash:
-        case TokenizeStateSawPercent:
-        case TokenizeStateSawPlus:
-        case TokenizeStateSawDash:
-        case TokenizeStateSawAmpersand:
-        case TokenizeStateSawCaret:
-        case TokenizeStateSawBar:
-        case TokenizeStateSawEq:
-        case TokenizeStateSawBang:
-        case TokenizeStateSawLessThan:
-        case TokenizeStateSawLessThanLessThan:
-        case TokenizeStateSawGreaterThan:
-        case TokenizeStateSawGreaterThanGreaterThan:
-        case TokenizeStateSawDot:
-        case TokenizeStateSawDotStar:
-        case TokenizeStateSawAtSign:
-        case TokenizeStateSawStarPercent:
-        case TokenizeStateSawPlusPercent:
-        case TokenizeStateSawMinusPercent:
-        case TokenizeStateLineString:
-        case TokenizeStateLineStringEnd:
-        case TokenizeStateDocComment:
-        case TokenizeStateContainerDocComment:
-            end_token(&t);
-            break;
-        case TokenizeStateSawDotDot:
-        case TokenizeStateSawBackslash:
-        case TokenizeStateLineStringContinue:
-            tokenize_error(&t, "unexpected EOF");
-            break;
-        case TokenizeStateLineComment:
-            break;
-        case TokenizeStateSawSlash2:
-            cancel_token(&t);
-            break;
-        case TokenizeStateSawSlash3:
-            set_token_id(&t, t.cur_tok, TokenIdDocComment);
-            end_token(&t);
-            break;
-        case TokenizeStateSawSlashBang:
-            set_token_id(&t, t.cur_tok, TokenIdContainerDocComment);
-            end_token(&t);
-            break;
-    }
-    if (t.state != TokenizeStateError) {
-        if (t.tokens->length > 0) {
-            Token *last_token = &t.tokens->last();
-            t.line = (int)last_token->start_line;
-            t.column = (int)last_token->start_column;
-            t.pos = last_token->start_pos;
-        } else {
-            t.pos = 0;
-        }
-        begin_token(&t, TokenIdEof);
-        end_token(&t);
-        assert(!t.cur_tok);
-    }
+eof:;
+
+    begin_token(&t, TokenIdEof);
 }
 
 const char * token_name(TokenId id) {
     switch (id) {
         case TokenIdAmpersand: return "&";
         case TokenIdArrow: return "->";
-        case TokenIdAtSign: return "@";
         case TokenIdBang: return "!";
         case TokenIdBarBar: return "||";
         case TokenIdBinOr: return "|";
@@ -1622,9 +1533,7 @@ const char * token_name(TokenId id) {
         case TokenIdMinusPercent: return "-%";
         case TokenIdMinusPercentEq: return "-%=";
         case TokenIdModEq: return "%=";
-        case TokenIdNumberSign: return "#";
         case TokenIdPercent: return "%";
-        case TokenIdPercentDot: return "%.";
         case TokenIdPlus: return "+";
         case TokenIdPlusEq: return "+=";
         case TokenIdPlusPercent: return "+%";
@@ -1638,33 +1547,15 @@ const char * token_name(TokenId id) {
         case TokenIdStar: return "*";
         case TokenIdStarStar: return "**";
         case TokenIdStringLiteral: return "StringLiteral";
-        case TokenIdMultilineStringLiteral: return "MultilineStringLiteral";
-        case TokenIdSymbol: return "Symbol";
+        case TokenIdMultilineStringLiteralLine: return "MultilineStringLiteralLine";
+        case TokenIdIdentifier: return "Identifier";
         case TokenIdTilde: return "~";
         case TokenIdTimesEq: return "*=";
         case TokenIdTimesPercent: return "*%";
         case TokenIdTimesPercentEq: return "*%=";
+        case TokenIdBuiltin: return "Builtin";
         case TokenIdCount:
             zig_unreachable();
     }
     return "(invalid token)";
 }
-
-void print_tokens(Buf *buf, ZigList<Token> *tokens) {
-    for (size_t i = 0; i < tokens->length; i += 1) {
-        Token *token = &tokens->at(i);
-        fprintf(stderr, "%s ", token_name(token->id));
-        if (token->start_pos != SIZE_MAX) {
-            fwrite(buf_ptr(buf) + token->start_pos, 1, token->end_pos - token->start_pos, stderr);
-        }
-        fprintf(stderr, "\n");
-    }
-}
-
-bool valid_symbol_starter(uint8_t c) {
-    switch (c) {
-        case SYMBOL_START:
-            return true;
-    }
-    return false;
-}
src/stage1/tokenizer.hpp
@@ -12,10 +12,9 @@
 #include "bigint.hpp"
 #include "bigfloat.hpp"
 
-enum TokenId {
+enum TokenId : uint8_t {
     TokenIdAmpersand,
     TokenIdArrow,
-    TokenIdAtSign,
     TokenIdBang,
     TokenIdBarBar,
     TokenIdBinOr,
@@ -27,6 +26,7 @@ enum TokenId {
     TokenIdBitShiftRight,
     TokenIdBitShiftRightEq,
     TokenIdBitXorEq,
+    TokenIdBuiltin,
     TokenIdCharLiteral,
     TokenIdCmpEq,
     TokenIdCmpGreaterOrEq,
@@ -109,9 +109,7 @@ enum TokenId {
     TokenIdMinusPercent,
     TokenIdMinusPercentEq,
     TokenIdModEq,
-    TokenIdNumberSign,
     TokenIdPercent,
-    TokenIdPercentDot,
     TokenIdPlus,
     TokenIdPlusEq,
     TokenIdPlusPercent,
@@ -125,75 +123,35 @@ enum TokenId {
     TokenIdStar,
     TokenIdStarStar,
     TokenIdStringLiteral,
-    TokenIdMultilineStringLiteral,
-    TokenIdSymbol,
+    TokenIdMultilineStringLiteralLine,
+    TokenIdIdentifier,
     TokenIdTilde,
     TokenIdTimesEq,
     TokenIdTimesPercent,
     TokenIdTimesPercentEq,
-    TokenIdCount,
-};
-
-struct TokenFloatLit {
-    BigFloat bigfloat;
-    // overflow is true if when parsing the number, we discovered it would not fit
-    // without losing data
-    bool overflow;
-};
-
-struct TokenIntLit {
-    BigInt bigint;
-};
-
-struct TokenStrLit {
-    Buf str;
-};
 
-struct TokenCharLit {
-    uint32_t c;
+    TokenIdCount,
 };
 
-struct Token {
-    TokenId id;
-    size_t start_pos;
-    size_t end_pos;
-    size_t start_line;
-    size_t start_column;
-
-    union {
-        // TokenIdIntLiteral
-        TokenIntLit int_lit;
-
-        // TokenIdFloatLiteral
-        TokenFloatLit float_lit;
+typedef uint32_t TokenIndex;
 
-        // TokenIdStringLiteral, TokenIdMultilineStringLiteral or TokenIdSymbol
-        TokenStrLit str_lit;
-
-        // TokenIdCharLiteral
-        TokenCharLit char_lit;
-    } data;
+struct TokenLoc {
+    uint32_t offset;
+    uint32_t line;
+    uint32_t column;
 };
-// work around conflicting name Token which is also found in libclang
-typedef Token ZigToken;
 
 struct Tokenization {
-    ZigList<Token> *tokens;
-    ZigList<size_t> *line_offsets;
+    ZigList<TokenId> ids;
+    ZigList<TokenLoc> locs;
 
     // if an error occurred
     Buf *err;
-    size_t err_line;
-    size_t err_column;
+    uint32_t err_byte_offset;
 };
 
-void tokenize(Buf *buf, Tokenization *out_tokenization);
-
-void print_tokens(Buf *buf, ZigList<Token> *tokens);
+void tokenize(const char *source, Tokenization *out_tokenization);
 
 const char * token_name(TokenId id);
 
-bool valid_symbol_starter(uint8_t c);
-bool is_zig_keyword(Buf *buf);
-
 #endif
src/stage1.zig
@@ -252,6 +252,8 @@ const Error = extern enum {
     ZigIsTheCCompiler,
     FileBusy,
     Locked,
+    InvalidCharacter,
+    UnicodePointTooLarge,
 };
 
 // ABI warning
test/behavior/misc.zig
@@ -1,6 +1,7 @@
 const std = @import("std");
 const expect = std.testing.expect;
 const expectEqualSlices = std.testing.expectEqualSlices;
+const expectEqualStrings = std.testing.expectEqualStrings;
 const mem = std.mem;
 const builtin = @import("builtin");
 
@@ -147,13 +148,13 @@ test "array mult operator" {
 }
 
 test "string escapes" {
-    try expect(mem.eql(u8, "\"", "\x22"));
-    try expect(mem.eql(u8, "\'", "\x27"));
-    try expect(mem.eql(u8, "\n", "\x0a"));
-    try expect(mem.eql(u8, "\r", "\x0d"));
-    try expect(mem.eql(u8, "\t", "\x09"));
-    try expect(mem.eql(u8, "\\", "\x5c"));
-    try expect(mem.eql(u8, "\u{1234}\u{069}\u{1}", "\xe1\x88\xb4\x69\x01"));
+    try expectEqualStrings("\"", "\x22");
+    try expectEqualStrings("\'", "\x27");
+    try expectEqualStrings("\n", "\x0a");
+    try expectEqualStrings("\r", "\x0d");
+    try expectEqualStrings("\t", "\x09");
+    try expectEqualStrings("\\", "\x5c");
+    try expectEqualStrings("\u{1234}\u{069}\u{1}", "\xe1\x88\xb4\x69\x01");
 }
 
 test "multiline string" {
CMakeLists.txt
@@ -292,7 +292,7 @@ set(ZIG0_SOURCES
 
 set(STAGE1_SOURCES
     "${CMAKE_SOURCE_DIR}/src/stage1/analyze.cpp"
-    "${CMAKE_SOURCE_DIR}/src/stage1/ast_render.cpp"
+    "${CMAKE_SOURCE_DIR}/src/stage1/astgen.cpp"
     "${CMAKE_SOURCE_DIR}/src/stage1/bigfloat.cpp"
     "${CMAKE_SOURCE_DIR}/src/stage1/bigint.cpp"
     "${CMAKE_SOURCE_DIR}/src/stage1/buffer.cpp"
@@ -307,11 +307,11 @@ set(STAGE1_SOURCES
     "${CMAKE_SOURCE_DIR}/src/stage1/os.cpp"
     "${CMAKE_SOURCE_DIR}/src/stage1/parser.cpp"
     "${CMAKE_SOURCE_DIR}/src/stage1/range_set.cpp"
+    "${CMAKE_SOURCE_DIR}/src/stage1/softfloat_ext.cpp"
     "${CMAKE_SOURCE_DIR}/src/stage1/stage1.cpp"
     "${CMAKE_SOURCE_DIR}/src/stage1/target.cpp"
     "${CMAKE_SOURCE_DIR}/src/stage1/tokenizer.cpp"
     "${CMAKE_SOURCE_DIR}/src/stage1/util.cpp"
-    "${CMAKE_SOURCE_DIR}/src/stage1/softfloat_ext.cpp"
 )
 set(OPTIMIZED_C_SOURCES
     "${CMAKE_SOURCE_DIR}/src/stage1/parse_f128.c"