Commit 9d19b8d66e

Andrew Kelley <superjoe30@gmail.com>
2016-11-10 05:21:02
IR: move unused codegen code to commented out in bottom of ir.cpp
1 parent b8379b4
Changed files (2)
src/codegen.cpp
@@ -22,7 +22,6 @@
 #include <stdio.h>
 #include <errno.h>
 
-
 static void init_darwin_native(CodeGen *g) {
     char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET");
     char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET");
@@ -227,27 +226,8 @@ void codegen_set_rdynamic(CodeGen *g, bool rdynamic) {
     g->linker_rdynamic = rdynamic;
 }
 
-static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node);
-static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node, TypeTableEntry **out_type_entry);
-static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue);
-static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl,
-        bool unwrap_maybe, LLVMValueRef *init_val, TypeTableEntry **init_val_type, bool var_is_ptr);
-static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op,
-        LLVMValueRef target_ref, LLVMValueRef value,
-        TypeTableEntry *op1_type, TypeTableEntry *op2_type);
-static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref);
-static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2,
-        TypeTableEntry *type_entry, bool exact);
 static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val);
 
-static TypeTableEntry *get_type_for_type_node(AstNode *node) {
-    Expr *expr = get_resolved_expr(node);
-    assert(expr->type_entry->id == TypeTableEntryIdMetaType);
-    ConstExprValue *const_val = &expr->const_val;
-    assert(const_val->ok);
-    return const_val->data.x_type;
-}
-
 static void set_debug_source_node(CodeGen *g, AstNode *node) {
     assert(node->block_context);
     ZigLLVMSetCurrentDebugLocation(g->builder, node->line + 1, node->column + 1, node->block_context->di_scope);
@@ -257,10 +237,6 @@ static void clear_debug_source_node(CodeGen *g) {
     ZigLLVMClearCurrentDebugLocation(g->builder);
 }
 
-static TypeTableEntry *get_expr_type(AstNode *node) {
-    return get_resolved_expr(node)->type_entry;
-}
-
 enum AddSubMul {
     AddSubMulAdd = 0,
     AddSubMulSub = 1,
@@ -329,24 +305,6 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
     return *fn;
 }
 
-static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, BuiltinFnId fn_id) {
-    // [0-ctz,1-clz][0-8,1-16,2-32,3-64]
-    size_t index0 = (fn_id == BuiltinFnIdCtz) ? 0 : 1;
-    size_t index1 = bits_index(int_type->data.integral.bit_count);
-    LLVMValueRef *fn = &g->int_builtin_fns[index0][index1];
-    if (!*fn) {
-        const char *fn_name = (fn_id == BuiltinFnIdCtz) ? "cttz" : "ctlz";
-        Buf *llvm_name = buf_sprintf("llvm.%s.i%zu", fn_name, int_type->data.integral.bit_count);
-        LLVMTypeRef param_types[] = {
-            int_type->type_ref,
-            LLVMInt1Type(),
-        };
-        LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, 2, false);
-        *fn = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type);
-    }
-    return *fn;
-}
-
 static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntry *type) {
     if (handle_is_ptr(type)) {
         return ptr;
@@ -413,349 +371,6 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val,
     LLVMPositionBuilderAtEnd(g->builder, ok_block);
 }
 
-static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeFnCallExpr);
-    assert(g->generate_error_name_table);
-
-    if (g->error_decls.length == 1) {
-        LLVMBuildUnreachable(g->builder);
-        return nullptr;
-    }
-
-
-    AstNode *err_val_node = node->data.fn_call_expr.params.at(0);
-    LLVMValueRef err_val = gen_expr(g, err_val_node);
-
-    if (want_debug_safety(g, node)) {
-        LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val));
-        LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->error_decls.length, false);
-        add_bounds_check(g, err_val, LLVMIntNE, zero, LLVMIntULT, end_val);
-    }
-
-    LLVMValueRef indices[] = {
-        LLVMConstNull(g->builtin_types.entry_usize->type_ref),
-        err_val,
-    };
-    return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, "");
-}
-
-static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
-    switch (atomic_order) {
-        case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered;
-        case AtomicOrderMonotonic: return LLVMAtomicOrderingMonotonic;
-        case AtomicOrderAcquire: return LLVMAtomicOrderingAcquire;
-        case AtomicOrderRelease: return LLVMAtomicOrderingRelease;
-        case AtomicOrderAcqRel: return LLVMAtomicOrderingAcquireRelease;
-        case AtomicOrderSeqCst: return LLVMAtomicOrderingSequentiallyConsistent;
-    }
-    zig_unreachable();
-}
-
-static LLVMValueRef gen_cmp_exchange(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeFnCallExpr);
-
-    AstNode *ptr_arg = node->data.fn_call_expr.params.at(0);
-    AstNode *cmp_arg = node->data.fn_call_expr.params.at(1);
-    AstNode *new_arg = node->data.fn_call_expr.params.at(2);
-    AstNode *success_order_arg = node->data.fn_call_expr.params.at(3);
-    AstNode *failure_order_arg = node->data.fn_call_expr.params.at(4);
-
-    LLVMValueRef ptr_val = gen_expr(g, ptr_arg);
-    LLVMValueRef cmp_val = gen_expr(g, cmp_arg);
-    LLVMValueRef new_val = gen_expr(g, new_arg);
-
-    ConstExprValue *success_order_val = &get_resolved_expr(success_order_arg)->const_val;
-    ConstExprValue *failure_order_val = &get_resolved_expr(failure_order_arg)->const_val;
-
-    assert(success_order_val->ok);
-    assert(failure_order_val->ok);
-
-    LLVMAtomicOrdering success_order = to_LLVMAtomicOrdering((AtomicOrder)success_order_val->data.x_enum.tag);
-    LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering((AtomicOrder)failure_order_val->data.x_enum.tag);
-
-    LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,
-            success_order, failure_order);
-
-    return LLVMBuildExtractValue(g->builder, result_val, 1, "");
-}
-
-static LLVMValueRef gen_fence(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeFnCallExpr);
-
-    AstNode *atomic_order_arg = node->data.fn_call_expr.params.at(0);
-    ConstExprValue *atomic_order_val = &get_resolved_expr(atomic_order_arg)->const_val;
-
-    assert(atomic_order_val->ok);
-
-    LLVMAtomicOrdering atomic_order = to_LLVMAtomicOrdering((AtomicOrder)atomic_order_val->data.x_enum.tag);
-
-    LLVMBuildFence(g->builder, atomic_order, false, "");
-    return nullptr;
-}
-
-static LLVMValueRef gen_div_exact(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeFnCallExpr);
-
-    AstNode *op1_node = node->data.fn_call_expr.params.at(0);
-    AstNode *op2_node = node->data.fn_call_expr.params.at(1);
-
-    LLVMValueRef op1_val = gen_expr(g, op1_node);
-    LLVMValueRef op2_val = gen_expr(g, op2_node);
-
-    return gen_div(g, node, op1_val, op2_val, get_expr_type(op1_node), true);
-}
-
-static LLVMValueRef gen_truncate(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeFnCallExpr);
-
-    TypeTableEntry *dest_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
-    AstNode *src_node = node->data.fn_call_expr.params.at(1);
-
-    LLVMValueRef src_val = gen_expr(g, src_node);
-
-    return LLVMBuildTrunc(g->builder, src_val, dest_type->type_ref, "");
-}
-
-static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeFnCallExpr);
-
-    size_t fn_call_param_count = node->data.fn_call_expr.params.length;
-    assert(fn_call_param_count == 4);
-
-    TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
-    assert(int_type->id == TypeTableEntryIdInt);
-
-    LLVMValueRef val1 = gen_expr(g, node->data.fn_call_expr.params.at(1));
-    LLVMValueRef val2 = gen_expr(g, node->data.fn_call_expr.params.at(2));
-    LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3));
-
-    LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, "");
-    LLVMValueRef orig_val;
-    if (int_type->data.integral.is_signed) {
-        orig_val = LLVMBuildAShr(g->builder, result, val2, "");
-    } else {
-        orig_val = LLVMBuildLShr(g->builder, result, val2, "");
-    }
-    LLVMValueRef overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, val1, orig_val, "");
-
-    LLVMBuildStore(g->builder, result, ptr_result);
-
-    return overflow_bit;
-}
-
-static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeFnCallExpr);
-    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-    assert(fn_ref_expr->type == NodeTypeSymbol);
-    BuiltinFnEntry *builtin_fn = node->data.fn_call_expr.builtin_fn;
-
-    switch (builtin_fn->id) {
-        case BuiltinFnIdInvalid:
-        case BuiltinFnIdTypeof:
-        case BuiltinFnIdCInclude:
-        case BuiltinFnIdCDefine:
-        case BuiltinFnIdCUndef:
-        case BuiltinFnIdImport:
-        case BuiltinFnIdCImport:
-        case BuiltinFnIdCompileErr:
-        case BuiltinFnIdIntType:
-            zig_unreachable();
-        case BuiltinFnIdCtz:
-        case BuiltinFnIdClz:
-            {
-                size_t fn_call_param_count = node->data.fn_call_expr.params.length;
-                assert(fn_call_param_count == 2);
-                TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
-                assert(int_type->id == TypeTableEntryIdInt);
-                LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, builtin_fn->id);
-                LLVMValueRef operand = gen_expr(g, node->data.fn_call_expr.params.at(1));
-                LLVMValueRef params[] {
-                    operand,
-                    LLVMConstNull(LLVMInt1Type()),
-                };
-                return LLVMBuildCall(g->builder, fn_val, params, 2, "");
-            }
-        case BuiltinFnIdAddWithOverflow:
-        case BuiltinFnIdSubWithOverflow:
-        case BuiltinFnIdMulWithOverflow:
-            {
-                size_t fn_call_param_count = node->data.fn_call_expr.params.length;
-                assert(fn_call_param_count == 4);
-
-                TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
-                AddSubMul add_sub_mul;
-                if (builtin_fn->id == BuiltinFnIdAddWithOverflow) {
-                    add_sub_mul = AddSubMulAdd;
-                } else if (builtin_fn->id == BuiltinFnIdSubWithOverflow) {
-                    add_sub_mul = AddSubMulSub;
-                } else if (builtin_fn->id == BuiltinFnIdMulWithOverflow) {
-                    add_sub_mul = AddSubMulMul;
-                } else {
-                    zig_unreachable();
-                }
-                LLVMValueRef fn_val = get_int_overflow_fn(g, int_type, add_sub_mul);
-
-                LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(1));
-                LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(2));
-                LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3));
-
-                LLVMValueRef params[] = {
-                    op1,
-                    op2,
-                };
-
-                LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
-                LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
-                LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
-                LLVMBuildStore(g->builder, result, ptr_result);
-
-                return overflow_bit;
-            }
-        case BuiltinFnIdShlWithOverflow:
-            return gen_shl_with_overflow(g, node);
-        case BuiltinFnIdMemcpy:
-            {
-                size_t fn_call_param_count = node->data.fn_call_expr.params.length;
-                assert(fn_call_param_count == 3);
-
-                AstNode *dest_node = node->data.fn_call_expr.params.at(0);
-                TypeTableEntry *dest_type = get_expr_type(dest_node);
-
-                LLVMValueRef dest_ptr = gen_expr(g, dest_node);
-                LLVMValueRef src_ptr = gen_expr(g, node->data.fn_call_expr.params.at(1));
-                LLVMValueRef len_val = gen_expr(g, node->data.fn_call_expr.params.at(2));
-
-                LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
-
-                LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
-                LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, ptr_u8, "");
-
-                uint64_t align_in_bytes = get_memcpy_align(g, dest_type->data.pointer.child_type);
-
-                LLVMValueRef params[] = {
-                    dest_ptr_casted, // dest pointer
-                    src_ptr_casted, // source pointer
-                    len_val, // byte count
-                    LLVMConstInt(LLVMInt32Type(), align_in_bytes, false), // align in bytes
-                    LLVMConstNull(LLVMInt1Type()), // is volatile
-                };
-
-                LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 5, "");
-                return nullptr;
-            }
-        case BuiltinFnIdMemset:
-            {
-                size_t fn_call_param_count = node->data.fn_call_expr.params.length;
-                assert(fn_call_param_count == 3);
-
-                AstNode *dest_node = node->data.fn_call_expr.params.at(0);
-                TypeTableEntry *dest_type = get_expr_type(dest_node);
-
-                LLVMValueRef dest_ptr = gen_expr(g, dest_node);
-                LLVMValueRef char_val = gen_expr(g, node->data.fn_call_expr.params.at(1));
-                LLVMValueRef len_val = gen_expr(g, node->data.fn_call_expr.params.at(2));
-
-                LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
-
-                LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
-
-                uint64_t align_in_bytes = get_memcpy_align(g, dest_type->data.pointer.child_type);
-
-                LLVMValueRef params[] = {
-                    dest_ptr_casted, // dest pointer
-                    char_val, // source pointer
-                    len_val, // byte count
-                    LLVMConstInt(LLVMInt32Type(), align_in_bytes, false), // align in bytes
-                    LLVMConstNull(LLVMInt1Type()), // is volatile
-                };
-
-                LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 5, "");
-                return nullptr;
-            }
-        case BuiltinFnIdSizeof:
-        case BuiltinFnIdAlignof:
-        case BuiltinFnIdMinValue:
-        case BuiltinFnIdMaxValue:
-        case BuiltinFnIdMemberCount:
-        case BuiltinFnIdConstEval:
-        case BuiltinFnIdEmbedFile:
-            // caught by constant expression eval codegen
-            zig_unreachable();
-        case BuiltinFnIdCompileVar:
-            return nullptr;
-        case BuiltinFnIdErrName:
-            return gen_err_name(g, node);
-        case BuiltinFnIdBreakpoint:
-            return LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
-        case BuiltinFnIdFrameAddress:
-        case BuiltinFnIdReturnAddress:
-            {
-                LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref);
-                return LLVMBuildCall(g->builder, builtin_fn->fn_val, &zero, 1, "");
-            }
-        case BuiltinFnIdCmpExchange:
-            return gen_cmp_exchange(g, node);
-        case BuiltinFnIdFence:
-            return gen_fence(g, node);
-        case BuiltinFnIdDivExact:
-            return gen_div_exact(g, node);
-        case BuiltinFnIdTruncate:
-            return gen_truncate(g, node);
-        case BuiltinFnIdUnreachable:
-            zig_panic("moved to ir render");
-        case BuiltinFnIdSetFnTest:
-        case BuiltinFnIdSetFnVisible:
-        case BuiltinFnIdSetFnStaticEval:
-        case BuiltinFnIdSetFnNoInline:
-        case BuiltinFnIdSetDebugSafety:
-            // do nothing
-            return nullptr;
-    }
-    zig_unreachable();
-}
-
-static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntry *enum_type,
-        AstNode *arg_node)
-{
-    assert(node->type == NodeTypeFieldAccessExpr);
-
-    uint64_t value = node->data.field_access_expr.type_enum_field->value;
-    LLVMTypeRef tag_type_ref = enum_type->data.enumeration.tag_type->type_ref;
-    LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, value, false);
-
-    if (enum_type->data.enumeration.gen_field_count == 0) {
-        return tag_value;
-    } else {
-        TypeTableEntry *arg_node_type = nullptr;
-        LLVMValueRef new_union_val = gen_expr(g, arg_node);
-        if (arg_node) {
-            arg_node_type = get_expr_type(arg_node);
-        } else {
-            arg_node_type = g->builtin_types.entry_void;
-        }
-
-        LLVMValueRef tmp_struct_ptr = node->data.field_access_expr.resolved_struct_val_expr.ptr;
-
-        // populate the new tag value
-        LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
-        LLVMBuildStore(g->builder, tag_value, tag_field_ptr);
-
-        if (arg_node_type->id != TypeTableEntryIdVoid) {
-            // populate the union value
-            TypeTableEntry *union_val_type = get_expr_type(arg_node);
-            LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
-            LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr,
-                    LLVMPointerType(union_val_type->type_ref, 0), "");
-
-            gen_assign_raw(g, arg_node, BinOpTypeAssign, bitcasted_union_field_ptr, new_union_val,
-                    union_val_type, union_val_type);
-
-        }
-
-        return tmp_struct_ptr;
-    }
-}
-
 static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeTableEntry *actual_type_non_canon,
         TypeTableEntry *wanted_type_non_canon, LLVMValueRef expr_val)
 {
@@ -839,111 +454,6 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT
     }
 }
 
-static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeFnCallExpr);
-
-    if (node->data.fn_call_expr.is_builtin) {
-        return gen_builtin_fn_call_expr(g, node);
-    }
-
-    FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
-    TypeTableEntry *struct_type = nullptr;
-    AstNode *first_param_expr = nullptr;
-
-    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-    if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
-        fn_ref_expr->data.field_access_expr.is_member_fn)
-    {
-        first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr;
-        struct_type = get_expr_type(first_param_expr);
-    }
-
-    TypeTableEntry *fn_type;
-    LLVMValueRef fn_val;
-    AstNode *generic_proto_node;
-    if (fn_table_entry) {
-        fn_val = fn_table_entry->fn_value;
-        fn_type = fn_table_entry->type_entry;
-        generic_proto_node = fn_table_entry->proto_node->data.fn_proto.generic_proto_node;
-    } else {
-        fn_val = gen_expr(g, fn_ref_expr);
-        fn_type = get_expr_type(fn_ref_expr);
-        generic_proto_node = nullptr;
-    }
-
-    TypeTableEntry *src_return_type = fn_type->data.fn.fn_type_id.return_type;
-
-    bool ret_has_bits = type_has_bits(src_return_type);
-
-    size_t fn_call_param_count = node->data.fn_call_expr.params.length;
-    bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
-    size_t actual_param_count = fn_call_param_count + (struct_type ? 1 : 0) + (first_arg_ret ? 1 : 0);
-    bool is_var_args = fn_type->data.fn.fn_type_id.is_var_args;
-
-    // don't really include void values
-    LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);
-
-    size_t gen_param_index = 0;
-    if (first_arg_ret) {
-        gen_param_values[gen_param_index] = node->data.fn_call_expr.tmp_ptr;
-        gen_param_index += 1;
-    }
-    if (struct_type && type_has_bits(struct_type)) {
-        gen_param_values[gen_param_index] = gen_expr(g, first_param_expr);
-        assert(gen_param_values[gen_param_index]);
-        gen_param_index += 1;
-    }
-
-    for (size_t call_i = 0; call_i < fn_call_param_count; call_i += 1) {
-        size_t proto_i = call_i + (struct_type ? 1 : 0);
-        if (generic_proto_node &&
-            generic_proto_node->data.fn_proto.params.at(proto_i)->data.param_decl.is_inline)
-        {
-            continue;
-        }
-        AstNode *expr_node = node->data.fn_call_expr.params.at(call_i);
-        LLVMValueRef param_value = gen_expr(g, expr_node);
-        assert(param_value);
-        TypeTableEntry *param_type = get_expr_type(expr_node);
-        if (is_var_args || type_has_bits(param_type)) {
-            gen_param_values[gen_param_index] = param_value;
-            gen_param_index += 1;
-        }
-    }
-
-    LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val,
-            gen_param_values, gen_param_index, fn_type->data.fn.calling_convention, "");
-
-    if (src_return_type->id == TypeTableEntryIdUnreachable) {
-        return LLVMBuildUnreachable(g->builder);
-    } else if (!ret_has_bits) {
-        return nullptr;
-    } else if (first_arg_ret) {
-        return node->data.fn_call_expr.tmp_ptr;
-    } else {
-        return result;
-    }
-}
-
-static LLVMValueRef gen_array_base_ptr(CodeGen *g, AstNode *node) {
-    TypeTableEntry *type_entry = get_expr_type(node);
-
-    LLVMValueRef array_ptr;
-    if (node->type == NodeTypeFieldAccessExpr) {
-        array_ptr = gen_field_access_expr(g, node, true);
-        if (type_entry->id == TypeTableEntryIdPointer) {
-            // we have a double pointer so we must dereference it once
-            array_ptr = LLVMBuildLoad(g->builder, array_ptr, "");
-        }
-    } else {
-        array_ptr = gen_expr(g, node);
-    }
-
-    assert(!array_ptr || LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
-
-    return array_ptr;
-}
-
 static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMValueRef array_ptr,
         TypeTableEntry *array_type, LLVMValueRef subscript_value)
 {
@@ -993,358 +503,78 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal
     }
 }
 
-static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeArrayAccessExpr);
-
-    AstNode *array_expr_node = node->data.array_access_expr.array_ref_expr;
-    TypeTableEntry *array_type = get_expr_type(array_expr_node);
+static LLVMValueRef gen_overflow_op(CodeGen *g, TypeTableEntry *type_entry, AddSubMul op,
+        LLVMValueRef val1, LLVMValueRef val2)
+{
+    LLVMValueRef fn_val = get_int_overflow_fn(g, type_entry, op);
+    LLVMValueRef params[] = {
+        val1,
+        val2,
+    };
+    LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
+    LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
+    LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
+    LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowFail");
+    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowOk");
+    LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block);
 
-    LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node);
+    LLVMPositionBuilderAtEnd(g->builder, fail_block);
+    gen_debug_safety_crash(g);
 
-    LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript);
-    return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value);
+    LLVMPositionBuilderAtEnd(g->builder, ok_block);
+    return result;
 }
 
-static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **out_type_entry) {
-    assert(node->type == NodeTypeFieldAccessExpr);
-
-    AstNode *struct_expr_node = node->data.field_access_expr.struct_expr;
-
-    *out_type_entry = node->data.field_access_expr.type_struct_field->type_entry;
-    if (!type_has_bits(*out_type_entry)) {
-        return nullptr;
-    }
+static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry,
+        LLVMValueRef val1, LLVMValueRef val2)
+{
+    // for unsigned left shifting, we do the wrapping shift, then logically shift
+    // right the same number of bits
+    // if the values don't match, we have an overflow
+    // for signed left shifting we do the same except arithmetic shift right
 
-    LLVMValueRef struct_ptr;
-    if (struct_expr_node->type == NodeTypeSymbol) {
-        VariableTableEntry *var = get_resolved_expr(struct_expr_node)->variable;
-        assert(var);
+    assert(type_entry->id == TypeTableEntryIdInt);
 
-        if (var->type->id == TypeTableEntryIdPointer) {
-            struct_ptr = LLVMBuildLoad(g->builder, var->value_ref, "");
-        } else {
-            struct_ptr = var->value_ref;
-        }
-    } else if (struct_expr_node->type == NodeTypeFieldAccessExpr) {
-        struct_ptr = gen_field_access_expr(g, struct_expr_node, true);
-        TypeTableEntry *field_type = get_expr_type(struct_expr_node);
-        if (field_type->id == TypeTableEntryIdPointer) {
-            // we have a double pointer so we must dereference it once
-            struct_ptr = LLVMBuildLoad(g->builder, struct_ptr, "");
-        }
+    LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, "");
+    LLVMValueRef orig_val;
+    if (type_entry->data.integral.is_signed) {
+        orig_val = LLVMBuildAShr(g->builder, result, val2, "");
     } else {
-        struct_ptr = gen_expr(g, struct_expr_node);
+        orig_val = LLVMBuildLShr(g->builder, result, val2, "");
     }
+    LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, orig_val, "");
 
-    assert(LLVMGetTypeKind(LLVMTypeOf(struct_ptr)) == LLVMPointerTypeKind);
-    assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(struct_ptr))) == LLVMStructTypeKind);
+    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowOk");
+    LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowFail");
+    LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
 
-    size_t gen_field_index = node->data.field_access_expr.type_struct_field->gen_index;
-    assert(gen_field_index != SIZE_MAX);
+    LLVMPositionBuilderAtEnd(g->builder, fail_block);
+    gen_debug_safety_crash(g);
 
-    return LLVMBuildStructGEP(g->builder, struct_ptr, gen_field_index, "");
+    LLVMPositionBuilderAtEnd(g->builder, ok_block);
+    return result;
 }
 
-static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeSliceExpr);
-
-    AstNode *array_ref_node = node->data.slice_expr.array_ref_expr;
-    TypeTableEntry *array_type = get_expr_type(array_ref_node);
-
-    LLVMValueRef tmp_struct_ptr = node->data.slice_expr.resolved_struct_val_expr.ptr;
-    LLVMValueRef array_ptr = gen_array_base_ptr(g, array_ref_node);
+static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2,
+        TypeTableEntry *type_entry, bool exact)
+{
 
-    if (array_type->id == TypeTableEntryIdArray) {
-        LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
-        LLVMValueRef end_val;
-        if (node->data.slice_expr.end) {
-            end_val = gen_expr(g, node->data.slice_expr.end);
+    if (want_debug_safety(g, source_node)) {
+        LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
+        LLVMValueRef is_zero_bit;
+        if (type_entry->id == TypeTableEntryIdInt) {
+            is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
+        } else if (type_entry->id == TypeTableEntryIdFloat) {
+            is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
         } else {
-            end_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false);
+            zig_unreachable();
         }
+        LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroOk");
+        LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroFail");
+        LLVMBuildCondBr(g->builder, is_zero_bit, fail_block, ok_block);
 
-        if (want_debug_safety(g, node)) {
-            add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
-            if (node->data.slice_expr.end) {
-                LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
-                        array_type->data.array.len, false);
-                add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);
-            }
-        }
-
-        LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
-        LLVMValueRef indices[] = {
-            LLVMConstNull(g->builtin_types.entry_usize->type_ref),
-            start_val,
-        };
-        LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
-        LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
-
-        LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
-        LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
-        LLVMBuildStore(g->builder, len_value, len_field_ptr);
-
-        return tmp_struct_ptr;
-    } else if (array_type->id == TypeTableEntryIdPointer) {
-        LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
-        LLVMValueRef end_val = gen_expr(g, node->data.slice_expr.end);
-
-        if (want_debug_safety(g, node)) {
-            add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
-        }
-
-        LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
-        LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
-        LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
-
-        LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
-        LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
-        LLVMBuildStore(g->builder, len_value, len_field_ptr);
-
-        return tmp_struct_ptr;
-    } else if (array_type->id == TypeTableEntryIdStruct) {
-        assert(array_type->data.structure.is_slice);
-        assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
-        assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
-
-        size_t ptr_index = array_type->data.structure.fields[0].gen_index;
-        assert(ptr_index != SIZE_MAX);
-        size_t len_index = array_type->data.structure.fields[1].gen_index;
-        assert(len_index != SIZE_MAX);
-
-        LLVMValueRef prev_end = nullptr;
-        if (!node->data.slice_expr.end || want_debug_safety(g, node)) {
-            LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, "");
-            prev_end = LLVMBuildLoad(g->builder, src_len_ptr, "");
-        }
-
-        LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
-        LLVMValueRef end_val;
-        if (node->data.slice_expr.end) {
-            end_val = gen_expr(g, node->data.slice_expr.end);
-        } else {
-            end_val = prev_end;
-        }
-
-        if (want_debug_safety(g, node)) {
-            assert(prev_end);
-            add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
-            if (node->data.slice_expr.end) {
-                add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end);
-            }
-        }
-
-        LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, "");
-        LLVMValueRef src_ptr = LLVMBuildLoad(g->builder, src_ptr_ptr, "");
-        LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, ptr_index, "");
-        LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, len_index, "");
-        LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
-
-        LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, len_index, "");
-        LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
-        LLVMBuildStore(g->builder, len_value, len_field_ptr);
-
-        return tmp_struct_ptr;
-    } else {
-        zig_unreachable();
-    }
-}
-
-
-static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) {
-    assert(node->type == NodeTypeArrayAccessExpr);
-
-    LLVMValueRef ptr = gen_array_ptr(g, node);
-    TypeTableEntry *child_type;
-    TypeTableEntry *array_type = get_expr_type(node->data.array_access_expr.array_ref_expr);
-    if (array_type->id == TypeTableEntryIdPointer) {
-        child_type = array_type->data.pointer.child_type;
-    } else if (array_type->id == TypeTableEntryIdStruct) {
-        assert(array_type->data.structure.is_slice);
-        TypeTableEntry *child_ptr_type = array_type->data.structure.fields[0].type_entry;
-        assert(child_ptr_type->id == TypeTableEntryIdPointer);
-        child_type = child_ptr_type->data.pointer.child_type;
-    } else if (array_type->id == TypeTableEntryIdArray) {
-        child_type = array_type->data.array.child_type;
-    } else {
-        zig_unreachable();
-    }
-
-    if (is_lvalue || !ptr || handle_is_ptr(child_type)) {
-        return ptr;
-    } else {
-        return LLVMBuildLoad(g->builder, ptr, "");
-    }
-}
-
-static LLVMValueRef gen_variable(CodeGen *g, AstNode *source_node, VariableTableEntry *variable) {
-    if (!type_has_bits(variable->type)) {
-        return nullptr;
-    } else {
-        assert(variable->value_ref);
-        return get_handle_value(g, variable->value_ref, variable->type);
-    }
-}
-
-static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) {
-    assert(node->type == NodeTypeFieldAccessExpr);
-
-    AstNode *struct_expr = node->data.field_access_expr.struct_expr;
-    TypeTableEntry *struct_type = get_expr_type(struct_expr);
-
-    if (struct_type->id == TypeTableEntryIdArray) {
-        Buf *name = node->data.field_access_expr.field_name;
-        assert(buf_eql_str(name, "len"));
-        return LLVMConstInt(g->builtin_types.entry_usize->type_ref,
-                struct_type->data.array.len, false);
-    } else if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer &&
-               struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct))
-    {
-        TypeTableEntry *type_entry;
-        LLVMValueRef ptr = gen_field_ptr(g, node, &type_entry);
-        if (is_lvalue || handle_is_ptr(type_entry)) {
-            return ptr;
-        } else {
-            return LLVMBuildLoad(g->builder, ptr, "");
-        }
-    } else if (struct_type->id == TypeTableEntryIdMetaType) {
-        assert(!is_lvalue);
-        TypeTableEntry *child_type = get_type_for_type_node(struct_expr);
-        if (child_type->id == TypeTableEntryIdEnum) {
-            return gen_enum_value_expr(g, node, child_type, nullptr);
-        } else {
-            zig_unreachable();
-        }
-    } else if (struct_type->id == TypeTableEntryIdNamespace) {
-        VariableTableEntry *variable = get_resolved_expr(node)->variable;
-        assert(variable);
-        return gen_variable(g, node, variable);
-    } else {
-        zig_unreachable();
-    }
-}
-
-static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node,
-        TypeTableEntry **out_type_entry)
-{
-    LLVMValueRef target_ref;
-
-    if (node->type == NodeTypeSymbol) {
-        VariableTableEntry *var = get_resolved_expr(node)->variable;
-        assert(var);
-
-        *out_type_entry = var->type;
-        target_ref = var->value_ref;
-    } else if (node->type == NodeTypeArrayAccessExpr) {
-        TypeTableEntry *array_type = get_expr_type(node->data.array_access_expr.array_ref_expr);
-        if (array_type->id == TypeTableEntryIdArray) {
-            *out_type_entry = array_type->data.array.child_type;
-            target_ref = gen_array_ptr(g, node);
-        } else if (array_type->id == TypeTableEntryIdPointer) {
-            *out_type_entry = array_type->data.pointer.child_type;
-            target_ref = gen_array_ptr(g, node);
-        } else if (array_type->id == TypeTableEntryIdStruct) {
-            assert(array_type->data.structure.is_slice);
-            *out_type_entry = array_type->data.structure.fields[0].type_entry->data.pointer.child_type;
-            target_ref = gen_array_ptr(g, node);
-        } else {
-            zig_unreachable();
-        }
-    } else if (node->type == NodeTypeFieldAccessExpr) {
-        AstNode *struct_expr_node = node->data.field_access_expr.struct_expr;
-        TypeTableEntry *struct_type = get_expr_type(struct_expr_node);
-        if (struct_type->id == TypeTableEntryIdNamespace) {
-            target_ref = gen_field_access_expr(g, node, true);
-            *out_type_entry = get_expr_type(node);
-        } else {
-            target_ref = gen_field_ptr(g, node, out_type_entry);
-        }
-    } else if (node->type == NodeTypePrefixOpExpr) {
-        assert(node->data.prefix_op_expr.prefix_op == PrefixOpDereference);
-        AstNode *target_expr = node->data.prefix_op_expr.primary_expr;
-        TypeTableEntry *type_entry = get_expr_type(target_expr);
-        assert(type_entry->id == TypeTableEntryIdPointer);
-        *out_type_entry = type_entry->data.pointer.child_type;
-        return gen_expr(g, target_expr);
-    } else {
-        zig_panic("bad assign target");
-    }
-
-    return target_ref;
-}
-
-static LLVMValueRef gen_overflow_op(CodeGen *g, TypeTableEntry *type_entry, AddSubMul op,
-        LLVMValueRef val1, LLVMValueRef val2)
-{
-    LLVMValueRef fn_val = get_int_overflow_fn(g, type_entry, op);
-    LLVMValueRef params[] = {
-        val1,
-        val2,
-    };
-    LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
-    LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
-    LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
-    LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowFail");
-    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowOk");
-    LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, fail_block);
-    gen_debug_safety_crash(g);
-
-    LLVMPositionBuilderAtEnd(g->builder, ok_block);
-    return result;
-}
-
-static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry,
-        LLVMValueRef val1, LLVMValueRef val2)
-{
-    // for unsigned left shifting, we do the wrapping shift, then logically shift
-    // right the same number of bits
-    // if the values don't match, we have an overflow
-    // for signed left shifting we do the same except arithmetic shift right
-
-    assert(type_entry->id == TypeTableEntryIdInt);
-
-    LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, "");
-    LLVMValueRef orig_val;
-    if (type_entry->data.integral.is_signed) {
-        orig_val = LLVMBuildAShr(g->builder, result, val2, "");
-    } else {
-        orig_val = LLVMBuildLShr(g->builder, result, val2, "");
-    }
-    LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, orig_val, "");
-
-    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowOk");
-    LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowFail");
-    LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, fail_block);
-    gen_debug_safety_crash(g);
-
-    LLVMPositionBuilderAtEnd(g->builder, ok_block);
-    return result;
-}
-
-static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2,
-        TypeTableEntry *type_entry, bool exact)
-{
-
-    if (want_debug_safety(g, source_node)) {
-        LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
-        LLVMValueRef is_zero_bit;
-        if (type_entry->id == TypeTableEntryIdInt) {
-            is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
-        } else if (type_entry->id == TypeTableEntryIdFloat) {
-            is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
-        } else {
-            zig_unreachable();
-        }
-        LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroOk");
-        LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroFail");
-        LLVMBuildCondBr(g->builder, is_zero_bit, fail_block, ok_block);
-
-        LLVMPositionBuilderAtEnd(g->builder, fail_block);
-        gen_debug_safety_crash(g);
+        LLVMPositionBuilderAtEnd(g->builder, fail_block);
+        gen_debug_safety_crash(g);
 
         LLVMPositionBuilderAtEnd(g->builder, ok_block);
     }
@@ -1529,18 +759,6 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
     }
     zig_unreachable();
 }
-static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeBinOpExpr);
-
-    LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1);
-    LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
-
-    TypeTableEntry *op1_type = get_expr_type(node->data.bin_op_expr.op1);
-    TypeTableEntry *op2_type = get_expr_type(node->data.bin_op_expr.op2);
-    return gen_arithmetic_bin_op(g, node, val1, val2, op1_type, op2_type, node->data.bin_op_expr.bin_op);
-
-}
-
 static LLVMIntPredicate cmp_op_to_int_predicate(IrBinOp cmp_op, bool is_signed) {
     switch (cmp_op) {
         case IrBinOpCmpEq:
@@ -1579,63 +797,6 @@ static LLVMRealPredicate cmp_op_to_real_predicate(IrBinOp cmp_op) {
     }
 }
 
-static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeBinOpExpr);
-
-    LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1);
-    LLVMBasicBlockRef post_val1_block = LLVMGetInsertBlock(g->builder);
-
-    // block for when val1 == true
-    LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndTrue");
-    // block for when val1 == false (don't even evaluate the second part)
-    LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndFalse");
-
-    LLVMBuildCondBr(g->builder, val1, true_block, false_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, true_block);
-    LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
-    LLVMBasicBlockRef post_val2_block = LLVMGetInsertBlock(g->builder);
-
-    LLVMBuildBr(g->builder, false_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, false_block);
-    LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
-    LLVMValueRef incoming_values[2] = {val1, val2};
-    LLVMBasicBlockRef incoming_blocks[2] = {post_val1_block, post_val2_block};
-    LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
-
-    return phi;
-}
-
-static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
-    assert(expr_node->type == NodeTypeBinOpExpr);
-
-    LLVMValueRef val1 = gen_expr(g, expr_node->data.bin_op_expr.op1);
-    LLVMBasicBlockRef post_val1_block = LLVMGetInsertBlock(g->builder);
-
-    // block for when val1 == false
-    LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrFalse");
-    // block for when val1 == true (don't even evaluate the second part)
-    LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrTrue");
-
-    LLVMBuildCondBr(g->builder, val1, true_block, false_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, false_block);
-    LLVMValueRef val2 = gen_expr(g, expr_node->data.bin_op_expr.op2);
-
-    LLVMBasicBlockRef post_val2_block = LLVMGetInsertBlock(g->builder);
-
-    LLVMBuildBr(g->builder, true_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, true_block);
-    LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
-    LLVMValueRef incoming_values[2] = {val1, val2};
-    LLVMBasicBlockRef incoming_blocks[2] = {post_val1_block, post_val2_block};
-    LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
-
-    return phi;
-}
-
 static LLVMValueRef gen_struct_memcpy(CodeGen *g, LLVMValueRef src, LLVMValueRef dest,
         TypeTableEntry *type_entry)
 {
@@ -1650,632 +811,53 @@ static LLVMValueRef gen_struct_memcpy(CodeGen *g, LLVMValueRef src, LLVMValueRef
     uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref);
     uint64_t align_bytes = get_memcpy_align(g, type_entry);
     assert(size_bytes > 0);
-    assert(align_bytes > 0);
-
-    LLVMValueRef params[] = {
-        dest_ptr, // dest pointer
-        src_ptr, // source pointer
-        LLVMConstInt(usize->type_ref, size_bytes, false),
-        LLVMConstInt(LLVMInt32Type(), align_bytes, false),
-        LLVMConstNull(LLVMInt1Type()), // is volatile
-    };
-
-    return LLVMBuildCall(g->builder, g->memcpy_fn_val, params, 5, "");
-}
-
-static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op,
-        LLVMValueRef target_ref, LLVMValueRef value,
-        TypeTableEntry *op1_type, TypeTableEntry *op2_type)
-{
-    if (!type_has_bits(op1_type)) {
-        return nullptr;
-    }
-    if (handle_is_ptr(op1_type)) {
-        assert(op1_type == op2_type);
-        assert(bin_op == BinOpTypeAssign);
-
-        return gen_struct_memcpy(g, value, target_ref, op1_type);
-    }
-
-    if (bin_op != BinOpTypeAssign) {
-        assert(source_node->type == NodeTypeBinOpExpr);
-        LLVMValueRef left_value = LLVMBuildLoad(g->builder, target_ref, "");
-
-        value = gen_arithmetic_bin_op(g, source_node, left_value, value, op1_type, op2_type, bin_op);
-    }
-
-    LLVMBuildStore(g->builder, value, target_ref);
-    return nullptr;
-}
-
-static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeBinOpExpr);
-
-    AstNode *lhs_node = node->data.bin_op_expr.op1;
-
-    TypeTableEntry *op1_type;
-
-    LLVMValueRef target_ref = gen_lvalue(g, node, lhs_node, &op1_type);
-
-    TypeTableEntry *op2_type = get_expr_type(node->data.bin_op_expr.op2);
-
-    LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2);
-
-    gen_assign_raw(g, node, node->data.bin_op_expr.bin_op, target_ref, value, op1_type, op2_type);
-    return nullptr;
-}
-
-static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref) {
-    TypeTableEntry *type_entry = get_expr_type(node);
-    assert(type_entry->id == TypeTableEntryIdMaybe);
-    TypeTableEntry *child_type = type_entry->data.maybe.child_type;
-    if (child_type->id == TypeTableEntryIdPointer ||
-        child_type->id == TypeTableEntryIdFn)
-    {
-        return maybe_struct_ref;
-    } else {
-        LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, "");
-        return get_handle_value(g, maybe_field_ptr, child_type);
-    }
-}
-
-static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeBinOpExpr);
-    assert(node->data.bin_op_expr.bin_op == BinOpTypeUnwrapMaybe);
-
-    AstNode *op1_node = node->data.bin_op_expr.op1;
-    AstNode *op2_node = node->data.bin_op_expr.op2;
-
-    LLVMValueRef maybe_struct_ref = gen_expr(g, op1_node);
-
-    TypeTableEntry *maybe_type = get_expr_type(op1_node);
-    assert(maybe_type->id == TypeTableEntryIdMaybe);
-    TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
-
-    LLVMValueRef cond_value;
-    if (child_type->id == TypeTableEntryIdPointer ||
-        child_type->id == TypeTableEntryIdFn)
-    {
-        cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, maybe_struct_ref,
-                LLVMConstNull(child_type->type_ref), "");
-    } else {
-        LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, "");
-        cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
-    }
-
-    LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNonNull");
-    LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNull");
-    LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEnd");
-
-    bool null_reachable = get_expr_type(op2_node)->id != TypeTableEntryIdUnreachable;
-
-    LLVMBuildCondBr(g->builder, cond_value, non_null_block, null_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, non_null_block);
-    LLVMValueRef non_null_result = gen_unwrap_maybe(g, op1_node, maybe_struct_ref);
-    LLVMBuildBr(g->builder, end_block);
-    LLVMBasicBlockRef post_non_null_result_block = LLVMGetInsertBlock(g->builder);
-
-    LLVMPositionBuilderAtEnd(g->builder, null_block);
-    LLVMValueRef null_result = gen_expr(g, op2_node);
-    if (null_reachable) {
-        LLVMBuildBr(g->builder, end_block);
-    }
-    LLVMBasicBlockRef post_null_result_block = LLVMGetInsertBlock(g->builder);
-
-    LLVMPositionBuilderAtEnd(g->builder, end_block);
-    if (null_reachable) {
-        LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), "");
-        LLVMValueRef incoming_values[2] = {non_null_result, null_result};
-        LLVMBasicBlockRef incoming_blocks[2] = {post_non_null_result_block, post_null_result_block};
-        LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
-        return phi;
-    } else {
-        return non_null_result;
-    }
-
-    return nullptr;
-}
-
-static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
-    switch (node->data.bin_op_expr.bin_op) {
-        case BinOpTypeInvalid:
-        case BinOpTypeArrayCat:
-        case BinOpTypeArrayMult:
-            zig_unreachable();
-        case BinOpTypeAssign:
-        case BinOpTypeAssignTimes:
-        case BinOpTypeAssignTimesWrap:
-        case BinOpTypeAssignDiv:
-        case BinOpTypeAssignMod:
-        case BinOpTypeAssignPlus:
-        case BinOpTypeAssignPlusWrap:
-        case BinOpTypeAssignMinus:
-        case BinOpTypeAssignMinusWrap:
-        case BinOpTypeAssignBitShiftLeft:
-        case BinOpTypeAssignBitShiftLeftWrap:
-        case BinOpTypeAssignBitShiftRight:
-        case BinOpTypeAssignBitAnd:
-        case BinOpTypeAssignBitXor:
-        case BinOpTypeAssignBitOr:
-        case BinOpTypeAssignBoolAnd:
-        case BinOpTypeAssignBoolOr:
-            return gen_assign_expr(g, node);
-        case BinOpTypeBoolOr:
-            return gen_bool_or_expr(g, node);
-        case BinOpTypeBoolAnd:
-            return gen_bool_and_expr(g, node);
-        case BinOpTypeCmpEq:
-        case BinOpTypeCmpNotEq:
-        case BinOpTypeCmpLessThan:
-        case BinOpTypeCmpGreaterThan:
-        case BinOpTypeCmpLessOrEq:
-        case BinOpTypeCmpGreaterOrEq:
-            zig_panic("moved to ir_render");
-        case BinOpTypeUnwrapMaybe:
-            return gen_unwrap_maybe_expr(g, node);
-        case BinOpTypeBinOr:
-        case BinOpTypeBinXor:
-        case BinOpTypeBinAnd:
-        case BinOpTypeBitShiftLeft:
-        case BinOpTypeBitShiftLeftWrap:
-        case BinOpTypeBitShiftRight:
-        case BinOpTypeAdd:
-        case BinOpTypeAddWrap:
-        case BinOpTypeSub:
-        case BinOpTypeSubWrap:
-        case BinOpTypeMult:
-        case BinOpTypeMultWrap:
-        case BinOpTypeDiv:
-        case BinOpTypeMod:
-            return gen_arithmetic_bin_op_expr(g, node);
-    }
-    zig_unreachable();
-}
-
-static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeUnwrapErrorExpr);
-
-    AstNode *op1 = node->data.unwrap_err_expr.op1;
-    AstNode *op2 = node->data.unwrap_err_expr.op2;
-    VariableTableEntry *var = node->data.unwrap_err_expr.var;
-
-    LLVMValueRef expr_val = gen_expr(g, op1);
-    TypeTableEntry *expr_type = get_expr_type(op1);
-    TypeTableEntry *op2_type = get_expr_type(op2);
-    assert(expr_type->id == TypeTableEntryIdErrorUnion);
-    TypeTableEntry *child_type = expr_type->data.error.child_type;
-    LLVMValueRef err_val;
-    if (handle_is_ptr(expr_type)) {
-        LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, "");
-        err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
-    } else {
-        err_val = expr_val;
-    }
-    LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
-    LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
-
-    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrOk");
-    LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError");
-    LLVMBasicBlockRef end_block;
-    bool err_reachable = op2_type->id != TypeTableEntryIdUnreachable;
-    bool have_end_block = err_reachable && type_has_bits(child_type);
-    if (have_end_block) {
-        end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrEnd");
-    }
-
-    LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, err_block);
-    if (var) {
-        LLVMBuildStore(g->builder, err_val, var->value_ref);
-    }
-    LLVMValueRef err_result = gen_expr(g, op2);
-    if (have_end_block) {
-        LLVMBuildBr(g->builder, end_block);
-    } else if (err_reachable) {
-        LLVMBuildBr(g->builder, ok_block);
-    }
-
-    LLVMPositionBuilderAtEnd(g->builder, ok_block);
-    if (!type_has_bits(child_type)) {
-        return nullptr;
-    }
-    LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");
-    LLVMValueRef child_val = get_handle_value(g, child_val_ptr, child_type);
-
-    if (!have_end_block) {
-        return child_val;
-    }
-
-    LLVMBuildBr(g->builder, end_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, end_block);
-    LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(err_result), "");
-    LLVMValueRef incoming_values[2] = {child_val, err_result};
-    LLVMBasicBlockRef incoming_blocks[2] = {ok_block, err_block};
-    LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
-    return phi;
-}
-
-static void gen_defers_for_block(CodeGen *g, BlockContext *inner_block, BlockContext *outer_block,
-        bool gen_error_defers, bool gen_maybe_defers)
-{
-    while (inner_block != outer_block) {
-        if (inner_block->node->type == NodeTypeDefer &&
-           ((inner_block->node->data.defer.kind == ReturnKindUnconditional) ||
-            (gen_error_defers && inner_block->node->data.defer.kind == ReturnKindError) ||
-            (gen_maybe_defers && inner_block->node->data.defer.kind == ReturnKindMaybe)))
-        {
-            gen_expr(g, inner_block->node->data.defer.expr);
-        }
-        inner_block = inner_block->parent;
-    }
-}
-
-static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
-    size_t result = 0;
-    while (inner_block != outer_block) {
-        if (inner_block->node->type == NodeTypeDefer &&
-           (inner_block->node->data.defer.kind == ReturnKindError ||
-            inner_block->node->data.defer.kind == ReturnKindMaybe))
-        {
-            result += 1;
-        }
-        inner_block = inner_block->parent;
-    }
-    return result;
-}
-
-static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value, ReturnKnowledge rk) {
-    BlockContext *defer_inner_block = source_node->block_context;
-    BlockContext *defer_outer_block = source_node->block_context->fn_entry->fn_def_node->block_context;
-    if (rk == ReturnKnowledgeUnknown) {
-        if (get_conditional_defer_count(defer_inner_block, defer_outer_block) > 0) {
-            // generate branching code that checks the return value and generates defers
-            // if the return value is error
-            zig_panic("TODO");
-        }
-    } else if (rk != ReturnKnowledgeSkipDefers) {
-        gen_defers_for_block(g, defer_inner_block, defer_outer_block,
-                rk == ReturnKnowledgeKnownError, rk == ReturnKnowledgeKnownNull);
-    }
-
-    TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
-    bool is_extern = g->cur_fn->type_entry->data.fn.fn_type_id.is_extern;
-    if (handle_is_ptr(return_type)) {
-        if (is_extern) {
-            LLVMValueRef by_val_value = LLVMBuildLoad(g->builder, value, "");
-            LLVMBuildRet(g->builder, by_val_value);
-        } else {
-            assert(g->cur_ret_ptr);
-            gen_assign_raw(g, source_node, BinOpTypeAssign, g->cur_ret_ptr, value, return_type, return_type);
-            LLVMBuildRetVoid(g->builder);
-        }
-    } else {
-        LLVMBuildRet(g->builder, value);
-    }
-    return nullptr;
-}
-
-static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeReturnExpr);
-    AstNode *param_node = node->data.return_expr.expr;
-    assert(param_node);
-    LLVMValueRef value = gen_expr(g, param_node);
-    TypeTableEntry *value_type = get_expr_type(param_node);
-
-    switch (node->data.return_expr.kind) {
-        case ReturnKindUnconditional:
-            {
-                Expr *expr = get_resolved_expr(param_node);
-                if (expr->const_val.ok) {
-                    if (value_type->id == TypeTableEntryIdErrorUnion) {
-                        if (expr->const_val.data.x_err.err) {
-                            expr->return_knowledge = ReturnKnowledgeKnownError;
-                        } else {
-                            expr->return_knowledge = ReturnKnowledgeKnownNonError;
-                        }
-                    } else if (value_type->id == TypeTableEntryIdMaybe) {
-                        if (expr->const_val.data.x_maybe) {
-                            expr->return_knowledge = ReturnKnowledgeKnownNonNull;
-                        } else {
-                            expr->return_knowledge = ReturnKnowledgeKnownNull;
-                        }
-                    }
-                }
-                return gen_return(g, node, value, expr->return_knowledge);
-            }
-        case ReturnKindError:
-            {
-                assert(value_type->id == TypeTableEntryIdErrorUnion);
-                TypeTableEntry *child_type = value_type->data.error.child_type;
-
-                LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetReturn");
-                LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetContinue");
-
-                LLVMValueRef err_val;
-                if (type_has_bits(child_type)) {
-                    LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, value, 0, "");
-                    err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
-                } else {
-                    err_val = value;
-                }
-                LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
-                LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
-                LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block);
-
-                LLVMPositionBuilderAtEnd(g->builder, return_block);
-                TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
-                if (return_type->id == TypeTableEntryIdPureError) {
-                    gen_return(g, node, err_val, ReturnKnowledgeKnownError);
-                } else if (return_type->id == TypeTableEntryIdErrorUnion) {
-                    if (type_has_bits(return_type->data.error.child_type)) {
-                        assert(g->cur_ret_ptr);
-
-                        LLVMValueRef tag_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 0, "");
-                        LLVMBuildStore(g->builder, err_val, tag_ptr);
-                        LLVMBuildRetVoid(g->builder);
-                    } else {
-                        gen_return(g, node, err_val, ReturnKnowledgeKnownError);
-                    }
-                } else {
-                    zig_unreachable();
-                }
-
-                LLVMPositionBuilderAtEnd(g->builder, continue_block);
-                if (type_has_bits(child_type)) {
-                    LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");
-                    return get_handle_value(g, val_ptr, child_type);
-                } else {
-                    return nullptr;
-                }
-            }
-        case ReturnKindMaybe:
-            {
-                assert(value_type->id == TypeTableEntryIdMaybe);
-                TypeTableEntry *child_type = value_type->data.maybe.child_type;
-
-                LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetReturn");
-                LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetContinue");
-
-                LLVMValueRef maybe_val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");
-                LLVMValueRef is_non_null = LLVMBuildLoad(g->builder, maybe_val_ptr, "");
-
-                LLVMValueRef zero = LLVMConstNull(LLVMInt1Type());
-                LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntNE, is_non_null, zero, "");
-                LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block);
-
-                LLVMPositionBuilderAtEnd(g->builder, return_block);
-                TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
-                assert(return_type->id == TypeTableEntryIdMaybe);
-                if (handle_is_ptr(return_type)) {
-                    assert(g->cur_ret_ptr);
-
-                    LLVMValueRef maybe_bit_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 1, "");
-                    LLVMBuildStore(g->builder, zero, maybe_bit_ptr);
-                    LLVMBuildRetVoid(g->builder);
-                } else {
-                    LLVMValueRef ret_zero_value = LLVMConstNull(return_type->type_ref);
-                    gen_return(g, node, ret_zero_value, ReturnKnowledgeKnownNull);
-                }
-
-                LLVMPositionBuilderAtEnd(g->builder, continue_block);
-                if (type_has_bits(child_type)) {
-                    LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 0, "");
-                    return get_handle_value(g, val_ptr, child_type);
-                } else {
-                    return nullptr;
-                }
-            }
-    }
-    zig_unreachable();
-}
-
-static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMValueRef cond_value,
-        AstNode *then_node, AstNode *else_node)
-{
-    assert(then_node);
-    assert(else_node);
-
-    TypeTableEntry *then_type = get_expr_type(then_node);
-    TypeTableEntry *else_type = get_expr_type(else_node);
-
-    bool use_then_value = type_has_bits(then_type);
-    bool use_else_value = type_has_bits(else_type);
-
-    LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
-    LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
-
-    LLVMBasicBlockRef endif_block = nullptr;
-    bool then_endif_reachable = then_type->id != TypeTableEntryIdUnreachable;
-    bool else_endif_reachable = else_type->id != TypeTableEntryIdUnreachable;
-    if (then_endif_reachable || else_endif_reachable) {
-        endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
-    }
-
-    LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, then_block);
-    LLVMValueRef then_expr_result = gen_expr(g, then_node);
-    if (then_endif_reachable) {
-        clear_debug_source_node(g);
-        LLVMBuildBr(g->builder, endif_block);
-    }
-    LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder);
-
-    LLVMPositionBuilderAtEnd(g->builder, else_block);
-    LLVMValueRef else_expr_result = gen_expr(g, else_node);
-    if (else_endif_reachable) {
-        clear_debug_source_node(g);
-        LLVMBuildBr(g->builder, endif_block);
-    }
-    LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder);
-
-    if (then_endif_reachable || else_endif_reachable) {
-        LLVMPositionBuilderAtEnd(g->builder, endif_block);
-        if (use_then_value && use_else_value) {
-            LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
-            LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
-            LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block};
-            LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
-            return phi;
-        } else if (use_then_value) {
-            return then_expr_result;
-        } else if (use_else_value) {
-            return else_expr_result;
-        }
-    }
-
-    return nullptr;
-}
-
-static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeIfBoolExpr);
-    assert(node->data.if_bool_expr.condition);
-    assert(node->data.if_bool_expr.then_block);
-
-    ConstExprValue *const_val = &get_resolved_expr(node->data.if_bool_expr.condition)->const_val;
-    if (const_val->ok) {
-        if (const_val->data.x_bool) {
-            return gen_expr(g, node->data.if_bool_expr.then_block);
-        } else if (node->data.if_bool_expr.else_node) {
-            return gen_expr(g, node->data.if_bool_expr.else_node);
-        } else {
-            return nullptr;
-        }
-    } else {
-        LLVMValueRef cond_value = gen_expr(g, node->data.if_bool_expr.condition);
-
-        return gen_if_bool_expr_raw(g, node, cond_value,
-                node->data.if_bool_expr.then_block,
-                node->data.if_bool_expr.else_node);
-    }
-}
-
-static void gen_var_debug_decl(CodeGen *g, VariableTableEntry *var) {
-    BlockContext *block_context = var->block_context;
-    AstNode *source_node = var->decl_node;
-    ZigLLVMDILocation *debug_loc = ZigLLVMGetDebugLoc(source_node->line + 1, source_node->column + 1,
-            block_context->di_scope);
-    ZigLLVMInsertDeclareAtEnd(g->dbuilder, var->value_ref, var->di_loc_var, debug_loc,
-            LLVMGetInsertBlock(g->builder));
-}
-
-static LLVMValueRef gen_if_var_then_block(CodeGen *g, AstNode *node, VariableTableEntry *variable, bool maybe_is_ptr,
-        LLVMValueRef init_val, TypeTableEntry *child_type, AstNode *then_node)
-{
-    if (node->data.if_var_expr.var_is_ptr) {
-        LLVMValueRef payload_ptr;
-        if (maybe_is_ptr) {
-            zig_panic("TODO");
-        } else {
-            payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, "");
-        }
-        LLVMBuildStore(g->builder, payload_ptr, variable->value_ref);
-    } else {
-        LLVMValueRef payload_val;
-        if (maybe_is_ptr) {
-            payload_val = init_val;
-        } else {
-            LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, "");
-            payload_val = get_handle_value(g, payload_ptr, child_type);
-        }
-        gen_assign_raw(g, node, BinOpTypeAssign, variable->value_ref, payload_val,
-                variable->type, child_type);
-    }
-    gen_var_debug_decl(g, variable);
-
-    return gen_expr(g, then_node);
-}
-
-static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeIfVarExpr);
-    assert(node->data.if_var_expr.var_decl.expr);
-
-    AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl;
-    VariableTableEntry *variable = var_decl->variable;
-
-    // test if value is the maybe state
-    TypeTableEntry *expr_type = get_expr_type(var_decl->expr);
-    TypeTableEntry *child_type = expr_type->data.maybe.child_type;
-
-    LLVMValueRef init_val = gen_expr(g, var_decl->expr);
-
-
-    AstNode *then_node = node->data.if_var_expr.then_block;
-    AstNode *else_node = node->data.if_var_expr.else_node;
-    bool maybe_is_ptr = child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn;
-
-    ConstExprValue *const_val = &get_resolved_expr(var_decl->expr)->const_val;
-    if (const_val->ok) {
-        if (const_val->data.x_maybe) {
-            return gen_if_var_then_block(g, node, variable, maybe_is_ptr, init_val, child_type, then_node);
-        } else {
-            return gen_expr(g, else_node);
-        }
-    }
-
-    LLVMValueRef cond_value;
-    if (maybe_is_ptr) {
-        cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, init_val, LLVMConstNull(child_type->type_ref), "");
-    } else {
-        LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, init_val, 1, "");
-        cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
-    }
-
-    TypeTableEntry *then_type = get_expr_type(then_node);
-    TypeTableEntry *else_type = get_expr_type(else_node);
+    assert(align_bytes > 0);
 
-    bool use_then_value = type_has_bits(then_type);
-    bool use_else_value = type_has_bits(else_type);
+    LLVMValueRef params[] = {
+        dest_ptr, // dest pointer
+        src_ptr, // source pointer
+        LLVMConstInt(usize->type_ref, size_bytes, false),
+        LLVMConstInt(LLVMInt32Type(), align_bytes, false),
+        LLVMConstNull(LLVMInt1Type()), // is volatile
+    };
 
-    LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeThen");
-    LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeElse");
+    return LLVMBuildCall(g->builder, g->memcpy_fn_val, params, 5, "");
+}
 
-    LLVMBasicBlockRef endif_block;
-    bool then_endif_reachable = then_type->id != TypeTableEntryIdUnreachable;
-    bool else_endif_reachable = else_type->id != TypeTableEntryIdUnreachable;
-    if (then_endif_reachable || else_endif_reachable) {
-        endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEndIf");
+static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op,
+        LLVMValueRef target_ref, LLVMValueRef value,
+        TypeTableEntry *op1_type, TypeTableEntry *op2_type)
+{
+    if (!type_has_bits(op1_type)) {
+        return nullptr;
     }
+    if (handle_is_ptr(op1_type)) {
+        assert(op1_type == op2_type);
+        assert(bin_op == BinOpTypeAssign);
 
-    LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
-
-    LLVMPositionBuilderAtEnd(g->builder, then_block);
-    LLVMValueRef then_expr_result = gen_if_var_then_block(g, node, variable, maybe_is_ptr, init_val, child_type, then_node);
-
-    if (then_endif_reachable) {
-        LLVMBuildBr(g->builder, endif_block);
+        return gen_struct_memcpy(g, value, target_ref, op1_type);
     }
-    LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder);
 
+    if (bin_op != BinOpTypeAssign) {
+        assert(source_node->type == NodeTypeBinOpExpr);
+        LLVMValueRef left_value = LLVMBuildLoad(g->builder, target_ref, "");
 
-    LLVMPositionBuilderAtEnd(g->builder, else_block);
-    LLVMValueRef else_expr_result = gen_expr(g, else_node);
-    if (else_endif_reachable) {
-        LLVMBuildBr(g->builder, endif_block);
-    }
-    LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder);
-
-    if (then_endif_reachable || else_endif_reachable) {
-        LLVMPositionBuilderAtEnd(g->builder, endif_block);
-        if (use_then_value && use_else_value) {
-            LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
-            LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
-            LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block};
-            LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
-            return phi;
-        } else if (use_then_value) {
-            return then_expr_result;
-        } else if (use_else_value) {
-            return else_expr_result;
-        }
+        value = gen_arithmetic_bin_op(g, source_node, left_value, value, op1_type, op2_type, bin_op);
     }
 
+    LLVMBuildStore(g->builder, value, target_ref);
     return nullptr;
 }
 
+static void gen_var_debug_decl(CodeGen *g, VariableTableEntry *var) {
+    BlockContext *block_context = var->block_context;
+    AstNode *source_node = var->decl_node;
+    ZigLLVMDILocation *debug_loc = ZigLLVMGetDebugLoc(source_node->line + 1, source_node->column + 1,
+            block_context->di_scope);
+    ZigLLVMInsertDeclareAtEnd(g->dbuilder, var->value_ref, var->di_loc_var, debug_loc,
+            LLVMGetInsertBlock(g->builder));
+}
+
 static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
     if (!type_has_bits(instruction->type_entry))
         return nullptr;
@@ -3025,745 +1607,6 @@ static void ir_render(CodeGen *g, FnTableEntry *fn_entry) {
     }
 }
 
-static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
-    assert(block_node->type == NodeTypeBlock);
-
-    LLVMValueRef 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);
-        return_value = gen_expr(g, statement_node);
-    }
-
-    bool end_unreachable = implicit_return_type && implicit_return_type->id == TypeTableEntryIdUnreachable;
-    if (end_unreachable) {
-        return nullptr;
-    }
-
-    gen_defers_for_block(g, block_node->data.block.nested_block, block_node->data.block.child_block,
-            false, false);
-
-    if (implicit_return_type) {
-        return gen_return(g, block_node, return_value, ReturnKnowledgeSkipDefers);
-    } else {
-        return return_value;
-    }
-}
-
-static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok) {
-    const char *ptr = buf_ptr(node->data.asm_expr.asm_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 LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeAsmExpr);
-
-    AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
-
-    Buf *src_template = asm_expr->asm_template;
-
-    Buf llvm_template = BUF_INIT;
-    buf_resize(&llvm_template, 0);
-
-    for (size_t token_i = 0; token_i < asm_expr->token_list.length; token_i += 1) {
-        AsmToken *asm_token = &asm_expr->token_list.at(token_i);
-        switch (asm_token->id) {
-            case AsmTokenIdTemplate:
-                for (size_t offset = asm_token->start; offset < asm_token->end; offset += 1) {
-                    uint8_t c = *((uint8_t*)(buf_ptr(src_template) + offset));
-                    if (c == '$') {
-                        buf_append_str(&llvm_template, "$$");
-                    } else {
-                        buf_append_char(&llvm_template, c);
-                    }
-                }
-                break;
-            case AsmTokenIdPercent:
-                buf_append_char(&llvm_template, '%');
-                break;
-            case AsmTokenIdVar:
-                size_t index = find_asm_index(g, node, asm_token);
-                assert(index < SIZE_MAX);
-                buf_appendf(&llvm_template, "$%zu", index);
-                break;
-        }
-    }
-
-    Buf constraint_buf = BUF_INIT;
-    buf_resize(&constraint_buf, 0);
-
-    assert(asm_expr->return_count == 0 || asm_expr->return_count == 1);
-
-    size_t total_constraint_count = asm_expr->output_list.length +
-                                 asm_expr->input_list.length +
-                                 asm_expr->clobber_list.length;
-    size_t input_and_output_count = asm_expr->output_list.length +
-                                 asm_expr->input_list.length -
-                                 asm_expr->return_count;
-    size_t total_index = 0;
-    size_t param_index = 0;
-    LLVMTypeRef *param_types = allocate<LLVMTypeRef>(input_and_output_count);
-    LLVMValueRef *param_values = allocate<LLVMValueRef>(input_and_output_count);
-    for (size_t i = 0; i < asm_expr->output_list.length; i += 1, total_index += 1) {
-        AsmOutput *asm_output = asm_expr->output_list.at(i);
-        bool is_return = (asm_output->return_type != nullptr);
-        assert(*buf_ptr(asm_output->constraint) == '=');
-        if (is_return) {
-            buf_appendf(&constraint_buf, "=%s", buf_ptr(asm_output->constraint) + 1);
-        } else {
-            buf_appendf(&constraint_buf, "=*%s", buf_ptr(asm_output->constraint) + 1);
-        }
-        if (total_index + 1 < total_constraint_count) {
-            buf_append_char(&constraint_buf, ',');
-        }
-
-        if (!is_return) {
-            VariableTableEntry *variable = asm_output->variable;
-            assert(variable);
-            param_types[param_index] = LLVMTypeOf(variable->value_ref);
-            param_values[param_index] = variable->value_ref;
-            param_index += 1;
-        }
-    }
-    for (size_t i = 0; i < asm_expr->input_list.length; i += 1, total_index += 1, param_index += 1) {
-        AsmInput *asm_input = asm_expr->input_list.at(i);
-        buf_append_buf(&constraint_buf, asm_input->constraint);
-        if (total_index + 1 < total_constraint_count) {
-            buf_append_char(&constraint_buf, ',');
-        }
-
-        TypeTableEntry *expr_type = get_expr_type(asm_input->expr);
-        param_types[param_index] = expr_type->type_ref;
-        param_values[param_index] = gen_expr(g, asm_input->expr);
-    }
-    for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1, total_index += 1) {
-        Buf *clobber_buf = asm_expr->clobber_list.at(i);
-        buf_appendf(&constraint_buf, "~{%s}", buf_ptr(clobber_buf));
-        if (total_index + 1 < total_constraint_count) {
-            buf_append_char(&constraint_buf, ',');
-        }
-    }
-
-    LLVMTypeRef ret_type;
-    if (asm_expr->return_count == 0) {
-        ret_type = LLVMVoidType();
-    } else {
-        ret_type = get_expr_type(node)->type_ref;
-    }
-    LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, input_and_output_count, false);
-
-    bool is_volatile = asm_expr->is_volatile || (asm_expr->output_list.length == 0);
-    LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template),
-            buf_ptr(&constraint_buf), is_volatile, false);
-
-    set_debug_source_node(g, node);
-    return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, "");
-}
-
-static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeContainerInitExpr);
-
-    TypeTableEntry *type_entry = get_expr_type(node);
-
-
-    if (node->data.container_init_expr.enum_type) {
-        size_t param_count = node->data.container_init_expr.entries.length;
-        AstNode *arg1_node;
-        if (param_count == 1) {
-            arg1_node = node->data.container_init_expr.entries.at(0);
-        } else {
-            assert(param_count == 0);
-            arg1_node = nullptr;
-        }
-        return gen_enum_value_expr(g, node->data.container_init_expr.type,
-                node->data.container_init_expr.enum_type, arg1_node);
-    }
-
-
-    if (type_entry->id == TypeTableEntryIdStruct) {
-        assert(node->data.container_init_expr.kind == ContainerInitKindStruct);
-
-        size_t src_field_count = type_entry->data.structure.src_field_count;
-        assert(src_field_count == node->data.container_init_expr.entries.length);
-
-        StructValExprCodeGen *struct_val_expr_node = &node->data.container_init_expr.resolved_struct_val_expr;
-        LLVMValueRef tmp_struct_ptr = struct_val_expr_node->ptr;
-
-        for (size_t i = 0; i < src_field_count; i += 1) {
-            AstNode *field_node = node->data.container_init_expr.entries.at(i);
-            assert(field_node->type == NodeTypeStructValueField);
-            TypeStructField *type_struct_field = field_node->data.struct_val_field.type_struct_field;
-            if (type_struct_field->type_entry->id == TypeTableEntryIdVoid) {
-                continue;
-            }
-            assert(buf_eql_buf(type_struct_field->name, field_node->data.struct_val_field.name));
-
-            set_debug_source_node(g, field_node);
-            LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, type_struct_field->gen_index, "");
-            AstNode *expr_node = field_node->data.struct_val_field.expr;
-            LLVMValueRef value = gen_expr(g, expr_node);
-            gen_assign_raw(g, field_node, BinOpTypeAssign, field_ptr, value,
-                    type_struct_field->type_entry, get_expr_type(expr_node));
-        }
-
-        return tmp_struct_ptr;
-    } else if (type_entry->id == TypeTableEntryIdVoid) {
-        assert(node->data.container_init_expr.entries.length == 0);
-        return nullptr;
-    } else if (type_entry->id == TypeTableEntryIdArray) {
-        StructValExprCodeGen *struct_val_expr_node = &node->data.container_init_expr.resolved_struct_val_expr;
-        LLVMValueRef tmp_array_ptr = struct_val_expr_node->ptr;
-
-        size_t field_count = type_entry->data.array.len;
-        assert(field_count == node->data.container_init_expr.entries.length);
-
-        TypeTableEntry *child_type = type_entry->data.array.child_type;
-
-        for (size_t i = 0; i < field_count; i += 1) {
-            AstNode *field_node = node->data.container_init_expr.entries.at(i);
-            LLVMValueRef elem_val = gen_expr(g, field_node);
-
-            LLVMValueRef indices[] = {
-                LLVMConstNull(g->builtin_types.entry_usize->type_ref),
-                LLVMConstInt(g->builtin_types.entry_usize->type_ref, i, false),
-            };
-            set_debug_source_node(g, field_node);
-            LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, tmp_array_ptr, indices, 2, "");
-            gen_assign_raw(g, field_node, BinOpTypeAssign, elem_ptr, elem_val,
-                    child_type, get_expr_type(field_node));
-        }
-
-        return tmp_array_ptr;
-    } else {
-        zig_unreachable();
-    }
-}
-
-static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeWhileExpr);
-    assert(node->data.while_expr.condition);
-    assert(node->data.while_expr.body);
-
-    //AstNode *continue_expr_node = node->data.while_expr.continue_expr;
-
-    bool condition_always_true = node->data.while_expr.condition_always_true;
-    //bool contains_break = node->data.while_expr.contains_break;
-    if (condition_always_true) {
-        // generate a forever loop
-        zig_panic("TODO IR");
-
-        //LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
-        //LLVMBasicBlockRef continue_block = continue_expr_node ?
-        //    LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileContinue") : body_block;
-        //LLVMBasicBlockRef end_block = nullptr;
-        //if (contains_break) {
-        //    end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
-        //}
-
-        //set_debug_source_node(g, node);
-        //LLVMBuildBr(g->builder, body_block);
-
-        //if (continue_expr_node) {
-        //    LLVMPositionBuilderAtEnd(g->builder, continue_block);
-
-        //    gen_expr(g, continue_expr_node);
-
-        //    set_debug_source_node(g, node);
-        //    LLVMBuildBr(g->builder, body_block);
-        //}
-
-        //LLVMPositionBuilderAtEnd(g->builder, body_block);
-        //g->break_block_stack.append(end_block);
-        //g->continue_block_stack.append(continue_block);
-        //gen_expr(g, node->data.while_expr.body);
-        //g->break_block_stack.pop();
-        //g->continue_block_stack.pop();
-
-        //if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
-        //    set_debug_source_node(g, node);
-        //    LLVMBuildBr(g->builder, continue_block);
-        //}
-
-        //if (contains_break) {
-        //    LLVMPositionBuilderAtEnd(g->builder, end_block);
-        //}
-    } else {
-        zig_panic("moved to ir.cpp");
-    }
-
-    return nullptr;
-}
-
-//static LLVMValueRef gen_break(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeBreak);
-//    LLVMBasicBlockRef dest_block = g->break_block_stack.last();
-//
-//    set_debug_source_node(g, node);
-//    return LLVMBuildBr(g->builder, dest_block);
-//}
-
-//static LLVMValueRef gen_continue(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeContinue);
-//    LLVMBasicBlockRef dest_block = g->continue_block_stack.last();
-//
-//    set_debug_source_node(g, node);
-//    return LLVMBuildBr(g->builder, dest_block);
-//}
-
-static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl,
-        bool unwrap_maybe, LLVMValueRef *init_value, TypeTableEntry **expr_type, bool var_is_ptr)
-{
-    VariableTableEntry *variable = var_decl->variable;
-
-    assert(variable);
-
-    if (var_decl->expr) {
-        *init_value = gen_expr(g, var_decl->expr);
-        *expr_type = get_expr_type(var_decl->expr);
-    }
-    if (!type_has_bits(variable->type)) {
-        return nullptr;
-    }
-
-    bool have_init_expr = false;
-    bool want_zeroes = false;
-    if (var_decl->expr) {
-        ConstExprValue *const_val = &get_resolved_expr(var_decl->expr)->const_val;
-        if (!const_val->ok || const_val->special == ConstValSpecialOther) {
-            have_init_expr = true;
-        }
-        if (const_val->ok && const_val->special == ConstValSpecialZeroes) {
-            want_zeroes = true;
-        }
-    }
-    if (have_init_expr) {
-        TypeTableEntry *expr_type = get_expr_type(var_decl->expr);
-        LLVMValueRef value;
-        if (unwrap_maybe) {
-            assert(var_decl->expr);
-            assert(expr_type->id == TypeTableEntryIdMaybe);
-            value = gen_unwrap_maybe(g, var_decl->expr, *init_value);
-            expr_type = expr_type->data.maybe.child_type;
-        } else {
-            value = *init_value;
-        }
-        gen_assign_raw(g, var_decl->expr, BinOpTypeAssign, variable->value_ref,
-                value, variable->type, expr_type);
-    } else {
-        bool ignore_uninit = false;
-        // handle runtime stack allocation
-        if (var_decl->type) {
-            TypeTableEntry *var_type = get_type_for_type_node(var_decl->type);
-            if (var_type->id == TypeTableEntryIdStruct &&
-                var_type->data.structure.is_slice)
-            {
-                assert(var_decl->type->type == NodeTypeArrayType);
-                AstNode *size_node = var_decl->type->data.array_type.size;
-                if (size_node) {
-                    ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val;
-                    if (!const_val->ok) {
-                        TypeTableEntry *ptr_type = var_type->data.structure.fields[0].type_entry;
-                        assert(ptr_type->id == TypeTableEntryIdPointer);
-                        TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
-
-                        LLVMValueRef size_val = gen_expr(g, size_node);
-
-                        set_debug_source_node(g, source_node);
-                        LLVMValueRef ptr_val = LLVMBuildArrayAlloca(g->builder, child_type->type_ref,
-                                size_val, "");
-
-                        size_t ptr_index = var_type->data.structure.fields[0].gen_index;
-                        assert(ptr_index != SIZE_MAX);
-                        size_t len_index = var_type->data.structure.fields[1].gen_index;
-                        assert(len_index != SIZE_MAX);
-
-                        // store the freshly allocated pointer in the unknown size array struct
-                        LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder,
-                                variable->value_ref, ptr_index, "");
-                        LLVMBuildStore(g->builder, ptr_val, ptr_field_ptr);
-
-                        // store the size in the len field
-                        LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder,
-                                variable->value_ref, len_index, "");
-                        LLVMBuildStore(g->builder, size_val, len_field_ptr);
-
-                        // don't clobber what we just did with debug initialization
-                        ignore_uninit = true;
-                    }
-                }
-            }
-        }
-        bool want_safe = want_debug_safety(g, source_node);
-        if (!ignore_uninit && (want_safe || want_zeroes)) {
-            TypeTableEntry *usize = g->builtin_types.entry_usize;
-            uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref);
-            uint64_t align_bytes = get_memcpy_align(g, variable->type);
-
-            // memset uninitialized memory to 0xa
-            set_debug_source_node(g, source_node);
-            LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
-            LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), want_zeroes ? 0x00 : 0xaa, false);
-            LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, variable->value_ref, ptr_u8, "");
-            LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false);
-            LLVMValueRef align_in_bytes = LLVMConstInt(LLVMInt32Type(), align_bytes, false);
-            LLVMValueRef params[] = {
-                dest_ptr,
-                fill_char,
-                byte_count,
-                align_in_bytes,
-                LLVMConstNull(LLVMInt1Type()), // is volatile
-            };
-
-            LLVMBuildCall(g->builder, g->memset_fn_val, params, 5, "");
-        }
-    }
-
-    gen_var_debug_decl(g, variable);
-    return nullptr;
-}
-
-static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) {
-    AstNode *init_expr = node->data.variable_declaration.expr;
-    if (node->data.variable_declaration.is_const && init_expr) {
-        TypeTableEntry *init_expr_type = get_expr_type(init_expr);
-        if (init_expr_type->id == TypeTableEntryIdNumLitFloat ||
-            init_expr_type->id == TypeTableEntryIdNumLitInt)
-        {
-            return nullptr;
-        }
-    }
-
-    LLVMValueRef init_val = nullptr;
-    TypeTableEntry *init_val_type;
-    return gen_var_decl_raw(g, node, &node->data.variable_declaration, false, &init_val, &init_val_type, false);
-}
-
-static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeSymbol);
-    VariableTableEntry *variable = get_resolved_expr(node)->variable;
-    if (variable) {
-        return gen_variable(g, node, variable);
-    }
-
-    zig_unreachable();
-}
-
-static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeSwitchExpr);
-
-    if (node->data.switch_expr.const_chosen_prong_index != SIZE_MAX) {
-        AstNode *prong_node = node->data.switch_expr.prongs.at(node->data.switch_expr.const_chosen_prong_index);
-        assert(prong_node->type == NodeTypeSwitchProng);
-        AstNode *prong_expr = prong_node->data.switch_prong.expr;
-        return gen_expr(g, prong_expr);
-    }
-
-    TypeTableEntry *target_type = get_expr_type(node->data.switch_expr.expr);
-    LLVMValueRef target_value_handle = gen_expr(g, node->data.switch_expr.expr);
-    LLVMValueRef target_value;
-    if (handle_is_ptr(target_type)) {
-        if (target_type->id == TypeTableEntryIdEnum) {
-            set_debug_source_node(g, node);
-            LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, 0, "");
-            target_value = LLVMBuildLoad(g->builder, tag_field_ptr, "");
-        } else if (target_type->id == TypeTableEntryIdErrorUnion) {
-            set_debug_source_node(g, node);
-            LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, 0, "");
-            target_value = LLVMBuildLoad(g->builder, tag_field_ptr, "");
-        } else {
-            zig_unreachable();
-        }
-    } else {
-        target_value = target_value_handle;
-    }
-
-
-    TypeTableEntry *switch_type = get_expr_type(node);
-    bool result_has_bits = type_has_bits(switch_type);
-    bool end_unreachable = (switch_type->id == TypeTableEntryIdUnreachable);
-
-    LLVMBasicBlockRef end_block = end_unreachable ?
-        nullptr : LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchEnd");
-    LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchElse");
-    size_t prong_count = node->data.switch_expr.prongs.length;
-
-    set_debug_source_node(g, node);
-    LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, prong_count);
-
-    ZigList<LLVMValueRef> incoming_values = {0};
-    ZigList<LLVMBasicBlockRef> incoming_blocks = {0};
-
-    AstNode *else_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);
-        VariableTableEntry *prong_var = prong_node->data.switch_prong.var;
-
-        LLVMBasicBlockRef prong_block;
-        if (prong_node->data.switch_prong.items.length == 0) {
-            assert(!else_prong);
-            else_prong = prong_node;
-            prong_block = else_block;
-        } else {
-            prong_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchProng");
-            size_t prong_item_count = prong_node->data.switch_prong.items.length;
-            bool make_item_blocks = prong_var && prong_item_count > 1;
-
-            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);
-                LLVMValueRef val;
-                if (target_type->id == TypeTableEntryIdEnum ||
-                    target_type->id == TypeTableEntryIdErrorUnion)
-                {
-                    assert(item_node->type == NodeTypeSymbol);
-                    TypeEnumField *enum_field = nullptr;
-                    uint32_t err_value = 0;
-                    if (target_type->id == TypeTableEntryIdEnum) {
-                        enum_field = item_node->data.symbol_expr.enum_field;
-                        assert(enum_field);
-                        val = LLVMConstInt(target_type->data.enumeration.tag_type->type_ref,
-                                enum_field->value, false);
-                    } else if (target_type->id == TypeTableEntryIdErrorUnion) {
-                        err_value = item_node->data.symbol_expr.err_value;
-                        val = LLVMConstInt(g->err_tag_type->type_ref, err_value, false);
-                    } else {
-                        zig_unreachable();
-                    }
-
-                    if (prong_var && type_has_bits(prong_var->type)) {
-                        LLVMBasicBlockRef item_block;
-
-                        if (make_item_blocks) {
-                            item_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchProngItem");
-                            LLVMAddCase(switch_instr, val, item_block);
-                            LLVMPositionBuilderAtEnd(g->builder, item_block);
-                        } else {
-                            LLVMAddCase(switch_instr, val, prong_block);
-                            LLVMPositionBuilderAtEnd(g->builder, prong_block);
-                        }
-
-                        AstNode *var_node = prong_node->data.switch_prong.var_symbol;
-                        set_debug_source_node(g, var_node);
-                        if (prong_node->data.switch_prong.var_is_target_expr) {
-                            gen_assign_raw(g, var_node, BinOpTypeAssign,
-                                    prong_var->value_ref, target_value, prong_var->type, target_type);
-                        } else if (target_type->id == TypeTableEntryIdEnum) {
-                            assert(enum_field);
-                            assert(type_has_bits(enum_field->type_entry));
-                            LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle,
-                                    1, "");
-                            LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr,
-                                    LLVMPointerType(enum_field->type_entry->type_ref, 0), "");
-                            LLVMValueRef handle_val = get_handle_value(g, bitcasted_union_field_ptr,
-                                    enum_field->type_entry);
-
-                            gen_assign_raw(g, var_node, BinOpTypeAssign,
-                                    prong_var->value_ref, handle_val, prong_var->type, enum_field->type_entry);
-                        } else if (target_type->id == TypeTableEntryIdErrorUnion) {
-                            if (err_value == 0) {
-                                // variable is the payload
-                                LLVMValueRef err_payload_ptr = LLVMBuildStructGEP(g->builder,
-                                        target_value_handle, 1, "");
-                                LLVMValueRef handle_val = get_handle_value(g, err_payload_ptr, prong_var->type);
-                                gen_assign_raw(g, var_node, BinOpTypeAssign,
-                                        prong_var->value_ref, handle_val, prong_var->type, prong_var->type);
-                            } else {
-                                // variable is the pure error value
-                                LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder,
-                                        target_value_handle, 0, "");
-                                LLVMValueRef handle_val = LLVMBuildLoad(g->builder, err_tag_ptr, "");
-                                gen_assign_raw(g, var_node, BinOpTypeAssign,
-                                        prong_var->value_ref, handle_val, prong_var->type, g->err_tag_type);
-                            }
-                        } else {
-                            zig_unreachable();
-                        }
-                        if (make_item_blocks) {
-                            set_debug_source_node(g, var_node);
-                            LLVMBuildBr(g->builder, prong_block);
-                        }
-                    } else {
-                        LLVMAddCase(switch_instr, val, prong_block);
-                    }
-                } else {
-                    assert(get_resolved_expr(item_node)->const_val.ok);
-                    val = gen_expr(g, item_node);
-                    LLVMAddCase(switch_instr, val, prong_block);
-                }
-            }
-        }
-
-        LLVMPositionBuilderAtEnd(g->builder, prong_block);
-        AstNode *prong_expr = prong_node->data.switch_prong.expr;
-        LLVMValueRef prong_val = gen_expr(g, prong_expr);
-
-        if (get_expr_type(prong_expr)->id != TypeTableEntryIdUnreachable) {
-            set_debug_source_node(g, prong_expr);
-            LLVMBuildBr(g->builder, end_block);
-            incoming_values.append(prong_val);
-            incoming_blocks.append(LLVMGetInsertBlock(g->builder));
-        }
-    }
-
-    if (!else_prong) {
-        LLVMPositionBuilderAtEnd(g->builder, else_block);
-        set_debug_source_node(g, node);
-        if (want_debug_safety(g, node)) {
-            gen_debug_safety_crash(g);
-        } else {
-            LLVMBuildUnreachable(g->builder);
-        }
-    }
-
-    if (end_unreachable) {
-        return nullptr;
-    }
-
-    LLVMPositionBuilderAtEnd(g->builder, end_block);
-
-    if (result_has_bits) {
-        set_debug_source_node(g, node);
-        LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(incoming_values.at(0)), "");
-        LLVMAddIncoming(phi, incoming_values.items, incoming_blocks.items, incoming_values.length);
-        return phi;
-    } else {
-        return nullptr;
-    }
-}
-
-static LLVMValueRef gen_goto(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeGoto);
-
-    // generate defers for blocks that we exit
-    LabelTableEntry *label = node->data.goto_expr.label_entry;
-    BlockContext *this_context = node->block_context;
-    BlockContext *target_context = label->decl_node->block_context;
-    gen_defers_for_block(g, this_context, target_context, false, false);
-
-    set_debug_source_node(g, node);
-    LLVMBuildBr(g->builder, node->data.goto_expr.label_entry->basic_block);
-    return nullptr;
-}
-
-static LLVMValueRef gen_label(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeLabel);
-
-    LabelTableEntry *label = node->data.label.label_entry;
-    assert(label);
-
-    LLVMBasicBlockRef basic_block = label->basic_block;
-    if (label->entered_from_fallthrough) {
-        set_debug_source_node(g, node);
-        LLVMBuildBr(g->builder, basic_block);
-    }
-    LLVMPositionBuilderAtEnd(g->builder, basic_block);
-    return nullptr;
-}
-
-static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
-    Expr *expr = get_resolved_expr(node);
-    if (expr->const_val.ok) {
-        if (!type_has_bits(expr->type_entry)) {
-            return nullptr;
-        } else {
-            assert(expr->const_llvm_val);
-            return expr->const_llvm_val;
-        }
-    }
-    switch (node->type) {
-        case NodeTypeBinOpExpr:
-            return gen_bin_op_expr(g, node);
-        case NodeTypeUnwrapErrorExpr:
-            return gen_unwrap_err_expr(g, node);
-        case NodeTypeReturnExpr:
-            return gen_return_expr(g, node);
-        case NodeTypeDefer:
-            // nothing to do
-            return nullptr;
-        case NodeTypeVariableDeclaration:
-            return gen_var_decl_expr(g, node);
-        case NodeTypePrefixOpExpr:
-            zig_panic("moved to ir render");
-        case NodeTypeFnCallExpr:
-            return gen_fn_call_expr(g, node);
-        case NodeTypeArrayAccessExpr:
-            return gen_array_access_expr(g, node, false);
-        case NodeTypeSliceExpr:
-            return gen_slice_expr(g, node);
-        case NodeTypeFieldAccessExpr:
-            return gen_field_access_expr(g, node, false);
-        case NodeTypeIfBoolExpr:
-            return gen_if_bool_expr(g, node);
-        case NodeTypeIfVarExpr:
-            return gen_if_var_expr(g, node);
-        case NodeTypeWhileExpr:
-            return gen_while_expr(g, node);
-        case NodeTypeForExpr:
-            zig_panic("moved to ir render");
-        case NodeTypeAsmExpr:
-            return gen_asm_expr(g, node);
-        case NodeTypeSymbol:
-            return gen_symbol(g, node);
-        case NodeTypeBlock:
-            return gen_block(g, node, nullptr);
-        case NodeTypeGoto:
-            return gen_goto(g, node);
-        case NodeTypeBreak:
-            zig_panic("TODO IR");
-        case NodeTypeContinue:
-            zig_panic("TODO IR");
-        case NodeTypeLabel:
-            return gen_label(g, node);
-        case NodeTypeContainerInitExpr:
-            return gen_container_init_expr(g, node);
-        case NodeTypeSwitchExpr:
-            return gen_switch_expr(g, node);
-        case NodeTypeNumberLiteral:
-        case NodeTypeBoolLiteral:
-        case NodeTypeStringLiteral:
-        case NodeTypeCharLiteral:
-        case NodeTypeNullLiteral:
-        case NodeTypeUndefinedLiteral:
-        case NodeTypeZeroesLiteral:
-        case NodeTypeThisLiteral:
-        case NodeTypeErrorType:
-        case NodeTypeTypeLiteral:
-        case NodeTypeArrayType:
-        case NodeTypeVarLiteral:
-            // caught by constant expression eval codegen
-            zig_unreachable();
-        case NodeTypeRoot:
-        case NodeTypeFnProto:
-        case NodeTypeFnDef:
-        case NodeTypeFnDecl:
-        case NodeTypeParamDecl:
-        case NodeTypeUse:
-        case NodeTypeContainerDecl:
-        case NodeTypeStructField:
-        case NodeTypeStructValueField:
-        case NodeTypeSwitchProng:
-        case NodeTypeSwitchRange:
-        case NodeTypeErrorValueDecl:
-        case NodeTypeTypeDecl:
-            zig_unreachable();
-    }
-    zig_unreachable();
-}
-
 static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val) {
     assert(const_val->ok);
 
src/ir.cpp
@@ -1693,7 +1693,7 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont
         case NodeTypeSwitchRange:
         case NodeTypeErrorValueDecl:
         case NodeTypeTypeDecl:
-            zig_panic("TODO more IR gen");
+            zig_panic("TODO more IR gen for node types");
     }
     zig_unreachable();
 }
@@ -3966,6 +3966,8 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
     return value;
 }
 
+// TODO port over all this commented out code into new IR way of doing things
+
 //static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context,
 //        AstNode *node, const char *err_format, bool is_max)
 //{
@@ -7401,3 +7403,1991 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
 //    return nullptr;
 //}
 
+//
+//static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeFnCallExpr);
+//    assert(g->generate_error_name_table);
+//
+//    if (g->error_decls.length == 1) {
+//        LLVMBuildUnreachable(g->builder);
+//        return nullptr;
+//    }
+//
+//
+//    AstNode *err_val_node = node->data.fn_call_expr.params.at(0);
+//    LLVMValueRef err_val = gen_expr(g, err_val_node);
+//
+//    if (want_debug_safety(g, node)) {
+//        LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val));
+//        LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->error_decls.length, false);
+//        add_bounds_check(g, err_val, LLVMIntNE, zero, LLVMIntULT, end_val);
+//    }
+//
+//    LLVMValueRef indices[] = {
+//        LLVMConstNull(g->builtin_types.entry_usize->type_ref),
+//        err_val,
+//    };
+//    return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, "");
+//}
+//
+//static LLVMValueRef gen_cmp_exchange(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeFnCallExpr);
+//
+//    AstNode *ptr_arg = node->data.fn_call_expr.params.at(0);
+//    AstNode *cmp_arg = node->data.fn_call_expr.params.at(1);
+//    AstNode *new_arg = node->data.fn_call_expr.params.at(2);
+//    AstNode *success_order_arg = node->data.fn_call_expr.params.at(3);
+//    AstNode *failure_order_arg = node->data.fn_call_expr.params.at(4);
+//
+//    LLVMValueRef ptr_val = gen_expr(g, ptr_arg);
+//    LLVMValueRef cmp_val = gen_expr(g, cmp_arg);
+//    LLVMValueRef new_val = gen_expr(g, new_arg);
+//
+//    ConstExprValue *success_order_val = &get_resolved_expr(success_order_arg)->const_val;
+//    ConstExprValue *failure_order_val = &get_resolved_expr(failure_order_arg)->const_val;
+//
+//    assert(success_order_val->ok);
+//    assert(failure_order_val->ok);
+//
+//    LLVMAtomicOrdering success_order = to_LLVMAtomicOrdering((AtomicOrder)success_order_val->data.x_enum.tag);
+//    LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering((AtomicOrder)failure_order_val->data.x_enum.tag);
+//
+//    LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,
+//            success_order, failure_order);
+//
+//    return LLVMBuildExtractValue(g->builder, result_val, 1, "");
+//}
+//
+//static LLVMValueRef gen_div_exact(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeFnCallExpr);
+//
+//    AstNode *op1_node = node->data.fn_call_expr.params.at(0);
+//    AstNode *op2_node = node->data.fn_call_expr.params.at(1);
+//
+//    LLVMValueRef op1_val = gen_expr(g, op1_node);
+//    LLVMValueRef op2_val = gen_expr(g, op2_node);
+//
+//    return gen_div(g, node, op1_val, op2_val, get_expr_type(op1_node), true);
+//}
+//
+//static LLVMValueRef gen_truncate(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeFnCallExpr);
+//
+//    TypeTableEntry *dest_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
+//    AstNode *src_node = node->data.fn_call_expr.params.at(1);
+//
+//    LLVMValueRef src_val = gen_expr(g, src_node);
+//
+//    return LLVMBuildTrunc(g->builder, src_val, dest_type->type_ref, "");
+//}
+//
+//static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeFnCallExpr);
+//
+//    size_t fn_call_param_count = node->data.fn_call_expr.params.length;
+//    assert(fn_call_param_count == 4);
+//
+//    TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
+//    assert(int_type->id == TypeTableEntryIdInt);
+//
+//    LLVMValueRef val1 = gen_expr(g, node->data.fn_call_expr.params.at(1));
+//    LLVMValueRef val2 = gen_expr(g, node->data.fn_call_expr.params.at(2));
+//    LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3));
+//
+//    LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, "");
+//    LLVMValueRef orig_val;
+//    if (int_type->data.integral.is_signed) {
+//        orig_val = LLVMBuildAShr(g->builder, result, val2, "");
+//    } else {
+//        orig_val = LLVMBuildLShr(g->builder, result, val2, "");
+//    }
+//    LLVMValueRef overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, val1, orig_val, "");
+//
+//    LLVMBuildStore(g->builder, result, ptr_result);
+//
+//    return overflow_bit;
+//}
+//
+//static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeFnCallExpr);
+//    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
+//    assert(fn_ref_expr->type == NodeTypeSymbol);
+//    BuiltinFnEntry *builtin_fn = node->data.fn_call_expr.builtin_fn;
+//
+//    switch (builtin_fn->id) {
+//        case BuiltinFnIdInvalid:
+//        case BuiltinFnIdTypeof:
+//        case BuiltinFnIdCInclude:
+//        case BuiltinFnIdCDefine:
+//        case BuiltinFnIdCUndef:
+//        case BuiltinFnIdImport:
+//        case BuiltinFnIdCImport:
+//        case BuiltinFnIdCompileErr:
+//        case BuiltinFnIdIntType:
+//            zig_unreachable();
+//        case BuiltinFnIdCtz:
+//        case BuiltinFnIdClz:
+//            {
+//                size_t fn_call_param_count = node->data.fn_call_expr.params.length;
+//                assert(fn_call_param_count == 2);
+//                TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
+//                assert(int_type->id == TypeTableEntryIdInt);
+//                LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, builtin_fn->id);
+//                LLVMValueRef operand = gen_expr(g, node->data.fn_call_expr.params.at(1));
+//                LLVMValueRef params[] {
+//                    operand,
+//                    LLVMConstNull(LLVMInt1Type()),
+//                };
+//                return LLVMBuildCall(g->builder, fn_val, params, 2, "");
+//            }
+//        case BuiltinFnIdAddWithOverflow:
+//        case BuiltinFnIdSubWithOverflow:
+//        case BuiltinFnIdMulWithOverflow:
+//            {
+//                size_t fn_call_param_count = node->data.fn_call_expr.params.length;
+//                assert(fn_call_param_count == 4);
+//
+//                TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
+//                AddSubMul add_sub_mul;
+//                if (builtin_fn->id == BuiltinFnIdAddWithOverflow) {
+//                    add_sub_mul = AddSubMulAdd;
+//                } else if (builtin_fn->id == BuiltinFnIdSubWithOverflow) {
+//                    add_sub_mul = AddSubMulSub;
+//                } else if (builtin_fn->id == BuiltinFnIdMulWithOverflow) {
+//                    add_sub_mul = AddSubMulMul;
+//                } else {
+//                    zig_unreachable();
+//                }
+//                LLVMValueRef fn_val = get_int_overflow_fn(g, int_type, add_sub_mul);
+//
+//                LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(1));
+//                LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(2));
+//                LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3));
+//
+//                LLVMValueRef params[] = {
+//                    op1,
+//                    op2,
+//                };
+//
+//                LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
+//                LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
+//                LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
+//                LLVMBuildStore(g->builder, result, ptr_result);
+//
+//                return overflow_bit;
+//            }
+//        case BuiltinFnIdShlWithOverflow:
+//            return gen_shl_with_overflow(g, node);
+//        case BuiltinFnIdMemcpy:
+//            {
+//                size_t fn_call_param_count = node->data.fn_call_expr.params.length;
+//                assert(fn_call_param_count == 3);
+//
+//                AstNode *dest_node = node->data.fn_call_expr.params.at(0);
+//                TypeTableEntry *dest_type = get_expr_type(dest_node);
+//
+//                LLVMValueRef dest_ptr = gen_expr(g, dest_node);
+//                LLVMValueRef src_ptr = gen_expr(g, node->data.fn_call_expr.params.at(1));
+//                LLVMValueRef len_val = gen_expr(g, node->data.fn_call_expr.params.at(2));
+//
+//                LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
+//
+//                LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
+//                LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, ptr_u8, "");
+//
+//                uint64_t align_in_bytes = get_memcpy_align(g, dest_type->data.pointer.child_type);
+//
+//                LLVMValueRef params[] = {
+//                    dest_ptr_casted, // dest pointer
+//                    src_ptr_casted, // source pointer
+//                    len_val, // byte count
+//                    LLVMConstInt(LLVMInt32Type(), align_in_bytes, false), // align in bytes
+//                    LLVMConstNull(LLVMInt1Type()), // is volatile
+//                };
+//
+//                LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 5, "");
+//                return nullptr;
+//            }
+//        case BuiltinFnIdMemset:
+//            {
+//                size_t fn_call_param_count = node->data.fn_call_expr.params.length;
+//                assert(fn_call_param_count == 3);
+//
+//                AstNode *dest_node = node->data.fn_call_expr.params.at(0);
+//                TypeTableEntry *dest_type = get_expr_type(dest_node);
+//
+//                LLVMValueRef dest_ptr = gen_expr(g, dest_node);
+//                LLVMValueRef char_val = gen_expr(g, node->data.fn_call_expr.params.at(1));
+//                LLVMValueRef len_val = gen_expr(g, node->data.fn_call_expr.params.at(2));
+//
+//                LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
+//
+//                LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
+//
+//                uint64_t align_in_bytes = get_memcpy_align(g, dest_type->data.pointer.child_type);
+//
+//                LLVMValueRef params[] = {
+//                    dest_ptr_casted, // dest pointer
+//                    char_val, // source pointer
+//                    len_val, // byte count
+//                    LLVMConstInt(LLVMInt32Type(), align_in_bytes, false), // align in bytes
+//                    LLVMConstNull(LLVMInt1Type()), // is volatile
+//                };
+//
+//                LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 5, "");
+//                return nullptr;
+//            }
+//        case BuiltinFnIdSizeof:
+//        case BuiltinFnIdAlignof:
+//        case BuiltinFnIdMinValue:
+//        case BuiltinFnIdMaxValue:
+//        case BuiltinFnIdMemberCount:
+//        case BuiltinFnIdConstEval:
+//        case BuiltinFnIdEmbedFile:
+//            // caught by constant expression eval codegen
+//            zig_unreachable();
+//        case BuiltinFnIdCompileVar:
+//            return nullptr;
+//        case BuiltinFnIdErrName:
+//            return gen_err_name(g, node);
+//        case BuiltinFnIdBreakpoint:
+//            return LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
+//        case BuiltinFnIdFrameAddress:
+//        case BuiltinFnIdReturnAddress:
+//            {
+//                LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref);
+//                return LLVMBuildCall(g->builder, builtin_fn->fn_val, &zero, 1, "");
+//            }
+//        case BuiltinFnIdCmpExchange:
+//            return gen_cmp_exchange(g, node);
+//        case BuiltinFnIdFence:
+//            return gen_fence(g, node);
+//        case BuiltinFnIdDivExact:
+//            return gen_div_exact(g, node);
+//        case BuiltinFnIdTruncate:
+//            return gen_truncate(g, node);
+//        case BuiltinFnIdUnreachable:
+//            zig_panic("moved to ir render");
+//        case BuiltinFnIdSetFnTest:
+//        case BuiltinFnIdSetFnVisible:
+//        case BuiltinFnIdSetFnStaticEval:
+//        case BuiltinFnIdSetFnNoInline:
+//        case BuiltinFnIdSetDebugSafety:
+//            // do nothing
+//            return nullptr;
+//    }
+//    zig_unreachable();
+//}
+//
+//static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntry *enum_type,
+//        AstNode *arg_node)
+//{
+//    assert(node->type == NodeTypeFieldAccessExpr);
+//
+//    uint64_t value = node->data.field_access_expr.type_enum_field->value;
+//    LLVMTypeRef tag_type_ref = enum_type->data.enumeration.tag_type->type_ref;
+//    LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, value, false);
+//
+//    if (enum_type->data.enumeration.gen_field_count == 0) {
+//        return tag_value;
+//    } else {
+//        TypeTableEntry *arg_node_type = nullptr;
+//        LLVMValueRef new_union_val = gen_expr(g, arg_node);
+//        if (arg_node) {
+//            arg_node_type = get_expr_type(arg_node);
+//        } else {
+//            arg_node_type = g->builtin_types.entry_void;
+//        }
+//
+//        LLVMValueRef tmp_struct_ptr = node->data.field_access_expr.resolved_struct_val_expr.ptr;
+//
+//        // populate the new tag value
+//        LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
+//        LLVMBuildStore(g->builder, tag_value, tag_field_ptr);
+//
+//        if (arg_node_type->id != TypeTableEntryIdVoid) {
+//            // populate the union value
+//            TypeTableEntry *union_val_type = get_expr_type(arg_node);
+//            LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
+//            LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr,
+//                    LLVMPointerType(union_val_type->type_ref, 0), "");
+//
+//            gen_assign_raw(g, arg_node, BinOpTypeAssign, bitcasted_union_field_ptr, new_union_val,
+//                    union_val_type, union_val_type);
+//
+//        }
+//
+//        return tmp_struct_ptr;
+//    }
+//}
+//
+//static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeFnCallExpr);
+//
+//    if (node->data.fn_call_expr.is_builtin) {
+//        return gen_builtin_fn_call_expr(g, node);
+//    }
+//
+//    FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
+//    TypeTableEntry *struct_type = nullptr;
+//    AstNode *first_param_expr = nullptr;
+//
+//    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
+//    if (fn_ref_expr->type == NodeTypeFieldAccessExpr &&
+//        fn_ref_expr->data.field_access_expr.is_member_fn)
+//    {
+//        first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr;
+//        struct_type = get_expr_type(first_param_expr);
+//    }
+//
+//    TypeTableEntry *fn_type;
+//    LLVMValueRef fn_val;
+//    AstNode *generic_proto_node;
+//    if (fn_table_entry) {
+//        fn_val = fn_table_entry->fn_value;
+//        fn_type = fn_table_entry->type_entry;
+//        generic_proto_node = fn_table_entry->proto_node->data.fn_proto.generic_proto_node;
+//    } else {
+//        fn_val = gen_expr(g, fn_ref_expr);
+//        fn_type = get_expr_type(fn_ref_expr);
+//        generic_proto_node = nullptr;
+//    }
+//
+//    TypeTableEntry *src_return_type = fn_type->data.fn.fn_type_id.return_type;
+//
+//    bool ret_has_bits = type_has_bits(src_return_type);
+//
+//    size_t fn_call_param_count = node->data.fn_call_expr.params.length;
+//    bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type);
+//    size_t actual_param_count = fn_call_param_count + (struct_type ? 1 : 0) + (first_arg_ret ? 1 : 0);
+//    bool is_var_args = fn_type->data.fn.fn_type_id.is_var_args;
+//
+//    // don't really include void values
+//    LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);
+//
+//    size_t gen_param_index = 0;
+//    if (first_arg_ret) {
+//        gen_param_values[gen_param_index] = node->data.fn_call_expr.tmp_ptr;
+//        gen_param_index += 1;
+//    }
+//    if (struct_type && type_has_bits(struct_type)) {
+//        gen_param_values[gen_param_index] = gen_expr(g, first_param_expr);
+//        assert(gen_param_values[gen_param_index]);
+//        gen_param_index += 1;
+//    }
+//
+//    for (size_t call_i = 0; call_i < fn_call_param_count; call_i += 1) {
+//        size_t proto_i = call_i + (struct_type ? 1 : 0);
+//        if (generic_proto_node &&
+//            generic_proto_node->data.fn_proto.params.at(proto_i)->data.param_decl.is_inline)
+//        {
+//            continue;
+//        }
+//        AstNode *expr_node = node->data.fn_call_expr.params.at(call_i);
+//        LLVMValueRef param_value = gen_expr(g, expr_node);
+//        assert(param_value);
+//        TypeTableEntry *param_type = get_expr_type(expr_node);
+//        if (is_var_args || type_has_bits(param_type)) {
+//            gen_param_values[gen_param_index] = param_value;
+//            gen_param_index += 1;
+//        }
+//    }
+//
+//    LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val,
+//            gen_param_values, gen_param_index, fn_type->data.fn.calling_convention, "");
+//
+//    if (src_return_type->id == TypeTableEntryIdUnreachable) {
+//        return LLVMBuildUnreachable(g->builder);
+//    } else if (!ret_has_bits) {
+//        return nullptr;
+//    } else if (first_arg_ret) {
+//        return node->data.fn_call_expr.tmp_ptr;
+//    } else {
+//        return result;
+//    }
+//}
+//
+//static LLVMValueRef gen_array_base_ptr(CodeGen *g, AstNode *node) {
+//    TypeTableEntry *type_entry = get_expr_type(node);
+//
+//    LLVMValueRef array_ptr;
+//    if (node->type == NodeTypeFieldAccessExpr) {
+//        array_ptr = gen_field_access_expr(g, node, true);
+//        if (type_entry->id == TypeTableEntryIdPointer) {
+//            // we have a double pointer so we must dereference it once
+//            array_ptr = LLVMBuildLoad(g->builder, array_ptr, "");
+//        }
+//    } else {
+//        array_ptr = gen_expr(g, node);
+//    }
+//
+//    assert(!array_ptr || LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
+//
+//    return array_ptr;
+//}
+//
+//static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeArrayAccessExpr);
+//
+//    AstNode *array_expr_node = node->data.array_access_expr.array_ref_expr;
+//    TypeTableEntry *array_type = get_expr_type(array_expr_node);
+//
+//    LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node);
+//
+//    LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript);
+//    return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value);
+//}
+//
+//static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **out_type_entry) {
+//    assert(node->type == NodeTypeFieldAccessExpr);
+//
+//    AstNode *struct_expr_node = node->data.field_access_expr.struct_expr;
+//
+//    *out_type_entry = node->data.field_access_expr.type_struct_field->type_entry;
+//    if (!type_has_bits(*out_type_entry)) {
+//        return nullptr;
+//    }
+//
+//    LLVMValueRef struct_ptr;
+//    if (struct_expr_node->type == NodeTypeSymbol) {
+//        VariableTableEntry *var = get_resolved_expr(struct_expr_node)->variable;
+//        assert(var);
+//
+//        if (var->type->id == TypeTableEntryIdPointer) {
+//            struct_ptr = LLVMBuildLoad(g->builder, var->value_ref, "");
+//        } else {
+//            struct_ptr = var->value_ref;
+//        }
+//    } else if (struct_expr_node->type == NodeTypeFieldAccessExpr) {
+//        struct_ptr = gen_field_access_expr(g, struct_expr_node, true);
+//        TypeTableEntry *field_type = get_expr_type(struct_expr_node);
+//        if (field_type->id == TypeTableEntryIdPointer) {
+//            // we have a double pointer so we must dereference it once
+//            struct_ptr = LLVMBuildLoad(g->builder, struct_ptr, "");
+//        }
+//    } else {
+//        struct_ptr = gen_expr(g, struct_expr_node);
+//    }
+//
+//    assert(LLVMGetTypeKind(LLVMTypeOf(struct_ptr)) == LLVMPointerTypeKind);
+//    assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(struct_ptr))) == LLVMStructTypeKind);
+//
+//    size_t gen_field_index = node->data.field_access_expr.type_struct_field->gen_index;
+//    assert(gen_field_index != SIZE_MAX);
+//
+//    return LLVMBuildStructGEP(g->builder, struct_ptr, gen_field_index, "");
+//}
+//
+//static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeSliceExpr);
+//
+//    AstNode *array_ref_node = node->data.slice_expr.array_ref_expr;
+//    TypeTableEntry *array_type = get_expr_type(array_ref_node);
+//
+//    LLVMValueRef tmp_struct_ptr = node->data.slice_expr.resolved_struct_val_expr.ptr;
+//    LLVMValueRef array_ptr = gen_array_base_ptr(g, array_ref_node);
+//
+//    if (array_type->id == TypeTableEntryIdArray) {
+//        LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
+//        LLVMValueRef end_val;
+//        if (node->data.slice_expr.end) {
+//            end_val = gen_expr(g, node->data.slice_expr.end);
+//        } else {
+//            end_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false);
+//        }
+//
+//        if (want_debug_safety(g, node)) {
+//            add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
+//            if (node->data.slice_expr.end) {
+//                LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
+//                        array_type->data.array.len, false);
+//                add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);
+//            }
+//        }
+//
+//        LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
+//        LLVMValueRef indices[] = {
+//            LLVMConstNull(g->builtin_types.entry_usize->type_ref),
+//            start_val,
+//        };
+//        LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
+//        LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
+//
+//        LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
+//        LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
+//        LLVMBuildStore(g->builder, len_value, len_field_ptr);
+//
+//        return tmp_struct_ptr;
+//    } else if (array_type->id == TypeTableEntryIdPointer) {
+//        LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
+//        LLVMValueRef end_val = gen_expr(g, node->data.slice_expr.end);
+//
+//        if (want_debug_safety(g, node)) {
+//            add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
+//        }
+//
+//        LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
+//        LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
+//        LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
+//
+//        LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 1, "");
+//        LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
+//        LLVMBuildStore(g->builder, len_value, len_field_ptr);
+//
+//        return tmp_struct_ptr;
+//    } else if (array_type->id == TypeTableEntryIdStruct) {
+//        assert(array_type->data.structure.is_slice);
+//        assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
+//        assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
+//
+//        size_t ptr_index = array_type->data.structure.fields[0].gen_index;
+//        assert(ptr_index != SIZE_MAX);
+//        size_t len_index = array_type->data.structure.fields[1].gen_index;
+//        assert(len_index != SIZE_MAX);
+//
+//        LLVMValueRef prev_end = nullptr;
+//        if (!node->data.slice_expr.end || want_debug_safety(g, node)) {
+//            LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, "");
+//            prev_end = LLVMBuildLoad(g->builder, src_len_ptr, "");
+//        }
+//
+//        LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
+//        LLVMValueRef end_val;
+//        if (node->data.slice_expr.end) {
+//            end_val = gen_expr(g, node->data.slice_expr.end);
+//        } else {
+//            end_val = prev_end;
+//        }
+//
+//        if (want_debug_safety(g, node)) {
+//            assert(prev_end);
+//            add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
+//            if (node->data.slice_expr.end) {
+//                add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end);
+//            }
+//        }
+//
+//        LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, "");
+//        LLVMValueRef src_ptr = LLVMBuildLoad(g->builder, src_ptr_ptr, "");
+//        LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, ptr_index, "");
+//        LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, len_index, "");
+//        LLVMBuildStore(g->builder, slice_start_ptr, ptr_field_ptr);
+//
+//        LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, len_index, "");
+//        LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
+//        LLVMBuildStore(g->builder, len_value, len_field_ptr);
+//
+//        return tmp_struct_ptr;
+//    } else {
+//        zig_unreachable();
+//    }
+//}
+//
+//
+//static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node,
+//        TypeTableEntry **out_type_entry)
+//{
+//    LLVMValueRef target_ref;
+//
+//    if (node->type == NodeTypeSymbol) {
+//        VariableTableEntry *var = get_resolved_expr(node)->variable;
+//        assert(var);
+//
+//        *out_type_entry = var->type;
+//        target_ref = var->value_ref;
+//    } else if (node->type == NodeTypeArrayAccessExpr) {
+//        TypeTableEntry *array_type = get_expr_type(node->data.array_access_expr.array_ref_expr);
+//        if (array_type->id == TypeTableEntryIdArray) {
+//            *out_type_entry = array_type->data.array.child_type;
+//            target_ref = gen_array_ptr(g, node);
+//        } else if (array_type->id == TypeTableEntryIdPointer) {
+//            *out_type_entry = array_type->data.pointer.child_type;
+//            target_ref = gen_array_ptr(g, node);
+//        } else if (array_type->id == TypeTableEntryIdStruct) {
+//            assert(array_type->data.structure.is_slice);
+//            *out_type_entry = array_type->data.structure.fields[0].type_entry->data.pointer.child_type;
+//            target_ref = gen_array_ptr(g, node);
+//        } else {
+//            zig_unreachable();
+//        }
+//    } else if (node->type == NodeTypeFieldAccessExpr) {
+//        AstNode *struct_expr_node = node->data.field_access_expr.struct_expr;
+//        TypeTableEntry *struct_type = get_expr_type(struct_expr_node);
+//        if (struct_type->id == TypeTableEntryIdNamespace) {
+//            target_ref = gen_field_access_expr(g, node, true);
+//            *out_type_entry = get_expr_type(node);
+//        } else {
+//            target_ref = gen_field_ptr(g, node, out_type_entry);
+//        }
+//    } else if (node->type == NodeTypePrefixOpExpr) {
+//        assert(node->data.prefix_op_expr.prefix_op == PrefixOpDereference);
+//        AstNode *target_expr = node->data.prefix_op_expr.primary_expr;
+//        TypeTableEntry *type_entry = get_expr_type(target_expr);
+//        assert(type_entry->id == TypeTableEntryIdPointer);
+//        *out_type_entry = type_entry->data.pointer.child_type;
+//        return gen_expr(g, target_expr);
+//    } else {
+//        zig_panic("bad assign target");
+//    }
+//
+//    return target_ref;
+//}
+//
+//static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeBinOpExpr);
+//
+//    LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1);
+//    LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
+//
+//    TypeTableEntry *op1_type = get_expr_type(node->data.bin_op_expr.op1);
+//    TypeTableEntry *op2_type = get_expr_type(node->data.bin_op_expr.op2);
+//    return gen_arithmetic_bin_op(g, node, val1, val2, op1_type, op2_type, node->data.bin_op_expr.bin_op);
+//
+//}
+//
+//static LLVMValueRef gen_bool_and_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeBinOpExpr);
+//
+//    LLVMValueRef val1 = gen_expr(g, node->data.bin_op_expr.op1);
+//    LLVMBasicBlockRef post_val1_block = LLVMGetInsertBlock(g->builder);
+//
+//    // block for when val1 == true
+//    LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndTrue");
+//    // block for when val1 == false (don't even evaluate the second part)
+//    LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolAndFalse");
+//
+//    LLVMBuildCondBr(g->builder, val1, true_block, false_block);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, true_block);
+//    LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
+//    LLVMBasicBlockRef post_val2_block = LLVMGetInsertBlock(g->builder);
+//
+//    LLVMBuildBr(g->builder, false_block);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, false_block);
+//    LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
+//    LLVMValueRef incoming_values[2] = {val1, val2};
+//    LLVMBasicBlockRef incoming_blocks[2] = {post_val1_block, post_val2_block};
+//    LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
+//
+//    return phi;
+//}
+//
+//static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
+//    assert(expr_node->type == NodeTypeBinOpExpr);
+//
+//    LLVMValueRef val1 = gen_expr(g, expr_node->data.bin_op_expr.op1);
+//    LLVMBasicBlockRef post_val1_block = LLVMGetInsertBlock(g->builder);
+//
+//    // block for when val1 == false
+//    LLVMBasicBlockRef false_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrFalse");
+//    // block for when val1 == true (don't even evaluate the second part)
+//    LLVMBasicBlockRef true_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoolOrTrue");
+//
+//    LLVMBuildCondBr(g->builder, val1, true_block, false_block);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, false_block);
+//    LLVMValueRef val2 = gen_expr(g, expr_node->data.bin_op_expr.op2);
+//
+//    LLVMBasicBlockRef post_val2_block = LLVMGetInsertBlock(g->builder);
+//
+//    LLVMBuildBr(g->builder, true_block);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, true_block);
+//    LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMInt1Type(), "");
+//    LLVMValueRef incoming_values[2] = {val1, val2};
+//    LLVMBasicBlockRef incoming_blocks[2] = {post_val1_block, post_val2_block};
+//    LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
+//
+//    return phi;
+//}
+//
+//static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeBinOpExpr);
+//
+//    AstNode *lhs_node = node->data.bin_op_expr.op1;
+//
+//    TypeTableEntry *op1_type;
+//
+//    LLVMValueRef target_ref = gen_lvalue(g, node, lhs_node, &op1_type);
+//
+//    TypeTableEntry *op2_type = get_expr_type(node->data.bin_op_expr.op2);
+//
+//    LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2);
+//
+//    gen_assign_raw(g, node, node->data.bin_op_expr.bin_op, target_ref, value, op1_type, op2_type);
+//    return nullptr;
+//}
+//
+//static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeBinOpExpr);
+//    assert(node->data.bin_op_expr.bin_op == BinOpTypeUnwrapMaybe);
+//
+//    AstNode *op1_node = node->data.bin_op_expr.op1;
+//    AstNode *op2_node = node->data.bin_op_expr.op2;
+//
+//    LLVMValueRef maybe_struct_ref = gen_expr(g, op1_node);
+//
+//    TypeTableEntry *maybe_type = get_expr_type(op1_node);
+//    assert(maybe_type->id == TypeTableEntryIdMaybe);
+//    TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
+//
+//    LLVMValueRef cond_value;
+//    if (child_type->id == TypeTableEntryIdPointer ||
+//        child_type->id == TypeTableEntryIdFn)
+//    {
+//        cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, maybe_struct_ref,
+//                LLVMConstNull(child_type->type_ref), "");
+//    } else {
+//        LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, "");
+//        cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
+//    }
+//
+//    LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNonNull");
+//    LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNull");
+//    LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEnd");
+//
+//    bool null_reachable = get_expr_type(op2_node)->id != TypeTableEntryIdUnreachable;
+//
+//    LLVMBuildCondBr(g->builder, cond_value, non_null_block, null_block);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, non_null_block);
+//    LLVMValueRef non_null_result = gen_unwrap_maybe(g, op1_node, maybe_struct_ref);
+//    LLVMBuildBr(g->builder, end_block);
+//    LLVMBasicBlockRef post_non_null_result_block = LLVMGetInsertBlock(g->builder);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, null_block);
+//    LLVMValueRef null_result = gen_expr(g, op2_node);
+//    if (null_reachable) {
+//        LLVMBuildBr(g->builder, end_block);
+//    }
+//    LLVMBasicBlockRef post_null_result_block = LLVMGetInsertBlock(g->builder);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, end_block);
+//    if (null_reachable) {
+//        LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), "");
+//        LLVMValueRef incoming_values[2] = {non_null_result, null_result};
+//        LLVMBasicBlockRef incoming_blocks[2] = {post_non_null_result_block, post_null_result_block};
+//        LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
+//        return phi;
+//    } else {
+//        return non_null_result;
+//    }
+//
+//    return nullptr;
+//}
+//
+//static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeUnwrapErrorExpr);
+//
+//    AstNode *op1 = node->data.unwrap_err_expr.op1;
+//    AstNode *op2 = node->data.unwrap_err_expr.op2;
+//    VariableTableEntry *var = node->data.unwrap_err_expr.var;
+//
+//    LLVMValueRef expr_val = gen_expr(g, op1);
+//    TypeTableEntry *expr_type = get_expr_type(op1);
+//    TypeTableEntry *op2_type = get_expr_type(op2);
+//    assert(expr_type->id == TypeTableEntryIdErrorUnion);
+//    TypeTableEntry *child_type = expr_type->data.error.child_type;
+//    LLVMValueRef err_val;
+//    if (handle_is_ptr(expr_type)) {
+//        LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, "");
+//        err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
+//    } else {
+//        err_val = expr_val;
+//    }
+//    LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
+//    LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
+//
+//    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrOk");
+//    LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError");
+//    LLVMBasicBlockRef end_block;
+//    bool err_reachable = op2_type->id != TypeTableEntryIdUnreachable;
+//    bool have_end_block = err_reachable && type_has_bits(child_type);
+//    if (have_end_block) {
+//        end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrEnd");
+//    }
+//
+//    LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, err_block);
+//    if (var) {
+//        LLVMBuildStore(g->builder, err_val, var->value_ref);
+//    }
+//    LLVMValueRef err_result = gen_expr(g, op2);
+//    if (have_end_block) {
+//        LLVMBuildBr(g->builder, end_block);
+//    } else if (err_reachable) {
+//        LLVMBuildBr(g->builder, ok_block);
+//    }
+//
+//    LLVMPositionBuilderAtEnd(g->builder, ok_block);
+//    if (!type_has_bits(child_type)) {
+//        return nullptr;
+//    }
+//    LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");
+//    LLVMValueRef child_val = get_handle_value(g, child_val_ptr, child_type);
+//
+//    if (!have_end_block) {
+//        return child_val;
+//    }
+//
+//    LLVMBuildBr(g->builder, end_block);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, end_block);
+//    LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(err_result), "");
+//    LLVMValueRef incoming_values[2] = {child_val, err_result};
+//    LLVMBasicBlockRef incoming_blocks[2] = {ok_block, err_block};
+//    LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
+//    return phi;
+//}
+//
+//static void gen_defers_for_block(CodeGen *g, BlockContext *inner_block, BlockContext *outer_block,
+//        bool gen_error_defers, bool gen_maybe_defers)
+//{
+//    while (inner_block != outer_block) {
+//        if (inner_block->node->type == NodeTypeDefer &&
+//           ((inner_block->node->data.defer.kind == ReturnKindUnconditional) ||
+//            (gen_error_defers && inner_block->node->data.defer.kind == ReturnKindError) ||
+//            (gen_maybe_defers && inner_block->node->data.defer.kind == ReturnKindMaybe)))
+//        {
+//            gen_expr(g, inner_block->node->data.defer.expr);
+//        }
+//        inner_block = inner_block->parent;
+//    }
+//}
+//
+//static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeReturnExpr);
+//    AstNode *param_node = node->data.return_expr.expr;
+//    assert(param_node);
+//    LLVMValueRef value = gen_expr(g, param_node);
+//    TypeTableEntry *value_type = get_expr_type(param_node);
+//
+//    switch (node->data.return_expr.kind) {
+//        case ReturnKindUnconditional:
+//            {
+//                Expr *expr = get_resolved_expr(param_node);
+//                if (expr->const_val.ok) {
+//                    if (value_type->id == TypeTableEntryIdErrorUnion) {
+//                        if (expr->const_val.data.x_err.err) {
+//                            expr->return_knowledge = ReturnKnowledgeKnownError;
+//                        } else {
+//                            expr->return_knowledge = ReturnKnowledgeKnownNonError;
+//                        }
+//                    } else if (value_type->id == TypeTableEntryIdMaybe) {
+//                        if (expr->const_val.data.x_maybe) {
+//                            expr->return_knowledge = ReturnKnowledgeKnownNonNull;
+//                        } else {
+//                            expr->return_knowledge = ReturnKnowledgeKnownNull;
+//                        }
+//                    }
+//                }
+//                return gen_return(g, node, value, expr->return_knowledge);
+//            }
+//        case ReturnKindError:
+//            {
+//                assert(value_type->id == TypeTableEntryIdErrorUnion);
+//                TypeTableEntry *child_type = value_type->data.error.child_type;
+//
+//                LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetReturn");
+//                LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrRetContinue");
+//
+//                LLVMValueRef err_val;
+//                if (type_has_bits(child_type)) {
+//                    LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, value, 0, "");
+//                    err_val = LLVMBuildLoad(g->builder, err_val_ptr, "");
+//                } else {
+//                    err_val = value;
+//                }
+//                LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref);
+//                LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
+//                LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block);
+//
+//                LLVMPositionBuilderAtEnd(g->builder, return_block);
+//                TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
+//                if (return_type->id == TypeTableEntryIdPureError) {
+//                    gen_return(g, node, err_val, ReturnKnowledgeKnownError);
+//                } else if (return_type->id == TypeTableEntryIdErrorUnion) {
+//                    if (type_has_bits(return_type->data.error.child_type)) {
+//                        assert(g->cur_ret_ptr);
+//
+//                        LLVMValueRef tag_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 0, "");
+//                        LLVMBuildStore(g->builder, err_val, tag_ptr);
+//                        LLVMBuildRetVoid(g->builder);
+//                    } else {
+//                        gen_return(g, node, err_val, ReturnKnowledgeKnownError);
+//                    }
+//                } else {
+//                    zig_unreachable();
+//                }
+//
+//                LLVMPositionBuilderAtEnd(g->builder, continue_block);
+//                if (type_has_bits(child_type)) {
+//                    LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");
+//                    return get_handle_value(g, val_ptr, child_type);
+//                } else {
+//                    return nullptr;
+//                }
+//            }
+//        case ReturnKindMaybe:
+//            {
+//                assert(value_type->id == TypeTableEntryIdMaybe);
+//                TypeTableEntry *child_type = value_type->data.maybe.child_type;
+//
+//                LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetReturn");
+//                LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetContinue");
+//
+//                LLVMValueRef maybe_val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");
+//                LLVMValueRef is_non_null = LLVMBuildLoad(g->builder, maybe_val_ptr, "");
+//
+//                LLVMValueRef zero = LLVMConstNull(LLVMInt1Type());
+//                LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntNE, is_non_null, zero, "");
+//                LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block);
+//
+//                LLVMPositionBuilderAtEnd(g->builder, return_block);
+//                TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
+//                assert(return_type->id == TypeTableEntryIdMaybe);
+//                if (handle_is_ptr(return_type)) {
+//                    assert(g->cur_ret_ptr);
+//
+//                    LLVMValueRef maybe_bit_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 1, "");
+//                    LLVMBuildStore(g->builder, zero, maybe_bit_ptr);
+//                    LLVMBuildRetVoid(g->builder);
+//                } else {
+//                    LLVMValueRef ret_zero_value = LLVMConstNull(return_type->type_ref);
+//                    gen_return(g, node, ret_zero_value, ReturnKnowledgeKnownNull);
+//                }
+//
+//                LLVMPositionBuilderAtEnd(g->builder, continue_block);
+//                if (type_has_bits(child_type)) {
+//                    LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 0, "");
+//                    return get_handle_value(g, val_ptr, child_type);
+//                } else {
+//                    return nullptr;
+//                }
+//            }
+//    }
+//    zig_unreachable();
+//}
+//
+//static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMValueRef cond_value,
+//        AstNode *then_node, AstNode *else_node)
+//{
+//    assert(then_node);
+//    assert(else_node);
+//
+//    TypeTableEntry *then_type = get_expr_type(then_node);
+//    TypeTableEntry *else_type = get_expr_type(else_node);
+//
+//    bool use_then_value = type_has_bits(then_type);
+//    bool use_else_value = type_has_bits(else_type);
+//
+//    LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then");
+//    LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else");
+//
+//    LLVMBasicBlockRef endif_block = nullptr;
+//    bool then_endif_reachable = then_type->id != TypeTableEntryIdUnreachable;
+//    bool else_endif_reachable = else_type->id != TypeTableEntryIdUnreachable;
+//    if (then_endif_reachable || else_endif_reachable) {
+//        endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf");
+//    }
+//
+//    LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, then_block);
+//    LLVMValueRef then_expr_result = gen_expr(g, then_node);
+//    if (then_endif_reachable) {
+//        clear_debug_source_node(g);
+//        LLVMBuildBr(g->builder, endif_block);
+//    }
+//    LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, else_block);
+//    LLVMValueRef else_expr_result = gen_expr(g, else_node);
+//    if (else_endif_reachable) {
+//        clear_debug_source_node(g);
+//        LLVMBuildBr(g->builder, endif_block);
+//    }
+//    LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder);
+//
+//    if (then_endif_reachable || else_endif_reachable) {
+//        LLVMPositionBuilderAtEnd(g->builder, endif_block);
+//        if (use_then_value && use_else_value) {
+//            LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
+//            LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
+//            LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block};
+//            LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
+//            return phi;
+//        } else if (use_then_value) {
+//            return then_expr_result;
+//        } else if (use_else_value) {
+//            return else_expr_result;
+//        }
+//    }
+//
+//    return nullptr;
+//}
+//
+//static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeIfBoolExpr);
+//    assert(node->data.if_bool_expr.condition);
+//    assert(node->data.if_bool_expr.then_block);
+//
+//    ConstExprValue *const_val = &get_resolved_expr(node->data.if_bool_expr.condition)->const_val;
+//    if (const_val->ok) {
+//        if (const_val->data.x_bool) {
+//            return gen_expr(g, node->data.if_bool_expr.then_block);
+//        } else if (node->data.if_bool_expr.else_node) {
+//            return gen_expr(g, node->data.if_bool_expr.else_node);
+//        } else {
+//            return nullptr;
+//        }
+//    } else {
+//        LLVMValueRef cond_value = gen_expr(g, node->data.if_bool_expr.condition);
+//
+//        return gen_if_bool_expr_raw(g, node, cond_value,
+//                node->data.if_bool_expr.then_block,
+//                node->data.if_bool_expr.else_node);
+//    }
+//}
+//
+//static LLVMValueRef gen_if_var_then_block(CodeGen *g, AstNode *node, VariableTableEntry *variable, bool maybe_is_ptr,
+//        LLVMValueRef init_val, TypeTableEntry *child_type, AstNode *then_node)
+//{
+//    if (node->data.if_var_expr.var_is_ptr) {
+//        LLVMValueRef payload_ptr;
+//        if (maybe_is_ptr) {
+//            zig_panic("TODO");
+//        } else {
+//            payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, "");
+//        }
+//        LLVMBuildStore(g->builder, payload_ptr, variable->value_ref);
+//    } else {
+//        LLVMValueRef payload_val;
+//        if (maybe_is_ptr) {
+//            payload_val = init_val;
+//        } else {
+//            LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, "");
+//            payload_val = get_handle_value(g, payload_ptr, child_type);
+//        }
+//        gen_assign_raw(g, node, BinOpTypeAssign, variable->value_ref, payload_val,
+//                variable->type, child_type);
+//    }
+//    gen_var_debug_decl(g, variable);
+//
+//    return gen_expr(g, then_node);
+//}
+//
+//static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeIfVarExpr);
+//    assert(node->data.if_var_expr.var_decl.expr);
+//
+//    AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl;
+//    VariableTableEntry *variable = var_decl->variable;
+//
+//    // test if value is the maybe state
+//    TypeTableEntry *expr_type = get_expr_type(var_decl->expr);
+//    TypeTableEntry *child_type = expr_type->data.maybe.child_type;
+//
+//    LLVMValueRef init_val = gen_expr(g, var_decl->expr);
+//
+//
+//    AstNode *then_node = node->data.if_var_expr.then_block;
+//    AstNode *else_node = node->data.if_var_expr.else_node;
+//    bool maybe_is_ptr = child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn;
+//
+//    ConstExprValue *const_val = &get_resolved_expr(var_decl->expr)->const_val;
+//    if (const_val->ok) {
+//        if (const_val->data.x_maybe) {
+//            return gen_if_var_then_block(g, node, variable, maybe_is_ptr, init_val, child_type, then_node);
+//        } else {
+//            return gen_expr(g, else_node);
+//        }
+//    }
+//
+//    LLVMValueRef cond_value;
+//    if (maybe_is_ptr) {
+//        cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, init_val, LLVMConstNull(child_type->type_ref), "");
+//    } else {
+//        LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, init_val, 1, "");
+//        cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
+//    }
+//
+//    TypeTableEntry *then_type = get_expr_type(then_node);
+//    TypeTableEntry *else_type = get_expr_type(else_node);
+//
+//    bool use_then_value = type_has_bits(then_type);
+//    bool use_else_value = type_has_bits(else_type);
+//
+//    LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeThen");
+//    LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeElse");
+//
+//    LLVMBasicBlockRef endif_block;
+//    bool then_endif_reachable = then_type->id != TypeTableEntryIdUnreachable;
+//    bool else_endif_reachable = else_type->id != TypeTableEntryIdUnreachable;
+//    if (then_endif_reachable || else_endif_reachable) {
+//        endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEndIf");
+//    }
+//
+//    LLVMBuildCondBr(g->builder, cond_value, then_block, else_block);
+//
+//    LLVMPositionBuilderAtEnd(g->builder, then_block);
+//    LLVMValueRef then_expr_result = gen_if_var_then_block(g, node, variable, maybe_is_ptr, init_val, child_type, then_node);
+//
+//    if (then_endif_reachable) {
+//        LLVMBuildBr(g->builder, endif_block);
+//    }
+//    LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder);
+//
+//
+//    LLVMPositionBuilderAtEnd(g->builder, else_block);
+//    LLVMValueRef else_expr_result = gen_expr(g, else_node);
+//    if (else_endif_reachable) {
+//        LLVMBuildBr(g->builder, endif_block);
+//    }
+//    LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder);
+//
+//    if (then_endif_reachable || else_endif_reachable) {
+//        LLVMPositionBuilderAtEnd(g->builder, endif_block);
+//        if (use_then_value && use_else_value) {
+//            LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), "");
+//            LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result};
+//            LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block};
+//            LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
+//            return phi;
+//        } else if (use_then_value) {
+//            return then_expr_result;
+//        } else if (use_else_value) {
+//            return else_expr_result;
+//        }
+//    }
+//
+//    return nullptr;
+//}
+//
+//static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) {
+//    assert(block_node->type == NodeTypeBlock);
+//
+//    LLVMValueRef 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);
+//        return_value = gen_expr(g, statement_node);
+//    }
+//
+//    bool end_unreachable = implicit_return_type && implicit_return_type->id == TypeTableEntryIdUnreachable;
+//    if (end_unreachable) {
+//        return nullptr;
+//    }
+//
+//    gen_defers_for_block(g, block_node->data.block.nested_block, block_node->data.block.child_block,
+//            false, false);
+//
+//    if (implicit_return_type) {
+//        return gen_return(g, block_node, return_value, ReturnKnowledgeSkipDefers);
+//    } else {
+//        return return_value;
+//    }
+//}
+//
+//static LLVMValueRef gen_asm_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeAsmExpr);
+//
+//    AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
+//
+//    Buf *src_template = asm_expr->asm_template;
+//
+//    Buf llvm_template = BUF_INIT;
+//    buf_resize(&llvm_template, 0);
+//
+//    for (size_t token_i = 0; token_i < asm_expr->token_list.length; token_i += 1) {
+//        AsmToken *asm_token = &asm_expr->token_list.at(token_i);
+//        switch (asm_token->id) {
+//            case AsmTokenIdTemplate:
+//                for (size_t offset = asm_token->start; offset < asm_token->end; offset += 1) {
+//                    uint8_t c = *((uint8_t*)(buf_ptr(src_template) + offset));
+//                    if (c == '$') {
+//                        buf_append_str(&llvm_template, "$$");
+//                    } else {
+//                        buf_append_char(&llvm_template, c);
+//                    }
+//                }
+//                break;
+//            case AsmTokenIdPercent:
+//                buf_append_char(&llvm_template, '%');
+//                break;
+//            case AsmTokenIdVar:
+//                size_t index = find_asm_index(g, node, asm_token);
+//                assert(index < SIZE_MAX);
+//                buf_appendf(&llvm_template, "$%zu", index);
+//                break;
+//        }
+//    }
+//
+//    Buf constraint_buf = BUF_INIT;
+//    buf_resize(&constraint_buf, 0);
+//
+//    assert(asm_expr->return_count == 0 || asm_expr->return_count == 1);
+//
+//    size_t total_constraint_count = asm_expr->output_list.length +
+//                                 asm_expr->input_list.length +
+//                                 asm_expr->clobber_list.length;
+//    size_t input_and_output_count = asm_expr->output_list.length +
+//                                 asm_expr->input_list.length -
+//                                 asm_expr->return_count;
+//    size_t total_index = 0;
+//    size_t param_index = 0;
+//    LLVMTypeRef *param_types = allocate<LLVMTypeRef>(input_and_output_count);
+//    LLVMValueRef *param_values = allocate<LLVMValueRef>(input_and_output_count);
+//    for (size_t i = 0; i < asm_expr->output_list.length; i += 1, total_index += 1) {
+//        AsmOutput *asm_output = asm_expr->output_list.at(i);
+//        bool is_return = (asm_output->return_type != nullptr);
+//        assert(*buf_ptr(asm_output->constraint) == '=');
+//        if (is_return) {
+//            buf_appendf(&constraint_buf, "=%s", buf_ptr(asm_output->constraint) + 1);
+//        } else {
+//            buf_appendf(&constraint_buf, "=*%s", buf_ptr(asm_output->constraint) + 1);
+//        }
+//        if (total_index + 1 < total_constraint_count) {
+//            buf_append_char(&constraint_buf, ',');
+//        }
+//
+//        if (!is_return) {
+//            VariableTableEntry *variable = asm_output->variable;
+//            assert(variable);
+//            param_types[param_index] = LLVMTypeOf(variable->value_ref);
+//            param_values[param_index] = variable->value_ref;
+//            param_index += 1;
+//        }
+//    }
+//    for (size_t i = 0; i < asm_expr->input_list.length; i += 1, total_index += 1, param_index += 1) {
+//        AsmInput *asm_input = asm_expr->input_list.at(i);
+//        buf_append_buf(&constraint_buf, asm_input->constraint);
+//        if (total_index + 1 < total_constraint_count) {
+//            buf_append_char(&constraint_buf, ',');
+//        }
+//
+//        TypeTableEntry *expr_type = get_expr_type(asm_input->expr);
+//        param_types[param_index] = expr_type->type_ref;
+//        param_values[param_index] = gen_expr(g, asm_input->expr);
+//    }
+//    for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1, total_index += 1) {
+//        Buf *clobber_buf = asm_expr->clobber_list.at(i);
+//        buf_appendf(&constraint_buf, "~{%s}", buf_ptr(clobber_buf));
+//        if (total_index + 1 < total_constraint_count) {
+//            buf_append_char(&constraint_buf, ',');
+//        }
+//    }
+//
+//    LLVMTypeRef ret_type;
+//    if (asm_expr->return_count == 0) {
+//        ret_type = LLVMVoidType();
+//    } else {
+//        ret_type = get_expr_type(node)->type_ref;
+//    }
+//    LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, input_and_output_count, false);
+//
+//    bool is_volatile = asm_expr->is_volatile || (asm_expr->output_list.length == 0);
+//    LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template),
+//            buf_ptr(&constraint_buf), is_volatile, false);
+//
+//    set_debug_source_node(g, node);
+//    return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, "");
+//}
+//
+//static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeContainerInitExpr);
+//
+//    TypeTableEntry *type_entry = get_expr_type(node);
+//
+//
+//    if (node->data.container_init_expr.enum_type) {
+//        size_t param_count = node->data.container_init_expr.entries.length;
+//        AstNode *arg1_node;
+//        if (param_count == 1) {
+//            arg1_node = node->data.container_init_expr.entries.at(0);
+//        } else {
+//            assert(param_count == 0);
+//            arg1_node = nullptr;
+//        }
+//        return gen_enum_value_expr(g, node->data.container_init_expr.type,
+//                node->data.container_init_expr.enum_type, arg1_node);
+//    }
+//
+//
+//    if (type_entry->id == TypeTableEntryIdStruct) {
+//        assert(node->data.container_init_expr.kind == ContainerInitKindStruct);
+//
+//        size_t src_field_count = type_entry->data.structure.src_field_count;
+//        assert(src_field_count == node->data.container_init_expr.entries.length);
+//
+//        StructValExprCodeGen *struct_val_expr_node = &node->data.container_init_expr.resolved_struct_val_expr;
+//        LLVMValueRef tmp_struct_ptr = struct_val_expr_node->ptr;
+//
+//        for (size_t i = 0; i < src_field_count; i += 1) {
+//            AstNode *field_node = node->data.container_init_expr.entries.at(i);
+//            assert(field_node->type == NodeTypeStructValueField);
+//            TypeStructField *type_struct_field = field_node->data.struct_val_field.type_struct_field;
+//            if (type_struct_field->type_entry->id == TypeTableEntryIdVoid) {
+//                continue;
+//            }
+//            assert(buf_eql_buf(type_struct_field->name, field_node->data.struct_val_field.name));
+//
+//            set_debug_source_node(g, field_node);
+//            LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, type_struct_field->gen_index, "");
+//            AstNode *expr_node = field_node->data.struct_val_field.expr;
+//            LLVMValueRef value = gen_expr(g, expr_node);
+//            gen_assign_raw(g, field_node, BinOpTypeAssign, field_ptr, value,
+//                    type_struct_field->type_entry, get_expr_type(expr_node));
+//        }
+//
+//        return tmp_struct_ptr;
+//    } else if (type_entry->id == TypeTableEntryIdVoid) {
+//        assert(node->data.container_init_expr.entries.length == 0);
+//        return nullptr;
+//    } else if (type_entry->id == TypeTableEntryIdArray) {
+//        StructValExprCodeGen *struct_val_expr_node = &node->data.container_init_expr.resolved_struct_val_expr;
+//        LLVMValueRef tmp_array_ptr = struct_val_expr_node->ptr;
+//
+//        size_t field_count = type_entry->data.array.len;
+//        assert(field_count == node->data.container_init_expr.entries.length);
+//
+//        TypeTableEntry *child_type = type_entry->data.array.child_type;
+//
+//        for (size_t i = 0; i < field_count; i += 1) {
+//            AstNode *field_node = node->data.container_init_expr.entries.at(i);
+//            LLVMValueRef elem_val = gen_expr(g, field_node);
+//
+//            LLVMValueRef indices[] = {
+//                LLVMConstNull(g->builtin_types.entry_usize->type_ref),
+//                LLVMConstInt(g->builtin_types.entry_usize->type_ref, i, false),
+//            };
+//            set_debug_source_node(g, field_node);
+//            LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, tmp_array_ptr, indices, 2, "");
+//            gen_assign_raw(g, field_node, BinOpTypeAssign, elem_ptr, elem_val,
+//                    child_type, get_expr_type(field_node));
+//        }
+//
+//        return tmp_array_ptr;
+//    } else {
+//        zig_unreachable();
+//    }
+//}
+//
+//static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeWhileExpr);
+//    assert(node->data.while_expr.condition);
+//    assert(node->data.while_expr.body);
+//
+//    //AstNode *continue_expr_node = node->data.while_expr.continue_expr;
+//
+//    bool condition_always_true = node->data.while_expr.condition_always_true;
+//    //bool contains_break = node->data.while_expr.contains_break;
+//    if (condition_always_true) {
+//        // generate a forever loop
+//        zig_panic("TODO IR");
+//
+//        //LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
+//        //LLVMBasicBlockRef continue_block = continue_expr_node ?
+//        //    LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileContinue") : body_block;
+//        //LLVMBasicBlockRef end_block = nullptr;
+//        //if (contains_break) {
+//        //    end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
+//        //}
+//
+//        //set_debug_source_node(g, node);
+//        //LLVMBuildBr(g->builder, body_block);
+//
+//        //if (continue_expr_node) {
+//        //    LLVMPositionBuilderAtEnd(g->builder, continue_block);
+//
+//        //    gen_expr(g, continue_expr_node);
+//
+//        //    set_debug_source_node(g, node);
+//        //    LLVMBuildBr(g->builder, body_block);
+//        //}
+//
+//        //LLVMPositionBuilderAtEnd(g->builder, body_block);
+//        //g->break_block_stack.append(end_block);
+//        //g->continue_block_stack.append(continue_block);
+//        //gen_expr(g, node->data.while_expr.body);
+//        //g->break_block_stack.pop();
+//        //g->continue_block_stack.pop();
+//
+//        //if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
+//        //    set_debug_source_node(g, node);
+//        //    LLVMBuildBr(g->builder, continue_block);
+//        //}
+//
+//        //if (contains_break) {
+//        //    LLVMPositionBuilderAtEnd(g->builder, end_block);
+//        //}
+//    } else {
+//        zig_panic("moved to ir.cpp");
+//    }
+//
+//    return nullptr;
+//}
+
+//static LLVMValueRef gen_break(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeBreak);
+//    LLVMBasicBlockRef dest_block = g->break_block_stack.last();
+//
+//    set_debug_source_node(g, node);
+//    return LLVMBuildBr(g->builder, dest_block);
+//}
+
+//static LLVMValueRef gen_continue(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeContinue);
+//    LLVMBasicBlockRef dest_block = g->continue_block_stack.last();
+//
+//    set_debug_source_node(g, node);
+//    return LLVMBuildBr(g->builder, dest_block);
+//}
+//
+//static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl,
+//        bool unwrap_maybe, LLVMValueRef *init_value, TypeTableEntry **expr_type, bool var_is_ptr)
+//{
+//    VariableTableEntry *variable = var_decl->variable;
+//
+//    assert(variable);
+//
+//    if (var_decl->expr) {
+//        *init_value = gen_expr(g, var_decl->expr);
+//        *expr_type = get_expr_type(var_decl->expr);
+//    }
+//    if (!type_has_bits(variable->type)) {
+//        return nullptr;
+//    }
+//
+//    bool have_init_expr = false;
+//    bool want_zeroes = false;
+//    if (var_decl->expr) {
+//        ConstExprValue *const_val = &get_resolved_expr(var_decl->expr)->const_val;
+//        if (!const_val->ok || const_val->special == ConstValSpecialOther) {
+//            have_init_expr = true;
+//        }
+//        if (const_val->ok && const_val->special == ConstValSpecialZeroes) {
+//            want_zeroes = true;
+//        }
+//    }
+//    if (have_init_expr) {
+//        TypeTableEntry *expr_type = get_expr_type(var_decl->expr);
+//        LLVMValueRef value;
+//        if (unwrap_maybe) {
+//            assert(var_decl->expr);
+//            assert(expr_type->id == TypeTableEntryIdMaybe);
+//            value = gen_unwrap_maybe(g, var_decl->expr, *init_value);
+//            expr_type = expr_type->data.maybe.child_type;
+//        } else {
+//            value = *init_value;
+//        }
+//        gen_assign_raw(g, var_decl->expr, BinOpTypeAssign, variable->value_ref,
+//                value, variable->type, expr_type);
+//    } else {
+//        bool ignore_uninit = false;
+//        // handle runtime stack allocation
+//        if (var_decl->type) {
+//            TypeTableEntry *var_type = get_type_for_type_node(var_decl->type);
+//            if (var_type->id == TypeTableEntryIdStruct &&
+//                var_type->data.structure.is_slice)
+//            {
+//                assert(var_decl->type->type == NodeTypeArrayType);
+//                AstNode *size_node = var_decl->type->data.array_type.size;
+//                if (size_node) {
+//                    ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val;
+//                    if (!const_val->ok) {
+//                        TypeTableEntry *ptr_type = var_type->data.structure.fields[0].type_entry;
+//                        assert(ptr_type->id == TypeTableEntryIdPointer);
+//                        TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
+//
+//                        LLVMValueRef size_val = gen_expr(g, size_node);
+//
+//                        set_debug_source_node(g, source_node);
+//                        LLVMValueRef ptr_val = LLVMBuildArrayAlloca(g->builder, child_type->type_ref,
+//                                size_val, "");
+//
+//                        size_t ptr_index = var_type->data.structure.fields[0].gen_index;
+//                        assert(ptr_index != SIZE_MAX);
+//                        size_t len_index = var_type->data.structure.fields[1].gen_index;
+//                        assert(len_index != SIZE_MAX);
+//
+//                        // store the freshly allocated pointer in the unknown size array struct
+//                        LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder,
+//                                variable->value_ref, ptr_index, "");
+//                        LLVMBuildStore(g->builder, ptr_val, ptr_field_ptr);
+//
+//                        // store the size in the len field
+//                        LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder,
+//                                variable->value_ref, len_index, "");
+//                        LLVMBuildStore(g->builder, size_val, len_field_ptr);
+//
+//                        // don't clobber what we just did with debug initialization
+//                        ignore_uninit = true;
+//                    }
+//                }
+//            }
+//        }
+//        bool want_safe = want_debug_safety(g, source_node);
+//        if (!ignore_uninit && (want_safe || want_zeroes)) {
+//            TypeTableEntry *usize = g->builtin_types.entry_usize;
+//            uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref);
+//            uint64_t align_bytes = get_memcpy_align(g, variable->type);
+//
+//            // memset uninitialized memory to 0xa
+//            set_debug_source_node(g, source_node);
+//            LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
+//            LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), want_zeroes ? 0x00 : 0xaa, false);
+//            LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, variable->value_ref, ptr_u8, "");
+//            LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false);
+//            LLVMValueRef align_in_bytes = LLVMConstInt(LLVMInt32Type(), align_bytes, false);
+//            LLVMValueRef params[] = {
+//                dest_ptr,
+//                fill_char,
+//                byte_count,
+//                align_in_bytes,
+//                LLVMConstNull(LLVMInt1Type()), // is volatile
+//            };
+//
+//            LLVMBuildCall(g->builder, g->memset_fn_val, params, 5, "");
+//        }
+//    }
+//
+//    gen_var_debug_decl(g, variable);
+//    return nullptr;
+//}
+//
+//static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeSwitchExpr);
+//
+//    if (node->data.switch_expr.const_chosen_prong_index != SIZE_MAX) {
+//        AstNode *prong_node = node->data.switch_expr.prongs.at(node->data.switch_expr.const_chosen_prong_index);
+//        assert(prong_node->type == NodeTypeSwitchProng);
+//        AstNode *prong_expr = prong_node->data.switch_prong.expr;
+//        return gen_expr(g, prong_expr);
+//    }
+//
+//    TypeTableEntry *target_type = get_expr_type(node->data.switch_expr.expr);
+//    LLVMValueRef target_value_handle = gen_expr(g, node->data.switch_expr.expr);
+//    LLVMValueRef target_value;
+//    if (handle_is_ptr(target_type)) {
+//        if (target_type->id == TypeTableEntryIdEnum) {
+//            set_debug_source_node(g, node);
+//            LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, 0, "");
+//            target_value = LLVMBuildLoad(g->builder, tag_field_ptr, "");
+//        } else if (target_type->id == TypeTableEntryIdErrorUnion) {
+//            set_debug_source_node(g, node);
+//            LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, 0, "");
+//            target_value = LLVMBuildLoad(g->builder, tag_field_ptr, "");
+//        } else {
+//            zig_unreachable();
+//        }
+//    } else {
+//        target_value = target_value_handle;
+//    }
+//
+//
+//    TypeTableEntry *switch_type = get_expr_type(node);
+//    bool result_has_bits = type_has_bits(switch_type);
+//    bool end_unreachable = (switch_type->id == TypeTableEntryIdUnreachable);
+//
+//    LLVMBasicBlockRef end_block = end_unreachable ?
+//        nullptr : LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchEnd");
+//    LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchElse");
+//    size_t prong_count = node->data.switch_expr.prongs.length;
+//
+//    set_debug_source_node(g, node);
+//    LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, prong_count);
+//
+//    ZigList<LLVMValueRef> incoming_values = {0};
+//    ZigList<LLVMBasicBlockRef> incoming_blocks = {0};
+//
+//    AstNode *else_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);
+//        VariableTableEntry *prong_var = prong_node->data.switch_prong.var;
+//
+//        LLVMBasicBlockRef prong_block;
+//        if (prong_node->data.switch_prong.items.length == 0) {
+//            assert(!else_prong);
+//            else_prong = prong_node;
+//            prong_block = else_block;
+//        } else {
+//            prong_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchProng");
+//            size_t prong_item_count = prong_node->data.switch_prong.items.length;
+//            bool make_item_blocks = prong_var && prong_item_count > 1;
+//
+//            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);
+//                LLVMValueRef val;
+//                if (target_type->id == TypeTableEntryIdEnum ||
+//                    target_type->id == TypeTableEntryIdErrorUnion)
+//                {
+//                    assert(item_node->type == NodeTypeSymbol);
+//                    TypeEnumField *enum_field = nullptr;
+//                    uint32_t err_value = 0;
+//                    if (target_type->id == TypeTableEntryIdEnum) {
+//                        enum_field = item_node->data.symbol_expr.enum_field;
+//                        assert(enum_field);
+//                        val = LLVMConstInt(target_type->data.enumeration.tag_type->type_ref,
+//                                enum_field->value, false);
+//                    } else if (target_type->id == TypeTableEntryIdErrorUnion) {
+//                        err_value = item_node->data.symbol_expr.err_value;
+//                        val = LLVMConstInt(g->err_tag_type->type_ref, err_value, false);
+//                    } else {
+//                        zig_unreachable();
+//                    }
+//
+//                    if (prong_var && type_has_bits(prong_var->type)) {
+//                        LLVMBasicBlockRef item_block;
+//
+//                        if (make_item_blocks) {
+//                            item_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchProngItem");
+//                            LLVMAddCase(switch_instr, val, item_block);
+//                            LLVMPositionBuilderAtEnd(g->builder, item_block);
+//                        } else {
+//                            LLVMAddCase(switch_instr, val, prong_block);
+//                            LLVMPositionBuilderAtEnd(g->builder, prong_block);
+//                        }
+//
+//                        AstNode *var_node = prong_node->data.switch_prong.var_symbol;
+//                        set_debug_source_node(g, var_node);
+//                        if (prong_node->data.switch_prong.var_is_target_expr) {
+//                            gen_assign_raw(g, var_node, BinOpTypeAssign,
+//                                    prong_var->value_ref, target_value, prong_var->type, target_type);
+//                        } else if (target_type->id == TypeTableEntryIdEnum) {
+//                            assert(enum_field);
+//                            assert(type_has_bits(enum_field->type_entry));
+//                            LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle,
+//                                    1, "");
+//                            LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr,
+//                                    LLVMPointerType(enum_field->type_entry->type_ref, 0), "");
+//                            LLVMValueRef handle_val = get_handle_value(g, bitcasted_union_field_ptr,
+//                                    enum_field->type_entry);
+//
+//                            gen_assign_raw(g, var_node, BinOpTypeAssign,
+//                                    prong_var->value_ref, handle_val, prong_var->type, enum_field->type_entry);
+//                        } else if (target_type->id == TypeTableEntryIdErrorUnion) {
+//                            if (err_value == 0) {
+//                                // variable is the payload
+//                                LLVMValueRef err_payload_ptr = LLVMBuildStructGEP(g->builder,
+//                                        target_value_handle, 1, "");
+//                                LLVMValueRef handle_val = get_handle_value(g, err_payload_ptr, prong_var->type);
+//                                gen_assign_raw(g, var_node, BinOpTypeAssign,
+//                                        prong_var->value_ref, handle_val, prong_var->type, prong_var->type);
+//                            } else {
+//                                // variable is the pure error value
+//                                LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder,
+//                                        target_value_handle, 0, "");
+//                                LLVMValueRef handle_val = LLVMBuildLoad(g->builder, err_tag_ptr, "");
+//                                gen_assign_raw(g, var_node, BinOpTypeAssign,
+//                                        prong_var->value_ref, handle_val, prong_var->type, g->err_tag_type);
+//                            }
+//                        } else {
+//                            zig_unreachable();
+//                        }
+//                        if (make_item_blocks) {
+//                            set_debug_source_node(g, var_node);
+//                            LLVMBuildBr(g->builder, prong_block);
+//                        }
+//                    } else {
+//                        LLVMAddCase(switch_instr, val, prong_block);
+//                    }
+//                } else {
+//                    assert(get_resolved_expr(item_node)->const_val.ok);
+//                    val = gen_expr(g, item_node);
+//                    LLVMAddCase(switch_instr, val, prong_block);
+//                }
+//            }
+//        }
+//
+//        LLVMPositionBuilderAtEnd(g->builder, prong_block);
+//        AstNode *prong_expr = prong_node->data.switch_prong.expr;
+//        LLVMValueRef prong_val = gen_expr(g, prong_expr);
+//
+//        if (get_expr_type(prong_expr)->id != TypeTableEntryIdUnreachable) {
+//            set_debug_source_node(g, prong_expr);
+//            LLVMBuildBr(g->builder, end_block);
+//            incoming_values.append(prong_val);
+//            incoming_blocks.append(LLVMGetInsertBlock(g->builder));
+//        }
+//    }
+//
+//    if (!else_prong) {
+//        LLVMPositionBuilderAtEnd(g->builder, else_block);
+//        set_debug_source_node(g, node);
+//        if (want_debug_safety(g, node)) {
+//            gen_debug_safety_crash(g);
+//        } else {
+//            LLVMBuildUnreachable(g->builder);
+//        }
+//    }
+//
+//    if (end_unreachable) {
+//        return nullptr;
+//    }
+//
+//    LLVMPositionBuilderAtEnd(g->builder, end_block);
+//
+//    if (result_has_bits) {
+//        set_debug_source_node(g, node);
+//        LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(incoming_values.at(0)), "");
+//        LLVMAddIncoming(phi, incoming_values.items, incoming_blocks.items, incoming_values.length);
+//        return phi;
+//    } else {
+//        return nullptr;
+//    }
+//}
+//
+//static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) {
+//    assert(node->type == NodeTypeArrayAccessExpr);
+//
+//    LLVMValueRef ptr = gen_array_ptr(g, node);
+//    TypeTableEntry *child_type;
+//    TypeTableEntry *array_type = get_expr_type(node->data.array_access_expr.array_ref_expr);
+//    if (array_type->id == TypeTableEntryIdPointer) {
+//        child_type = array_type->data.pointer.child_type;
+//    } else if (array_type->id == TypeTableEntryIdStruct) {
+//        assert(array_type->data.structure.is_slice);
+//        TypeTableEntry *child_ptr_type = array_type->data.structure.fields[0].type_entry;
+//        assert(child_ptr_type->id == TypeTableEntryIdPointer);
+//        child_type = child_ptr_type->data.pointer.child_type;
+//    } else if (array_type->id == TypeTableEntryIdArray) {
+//        child_type = array_type->data.array.child_type;
+//    } else {
+//        zig_unreachable();
+//    }
+//
+//    if (is_lvalue || !ptr || handle_is_ptr(child_type)) {
+//        return ptr;
+//    } else {
+//        return LLVMBuildLoad(g->builder, ptr, "");
+//    }
+//}
+//
+//static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) {
+//    assert(node->type == NodeTypeFieldAccessExpr);
+//
+//    AstNode *struct_expr = node->data.field_access_expr.struct_expr;
+//    TypeTableEntry *struct_type = get_expr_type(struct_expr);
+//
+//    if (struct_type->id == TypeTableEntryIdArray) {
+//        Buf *name = node->data.field_access_expr.field_name;
+//        assert(buf_eql_str(name, "len"));
+//        return LLVMConstInt(g->builtin_types.entry_usize->type_ref,
+//                struct_type->data.array.len, false);
+//    } else if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer &&
+//               struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct))
+//    {
+//        TypeTableEntry *type_entry;
+//        LLVMValueRef ptr = gen_field_ptr(g, node, &type_entry);
+//        if (is_lvalue || handle_is_ptr(type_entry)) {
+//            return ptr;
+//        } else {
+//            return LLVMBuildLoad(g->builder, ptr, "");
+//        }
+//    } else if (struct_type->id == TypeTableEntryIdMetaType) {
+//        assert(!is_lvalue);
+//        TypeTableEntry *child_type = get_type_for_type_node(struct_expr);
+//        if (child_type->id == TypeTableEntryIdEnum) {
+//            return gen_enum_value_expr(g, node, child_type, nullptr);
+//        } else {
+//            zig_unreachable();
+//        }
+//    } else if (struct_type->id == TypeTableEntryIdNamespace) {
+//        VariableTableEntry *variable = get_resolved_expr(node)->variable;
+//        assert(variable);
+//        return gen_variable(g, node, variable);
+//    } else {
+//        zig_unreachable();
+//    }
+//}
+//
+//static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value, ReturnKnowledge rk) {
+//    BlockContext *defer_inner_block = source_node->block_context;
+//    BlockContext *defer_outer_block = source_node->block_context->fn_entry->fn_def_node->block_context;
+//    if (rk == ReturnKnowledgeUnknown) {
+//        if (get_conditional_defer_count(defer_inner_block, defer_outer_block) > 0) {
+//            // generate branching code that checks the return value and generates defers
+//            // if the return value is error
+//            zig_panic("TODO");
+//        }
+//    } else if (rk != ReturnKnowledgeSkipDefers) {
+//        gen_defers_for_block(g, defer_inner_block, defer_outer_block,
+//                rk == ReturnKnowledgeKnownError, rk == ReturnKnowledgeKnownNull);
+//    }
+//
+//    TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
+//    bool is_extern = g->cur_fn->type_entry->data.fn.fn_type_id.is_extern;
+//    if (handle_is_ptr(return_type)) {
+//        if (is_extern) {
+//            LLVMValueRef by_val_value = LLVMBuildLoad(g->builder, value, "");
+//            LLVMBuildRet(g->builder, by_val_value);
+//        } else {
+//            assert(g->cur_ret_ptr);
+//            gen_assign_raw(g, source_node, BinOpTypeAssign, g->cur_ret_ptr, value, return_type, return_type);
+//            LLVMBuildRetVoid(g->builder);
+//        }
+//    } else {
+//        LLVMBuildRet(g->builder, value);
+//    }
+//    return nullptr;
+//}
+//
+//static LLVMValueRef gen_goto(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeGoto);
+//
+//    // generate defers for blocks that we exit
+//    LabelTableEntry *label = node->data.goto_expr.label_entry;
+//    BlockContext *this_context = node->block_context;
+//    BlockContext *target_context = label->decl_node->block_context;
+//    gen_defers_for_block(g, this_context, target_context, false, false);
+//
+//    set_debug_source_node(g, node);
+//    LLVMBuildBr(g->builder, node->data.goto_expr.label_entry->basic_block);
+//    return nullptr;
+//}
+//
+//static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) {
+//    AstNode *init_expr = node->data.variable_declaration.expr;
+//    if (node->data.variable_declaration.is_const && init_expr) {
+//        TypeTableEntry *init_expr_type = get_expr_type(init_expr);
+//        if (init_expr_type->id == TypeTableEntryIdNumLitFloat ||
+//            init_expr_type->id == TypeTableEntryIdNumLitInt)
+//        {
+//            return nullptr;
+//        }
+//    }
+//
+//    LLVMValueRef init_val = nullptr;
+//    TypeTableEntry *init_val_type;
+//    return gen_var_decl_raw(g, node, &node->data.variable_declaration, false, &init_val, &init_val_type, false);
+//}
+//
+//static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, BuiltinFnId fn_id) {
+//    // [0-ctz,1-clz][0-8,1-16,2-32,3-64]
+//    size_t index0 = (fn_id == BuiltinFnIdCtz) ? 0 : 1;
+//    size_t index1 = bits_index(int_type->data.integral.bit_count);
+//    LLVMValueRef *fn = &g->int_builtin_fns[index0][index1];
+//    if (!*fn) {
+//        const char *fn_name = (fn_id == BuiltinFnIdCtz) ? "cttz" : "ctlz";
+//        Buf *llvm_name = buf_sprintf("llvm.%s.i%zu", fn_name, int_type->data.integral.bit_count);
+//        LLVMTypeRef param_types[] = {
+//            int_type->type_ref,
+//            LLVMInt1Type(),
+//        };
+//        LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, 2, false);
+//        *fn = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type);
+//    }
+//    return *fn;
+//}
+//
+//static LLVMValueRef gen_fence(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeFnCallExpr);
+//
+//    AstNode *atomic_order_arg = node->data.fn_call_expr.params.at(0);
+//    ConstExprValue *atomic_order_val = &get_resolved_expr(atomic_order_arg)->const_val;
+//
+//    assert(atomic_order_val->ok);
+//
+//    LLVMAtomicOrdering atomic_order = to_LLVMAtomicOrdering((AtomicOrder)atomic_order_val->data.x_enum.tag);
+//
+//    LLVMBuildFence(g->builder, atomic_order, false, "");
+//    return nullptr;
+//}
+//
+//static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) {
+//    switch (atomic_order) {
+//        case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered;
+//        case AtomicOrderMonotonic: return LLVMAtomicOrderingMonotonic;
+//        case AtomicOrderAcquire: return LLVMAtomicOrderingAcquire;
+//        case AtomicOrderRelease: return LLVMAtomicOrderingRelease;
+//        case AtomicOrderAcqRel: return LLVMAtomicOrderingAcquireRelease;
+//        case AtomicOrderSeqCst: return LLVMAtomicOrderingSequentiallyConsistent;
+//    }
+//    zig_unreachable();
+//}
+//
+//static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref) {
+//    TypeTableEntry *type_entry = get_expr_type(node);
+//    assert(type_entry->id == TypeTableEntryIdMaybe);
+//    TypeTableEntry *child_type = type_entry->data.maybe.child_type;
+//    if (child_type->id == TypeTableEntryIdPointer ||
+//        child_type->id == TypeTableEntryIdFn)
+//    {
+//        return maybe_struct_ref;
+//    } else {
+//        LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, "");
+//        return get_handle_value(g, maybe_field_ptr, child_type);
+//    }
+//}
+//
+//static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
+//    size_t result = 0;
+//    while (inner_block != outer_block) {
+//        if (inner_block->node->type == NodeTypeDefer &&
+//           (inner_block->node->data.defer.kind == ReturnKindError ||
+//            inner_block->node->data.defer.kind == ReturnKindMaybe))
+//        {
+//            result += 1;
+//        }
+//        inner_block = inner_block->parent;
+//    }
+//    return result;
+//}
+//
+//static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok) {
+//    const char *ptr = buf_ptr(node->data.asm_expr.asm_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 LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeSymbol);
+//    VariableTableEntry *variable = get_resolved_expr(node)->variable;
+//    if (variable) {
+//        return gen_variable(g, node, variable);
+//    }
+//
+//    zig_unreachable();
+//}
+//
+//static LLVMValueRef gen_label(CodeGen *g, AstNode *node) {
+//    assert(node->type == NodeTypeLabel);
+//
+//    LabelTableEntry *label = node->data.label.label_entry;
+//    assert(label);
+//
+//    LLVMBasicBlockRef basic_block = label->basic_block;
+//    if (label->entered_from_fallthrough) {
+//        set_debug_source_node(g, node);
+//        LLVMBuildBr(g->builder, basic_block);
+//    }
+//    LLVMPositionBuilderAtEnd(g->builder, basic_block);
+//    return nullptr;
+//}
+//
+//static LLVMValueRef gen_variable(CodeGen *g, AstNode *source_node, VariableTableEntry *variable) {
+//    if (!type_has_bits(variable->type)) {
+//        return nullptr;
+//    } else {
+//        assert(variable->value_ref);
+//        return get_handle_value(g, variable->value_ref, variable->type);
+//    }
+//}
+//